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