View Javadoc

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 }