View Javadoc

1   package serp.bytecode;
2   
3   import java.util.*;
4   
5   /***
6    * Caching and conversion of names in both internal and external form.
7    *
8    * @author Abe White
9    */
10  public class NameCache {
11      static final Object[][] _codes = new Object[][] {
12          { byte.class, "B" },
13          { char.class, "C" },
14          { double.class, "D" },
15          { float.class, "F" },
16          { int.class, "I" },
17          { long.class, "J" },
18          { short.class, "S" },
19          { boolean.class, "Z" },
20          { void.class, "V" },
21      };
22  
23      // caches of internal and external forms of strings
24      private final Map _internal = new HashMap();
25      private final Map _internalDescriptor = new HashMap();
26      private final Map _external = new HashMap();
27      private final Map _externalHuman = new HashMap();
28  
29      /***
30       * Converts the given class name to its internal form.
31       *
32       * @param className the name to convert
33       * @param descriptor true if the name is to be used for a descriptor
34       * section -- the difference seems to be that for
35       * descriptors, non-primitives are prefixed with 'L' and ended with ';'
36       */
37      public String getInternalForm(String className, boolean descriptor) {
38          if (className == null || className.length() == 0)
39              return className;
40  
41          Map cache = (descriptor) ? _internalDescriptor : _internal;
42          String cached = (String) cache.get(className);
43          if (cached != null)
44              return cached;
45  
46          String ret = getInternalFormInternal(className, descriptor);
47          cache.put(className, ret);
48          return ret;
49      }
50  
51      /***
52       * @see #getInternalForm
53       */
54      private String getInternalFormInternal(String cls, boolean descriptor) {
55          // handle array types, whether already in internal form or not
56          StringBuffer prefix = new StringBuffer();
57          while (true) {
58              if (cls.endsWith("[]")) {
59                  prefix.append("[");
60                  cls = cls.substring(0, cls.length() - 2);
61              } else if (cls.startsWith("[")) {
62                  prefix.append("[");
63                  cls = cls.substring(1);
64              } else
65                  break;
66          }
67  
68          // handle primitive array types
69          for (int i = 0; i < _codes.length; i++)
70              if (cls.equals(_codes[i][1].toString()) 
71                  || cls.equals(_codes[i][0].toString()))
72                  return prefix.append(_codes[i][1]).toString();
73  
74          // if in descriptor form, strip leading 'L' and trailing ';'
75          if (cls.startsWith("L") && cls.endsWith(";"))
76              cls = cls.substring(1, cls.length() - 1);
77  
78          // non-primitive; make sure we don't prefix method descriptors with 'L'
79          cls = cls.replace('.', '/');
80          if ((descriptor || prefix.length() > 0) && cls.charAt(0) != '(')
81              return prefix.append("L").append(cls).append(";").toString();
82          return prefix.append(cls).toString();
83      }
84  
85      /***
86       * Given the internal name of the class, return the 'normal' java name.
87       *
88       * @param internalName the internal name being used
89       * @param humanReadable if the returned name should be in human-readable
90       * form, rather than a form suitable for a
91       * {@link Class#forName} call -- the difference
92       * lies in the handling of arrays
93       */
94      public String getExternalForm(String internalName, boolean humanReadable) {
95          if (internalName == null || internalName.length() == 0)
96              return internalName;
97  
98          Map cache = (humanReadable) ? _externalHuman : _external;
99          String cached = (String) cache.get(internalName);
100         if (cached != null)
101             return cached;
102 
103         String ret = getExternalFormInternal(internalName, humanReadable);
104         cache.put(internalName, ret);
105         return ret;
106     }
107 
108     /***
109      * @see #getExternalForm
110      */
111     private String getExternalFormInternal(String intern, 
112         boolean humanReadable) {
113         if (!humanReadable) {
114             // check against primitives
115             for (int i = 0; i < _codes.length; i++) {
116                 if (intern.equals(_codes[i][1].toString()))
117                     return _codes[i][0].toString();
118                 if (intern.equals(_codes[i][0].toString()))
119                     return intern;
120             }
121             intern = getInternalForm(intern, false);
122             return intern.replace('/', '.');
123         }
124 
125         // handle arrays
126         StringBuffer postfix = new StringBuffer(2);
127         while (intern.startsWith("[")) {
128             intern = intern.substring(1);
129             postfix.append("[]");
130         }
131 
132         // strip off leading 'L' and trailing ';'
133         if (intern.endsWith(";"))
134             intern = intern.substring(1, intern.length() - 1);
135 
136         // check primitives
137         for (int i = 0; i < _codes.length; i++)
138             if (intern.equals(_codes[i][1].toString()))
139                 return _codes[i][0].toString() + postfix;
140         return intern.replace('/', '.') + postfix;
141     }
142 
143     /***
144      * Construct a method descriptor from the given return and parameter
145      * types, which will be converted to internal form.
146      */
147     public String getDescriptor(String returnType, String[] paramTypes) {
148         StringBuffer buf = new StringBuffer();
149         buf.append("(");
150         if (paramTypes != null) {
151             for (int i = 0; i < paramTypes.length; i++) {
152                 if (paramTypes[i] == null) 
153                     throw new NullPointerException("paramTypes[" + i 
154                         + "] = null");
155                 buf.append(getInternalForm(paramTypes[i], true));
156             }
157         }
158 
159         buf.append(")");
160         if (returnType == null)
161             throw new NullPointerException("returnType = null");
162 
163         buf.append(getInternalForm(returnType, true));
164         return buf.toString();
165     }
166 
167     /***
168      * Return the return type, in internal form, for the given method
169      * descriptor string.
170      */
171     public String getDescriptorReturnName(String descriptor) {
172         int index = descriptor.indexOf(')');
173         if (index == -1)
174             return "";
175         return descriptor.substring(descriptor.indexOf(')') + 1);
176     }
177 
178     /***
179      * Return the parameter types, in internal form, for the given method
180      * descriptor string.
181      */
182     public String[] getDescriptorParamNames(String descriptor) {
183         if (descriptor == null || descriptor.length() == 0)
184             return new String[0];
185 
186         int index = descriptor.indexOf(')');
187         if (index == -1)
188             return new String[0];
189 
190         // get rid of the parens and the return type
191         descriptor = descriptor.substring(1, index);
192 
193         // break the param string into individual params
194         List tokens = new LinkedList();
195         while (descriptor.length() > 0) {
196             index = 0;
197 
198             // skip the '[' up to the first letter code
199             while (!Character.isLetter(descriptor.charAt(index)))
200                 index++;
201 
202             // non-primitives always start with 'L' and end with ';'
203             if (descriptor.charAt(index) == 'L')
204                 index = descriptor.indexOf(';');
205 
206             tokens.add(descriptor.substring(0, index + 1));
207             descriptor = descriptor.substring(index + 1);
208         }
209         return (String[]) tokens.toArray(new String[tokens.size()]);
210     }
211 
212     /***
213      * Return the component type name for the given array type, or null
214      * if the given string does not represent an array type name. The name
215      * given should be in proper {@link Class#forName} form.
216      */
217     public String getComponentName(String name) {
218         if (name == null || !name.startsWith("["))
219             return null;
220 
221         name = name.substring(1);
222         if (!name.startsWith("[") && name.endsWith(";"))
223             name = name.substring(1, name.length() - 1);
224 
225         // will convert primitive type codes to names
226         return getExternalForm(name, false);
227     }
228 
229     /***
230      * Clear the cache.
231      */
232     public void clear() {
233         _internal.clear();
234         _internalDescriptor.clear();
235         _external.clear();
236         _externalHuman.clear();
237     }
238 }