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.option;
018
019 import java.util.ArrayList;
020 import java.util.Collections;
021 import java.util.Comparator;
022 import java.util.HashSet;
023 import java.util.Iterator;
024 import java.util.List;
025 import java.util.ListIterator;
026 import java.util.Set;
027
028 import net.dpml.cli.Argument;
029 import net.dpml.cli.DisplaySetting;
030 import net.dpml.cli.Group;
031 import net.dpml.cli.OptionException;
032 import net.dpml.cli.WriteableCommandLine;
033 import net.dpml.cli.resource.ResourceConstants;
034 import net.dpml.cli.resource.ResourceHelper;
035
036 /**
037 * Represents a cvs "update" style command line option.
038 *
039 * Like all Parents, Commands can have child options and can be part of
040 * Arguments.
041 *
042 * @author <a href="@PUBLISHER-URL@">@PUBLISHER-NAME@</a>
043 * @version @PROJECT-VERSION@
044 */
045 public class Command extends ParentImpl
046 {
047 /** The display name for the command */
048 private final String m_preferredName;
049
050 /** The aliases for this command */
051 private final Set m_aliases;
052
053 /** All the names for this command */
054 private final Set m_triggers;
055
056 /**
057 * Creates a new Command instance.
058 *
059 * @param preferredName the name normally used to refer to the Command
060 * @param description a description of the Command
061 * @param aliases alternative names for the Command
062 * @param required true if the Command is required
063 * @param argument an Argument that the command takes
064 * @param children the Group of child options for this Command
065 * @param id a unique id for the Command
066 * @see ParentImpl#ParentImpl(Argument, Group, String, int, boolean)
067 */
068 public Command(
069 final String preferredName, final String description, final Set aliases,
070 final boolean required, final Argument argument, final Group children, final int id )
071 {
072 super( argument, children, description, id, required );
073
074 // check the preferred name is valid
075 if( ( preferredName == null ) || ( preferredName.length() < 1 ) )
076 {
077 throw new IllegalArgumentException(
078 ResourceHelper.getResourceHelper().getMessage(
079 ResourceConstants.COMMAND_PREFERRED_NAME_TOO_SHORT ) );
080 }
081
082 m_preferredName = preferredName;
083
084 // gracefully and defensively handle aliases
085
086 if( null == aliases )
087 {
088 m_aliases = Collections.EMPTY_SET;
089 }
090 else
091 {
092 m_aliases = Collections.unmodifiableSet( new HashSet( aliases ) );
093 }
094
095 // populate the triggers Set
096 final Set newTriggers = new HashSet();
097 newTriggers.add( preferredName );
098 newTriggers.addAll( m_aliases );
099 m_triggers = Collections.unmodifiableSet( newTriggers );
100 }
101
102 /**
103 * Process the parent.
104 * @param commandLine the commandline
105 * @param arguments an iterator of arguments
106 * @exception OptionException if an error occurs
107 */
108 public void processParent(
109 final WriteableCommandLine commandLine, final ListIterator arguments )
110 throws OptionException
111 {
112 // grab the argument to process
113 final String arg = (String) arguments.next();
114
115 // if we can process it
116 if( canProcess( commandLine, arg ) )
117 {
118 // then note the option
119 commandLine.addOption( this );
120
121 // normalise the argument list
122 arguments.set( m_preferredName );
123 }
124 else
125 {
126 throw new OptionException(
127 this,
128 ResourceConstants.UNEXPECTED_TOKEN,
129 arg );
130 }
131 }
132
133 /**
134 * Identifies the argument prefixes that should trigger this option. This
135 * is used to decide which of many Options should be tried when processing
136 * a given argument string.
137 *
138 * The returned Set must not be null.
139 *
140 * @return The set of triggers for this Option
141 */
142 public Set getTriggers()
143 {
144 return m_triggers;
145 }
146
147 /**
148 * Checks that the supplied CommandLine is valid with respect to this
149 * option.
150 *
151 * @param commandLine the CommandLine to check.
152 * @throws OptionException if the CommandLine is not valid.
153 */
154 public void validate( WriteableCommandLine commandLine ) throws OptionException
155 {
156 if( isRequired() && !commandLine.hasOption( this ) )
157 {
158 throw new OptionException(
159 this,
160 ResourceConstants.OPTION_MISSING_REQUIRED,
161 getPreferredName() );
162 }
163 super.validate( commandLine );
164 }
165
166 /**
167 * Appends usage information to the specified StringBuffer
168 *
169 * @param buffer the buffer to append to
170 * @param helpSettings a set of display settings @see DisplaySetting
171 * @param comp a comparator used to sort the Options
172 */
173 public void appendUsage(
174 final StringBuffer buffer, final Set helpSettings, final Comparator comp )
175 {
176 // do we display optionality
177 final boolean optional =
178 !isRequired() && helpSettings.contains( DisplaySetting.DISPLAY_OPTIONAL );
179 final boolean displayAliases =
180 helpSettings.contains( DisplaySetting.DISPLAY_ALIASES );
181
182 if( optional )
183 {
184 buffer.append( '[' );
185 }
186
187 buffer.append( m_preferredName );
188
189 if( displayAliases && !m_aliases.isEmpty() )
190 {
191 buffer.append( " (" );
192 final List list = new ArrayList( m_aliases );
193 Collections.sort( list );
194 for( final Iterator i = list.iterator(); i.hasNext();)
195 {
196 final String alias = (String) i.next();
197 buffer.append( alias );
198 if( i.hasNext() )
199 {
200 buffer.append( ',' );
201 }
202 }
203 buffer.append( ')' );
204 }
205
206 super.appendUsage( buffer, helpSettings, comp );
207 if( optional )
208 {
209 buffer.append( ']' );
210 }
211 }
212
213 /**
214 * The preferred name of an option is used for generating help and usage
215 * information.
216 *
217 * @return The preferred name of the option
218 */
219 public String getPreferredName()
220 {
221 return m_preferredName;
222 }
223 }