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.Collections;
020import java.util.Comparator;
021import java.util.List;
022import java.util.ListIterator;
023import java.util.Set;
024
025import net.dpml.cli.DisplaySetting;
026import net.dpml.cli.HelpLine;
027import net.dpml.cli.OptionException;
028import net.dpml.cli.WriteableCommandLine;
029import net.dpml.cli.resource.ResourceConstants;
030
031/**
032 * Handles the java style "-Dprop=value" opions
033 * @author <a href="@PUBLISHER-URL@">@PUBLISHER-NAME@</a>
034 * @version @PROJECT-VERSION@
035 */
036public class PropertyOption extends OptionImpl
037{
038   /** 
039    * The default property option name.
040    */
041    public static final String DEFAULT_OPTION_STRING = "-D";
042    
043   /** 
044    * The default property option description.
045    */
046    public static final String DEFAULT_DESCRIPTION = "Set property values.";
047
048    /**
049     * A default PropertyOption instance
050     */
051    public static final PropertyOption INSTANCE = new PropertyOption();
052    
053    private final String m_optionString;
054    private final String m_description;
055    private final Set m_prefixes;
056
057    /**
058     * Creates a new PropertyOption using the default settings of a "-D" trigger
059     * and an id of 'D'
060     */
061    public PropertyOption() 
062    {
063        this( DEFAULT_OPTION_STRING, DEFAULT_DESCRIPTION, 'D' );
064    }
065
066    /**
067     * Creates a new PropertyOption using the specified parameters
068     * @param optionString the trigger for the Option
069     * @param description the description of the Option
070     * @param id the id of the Option
071     */
072    public PropertyOption(
073      final String optionString, final String description, final int id )
074    {
075        super( id, false );
076        m_optionString = optionString;
077        m_description = description;
078        m_prefixes = Collections.singleton( optionString );
079    }
080
081    /**
082     * Indicates whether this Option will be able to process the particular
083     * argument.
084     * 
085     * @param commandLine the CommandLine object to store defaults in
086     * @param argument the argument to be tested
087     * @return true if the argument can be processed by this Option
088     */
089    public boolean canProcess(
090      final WriteableCommandLine commandLine, final String argument )
091    {
092        return ( argument != null ) 
093          && argument.startsWith( m_optionString ) 
094          && ( argument.length() > m_optionString.length() );
095    }
096
097    /**
098     * Identifies the argument prefixes that should be considered options. This
099     * is used to identify whether a given string looks like an option or an
100     * argument value. Typically an option would return the set [--,-] while
101     * switches might offer [-,+].
102     * 
103     * The returned Set must not be null.
104     * 
105     * @return The set of prefixes for this Option
106     */
107    public Set getPrefixes() 
108    {
109        return m_prefixes;
110    }
111
112    /**
113     * Processes String arguments into a CommandLine.
114     * 
115     * The iterator will initially point at the first argument to be processed
116     * and at the end of the method should point to the first argument not
117     * processed. This method MUST process at least one argument from the
118     * ListIterator.
119     * 
120     * @param commandLine the CommandLine object to store results in
121     * @param arguments the arguments to process
122     * @throws OptionException if any problems occur
123     */
124    public void process(
125      final WriteableCommandLine commandLine, final ListIterator arguments )
126      throws OptionException 
127    {
128        final String arg = (String) arguments.next();
129
130        if( !canProcess( commandLine, arg ) )
131        {
132            throw new OptionException(
133              this,
134              ResourceConstants.UNEXPECTED_TOKEN, 
135              arg );
136        }
137        
138        final int propertyStart = m_optionString.length();
139        final int equalsIndex = arg.indexOf( '=', propertyStart );
140        final String property;
141        final String value;
142
143        if( equalsIndex < 0 )
144        {
145            property = arg.substring( propertyStart );
146            value = "true";
147        }
148        else
149        {
150            property = arg.substring( propertyStart, equalsIndex );
151            value = arg.substring( equalsIndex + 1 );
152        }
153        commandLine.addProperty( property, value );
154    }
155
156    /**
157     * Identifies the argument prefixes that should trigger this option. This
158     * is used to decide which of many Options should be tried when processing
159     * a given argument string.
160     * 
161     * The returned Set must not be null.
162     * 
163     * @return The set of triggers for this Option
164     */
165    public Set getTriggers()
166    {
167        return Collections.singleton( m_optionString );
168    }
169
170    /**
171     * Checks that the supplied CommandLine is valid with respect to this
172     * option.
173     * 
174     * @param commandLine the CommandLine to check.
175     * @throws OptionException if the CommandLine is not valid.
176     */
177    public void validate( WriteableCommandLine commandLine ) throws OptionException
178    {
179        // PropertyOption needs no validation
180    }
181
182    /**
183     * Appends usage information to the specified StringBuffer
184     * 
185     * @param buffer the buffer to append to
186     * @param helpSettings a set of display settings @see DisplaySetting
187     * @param comp a comparator used to sort the Options
188     */
189    public void appendUsage(
190      final StringBuffer buffer, final Set helpSettings, final Comparator comp ) 
191    {
192        final boolean display = helpSettings.contains( DisplaySetting.DISPLAY_PROPERTY_OPTION );
193        final boolean bracketed = helpSettings.contains( DisplaySetting.DISPLAY_ARGUMENT_BRACKETED );
194
195        if( display )
196        {
197            buffer.append( m_optionString );
198            if( bracketed ) 
199            {
200                buffer.append( '<' );
201            }
202            buffer.append( "property" );
203            if( bracketed ) 
204            {
205                buffer.append( '>' );
206            }
207            buffer.append( "=" );
208            if( bracketed )
209            {
210                buffer.append( '<' );
211            }
212            buffer.append( "value" );
213            if( bracketed )
214            {
215                buffer.append( '>' );
216            }
217        }
218    }
219
220    /**
221     * The preferred name of an option is used for generating help and usage
222     * information.
223     * 
224     * @return The preferred name of the option
225     */
226    public String getPreferredName() 
227    {
228        return m_optionString;
229    }
230
231    /**
232     * Returns a description of the option. This string is used to build help
233     * messages as in the HelpFormatter.
234     * 
235     * @see net.dpml.cli.util.HelpFormatter
236     * @return a description of the option.
237     */
238    public String getDescription()
239    {
240        return m_description;
241    }
242
243    /**
244     * Builds up a list of HelpLineImpl instances to be presented by HelpFormatter.
245     * 
246     * @see HelpLine
247     * @see net.dpml.cli.util.HelpFormatter
248     * @param depth the initial indent depth
249     * @param helpSettings the HelpSettings that should be applied
250     * @param comp a comparator used to sort options when applicable.
251     * @return a List of HelpLineImpl objects
252     */
253    public List helpLines(
254      final int depth, final Set helpSettings, final Comparator comp )
255    {
256        if( helpSettings.contains( DisplaySetting.DISPLAY_PROPERTY_OPTION ) ) 
257        {
258            final HelpLine helpLine = new HelpLineImpl( this, depth );
259            return Collections.singletonList( helpLine );
260        } 
261        else
262        {
263            return Collections.EMPTY_LIST;
264        }
265    }
266}