Source for net.dpml.cli.validation.ClassValidator

   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.validation;
  18: 
  19: import java.util.List;
  20: import java.util.ListIterator;
  21: 
  22: import net.dpml.cli.resource.ResourceConstants;
  23: import net.dpml.cli.resource.ResourceHelper;
  24: 
  25: /**
  26:  * The <code>ClassValidator</code> validates the string argument
  27:  * values are class names.
  28:  *
  29:  * The following example shows how to validate the 'logger'
  30:  * argument value is a class name, that can be instantiated.
  31:  *
  32:  * <pre>
  33:  * ...
  34:  * ClassValidator validator = new ClassValidator();
  35:  * validator.setInstance(true);
  36:  *
  37:  * ArgumentBuilder builder = new ArgumentBuilder();
  38:  * Argument logger =
  39:  *     builder.withName("logger");
  40:  *            .withValidator(validator);
  41:  * </pre>
  42:  *
  43:  * @author <a href="@PUBLISHER-URL@">@PUBLISHER-NAME@</a>
  44:  * @version @PROJECT-VERSION@
  45:  */
  46: public class ClassValidator implements Validator 
  47: {
  48:     /** i18n */
  49:     private static final ResourceHelper RESOURCES = 
  50:       ResourceHelper.getResourceHelper();
  51: 
  52:     /** whether the class argument is loadable */
  53:     private boolean m_loadable;
  54: 
  55:     /** whether to create an instance of the class */
  56:     private boolean m_instance;
  57: 
  58:     /** the classloader to load classes from */
  59:     private ClassLoader m_loader;
  60: 
  61:    /**
  62:     * Validate each argument value in the specified List against this instances
  63:     * permitted attributes.
  64:     *
  65:     * If a value is valid then it's <code>String</code> value in the list is
  66:     * replaced with it's <code>Class</code> value or instance.
  67:     *
  68:     * @param values the list of values to validate 
  69:     * @see net.dpml.cli.validation.Validator#validate(java.util.List)
  70:     * @exception InvalidArgumentException if a value is invalid
  71:     */
  72:     public void validate( final List values ) throws InvalidArgumentException 
  73:     {
  74:         for( final ListIterator i = values.listIterator(); i.hasNext();)
  75:         {
  76:             final String name = (String) i.next();
  77: 
  78:             if( !isPotentialClassName( name ) )
  79:             {
  80:                 throw new InvalidArgumentException(
  81:                   RESOURCES.getMessage(
  82:                     ResourceConstants.CLASSVALIDATOR_BAD_CLASSNAME,
  83:                     name ) );
  84:             }
  85: 
  86:             if( m_loadable || m_instance )
  87:             {
  88:                 final ClassLoader theLoader = getClassLoader();
  89: 
  90:                 try 
  91:                 {
  92:                     final Class clazz = theLoader.loadClass( name );
  93:                     if( m_instance )
  94:                     {
  95:                         i.set( clazz.newInstance() );
  96:                     } 
  97:                     else 
  98:                     {
  99:                         i.set( clazz );
 100:                     }
 101:                 } 
 102:                 catch( final ClassNotFoundException exp )
 103:                 {
 104:                     throw new InvalidArgumentException(
 105:                       RESOURCES.getMessage(
 106:                         ResourceConstants.CLASSVALIDATOR_CLASS_NOTFOUND,
 107:                         name ) );
 108:                 } 
 109:                 catch( final IllegalAccessException exp )
 110:                 {
 111:                     throw new InvalidArgumentException(
 112:                       RESOURCES.getMessage(
 113:                         ResourceConstants.CLASSVALIDATOR_CLASS_ACCESS,
 114:                         name, 
 115:                         exp.getMessage() ) );
 116:                 } 
 117:                 catch( final InstantiationException exp )  
 118:                 {
 119:                     throw new InvalidArgumentException(
 120:                       RESOURCES.getMessage(
 121:                         ResourceConstants.CLASSVALIDATOR_CLASS_CREATE,
 122:                         name ) );
 123:                 }
 124:             }
 125:         }
 126:     }
 127: 
 128:     /**
 129:      * Returns whether the argument value must represent a
 130:      * class that is loadable.
 131:      *
 132:      * @return whether the argument value must represent a
 133:      * class that is loadable.
 134:      */
 135:     public boolean isLoadable() 
 136:     {
 137:         return m_loadable;
 138:     }
 139: 
 140:     /**
 141:      * Specifies whether the argument value must represent a
 142:      * class that is loadable.
 143:      *
 144:      * @param loadable whether the argument value must
 145:      * represent a class that is loadable.
 146:      */
 147:     public void setLoadable( boolean loadable )
 148:     {
 149:         m_loadable = loadable;
 150:     }
 151: 
 152:     /**
 153:      * Returns the {@link ClassLoader} used to resolve and load
 154:      * the classes specified by the argument values.
 155:      *
 156:      * @return the {@link ClassLoader} used to resolve and load
 157:      * the classes specified by the argument values.
 158:      */
 159:     public ClassLoader getClassLoader()
 160:     {
 161:         if( m_loader == null )
 162:         {
 163:             m_loader = getClass().getClassLoader();
 164:         }
 165:         return m_loader;
 166:     }
 167: 
 168:     /**
 169:      * Specifies the {@link ClassLoader} used to resolve and load
 170:      * the classes specified by the argument values.
 171:      *
 172:      * @param loader the {@link ClassLoader} used to resolve and load
 173:      * the classes specified by the argument values.
 174:      */
 175:     public void setClassLoader( ClassLoader loader ) 
 176:     {
 177:         m_loader = loader;
 178:     }
 179: 
 180:     /**
 181:      * Returns whether the argument value must represent a
 182:      * class that can be instantiated.
 183:      *
 184:      * @return whether the argument value must represent a
 185:      * class that can be instantiated.
 186:      */
 187:     public boolean isInstance()
 188:     {
 189:         return m_instance;
 190:     }
 191: 
 192:     /**
 193:      * Specifies whether the argument value must represent a
 194:      * class that can be instantiated.
 195:      *
 196:      * @param instance whether the argument value must
 197:      * represent a class that can be instantiated.
 198:      */
 199:     public void setInstance( boolean instance )
 200:     {
 201:         m_instance = instance;
 202:     }
 203: 
 204:     /**
 205:      * Returns whether the specified name is allowed as
 206:      * a Java class name.
 207:      * @param name the potential classname
 208:      * @return true if the name is a potential classname
 209:      */
 210:     protected boolean isPotentialClassName( final String name ) 
 211:     {
 212:         final char[] chars = name.toCharArray();
 213: 
 214:         boolean expectingStart = true;
 215: 
 216:         for( int i = 0; i < chars.length; ++i ) 
 217:         {
 218:             final char c = chars[i];
 219: 
 220:             if( expectingStart ) 
 221:             {
 222:                 if( !Character.isJavaIdentifierStart( c ) )
 223:                 {
 224:                     return false;
 225:                 }
 226:                 expectingStart = false;
 227:             } 
 228:             else 
 229:             {
 230:                 if( c == '.' ) 
 231:                 {
 232:                     expectingStart = true;
 233:                 } 
 234:                 else if( !Character.isJavaIdentifierPart( c ) ) 
 235:                 {
 236:                     return false;
 237:                 }
 238:             }
 239:         }
 240: 
 241:         return !expectingStart;
 242:     }
 243: }