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 */
017
018package org.apache.commons.cli;
019
020import java.io.File;
021import java.io.FileInputStream;
022import java.net.URL;
023import java.util.Date;
024
025/**
026 * <p>
027 * Allows Options to be created from a single String.
028 * The pattern contains various single character flags and via
029 * an optional punctuation character, their expected type.
030 * </p>
031 *
032 * <table border="1">
033 * <tr><td>a</td><td>-a flag</td></tr>
034 * <tr><td>b@</td><td>-b [classname]</td></tr>
035 * <tr><td>c&gt;</td><td>-c [filename]</td></tr>
036 * <tr><td>d+</td><td>-d [classname] (creates object via empty contructor)</td></tr>
037 * <tr><td>e%</td><td>-e [number] (creates Double/Long instance depeding on existing of a '.')</td></tr>
038 * <tr><td>f/</td><td>-f [url]</td></tr>
039 * <tr><td>g:</td><td>-g [string]</td></tr>
040 * </table>
041 *
042 * <p>
043 * For example, the following allows command line flags of '-v -p string-value -f /dir/file'.
044 * The exclamation mark precede a mandatory option.
045 * </p>
046 * <code>Options options = PatternOptionBuilder.parsePattern("vp:!f/");</code>
047 *
048 * <p>
049 * TODO These need to break out to OptionType and also
050 * to be pluggable.
051 * </p>
052 *
053 * @version $Revision: 734339 $, $Date: 2009-01-13 21:56:47 -0800 (Tue, 13 Jan 2009) $
054 */
055public class PatternOptionBuilder
056{
057    /** String class */
058    public static final Class STRING_VALUE = String.class;
059
060    /** Object class */
061    public static final Class OBJECT_VALUE = Object.class;
062
063    /** Number class */
064    public static final Class NUMBER_VALUE = Number.class;
065
066    /** Date class */
067    public static final Class DATE_VALUE = Date.class;
068
069    /** Class class */
070    public static final Class CLASS_VALUE = Class.class;
071
072    /// can we do this one??
073    // is meant to check that the file exists, else it errors.
074    // ie) it's for reading not writing.
075
076    /** FileInputStream class */
077    public static final Class EXISTING_FILE_VALUE = FileInputStream.class;
078
079    /** File class */
080    public static final Class FILE_VALUE = File.class;
081
082    /** File array class */
083    public static final Class FILES_VALUE = File[].class;
084
085    /** URL class */
086    public static final Class URL_VALUE = URL.class;
087
088    /**
089     * Retrieve the class that <code>ch</code> represents.
090     *
091     * @param ch the specified character
092     * @return The class that <code>ch</code> represents
093     */
094    public static Object getValueClass(char ch)
095    {
096        switch (ch)
097        {
098            case '@':
099                return PatternOptionBuilder.OBJECT_VALUE;
100            case ':':
101                return PatternOptionBuilder.STRING_VALUE;
102            case '%':
103                return PatternOptionBuilder.NUMBER_VALUE;
104            case '+':
105                return PatternOptionBuilder.CLASS_VALUE;
106            case '#':
107                return PatternOptionBuilder.DATE_VALUE;
108            case '<':
109                return PatternOptionBuilder.EXISTING_FILE_VALUE;
110            case '>':
111                return PatternOptionBuilder.FILE_VALUE;
112            case '*':
113                return PatternOptionBuilder.FILES_VALUE;
114            case '/':
115                return PatternOptionBuilder.URL_VALUE;
116        }
117
118        return null;
119    }
120
121    /**
122     * Returns whether <code>ch</code> is a value code, i.e.
123     * whether it represents a class in a pattern.
124     *
125     * @param ch the specified character
126     * @return true if <code>ch</code> is a value code, otherwise false.
127     */
128    public static boolean isValueCode(char ch)
129    {
130        return ch == '@'
131                || ch == ':'
132                || ch == '%'
133                || ch == '+'
134                || ch == '#'
135                || ch == '<'
136                || ch == '>'
137                || ch == '*'
138                || ch == '/'
139                || ch == '!';
140    }
141
142    /**
143     * Returns the {@link Options} instance represented by <code>pattern</code>.
144     *
145     * @param pattern the pattern string
146     * @return The {@link Options} instance
147     */
148    public static Options parsePattern(String pattern)
149    {
150        char opt = ' ';
151        boolean required = false;
152        Object type = null;
153
154        Options options = new Options();
155
156        for (int i = 0; i < pattern.length(); i++)
157        {
158            char ch = pattern.charAt(i);
159
160            // a value code comes after an option and specifies
161            // details about it
162            if (!isValueCode(ch))
163            {
164                if (opt != ' ')
165                {
166                    OptionBuilder.hasArg(type != null);
167                    OptionBuilder.isRequired(required);
168                    OptionBuilder.withType(type);
169
170                    // we have a previous one to deal with
171                    options.addOption(OptionBuilder.create(opt));
172                    required = false;
173                    type = null;
174                    opt = ' ';
175                }
176
177                opt = ch;
178            }
179            else if (ch == '!')
180            {
181                required = true;
182            }
183            else
184            {
185                type = getValueClass(ch);
186            }
187        }
188
189        if (opt != ' ')
190        {
191            OptionBuilder.hasArg(type != null);
192            OptionBuilder.isRequired(required);
193            OptionBuilder.withType(type);
194
195            // we have a final one to deal with
196            options.addOption(OptionBuilder.create(opt));
197        }
198
199        return options;
200    }
201}