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 org.apache.commons.cli2.builder;
018
019import java.util.Iterator;
020import java.util.LinkedHashSet;
021import java.util.Set;
022
023import org.apache.commons.cli2.Argument;
024import org.apache.commons.cli2.Option;
025import org.apache.commons.cli2.validation.ClassValidator;
026import org.apache.commons.cli2.validation.DateValidator;
027import org.apache.commons.cli2.validation.FileValidator;
028import org.apache.commons.cli2.validation.NumberValidator;
029import org.apache.commons.cli2.validation.UrlValidator;
030import org.apache.commons.cli2.validation.Validator;
031
032/**
033 * Builds Options using a String pattern
034 */
035//TODO Document and link to the acceptable patterns
036public class PatternBuilder {
037
038    private final GroupBuilder gbuilder;
039    private final DefaultOptionBuilder obuilder;
040    private final ArgumentBuilder abuilder;
041
042    /**
043     * Creates a new PatternBuilder
044     */
045    public PatternBuilder() {
046        this(
047            new GroupBuilder(),
048            new DefaultOptionBuilder(),
049            new ArgumentBuilder());
050    }
051
052    /**
053     * Creates a new PatternBuilder
054     * @param gbuilder the GroupBuilder to use
055     * @param obuilder the DefaultOptionBuilder to use
056     * @param abuilder the ArgumentBuilder to use
057     */
058    public PatternBuilder(
059        final GroupBuilder gbuilder,
060        final DefaultOptionBuilder obuilder,
061        final ArgumentBuilder abuilder) {
062        this.gbuilder = gbuilder;
063        this.obuilder = obuilder;
064        this.abuilder = abuilder;
065    }
066
067    private final Set options = new LinkedHashSet();
068
069    /**
070     * Creates a new Option instance.
071     * @return a new Option instance
072     */
073    public Option create() {
074        final Option option;
075
076        if (options.size() == 1) {
077            option = (Option)options.iterator().next();
078        }
079        else {
080            gbuilder.reset();
081            for (final Iterator i = options.iterator(); i.hasNext();) {
082                gbuilder.withOption((Option)i.next());
083            }
084            option = gbuilder.create();
085        }
086
087        reset();
088
089        return option;
090    }
091
092    /**
093     * Resets this builder.
094     * @return this builder
095     */
096    public PatternBuilder reset() {
097        options.clear();
098        return this;
099    }
100
101    private void createOption(
102        final char type,
103        final boolean required,
104        final char opt) {
105        final Argument argument;
106        if (type != ' ') {
107            abuilder.reset();
108            abuilder.withValidator(validator(type));
109            if (required) {
110                abuilder.withMinimum(1);
111            }
112            if (type != '*') {
113                abuilder.withMaximum(1);
114            }
115            argument = abuilder.create();
116        }
117        else {
118            argument = null;
119        }
120
121        obuilder.reset();
122        obuilder.withArgument(argument);
123        obuilder.withShortName(String.valueOf(opt));
124        obuilder.withRequired(required);
125
126        options.add(obuilder.create());
127    }
128
129    /**
130     * Builds an Option using a pattern string.
131     * @param pattern the pattern to build from
132     */
133    public void withPattern(final String pattern) {
134        int sz = pattern.length();
135
136        char opt = ' ';
137        char ch = ' ';
138        char type = ' ';
139        boolean required = false;
140
141        for (int i = 0; i < sz; i++) {
142            ch = pattern.charAt(i);
143
144            switch (ch) {
145                case '!' :
146                    required = true;
147                    break;
148                case '@' :
149                case ':' :
150                case '%' :
151                case '+' :
152                case '#' :
153                case '<' :
154                case '>' :
155                case '*' :
156                case '/' :
157                    type = ch;
158                    break;
159                default :
160                    if (opt != ' ') {
161                        createOption(type, required, opt);
162                        required = false;
163                        type = ' ';
164                    }
165
166                    opt = ch;
167            }
168        }
169
170        if (opt != ' ') {
171            createOption(type, required, opt);
172        }
173    }
174
175    private static Validator validator(final char c) {
176        switch (c) {
177            case '@' :
178                final ClassValidator classv = new ClassValidator();
179                classv.setInstance(true);
180                return classv;
181            case '+' :
182                final ClassValidator instancev = new ClassValidator();
183                return instancev;
184                //case ':':// no validator needed for a string
185            case '%' :
186                return NumberValidator.getNumberInstance();
187            case '#' :
188                return DateValidator.getDateInstance();
189            case '<' :
190                final FileValidator existingv = new FileValidator();
191                existingv.setExisting(true);
192                existingv.setFile(true);
193                return existingv;
194            case '>' :
195            case '*' :
196                return new FileValidator();
197            case '/' :
198                return new UrlValidator();
199            default :
200                return null;
201        }
202    }
203}