KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > classfile > ClassName


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 2000-2001 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.modules.classfile;
21
22 import java.io.ObjectStreamException JavaDoc;
23 import java.io.Serializable JavaDoc;
24 import java.lang.ref.WeakReference JavaDoc;
25 import java.util.Comparator JavaDoc;
26 import java.util.WeakHashMap JavaDoc;
27
28 /**
29  * Class representing the name of a Java class. This class is immutable, and
30  * can therefore be safely used by multiple threads.
31  * <p>
32  * <b>Warning:</b> The same reference is often returned by the getClassName
33  * factory method for multiple calls which use the same type name as its
34  * parameter. However, no guarantee is made that this behavior will always
35  * be true, so the equals method should always be used to compare ClassName
36  * instances.
37  *
38  * @author Thomas Ball
39  */

40 public final class ClassName implements Comparable JavaDoc, Comparator JavaDoc, Serializable JavaDoc {
41
42     static final long serialVersionUID = -8444469778945723553L;
43
44     private final String JavaDoc type;
45     private final transient String JavaDoc internalName;
46     private volatile transient String JavaDoc externalName;
47     private volatile transient String JavaDoc packageName;
48     private volatile transient String JavaDoc simpleName;
49
50     private final static WeakHashMap JavaDoc<String JavaDoc,WeakReference JavaDoc<ClassName>> cache =
51             new WeakHashMap JavaDoc<String JavaDoc,WeakReference JavaDoc<ClassName>>();
52
53     /**
54      * Returns the ClassName object referenced by a class
55      * type string (field descriptor), as defined in the
56      * JVM Specification, sections 4.3.2 and 4.2.
57      * <P>
58      * Basically, the JVM Specification defines a class type
59      * string where the periods ('.') separating
60      * a package name are replaced by forward slashes ('/').
61      * Not documented in the second edition of the specification
62      * is that periods separating inner and outer classes are
63      * replaced with dollar signs ('$'). Array classes have one
64      * or more left brackets ('[') prepending the class type.
65      * For example:
66      * <PRE><CODE>
67      * java.lang.String java/lang/String
68      * java.util.HashMap.Entry java/util/HashMap$Entry
69      * java.lang.Integer[] [java/lang/Integer
70      * java.awt.Point[][] [[java/awt/Point
71      * </CODE><PRE>
72      * <P>
73      * This method also accepts type strings which contain with
74      * 'L' and end with ';' characters. This format is used
75      * to reference a class in other type names, such as
76      * method arguments. These two characters are removed from the
77      * type string.
78      * <P>
79      * Because ClassNames are immutable, multiple requests to
80      * get the same type string may return identical object
81      * references. This cannot be assumed, however, and the
82      * ClassName.equals() method should be used instead of
83      * "==" to test for equality.
84      *
85      * @param classType the class type string, as defined by the JVM spec.
86      * @throws IllegalArgumentException if classType isn't of the correct
87      * format.
88      */

89     public static ClassName getClassName(String JavaDoc classType) {
90         // A null superclass name is supposed to be null, but may be
91
// an empty string depending on the compiler.
92
if (classType == null || classType.length() == 0)
93         return null;
94
95         ClassName cn = getCacheEntry(classType);
96     if (cn == null)
97         synchronized (cache) {
98         cn = getCacheEntry(classType);
99         if (cn == null) {
100             // check for valid class type
101
int i = classType.indexOf('L');
102             String JavaDoc _type;
103             char lastChar = classType.charAt(classType.length()-1);
104             if (i != -1 && lastChar == ';') {
105                         // remove 'L' and ';' from type
106
_type = classType.substring(i+1, classType.length()-1);
107                         if (i > 0)
108                             // add array prefix
109
_type = classType.substring(0, i) + _type;
110             cn = getCacheEntry(_type);
111             if (cn != null)
112                 return cn;
113             } else {
114             _type = classType;
115             }
116
117             cn = new ClassName(_type);
118             cache.put(_type, new WeakReference JavaDoc<ClassName>(cn));
119         }
120         }
121     return cn;
122     }
123
124     private static ClassName getCacheEntry(String JavaDoc key) {
125     WeakReference JavaDoc ref = cache.get(key);
126     return ref != null ? (ClassName)ref.get() : null;
127     }
128
129     /**
130      * Create a ClassName object via its internal type name, as
131      * defined by the JVM spec.
132      */

133     private ClassName(String JavaDoc type) {
134         this.type = type;
135
136     // internal name is a type stripped of any array designators
137
int i = type.lastIndexOf('[');
138     internalName = (i > -1) ? type.substring(i+1) : type;
139     }
140
141     /**
142      * Returns the type string of this class, as stored in the
143      * classfile (it's "raw" form). For example, an array of
144      * Floats would have a type of "[java/lang/Float".
145      */

146     public String JavaDoc getType() {
147         return type;
148     }
149
150     /**
151      * Returns the "internal" classname, as defined by the JVM
152      * Specification, without any parameter or return type
153      * information. For example, the name for the String
154      * class would be "java/lang/String". Inner classes are
155      * separated from their outer class with '$'; such as
156      * "java/util/HashMap$Entry". Array specifications are
157      * stripped; use getType() instead.
158      */

159     public String JavaDoc getInternalName() {
160         return internalName;
161     }
162
163     /**
164      * Returns the "external" classname, as defined by the
165      * Java Language Specification, without any parameter
166      * or return type information. For example, the name for the
167      * String class would be "java.lang.String". Inner classes
168      * are separated from their outer class with '.'; such as
169      * "java.util.HashMap.Entry". Arrays are shown as having one
170      * or more "[]" characters behind the base classname, such
171      * as "java.io.Files[]".
172      */

173     public String JavaDoc getExternalName() {
174         return getExternalName(false);
175     }
176
177     /**
178      * Returns the "external" classname, as defined by the
179      * Java Language Specification, without any parameter
180      * or return type information. For example, the name for the
181      * String class would be "java.lang.String". Inner classes
182      * are separated from their outer class with '.'; such as
183      * "java.util.HashMap.Entry". Unless suppressed, arrays are
184      * shown as having one or more "[]" characters behind the
185      * base classname, such as "java.io.Files[]".
186      */

187     public String JavaDoc getExternalName(boolean suppressArrays) {
188         initExternalName();
189         int i;
190         if (suppressArrays && (i = externalName.indexOf('[')) != -1)
191         return externalName.substring(0, i);
192         return externalName;
193     }
194     
195     private synchronized void initExternalName() {
196         if (externalName == null)
197             externalName = externalizeClassName();
198     }
199
200     /**
201      * Return the package portion of this classname.
202      */

203     public String JavaDoc getPackage() {
204         if (packageName == null)
205             initPackage();
206     return packageName;
207     }
208
209     private synchronized void initPackage() {
210         int i = internalName.lastIndexOf('/');
211         packageName = (i != -1) ?
212             internalName.substring(0, i).replace('/', '.') : "";
213     }
214
215     /**
216      * Returns the classname without any package specification.
217      */

218     public String JavaDoc getSimpleName() {
219         if (simpleName == null)
220             initSimpleName();
221     return simpleName;
222     }
223     
224     private synchronized void initSimpleName() {
225         String JavaDoc pkg = getPackage();
226         int i = pkg.length();
227         String JavaDoc extName = getExternalName();
228         if (i == 0)
229             simpleName = extName; // no package
230
else
231             simpleName = extName.substring(i + 1);
232     }
233
234     public boolean equals(Object JavaDoc obj) {
235         if (this == obj)
236         return true;
237     return (obj instanceof ClassName) ?
238       (type.equals(((ClassName)obj).type)) : false;
239     }
240
241     /**
242      * Compares this ClassName to another Object. If the Object is a
243      * ClassName, this function compares the ClassName's types. Otherwise,
244      * it throws a <code>ClassCastException</code>.
245      *
246      * @param obj the <code>Object</code> to be compared.
247      * @return the value <code>0</code> if the argument is a string
248      * lexicographically equal to this string; a value less than
249      * <code>0</code> if the argument is a string lexicographically
250      * greater than this string; and a value greater than
251      * <code>0</code> if the argument is a string lexicographically
252      * less than this string.
253      * @exception <code>ClassCastException</code> if the argument is not a
254      * <code>ClassName</code>.
255      * @see java.lang.Comparable
256      */

257     public int compareTo(Object JavaDoc obj) {
258         // If obj isn't a ClassName, the correct ClassCastException
259
// will be thrown by the cast.
260
return type.compareTo(((ClassName)obj).type);
261     }
262
263     /**
264      * Compares its two arguments for order. Returns a negative integer,
265      * zero, or a positive integer as the first argument is less than, equal
266      * to, or greater than the second.<p>
267      *
268      * @param o1 the first object to be compared.
269      * @param o2 the second object to be compared.
270      * @return a negative integer, zero, or a positive integer as the
271      * first argument is less than, equal to, or greater than the
272      * second.
273      * @throws ClassCastException if the arguments' types prevent them from
274      * being compared by this Comparator.
275      */

276     public int compare(Object JavaDoc o1, Object JavaDoc o2) {
277         return ((ClassName)o1).compareTo(o2);
278     }
279
280     public int hashCode() {
281         return type.hashCode();
282     }
283
284     public String JavaDoc toString() {
285         return getExternalName();
286     }
287
288     // Called from synchronization block, do not call out!
289
private String JavaDoc externalizeClassName() {
290         StringBuffer JavaDoc sb = new StringBuffer JavaDoc(type);
291     int arrays = 0;
292     boolean atBeginning = true;
293     int length = sb.length();
294     for (int i = 0; i < length; i++) {
295         char ch = sb.charAt(i);
296         switch (ch) {
297           case '[':
298         if (atBeginning)
299             arrays++;
300         break;
301
302           case '/':
303           case '$':
304         sb.setCharAt(i, '.');
305         atBeginning = false;
306         break;
307
308           default:
309         atBeginning = false;
310         }
311     }
312
313     if (arrays > 0) {
314         sb.delete(0, arrays);
315         for (int i = 0; i < arrays; i++)
316           sb.append("[]");
317     }
318
319         return sb.toString();
320     }
321     
322     /**
323      * Empties the cache -- used by unit tests.
324      */

325     static void clearCache() {
326         cache.clear();
327     }
328
329     /**
330      * Suppress multiple instances of the same type, as well as any
331      * immutability attacks (unlikely as that might be). For more information
332      * on this technique, check out Effective Java, Item 57, by Josh Bloch.
333      */

334     private Object JavaDoc readResolve() throws ObjectStreamException JavaDoc {
335         return getClassName(internalName);
336     }
337 }
338
Popular Tags