1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. 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 org.apache.commons.cli2.validation; 18 19 import java.util.List; 20 import java.util.ListIterator; 21 22 import org.apache.commons.cli2.resource.ResourceConstants; 23 import org.apache.commons.cli2.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 John Keyes 44 */ 45 public class ClassValidator implements Validator { 46 /** i18n */ 47 private static final ResourceHelper resources = ResourceHelper.getResourceHelper(); 48 49 /** whether the class argument is loadable */ 50 private boolean loadable; 51 52 /** whether to create an instance of the class */ 53 private boolean instance; 54 55 /** the classloader to load classes from */ 56 private ClassLoader loader; 57 58 /** 59 * Validate each argument value in the specified List against this instances 60 * permitted attributes. 61 * 62 * If a value is valid then it's <code>String</code> value in the list is 63 * replaced with it's <code>Class</code> value or instance. 64 * 65 * @see org.apache.commons.cli2.validation.Validator#validate(java.util.List) 66 */ 67 public void validate(final List values) 68 throws InvalidArgumentException { 69 for (final ListIterator i = values.listIterator(); i.hasNext();) { 70 final String name = (String) i.next(); 71 72 if (!isPotentialClassName(name)) { 73 throw new InvalidArgumentException(resources.getMessage(ResourceConstants.CLASSVALIDATOR_BAD_CLASSNAME, 74 name)); 75 } 76 77 if (loadable || instance) { 78 final ClassLoader theLoader = getClassLoader(); 79 80 try { 81 final Class clazz = theLoader.loadClass(name); 82 83 if (instance) { 84 i.set(clazz.newInstance()); 85 } else { 86 i.set(clazz); 87 } 88 } catch (final ClassNotFoundException exp) { 89 throw new InvalidArgumentException(resources.getMessage(ResourceConstants.CLASSVALIDATOR_CLASS_NOTFOUND, 90 name)); 91 } catch (final IllegalAccessException exp) { 92 throw new InvalidArgumentException(resources.getMessage(ResourceConstants.CLASSVALIDATOR_CLASS_ACCESS, 93 name, exp.getMessage())); 94 } catch (final InstantiationException exp) { 95 throw new InvalidArgumentException(resources.getMessage(ResourceConstants.CLASSVALIDATOR_CLASS_CREATE, 96 name)); 97 } 98 } 99 } 100 } 101 102 /** 103 * Returns whether the argument value must represent a 104 * class that is loadable. 105 * 106 * @return whether the argument value must represent a 107 * class that is loadable. 108 */ 109 public boolean isLoadable() { 110 return loadable; 111 } 112 113 /** 114 * Specifies whether the argument value must represent a 115 * class that is loadable. 116 * 117 * @param loadable whether the argument value must 118 * represent a class that is loadable. 119 */ 120 public void setLoadable(boolean loadable) { 121 this.loadable = loadable; 122 } 123 124 /** 125 * Returns the {@link ClassLoader} used to resolve and load 126 * the classes specified by the argument values. 127 * 128 * @return the {@link ClassLoader} used to resolve and load 129 * the classes specified by the argument values. 130 */ 131 public ClassLoader getClassLoader() { 132 if (loader == null) { 133 loader = getClass().getClassLoader(); 134 } 135 136 return loader; 137 } 138 139 /** 140 * Specifies the {@link ClassLoader} used to resolve and load 141 * the classes specified by the argument values. 142 * 143 * @param loader the {@link ClassLoader} used to resolve and load 144 * the classes specified by the argument values. 145 */ 146 public void setClassLoader(ClassLoader loader) { 147 this.loader = loader; 148 } 149 150 /** 151 * Returns whether the argument value must represent a 152 * class that can be instantiated. 153 * 154 * @return whether the argument value must represent a 155 * class that can be instantiated. 156 */ 157 public boolean isInstance() { 158 return instance; 159 } 160 161 /** 162 * Specifies whether the argument value must represent a 163 * class that can be instantiated. 164 * 165 * @param instance whether the argument value must 166 * represent a class that can be instantiated. 167 */ 168 public void setInstance(boolean instance) { 169 this.instance = instance; 170 } 171 172 /** 173 * Returns whether the specified name is allowed as 174 * a Java class name. 175 * @param name the name to be checked 176 * @return true if allowed as Java class name 177 */ 178 protected boolean isPotentialClassName(final String name) { 179 final char[] chars = name.toCharArray(); 180 181 boolean expectingStart = true; 182 183 for (int i = 0; i < chars.length; ++i) { 184 final char c = chars[i]; 185 186 if (expectingStart) { 187 if (!Character.isJavaIdentifierStart(c)) { 188 return false; 189 } 190 191 expectingStart = false; 192 } else { 193 if (c == '.') { 194 expectingStart = true; 195 } else if (!Character.isJavaIdentifierPart(c)) { 196 return false; 197 } 198 } 199 } 200 201 return !expectingStart; 202 } 203 }