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    
018    package org.apache.commons.cli;
019    
020    import java.io.File;
021    import java.io.FileInputStream;
022    import java.net.URL;
023    import 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     */
055    public 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    }