View Javadoc

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 }