package at.ac.tuwien.infosys.jaxb;
import java.lang.annotation.Annotation;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.sun.tools.javac.code.Attribute;
import com.sun.tools.javac.code.Attribute.Compound;
import com.sun.tools.javac.code.Symbol.MethodSymbol;
import com.sun.tools.javac.util.Pair;
/**
* This class provides utility methods to cope with JAXB schemagen, which uses
* com.sun.sun.tools.javac.** to parse Java source code for XSD generation. We
* require a way to extract the information from the com.sun.sun.tools.javac.**
* model classes, which is provided by this class.
*
* @author Waldemar Hummer
*/
public class SchemagenUtil {
public static final Logger logger = Logger
.getLogger(SchemagenUtil.class.getName());
/**
* Extract the list of annotations from a given parameterized type.
*
* @param type The runtime type of this parameter is
* assumed to be either of:
* * com.sun.tools.javac.code.Type$ClassType
* * com.sun.tools.javac.code.Symbol$TypeSymbol
* @return
* @throws Exception
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public static <T> List<? extends Annotation> extractAnnotations(T type) throws Exception {
List<Annotation> result = new LinkedList<Annotation>();
List<?> annos = null;
Object tsym = type;
if(type.getClass().getName().endsWith("ClassType")) {
tsym = type.getClass().getField("tsym").get(type);
}
annos = (List<?>) tsym.getClass().getMethod("getAnnotationMirrors").invoke(tsym);
for (Object anno : annos) {
String annoType = anno.getClass().getField("type").get(anno).toString();
Class<? extends Annotation> annoClass = (Class<? extends Annotation>)Class.forName(annoType);
List<?> values = (List<?>)anno.getClass().getField("values").get(anno);
Map<String,Object> annoValues = new HashMap<String,Object>();
for (Object value : values) {
Object fst = value.getClass().getField("fst").get(value);
Object snd = value.getClass().getField("snd").get(value);
String attrName = fst.getClass().getField("name").get(fst).toString();
Object attrValue = snd.getClass().getMethod("getValue").invoke(snd);
if(attrValue instanceof List<?>) {
List<?> list = (List<?>)attrValue;
List newList = new LinkedList();
for(Object o : list) {
if(o instanceof Compound) {
o = convertCompound((Compound)o);
}
newList.add(o);
}
list = newList;
if(list.isEmpty()) {
attrValue = null;
} else {
attrValue = toArray(list);
}
}
annoValues.put(attrName, attrValue);
}
Annotation annoInst = AnnotationUtils.createAnnotationProxy(annoClass, annoValues);
result.add(annoInst);
}
return result;
}
private static Object convertCompound(Compound o) throws Exception {
@SuppressWarnings("unchecked")
Class<? extends Annotation> annoClass = (Class<? extends Annotation>) Class.forName(o.type.toString());
Map<String,Object> annoValues = new HashMap<String, Object>();
for(Pair<MethodSymbol, Attribute> pair: o.values) {
annoValues.put(pair.fst.getQualifiedName().toString(), pair.snd.getValue());
}
Object result = AnnotationUtils.createAnnotationProxy(annoClass, annoValues);
return result;
}
@SuppressWarnings("unchecked")
private static <T> T[] toArray(List<T> list) {
T[] toR = (T[]) java.lang.reflect.Array.newInstance(list.get(0).getClass(), list.size());
for (int i = 0; i < list.size(); i++) {
toR[i] = list.get(i);
}
return toR;
}
/**
* Extract an annotation from a type info.
* @param type
* @param annoType
* @return
*/
@SuppressWarnings("all")
public static <T, A extends Annotation> A extractAnnotation(T type, Class<A> annoType) {
try {
for(Annotation a : extractAnnotations(type)) {
if(annoType.isAssignableFrom(a.getClass())) {
return (A)a;
}
}
} catch (Exception e) {
logger.log(Level.WARNING, "Unable to extract annotation '" +
annoType + "' from type '" + type.getClass() + "'", e);
}
return null;
}
}