001 /** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018 package org.apache.commons.cli; 019 020 import java.util.ArrayList; 021 import java.util.Arrays; 022 import java.util.Iterator; 023 import java.util.List; 024 025 /** 026 * The class PosixParser provides an implementation of the 027 * {@link Parser#flatten(Options,String[],boolean) flatten} method. 028 * 029 * @author John Keyes (john at integralsource.com) 030 * @version $Revision: 695760 $, $Date: 2008-09-16 01:05:03 -0700 (Tue, 16 Sep 2008) $ 031 */ 032 public class PosixParser extends Parser 033 { 034 /** holder for flattened tokens */ 035 private List tokens = new ArrayList(); 036 037 /** specifies if bursting should continue */ 038 private boolean eatTheRest; 039 040 /** holder for the current option */ 041 private Option currentOption; 042 043 /** the command line Options */ 044 private Options options; 045 046 /** 047 * Resets the members to their original state i.e. remove 048 * all of <code>tokens</code> entries and set <code>eatTheRest</code> 049 * to false. 050 */ 051 private void init() 052 { 053 eatTheRest = false; 054 tokens.clear(); 055 } 056 057 /** 058 * <p>An implementation of {@link Parser}'s abstract 059 * {@link Parser#flatten(Options,String[],boolean) flatten} method.</p> 060 * 061 * <p>The following are the rules used by this flatten method. 062 * <ol> 063 * <li>if <code>stopAtNonOption</code> is <b>true</b> then do not 064 * burst anymore of <code>arguments</code> entries, just add each 065 * successive entry without further processing. Otherwise, ignore 066 * <code>stopAtNonOption</code>.</li> 067 * <li>if the current <code>arguments</code> entry is "<b>--</b>" 068 * just add the entry to the list of processed tokens</li> 069 * <li>if the current <code>arguments</code> entry is "<b>-</b>" 070 * just add the entry to the list of processed tokens</li> 071 * <li>if the current <code>arguments</code> entry is two characters 072 * in length and the first character is "<b>-</b>" then check if this 073 * is a valid {@link Option} id. If it is a valid id, then add the 074 * entry to the list of processed tokens and set the current {@link Option} 075 * member. If it is not a valid id and <code>stopAtNonOption</code> 076 * is true, then the remaining entries are copied to the list of 077 * processed tokens. Otherwise, the current entry is ignored.</li> 078 * <li>if the current <code>arguments</code> entry is more than two 079 * characters in length and the first character is "<b>-</b>" then 080 * we need to burst the entry to determine its constituents. For more 081 * information on the bursting algorithm see 082 * {@link PosixParser#burstToken(String, boolean) burstToken}.</li> 083 * <li>if the current <code>arguments</code> entry is not handled 084 * by any of the previous rules, then the entry is added to the list 085 * of processed tokens.</li> 086 * </ol> 087 * </p> 088 * 089 * @param options The command line {@link Options} 090 * @param arguments The command line arguments to be parsed 091 * @param stopAtNonOption Specifies whether to stop flattening 092 * when an non option is found. 093 * @return The flattened <code>arguments</code> String array. 094 */ 095 protected String[] flatten(Options options, String[] arguments, boolean stopAtNonOption) 096 { 097 init(); 098 this.options = options; 099 100 // an iterator for the command line tokens 101 Iterator iter = Arrays.asList(arguments).iterator(); 102 103 // process each command line token 104 while (iter.hasNext()) 105 { 106 // get the next command line token 107 String token = (String) iter.next(); 108 109 // handle long option --foo or --foo=bar 110 if (token.startsWith("--")) 111 { 112 int pos = token.indexOf('='); 113 String opt = pos == -1 ? token : token.substring(0, pos); // --foo 114 115 if (!options.hasOption(opt)) 116 { 117 processNonOptionToken(token, stopAtNonOption); 118 } 119 else 120 { 121 currentOption = options.getOption(opt); 122 123 tokens.add(opt); 124 if (pos != -1) 125 { 126 tokens.add(token.substring(pos + 1)); 127 } 128 } 129 } 130 131 // single hyphen 132 else if ("-".equals(token)) 133 { 134 tokens.add(token); 135 } 136 else if (token.startsWith("-")) 137 { 138 if (token.length() == 2 || options.hasOption(token)) 139 { 140 processOptionToken(token, stopAtNonOption); 141 } 142 // requires bursting 143 else 144 { 145 burstToken(token, stopAtNonOption); 146 } 147 } 148 else 149 { 150 processNonOptionToken(token, stopAtNonOption); 151 } 152 153 gobble(iter); 154 } 155 156 return (String[]) tokens.toArray(new String[tokens.size()]); 157 } 158 159 /** 160 * Adds the remaining tokens to the processed tokens list. 161 * 162 * @param iter An iterator over the remaining tokens 163 */ 164 private void gobble(Iterator iter) 165 { 166 if (eatTheRest) 167 { 168 while (iter.hasNext()) 169 { 170 tokens.add(iter.next()); 171 } 172 } 173 } 174 175 /** 176 * Add the special token "<b>--</b>" and the current <code>value</code> 177 * to the processed tokens list. Then add all the remaining 178 * <code>argument</code> values to the processed tokens list. 179 * 180 * @param value The current token 181 */ 182 private void processNonOptionToken(String value, boolean stopAtNonOption) 183 { 184 if (stopAtNonOption && (currentOption == null || !currentOption.hasArg())) 185 { 186 eatTheRest = true; 187 tokens.add("--"); 188 } 189 190 tokens.add(value); 191 } 192 193 /** 194 * <p>If an {@link Option} exists for <code>token</code> then 195 * add the token to the processed list.</p> 196 * 197 * <p>If an {@link Option} does not exist and <code>stopAtNonOption</code> 198 * is set then add the remaining tokens to the processed tokens list 199 * directly.</p> 200 * 201 * @param token The current option token 202 * @param stopAtNonOption Specifies whether flattening should halt 203 * at the first non option. 204 */ 205 private void processOptionToken(String token, boolean stopAtNonOption) 206 { 207 if (stopAtNonOption && !options.hasOption(token)) 208 { 209 eatTheRest = true; 210 } 211 212 if (options.hasOption(token)) 213 { 214 currentOption = options.getOption(token); 215 } 216 217 tokens.add(token); 218 } 219 220 /** 221 * Breaks <code>token</code> into its constituent parts 222 * using the following algorithm. 223 * 224 * <ul> 225 * <li>ignore the first character ("<b>-</b>")</li> 226 * <li>foreach remaining character check if an {@link Option} 227 * exists with that id.</li> 228 * <li>if an {@link Option} does exist then add that character 229 * prepended with "<b>-</b>" to the list of processed tokens.</li> 230 * <li>if the {@link Option} can have an argument value and there 231 * are remaining characters in the token then add the remaining 232 * characters as a token to the list of processed tokens.</li> 233 * <li>if an {@link Option} does <b>NOT</b> exist <b>AND</b> 234 * <code>stopAtNonOption</code> <b>IS</b> set then add the special token 235 * "<b>--</b>" followed by the remaining characters and also 236 * the remaining tokens directly to the processed tokens list.</li> 237 * <li>if an {@link Option} does <b>NOT</b> exist <b>AND</b> 238 * <code>stopAtNonOption</code> <b>IS NOT</b> set then add that 239 * character prepended with "<b>-</b>".</li> 240 * </ul> 241 * 242 * @param token The current token to be <b>burst</b> 243 * @param stopAtNonOption Specifies whether to stop processing 244 * at the first non-Option encountered. 245 */ 246 protected void burstToken(String token, boolean stopAtNonOption) 247 { 248 for (int i = 1; i < token.length(); i++) 249 { 250 String ch = String.valueOf(token.charAt(i)); 251 252 if (options.hasOption(ch)) 253 { 254 tokens.add("-" + ch); 255 currentOption = options.getOption(ch); 256 257 if (currentOption.hasArg() && (token.length() != (i + 1))) 258 { 259 tokens.add(token.substring(i + 1)); 260 261 break; 262 } 263 } 264 else if (stopAtNonOption) 265 { 266 processNonOptionToken(token.substring(i), true); 267 break; 268 } 269 else 270 { 271 tokens.add(token); 272 break; 273 } 274 } 275 } 276 }