Source for net.dpml.cli.option.DefaultOption

   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.HashSet;
  23: import java.util.Iterator;
  24: import java.util.List;
  25: import java.util.ListIterator;
  26: import java.util.Set;
  27: 
  28: import net.dpml.cli.Argument;
  29: import net.dpml.cli.DisplaySetting;
  30: import net.dpml.cli.Group;
  31: import net.dpml.cli.OptionException;
  32: import net.dpml.cli.WriteableCommandLine;
  33: import net.dpml.cli.resource.ResourceConstants;
  34: 
  35: /**
  36:  * A Parent implementation representing normal options.
  37:  *
  38:  * @author <a href="@PUBLISHER-URL@">@PUBLISHER-NAME@</a>
  39:  * @version @PROJECT-VERSION@
  40:  */
  41: public class DefaultOption extends ParentImpl
  42: {
  43:     /**
  44:      * The default token used to prefix a short option
  45:      */
  46:     public static final String DEFAULT_SHORT_PREFIX = "-";
  47: 
  48:     /**
  49:      * The default token used to prefix a long option
  50:      */
  51:     public static final String DEFAULT_LONG_PREFIX = "--";
  52: 
  53:     /**
  54:      * The default value for the burstEnabled constructor parameter
  55:      */
  56:     public static final boolean DEFAULT_BURST_ENABLED = true;
  57:     
  58:     private final String m_preferredName;
  59:     private final Set m_aliases;
  60:     private final Set m_burstAliases;
  61:     private final Set m_triggers;
  62:     private final Set m_prefixes;
  63:     private final String m_shortPrefix;
  64:     private final boolean m_burstEnabled;
  65:     private final int m_burstLength;
  66: 
  67:     /**
  68:      * Creates a new DefaultOption
  69:      *
  70:      * @param shortPrefix the prefix used for short options
  71:      * @param longPrefix the prefix used for long options
  72:      * @param burstEnabled should option bursting be enabled
  73:      * @param preferredName the preferred name for this Option, this should 
  74:      *   begin with either shortPrefix or longPrefix
  75:      * @param description a description of this Option
  76:      * @param aliases the alternative names for this Option
  77:      * @param burstAliases the aliases that can be burst
  78:      * @param required whether the Option is strictly required
  79:      * @param argument the Argument belonging to this Parent, or null
  80:      * @param children the Group children belonging to this Parent, ot null
  81:      * @param id the unique identifier for this Option
  82:      * @throws IllegalArgumentException if the preferredName or an alias isn't
  83:      *     prefixed with shortPrefix or longPrefix
  84:      */
  85:     public DefaultOption(
  86:       final String shortPrefix, final String longPrefix, final boolean burstEnabled,
  87:       final String preferredName, final String description, final Set aliases,
  88:       final Set burstAliases, final boolean required, final Argument argument,
  89:       final Group children, final int id ) 
  90:       throws IllegalArgumentException
  91:     {
  92:         super( argument, children, description, id, required );
  93: 
  94:         m_shortPrefix = shortPrefix;
  95:         m_burstEnabled = burstEnabled;
  96:         m_burstLength = shortPrefix.length() + 1;
  97:         m_preferredName = preferredName;
  98:         
  99:         if( aliases == null )
 100:         {
 101:             m_aliases = Collections.EMPTY_SET;
 102:         }
 103:         else
 104:         {
 105:             m_aliases = Collections.unmodifiableSet( new HashSet( aliases ) );
 106:         }
 107:         
 108:         if( burstAliases == null )
 109:         {
 110:             m_burstAliases = Collections.EMPTY_SET;
 111:         }
 112:         else
 113:         {
 114:             m_burstAliases = Collections.unmodifiableSet( new HashSet( burstAliases ) );
 115:         }
 116:         
 117:         final Set newTriggers = new HashSet();
 118:         newTriggers.add( m_preferredName );
 119:         newTriggers.addAll( m_aliases );
 120:         newTriggers.addAll( m_burstAliases );
 121:         m_triggers = Collections.unmodifiableSet( newTriggers );
 122: 
 123:         final Set newPrefixes = new HashSet( super.getPrefixes() );
 124:         newPrefixes.add( m_shortPrefix );
 125:         newPrefixes.add( longPrefix );
 126:         m_prefixes = Collections.unmodifiableSet( newPrefixes );
 127: 
 128:         checkPrefixes( newPrefixes );
 129:     }
 130: 
 131:     /**
 132:      * Indicates whether this Option will be able to process the particular
 133:      * argument.
 134:      * 
 135:      * @param commandLine the CommandLine object to store defaults in
 136:      * @param argument the argument to be tested
 137:      * @return true if the argument can be processed by this Option
 138:      */
 139:     public boolean canProcess(
 140:       final WriteableCommandLine commandLine, final String argument )
 141:     {
 142:         return 
 143:           ( argument != null ) 
 144:           && ( 
 145:             super.canProcess( commandLine, argument ) 
 146:             || ( 
 147:               ( argument.length() >= m_burstLength ) 
 148:               && m_burstAliases.contains( 
 149:                 argument.substring( 0, m_burstLength ) ) 
 150:             ) 
 151:           );
 152:     }
 153: 
 154:     /**
 155:      * Process the parent.
 156:      * @param commandLine the CommandLine object to store defaults in
 157:      * @param arguments the ListIterator over String arguments
 158:      * @exception OptionException if an error occurs
 159:      */
 160:     public void processParent( WriteableCommandLine commandLine, ListIterator arguments )
 161:       throws OptionException 
 162:     {
 163:         final String argument = (String) arguments.next();
 164: 
 165:         if( m_triggers.contains( argument ) )
 166:         {
 167:             commandLine.addOption( this );
 168:             arguments.set( m_preferredName );
 169:         } 
 170:         else if( m_burstEnabled && ( argument.length() >= m_burstLength ) )
 171:         {
 172:             final String burst = argument.substring( 0, m_burstLength );
 173:             if( m_burstAliases.contains( burst ) )
 174:             {
 175:                 commandLine.addOption( this );
 176:                 //HMM test bursting all vs bursting one by one.
 177:                 arguments.set( m_preferredName );
 178: 
 179:                 if( getArgument() == null )
 180:                 {
 181:                     arguments.add( m_shortPrefix + argument.substring( m_burstLength ) );
 182:                 }
 183:                 else
 184:                 {
 185:                     arguments.add( argument.substring( m_burstLength ) );
 186:                 }
 187:                 arguments.previous();
 188:             } 
 189:             else
 190:             {
 191:                 throw new OptionException(
 192:                   this, ResourceConstants.CANNOT_BURST, argument );
 193:             }
 194:         }
 195:         else
 196:         {
 197:             throw new OptionException(
 198:               this, 
 199:               ResourceConstants.UNEXPECTED_TOKEN,
 200:               argument );
 201:         }
 202:     }
 203: 
 204:     /**
 205:      * Identifies the argument prefixes that should trigger this option. This
 206:      * is used to decide which of many Options should be tried when processing
 207:      * a given argument string.
 208:      * 
 209:      * The returned Set must not be null.
 210:      * 
 211:      * @return The set of triggers for this Option
 212:      */
 213:     public Set getTriggers()
 214:     {
 215:         return m_triggers;
 216:     }
 217: 
 218:     /**
 219:      * Identifies the argument prefixes that should be considered options. This
 220:      * is used to identify whether a given string looks like an option or an
 221:      * argument value. Typically an option would return the set [--,-] while
 222:      * switches might offer [-,+].
 223:      * 
 224:      * The returned Set must not be null.
 225:      * 
 226:      * @return The set of prefixes for this Option
 227:      */
 228:     public Set getPrefixes() 
 229:     {
 230:         return m_prefixes;
 231:     }
 232: 
 233:     /**
 234:      * Checks that the supplied CommandLine is valid with respect to this
 235:      * option.
 236:      * 
 237:      * @param commandLine the CommandLine to check.
 238:      * @throws OptionException if the CommandLine is not valid.
 239:      */
 240:     public void validate( WriteableCommandLine commandLine )
 241:       throws OptionException
 242:     {
 243:         if( isRequired() && !commandLine.hasOption( this ) )
 244:         {
 245:             throw new OptionException(
 246:               this,
 247:               ResourceConstants.OPTION_MISSING_REQUIRED,
 248:               getPreferredName() );
 249:         }
 250:         super.validate( commandLine );
 251:     }
 252: 
 253:     /**
 254:      * Appends usage information to the specified StringBuffer
 255:      * 
 256:      * @param buffer the buffer to append to
 257:      * @param helpSettings a set of display settings @see DisplaySetting
 258:      * @param comp a comparator used to sort the Options
 259:      */
 260:     public void appendUsage(
 261:       final StringBuffer buffer, final Set helpSettings, final Comparator comp )
 262:     {
 263:         // do we display optionality
 264:         final boolean optional =
 265:           !isRequired() 
 266:           && helpSettings.contains( DisplaySetting.DISPLAY_OPTIONAL );
 267:           
 268:         final boolean displayAliases = 
 269:           helpSettings.contains( DisplaySetting.DISPLAY_ALIASES );
 270: 
 271:         if( optional )
 272:         {
 273:             buffer.append( '[' );
 274:         }
 275: 
 276:         buffer.append( m_preferredName );
 277: 
 278:         if( displayAliases && !m_aliases.isEmpty() ) 
 279:         {
 280:             buffer.append( " (" );
 281: 
 282:             final List list = new ArrayList( m_aliases );
 283:             Collections.sort( list );
 284:             for( final Iterator i = list.iterator(); i.hasNext();)
 285:             {
 286:                 final String alias = (String) i.next();
 287:                 buffer.append( alias );
 288:                 if( i.hasNext() )
 289:                 {
 290:                     buffer.append( ',' );
 291:                 }
 292:             }
 293:             buffer.append( ')' );
 294:         }
 295: 
 296:         super.appendUsage( buffer, helpSettings, comp );
 297: 
 298:         if( optional )
 299:         {
 300:             buffer.append( ']' );
 301:         }
 302:     }
 303: 
 304:     /**
 305:      * The preferred name of an option is used for generating help and usage
 306:      * information.
 307:      * 
 308:      * @return The preferred name of the option
 309:      */
 310:     public String getPreferredName()
 311:     {
 312:         return m_preferredName;
 313:     }
 314: }