package jeffaschenk.commons.parsers; import javassist.*; import javassist.bytecode.AnnotationsAttribute; import jeffaschenk.commons.parsers.objects.ClassAnnotationData; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; /** * Simple Annotation Parser Simple Parsers a Classes Annotations and returns a * Collection Map of those attributes and properties of the Object class. This * parser uses Javassist to perform introspection of the Java ByteCode. * * @author jeffaschenk@gmail.com * @version $Id: $ */ public class AnnotationParser { // *************************************** // Logging /** * Constant <code>log</code> */ protected static Log log = LogFactory.getLog(AnnotationParser.class); // ******************************************* // ClassPool for Loading Compile time Classes ClassPool cp; /** * Prevent the Instantiation */ public AnnotationParser() { cp = ClassPool.getDefault(); cp.insertClassPath(new ClassClassPath(this.getClass())); } /** * Parse a Classes Annotations and return in an Named Value Association * Form, return Class, Field and Method Annotations. * * @param clazz a {@link java.lang.Class} object. * @return {@link jeffaschenk.commons.parsers.objects.ClassAnnotationData} object. */ public ClassAnnotationData parse(Class<?> clazz) { // ************************************ // Obtain Our Class. CtClass cc = null; try { cc = cp.get(clazz.getName()); } catch (javassist.NotFoundException nfe) { log.error("Class Not Found:[" + clazz.getName() + "], " + nfe.getMessage() + "."); } // ************************************ // Did we obtain the Javassist Object? if (cc == null) { return null; } // ************************************ // Obtain the Class Annotations. ClassAnnotationData ad = new ClassAnnotationData(cc.getName()); ad.setSimpleName(cc.getSimpleName()); ad.setClassAnnotations(this.parseClassAnnotations(cc)); ad.setFieldAnnotations(this.parseClassFieldAnnotations(cc)); ad.setMethodAnnotations(this .parseClassMethodAnnotations(cc)); // ************************************ // Return the Association Map. return ad; } /** * Parse a Classes Annotations and return in an Named Value Association * Form, return Class Annotations. * * @param clazz a {@link java.lang.Class} object. * @return {@link java.util.Map} object. */ public Map<String, String> parseClassAnnotations(Class<?> clazz) { CtClass cc = null; try { cc = cp.get(clazz.getName()); // ************************************ // Return the Association Map. return parseClassAnnotations(cc); } catch (javassist.NotFoundException nfe) { log.error("Class Not Found:[" + clazz.getName() + "], " + nfe.getMessage() + "."); return null; } } /** * Parse a Classes Annotations and return in an Named Value Association * Form, return Class, Field and Method Annotations. * * @param cc a {@link javassist.CtClass} object. * @return {@link java.util.Map} object. */ public Map<String, String> parseClassAnnotations(CtClass cc) { // ************************************* // Initialize. Map<String, String> annotations = new HashMap<String, String>(); // ************************************* // Parse the Annotations for this Class. if (log.isTraceEnabled()) { log.trace("Class:[" + cc.getSimpleName() + "]"); } // *************************************************** // Check for all Visible Annotations. AnnotationsAttribute attr = (AnnotationsAttribute) cc.getClassFile() .getAttribute(AnnotationsAttribute.visibleTag); if (attr != null) { for (javassist.bytecode.annotation.Annotation an : attr .getAnnotations()) { // ********************************************** // Check for any Annotation Members if (an.getMemberNames() != null) { for (Object name : an.getMemberNames()) { if (log.isTraceEnabled()) { log.trace("*** Class Annotation:[" + an.getTypeName() + "], Member Name:[" + name + "], Value:[" + an.getMemberValue(name.toString()) .toString().replace("\042", "").trim() + "]"); } // ************************************************ // Save Annotations in association Named Value Map. annotations.put(an.getTypeName() + "." + name.toString(), an.getMemberValue( name.toString()).toString().replace("\042", "").trim()); } // End of Inner For Each Loop for Annotation Members } else { if (log.isTraceEnabled()) { log.trace("*** Class Annotation:[" + an.getTypeName() + "]"); } // ************************************************ // Save Annotations in association Named Value Map. annotations.put(an.getTypeName(), ""); } } // End of Outer For Loop for Visible Annotations } // End of Check for Visible Annotations. // ************************************ // Return the Association Map. return annotations; } /** * Parse a Classes Annotations and return in an Named Value Association * Form, return Field Annotations. * * @param clazz a {@link java.lang.Class} object. * @return {@link java.util.Map} object. */ public Map<String, String> parseClassFieldAnnotations(Class<?> clazz) { CtClass cc = null; try { cc = cp.get(clazz.getName()); // ************************************ // Return the Association Map. return parseClassFieldAnnotations(cc); } catch (javassist.NotFoundException nfe) { log.error("Class Not Found:[" + clazz.getName() + "], " + nfe.getMessage() + "."); return null; } } /** * Parse a Classes Annotations and return in an Named Value Association * Form, return Field Annotations. * * @param cc a {@link javassist.CtClass} object. * @return {@link java.util.Map} object. */ public Map<String, String> parseClassFieldAnnotations(CtClass cc) { // ************************************* // Initialize. Map<String, String> annotations = new HashMap<String, String>(); // ******************************************* // Parse the Field Annotations for this Class. CtField[] fields = cc.getDeclaredFields(); if (fields == null) { return annotations; } for (CtField field : fields) { // ****************************************************** // Skip standard serialVersionUID Fields. if (field.getName().equalsIgnoreCase("serialVersionUID")) { continue; } if (log.isTraceEnabled()) { log.trace("*** Field:[" + field.getName() + "]"); } // *************************************************** // Check for all Visible Field Annotations. AnnotationsAttribute attr = (AnnotationsAttribute) field .getFieldInfo().getAttribute( AnnotationsAttribute.visibleTag); if (attr != null) { for (javassist.bytecode.annotation.Annotation an : attr .getAnnotations()) { // ********************************************** // Check for any Annotation Members if (an.getMemberNames() != null) { for (Object name : an.getMemberNames()) { if (log.isTraceEnabled()) { log.trace("*** Field:[" + field.getName() + "] Annotation:[" + an.getTypeName() + "], Member Name:[" + name + "], Value:[" + an.getMemberValue(name.toString()) .toString().replace("\042", "").trim() + "]"); } // ************************************************ // Save Annotations in association Named Value Map. annotations.put(field.getName() + ":" + an.getTypeName() + "." + name.toString(), an.getMemberValue( name.toString()).toString().replace("\042", "").trim()); } // End of Inner For Each Loop for Annotation // Members } else { if (log.isTraceEnabled()) { log .trace("*** Field:[" + field.getName() + "] Annotation:[" + an.getTypeName() + "]"); } // ************************************************ // Save Annotations in association Named Value Map. annotations.put(an.getTypeName(), ""); } } // End of Outer For Loop for Visible Annotations } // End of Check for Visible Annotations. } // End of Fields For Each Loop. // ************************************ // Return the Association Map. return annotations; } /** * Parse a Classes Annotations and return in an Named Value Association * Form, return Method Annotations. * * @param clazz a {@link java.lang.Class} object. * @return {@link java.util.Map} object. */ public Map<String, String> parseClassMethodAnnotations(Class<?> clazz) { CtClass cc = null; try { ClassPool cp = new ClassPool(); cc = cp.get(clazz.getName()); // ************************************ // Return the Association Map. return parseClassMethodAnnotations(cc); } catch (javassist.NotFoundException nfe) { log.error("Class Not Found:[" + clazz.getName() + "], " + nfe.getMessage() + "."); return null; } } /** * Parse a Classes Annotations and return in an Named Value Association * Form, return Method Annotations. * * @param cc a {@link javassist.CtClass} object. * @return {@link java.util.Map} object. */ public Map<String, String> parseClassMethodAnnotations(CtClass cc) { // ************************************* // Initialize. Map<String, String> annotations = new HashMap<String, String>(); // ******************************************* // Parse the Field Annotations for this Class. CtMethod[] methods = cc.getDeclaredMethods(); if (methods == null) { return annotations; } for (CtMethod method : methods) { if (log.isTraceEnabled()) { log.trace("*** Method:[" + method.getName() + "]"); } // *************************************************** // Check for all Visible Field Annotations. AnnotationsAttribute attr = (AnnotationsAttribute) method .getMethodInfo().getAttribute( AnnotationsAttribute.visibleTag); if (attr != null) { for (javassist.bytecode.annotation.Annotation an : attr .getAnnotations()) { // ********************************************** // Check for any Annotation Members if (an.getMemberNames() != null) { for (Object name : an.getMemberNames()) { if (log.isTraceEnabled()) { log.trace("*** Method:[" + method.getName() + "] Annotation:[" + an.getTypeName() + "], Member Name:[" + name + "], Value:[" + an.getMemberValue(name.toString()) .toString().replace("\042", "").trim() + "]"); } // ************************************************ // Save Annotations in association Named Value Map. annotations.put(method.getName() + ":" + an.getTypeName() + "." + name.toString(), an.getMemberValue( name.toString()).toString().replace("\042", "").trim()); } // End of Inner For Each Loop for Annotation // Members } else { if (log.isTraceEnabled()) { log .trace("*** Method:[" + method.getName() + "] Annotation:[" + an.getTypeName() + "]"); } // ************************************************ // Save Annotations in association Named Value Map. annotations.put(an.getTypeName(), ""); } } // End of Outer For Loop for Visible Annotations } // End of Check for Visible Annotations. } // End of Fields For Each Loop. // ************************************ // Return the Association Map. return annotations; } /** * Parse a Classes Annotations and return in an Named Value Association * Form, return Method Annotations. * * @param method a {@link java.lang.reflect.Method} object. * @return {@link java.util.Map} object. */ public Map<String, String> parseSpecificClassMethodAnnotations(Method method) { // ************************************* // Initialize. Map<String, String> annotations = new HashMap<String, String>(); // ************************************ // Obtain Our Class. CtClass cc = null; try { cc = cp.get(method.getDeclaringClass().getName()); } catch (javassist.NotFoundException nfe) { log.error("Class Not Found:[" + method.getDeclaringClass().getName() + "], " + nfe.getMessage() + "."); } // ************************************ // Did we obtain the Javassist Object? if (cc == null) { return null; } // ******************************************* // Parse the Annotations for this Method. CtMethod[] methods = cc.getDeclaredMethods(); if (methods == null) { return annotations; } for (CtMethod ctmethod : methods) { if (log.isTraceEnabled()) { log.trace("*** Method:[" + method.getName() + "]"); } // *************************************************** // Check for all Visible Field Annotations. AnnotationsAttribute attr = (AnnotationsAttribute) ctmethod .getMethodInfo().getAttribute( AnnotationsAttribute.visibleTag); if (attr != null) { for (javassist.bytecode.annotation.Annotation an : attr .getAnnotations()) { // ********************************************** // Check for any Annotation Members if (an.getMemberNames() != null) { for (Object name : an.getMemberNames()) { if (log.isTraceEnabled()) { log.trace("*** Method:[" + method.getName() + "] Annotation:[" + an.getTypeName() + "], Member Name:[" + name + "], Value:[" + an.getMemberValue(name.toString()) .toString().replace("\042", "").trim() + "]"); } // ************************************************ // Save Annotations in association Named Value Map. annotations.put(method.getName() + ":" + an.getTypeName() + "." + name.toString(), an.getMemberValue( name.toString()).toString().replace("\042", "").trim()); } // End of Inner For Each Loop for Annotation // Members } else { if (log.isTraceEnabled()) { log .trace("*** Method:[" + method.getName() + "] Annotation:[" + an.getTypeName() + "]"); } // ************************************************ // Save Annotations in association Named Value Map. annotations.put(an.getTypeName(), ""); } } // End of Outer For Loop for Visible Annotations } // End of Check for Visible Annotations. } // End of Fields For Each Loop. // ************************************ // Return the Association Map. return annotations; } }