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