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.commandline;
018
019import java.util.LinkedList;
020import java.util.List;
021import java.util.ListIterator;
022
023import org.apache.commons.cli2.CommandLine;
024import org.apache.commons.cli2.Group;
025import org.apache.commons.cli2.Option;
026import org.apache.commons.cli2.OptionException;
027import org.apache.commons.cli2.WriteableCommandLine;
028import org.apache.commons.cli2.resource.ResourceConstants;
029import org.apache.commons.cli2.util.HelpFormatter;
030
031/**
032 * A class that implements the <code>Parser</code> interface can parse a
033 * String array according to the {@link Group}specified and return a
034 * {@link CommandLine}.
035 *
036 * @author John Keyes (john at integralsource.com)
037 */
038public class Parser {
039    private HelpFormatter helpFormatter = new HelpFormatter();
040    private Option helpOption = null;
041    private String helpTrigger = null;
042    private Group group = null;
043
044    /**
045     * Parse the arguments according to the specified options and properties.
046     *
047     * @param arguments
048     *            the command line arguments
049     *
050     * @return the list of atomic option and value tokens
051     * @throws OptionException
052     *             if there are any problems encountered while parsing the
053     *             command line tokens.
054     */
055    public CommandLine parse(final String[] arguments)
056        throws OptionException {
057        // build a mutable list for the arguments
058        final List argumentList = new LinkedList();
059
060        // copy the arguments into the new list
061        for (int i = 0; i < arguments.length; i++) {
062            final String argument = arguments[i];
063
064            // ensure non intern'd strings are used
065            // so that == comparisons work as expected
066            argumentList.add(new String(argument));
067        }
068
069        // wet up a command line for this group
070        final WriteableCommandLine commandLine = new WriteableCommandLineImpl(group, argumentList);
071
072        // pick up any defaults from the model
073        group.defaults(commandLine);
074
075        // process the options as far as possible
076        final ListIterator iterator = argumentList.listIterator();
077        Object previous = null;
078
079        while (group.canProcess(commandLine, iterator)) {
080            // peek at the next item and backtrack
081            final Object next = iterator.next();
082            iterator.previous();
083
084            // if we have just tried to process this instance
085            if (next == previous) {
086                // abort
087                break;
088            }
089
090            // remember previous
091            previous = next;
092
093            group.process(commandLine, iterator);
094        }
095
096        // if there are more arguments we have a problem
097        if (iterator.hasNext()) {
098            final String arg = (String) iterator.next();
099            throw new OptionException(group, ResourceConstants.UNEXPECTED_TOKEN, arg);
100        }
101
102        // no need to validate if the help option is present
103        if (!commandLine.hasOption(helpOption) && !commandLine.hasOption(helpTrigger)) {
104            group.validate(commandLine);
105        }
106
107        return commandLine;
108    }
109
110    /**
111     * Parse the arguments according to the specified options and properties and
112     * displays the usage screen if the CommandLine is not valid or the help
113     * option was specified.
114     *
115     * @param arguments the command line arguments
116     * @return a valid CommandLine or null if the parse was unsuccessful
117     */
118    public CommandLine parseAndHelp(final String[] arguments) {
119        helpFormatter.setGroup(group);
120
121        try {
122            // attempt to parse the command line
123            final CommandLine commandLine = parse(arguments);
124
125            if (!commandLine.hasOption(helpOption) && !commandLine.hasOption(helpTrigger)) {
126                return commandLine;
127            }
128        } catch (final OptionException oe) {
129            // display help regarding the exception
130            helpFormatter.setException(oe);
131        }
132
133        // print help
134        helpFormatter.print();
135
136        return null;
137    }
138
139    /**
140     * Sets the Group of options to parse against
141     * @param group the group of options to parse against
142     */
143    public void setGroup(final Group group) {
144        this.group = group;
145    }
146
147    /**
148     * Sets the HelpFormatter to use with the simplified parsing.
149     * @see #parseAndHelp(String[])
150     * @param helpFormatter the HelpFormatter to use with the simplified parsing
151     */
152    public void setHelpFormatter(final HelpFormatter helpFormatter) {
153        this.helpFormatter = helpFormatter;
154    }
155
156    /**
157     * Sets the help option to use with the simplified parsing.  For example
158     * <code>--help</code>, <code>-h</code> and <code>-?</code> are often used.
159     * @see #parseAndHelp(String[])
160     * @param helpOption the help Option
161     */
162    public void setHelpOption(final Option helpOption) {
163        this.helpOption = helpOption;
164    }
165
166    /**
167     * Sets the help option to use with the simplified parsing.  For example
168     * <code>--help</code>, <code>-h</code> and <code>-?</code> are often used.
169     * @see #parseAndHelp(String[])
170     * @param helpTrigger the trigger of the help Option
171     */
172    public void setHelpTrigger(final String helpTrigger) {
173        this.helpTrigger = helpTrigger;
174    }
175}