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
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
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
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
75 if (cls.startsWith("L") && cls.endsWith(";"))
76 cls = cls.substring(1, cls.length() - 1);
77
78
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
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
126 StringBuffer postfix = new StringBuffer(2);
127 while (intern.startsWith("[")) {
128 intern = intern.substring(1);
129 postfix.append("[]");
130 }
131
132
133 if (intern.endsWith(";"))
134 intern = intern.substring(1, intern.length() - 1);
135
136
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
191 descriptor = descriptor.substring(1, index);
192
193
194 List tokens = new LinkedList();
195 while (descriptor.length() > 0) {
196 index = 0;
197
198
199 while (!Character.isLetter(descriptor.charAt(index)))
200 index++;
201
202
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
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 }