1 /*
2 * Copyright 2006-2016 The JGUIraffe Team.
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 package net.sf.jguiraffe.di.impl;
17
18 import net.sf.jguiraffe.di.ClassLoaderProvider;
19
20 /**
21 * <p>
22 * A helper class for defining references to classes.
23 * </p>
24 * <p>
25 * Handling of classes can become quite complex because of issues with different
26 * class loaders. Because of that the dependency injection framework supports
27 * multiple ways of defining classes:
28 * <ul>
29 * <li>by directly specifying <code>Class</code> objects; this can be used
30 * when the class is already known at compile time</li>
31 * <li>by providing only the class name; the class will then be resolved using
32 * a default class loader</li>
33 * <li>by providing a class name and a symbolic name for a class loader; this
34 * works together with the class loader registration mechanism supported by
35 * {@link net.sf.jguiraffe.di.BeanContext BeanContext}</li>
36 * </ul>
37 * </p>
38 * <p>
39 * Instances of this class encapsulate the different ways of specifying a class.
40 * They can be initialized with different variants of class descriptions. The
41 * <code>getTargetClass()</code> method can be used for obtaining a reference
42 * to the wrapped class. Instances are thread-safe and can be shared between
43 * multiple components. New instances are created using the static factory
44 * methods.
45 * </p>
46 *
47 * @author Oliver Heger
48 * @version $Id: ClassDescription.java 205 2012-01-29 18:29:57Z oheger $
49 */
50 public class ClassDescription
51 {
52 /** Stores the resolved class. */
53 private Class<?> targetClass;
54
55 /** Stores the name of the target class. */
56 private final String targetClassName;
57
58 /** Stores the name of the class loader. */
59 private final String classLoaderName;
60
61 /**
62 * Creates a new instance of <code>ClassDescription</code> and initializes
63 * it. Clients call the static factory methods for creating new instances.
64 *
65 * @param cls the target class
66 * @param clsName the name of the target class
67 * @param loaderName the name of the class loader
68 */
69 ClassDescription(Class<?> cls, String clsName, String loaderName)
70 {
71 targetClass = cls;
72 targetClassName = clsName;
73 classLoaderName = loaderName;
74 }
75
76 /**
77 * Returns the target class of this description. The passed in
78 * {@code ClassLoaderProvider} is used for resolving the class if necessary.
79 * This implementation will cache the <code>Class</code> objects when they
80 * have been resolved.
81 *
82 * @param clProvider the {@code ClassLoaderProvider}
83 * @return the target class of this description instance
84 * @throws net.sf.jguiraffe.di.InjectionException if the class cannot be
85 * resolved
86 * @throws IllegalArgumentException if the {@code ClassLoaderProvider} is
87 * needed, but is <b>null</b>
88 */
89 public synchronized Class<?> getTargetClass(ClassLoaderProvider clProvider)
90 {
91 if (targetClass == null)
92 {
93 if (clProvider == null)
94 {
95 throw new IllegalArgumentException(
96 "ClassLoaderProvider must not be null!");
97 }
98 targetClass = clProvider.loadClass(getTargetClassName(),
99 getClassLoaderName());
100 }
101 return targetClass;
102 }
103
104 /**
105 * Returns the name of the target class of this description.
106 *
107 * @return the name of the target class
108 */
109 public String getTargetClassName()
110 {
111 return targetClassName;
112 }
113
114 /**
115 * Returns the symbolic name of the class loader for resolving the class.
116 * This can be <b>null</b> if the default class loader is to be used.
117 *
118 * @return the name of the class loader to use
119 */
120 public String getClassLoaderName()
121 {
122 return classLoaderName;
123 }
124
125 /**
126 * Tests the passed in object for equality. Two objects of this class are
127 * considered equal if they have the same target class name and class loader
128 * name.
129 *
130 * @param obj the object to compare to
131 * @return a flag whether the objects are equal
132 */
133 @Override
134 public boolean equals(Object obj)
135 {
136 if (this == obj)
137 {
138 return true;
139 }
140 if (!(obj instanceof ClassDescription))
141 {
142 return false;
143 }
144
145 ClassDescription c = (ClassDescription) obj;
146 return getTargetClassName().equals(c.getTargetClassName())
147 && ((getClassLoaderName() == null) ? c.getClassLoaderName() == null
148 : getClassLoaderName().equals(c.getClassLoaderName()));
149 }
150
151 /**
152 * Returns a hash code for this object.
153 *
154 * @return a hash code
155 */
156 @Override
157 public int hashCode()
158 {
159 final int seed = 47;
160 final int factor = 17;
161
162 int result = seed;
163 result = factor * result + getTargetClassName().hashCode();
164 if (getClassLoaderName() != null)
165 {
166 result = factor * result + getClassLoaderName().hashCode();
167 }
168 return result;
169 }
170
171 /**
172 * Returns a string representation of this object. This string will contain
173 * the name of the target class. If a class loader name is specified, this
174 * name will also be contained in the resulting string.
175 *
176 * @return a string for this object
177 */
178 @Override
179 public String toString()
180 {
181 StringBuilder buf = new StringBuilder(getClass().getName());
182 buf.append('@').append(System.identityHashCode(this));
183 buf.append("[ className = ").append(getTargetClassName());
184 if (getClassLoaderName() != null)
185 {
186 buf.append(" classLoader = ").append(getClassLoaderName());
187 }
188 buf.append(" ]");
189 return buf.toString();
190 }
191
192 /**
193 * Returns an instance for the specified class. This method can be used when
194 * the target class is already known at compile time.
195 *
196 * @param cls the target class
197 * @return the description instance for this class
198 */
199 public static ClassDescription getInstance(Class<?> cls)
200 {
201 if (cls == null)
202 {
203 throw new IllegalArgumentException("Class must not be null!");
204 }
205 return new ClassDescription(cls, cls.getName(), null);
206 }
207
208 /**
209 * Returns an instance for the specified class name and class loader name.
210 * The target class will be resolved using reflection. It is loaded from the
211 * class loader with the given symbolic name.
212 *
213 * @param clsName the name of the class
214 * @param clsLoaderName the symbolic name of the class loader
215 * @return the description instance for this class
216 */
217 public static ClassDescription getInstance(String clsName,
218 String clsLoaderName)
219 {
220 if (clsName == null)
221 {
222 throw new IllegalArgumentException("Class name must not be null!");
223 }
224 return new ClassDescription(null, clsName, clsLoaderName);
225 }
226
227 /**
228 * Returns an instance for the specified class name that will be resolved
229 * using the default class loader.
230 *
231 * @param clsName the name of the class
232 * @return the description instance for this class
233 */
234 public static ClassDescription getInstance(String clsName)
235 {
236 return getInstance(clsName, null);
237 }
238 }