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 }