View Javadoc
1   /**************************************************************************
2    *
3    * Copyright (c) 2015-2019 Yawg project contributors.
4    *
5    **************************************************************************/
6   
7   package com.varmateo.yawg.cli;
8   
9   import java.nio.file.InvalidPathException;
10  import java.nio.file.Path;
11  import java.nio.file.Paths;
12  
13  import io.vavr.collection.Array;
14  import io.vavr.collection.Seq;
15  import io.vavr.collection.Set;
16  import io.vavr.control.Option;
17  
18  import org.apache.commons.cli.CommandLine;
19  import org.apache.commons.cli.CommandLineParser;
20  import org.apache.commons.cli.GnuParser;
21  import org.apache.commons.cli.MissingArgumentException;
22  import org.apache.commons.cli.MissingOptionException;
23  import org.apache.commons.cli.Options;
24  import org.apache.commons.cli.ParseException;
25  import org.apache.commons.cli.UnrecognizedOptionException;
26  
27  
28  /**
29   * Represents the set of options supported in the command line.
30   */
31  /* default */ final class CliParameterSet {
32  
33  
34      private static final String FLAG_TRUE  = "true";
35  
36      private final Set<CliParameter> _allOptions;
37      private final CommandLine _cmdLine;
38  
39  
40      /**
41       * Only used internally.
42       */
43      private CliParameterSet(
44              final Set<CliParameter> allOptions,
45              final CommandLine cmdLine) {
46  
47          _allOptions = allOptions;
48          _cmdLine = cmdLine;
49      }
50  
51  
52      /**
53       *
54       */
55      public static CliParameterSet parse(
56              final Set<CliParameter> options,
57              final String[] args)
58              throws CliException {
59  
60          Options apacheOptions = buildApacheOptions(options);
61          CommandLineParser cliParser = new GnuParser();
62          final CommandLine cmdLine;
63  
64          try {
65              cmdLine = cliParser.parse(apacheOptions, args, false);
66          } catch ( ParseException e ) {
67              throw raiseCliException(apacheOptions, e);
68          }
69  
70          @SuppressWarnings("unchecked")
71          java.util.List<String> argList = cmdLine.getArgList();
72          if ( (argList!=null) && !argList.isEmpty() ) {
73              // There were arguments that were not options. We require
74              // all arguments to be options, thus this is an invalid
75              // command line.
76              throw CliException.unknownOption(argList.get(0));
77          }
78  
79          return new CliParameterSet(options, cmdLine);
80      }
81  
82  
83      /**
84       *
85       */
86      private static Options buildApacheOptions(final Set<CliParameter> options) {
87  
88          return options
89                  .map(CliParameter::apacheOption)
90                  .foldLeft(
91                          new Options(),
92                          (xs, x) -> xs.addOption(x));
93      }
94  
95  
96      /**
97       *
98       */
99      private static CliException raiseCliException(
100             final Options options,
101             final ParseException e)
102             throws CliException {
103 
104         final CliException cause;
105 
106         if ( e instanceof MissingOptionException ) {
107             final MissingOptionException ex = (MissingOptionException)e;
108             final String shortOpt = (String)ex.getMissingOptions().get(0);
109             final org.apache.commons.cli.Option option = options.getOption(shortOpt);
110             final String optionName = getApacheOptionName(option);
111             cause = CliException.missingOption(optionName);
112         } else if ( e instanceof UnrecognizedOptionException ) {
113             final UnrecognizedOptionException ex = (UnrecognizedOptionException)e;
114             final String optionName = ex.getOption();
115             cause = CliException.unknownOption(optionName);
116         } else if ( e instanceof MissingArgumentException ) {
117             final MissingArgumentException ex = (MissingArgumentException)e;
118             final String optionName = getApacheOptionName(ex.getOption());
119             cause = CliException.missingOptionArg(optionName);
120         } else {
121             cause = CliException.optionParseFailure(e);
122         }
123 
124         throw cause;
125     }
126 
127 
128     /**
129      *
130      */
131     private static String getApacheOptionName(
132             final org.apache.commons.cli.Option apacheOption) {
133 
134         final String result;
135 
136         String longName = apacheOption.getLongOpt();
137         if ( longName != null ) {
138             result = "--" + longName;
139         } else {
140             String shortName = apacheOption.getOpt();
141             result = "-" + shortName;
142         }
143 
144         return result;
145     }
146 
147 
148     /**
149      *
150      */
151     public Set<CliParameter> supportedOptions() {
152 
153         return _allOptions;
154     }
155 
156 
157     /**
158      *
159      */
160     public boolean hasOption(final CliParameter option) {
161 
162         final String optionName = option.name();
163 
164         return _cmdLine.hasOption(optionName);
165     }
166 
167 
168     /**
169      * Retrieves all the values for an option.
170      *
171      * <p>An option will have multiple values when it is given more
172      * than onde in the list of command line arguments.</p>
173      *
174      * @param option The name of the option whose values are to be
175      * returned.
176      *
177      * @return A list with the values of the given option, in the same
178      * order theay appeared in the command line. It will be empty if
179      * the option was not given in the command line.
180      */
181     public Seq<String> getAll(final CliParameter option) {
182 
183         final String   optionName   = option.name();
184         final String[] optionValues = _cmdLine.getOptionValues(optionName);
185 
186         return Option.of(optionValues)
187                 .map(Array::of)
188                 .getOrElse(Array::of);
189     }
190 
191 
192     /**
193      * Retrieves the value of a mandatory option.
194      *
195      * <p>If the given option does not exist, then a
196      * <code>CliException</code> will be thrown.
197      *
198      * @param option The name (short or long) of the option whose
199      * value is to be retrieved.
200      *
201      * @return The value of the given option.
202      *
203      * @exception CliException Thrown if the given command line does
204      * not contain the option.
205      */
206     public String get(final CliParameter option)
207             throws CliException {
208 
209         final String optionValue;
210         final String optionName = option.name();
211         final String[] optionValues = _cmdLine.getOptionValues(optionName);
212 
213         if ( optionValues != null ) {
214             final int indexOfLast = optionValues.length-1;
215             optionValue = optionValues[indexOfLast];
216         } else {
217             throw CliException.missingOption(option.literal());
218         }
219 
220         return optionValue;
221     }
222 
223 
224     /**
225      * Retrieves the value of a conditional option.
226      *
227      * @param option The name (short or long) of the option whose
228      * value is to be retrieved.
229      *
230      * @param defaultValue The value to be returned if the option is
231      * not present.
232      */
233     public String get(
234             final CliParameter option,
235             final String defaultValue) {
236 
237         final String optionValue;
238         final String optionName = option.name();
239         final String[] optionValues = _cmdLine.getOptionValues(optionName);
240 
241         if ( optionValues != null ) {
242             final int indexOfLast = optionValues.length-1;
243             optionValue = optionValues[indexOfLast];
244         } else {
245             optionValue = defaultValue;
246         }
247 
248         return optionValue;
249     }
250 
251 
252     /**
253      * Retrieves the value of a mandatory option as a path.
254      */
255     public Path getPath(final CliParameter option)
256             throws CliException {
257 
258         String pathStr = get(option);
259 
260         return stringToPath(option, pathStr);
261     }
262 
263 
264     /**
265      *
266      */
267     public Path getPath(
268             final CliParameter option,
269             final Path defaultValue)
270             throws CliException {
271 
272         String pathStr = get(option, null);
273 
274         return pathStr == null
275                 ? defaultValue
276                 : stringToPath(option, pathStr);
277     }
278 
279 
280     /**
281      *
282      */
283     private Path stringToPath(
284             final CliParameter option,
285             final String pathStr)
286             throws CliException {
287 
288         try {
289             return Paths.get(pathStr);
290         } catch ( InvalidPathException e ) {
291             throw CliException.invalidPath(option.literal(), pathStr);
292         }
293     }
294 
295 
296     /**
297      * Checks the given option represents a "true" valued flag.
298      */
299     public boolean isTrue(final CliParameter option) {
300 
301         boolean result = false;
302 
303         if ( hasOption(option) ) {
304             String value = get(option, FLAG_TRUE);
305 
306             result = FLAG_TRUE.equals(value);
307         }
308 
309         return result;
310     }
311 
312 
313 }