package org.saintandreas.serket.didl; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; import org.saintandreas.serket.didl.annotations.DIDLAttribute; import org.saintandreas.serket.didl.annotations.DIDLElement; import org.saintandreas.serket.didl.annotations.DIDLProperty; import org.saintandreas.serket.didl.annotations.DIDLText; import org.saintandreas.util.ReflectUtil; import org.saintandreas.util.ReflectUtil.ValueAccessor; import org.w3c.dom.Element; import org.w3c.dom.Node; public class AnnotatedClassInfo { private static Map<Class<?>, AnnotatedClassInfo> ANNOTATION_MAP = new HashMap<Class<?>, AnnotatedClassInfo>(); public static AnnotatedClassInfo getInfo(Class<?> clazz) { if (!ANNOTATION_MAP.containsKey(clazz)) { synchronized (ANNOTATION_MAP) { if (!ANNOTATION_MAP.containsKey(clazz)) { ANNOTATION_MAP.put(clazz, new AnnotatedClassInfo(clazz)); } } } return ANNOTATION_MAP.get(clazz); } private DIDLElement itemAnnotation = null; private class DIDLElementComparator implements Comparator<DIDLProperty> { @Override public int compare(DIDLProperty o1, DIDLProperty o2) { return o1.order() == o2.order() ? 0 : (o1.order() > o2.order() ? 1 : -1); } } private SortedMap<DIDLProperty, ValueAccessor<Method>> elementMap = new TreeMap<DIDLProperty, ValueAccessor<Method>>(new DIDLElementComparator()); private Map<DIDLAttribute, ValueAccessor<Method>> attributeMap = new HashMap<DIDLAttribute, ValueAccessor<Method>>(); private Map<DIDLElement, ValueAccessor<Method>> childMap = new HashMap<DIDLElement, ValueAccessor<Method>>(); private ValueAccessor<Method> textAccessor = null; public static List<Class<?>> getAncestorsAndInterfaces(Class<?> clazz) { Set<Class<?>> seen = new HashSet<Class<?>>(); return getAncestorsAndInterfaces(clazz, seen); } public static List<Class<?>> getAncestorsAndInterfaces(Class<?> clazz, Set<Class<?>> seen) { List<Class<?>> retVal = new ArrayList<Class<?>>(); retVal.addAll(getAncestors(clazz, seen)); retVal.addAll(getInterfaces(clazz, seen)); while (clazz != null) { for (Class<?> interfaze : clazz.getInterfaces()) { retVal.addAll(getAncestorsAndInterfaces(interfaze, seen)); } clazz = clazz.getSuperclass(); } return retVal; } public static List<Class<?>> getAncestors(Class<?> clazz, Set<Class<?>> seen) { List<Class<?>> retVal = new ArrayList<Class<?>>(); while (clazz != null) { if (seen.add(clazz)) { retVal.add(clazz); } clazz = clazz.getSuperclass(); } return retVal; } public static List<Class<?>> getInterfaces(Class<?> clazz, Set<Class<?>> seen) { List<Class<?>> retVal = new ArrayList<Class<?>>(); for (Class<?> interfaze : clazz.getInterfaces()) { if (seen.add(interfaze)) { retVal.add(interfaze); } } return retVal; } public AnnotatedClassInfo(Class<?> annotatedClass) { List<Class<?>> clazzes = getAncestorsAndInterfaces(annotatedClass); for (Class<?> clazz : clazzes) { if (itemAnnotation == null) { itemAnnotation = clazz.getAnnotation(DIDLElement.class); } for (Method m : clazz.getMethods()) { DIDLAttribute attr = m.getAnnotation(DIDLAttribute.class); if (attr != null && !attributeMap.containsKey(attr)) { attributeMap.put(attr, ReflectUtil.getValueAccessor(m)); } DIDLProperty childElement = m.getAnnotation(DIDLProperty.class); if (childElement != null && !elementMap.containsKey(childElement)) { elementMap.put(childElement, ReflectUtil.getValueAccessor(m)); } DIDLElement childNode = m.getAnnotation(DIDLElement.class); if (childNode != null && !childMap.containsKey(childNode)) { childMap.put(childNode, ReflectUtil.getValueAccessor(m)); } if (m.getAnnotation(DIDLText.class) != null) { if (this.textAccessor != null) { throw new IllegalStateException("A DIDLElement may not have more than one DIDLText annotation"); } textAccessor = ReflectUtil.getValueAccessor(m); } if (childNode != null && !childMap.containsKey(childNode)) { childMap.put(childNode, ReflectUtil.getValueAccessor(m)); } } } if (!childMap.isEmpty() && textAccessor != null) { throw new IllegalStateException("A DIDLElement may not have both DIDLProperty and DIDLText annotations"); } if (itemAnnotation == null) { throw new RuntimeException(); } } public Node createNode(Object obj, Node parent) { Element retVal = parent.getOwnerDocument().createElementNS(DIDLNamespace.DIDL.uri, itemAnnotation.value()); for (Map.Entry<DIDLAttribute, ValueAccessor<Method>> entry : attributeMap.entrySet()) { addAttribute(retVal, entry.getKey(), entry.getValue().getValueSafely(obj)); } for (Map.Entry<DIDLProperty, ValueAccessor<Method>> entry : elementMap.entrySet()) { addElement(retVal, entry.getKey(), entry.getValue().getValueSafely(obj)); } for (Map.Entry<DIDLElement, ValueAccessor<Method>> entry : childMap.entrySet()) { Object childObj = entry.getValue().getValueSafely(obj); if (childObj != null) { AnnotatedClassInfo.getInfo(childObj.getClass()).createNode(childObj, retVal); } } if (textAccessor != null) { Object value = textAccessor.getValueSafely(obj); if (value != null) { retVal.setTextContent(value.toString()); } } return retVal; } private static void addAttribute(Element node, DIDLAttribute annotation, Object value) { if (value == null) { if (annotation.required()) { throw new RuntimeException("required attribute " + annotation.value() + " had null value"); } return; } node.setAttribute(annotation.value(), value.toString()); } private static void addElement(Element node, DIDLProperty annotation, Object value) { if (value == null) { if (annotation.required()) { throw new RuntimeException("required element " + annotation.value() + " had null value"); } return; } Element newChild = node.getOwnerDocument().createElementNS(annotation.namespace().uri, annotation.value()); if (annotation.namespace() != DIDLNamespace.DIDL) { newChild.setPrefix(annotation.namespace().prefix); } newChild.setTextContent(value.toString()); node.appendChild(newChild); } }