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    }