Source for net.dpml.cli.option.Switch

   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: import net.dpml.cli.resource.ResourceHelper;
  35: 
  36: /**
  37:  * A Parent implementation representing normal switch options.
  38:  * For example: <code>+d|-d</code> or <code>--enable-x|--disable-x</code>.
  39:  * @author <a href="@PUBLISHER-URL@">@PUBLISHER-NAME@</a>
  40:  * @version @PROJECT-VERSION@
  41:  */
  42: public class Switch extends ParentImpl
  43: {
  44:     /** i18n */
  45:     public static final ResourceHelper RESOURCES = 
  46:       ResourceHelper.getResourceHelper();
  47: 
  48:     /**
  49:      * The default prefix for enabled switches
  50:      */
  51:     public static final String DEFAULT_ENABLED_PREFIX = "+";
  52: 
  53:     /**
  54:      * The default prefix for disabled switches
  55:      */
  56:     public static final String DEFAULT_DISABLED_PREFIX = "-";
  57:     
  58:     private final String m_enabledPrefix;
  59:     private final String m_disabledPrefix;
  60:     private final Set m_triggers;
  61:     private final String m_preferredName;
  62:     private final Set m_aliases;
  63:     private final Set m_prefixes;
  64:     private final Boolean m_defaultSwitch;
  65: 
  66:     /**
  67:      * Creates a new Switch with the specified parameters
  68:      * @param enabledPrefix the prefix used for enabled switches
  69:      * @param disabledPrefix the prefix used for disabled switches
  70:      * @param preferredName the preferred name of the switch
  71:      * @param aliases the aliases by which the Switch is known
  72:      * @param description a description of the Switch
  73:      * @param required whether the Option is strictly required
  74:      * @param argument the Argument belonging to this Parent, or null
  75:      * @param children the Group children belonging to this Parent, ot null
  76:      * @param id the unique identifier for this Option
  77:      * @param switchDefault the switch default value
  78:      * @throws IllegalArgumentException if the preferredName or an alias isn't
  79:      *     prefixed with enabledPrefix or disabledPrefix
  80:      */
  81:     public Switch(
  82:       final String enabledPrefix, final String disabledPrefix, final String preferredName,
  83:       final Set aliases, final String description, final boolean required,
  84:       final Argument argument, final Group children, final int id, 
  85:       final Boolean switchDefault )
  86:       throws IllegalArgumentException
  87:     {
  88:         super( argument, children, description, id, required );
  89: 
  90:         if( enabledPrefix == null )
  91:         {
  92:             throw new IllegalArgumentException(
  93:               RESOURCES.getMessage( 
  94:                 ResourceConstants.SWITCH_NO_ENABLED_PREFIX ) );
  95:         }
  96: 
  97:         if( disabledPrefix == null )
  98:         {
  99:             throw new IllegalArgumentException(
 100:               RESOURCES.getMessage( 
 101:                 ResourceConstants.SWITCH_NO_DISABLED_PREFIX ) );
 102:         }
 103: 
 104:         if( enabledPrefix.startsWith( disabledPrefix ) )
 105:         {
 106:             throw new IllegalArgumentException(
 107:               RESOURCES.getMessage( 
 108:                 ResourceConstants.SWITCH_ENABLED_STARTS_WITH_DISABLED ) );
 109:         }
 110: 
 111:         if( disabledPrefix.startsWith( enabledPrefix ) )
 112:         {
 113:             throw new IllegalArgumentException(
 114:               RESOURCES.getMessage( 
 115:                 ResourceConstants.SWITCH_DISABLED_STARTWS_WITH_ENABLED ) );
 116:         }
 117: 
 118:         m_enabledPrefix = enabledPrefix;
 119:         m_disabledPrefix = disabledPrefix;
 120:         m_preferredName = preferredName;
 121: 
 122:         if( ( preferredName == null ) || ( preferredName.length() < 1 ) )
 123:         {
 124:             throw new IllegalArgumentException(
 125:               RESOURCES.getMessage(
 126:                 ResourceConstants.SWITCH_PREFERRED_NAME_TOO_SHORT ) );
 127:         }
 128: 
 129:         final Set newTriggers = new HashSet();
 130:         newTriggers.add( enabledPrefix + preferredName );
 131:         newTriggers.add( disabledPrefix + preferredName );
 132:         m_triggers = Collections.unmodifiableSet( newTriggers );
 133: 
 134:         if( aliases == null )
 135:         {
 136:             m_aliases = Collections.EMPTY_SET;
 137:         } 
 138:         else
 139:         {
 140:             m_aliases = Collections.unmodifiableSet( new HashSet( aliases ) );
 141: 
 142:             for( final Iterator i = aliases.iterator(); i.hasNext();)
 143:             {
 144:                 final String alias = (String) i.next();
 145:                 newTriggers.add( enabledPrefix + alias );
 146:                 newTriggers.add( disabledPrefix + alias );
 147:             }
 148:         }
 149: 
 150:         final Set newPrefixes = new HashSet( super.getPrefixes() );
 151:         newPrefixes.add( enabledPrefix );
 152:         newPrefixes.add( disabledPrefix );
 153:         m_prefixes = Collections.unmodifiableSet( newPrefixes );
 154:         m_defaultSwitch = switchDefault;
 155:         checkPrefixes( newPrefixes );
 156:     }
 157: 
 158:     /**
 159:      * Processes the parent part of the Option.  The combination of parent,
 160:      * argument and children is handled by the process method.
 161:      * @see net.dpml.cli.Option#process(WriteableCommandLine, ListIterator)
 162:      * 
 163:      * @param commandLine the CommandLine to write results to
 164:      * @param arguments a ListIterator over argument strings positioned at the next
 165:      *             argument to process
 166:      * @throws OptionException if an error occurs while processing
 167:      */
 168:     public void processParent(
 169:       final WriteableCommandLine commandLine, final ListIterator arguments )
 170:       throws OptionException
 171:     {
 172:         final String arg = (String) arguments.next();
 173: 
 174:         if( canProcess( commandLine, arg ) )
 175:         {
 176:             if( arg.startsWith( m_enabledPrefix ) )
 177:             {
 178:                 commandLine.addSwitch( this, true );
 179:                 arguments.set( m_enabledPrefix + m_preferredName );
 180:             }
 181:             if( arg.startsWith( m_disabledPrefix ) )
 182:             {
 183:                 commandLine.addSwitch( this, false );
 184:                 arguments.set( m_disabledPrefix + m_preferredName );
 185:             }
 186:         } 
 187:         else
 188:         {
 189:             throw new OptionException(
 190:               this, 
 191:               ResourceConstants.UNEXPECTED_TOKEN, 
 192:               arg );
 193:         }
 194:     }
 195: 
 196:     /**
 197:      * Identifies the argument prefixes that should trigger this option. This
 198:      * is used to decide which of many Options should be tried when processing
 199:      * a given argument string.
 200:      * 
 201:      * The returned Set must not be null.
 202:      * 
 203:      * @return The set of triggers for this Option
 204:      */
 205:     public Set getTriggers() 
 206:     {
 207:         return m_triggers;
 208:     }
 209: 
 210:     /**
 211:      * Identifies the argument prefixes that should be considered options. This
 212:      * is used to identify whether a given string looks like an option or an
 213:      * argument value. Typically an option would return the set [--,-] while
 214:      * switches might offer [-,+].
 215:      * 
 216:      * The returned Set must not be null.
 217:      * 
 218:      * @return The set of prefixes for this Option
 219:      */
 220:     public Set getPrefixes() 
 221:     {
 222:         return m_prefixes;
 223:     }
 224: 
 225:     /**
 226:      * Checks that the supplied CommandLine is valid with respect to this
 227:      * option.
 228:      * 
 229:      * @param commandLine the CommandLine to check.
 230:      * @throws OptionException if the CommandLine is not valid.
 231:      */
 232:     public void validate( WriteableCommandLine commandLine )
 233:       throws OptionException
 234:     {
 235:         if( isRequired() && !commandLine.hasOption( this ) )
 236:         {
 237:             throw new OptionException(
 238:               this, 
 239:               ResourceConstants.OPTION_MISSING_REQUIRED,
 240:               getPreferredName() );
 241:         }
 242:         super.validate( commandLine );
 243:     }
 244: 
 245:     /**
 246:      * Appends usage information to the specified StringBuffer
 247:      * 
 248:      * @param buffer the buffer to append to
 249:      * @param helpSettings a set of display settings @see DisplaySetting
 250:      * @param comp a comparator used to sort the Options
 251:      */
 252:     public void appendUsage(
 253:       final StringBuffer buffer, final Set helpSettings, final Comparator comp )
 254:     {
 255:         // do we display optionality
 256:         final boolean optional =
 257:           !isRequired() 
 258:           && helpSettings.contains( DisplaySetting.DISPLAY_OPTIONAL );
 259:           
 260:         final boolean displayAliases = 
 261:           helpSettings.contains( DisplaySetting.DISPLAY_ALIASES );
 262:         final boolean disabled = 
 263:           helpSettings.contains( DisplaySetting.DISPLAY_SWITCH_DISABLED );
 264:         final boolean enabled =
 265:             !disabled || helpSettings.contains( DisplaySetting.DISPLAY_SWITCH_ENABLED );
 266:         final boolean both = disabled && enabled;
 267: 
 268:         if( optional )
 269:         {
 270:             buffer.append( '[' );
 271:         }
 272: 
 273:         if( enabled )
 274:         {
 275:             buffer.append( m_enabledPrefix ).append( m_preferredName );
 276:         }
 277: 
 278:         if( both )
 279:         {
 280:             buffer.append( '|' );
 281:         }
 282: 
 283:         if( disabled )
 284:         {
 285:             buffer.append( m_disabledPrefix ).append( m_preferredName );
 286:         }
 287: 
 288:         if( displayAliases && !m_aliases.isEmpty() )
 289:         {
 290:             buffer.append( " (" );
 291: 
 292:             final List list = new ArrayList( m_aliases );
 293:             Collections.sort( list );
 294:             for( final Iterator i = list.iterator(); i.hasNext();)
 295:             {
 296:                 final String alias = (String) i.next();
 297: 
 298:                 if( enabled )
 299:                 {
 300:                     buffer.append( m_enabledPrefix ).append( alias );
 301:                 }
 302:                 
 303:                 if( both )
 304:                 {
 305:                     buffer.append( '|' );
 306:                 }
 307: 
 308:                 if( disabled )
 309:                 {
 310:                     buffer.append( m_disabledPrefix ).append( alias );
 311:                 }
 312: 
 313:                 if( i.hasNext() )
 314:                 {
 315:                     buffer.append( ',' );
 316:                 }
 317:             }
 318:             
 319:             buffer.append( ')' );
 320:         }
 321: 
 322:         super.appendUsage( buffer, helpSettings, comp );
 323: 
 324:         if( optional )
 325:         {
 326:             buffer.append( ']' );
 327:         }
 328:     }
 329: 
 330:     /**
 331:      * The preferred name of an option is used for generating help and usage
 332:      * information.
 333:      * 
 334:      * @return The preferred name of the option
 335:      */
 336:     public String getPreferredName()
 337:     {
 338:         return m_enabledPrefix + m_preferredName;
 339:     }
 340: 
 341:     /**
 342:      * Adds defaults to a CommandLine.
 343:      * 
 344:      * Any defaults for this option are applied as well as the defaults for 
 345:      * any contained options
 346:      * 
 347:      * @param commandLine the CommandLine object to store defaults in
 348:      */
 349:     public void defaults( final WriteableCommandLine commandLine )
 350:     {
 351:         commandLine.setDefaultSwitch( this, m_defaultSwitch );
 352:     }
 353: }