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 018package org.apache.commons.cli; 019 020import java.util.ArrayList; 021import java.util.Arrays; 022import java.util.Iterator; 023import java.util.List; 024 025/** 026 * The class PosixParser provides an implementation of the {@link Parser#flatten(Options,String[],boolean) flatten} 027 * method. 028 * 029 * @deprecated since 1.3, use the {@link DefaultParser} instead 030 */ 031@Deprecated 032public class PosixParser extends Parser { 033 /** Holder for flattened tokens */ 034 private final List<String> tokens = new ArrayList<>(); 035 036 /** Specifies if bursting should continue */ 037 private boolean eatTheRest; 038 039 /** Holder for the current option */ 040 private Option currentOption; 041 042 /** The command line Options */ 043 private Options options; 044 045 /** 046 * Breaks {@code token} into its constituent parts using the following algorithm. 047 * 048 * <ul> 049 * <li>ignore the first character ("<b>-</b>")</li> 050 * <li>for each remaining character check if an {@link Option} exists with that id.</li> 051 * <li>if an {@link Option} does exist then add that character prepended with "<b>-</b>" to the list of processed 052 * tokens.</li> 053 * <li>if the {@link Option} can have an argument value and there are remaining characters in the token then add the 054 * remaining characters as a token to the list of processed tokens.</li> 055 * <li>if an {@link Option} does <b>NOT</b> exist <b>AND</b> {@code stopAtNonOption} <b>IS</b> set then add the 056 * special token "<b>--</b>" followed by the remaining characters and also the remaining tokens directly to the 057 * processed tokens list.</li> 058 * <li>if an {@link Option} does <b>NOT</b> exist <b>AND</b> {@code stopAtNonOption} <b>IS NOT</b> set then add 059 * that character prepended with "<b>-</b>".</li> 060 * </ul> 061 * 062 * @param token The current token to be <b>burst</b> 063 * @param stopAtNonOption Specifies whether to stop processing at the first non-Option encountered. 064 */ 065 protected void burstToken(final String token, final boolean stopAtNonOption) { 066 for (int i = 1; i < token.length(); i++) { 067 final String ch = String.valueOf(token.charAt(i)); 068 069 if (!options.hasOption(ch)) { 070 if (stopAtNonOption) { 071 processNonOptionToken(token.substring(i), true); 072 } else { 073 tokens.add(token); 074 } 075 break; 076 } 077 tokens.add("-" + ch); 078 currentOption = options.getOption(ch); 079 080 if (currentOption.hasArg() && token.length() != i + 1) { 081 tokens.add(token.substring(i + 1)); 082 083 break; 084 } 085 } 086 } 087 088 /** 089 * <p> 090 * An implementation of {@link Parser}'s abstract {@link Parser#flatten(Options,String[],boolean) flatten} method. 091 * </p> 092 * 093 * <p> 094 * The following are the rules used by this flatten method. 095 * </p> 096 * <ol> 097 * <li>if {@code stopAtNonOption} is <b>true</b> then do not burst anymore of {@code arguments} entries, just 098 * add each successive entry without further processing. Otherwise, ignore {@code stopAtNonOption}.</li> 099 * <li>if the current {@code arguments} entry is "<b>--</b>" just add the entry to the list of processed 100 * tokens</li> 101 * <li>if the current {@code arguments} entry is "<b>-</b>" just add the entry to the list of processed tokens</li> 102 * <li>if the current {@code arguments} entry is two characters in length and the first character is "<b>-</b>" 103 * then check if this is a valid {@link Option} id. If it is a valid id, then add the entry to the list of processed 104 * tokens and set the current {@link Option} member. If it is not a valid id and {@code stopAtNonOption} is true, 105 * then the remaining entries are copied to the list of processed tokens. Otherwise, the current entry is ignored.</li> 106 * <li>if the current {@code arguments} entry is more than two characters in length and the first character is 107 * "<b>-</b>" then we need to burst the entry to determine its constituents. For more information on the bursting 108 * algorithm see {@link PosixParser#burstToken(String, boolean) burstToken}.</li> 109 * <li>if the current {@code arguments} entry is not handled by any of the previous rules, then the entry is added 110 * to the list of processed tokens.</li> 111 * </ol> 112 * 113 * @param options The command line {@link Options} 114 * @param arguments The command line arguments to be parsed 115 * @param stopAtNonOption Specifies whether to stop flattening when an non option is found. 116 * @return The flattened {@code arguments} String array. 117 */ 118 @Override 119 protected String[] flatten(final Options options, final String[] arguments, final boolean stopAtNonOption) throws ParseException { 120 init(); 121 this.options = options; 122 123 // an iterator for the command line tokens 124 final Iterator<String> iter = Arrays.asList(arguments).iterator(); 125 126 // process each command line token 127 while (iter.hasNext()) { 128 // get the next command line token 129 final String token = iter.next(); 130 131 // single or double hyphen 132 if ("-".equals(token) || "--".equals(token)) { 133 tokens.add(token); 134 } 135 136 // handle long option --foo or --foo=bar 137 else if (token.startsWith("--")) { 138 final int pos = DefaultParser.indexOfEqual(token); 139 final String opt = pos == -1 ? token : token.substring(0, pos); // --foo 140 141 final List<String> matchingOpts = options.getMatchingOptions(opt); 142 143 if (matchingOpts.isEmpty()) { 144 processNonOptionToken(token, stopAtNonOption); 145 } else if (matchingOpts.size() > 1) { 146 throw new AmbiguousOptionException(opt, matchingOpts); 147 } else { 148 currentOption = options.getOption(matchingOpts.get(0)); 149 150 tokens.add("--" + currentOption.getLongOpt()); 151 if (pos != -1) { 152 tokens.add(token.substring(pos + 1)); 153 } 154 } 155 } 156 157 else if (token.startsWith("-")) { 158 if (token.length() == 2 || options.hasOption(token)) { 159 processOptionToken(token, stopAtNonOption); 160 } else if (!options.getMatchingOptions(token).isEmpty()) { 161 final List<String> matchingOpts = options.getMatchingOptions(token); 162 if (matchingOpts.size() > 1) { 163 throw new AmbiguousOptionException(token, matchingOpts); 164 } 165 final Option opt = options.getOption(matchingOpts.get(0)); 166 processOptionToken("-" + opt.getLongOpt(), stopAtNonOption); 167 } 168 // requires bursting 169 else { 170 burstToken(token, stopAtNonOption); 171 } 172 } else { 173 processNonOptionToken(token, stopAtNonOption); 174 } 175 176 gobble(iter); 177 } 178 179 return tokens.toArray(Util.EMPTY_STRING_ARRAY); 180 } 181 182 /** 183 * Adds the remaining tokens to the processed tokens list. 184 * 185 * @param iter An iterator over the remaining tokens 186 */ 187 private void gobble(final Iterator<String> iter) { 188 if (eatTheRest) { 189 while (iter.hasNext()) { 190 tokens.add(iter.next()); 191 } 192 } 193 } 194 195 /** 196 * Resets the members to their original state i.e. remove all of {@code tokens} entries and set 197 * {@code eatTheRest} to false. 198 */ 199 private void init() { 200 eatTheRest = false; 201 tokens.clear(); 202 } 203 204 /** 205 * Add the special token "<b>--</b>" and the current {@code value} to the processed tokens list. Then add all the 206 * remaining {@code argument} values to the processed tokens list. 207 * 208 * @param value The current token 209 */ 210 private void processNonOptionToken(final String value, final boolean stopAtNonOption) { 211 if (stopAtNonOption && (currentOption == null || !currentOption.hasArg())) { 212 eatTheRest = true; 213 tokens.add("--"); 214 } 215 216 tokens.add(value); 217 } 218 219 /** 220 * <p> 221 * If an {@link Option} exists for {@code token} then add the token to the processed list. 222 * </p> 223 * 224 * <p> 225 * If an {@link Option} does not exist and {@code stopAtNonOption} is set then add the remaining tokens to the 226 * processed tokens list directly. 227 * </p> 228 * 229 * @param token The current option token 230 * @param stopAtNonOption Specifies whether flattening should halt at the first non option. 231 */ 232 private void processOptionToken(final String token, final boolean stopAtNonOption) { 233 if (stopAtNonOption && !options.hasOption(token)) { 234 eatTheRest = true; 235 } 236 237 if (options.hasOption(token)) { 238 currentOption = options.getOption(token); 239 } 240 241 tokens.add(token); 242 } 243}