001
002 package net.sourceforge.retroweaver.translator;
003
004 import java.util.HashMap;
005 import java.util.LinkedList;
006 import java.util.List;
007 import java.util.Map;
008
009 import net.sourceforge.retroweaver.RetroWeaverException;
010
011 import org.objectweb.asm.Type;
012
013 /**
014 * Substitutes JDK 1.5 classes for their mirrors.
015 *
016 * Out of the box, Retroweaver supports:
017 * all new language features and their associated runtime
018 * (autoboxing, generics, annotations, extended for loop, static import, varargs)
019 * java.util.concurrent
020 * TODO: full list of what is supported
021 *
022 * Additional runtime support can be added to Retroweaver by writing a mirror class and adding it
023 * to the class path. A mirror class can be one of two types: class or methods mirror.
024 *
025 * 1) Class mirror: Retroweaver replaces every single reference to the JDK 1.5 class directly with the mirror
026 * class.
027 *
028 * 2) Methods mirror: Retroweaver replaces calls to the JDK 1.5 class with static calls to the mirror
029 * class. Mirrors for instance calls are different from mirrors from static calls in that Retroweaver
030 * adds the JDK 1.5 object as the first parameter to the mirror. For example, a static call to
031 * java.lang.Integer.valueOf( int ) is replaced directly by net.sourceforge.retroweaver.runtime.java.lang.Integer_.valueOf( int ).
032 * However, an instance call to Class.getAnnotations() is replaced with a static call to
033 * net.sourceforge.retroweaver.runtime.java.lang.Class_.getAnnotations( Class ). Notice how the receiver (Class) is
034 * added as the first parameter to the mirror call.
035 *
036 * In order for Retroweaver to find your mirror classes, you must place them in Retroweaver's class path and
037 * register their mirror namespace. A mirror namespace defines the prefix of the package being
038 * replaced and the prefix of the package doing the replacing. For example, the namespace for the
039 * java.util.concurrent backport mirrors is "java.util.concurrent/edu.emory.mathcs.backport.java.util.concurrent".
040 * Retroweaver has a default mirror namespace "/net.sourceforge.retroweaver.runtime".
041 * As an example, java.lang.annotation.Annotation is replaced with
042 * net.sourceforge.retroweaver.runtime.java.lang.annotation.Annotation. In order to differentiate class mirrors
043 * from methods mirrors, a methods mirror must have exactly one trailing underscore in its class name:
044 * for example, net.sourceforge.retroweaver.runtime.java.lang.Class_
045 *
046 */
047
048 public class NameTranslator {
049
050 private static final NameSpace defaultNamespace = new NameSpace("", "net.sourceforge.retroweaver.runtime");
051
052 private static final NameSpace concurrentNamespace = new NameSpace("java.util.concurrent", "edu.emory.mathcs.backport.java.util.concurrent");
053
054 /**
055 * Only select classes from the java.util concurrent package can be mirrored
056 */
057 private static final String[] javaUtilClasses = new String[] {
058 "AbstractQueue",
059 "ArrayDeque",
060 "Deque",
061 "NavigableMap",
062 "NavigableSet",
063 "PriorityQueue",
064 "Queue"
065 };
066
067 private static final Mirror noMirror = new NoMirror();
068
069 private final List<NameSpace> namespaces = new LinkedList<NameSpace>();
070
071 private final Map<String, Mirror> mirrors = new HashMap<String, Mirror>();
072
073 private static final NameTranslator generalTranslator ;
074
075 public static final NameTranslator getGeneralTranslator() {
076 return generalTranslator;
077 }
078
079 private static final NameTranslator stringBuilderTranslator;
080
081 public static final NameTranslator getStringBuilderTranslator() {
082 return stringBuilderTranslator;
083 }
084
085 private NameTranslator() {
086 // private constructor
087 }
088
089 static {
090 generalTranslator = new NameTranslator();
091 generalTranslator.addNameSpace(defaultNamespace);
092 generalTranslator.addNameSpace(concurrentNamespace);
093 for (String s: javaUtilClasses) {
094 NameSpace n = new NameSpace("java.util." + s, "edu.emory.mathcs.backport.java.util." + s);
095 generalTranslator.addNameSpace(n);
096 }
097
098 // special rule around StringBuilder
099 stringBuilderTranslator = new NameTranslator();
100 stringBuilderTranslator.mirrors.put("java/lang/StringBuilder", new ClassMirror(StringBuffer.class));
101 }
102
103 /**
104 * Adds a new runtime subsystem for the name translation. For instance
105 * the concurrency backport translation is done with the NameSpace
106 * "java.util.concurrent", "edu.emory.mathcs.backport.java.util.concurrent"
107 */
108 public void addNameSpace(NameSpace nameSpace) {
109 namespaces.add(nameSpace);
110 }
111
112 /**
113 * Returns either a class or methods mirror
114 * Returns noMirror if there is no match
115 */
116 protected Mirror getMirror(final String class_) {
117 if (class_ == null) {
118 return noMirror;
119 }
120
121 // See if we can find an existing mirror
122 final Mirror cachedMirror = mirrors.get(class_);
123
124 if (cachedMirror != null) {
125 return cachedMirror;
126 }
127
128 // Perform the lookup (on both class and methods if necessary)
129 for (NameSpace n : namespaces) {
130 String mirrorClass = n.getMirrorClassName(class_);
131
132 if (mirrorClass == null) {
133 continue;
134 }
135
136 mirrorClass = mirrorClass.replace('/', '.');
137
138 // Attempt class mirror first
139 try {
140 final Class clazz = Class.forName(mirrorClass);
141 final Mirror mirror = new ClassMirror(clazz);
142 mirrors.put(class_, mirror);
143 return mirror;
144 } catch (ClassNotFoundException e) { // NOPMD by xlv
145 }
146
147 // Attempt methods mirror
148 mirrorClass += '_';
149 try {
150 final Class clazz = Class.forName(mirrorClass);
151 final Mirror mirror = new MethodsMirror(clazz);
152 mirrors.put(class_, mirror);
153
154 return mirror;
155 } catch (ClassNotFoundException e) { // NOPMD by xlv
156 }
157 }
158
159 // No matches in any of the namespaces
160 mirrors.put(class_, noMirror);
161 return noMirror;
162 }
163
164 /**
165 * Translate an id or a method signature into its retroweaver runtime or
166 * concurrent backport equivalent.
167 *
168 * @param name The <code>String</code> to translate.
169 *
170 * @return the translated name
171 */
172 protected String translate(final String name) {
173 if (name == null) {
174 return null;
175 }
176
177 final StringBuffer buffer = new StringBuffer();
178 translate(false, name, buffer, 0, name.length());
179
180 return buffer.toString();
181 }
182
183 /**
184 * Translates the name only if it has a mirror.
185 */
186 private String getMirrorTranslation(final String name) {
187 final Mirror mirror = getMirror(name);
188 return mirror.exists() ? mirror.getTranslatedName() : name;
189 }
190
191 /**
192 * Translates the name only if it represents a fully mirrored class.
193 */
194 public String getClassMirrorTranslation(final String name) {
195 final Mirror mirror = getMirror(name);
196 return mirror.isClassMirror() ? mirror.getTranslatedName() : name;
197 }
198
199 /**
200 * Translates the name only if it represents a fully mirrored class.
201 */
202 public String getClassMirrorTranslationDescriptor(final String name) {
203 if (name == null) {
204 return null;
205 }
206
207 final StringBuffer buffer = new StringBuffer();
208 translate(true, name, buffer, 0, name.length());
209
210 return buffer.toString();
211 }
212
213 private void translate(final boolean classMirrorsOnly, final String in, final StringBuffer out, final int start, final int end) {
214 if (start >= end) {
215 return;
216 }
217
218 final char firstChar = in.charAt(start);
219
220 switch (firstChar) {
221 case 'Z': // boolean
222 case 'B': // byte
223 case 'C': // char
224 case 'S': // short
225 case 'I': // int
226 case 'J': // long
227 case 'F': // float
228 case 'D': // double
229 case '[': // type[]
230 case 'V': // void
231 out.append(firstChar);
232 translate(classMirrorsOnly, in, out, start + 1, end);
233 break;
234 case 'L': // L fully-qualified-class;
235 final int endName = in.indexOf(';', start + 1);
236 if (endName == -1) {
237 // false positive: it's an id, translate the entire string
238 final String name = in.substring(start, end);
239 final String newName = classMirrorsOnly?getClassMirrorTranslation(name):getMirrorTranslation(name);
240 out.append(newName);
241 } else {
242 final String className = in.substring(start + 1, endName);
243 final String newClassName = classMirrorsOnly?getClassMirrorTranslation(className):getMirrorTranslation(className);
244
245 out.append('L').append(newClassName).append(';');
246 translate(classMirrorsOnly, in, out, endName + 1, end);
247 }
248 break;
249 case '(': // ( arg-types ) ret-type
250 final int endArgs = in.indexOf(')', start + 1);
251 if (endArgs == -1) {
252 throw new RetroWeaverException("Class name parsing error: missing ')' in " + in);
253 }
254
255 out.append('(');
256 if (endArgs != start + 1) {
257 translate(classMirrorsOnly, in, out, start + 1, endArgs);
258 }
259 out.append(')');
260 translate(classMirrorsOnly, in, out, endArgs + 1, end);
261 break;
262 default:
263 // translate the entire string
264 final String name = in.substring(start, end);
265 final String newName = classMirrorsOnly?getClassMirrorTranslation(name):getMirrorTranslation(name);
266 out.append(newName);
267 }
268 }
269
270
271 /**
272 * Translates a descriptor, specifically. Only translates names in the
273 * descriptor, if they are represented by class mirrors.
274 *
275 */
276 protected String translateMethodDescriptor(final String descriptor) {
277 Type[] argTypes = Type.getArgumentTypes(descriptor);
278
279 for (int i = 0; i < argTypes.length; ++i) {
280 argTypes[i] = getMirrorType(argTypes[i]);
281 }
282
283 final Type returnType = getMirrorType(Type.getReturnType(descriptor));
284
285 return Type.getMethodDescriptor(returnType, argTypes);
286 }
287
288 /**
289 * Translates a simple type descriptor, specifically. Only translates names in the
290 * descriptor, if they are represented by class mirrors.
291 *
292 */
293 protected String translateDescriptor(final String descriptor) {
294 Type type = Type.getType(descriptor);
295
296 type = getMirrorType(type);
297
298 return type.getDescriptor();
299 }
300
301 private Type getMirrorType(final Type type) {
302 int numDimensions = 0;
303 final Type basicType;
304
305 if (type.getSort() == Type.ARRAY) {
306 numDimensions = type.getDimensions();
307 basicType = type.getElementType();
308 } else {
309 basicType = type;
310 }
311
312 if (basicType.getSort() != Type.OBJECT) {
313 return type;
314 }
315
316 final Mirror mirror = getMirror(basicType.getInternalName());
317
318 if (mirror.isClassMirror()) {
319 final StringBuilder name = new StringBuilder();
320
321 for (int i = 0; i < numDimensions; ++i) {
322 name.append('[');
323 }
324 name.append('L').append(mirror.getTranslatedName()).append(';');
325
326 return Type.getType(name.toString());
327 }
328
329 return type;
330 }
331
332 }