001: /**************************************************************************************
002: * Copyright (c) Jonas Bon?r, Alexandre Vasseur. All rights reserved. *
003: * http://aspectwerkz.codehaus.org *
004: * ---------------------------------------------------------------------------------- *
005: * The software in this package is published under the terms of the LGPL license *
006: * a copy of which has been included with this distribution in the license.txt file. *
007: **************************************************************************************/package org.codehaus.aspectwerkz.annotation;
008:
009: import org.codehaus.aspectwerkz.annotation.expression.AnnotationVisitor;
010: import org.codehaus.aspectwerkz.util.Strings;
011:
012: import java.lang.reflect.InvocationHandler;
013: import java.lang.reflect.Method;
014: import java.io.Serializable;
015: import java.util.Map;
016: import java.util.HashMap;
017: import java.util.Iterator;
018:
019: /**
020: * A Java 1.3 / 1.4 strongly typed Annotation handler.
021: * This proxy handler gets serialized alongside the annotationInfo within the AnnotationC compiled class.
022: *
023: * @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur</a>
024: */
025: public class Java14AnnotationInvocationHander implements
026: InvocationHandler, Serializable {
027:
028: //TODO calculate
029: private static final long serialVersionUID = 1L;
030:
031: private String m_annotationClassName;
032: private String m_rawAnnotationName;//nickname f.e. @Before in 1.4
033: private String m_rawAnnotationValue;
034: private final boolean m_isUntyped;
035: private final Map m_elements = new HashMap();
036:
037: /**
038: * Constructor that will trigger the parsing if required
039: *
040: * @param annotationInterface
041: * @param rawAnnotationName
042: * @param rawAnnotationValue
043: */
044: public Java14AnnotationInvocationHander(Class annotationInterface,
045: String rawAnnotationName, String rawAnnotationValue) {
046: m_annotationClassName = annotationInterface.getName().replace(
047: '/', '.');
048: m_rawAnnotationName = rawAnnotationName;
049: m_rawAnnotationValue = rawAnnotationValue;
050:
051: // untyped
052: if (annotationInterface.getName().equals(
053: UntypedAnnotation.class.getName())) {
054: m_isUntyped = true;
055: } else {
056: m_isUntyped = false;
057: }
058:
059: // for @AfterReturning etc, we allow anonymous style but are using typed annotation
060: // hence the @Around pc is a non supported syntax (should be @Around "pc")
061: // but for compatibility purpose we fake it here.
062: if ((m_annotationClassName
063: .equals("org.codehaus.aspectwerkz.annotation.AfterReturning")
064: || m_annotationClassName
065: .equals("org.codehaus.aspectwerkz.annotation.AfterThrowing")
066: || m_annotationClassName
067: .startsWith("org.codehaus.aspectwerkz.annotation.") || isSingleStringValued(annotationInterface))
068: && !m_isUntyped) {//annotationClassName.equals("org.codehaus.aspectwerkz.annotation.UntypedAnnotation")) {
069: String trimed = m_rawAnnotationValue.trim();
070: if (!isSingleStringValued(annotationInterface)
071: && (trimed.startsWith("type")
072: || trimed.startsWith("pointcut") || trimed
073: .startsWith("deploymentModel"))) {
074: ;// not using untyped syntax
075: } else {
076: if (m_rawAnnotationValue.startsWith("\"")
077: && m_rawAnnotationValue.endsWith("\"")) {
078: ;
079: } else {
080: m_rawAnnotationValue = "\""
081: + Strings.replaceSubString(
082: m_rawAnnotationValue, "\"", "\\\"")
083: + "\"";
084: }
085: }
086: } else if (m_isUntyped) {
087: if (m_rawAnnotationValue.startsWith("\"")
088: && m_rawAnnotationValue.endsWith("\"")) {
089: if (m_rawAnnotationValue.length() > 2) {
090: m_rawAnnotationValue = m_rawAnnotationValue
091: .substring(1,
092: m_rawAnnotationValue.length() - 1);
093: }
094: }
095: }
096:
097: // parse the raw representation for typed annotation
098: if (!m_isUntyped) {
099: StringBuffer representation = new StringBuffer("@");
100: representation.append(m_annotationClassName).append('(');
101: if (m_rawAnnotationValue != null) {
102: // @Aspect perJVM is allowed, while should be @Aspect "perJVM"
103: // for now patch it here...
104: // FIXME
105: if (m_annotationClassName
106: .equals("org.codehaus.aspectwerkz.annotation.Aspect")) {
107: if (m_rawAnnotationValue.indexOf("name") < 0) {
108: representation.append(m_rawAnnotationValue);
109: }
110: } else {
111: representation.append(m_rawAnnotationValue);
112: }
113: }
114: representation.append(')');
115: //TODO support for LazyClass
116: AnnotationVisitor.parse(m_elements, representation
117: .toString(), annotationInterface);
118: }
119: }
120:
121: private static boolean isSingleStringValued(
122: Class annotationInterface) {
123: if (annotationInterface.getDeclaredMethods().length == 1) {
124: Method m = annotationInterface.getDeclaredMethods()[0];
125: return (m.getName().equals("value") && m.getReturnType()
126: .equals(String.class));
127: }
128: return false;
129: }
130:
131: /**
132: * Raw constructor that assumes an already analysed annotation instance
133: * Used for nested annotation
134: *
135: * @param annotationInterface
136: * @param elements
137: */
138: public Java14AnnotationInvocationHander(Class annotationInterface,
139: Map elements) {
140: m_annotationClassName = annotationInterface.getName().replace(
141: '/', '.');
142: m_rawAnnotationName = m_annotationClassName;
143: m_isUntyped = false;
144: m_rawAnnotationValue = null;
145:
146: m_elements.putAll(elements);
147: }
148:
149: public Object invoke(Object proxy, Method method, Object[] args)
150: throws Throwable {
151: String methodName = method.getName();
152: Object returned = null;
153: if ("toString".equals(methodName)) {
154: StringBuffer sb = new StringBuffer();
155: sb.append('@').append(m_rawAnnotationName);
156: sb.append("(");
157: String sep = "";
158: for (Iterator iterator = m_elements.keySet().iterator(); iterator
159: .hasNext();) {
160: String elementName = (String) iterator.next();
161: AnnotationElement element = (AnnotationElement) m_elements
162: .get(elementName);
163: sb.append(sep).append(
164: element.name + "=" + element.toString());
165: sep = ", ";
166: }
167: sb.append(")");
168: returned = sb.toString();
169: } else if ("annotationType".equals(methodName)) {
170: return Class.forName(m_annotationClassName, false, proxy
171: .getClass().getClassLoader());
172: } else if (m_isUntyped) {
173: if ("value".equals(methodName)) {
174: returned = m_rawAnnotationValue;
175: } else if ("name".equals(methodName)) {
176: returned = m_rawAnnotationName;
177: } else if ("annotationType".equals(methodName)) {
178: returned = Class.forName(m_annotationClassName, false,
179: proxy.getClass().getClassLoader());
180: } else {
181: throw new RuntimeException(
182: "No such element on Annotation @"
183: + m_annotationClassName + " : "
184: + methodName);
185: }
186: } else if (m_elements.containsKey(methodName)) {
187: AnnotationElement element = (AnnotationElement) m_elements
188: .get(methodName);
189: Object valueHolder = element.resolveValueHolderFrom(proxy
190: .getClass().getClassLoader());
191: returned = valueHolder;
192: } else {
193: returned = null;
194: }
195:
196: //handle default value for primitive types
197: if (returned == null && method.getReturnType().isPrimitive()) {
198: Class returnedTyped = method.getReturnType();
199: if (boolean.class.equals(returnedTyped)) {
200: return Boolean.FALSE;
201: } else {
202: short s0 = 0;
203: return new Short(s0);
204: }
205: } else {
206: return returned;
207: }
208: }
209:
210: // private void readObject(final ObjectInputStream stream) throws Exception {
211: // ObjectInputStream.GetField fields = stream.readFields();
212: // m_value = (String) fields.get("m_value", null);
213: // m_name = (String) fields.get("m_name", null);
214: // }
215:
216: }
|