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.validation; 018 019 import java.util.List; 020 import java.util.ListIterator; 021 022 import net.dpml.cli.resource.ResourceConstants; 023 import net.dpml.cli.resource.ResourceHelper; 024 025 /** 026 * The <code>ClassValidator</code> validates the string argument 027 * values are class names. 028 * 029 * The following example shows how to validate the 'logger' 030 * argument value is a class name, that can be instantiated. 031 * 032 * <pre> 033 * ... 034 * ClassValidator validator = new ClassValidator(); 035 * validator.setInstance(true); 036 * 037 * ArgumentBuilder builder = new ArgumentBuilder(); 038 * Argument logger = 039 * builder.withName("logger"); 040 * .withValidator(validator); 041 * </pre> 042 * 043 * @author <a href="http://www.dpml.net">Digital Product Meta Library</a> 044 * @version 1.0.0 045 */ 046 public class ClassValidator implements Validator 047 { 048 /** i18n */ 049 private static final ResourceHelper RESOURCES = 050 ResourceHelper.getResourceHelper(); 051 052 /** whether the class argument is loadable */ 053 private boolean m_loadable; 054 055 /** whether to create an instance of the class */ 056 private boolean m_instance; 057 058 /** the classloader to load classes from */ 059 private ClassLoader m_loader; 060 061 /** 062 * Validate each argument value in the specified List against this instances 063 * permitted attributes. 064 * 065 * If a value is valid then it's <code>String</code> value in the list is 066 * replaced with it's <code>Class</code> value or instance. 067 * 068 * @param values the list of values to validate 069 * @see net.dpml.cli.validation.Validator#validate(java.util.List) 070 * @exception InvalidArgumentException if a value is invalid 071 */ 072 public void validate( final List values ) throws InvalidArgumentException 073 { 074 for( final ListIterator i = values.listIterator(); i.hasNext();) 075 { 076 final String name = (String) i.next(); 077 078 if( !isPotentialClassName( name ) ) 079 { 080 throw new InvalidArgumentException( 081 RESOURCES.getMessage( 082 ResourceConstants.CLASSVALIDATOR_BAD_CLASSNAME, 083 name ) ); 084 } 085 086 if( m_loadable || m_instance ) 087 { 088 final ClassLoader theLoader = getClassLoader(); 089 090 try 091 { 092 final Class clazz = theLoader.loadClass( name ); 093 if( m_instance ) 094 { 095 i.set( clazz.newInstance() ); 096 } 097 else 098 { 099 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 }