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 }