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 */
017package net.dpml.cli.option;
018
019import java.util.Iterator;
020import java.util.ListIterator;
021import java.util.Set;
022
023import net.dpml.cli.DisplaySetting;
024import net.dpml.cli.Option;
025import net.dpml.cli.WriteableCommandLine;
026import net.dpml.cli.resource.ResourceConstants;
027import net.dpml.cli.resource.ResourceHelper;
028
029/**
030 * A base implementation of Option providing limited ground work for further
031 * Option implementations.
032 * @author <a href="@PUBLISHER-URL@">@PUBLISHER-NAME@</a>
033 * @version @PROJECT-VERSION@
034 */
035public abstract class OptionImpl implements Option 
036{
037    private final int m_id;
038    private final boolean m_required;
039
040    /**
041     * Creates an OptionImpl with the specified id
042     * @param id the unique id of this Option
043     * @param required true iff this Option must be present
044     */
045    public OptionImpl( final int id, final boolean required ) 
046    {
047        m_id = id;
048        m_required = required;
049    }
050
051    /**
052     * Indicates whether this Option will be able to process the particular
053     * argument. The ListIterator must be restored to the initial state before
054     * returning the boolean.
055     * 
056     * @see #canProcess(WriteableCommandLine,String)
057     * @param commandLine the CommandLine object to store defaults in
058     * @param arguments the ListIterator over String arguments
059     * @return true if the argument can be processed by this Option
060     */
061    public boolean canProcess( 
062      final WriteableCommandLine commandLine, final ListIterator arguments )
063    {
064        if( arguments.hasNext() )
065        {
066            final String argument = (String) arguments.next();
067            arguments.previous();
068            return canProcess( commandLine, argument );
069        } 
070        else 
071        {
072            return false;
073        }
074    }
075
076   /**
077    * Returns a string representation of the option.
078    * @return the string value
079    */
080    public String toString() 
081    {
082        final StringBuffer buffer = new StringBuffer();
083        appendUsage( buffer, DisplaySetting.ALL, null );
084        return buffer.toString();
085    }
086
087    /**
088     * Returns the id of the option.  This can be used in a loop and switch 
089     * construct:
090     * 
091     * <code>
092     * for(Option o : cmd.getOptions()){
093     *     switch(o.getId()){
094     *         case POTENTIAL_OPTION:
095     *             ...
096     *     }
097     * }
098     * </code> 
099     * 
100     * The returned value is not guarenteed to be unique.
101     * 
102     * @return the id of the option.
103     */
104    public int getId() 
105    {
106        return m_id;
107    }
108
109   /**
110    * Evaluate this instance against the supplied instance for equality.
111    * @param thatObj the other object
112    * @return true if the supplied instance is equal to this instance
113    */
114    public boolean equals( final Object thatObj )
115    {
116        if( thatObj instanceof OptionImpl )
117        {
118            final OptionImpl that = (OptionImpl) thatObj;
119            return ( getId() == that.getId() ) 
120              && equals( getPreferredName(), that.getPreferredName() ) 
121              && equals( getDescription(), that.getDescription() ) 
122              && equals( getPrefixes(), that.getPrefixes() ) 
123              && equals( getTriggers(), that.getTriggers() );
124        }
125        else
126        {
127            return false;
128        }
129    }
130
131    private boolean equals( Object left, Object right )
132    {
133        if( ( left == null ) && ( right == null ) )
134        {
135            return true;
136        }
137        else if( ( left == null ) || ( right == null ) )
138        {
139            return false;
140        } 
141        else
142        {
143            return left.equals( right );
144        }
145    }
146
147   /**
148    * Return the hashcode value for this instance.
149    * @return the hash value
150    */
151    public int hashCode()
152    {
153        int hashCode = getId();
154        hashCode = ( hashCode * 37 ) + getPreferredName().hashCode();
155        if( getDescription() != null )
156        {
157            hashCode = ( hashCode * 37 ) + getDescription().hashCode();
158        }
159        hashCode = ( hashCode * 37 ) + getPrefixes().hashCode();
160        hashCode = ( hashCode * 37 ) + getTriggers().hashCode();
161        return hashCode;
162    }
163
164   /**
165    * Recursively searches for an option with the supplied trigger.
166    *
167    * @param trigger the trigger to search for.
168    * @return the matching option or null.
169    */
170    public Option findOption( String trigger )
171    {
172        if( getTriggers().contains( trigger ) )
173        {
174            return this;
175        } 
176        else 
177        {
178            return null;
179        }
180    }
181
182    /**
183     * Indicates whether this option is required to be present.
184     * @return true if the CommandLine will be invalid without this Option
185     */
186    public boolean isRequired() 
187    {
188        return m_required;
189    }
190
191    /**
192     * Adds defaults to a CommandLine.
193     * 
194     * Any defaults for this option are applied as well as the defaults for 
195     * any contained options
196     * 
197     * @param commandLine the CommandLine object to store defaults in
198     */
199    public void defaults( final WriteableCommandLine commandLine ) 
200    {
201        // nothing to do normally
202    }
203
204   /**
205    * Check prefixes.
206    * @param prefixes the prefixes set
207    */
208    protected void checkPrefixes( final Set prefixes ) 
209    {
210        // nothing to do if empty prefix list
211        if( prefixes.isEmpty() )
212        {
213            return;
214        }
215
216        // check preferred name
217        checkPrefix( prefixes, getPreferredName() );
218
219        // check triggers
220        getTriggers();
221
222        for( final Iterator i = getTriggers().iterator(); i.hasNext();)
223        {
224            checkPrefix( prefixes, (String) i.next() );
225        }
226    }
227
228   /**
229    * Check prefixes.
230    * @param prefixes the prefixes set
231    * @param trigger the trigger
232    */
233    private void checkPrefix( final Set prefixes, final String trigger )
234    {
235        for( final Iterator i = prefixes.iterator(); i.hasNext();) 
236        {
237            String prefix = (String) i.next();
238            if( trigger.startsWith( prefix ) ) 
239            {
240                return;
241            }
242        }
243
244        final ResourceHelper helper = ResourceHelper.getResourceHelper();
245        final String message =
246          helper.getMessage( 
247            ResourceConstants.OPTION_TRIGGER_NEEDS_PREFIX, 
248            trigger, 
249            prefixes.toString() );
250        throw new IllegalArgumentException( message );
251    }
252}