View Javadoc
1 /*
2 * ValueResolver.java
3 * Created on September 18, 2003
4 *
5 * The Blues Framework - A lightweight application framework
6 * Copyright (C) 2003 Lonnie Pryor
7 * http://blues.lonniepryor.com
8 *
9 * This library is free software; you can redistribute it and/or modify it under the
10 * terms of the GNU Lesser General Public License as published by the Free Software
11 * Foundation; either version 2.1 of the License, or (at your option) any later
12 * version.
13 *
14 * This library is distributed in the hope that it will be useful, but WITHOUT ANY
15 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
16 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public License along
19 * with this library; if not, write to:
20 *
21 * The Free Software Foundation, Inc.
22 * 59 Temple Place, Suite 330
23 * Boston, MA 02111-1307 USA
24 *
25 */
26 package com.lonniepryor.blues.xml;
27
28 import java.net.URL;
29
30 import java.util.HashMap;
31 import java.util.Map;
32 import java.util.StringTokenizer;
33
34 /***
35 * Transforms the values of attributes in an XML document into JavaBean property
36 * values. NOTE: this class is NOT thread-safe and instances may only be used on a
37 * single thread at a time.
38 *
39 * @author Lonnie Pryor
40 * @version $Revision: 1.1 $
41 */
42 public class ValueResolver {
43 /*** The ClassLoader to load classes from. */
44 private final ClassLoader classLoader;
45 /*** The relative path base to resolve Resource paths against. */
46 private final String relativePathBase;
47 /*** The first element in the chained sequence of class imports. */
48 private final Import importsChain;
49 /*** The short class name cache. */
50 private final Map classCache = new HashMap();
51
52 /***
53 * Creates a new ValueResolver object.
54 *
55 * @param classLoader The ClassLoader to load classes from.
56 * @param relativePathBase The relative path base to resolve Resource paths
57 * against.
58 * @param imports A whitespace-delimeted sequence of Java import statements
59 * (minus the 'import' keyword and line-ending semi-colon).
60 */
61 public ValueResolver (
62 ClassLoader classLoader, String relativePathBase, String imports) {
63 if (classLoader == null)
64 throw new NullPointerException("classLoader");
65 if (relativePathBase == null)
66 throw new NullPointerException("relativePathBase");
67 this.classLoader = classLoader;
68 if (relativePathBase.startsWith("/"))
69 relativePathBase = relativePathBase.substring(1);
70 if ((relativePathBase.length() > 0) && !relativePathBase.endsWith("/"))
71 relativePathBase = relativePathBase + "/";
72 this.relativePathBase = relativePathBase;
73 Import tmpImport = null;
74 if (imports != null) {
75 for (StringTokenizer st = new StringTokenizer(imports); st.hasMoreTokens();) {
76 String importStr = st.nextToken();
77 if (importStr.endsWith("*"))
78 tmpImport = newPackageImport(
79 importStr.substring(0, importStr.length() - 1), tmpImport);
80 else
81 tmpImport = newClassImport(importStr, tmpImport);
82 }
83 }
84 this.importsChain = tmpImport;
85 }
86
87 /***
88 * Resolves the supplied path into an absoulte path, prepending the relative path
89 * base if the resource name does not begin with a '/'.
90 *
91 * @param path The path to resolve.
92 *
93 * @return The supplied path resolved into an absoulte path.
94 */
95 public String resolvePath (String path) {
96 if (path == null)
97 throw new NullPointerException("path");
98 return path.startsWith("/") ? path.substring(1) : (relativePathBase + path);
99 }
100
101 /***
102 * Resolves the supplied resource name path and loads the requested resource from
103 * the class loader.
104 *
105 * @param name The name of the resource to load.
106 *
107 * @return The URL of the resource or null if it is not found.
108 */
109 public URL resolveResource (String name) {
110 if (name == null)
111 throw new NullPointerException("name");
112 URL resource = classLoader.getResource(resolvePath(name));
113 if (resource == null)
114 throw new XmlException("Resource '" + name + "' not found");
115 return resource;
116 }
117
118 /***
119 * Resolves the supplied class name into a Class instance, searching this
120 * resolver's import statements for the approprite fully-qualified name.
121 *
122 * @param name The name of the class to load.
123 *
124 * @return The requested class or null if it is not found.
125 */
126 public Class resolveClass (String name) {
127 if (classCache.containsKey(name))
128 return (Class)classCache.get(name);
129 Class result = null;
130 try {
131 result = classLoader.loadClass(name);
132 } catch (ClassNotFoundException cnfe) {
133 if (importsChain != null)
134 result = importsChain.loadClass(name);
135 }
136 if (result == null)
137 throw new XmlException("Class '" + name + "' not found");
138 classCache.put(name, result);
139 return result;
140 }
141
142 /***
143 * Transforms the supplied string value into the specified type.
144 *
145 * @param toType The type to transform to.
146 * @param stringValue The string value to transform.
147 *
148 * @return The transformed property value.
149 */
150 public Object resolve (Class toType, String stringValue) {
151 if (toType == null)
152 throw new NullPointerException("toType");
153 if (stringValue == null)
154 throw new NullPointerException("stringValue");
155 if (toType.equals(Class.class))
156 return resolveClass(stringValue);
157 if (toType.equals(URL.class))
158 return resolveResource(stringValue);
159 return null;
160 }
161
162 /***
163 * Creates a new Import for the specified class name.
164 *
165 * @param className The fully-qualified class name.
166 * @param next The next import in the chain.
167 *
168 * @return A new Import for the specified class name.
169 */
170 private Import newClassImport (final String className, Import next) {
171 return new Import(next) {
172 Class findClass (String name)
173 throws ClassNotFoundException {
174 return className.endsWith(name) ? classLoader.loadClass(className) : null;
175 }
176 };
177 }
178
179 /***
180 * Creates a new Import for the specified package name.
181 *
182 * @param packageName The fully-qualified package name ending with a dot.
183 * @param next The next import in the chain.
184 *
185 * @return A new Import for the specified package name.
186 */
187 private Import newPackageImport (final String packageName, Import next) {/package-summary.html">b> Import newPackageImport (final String packageName, Import next) {
188 return new Import(next) {
189 Class findClass (String name)
190 throws ClassNotFoundException {
191 return classLoader.loadClass(packageName + name);
192 }
193 };
194 }
195
196 /***
197 * Chained strategy class for loading classes against a set of import statements.
198 *
199 * @author Lonnie Pryor
200 * @version $Revision: 1.1 $
201 */
202 private abstract class Import {
203 /*** The next import in the chain. */
204 private final Import next;
205
206 /***
207 * Creates a new Import object.
208 *
209 * @param next The next import in the chain.
210 */
211 Import (Import next) {
212 this.next = next;
213 }
214
215 /***
216 * Loads a class from this import chain.
217 *
218 * @param name The name of the class to load.
219 *
220 * @return The requested class or null if it is not found.
221 */
222 final Class loadClass (String name) {
223 Class nextClass = (next == null) ? null : next.loadClass(name);
224 try {
225 Class myClass = findClass(name);
226 if (myClass == null)
227 return nextClass;
228 if (nextClass != null)
229 throw new IllegalArgumentException(
230 "Ambigous class name '" + name + "' matches " + nextClass.getName()
231 + " and " + myClass.getName());
232 return myClass;
233 } catch (ClassNotFoundException cnfe) {
234 return nextClass;
235 }
236 }
237
238 /***
239 * Overridden by subclasses to specialize searching behaviour.
240 *
241 * @param name The name of the class to load.
242 *
243 * @return The requested class.
244 *
245 * @throws ClassNotFoundException if the requested class is not found.
246 */
247 abstract Class findClass (String name)
248 throws ClassNotFoundException;
249 }
250 }
This page was automatically generated by Maven