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    package org.apache.commons.cli2.commandline;
018    
019    import java.util.LinkedList;
020    import java.util.List;
021    import java.util.ListIterator;
022    
023    import org.apache.commons.cli2.CommandLine;
024    import org.apache.commons.cli2.Group;
025    import org.apache.commons.cli2.Option;
026    import org.apache.commons.cli2.OptionException;
027    import org.apache.commons.cli2.WriteableCommandLine;
028    import org.apache.commons.cli2.resource.ResourceConstants;
029    import 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     */
038    public 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    }