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