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 */ 017package Apache.CLI; 018 019import java.io.Serializable; 020import java.util.ArrayList; 021import java.util.Collection; 022import java.util.Collections; 023import java.util.HashSet; 024import java.util.LinkedHashMap; 025import java.util.List; 026import java.util.Map; 027 028/** 029 * Main entry-point into the library. 030 * 031 * <BR /><BR />Options represents a collection of {@link Option} objects, which describe the 032 * possible options for a command-line. 033 * 034 * <BR /><BR />It may flexibly parse long and short options, with or without values. Additionally, 035 * it may parse only a portion of a commandline, allowing for flexible multi-stage parsing. 036 * 037 * @see Apache.CLI.CommandLine 038 */ 039@Torello.JavaDoc.JDHeaderBackgroundImg(EmbedTagFileID="LICENSE") 040public class Options implements Serializable 041{ 042 // The serial version UID. 043 private static final long serialVersionUID = 1L; 044 045 // A map of the options with the character key 046 private final Map<String, Option> shortOpts = new LinkedHashMap<>(); 047 048 // A map of the options with the long key 049 private final Map<String, Option> longOpts = new LinkedHashMap<>(); 050 051 // A map of the required options 052 // N.B. This can contain either a String (addOption) or an OptionGroup (addOptionGroup) 053 // TODO this seems wrong 054 055 private final List<Object> requiredOpts = new ArrayList<>(); 056 057 // A map of the option groups 058 private final Map<String, OptionGroup> optionGroups = new LinkedHashMap<>(); 059 060 /** 061 * Adds an option instance 062 * @param opt the option that is to be added 063 * @return the resulting Options instance 064 */ 065 public Options addOption(final Option opt) 066 { 067 final String key = opt.getKey(); 068 069 // add it to the long option list 070 if (opt.hasLongOpt()) longOpts.put(opt.getLongOpt(), opt); 071 072 // if the option is required add it to the required list 073 if (opt.isRequired()) 074 { 075 if (requiredOpts.contains(key)) requiredOpts.remove(requiredOpts.indexOf(key)); 076 requiredOpts.add(key); 077 } 078 079 shortOpts.put(key, opt); 080 081 return this; 082 } 083 084 /** 085 * Add an option that only contains a short-name. 086 * 087 * <BR /><BR />It may be specified as requiring an argument. 088 * 089 * @param opt Short single-character name of the option. 090 * @param hasArg flag signalling if an argument is required after this option 091 * @param description Self-documenting description 092 * @return the resulting Options instance 093 */ 094 public Options addOption(final String opt, final boolean hasArg, final String description) 095 { 096 addOption(opt, null, hasArg, description); 097 return this; 098 } 099 100 /** 101 * Add an option that only contains a short name. 102 * 103 * <BR /><BR />The option does not take an argument. 104 * 105 * @param opt Short single-character name of the option. 106 * @param description Self-documenting description 107 * @return the resulting Options instance 108 */ 109 public Options addOption(final String opt, final String description) 110 { 111 addOption(opt, null, false, description); 112 return this; 113 } 114 115 /** 116 * Add an option that contains a short-name and a long-name. 117 * 118 * <BR /><BR />It may be specified as requiring an argument. 119 * 120 * @param opt Short single-character name of the option. 121 * @param longOpt Long multi-character name of the option. 122 * @param hasArg flag signalling if an argument is required after this option 123 * @param description Self-documenting description 124 * @return the resulting Options instance 125 */ 126 public Options addOption 127 (final String opt, final String longOpt, final boolean hasArg, final String description) 128 { 129 addOption(new Option(opt, longOpt, hasArg, description)); 130 return this; 131 } 132 133 /** 134 * Add the specified option group. 135 * @param group the OptionGroup that is to be added 136 * @return the resulting Options instance 137 */ 138 public Options addOptionGroup(final OptionGroup group) 139 { 140 if (group.isRequired()) requiredOpts.add(group); 141 142 for (final Option option : group.getOptions()) 143 { 144 // an Option cannot be required if it is in an OptionGroup, either the group is 145 // required or nothing is required 146 147 option.setRequired(false); 148 addOption(option); 149 150 optionGroups.put(option.getKey(), group); 151 } 152 153 return this; 154 } 155 156 /** 157 * Add an option that contains a short-name and a long-name. 158 * 159 * <BR /><BR />The added option is set as required. It may be specified as requiring an 160 * argument. This method is a shortcut for: 161 * 162 * <DIV CLASS=EXAMPLE>{@code 163 * Options option = new Option(opt, longOpt, hasArg, description); 164 * option.setRequired(true); 165 * options.add(option); 166 * }</DIV> 167 * 168 * @param opt Short single-character name of the option. 169 * @param longOpt Long multi-character name of the option. 170 * @param hasArg flag signalling if an argument is required after this option 171 * @param description Self-documenting description 172 * @return the resulting Options instance 173 */ 174 public Options addRequiredOption( 175 final String opt, 176 final String longOpt, 177 final boolean hasArg, 178 final String description 179 ) 180 { 181 final Option option = new Option(opt, longOpt, hasArg, description); 182 option.setRequired(true); 183 addOption(option); 184 return this; 185 } 186 187 /** 188 * Gets the options with a long name starting with the name specified. 189 * @param opt the partial name of the option 190 * @return the options matching the partial name specified, or an empty list if none matches 191 */ 192 public List<String> getMatchingOptions(String opt) 193 { 194 opt = Util.stripLeadingHyphens(opt); 195 196 final List<String> matchingOpts = new ArrayList<>(); 197 198 // for a perfect match return the single option only 199 if (longOpts.containsKey(opt)) return Collections.singletonList(opt); 200 201 for (final String longOpt : longOpts.keySet()) 202 if (longOpt.startsWith(opt)) 203 matchingOpts.add(longOpt); 204 205 return matchingOpts; 206 } 207 208 /** 209 * Gets the {@link Option} matching the long or short name specified. 210 * 211 * <BR /><BR />The leading hyphens in the name are ignored (up to 2). 212 * 213 * @param opt short or long name of the {@link Option} 214 * @return the option represented by opt 215 */ 216 public Option getOption(String opt) 217 { 218 opt = Util.stripLeadingHyphens(opt); 219 220 final Option option = shortOpts.get(opt); 221 222 return option != null ? option : longOpts.get(opt); 223 } 224 225 /** 226 * Gets the OptionGroup the {@code opt} belongs to. 227 * @param opt the option whose OptionGroup is being queried. 228 * @return the OptionGroup if {@code opt} is part of an OptionGroup, otherwise return null 229 */ 230 public OptionGroup getOptionGroup(final Option opt) 231 { return optionGroups.get(opt.getKey()); } 232 233 /** 234 * Gets the OptionGroups that are members of this Options instance. 235 * @return a Collection of OptionGroup instances. 236 */ 237 Collection<OptionGroup> getOptionGroups() 238 { return new HashSet<>(optionGroups.values()); } 239 240 /** 241 * Gets a read-only list of options in this set 242 * @return read-only Collection of {@link Option} objects in this descriptor 243 */ 244 public Collection<Option> getOptions() 245 { return Collections.unmodifiableCollection(helpOptions()); } 246 247 /** 248 * Gets the required options. 249 * @return read-only List of required options 250 */ 251 @SuppressWarnings("rawtypes") 252 public List getRequiredOptions() 253 { return Collections.unmodifiableList(requiredOpts); } 254 255 /** 256 * Returns whether the named {@link Option} is a member of this {@link Options}. 257 * @param opt long name of the {@link Option} 258 * @return true if the named {@link Option} is a member of this {@link Options} 259 */ 260 public boolean hasLongOption(String opt) 261 { 262 opt = Util.stripLeadingHyphens(opt); 263 return longOpts.containsKey(opt); 264 } 265 266 /** 267 * Returns whether the named {@link Option} is a member of this {@link Options}. 268 * @param opt short or long name of the {@link Option} 269 * @return true if the named {@link Option} is a member of this {@link Options} 270 */ 271 public boolean hasOption(String opt) 272 { 273 opt = Util.stripLeadingHyphens(opt); 274 return shortOpts.containsKey(opt) || longOpts.containsKey(opt); 275 } 276 277 /** 278 * Returns whether the named {@link Option} is a member of this {@link Options}. 279 * @param opt short name of the {@link Option} 280 * @return true if the named {@link Option} is a member of this {@link Options} 281 */ 282 public boolean hasShortOption(String opt) 283 { 284 opt = Util.stripLeadingHyphens(opt); 285 return shortOpts.containsKey(opt); 286 } 287 288 /** 289 * Returns the Options for use by the HelpFormatter. 290 * @return the List of Options 291 */ 292 List<Option> helpOptions() 293 { return new ArrayList<>(shortOpts.values()); } 294 295 /** 296 * Dump state, suitable for debugging. 297 * @return Stringified form of this object 298 */ 299 @Override 300 public String toString() 301 { 302 final StringBuilder buf = new StringBuilder(); 303 304 buf.append("[ Options: [ short "); 305 buf.append(shortOpts.toString()); 306 buf.append(" ] [ long "); 307 buf.append(longOpts); 308 buf.append(" ]"); 309 310 return buf.toString(); 311 } 312}