001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. 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 */ 017package org.apache.commons.cli2.validation; 018 019import java.util.List; 020import java.util.ListIterator; 021 022import org.apache.commons.cli2.resource.ResourceConstants; 023import org.apache.commons.cli2.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 John Keyes 044 */ 045public class ClassValidator implements Validator { 046 /** i18n */ 047 private static final ResourceHelper resources = ResourceHelper.getResourceHelper(); 048 049 /** whether the class argument is loadable */ 050 private boolean loadable; 051 052 /** whether to create an instance of the class */ 053 private boolean instance; 054 055 /** the classloader to load classes from */ 056 private ClassLoader loader; 057 058 /** 059 * Validate each argument value in the specified List against this instances 060 * permitted attributes. 061 * 062 * If a value is valid then it's <code>String</code> value in the list is 063 * replaced with it's <code>Class</code> value or instance. 064 * 065 * @see org.apache.commons.cli2.validation.Validator#validate(java.util.List) 066 */ 067 public void validate(final List values) 068 throws InvalidArgumentException { 069 for (final ListIterator i = values.listIterator(); i.hasNext();) { 070 final String name = (String) i.next(); 071 072 if (!isPotentialClassName(name)) { 073 throw new InvalidArgumentException(resources.getMessage(ResourceConstants.CLASSVALIDATOR_BAD_CLASSNAME, 074 name)); 075 } 076 077 if (loadable || instance) { 078 final ClassLoader theLoader = getClassLoader(); 079 080 try { 081 final Class clazz = theLoader.loadClass(name); 082 083 if (instance) { 084 i.set(clazz.newInstance()); 085 } else { 086 i.set(clazz); 087 } 088 } catch (final ClassNotFoundException exp) { 089 throw new InvalidArgumentException(resources.getMessage(ResourceConstants.CLASSVALIDATOR_CLASS_NOTFOUND, 090 name)); 091 } catch (final IllegalAccessException exp) { 092 throw new InvalidArgumentException(resources.getMessage(ResourceConstants.CLASSVALIDATOR_CLASS_ACCESS, 093 name, exp.getMessage())); 094 } catch (final InstantiationException exp) { 095 throw new InvalidArgumentException(resources.getMessage(ResourceConstants.CLASSVALIDATOR_CLASS_CREATE, 096 name)); 097 } 098 } 099 } 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}