1 8 package org.codehaus.aspectwerkz.annotation.expression; 9 10 import org.codehaus.aspectwerkz.annotation.expression.ast.ASTAnnotation; 11 import org.codehaus.aspectwerkz.annotation.expression.ast.ASTArray; 12 import org.codehaus.aspectwerkz.annotation.expression.ast.ASTBoolean; 13 import org.codehaus.aspectwerkz.annotation.expression.ast.ASTChar; 14 import org.codehaus.aspectwerkz.annotation.expression.ast.ASTFloat; 15 import org.codehaus.aspectwerkz.annotation.expression.ast.ASTHex; 16 import org.codehaus.aspectwerkz.annotation.expression.ast.ASTIdentifier; 17 import org.codehaus.aspectwerkz.annotation.expression.ast.ASTInteger; 18 import org.codehaus.aspectwerkz.annotation.expression.ast.ASTKeyValuePair; 19 import org.codehaus.aspectwerkz.annotation.expression.ast.ASTOct; 20 import org.codehaus.aspectwerkz.annotation.expression.ast.ASTRoot; 21 import org.codehaus.aspectwerkz.annotation.expression.ast.ASTString; 22 import org.codehaus.aspectwerkz.annotation.expression.ast.AnnotationParserVisitor; 23 import org.codehaus.aspectwerkz.annotation.expression.ast.SimpleNode; 24 import org.codehaus.aspectwerkz.annotation.expression.ast.AnnotationParser; 25 import org.codehaus.aspectwerkz.annotation.expression.ast.ParseException; 26 import org.codehaus.aspectwerkz.annotation.AnnotationElement; 27 import org.codehaus.aspectwerkz.annotation.AnnotationManager; 28 import org.codehaus.aspectwerkz.annotation.Annotation; 29 import org.codehaus.aspectwerkz.exception.WrappedRuntimeException; 30 import org.codehaus.aspectwerkz.util.Strings; 31 import org.objectweb.asm.Type; 32 33 import java.lang.reflect.Field ; 34 import java.lang.reflect.Method ; 35 import java.lang.reflect.Array ; 36 import java.util.Map ; 37 import java.util.HashMap ; 38 39 53 public class AnnotationVisitor implements AnnotationParserVisitor { 54 55 58 protected static final AnnotationParser PARSER = new AnnotationParser(System.in); 59 60 protected Map m_annotationElementValueHoldersByName; 61 62 65 protected Class m_annotationClass; 66 67 70 public AnnotationVisitor(final Map annotationElementValueHoldersByName, final Class annotationClass) { 71 m_annotationElementValueHoldersByName = annotationElementValueHoldersByName; 72 m_annotationClass = annotationClass; 73 } 74 75 83 public static void parse(final Map annotationElements, final String annotationRepresentation, 84 final Class annotationClass) { 85 try { 86 ASTRoot root = PARSER.parse(annotationRepresentation); 87 new AnnotationVisitor(annotationElements, annotationClass).visit(root, null); 88 } catch (ParseException e) { 89 throw new WrappedRuntimeException("cannot parse annotation [" + annotationRepresentation + "]", e); 90 } 91 } 92 93 public Object visit(SimpleNode node, Object data) { 94 return node.jjtGetChild(0).jjtAccept(this, data); 95 } 96 97 public Object visit(ASTRoot node, Object data) { 98 return node.jjtGetChild(0).jjtAccept(this, data); 99 } 100 101 public Object visit(ASTAnnotation node, Object data) { 102 int nr = node.jjtGetNumChildren(); 103 104 if (nr == 1 && !(node.jjtGetChild(0) instanceof ASTKeyValuePair)) { 105 Object value = node.jjtGetChild(0).jjtAccept(this, data); 107 108 if(!(node.jjtGetChild(0) instanceof ASTAnnotation)) { m_annotationElementValueHoldersByName.put("value", 110 new AnnotationElement("value", value)); 111 } 112 } else { 113 for (int i = 0; i < nr; i++) { 114 node.jjtGetChild(i).jjtAccept(this, data); 115 } 116 } 117 return null; 118 } 119 120 public Object visit(ASTKeyValuePair node, Object data) { 121 String elementName = node.getKey(); 122 123 MethodInfo elementMethod = getMethodInfo(elementName); 125 126 if (node.jjtGetChild(0) instanceof ASTAnnotation) { 128 Map nestedAnnotationElementValueHoldersByName = new HashMap (); 129 AnnotationVisitor nestedAnnotationVisitor = new AnnotationVisitor( 130 nestedAnnotationElementValueHoldersByName, 131 elementMethod.elementType 132 ); 133 nestedAnnotationVisitor.visit((ASTAnnotation)node.jjtGetChild(0), data); 134 m_annotationElementValueHoldersByName.put(elementName, 135 new AnnotationElement(elementName, 136 AnnotationManager.instantiateNestedAnnotation(elementMethod.elementType, nestedAnnotationElementValueHoldersByName))); 137 } else { 138 Object typedValue = node.jjtGetChild(0).jjtAccept(this, elementMethod); 139 m_annotationElementValueHoldersByName.put(elementName, 140 new AnnotationElement(elementName, typedValue)); 141 } 142 return null; 143 } 144 145 public Object visit(ASTArray node, Object data) { 146 MethodInfo methodInfo = (MethodInfo) data; 147 Class elementType = methodInfo.elementType; 148 if (!elementType.isArray()) { 149 throw new RuntimeException ( 150 "type for element [" 151 + methodInfo.elementMethod.getName() 152 + "] is not of type array" 153 ); 154 } 155 Class componentType = elementType.getComponentType(); 156 if (componentType.isArray()) { 157 throw new UnsupportedOperationException ( 158 "multidimensional arrays are not supported for element type, was required method [" 159 + methodInfo.elementMethod.getName() 160 + "]" 161 ); 162 } 163 return createTypedArray(node, data, node.jjtGetNumChildren(), componentType); 164 } 165 166 public Object visit(ASTIdentifier node, Object data) { 167 String identifier = node.getValue(); 168 if (identifier.endsWith(".class")) { 169 return handleClassIdentifier(identifier); 170 } else if (isJavaReferenceType(identifier)) { 171 return handleReferenceIdentifier(identifier); 172 } else { 173 throw new RuntimeException ("unsupported format for java type or reference [" + identifier + "]"); 174 } 175 } 176 177 public Object visit(ASTBoolean node, Object data) { 178 return Boolean.valueOf(node.getValue()); 179 } 180 181 public Object visit(ASTChar node, Object data) { 182 return new Character (node.getValue().charAt(0)); 183 } 184 185 public Object visit(ASTString node, Object data) { 186 if (node.getValue().length() >= 2) { 188 String escaped = node.getValue().substring(1, node.getValue().length() - 1); 189 return Strings.replaceSubString(escaped, "\\\"", "\""); 190 } else { 191 return node.getValue(); 192 } 193 } 194 195 public Object visit(ASTInteger node, Object data) { 196 String value = node.getValue(); 197 char lastChar = value.charAt(value.length() - 1); 198 if ((lastChar == 'L') || (lastChar == 'l')) { 199 return new Long (value.substring(0, value.length() - 1)); 200 } else if (value.length() > 9) { 201 return new Long (value); 202 } else { 203 return new Integer (value); 204 } 205 } 206 207 public Object visit(ASTFloat node, Object data) { 208 String value = node.getValue(); 209 char lastChar = value.charAt(value.length() - 1); 210 if ((lastChar == 'D') || (lastChar == 'd')) { 211 return new Double (value.substring(0, value.length() - 1)); 212 } else if ((lastChar == 'F') || (lastChar == 'f')) { 213 return new Float (value.substring(0, value.length() - 1)); 214 } else { 215 return new Double (value); 216 } 217 } 218 219 public Object visit(ASTHex node, Object data) { 220 throw new UnsupportedOperationException ("hex numbers not yet supported"); 221 } 222 223 public Object visit(ASTOct node, Object data) { 224 throw new UnsupportedOperationException ("octal numbers not yet supported"); 225 } 226 227 235 private MethodInfo getMethodInfo(final String elementName) { 236 StringBuffer javaBeanMethodPostfix = new StringBuffer (); 237 javaBeanMethodPostfix.append(elementName.substring(0, 1).toUpperCase()); 238 if (elementName.length() > 1) { 239 javaBeanMethodPostfix.append(elementName.substring(1)); 240 } 241 242 MethodInfo methodInfo = new MethodInfo(); 243 Method [] methods = m_annotationClass.getDeclaredMethods(); 244 for (int i = 0; i < methods.length; i++) { 246 Method elementMethod = methods[i]; 247 if (elementMethod.getName().equals(elementName)) { 248 methodInfo.elementMethod = elementMethod; 249 methodInfo.elementType = elementMethod.getReturnType(); 250 break; 251 } 252 } 253 if (methodInfo.elementMethod == null) { 254 throw new RuntimeException ( 255 "method for the annotation element [" 256 + elementName 257 + "] can not be found in annotation interface [" 258 + m_annotationClass.getName() 259 + "]" 260 ); 261 } 262 return methodInfo; 263 } 264 265 private boolean isJavaReferenceType(final String valueAsString) { 266 int first = valueAsString.indexOf('.'); 267 int last = valueAsString.lastIndexOf('.'); 268 int comma = valueAsString.indexOf(','); 269 if ((first > 0) && (last > 0) && (first != last) && (comma < 0)) { 270 return true; 271 } else { 272 return false; 273 } 274 } 275 276 private Object createTypedArray(final ASTArray node, 277 final Object data, 278 final int nrOfElements, 279 final Class componentType) { 280 if (componentType.equals(String .class)) { 281 String [] array = new String [nrOfElements]; 282 for (int i = 0; i < nrOfElements; i++) { 283 String value = (String ) node.jjtGetChild(i).jjtAccept(this, data); 284 array[i] = value; 285 } 286 return array; 287 } else if (componentType.equals(long.class)) { 288 long[] array = new long[nrOfElements]; 289 for (int i = 0; i < nrOfElements; i++) { 290 array[i] = ((Long ) node.jjtGetChild(i).jjtAccept(this, data)).longValue(); 291 } 292 return array; 293 } else if (componentType.equals(int.class)) { 294 int[] array = new int[nrOfElements]; 295 for (int i = 0; i < nrOfElements; i++) { 296 array[i] = ((Integer ) node.jjtGetChild(i).jjtAccept(this, data)).intValue(); 297 } 298 return array; 299 } else if (componentType.equals(short.class)) { 300 short[] array = new short[nrOfElements]; 301 for (int i = 0; i < nrOfElements; i++) { 302 array[i] = ((Short ) node.jjtGetChild(i).jjtAccept(this, data)).shortValue(); 303 } 304 return array; 305 } else if (componentType.equals(double.class)) { 306 double[] array = new double[nrOfElements]; 307 for (int i = 0; i < nrOfElements; i++) { 308 array[i] = ((Double ) node.jjtGetChild(i).jjtAccept(this, data)).doubleValue(); 309 } 310 return array; 311 } else if (componentType.equals(float.class)) { 312 float[] array = new float[nrOfElements]; 313 for (int i = 0; i < nrOfElements; i++) { 314 array[i] = ((Float ) node.jjtGetChild(i).jjtAccept(this, data)).floatValue(); 315 } 316 return array; 317 } else if (componentType.equals(byte.class)) { 318 byte[] array = new byte[nrOfElements]; 319 for (int i = 0; i < nrOfElements; i++) { 320 array[i] = ((Byte ) node.jjtGetChild(i).jjtAccept(this, data)).byteValue(); 321 } 322 return array; 323 } else if (componentType.equals(char.class)) { 324 char[] array = new char[nrOfElements]; 325 for (int i = 0; i < nrOfElements; i++) { 326 array[i] = ((Character ) node.jjtGetChild(i).jjtAccept(this, data)).charValue(); 327 } 328 return array; 329 } else if (componentType.equals(boolean.class)) { 330 boolean[] array = new boolean[nrOfElements]; 331 for (int i = 0; i < nrOfElements; i++) { 332 array[i] = ((Boolean ) node.jjtGetChild(i).jjtAccept(this, data)).booleanValue(); 333 } 334 return array; 335 } else if (componentType.equals(Class .class)) { 336 AnnotationElement.LazyClass[] array = new AnnotationElement.LazyClass[nrOfElements]; 337 for (int i = 0; i < nrOfElements; i++) { 338 array[i] = (AnnotationElement.LazyClass) node.jjtGetChild(i).jjtAccept(this, data); 339 } 340 return array; 341 } else { 342 if (nrOfElements > 1 && node.jjtGetChild(0) instanceof ASTAnnotation) { 343 Object [] nestedTyped = (Object [])Array.newInstance(componentType, nrOfElements); 345 for (int i = 0; i < nrOfElements; i++) { 346 Map nestedAnnotationElementValueHoldersByName = new HashMap (); 347 AnnotationVisitor nestedAnnotationVisitor = new AnnotationVisitor( 348 nestedAnnotationElementValueHoldersByName, 349 componentType 350 ); 351 nestedAnnotationVisitor.visit((ASTAnnotation)node.jjtGetChild(i), data); 352 nestedTyped[i] = AnnotationManager.instantiateNestedAnnotation(componentType, nestedAnnotationElementValueHoldersByName); 353 } 354 return nestedTyped; 355 } else { 356 Object [] array = new Object [nrOfElements]; 358 for (int i = 0; i < nrOfElements; i++) { 359 array[i] = node.jjtGetChild(i).jjtAccept(this, data); 360 } 361 return array; 362 } 363 } 364 } 365 366 private Object handleClassIdentifier(String identifier) { 367 int index = identifier.lastIndexOf('.'); 368 String className = identifier.substring(0, index); 369 370 int dimension = 0; 371 String componentClassName = className; 372 while (componentClassName.endsWith("[]")) { 373 dimension++; 374 componentClassName = componentClassName.substring(0, componentClassName.length()-2); 375 } 376 377 Class componentClass = null; 378 boolean isComponentPrimitive = true; 379 if (componentClassName.equals("long")) { 380 componentClass = long.class; 381 } else if (componentClassName.equals("int")) { 382 componentClass = int.class; 383 } else if (componentClassName.equals("short")) { 384 componentClass = short.class; 385 } else if (componentClassName.equals("double")) { 386 componentClass = double.class; 387 } else if (componentClassName.equals("float")) { 388 componentClass = float.class; 389 } else if (componentClassName.equals("byte")) { 390 componentClass = byte.class; 391 } else if (componentClassName.equals("char")) { 392 componentClass = char.class; 393 } else if (componentClassName.equals("boolean")) { 394 componentClass = boolean.class; 395 } else { 396 isComponentPrimitive = false; 397 try { 398 componentClass = Class.forName(componentClassName, false, m_annotationClass.getClassLoader()); 399 } catch (ClassNotFoundException e) { 400 throw new RuntimeException ("could not load class [" + className + "] due to: " + e.toString()); 401 } 402 } 403 404 if (isComponentPrimitive) { 406 if (dimension <= 0) { 407 return componentClass; 408 } else { 409 return Array.newInstance(componentClass, dimension); 410 } 411 } else { 412 String componentType = Type.getType(componentClass).getDescriptor(); 413 for (int i = 0; i < dimension; i++) { 414 componentType = "[" + componentType; 415 } 416 Type type = Type.getType(componentType); 417 return new AnnotationElement.LazyClass(type.getClassName()); 418 } 419 } 420 421 private Object handleReferenceIdentifier(String identifier) { 422 int index = identifier.lastIndexOf('.'); 423 String className = identifier.substring(0, index); 424 String fieldName = identifier.substring(index + 1, identifier.length()); 425 try { 426 Class clazz = Class.forName(className, false, m_annotationClass.getClassLoader()); 428 Field field = clazz.getDeclaredField(fieldName); 429 return field.get(null); 430 } catch (Exception e) { 431 throw new RuntimeException ( 432 "could not access reference field [" + identifier + "] due to: " + e.toString() 433 ); 434 } 435 } 436 437 440 private static class MethodInfo { 441 442 public Method elementMethod; 443 444 public Class elementType; 445 } 446 } | Popular Tags |