package net.sourceforge.retroweaver.translator;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import net.sourceforge.retroweaver.RetroWeaverException;
import org.objectweb.asm.Type;
/**
* Substitutes JDK 1.5 classes for their mirrors.
*
* Out of the box, Retroweaver supports:
* all new language features and their associated runtime
* (autoboxing, generics, annotations, extended for loop, static import, varargs)
* java.util.concurrent
* TODO: full list of what is supported
*
* Additional runtime support can be added to Retroweaver by writing a mirror class and adding it
* to the class path. A mirror class can be one of two types: class or methods mirror.
*
* 1) Class mirror: Retroweaver replaces every single reference to the JDK 1.5 class directly with the mirror
* class.
*
* 2) Methods mirror: Retroweaver replaces calls to the JDK 1.5 class with static calls to the mirror
* class. Mirrors for instance calls are different from mirrors from static calls in that Retroweaver
* adds the JDK 1.5 object as the first parameter to the mirror. For example, a static call to
* java.lang.Integer.valueOf( int ) is replaced directly by net.sourceforge.retroweaver.runtime.java.lang.Integer_.valueOf( int ).
* However, an instance call to Class.getAnnotations() is replaced with a static call to
* net.sourceforge.retroweaver.runtime.java.lang.Class_.getAnnotations( Class ). Notice how the receiver (Class) is
* added as the first parameter to the mirror call.
*
* In order for Retroweaver to find your mirror classes, you must place them in Retroweaver's class path and
* register their mirror namespace. A mirror namespace defines the prefix of the package being
* replaced and the prefix of the package doing the replacing. For example, the namespace for the
* java.util.concurrent backport mirrors is "java.util.concurrent/edu.emory.mathcs.backport.java.util.concurrent".
* Retroweaver has a default mirror namespace "/net.sourceforge.retroweaver.runtime".
* As an example, java.lang.annotation.Annotation is replaced with
* net.sourceforge.retroweaver.runtime.java.lang.annotation.Annotation. In order to differentiate class mirrors
* from methods mirrors, a methods mirror must have exactly one trailing underscore in its class name:
* for example, net.sourceforge.retroweaver.runtime.java.lang.Class_
*
*/
public class NameTranslator {
private static final NameSpace defaultNamespace = new NameSpace("", "net.sourceforge.retroweaver.runtime");
private static final NameSpace concurrentNamespace = new NameSpace("java.util.concurrent", "edu.emory.mathcs.backport.java.util.concurrent");
private static final NameSpace harmonyNamespace = new NameSpace("", "net.sourceforge.retroweaver.harmony.runtime");
/**
* Only select classes from the java.util concurrent package can be mirrored
*/
private static final String[] javaUtilClasses = new String[] {
"AbstractQueue",
"ArrayDeque",
"Deque",
"NavigableMap",
"NavigableSet",
"PriorityQueue",
"Queue"
};
private static final Mirror noMirror = new NoMirror();
private final List<NameSpace> namespaces = new LinkedList<NameSpace>();
private final Map<String, Mirror> mirrors = new HashMap<String, Mirror>();
private static final NameTranslator generalTranslator ;
public static final NameTranslator getGeneralTranslator() {
return generalTranslator;
}
private static final NameTranslator harmonyTranslator;
public static final NameTranslator getHarmonyTranslator() {
return harmonyTranslator;
}
private static final NameTranslator stringBuilderTranslator;
public static final NameTranslator getStringBuilderTranslator() {
return stringBuilderTranslator;
}
private String name;
private NameTranslator(String name) {
// private constructor
this.name = name;
}
static {
generalTranslator = new NameTranslator("general");
generalTranslator.addNameSpace(defaultNamespace);
generalTranslator.addNameSpace(concurrentNamespace);
for (String s: javaUtilClasses) {
NameSpace n = new NameSpace("java.util." + s, "edu.emory.mathcs.backport.java.util." + s);
generalTranslator.addNameSpace(n);
}
harmonyTranslator = new NameTranslator("harmony");
harmonyTranslator.addNameSpace(harmonyNamespace);
// special rule around StringBuilder
stringBuilderTranslator = new NameTranslator("StringBuilder");
stringBuilderTranslator.mirrors.put("java/lang/StringBuilder", new ClassMirror(StringBuffer.class));
}
/**
* Adds a new runtime subsystem for the name translation. For instance
* the concurrency backport translation is done with the NameSpace
* "java.util.concurrent", "edu.emory.mathcs.backport.java.util.concurrent"
*/
public void addNameSpace(NameSpace nameSpace) {
namespaces.add(nameSpace);
}
/**
* Returns either a class or methods mirror
* Returns noMirror if there is no match
*/
protected Mirror getMirror(final String class_) {
if (class_ == null) {
return noMirror;
}
// See if we can find an existing mirror
final Mirror cachedMirror = mirrors.get(class_);
if (cachedMirror != null) {
return cachedMirror;
}
// Perform the lookup (on both class and methods if necessary)
for (NameSpace n : namespaces) {
String mirrorClass = n.getMirrorClassName(class_);
if (mirrorClass == null) {
continue;
}
mirrorClass = mirrorClass.replace('/', '.');
// Attempt class mirror first
try {
final Class clazz = Class.forName(mirrorClass);
final Mirror mirror = new ClassMirror(clazz);
mirrors.put(class_, mirror);
return mirror;
} catch (ClassNotFoundException e) { // NOPMD by xlv
}
// Attempt methods mirror
mirrorClass += '_';
try {
final Class clazz = Class.forName(mirrorClass);
final Mirror mirror = new MethodsMirror(clazz);
mirrors.put(class_, mirror);
return mirror;
} catch (ClassNotFoundException e) { // NOPMD by xlv
}
}
// No matches in any of the namespaces
mirrors.put(class_, noMirror);
return noMirror;
}
/**
* Translate an id or a method signature into its retroweaver runtime or
* concurrent backport equivalent.
*
* @param name The <code>String</code> to translate.
*
* @return the translated name
*/
protected String translate(final String name) {
if (name == null) {
return null;
}
final StringBuffer buffer = new StringBuffer();
translate(false, name, buffer, 0, name.length());
return buffer.toString();
}
/**
* Translates the name only if it has a mirror.
*/
private String getMirrorTranslation(final String name) {
final Mirror mirror = getMirror(name);
return mirror.exists() ? mirror.getTranslatedName() : name;
}
/**
* Translates the name only if it represents a fully mirrored class.
*/
public String getClassMirrorTranslation(final String name) {
final Mirror mirror = getMirror(name);
return mirror.isClassMirror() ? mirror.getTranslatedName() : name;
}
/**
* Translates the name only if it represents a fully mirrored class.
*/
public String getClassMirrorTranslationDescriptor(final String name) {
if (name == null) {
return null;
}
final StringBuffer buffer = new StringBuffer();
translate(true, name, buffer, 0, name.length());
return buffer.toString();
}
private void translate(final boolean classMirrorsOnly, final String in, final StringBuffer out, final int start, final int end) {
if (start >= end) {
return;
}
final char firstChar = in.charAt(start);
switch (firstChar) {
case 'Z': // boolean
case 'B': // byte
case 'C': // char
case 'S': // short
case 'I': // int
case 'J': // long
case 'F': // float
case 'D': // double
case '[': // type[]
case 'V': // void
out.append(firstChar);
translate(classMirrorsOnly, in, out, start + 1, end);
break;
case 'L': // L fully-qualified-class;
final int endName = in.indexOf(';', start + 1);
if (endName == -1) {
// false positive: it's an id, translate the entire string
final String name = in.substring(start, end);
final String newName = classMirrorsOnly?getClassMirrorTranslation(name):getMirrorTranslation(name);
out.append(newName);
} else {
final String className = in.substring(start + 1, endName);
final String newClassName = classMirrorsOnly?getClassMirrorTranslation(className):getMirrorTranslation(className);
out.append('L').append(newClassName).append(';');
translate(classMirrorsOnly, in, out, endName + 1, end);
}
break;
case '(': // ( arg-types ) ret-type
final int endArgs = in.indexOf(')', start + 1);
if (endArgs == -1) {
throw new RetroWeaverException("Class name parsing error: missing ')' in " + in);
}
out.append('(');
if (endArgs != start + 1) {
translate(classMirrorsOnly, in, out, start + 1, endArgs);
}
out.append(')');
translate(classMirrorsOnly, in, out, endArgs + 1, end);
break;
default:
// translate the entire string
final String name = in.substring(start, end);
final String newName = classMirrorsOnly?getClassMirrorTranslation(name):getMirrorTranslation(name);
out.append(newName);
}
}
/**
* Translates a descriptor, specifically. Only translates names in the
* descriptor, if they are represented by class mirrors.
*
*/
protected String translateMethodDescriptor(final String descriptor) {
Type[] argTypes = Type.getArgumentTypes(descriptor);
for (int i = 0; i < argTypes.length; ++i) {
argTypes[i] = getMirrorType(argTypes[i]);
}
final Type returnType = getMirrorType(Type.getReturnType(descriptor));
return Type.getMethodDescriptor(returnType, argTypes);
}
/**
* Translates a simple type descriptor, specifically. Only translates names in the
* descriptor, if they are represented by class mirrors.
*
*/
protected String translateDescriptor(final String descriptor) {
Type type = Type.getType(descriptor);
type = getMirrorType(type);
return type.getDescriptor();
}
private Type getMirrorType(final Type type) {
int numDimensions = 0;
final Type basicType;
if (type.getSort() == Type.ARRAY) {
numDimensions = type.getDimensions();
basicType = type.getElementType();
} else {
basicType = type;
}
if (basicType.getSort() != Type.OBJECT) {
return type;
}
final Mirror mirror = getMirror(basicType.getInternalName());
if (mirror.isClassMirror()) {
final StringBuilder name = new StringBuilder();
for (int i = 0; i < numDimensions; ++i) {
name.append('[');
}
name.append('L').append(mirror.getTranslatedName()).append(';');
return Type.getType(name.toString());
}
return type;
}
}