Source for net.dpml.cli.option.ParentImpl

   1: /*
   2:  * Copyright 2003-2005 The Apache Software Foundation
   3:  * Copyright 2005 Stephen McConnell
   4:  *
   5:  * Licensed under the Apache License, Version 2.0 (the "License");
   6:  * you may not use this file except in compliance with the License.
   7:  * You may obtain a copy of the License at
   8:  *
   9:  *     http://www.apache.org/licenses/LICENSE-2.0
  10:  *
  11:  * Unless required by applicable law or agreed to in writing, software
  12:  * distributed under the License is distributed on an "AS IS" BASIS,
  13:  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14:  * See the License for the specific language governing permissions and
  15:  * limitations under the License.
  16:  */
  17: package net.dpml.cli.option;
  18: 
  19: import java.util.ArrayList;
  20: import java.util.Collections;
  21: import java.util.Comparator;
  22: import java.util.List;
  23: import java.util.ListIterator;
  24: import java.util.Set;
  25: 
  26: import net.dpml.cli.Argument;
  27: import net.dpml.cli.DisplaySetting;
  28: import net.dpml.cli.Group;
  29: import net.dpml.cli.Option;
  30: import net.dpml.cli.OptionException;
  31: import net.dpml.cli.Parent;
  32: import net.dpml.cli.WriteableCommandLine;
  33: 
  34: /**
  35:  * A base implementation of Parent providing limited ground work for further
  36:  * Parent implementations.
  37:  * @author <a href="@PUBLISHER-URL@">@PUBLISHER-NAME@</a>
  38:  * @version @PROJECT-VERSION@
  39:  */
  40: public abstract class ParentImpl extends OptionImpl implements Parent
  41: {
  42:     private static final char NUL = '\0';
  43:     private final Group m_children;
  44:     private final Argument m_argument;
  45:     private final String m_description;
  46: 
  47:    /**
  48:     * Creation of a new ParaentImpl.
  49:     * @param argument an argument
  50:     * @param children the children
  51:     * @param description the description
  52:     * @param id the id
  53:     * @param required the required flag
  54:     */
  55:     protected ParentImpl(
  56:       final Argument argument, final Group children, final String description,
  57:       final int id, final boolean required )
  58:     {
  59:         super( id, required );
  60:         
  61:         m_children = children;
  62:         m_argument = argument;
  63:         m_description = description;
  64:     }
  65: 
  66:     /**
  67:      * Processes String arguments into a CommandLine.
  68:      * 
  69:      * The iterator will initially point at the first argument to be processed
  70:      * and at the end of the method should point to the first argument not
  71:      * processed. This method MUST process at least one argument from the
  72:      * ListIterator.
  73:      * 
  74:      * @param commandLine the CommandLine object to store results in
  75:      * @param arguments the arguments to process
  76:      * @throws OptionException if any problems occur
  77:      */
  78:     public void process( final WriteableCommandLine commandLine, final ListIterator arguments )
  79:         throws OptionException
  80:     {
  81:         if( m_argument != null )
  82:         {
  83:             handleInitialSeparator( arguments, m_argument.getInitialSeparator() );
  84:         }
  85: 
  86:         processParent( commandLine, arguments );
  87: 
  88:         if( m_argument != null )
  89:         {
  90:             m_argument.processValues( commandLine, arguments, this );
  91:         }
  92: 
  93:         if( ( m_children != null ) && m_children.canProcess( commandLine, arguments ) ) 
  94:         {
  95:             m_children.process( commandLine, arguments );
  96:         }
  97:     }
  98: 
  99:     /**
 100:      * Indicates whether this Option will be able to process the particular
 101:      * argument.
 102:      * 
 103:      * @param commandLine the CommandLine object to store defaults in
 104:      * @param arg the argument to be tested
 105:      * @return true if the argument can be processed by this Option
 106:      */
 107:     public boolean canProcess(
 108:       final WriteableCommandLine commandLine, final String arg )
 109:     {
 110:         final Set triggers = getTriggers();
 111:         if( m_argument != null )
 112:         {
 113:             final char separator = m_argument.getInitialSeparator();
 114: 
 115:             // if there is a valid separator character
 116:             if( separator != NUL )
 117:             {
 118:                 final int initialIndex = arg.indexOf( separator );
 119:                 // if there is a separator present
 120:                 if( initialIndex > 0 )
 121:                 {
 122:                     return triggers.contains( arg.substring( 0, initialIndex ) );
 123:                 }
 124:             }
 125:         }
 126: 
 127:         return triggers.contains( arg );
 128:     }
 129: 
 130:     /**
 131:      * Identifies the argument prefixes that should be considered options. This
 132:      * is used to identify whether a given string looks like an option or an
 133:      * argument value. Typically an option would return the set [--,-] while
 134:      * switches might offer [-,+].
 135:      * 
 136:      * The returned Set must not be null.
 137:      * 
 138:      * @return The set of prefixes for this Option
 139:      */
 140:     public Set getPrefixes()
 141:     {
 142:         if( null == m_children )
 143:         {
 144:             return Collections.EMPTY_SET;
 145:         }
 146:         else
 147:         {
 148:             return m_children.getPrefixes();
 149:         }
 150:     }
 151: 
 152:     /**
 153:      * Checks that the supplied CommandLine is valid with respect to this
 154:      * option.
 155:      * 
 156:      * @param commandLine the CommandLine to check.
 157:      * @throws OptionException if the CommandLine is not valid.
 158:      */
 159:     public void validate( WriteableCommandLine commandLine ) throws OptionException
 160:     {
 161:         if( commandLine.hasOption( this ) )
 162:         {
 163:             if( m_argument != null )
 164:             {
 165:                 m_argument.validate( commandLine, this );
 166:             }
 167: 
 168:             if( m_children != null )
 169:             {
 170:                 m_children.validate( commandLine );
 171:             }
 172:         }
 173:     }
 174: 
 175:     /**
 176:      * Appends usage information to the specified StringBuffer
 177:      * 
 178:      * @param buffer the buffer to append to
 179:      * @param helpSettings a set of display settings @see DisplaySetting
 180:      * @param comp a comparator used to sort the Options
 181:      */
 182:     public void appendUsage(
 183:       final StringBuffer buffer, final Set helpSettings, final Comparator comp )
 184:     {
 185:         final boolean displayArgument =
 186:           ( m_argument != null ) 
 187:           && helpSettings.contains( DisplaySetting.DISPLAY_PARENT_ARGUMENT );
 188:         final boolean displayChildren =
 189:           ( m_children != null ) 
 190:           && helpSettings.contains( DisplaySetting.DISPLAY_PARENT_CHILDREN );
 191: 
 192:         if( displayArgument )
 193:         {
 194:             buffer.append( ' ' );
 195:             m_argument.appendUsage( buffer, helpSettings, comp );
 196:         }
 197: 
 198:         if( displayChildren )
 199:         {
 200:             buffer.append( ' ' );
 201:             m_children.appendUsage( buffer, helpSettings, comp );
 202:         }
 203:     }
 204: 
 205:     /**
 206:      * Returns a description of the option. This string is used to build help
 207:      * messages as in the HelpFormatter.
 208:      * 
 209:      * @see net.dpml.cli.util.HelpFormatter
 210:      * @return a description of the option.
 211:      */
 212:     public String getDescription()
 213:     {
 214:         return m_description;
 215:     }
 216: 
 217:     /**
 218:      * Builds up a list of HelpLineImpl instances to be presented by HelpFormatter.
 219:      * 
 220:      * @see net.dpml.cli.HelpLine
 221:      * @see net.dpml.cli.util.HelpFormatter
 222:      * @param depth the initial indent depth
 223:      * @param helpSettings the HelpSettings that should be applied
 224:      * @param comp a comparator used to sort options when applicable.
 225:      * @return a List of HelpLineImpl objects
 226:      */
 227:     public List helpLines(
 228:       final int depth, final Set helpSettings, final Comparator comp )
 229:     {
 230:         final List helpLines = new ArrayList();
 231:         helpLines.add( new HelpLineImpl( this, depth ) );
 232: 
 233:         if( helpSettings.contains( DisplaySetting.DISPLAY_PARENT_ARGUMENT ) && ( m_argument != null ) )
 234:         {
 235:             helpLines.addAll( m_argument.helpLines( depth + 1, helpSettings, comp ) );
 236:         }
 237: 
 238:         if( helpSettings.contains( DisplaySetting.DISPLAY_PARENT_CHILDREN ) && ( m_children != null ) )
 239:         {
 240:             helpLines.addAll( m_children.helpLines( depth + 1, helpSettings, comp ) );
 241:         }
 242: 
 243:         return helpLines;
 244:     }
 245: 
 246:    /**
 247:     * Return the argument value if any. 
 248:     * @return Returns the argument.
 249:     */
 250:     public Argument getArgument()
 251:     {
 252:         return m_argument;
 253:     }
 254: 
 255:     /**
 256:      * Return any children.
 257:      * @return Returns the children.
 258:      */
 259:     public Group getChildren()
 260:     {
 261:         return m_children;
 262:     }
 263: 
 264:     /**
 265:      * Split the token using the specified separator character.
 266:      * @param arguments the current position in the arguments iterator
 267:      * @param separator the separator char to split on
 268:      */
 269:     private void handleInitialSeparator(
 270:       final ListIterator arguments, final char separator )
 271:     {
 272:         // next token
 273:         final String newArgument = (String) arguments.next();
 274: 
 275:         // split the token
 276:         final int initialIndex = newArgument.indexOf( separator );
 277: 
 278:         if( initialIndex > 0 )
 279:         {
 280:             arguments.remove();
 281:             arguments.add( newArgument.substring( 0, initialIndex ) );
 282:             arguments.add( newArgument.substring( initialIndex + 1 ) );
 283:             arguments.previous();
 284:         }
 285:         arguments.previous();
 286:     }
 287:     
 288:    /**
 289:     * Recursively searches for an option with the supplied trigger.
 290:     *
 291:     * @param trigger the trigger to search for.
 292:     * @return the matching option or null.
 293:     */
 294:     public Option findOption( final String trigger )
 295:     {
 296:         final Option found = super.findOption( trigger );
 297:         if( ( found == null ) && ( m_children != null ) )
 298:         {
 299:             return m_children.findOption( trigger );
 300:         } 
 301:         else 
 302:         {
 303:             return found;
 304:         }
 305:     }
 306: 
 307:     /**
 308:      * Adds defaults to a CommandLine.
 309:      * 
 310:      * Any defaults for this option are applied as well as the defaults for 
 311:      * any contained options
 312:      * 
 313:      * @param commandLine the CommandLine object to store defaults in
 314:      */
 315:     public void defaults( final WriteableCommandLine commandLine )
 316:     {
 317:         super.defaults( commandLine );
 318:         if( m_argument != null )
 319:         {
 320:             m_argument.defaultValues( commandLine, this );
 321:         }
 322:         if( m_children != null )
 323:         {
 324:             m_children.defaults( commandLine );
 325:         }
 326:     }
 327: }