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