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     */
017    package net.dpml.cli.commandline;
018    
019    import java.util.ArrayList;
020    import java.util.Collections;
021    import java.util.HashMap;
022    import java.util.Iterator;
023    import java.util.List;
024    import java.util.Map;
025    import java.util.Properties;
026    import java.util.Set;
027    
028    import net.dpml.cli.Argument;
029    import net.dpml.cli.Option;
030    import net.dpml.cli.WriteableCommandLine;
031    import net.dpml.cli.resource.ResourceConstants;
032    import 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="http://www.dpml.net">Digital Product Meta Library</a>
039     * @version 1.0.0
040     */
041    public 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    }