001/*
002 * Copyright 2004-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.commandline;
018
019import java.util.ArrayList;
020import java.util.Collections;
021import java.util.HashMap;
022import java.util.Iterator;
023import java.util.List;
024import java.util.Map;
025import java.util.Properties;
026import java.util.Set;
027
028import net.dpml.cli.Argument;
029import net.dpml.cli.Option;
030import net.dpml.cli.WriteableCommandLine;
031import net.dpml.cli.resource.ResourceConstants;
032import net.dpml.cli.resource.ResourceHelper;
033
034/**
035 * A WriteableCommandLine implementation allowing Options to write their
036 * processed information to a CommandLine.
037 *
038 * @author <a href="@PUBLISHER-URL@">@PUBLISHER-NAME@</a>
039 * @version @PROJECT-VERSION@
040 */
041public class WriteableCommandLineImpl extends CommandLineImpl implements WriteableCommandLine 
042{
043    private final Properties m_properties = new Properties();
044    private final List m_options = new ArrayList();
045    private final Map m_nameToOption = new HashMap();
046    private final Map m_values = new HashMap();
047    private final Map m_switches = new HashMap();
048    private final Map m_defaultValues = new HashMap();
049    private final Map m_defaultSwitches = new HashMap();
050    private final List m_normalised;
051    private final Set m_prefixes;
052
053    /**
054     * Creates a new WriteableCommandLineImpl rooted on the specified Option, to
055     * hold the parsed arguments.
056     *
057     * @param rootOption the CommandLine's root Option
058     * @param arguments the arguments this CommandLine represents
059     */
060    public WriteableCommandLineImpl(
061      final Option rootOption, final List arguments )
062    {
063        m_prefixes = rootOption.getPrefixes();
064        m_normalised = arguments;
065    }
066
067   /**
068    * Add an option.
069    * @param option the option to add
070    */
071    public void addOption( Option option )
072    {
073        m_options.add( option );
074        m_nameToOption.put( option.getPreferredName(), option );
075        for( Iterator i = option.getTriggers().iterator(); i.hasNext();)
076        {
077            m_nameToOption.put( i.next(), option );
078        }
079    }
080
081   /**
082    * Add an option.
083    * @param option the option to add
084    * @param value the option value
085    */
086    public void addValue( final Option option, final Object value )
087    {
088        if( option instanceof Argument )
089        {
090            addOption( option );
091        }
092        List valueList = (List) m_values.get( option );
093        if( valueList == null )
094        {
095            valueList = new ArrayList();
096            m_values.put( option, valueList );
097        }
098        valueList.add( value );
099    }
100
101   /**
102    * Add a switch.
103    * @param option the option to add
104    * @param value the option value
105    */
106    public void addSwitch( final Option option, final boolean value )
107    {
108        addOption( option );
109        if( m_switches.containsKey( option ) )
110        {
111            throw new IllegalStateException(
112              ResourceHelper.getResourceHelper().getMessage(
113                ResourceConstants.SWITCH_ALREADY_SET ) );
114        } 
115        else 
116        {
117            if( value )
118            {
119                m_switches.put( option, Boolean.TRUE );
120            }
121            else
122            {
123                m_switches.put( option, Boolean.FALSE );
124            }
125        }
126    }
127
128    /**
129     * Detects the presence of an option in this CommandLine.
130     * 
131     * @param option the Option to search for
132     * @return true iff the option is present
133     */
134    public boolean hasOption( final Option option )
135    {
136        return m_options.contains( option );
137    }
138
139    /**
140     * Finds the Option with the specified trigger
141     * 
142     * @param trigger the name of the option to retrieve
143     * @return the Option matching the trigger or null if none exists
144     */
145    public Option getOption( final String trigger )
146    {
147        return (Option) m_nameToOption.get( trigger );
148    }
149
150    /**
151     * Retrieves the Argument values associated with the specified Option
152     * 
153     * @param option the Option associated with the values
154     * @param defaultValues the result to return if no values are found
155     * @return a list of values or defaultValues if none are found
156     */
157    public List getValues( final Option option, final List defaultValues )
158    {
159        // First grab the command line values
160        List valueList = (List) m_values.get( option );
161
162        // Secondly try the defaults supplied to the method
163        if( ( valueList == null ) || valueList.isEmpty() )
164        {
165            valueList = defaultValues;
166        }
167
168        // Thirdly try the option's default values
169        if( ( valueList == null ) || valueList.isEmpty() )
170        {
171            valueList = (List) m_defaultValues.get( option );
172        }
173
174        // Finally use an empty list
175        if( valueList == null )
176        {
177            valueList = Collections.EMPTY_LIST;
178        }
179
180        return valueList;
181    }
182
183    /**
184     * Retrieves the Boolean value associated with the specified Switch
185     * 
186     * @param option the Option associated with the value
187     * @param defaultValue the Boolean to use if none match
188     * @return the Boolean associated with option or defaultValue if none exists
189     */
190    public Boolean getSwitch( final Option option, final Boolean defaultValue )
191    {
192        // First grab the command line values
193        Boolean bool = (Boolean) m_switches.get( option );
194
195        // Secondly try the defaults supplied to the method
196        if( bool == null )
197        {
198            bool = defaultValue;
199        }
200
201        // Thirdly try the option's default values
202        if( bool == null )
203        {
204            bool = (Boolean) m_defaultSwitches.get( option );
205        }
206
207        return bool;
208    }
209
210   /**
211    * Add a property to the commandline.
212    * @param property the property name
213    * @param value the property value
214    */
215    public void addProperty( final String property, final String value )
216    {
217        m_properties.setProperty( property, value );
218    }
219
220    /**
221     * Retrieves the value associated with the specified property 
222     * 
223     * @param property the property name to lookup
224     * @param defaultValue the value to use if no other is found
225     * @return the value of the property or defaultValue
226     */
227    public String getProperty( final String property, final String defaultValue )
228    {
229        return m_properties.getProperty( property, defaultValue );
230    }
231
232    /**
233     * Retrieves the set of all property names associated with this CommandLine
234     * 
235     * @return a none null set of property names 
236     */
237    public Set getProperties()
238    {
239        return Collections.unmodifiableSet( m_properties.keySet() );
240    }
241
242   /**
243    * Return true if the trigger argument looks like an option.
244    * @param trigger the trigger to evaluate
245    * @return true if the trigger looks like an option
246    */
247    public boolean looksLikeOption( final String trigger )
248    {
249        for( final Iterator i = m_prefixes.iterator(); i.hasNext();)
250        {
251            final String prefix = (String) i.next();
252            if( trigger.startsWith( prefix ) )
253            {
254                return true;
255            }
256        }
257        return false;
258    }
259
260   /**
261    * Return this commandline as a string.
262    * @return the string representation
263    */
264    public String toString() 
265    {
266        final StringBuffer buffer = new StringBuffer();
267        // need to add group header
268        for( final Iterator i = m_normalised.iterator(); i.hasNext();) 
269        {
270            final String arg = ( String ) i.next();
271            if( arg.indexOf( ' ' ) >= 0 )
272            {
273                buffer.append( "\"" ).append( arg ).append( "\"" );
274            } 
275            else 
276            {
277                buffer.append( arg );
278            }
279            if( i.hasNext() )
280            {
281                buffer.append( ' ' );
282            }
283        }
284        return buffer.toString();
285    }
286
287    /**
288     * Retrieves a list of all Options found in this CommandLine
289     * 
290     * @return a none null list of Options
291     */
292    public List getOptions()
293    {
294        return Collections.unmodifiableList( m_options );
295    }
296
297    /**
298     * Retrieves a list of all Option triggers found in this CommandLine
299     * 
300     * @return a none null list of Option triggers
301     */
302    public Set getOptionTriggers()
303    {
304        return Collections.unmodifiableSet( m_nameToOption.keySet() );
305    }
306
307   /**
308    * Set default values.
309    * @param option the option
310    * @param defaults a list of defaults
311    */
312    public void setDefaultValues( final Option option, final List defaults )
313    {
314        if( defaults == null )
315        {
316            m_defaultValues.remove( option );
317        } 
318        else 
319        {
320            m_defaultValues.put( option, defaults );
321        }
322    }
323
324   /**
325    * Set default switch.
326    * @param option the option
327    * @param defaultSwitch the default switch state
328    */
329    public void setDefaultSwitch( final Option option, final Boolean defaultSwitch ) 
330    {
331        if( defaultSwitch == null )
332        {
333            m_defaultSwitches.remove( defaultSwitch );
334        } 
335        else 
336        {
337            m_defaultSwitches.put( option, defaultSwitch );
338        }
339    }
340
341   /**
342    * Return the normalized collection.
343    * @return the moprmalized collection
344    */
345    public List getNormalised()
346    {
347        return Collections.unmodifiableList( m_normalised );
348    }
349}