Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
ObjectFactory |
|
| 2.6;2.6 |
1 | /* | |
2 | * Copyright 2003-2004 The Apache Software Foundation. | |
3 | * | |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | * you may not use this file except in compliance with the License. | |
6 | * You may obtain a copy of the License at | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | */ | |
16 | ||
17 | package org.apache.commons.mapper.util; | |
18 | ||
19 | import java.io.Serializable; | |
20 | import java.util.HashMap; | |
21 | import java.util.Map; | |
22 | ||
23 | /** | |
24 | * <p> | |
25 | * <code>ObjectFactory</code> maps string names to classes to be instantiated. | |
26 | * Given a name it will construct a new object of the mapped type using | |
27 | * reflection. Pass in a <code>Map</code> with entries in the form | |
28 | * "logical name"="fully qualified class name". Both key and value must be | |
29 | * Strings. | |
30 | * </p> | |
31 | * | |
32 | * <p> | |
33 | * For quick instantiation from a String, use the class method | |
34 | * <code>ObjectFactory.construct(String)</code>. | |
35 | * </p> | |
36 | * | |
37 | * <p> | |
38 | * The classes to be constructed must have a public no-arg constructor. An | |
39 | * Exception thrown during the loading of a Class or creation of an Object is | |
40 | * considered a programmer error and is converted to an | |
41 | * <code>IllegalArgumentException</code>. | |
42 | * This greatly simplifies client code and indicates a misconfiguration of the | |
43 | * <code>Map</code> passed to this factory. This class is thread-safe. | |
44 | * </p> | |
45 | * | |
46 | * <p> | |
47 | * Example: | |
48 | * <pre> | |
49 | * Map map = new HashMap(); | |
50 | * map.put("list", "java.util.ArrayList"); | |
51 | * map.put("set", "java.util.HashSet"); | |
52 | * | |
53 | * ObjectFactory factory = new ObjectFactory(map); | |
54 | * Set mySet = (Set) factory.construct("set"); | |
55 | * </pre> | |
56 | * </p> | |
57 | * | |
58 | * <p> | |
59 | * Using ObjectFactory without a map: | |
60 | * <br/> | |
61 | * <code>Set mySet = (Set) ObjectFactory.construct("java.util.HashSet");</code> | |
62 | * </p> | |
63 | * | |
64 | * <p> | |
65 | * This class is useful as a backing class for higher level factory classes. | |
66 | * The higher level class can use <code>ObjectFactory</code> to do the mapping | |
67 | * and creation work while it implements factory specific semantics and caching. | |
68 | * </p> | |
69 | */ | |
70 | public class ObjectFactory implements Cloneable, Serializable { | |
71 | ||
72 | /** | |
73 | * Stores "name"="qualified class name". | |
74 | */ | |
75 | 0 | protected Map nameMap = new HashMap(); |
76 | ||
77 | /** | |
78 | * Stores "name"=Class. Caches Class objects for quick lookup. These Class | |
79 | * objects act as prototypes to create new instances from. | |
80 | */ | |
81 | 0 | private Map prototypes = new HashMap(); |
82 | ||
83 | /** | |
84 | * Create an ObjectFactory with the given mapping of names to fully | |
85 | * qualified class names. | |
86 | * @param map A map of logical names to fully qualified class names. | |
87 | */ | |
88 | public ObjectFactory(Map map) { | |
89 | 0 | super(); |
90 | 0 | this.nameMap = map; |
91 | 0 | } |
92 | ||
93 | /** | |
94 | * Convenience method that creates an object of the class given as a string. | |
95 | * @param className The fully qualified class name of the object to be | |
96 | * created. | |
97 | * @throws IllegalArgumentException if the Class for the given name could | |
98 | * not be found. | |
99 | */ | |
100 | public static Object construct(String className) { | |
101 | 0 | return newInstance(loadClass(className)); |
102 | } | |
103 | ||
104 | /** | |
105 | * Returns an object of the class associated with the given command. | |
106 | * @throws IllegalArgumentException if given name was not found in the map, | |
107 | * the Class for the name could not be found, or the Class is missing a | |
108 | * no-arg constructor. | |
109 | */ | |
110 | public Object create(String name) { | |
111 | 0 | return newInstance(this.getPrototype(name)); |
112 | } | |
113 | ||
114 | /** | |
115 | * Calls c.newInstance() and converts exceptions. | |
116 | * @throws IllegalArgumentException if there is an error instantiating an | |
117 | * object of the class. This usually indicates the class is missing a | |
118 | * public no-arg constructor. | |
119 | */ | |
120 | private static Object newInstance(Class c) { | |
121 | try { | |
122 | 0 | return c.newInstance(); |
123 | ||
124 | 0 | } catch (InstantiationException e) { |
125 | 0 | throw new IllegalArgumentException( |
126 | c | |
127 | + " could not be created: " | |
128 | + e.getMessage() | |
129 | + ". Check for public no-arg constructor."); | |
130 | 0 | } catch (IllegalAccessException e) { |
131 | 0 | throw new IllegalArgumentException( |
132 | c | |
133 | + " could not be created: " | |
134 | + e.getMessage() | |
135 | + ". Check for public no-arg constructor."); | |
136 | } | |
137 | } | |
138 | ||
139 | /** | |
140 | * Returns the Class object mapped to the given name. If not found in the | |
141 | * cache, the Class object is stored there for quick retrieval. | |
142 | * @throws IllegalArgumentException if the given name is not mapped to a | |
143 | * class name or the Class could not be found. | |
144 | */ | |
145 | private Class getPrototype(String logicalName) { | |
146 | ||
147 | 0 | Class c = (Class) this.prototypes.get(logicalName); |
148 | 0 | if (c == null) { |
149 | 0 | String className = (String) this.nameMap.get(logicalName); |
150 | ||
151 | 0 | if (className == null) { |
152 | 0 | throw new IllegalArgumentException( |
153 | "Name not found in map: " + logicalName); | |
154 | } | |
155 | ||
156 | 0 | c = loadClass(className); |
157 | ||
158 | 0 | this.prototypes.put(logicalName, c); |
159 | } | |
160 | ||
161 | 0 | return c; |
162 | } | |
163 | ||
164 | /** | |
165 | * Attempts to load the Class object for the given class name. | |
166 | * @param className The fully qualified class name to load. | |
167 | * @return The Class object for the class name. | |
168 | * @throws IllegalArgumentException if the Class is not found for the | |
169 | * given name. | |
170 | */ | |
171 | private static Class loadClass(String className) { | |
172 | try { | |
173 | 0 | return Class.forName(className); |
174 | ||
175 | 0 | } catch (ClassNotFoundException e) { |
176 | 0 | throw new IllegalArgumentException( |
177 | "Class '" | |
178 | + className | |
179 | + "' could not be found: " | |
180 | + e.getMessage()); | |
181 | } | |
182 | } | |
183 | ||
184 | /** | |
185 | * Gets a copy of the <code>Map</code> this factory is using to | |
186 | * construct instances of classes. | |
187 | */ | |
188 | public Map getMap() { | |
189 | 0 | return new HashMap(this.nameMap); |
190 | } | |
191 | ||
192 | /** | |
193 | * Returns true if the ObjectFactory's maps are equal. | |
194 | */ | |
195 | public boolean equals(Object o) { | |
196 | 0 | if (o == this) { |
197 | 0 | return true; |
198 | } | |
199 | ||
200 | 0 | if (o instanceof ObjectFactory) { |
201 | 0 | ObjectFactory of = (ObjectFactory) o; |
202 | 0 | return of.nameMap.equals(this.nameMap); |
203 | } | |
204 | ||
205 | 0 | return false; |
206 | } | |
207 | ||
208 | /** | |
209 | * Returns a cloned instance of this ObjectFactory. | |
210 | */ | |
211 | public Object clone() { | |
212 | 0 | ObjectFactory of = null; |
213 | try { | |
214 | 0 | of = (ObjectFactory) super.clone(); |
215 | 0 | } catch (CloneNotSupportedException e) { |
216 | // Object does support clone | |
217 | 0 | } |
218 | ||
219 | 0 | of.prototypes = new HashMap(this.prototypes); |
220 | 0 | of.nameMap = new HashMap(this.nameMap); |
221 | ||
222 | 0 | return of; |
223 | } | |
224 | ||
225 | /** | |
226 | * ObjectFactory's hashcode is based on its underlying Map's hashcodes. | |
227 | */ | |
228 | public int hashCode() { | |
229 | 0 | return this.nameMap.hashCode() * 176543; |
230 | } | |
231 | ||
232 | } |