package net.sourceforge.javautil.common.jaxb;
import java.lang.annotation.Annotation;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.xml.bind.JAXBException;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAnyElement;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
import javax.xml.bind.annotation.XmlValue;
import net.sourceforge.javautil.common.ReflectionUtil;
import net.sourceforge.javautil.common.reflection.cache.ClassDescriptor;
import net.sourceforge.javautil.common.reflection.cache.ClassField;
import net.sourceforge.javautil.common.reflection.cache.ClassProperty;
import net.sourceforge.javautil.common.reflection.cache.IClassMemberWritableValue;
/**
* This will map standard JAXB annotations to {@link JavaXMLBean} templates.
*
* @author elponderador
* @author $Author$
* @version $Id$
*/
public class StandardJAXBMapper implements IJavaXMLBeanMapper {
@Override public JavaXMLBean createElement(Class<?> bean) throws JAXBException {
String name = null;
String namespace = "";
boolean rootElement = false;
XmlRootElement root = bean.getAnnotation(XmlRootElement.class);
if (root != null) {
name = root.name();
namespace = !root.namespace().equals("##default") ? root.namespace() : "";
rootElement = true;
} else {
XmlElement element = bean.getAnnotation(XmlElement.class);
if (element != null) {
name = element.name();
namespace = !element.namespace().equals("##default") ? element.namespace() : "";
}
}
return new JavaXMLBean(bean, name, namespace, rootElement);
}
@Override public Map<String, IJavaXMLAttribute> getAttributes(ClassDescriptor<?> bean, JavaXMLBeanInitializationContext context) throws JAXBException {
Map<String, IJavaXMLAttribute> attributes = new HashMap<String, IJavaXMLAttribute>();
ClassField[] fields = bean.getFields(XmlAttribute.class);
ClassProperty[] properties = bean.getProperties(XmlAttribute.class);
List<IClassMemberWritableValue> members = new ArrayList<IClassMemberWritableValue>();
members.addAll(Arrays.asList(properties));
members.addAll(Arrays.asList(fields));
for (IClassMemberWritableValue member : members) {
XmlAttribute attribute = member.getAnnotation(XmlAttribute.class);
String namespace = attribute != null && !attribute.namespace().equals("##default") ? attribute.namespace() : "";
String xmlName = attribute != null && !"".equals( attribute.name() ) ? attribute.name() : member.getName();
attributes.put("".equals(namespace) ? xmlName : (namespace + ":" + xmlName), new JavaXMLAttribute(member));
}
return attributes;
}
@Override public Map<String, IJavaXMLElement> getElements(ClassDescriptor<?> bean, JavaXMLBeanInitializationContext context) throws JAXBException {
Map<String, IJavaXMLElement> elements = new HashMap<String, IJavaXMLElement>();
XmlAccessorType xatDecl = bean.getAnnotation(XmlAccessorType.class);
XmlAccessType xat = xatDecl == null ? XmlAccessType.PUBLIC_MEMBER : xatDecl.value();
List<IClassMemberWritableValue> members = this.filter( this.getMembers(bean, context), XmlElement.class, xat );
for (IClassMemberWritableValue member : members) {
if (member.getAnnotation(XmlAttribute.class) != null) continue;
if (member.getAnnotation(XmlAnyElement.class) != null) continue;
XmlElement element = member.getAnnotation(XmlElement.class);
String namespace = element != null && !element.namespace().equals("##default") ? element.namespace() : "";
String xmlName = element != null && !"".equals( element.name() ) ? element.name() : member.getName();
if (this.isSimple(member)) {
elements.put("".equals(namespace) ? xmlName : (namespace + ":" + xmlName), new JavaXMLSimpleElement(member, namespace, xmlName));
} else {
elements.put("".equals(namespace) ? xmlName : (namespace + ":" + xmlName), this.createRealType(member, context));
}
}
return elements;
}
@Override public IJavaXMLElement getAnyElementHandler(ClassDescriptor<?> bean, JavaXMLBeanInitializationContext context) throws JAXBException {
XmlAccessorType xatDecl = bean.getAnnotation(XmlAccessorType.class);
XmlAccessType xat = xatDecl == null ? XmlAccessType.PUBLIC_MEMBER : xatDecl.value();
List<IClassMemberWritableValue> members = this.filter( this.getMembers(bean, context), XmlAnyElement.class, xat );
Iterator<IClassMemberWritableValue> it = members.iterator();
while (it.hasNext()) {
IClassMemberWritableValue member = it.next();
if (member.getAnnotation(XmlAnyElement.class) == null) {
it.remove();
}
}
if (members.size() > 1)
throw new JAXBException("More than one member declared with XmlValue on" + bean);
return members.size() == 1 ? this.createRealType(members.get(0), context) : null;
}
@Override public IJavaXMLContent getContentHandler(ClassDescriptor<?> bean, JavaXMLBeanInitializationContext context) throws JAXBException {
XmlAccessorType xatDecl = bean.getAnnotation(XmlAccessorType.class);
XmlAccessType xat = xatDecl == null ? XmlAccessType.PUBLIC_MEMBER : xatDecl.value();
List<IClassMemberWritableValue> members = this.filter( this.getMembers(bean, context), XmlValue.class, xat );
Iterator<IClassMemberWritableValue> it = members.iterator();
while (it.hasNext()) {
IClassMemberWritableValue member = it.next();
if (member.getAnnotation(XmlValue.class) == null) {
it.remove();
}
}
if (members.size() > 1)
throw new JAXBException("More than one member declared with XmlValue on" + bean);
return members.size() == 1 ? new JavaXMLContent(members.get(0)) : null;
}
protected IJavaXMLElement createRealType (IClassMemberWritableValue member, JavaXMLBeanInitializationContext context) throws JAXBException {
if (Collection.class.isAssignableFrom(member.getBaseType())) {
Class<?> realType = ReflectionUtil.getConcreteGeneric(member.getGenericType(), 0);
if (realType == null) {
throw new JAXBException("Could not determine real collection type for: " + member);
}
XmlElementWrapper wrapper = member.getAnnotation(XmlElementWrapper.class);
String wname = wrapper != null ? ("".equals(wrapper.name()) ? member.getName() : wrapper.name()) : null;
return wname == null ?
new JavaXMLCummulativeCollection(member, context.resolve(realType)) :
new JavaXMLWrappedCollection(member, context.resolve(member.getBaseType()), context.resolve(realType));
} else if (member.getBaseType().isArray()) {
return null;
} else {
return new JavaXMLComplexElement(member, context.resolve(member.getBaseType()));
}
}
protected List<IClassMemberWritableValue> getMembers (ClassDescriptor<?> bean, JavaXMLBeanInitializationContext context) {
List<IClassMemberWritableValue> members = new ArrayList<IClassMemberWritableValue>();
Map<String, List<ClassField>> fields = bean.getFields();
for (String name : fields.keySet()) {
members.addAll(fields.get(name));
}
members.addAll(bean.getProperties().values());
return members;
}
protected List<IClassMemberWritableValue> filter (List<IClassMemberWritableValue> members, Class<? extends Annotation> atype, XmlAccessType xat) {
for (Iterator<IClassMemberWritableValue> it = members.iterator(); it.hasNext(); ) {
IClassMemberWritableValue member = it.next();
if (member.isStatic() || Modifier.isTransient(member.getJavaMember().getModifiers()) ||
member instanceof ClassProperty && !((ClassProperty)member).isWritable() ||
member.getAnnotation(XmlTransient.class) != null) {
it.remove();
continue;
}
Annotation attribute = member.getAnnotation(atype);
switch (xat) {
case PUBLIC_MEMBER:
if (!Modifier.isPublic( member.getJavaMember().getModifiers() )) it.remove();
break;
case NONE:
if (attribute == null) it.remove();
break;
case PROPERTY:
if (member instanceof ClassField && attribute == null) it.remove();
break;
case FIELD:
if (member instanceof ClassProperty && attribute == null) it.remove();
}
}
return members;
}
protected boolean isSimple (IClassMemberWritableValue member) {
if (member.getBaseType().isPrimitive() || ReflectionUtil.isBoxedType(member.getBaseType())) return true;
if (member.getBaseType() == String.class) return true;
return false;
}
}