package org.tldgen.util;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.sun.javadoc.AnnotationDesc;
import com.sun.javadoc.AnnotationDesc.ElementValuePair;
import com.sun.javadoc.AnnotationTypeDoc;
import com.sun.javadoc.AnnotationValue;
import com.sun.javadoc.ClassDoc;
import com.sun.javadoc.FieldDoc;
import com.sun.javadoc.PackageDoc;
import com.sun.javadoc.ProgramElementDoc;
import com.sun.tools.javadoc.AnnotationDescImpl.ElementValuePairImpl;
/**
* Methods that should be part of the javadoc standard
* @author icoloma
*
*/
public class JavadocUtils {
/** Use only static methods. */
private JavadocUtils() {}
/** hack to get access to each annotation attribute name */
private static Field methAccessor;
private static Logger log = LoggerFactory.getLogger(JavadocUtils.class);
static {
// hack: ElementValuePairImpl.meth is not accessible, and we need it to know about attribute names
try {
methAccessor = ElementValuePairImpl.class.getDeclaredField("meth");
methAccessor.setAccessible(true);
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
}
}
/**
*
* @param doc the TLD element to inspect
* @param annotationClass the annotation class to search for
* @return the annotation instance, if found. Null if none
*/
public static AnnotationDesc getAnnotation(ProgramElementDoc doc, Class<? extends Annotation> annotationClass) {
return getAnnotation(doc.annotations(), annotationClass);
}
/**
*
* @param doc the package to inspect
* @param annotationClass the annotation class to search for
* @return the annotation instance, if found. Null if none
*/
public static AnnotationDesc getAnnotation(PackageDoc doc, Class<? extends Annotation> annotationClass) {
return getAnnotation(doc.annotations(), annotationClass);
}
private static AnnotationDesc getAnnotation(AnnotationDesc[] annotations, Class<? extends Annotation> annotationClass) {
for (AnnotationDesc annotation : annotations) {
if (isInstanceOf(annotation, annotationClass)) {
return annotation;
}
}
return null;
}
/**
* @return the String value of the specified attribute
*/
public static String getStringAttribute(AnnotationDesc annotation, String name) {
return (String) getAttribute(annotation, name);
}
/**
* @return the Class value of the specified attribute
*/
public static ClassDoc getClassAttribute(AnnotationDesc annotation, String name) {
return (ClassDoc) getAttribute(annotation, name);
}
/**
* @return the String[] value of the specified attribute, the empty array if it's empty
*/
public static String[] getStringArrayAttribute(AnnotationDesc annotation, String name) {
AnnotationValue[] annValues = (AnnotationValue[]) getAttribute(annotation, name);
String[] values = new String[annValues.length];
for (int i = 0; i < annValues.length; i++) {
values[i] = (String) annValues[i].value();
}
return values;
}
/**
* @return the AnnotationValue[] value of the specified attribute, the empty array if it's empty
*/
public static AnnotationDesc[] getAnnotationArrayAttribute(AnnotationDesc annotation, String name) {
AnnotationValue[] values = (AnnotationValue[]) getAttribute(annotation, name);
if (values == null) {
return null;
}
AnnotationDesc[] ad = new AnnotationDesc[values.length];
for (int i = 0; i < values.length; i++) {
ad[i] = (AnnotationDesc) values[i].value();
}
return ad;
}
/**
* @return the String value of the specified Enum attribute
*/
public static String getEnumAttribute(AnnotationDesc annotation, String name) {
FieldDoc fieldDoc = (FieldDoc) getAttribute(annotation, name);
if (fieldDoc == null) {
return null;
}
String value = fieldDoc.toString();
return value.substring(value.lastIndexOf('.') + 1);
}
/**
* @return the Boolean value of the specified attribute
*/
public static Boolean getBooleanAttribute(AnnotationDesc annotation, String name) {
Object value = getAttribute(annotation, name);
return value == null? null : Boolean.parseBoolean(value.toString());
}
/**
*
* @param annotation the annotation to inspect
* @param name the name of the attribute to retrieve
* @return the value of the specified attribute in the specified annotation
*/
private static Object getAttribute(AnnotationDesc annotation, String name) {
try {
name += "()";
for (ElementValuePair pair : annotation.elementValues()) {
String attributeName = methAccessor.get(pair).toString();
if (name.equals(attributeName)) {
return pair.value().value();
}
}
return null;
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
/**
* @return true if the annotation is an instance of the provided annotation class
*/
private static boolean isInstanceOf(AnnotationDesc annotation, Class<? extends Annotation> annotationClass) {
try {
AnnotationTypeDoc typeDoc = annotation.annotationType();
return annotationClass.getName().equals(typeDoc.qualifiedName());
} catch (ClassCastException e) {
// HEY! If you are looking here because
// "ClassDocImpl cannot be cast to AnnotationTypeDoc", the workaround is
// adding your runtime jars to the classpath:
// http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6442982
// but we are safe to assume that it was no TLDGen annotation, so we are safe in returning false.
log.debug("Detected CCE (javadoc bug 6442982), ignoring");
return false;
}
}
}