View Javadoc
1   /*
2     Licensed to the Apache Software Foundation (ASF) under one or more
3     contributor license agreements.  See the NOTICE file distributed with
4     this work for additional information regarding copyright ownership.
5     The ASF licenses this file to You under the Apache License, Version 2.0
6     (the "License"); you may not use this file except in compliance with
7     the License.  You may obtain a copy of the License at
8   
9         http://www.apache.org/licenses/LICENSE-2.0
10  
11    Unless required by applicable law or agreed to in writing, software
12    distributed under the License is distributed on an "AS IS" BASIS,
13    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14    See the License for the specific language governing permissions and
15    limitations under the License.
16   */
17  
18  package org.apache.commons.cli;
19  
20  import java.io.File;
21  import java.io.FileInputStream;
22  import java.net.URL;
23  import java.util.Date;
24  
25  /**
26   * Allows Options to be created from a single String. The pattern contains various single character flags and via an
27   * optional punctuation character, their expected type.
28   *
29   * <table border="1">
30   * <caption>Overview of PatternOptionBuilder patterns</caption>
31   * <tr>
32   * <td>a</td>
33   * <td>-a flag</td>
34   * </tr>
35   * <tr>
36   * <td>b@</td>
37   * <td>-b [class name]</td>
38   * </tr>
39   * <tr>
40   * <td>c&gt;</td>
41   * <td>-c [file name]</td>
42   * </tr>
43   * <tr>
44   * <td>d+</td>
45   * <td>-d [class name] (creates object via empty constructor)</td>
46   * </tr>
47   * <tr>
48   * <td>e%</td>
49   * <td>-e [number] (creates Double/Long instance depending on existing of a '.')</td>
50   * </tr>
51   * <tr>
52   * <td>f/</td>
53   * <td>-f [URL]</td>
54   * </tr>
55   * <tr>
56   * <td>g:</td>
57   * <td>-g [string]</td>
58   * </tr>
59   * </table>
60   *
61   * <p>
62   * For example, the following allows command line flags of '-v -p string-value -f /dir/file'. The exclamation mark
63   * precede a mandatory option.
64   * </p>
65   *
66   * <pre>
67   * Options options = PatternOptionBuilder.parsePattern("vp:!f/");
68   * </pre>
69   *
70   * <p>
71   * TODO These need to break out to OptionType and also to be pluggable.
72   * </p>
73   */
74  public class PatternOptionBuilder {
75      /** String class */
76      public static final Class<String> STRING_VALUE = String.class;
77  
78      /** Object class */
79      public static final Class<Object> OBJECT_VALUE = Object.class;
80  
81      /** Number class */
82      public static final Class<Number> NUMBER_VALUE = Number.class;
83  
84      /** Date class */
85      public static final Class<Date> DATE_VALUE = Date.class;
86  
87      /** Class class */
88      public static final Class<?> CLASS_VALUE = Class.class;
89  
90      /// can we do this one??
91      // is meant to check that the file exists, else it errors.
92      // ie) it's for reading not writing.
93  
94      /** FileInputStream class */
95      public static final Class<FileInputStream> EXISTING_FILE_VALUE = FileInputStream.class;
96  
97      /** File class */
98      public static final Class<File> FILE_VALUE = File.class;
99  
100     /** File array class */
101     public static final Class<File[]> FILES_VALUE = File[].class;
102 
103     /** URL class */
104     public static final Class<URL> URL_VALUE = URL.class;
105 
106     /**
107      * Retrieve the class that {@code ch} represents.
108      *
109      * @param ch the specified character
110      * @return The class that {@code ch} represents
111      */
112     public static Object getValueClass(final char ch) {
113         switch (ch) {
114         case '@':
115             return PatternOptionBuilder.OBJECT_VALUE;
116         case ':':
117             return PatternOptionBuilder.STRING_VALUE;
118         case '%':
119             return PatternOptionBuilder.NUMBER_VALUE;
120         case '+':
121             return PatternOptionBuilder.CLASS_VALUE;
122         case '#':
123             return PatternOptionBuilder.DATE_VALUE;
124         case '<':
125             return PatternOptionBuilder.EXISTING_FILE_VALUE;
126         case '>':
127             return PatternOptionBuilder.FILE_VALUE;
128         case '*':
129             return PatternOptionBuilder.FILES_VALUE;
130         case '/':
131             return PatternOptionBuilder.URL_VALUE;
132         }
133 
134         return null;
135     }
136 
137     /**
138      * Returns whether {@code ch} is a value code, i.e. whether it represents a class in a pattern.
139      *
140      * @param ch the specified character
141      * @return true if {@code ch} is a value code, otherwise false.
142      */
143     public static boolean isValueCode(final char ch) {
144         return ch == '@' || ch == ':' || ch == '%' || ch == '+' || ch == '#' || ch == '<' || ch == '>' || ch == '*' || ch == '/' || ch == '!';
145     }
146 
147     /**
148      * Returns the {@link Options} instance represented by {@code pattern}.
149      *
150      * @param pattern the pattern string
151      * @return The {@link Options} instance
152      */
153     public static Options parsePattern(final String pattern) {
154         char opt = ' ';
155         boolean required = false;
156         Class<?> type = null;
157 
158         final Options options = new Options();
159 
160         for (int i = 0; i < pattern.length(); i++) {
161             final char ch = pattern.charAt(i);
162 
163             // a value code comes after an option and specifies
164             // details about it
165             if (!isValueCode(ch)) {
166                 if (opt != ' ') {
167                     final Option option = Option.builder(String.valueOf(opt)).hasArg(type != null).required(required).type(type).build();
168 
169                     // we have a previous one to deal with
170                     options.addOption(option);
171                     required = false;
172                     type = null;
173                 }
174 
175                 opt = ch;
176             } else if (ch == '!') {
177                 required = true;
178             } else {
179                 type = (Class<?>) getValueClass(ch);
180             }
181         }
182 
183         if (opt != ' ') {
184             final Option option = Option.builder(String.valueOf(opt)).hasArg(type != null).required(required).type(type).build();
185 
186             // we have a final one to deal with
187             options.addOption(option);
188         }
189 
190         return options;
191     }
192 }