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   * <p>Allows Options to be created from a single String.
27   * The pattern contains various single character flags and via
28   * an optional punctuation character, their expected type.
29   * </p>
30   * 
31   * <table border="1">
32   *   <caption>Overview of PatternOptionBuilder patterns</caption>
33   *   <tr><td>a</td><td>-a flag</td></tr>
34   *   <tr><td>b@</td><td>-b [classname]</td></tr>
35   *   <tr><td>c&gt;</td><td>-c [filename]</td></tr>
36   *   <tr><td>d+</td><td>-d [classname] (creates object via empty constructor)</td></tr>
37   *   <tr><td>e%</td><td>-e [number] (creates Double/Long instance depending on existing of a '.')</td></tr>
38   *   <tr><td>f/</td><td>-f [url]</td></tr>
39   *   <tr><td>g:</td><td>-g [string]</td></tr>
40   * </table>
41   * 
42   * <p>
43   * For example, the following allows command line flags of '-v -p string-value -f /dir/file'.
44   * The exclamation mark precede a mandatory option.
45   * </p>
46   *
47   * <pre>
48   *     Options options = PatternOptionBuilder.parsePattern("vp:!f/");
49   * </pre>
50   *
51   * <p>
52   * TODO: These need to break out to OptionType and also to be pluggable.
53   * </p>
54   *
55   * @version $Id: PatternOptionBuilder.java 1677406 2015-05-03 14:27:31Z britter $
56   */
57  public class PatternOptionBuilder
58  {
59      /** String class */
60      public static final Class<String> STRING_VALUE = String.class;
61  
62      /** Object class */
63      public static final Class<Object> OBJECT_VALUE = Object.class;
64  
65      /** Number class */
66      public static final Class<Number> NUMBER_VALUE = Number.class;
67  
68      /** Date class */
69      public static final Class<Date> DATE_VALUE = Date.class;
70  
71      /** Class class */
72      public static final Class<?> CLASS_VALUE = Class.class;
73  
74      /// can we do this one??
75      // is meant to check that the file exists, else it errors.
76      // ie) it's for reading not writing.
77  
78      /** FileInputStream class */
79      public static final Class<FileInputStream> EXISTING_FILE_VALUE = FileInputStream.class;
80  
81      /** File class */
82      public static final Class<File> FILE_VALUE = File.class;
83  
84      /** File array class */
85      public static final Class<File[]> FILES_VALUE = File[].class;
86  
87      /** URL class */
88      public static final Class<URL> URL_VALUE = URL.class;
89  
90      /**
91       * Retrieve the class that <code>ch</code> represents.
92       *
93       * @param ch the specified character
94       * @return The class that <code>ch</code> represents
95       */
96      public static Object getValueClass(char ch)
97      {
98          switch (ch)
99          {
100             case '@':
101                 return PatternOptionBuilder.OBJECT_VALUE;
102             case ':':
103                 return PatternOptionBuilder.STRING_VALUE;
104             case '%':
105                 return PatternOptionBuilder.NUMBER_VALUE;
106             case '+':
107                 return PatternOptionBuilder.CLASS_VALUE;
108             case '#':
109                 return PatternOptionBuilder.DATE_VALUE;
110             case '<':
111                 return PatternOptionBuilder.EXISTING_FILE_VALUE;
112             case '>':
113                 return PatternOptionBuilder.FILE_VALUE;
114             case '*':
115                 return PatternOptionBuilder.FILES_VALUE;
116             case '/':
117                 return PatternOptionBuilder.URL_VALUE;
118         }
119 
120         return null;
121     }
122 
123     /**
124      * Returns whether <code>ch</code> is a value code, i.e.
125      * whether it represents a class in a pattern.
126      *
127      * @param ch the specified character
128      * @return true if <code>ch</code> is a value code, otherwise false.
129      */
130     public static boolean isValueCode(char ch)
131     {
132         return ch == '@'
133                 || ch == ':'
134                 || ch == '%'
135                 || ch == '+'
136                 || ch == '#'
137                 || ch == '<'
138                 || ch == '>'
139                 || ch == '*'
140                 || ch == '/'
141                 || ch == '!';
142     }
143 
144     /**
145      * Returns the {@link Options} instance represented by <code>pattern</code>.
146      *
147      * @param pattern the pattern string
148      * @return The {@link Options} instance
149      */
150     public static Options parsePattern(String pattern)
151     {
152         char opt = ' ';
153         boolean required = false;
154         Class<?> type = null;
155 
156         Options options = new Options();
157 
158         for (int i = 0; i < pattern.length(); i++)
159         {
160             char ch = pattern.charAt(i);
161 
162             // a value code comes after an option and specifies
163             // details about it
164             if (!isValueCode(ch))
165             {
166                 if (opt != ' ')
167                 {
168                     final Option option = Option.builder(String.valueOf(opt))
169                         .hasArg(type != null)
170                         .required(required)
171                         .type(type)
172                         .build();
173                     
174                     // we have a previous one to deal with
175                     options.addOption(option);
176                     required = false;
177                     type = null;
178                     opt = ' ';
179                 }
180 
181                 opt = ch;
182             }
183             else if (ch == '!')
184             {
185                 required = true;
186             }
187             else
188             {
189                 type = (Class<?>) getValueClass(ch);
190             }
191         }
192 
193         if (opt != ' ')
194         {
195             final Option option = Option.builder(String.valueOf(opt))
196                 .hasArg(type != null)
197                 .required(required)
198                 .type(type)
199                 .build();
200             
201             // we have a final one to deal with
202             options.addOption(option);
203         }
204 
205         return options;
206     }
207 }