/*******************************************************************************
* Copyright (c) 2006-2010 eBay Inc. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*******************************************************************************/
package org.ebayopensource.turmeric.runtime.binding.impl.jaxb;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
import javax.xml.bind.annotation.XmlNsForm;
import javax.xml.bind.annotation.XmlSchema;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import org.ebayopensource.turmeric.runtime.binding.ISerializationContext;
import org.ebayopensource.turmeric.runtime.binding.ITypeConversionContext;
import org.ebayopensource.turmeric.runtime.binding.exception.ElementFormMismatchException;
import com.sun.xml.bind.v2.model.annotation.Locatable;
import com.sun.xml.bind.v2.model.annotation.RuntimeAnnotationReader;
import com.sun.xml.bind.v2.model.annotation.RuntimeInlineAnnotationReader;
import com.sun.xml.bind.v2.model.core.ErrorHandler;
/**
* @author wdeng
*
*/
public class JAXBInlineAnnotationReader implements RuntimeAnnotationReader{
private static final RuntimeInlineAnnotationReader DEFAULT_READER
= new RuntimeInlineAnnotationReader();
private XmlJavaTypeAdapter m_adapterAnnotation = null;
private Collection<String> m_boundTypes;
private XmlSchemaAnnotationBuilder m_xmlSchemaAnnoBuilder;
public JAXBInlineAnnotationReader(ISerializationContext ctxt, Map<String, String> options) {
ITypeConversionContext typeConvCtxt = ctxt.getTypeConversionContext();
if (null != typeConvCtxt && !typeConvCtxt.isEmpty()) {
m_adapterAnnotation = new XmlJavaTypeAdapterBean(typeConvCtxt.getTypeConversionAdapterClass(), null);
m_boundTypes = typeConvCtxt.getBoundTypes();
} else {
m_boundTypes = new ArrayList<String>();
}
//TODO: we need to find a place to hold this object. it is shared by
// all JAXB based data binding of the same service, one way is to have
// a setProperty/getProperty() on ServiceDesc to carry custom property
// objects.
m_xmlSchemaAnnoBuilder = new XmlSchemaAnnotationBuilder(ctxt, options);
}
public Annotation[] getAllFieldAnnotations(Field field, Locatable srcPos) {
Annotation[] annotations = DEFAULT_READER.getAllFieldAnnotations(field, srcPos);
return annotations;
}
public Annotation[] getAllMethodAnnotations(Method method, Locatable srcPos) {
Class propertyClass = getPropertyClass(method);
Annotation[] annotations = DEFAULT_READER.getAllMethodAnnotations(method, srcPos);
if (null != propertyClass && m_boundTypes.contains(propertyClass.getName())) {
return addTypeConverterAdapter(annotations, false);
}
return annotations;
}
public <A extends Annotation> A getClassAnnotation(Class<A> a, Class clazz, Locatable srcPos) {
if (a == XmlSeeAlso.class) {
return null;
}
return DEFAULT_READER.getClassAnnotation(a, clazz, srcPos);
}
public Class[] getClassArrayValue(Annotation a, String name) {
return DEFAULT_READER.getClassArrayValue(a, name);
}
public Class getClassValue(Annotation a, String name) {
return DEFAULT_READER.getClassValue(a, name);
}
public <A extends Annotation> A getFieldAnnotation(Class<A> annotation, Field field, Locatable srcPos) {
A a = DEFAULT_READER.getFieldAnnotation(annotation, field, srcPos);
return a;
}
@SuppressWarnings("unchecked")
public <A extends Annotation> A getMethodAnnotation(Class<A> annotation, Method method, Locatable srcPos) {
if (null == m_adapterAnnotation) {
return DEFAULT_READER.getMethodAnnotation(annotation, method, srcPos);
}
if (annotation != XmlJavaTypeAdapter.class) {
return DEFAULT_READER.getMethodAnnotation(annotation, method, srcPos);
}
Class propertyClass = getPropertyClass(method);
if (null == propertyClass || !m_boundTypes.contains(propertyClass.getName())) {
return DEFAULT_READER.getMethodAnnotation(annotation, method, srcPos);
}
return (A)m_adapterAnnotation;
}
@SuppressWarnings("unchecked")
public <A extends Annotation> A getMethodAnnotation(Class<A> annotation, Method getter, Method setter, Locatable srcpos) {
if (null == m_adapterAnnotation) {
return DEFAULT_READER.getMethodAnnotation(annotation, getter, setter, srcpos);
}
if (annotation != XmlJavaTypeAdapter.class) {
return DEFAULT_READER.getMethodAnnotation(annotation, getter, setter, srcpos);
}
Class propertyClass = getPropertyClass(getter);
if (null == propertyClass || !m_boundTypes.contains(propertyClass.getName())) {
return DEFAULT_READER.getMethodAnnotation(annotation, getter, setter, srcpos);
}
return (A)m_adapterAnnotation;
}
public <A extends Annotation> A getMethodParameterAnnotation(Class<A> annotation, Method method, int paramIndex, Locatable srcPos) {
return DEFAULT_READER.getMethodParameterAnnotation(annotation, method, paramIndex, srcPos);
}
@SuppressWarnings("unchecked")
public <A extends Annotation> A getPackageAnnotation(Class<A> a, Class clazz, Locatable srcPos) {
A annoFromDefault = DEFAULT_READER.getPackageAnnotation(a, clazz, srcPos);
if (!(a == XmlSchema.class)) {
return annoFromDefault;
}
XmlSchema xsAnno = (XmlSchema)annoFromDefault;
XmlSchema anno = m_xmlSchemaAnnoBuilder.getXmlSchemaReplacement(clazz, xsAnno);
return (A)anno;
}
void checkElementFormDefault(Object annoFromDefault) {
XmlNsForm efd = m_xmlSchemaAnnoBuilder.getElementFormDefault();
if (annoFromDefault instanceof XmlSchema) {
XmlNsForm nsForm = ((XmlSchema)annoFromDefault).elementFormDefault();
if (!nsForm.equals(XmlNsForm.UNSET) && !efd.equals(nsForm)) {
throw new ElementFormMismatchException();
}
}
}
public boolean hasClassAnnotation(Class clazz, Class<? extends Annotation> annotationType) {
return DEFAULT_READER.hasClassAnnotation(clazz, annotationType);
}
public boolean hasFieldAnnotation(Class<? extends Annotation> annotationType, Field field) {
return DEFAULT_READER.hasFieldAnnotation(annotationType, field);
}
@Override
public int hashCode() {
return m_xmlSchemaAnnoBuilder.hashCode() ^ m_boundTypes.hashCode();
}
public boolean hasMethodAnnotation(Class<? extends Annotation> annotation, Method method) {
if (annotation != XmlJavaTypeAdapter.class) {
return DEFAULT_READER.hasMethodAnnotation(annotation, method);
}
Class propertyClass = getPropertyClass(method);
if (null == propertyClass || !m_boundTypes.contains(propertyClass.getName())) {
return DEFAULT_READER.hasMethodAnnotation(annotation, method);
}
return true;
}
public boolean hasMethodAnnotation(Class<? extends Annotation> annotation, String propertyName, Method getter, Method setter, Locatable srcPos) {
if (annotation != XmlJavaTypeAdapter.class) {
return DEFAULT_READER.hasMethodAnnotation(annotation, propertyName, getter, setter, srcPos);
}
Class propertyClass = getPropertyClass(getter);
if (null == propertyClass || !m_boundTypes.contains(propertyClass.getName())) {
return DEFAULT_READER.hasMethodAnnotation(annotation, propertyName, getter, setter, srcPos);
}
return true;
}
public void setErrorHandler(ErrorHandler errorHandler) {
DEFAULT_READER.setErrorHandler(errorHandler);
}
private Class getPropertyClass(Method method) {
String methodName = method.getName();
if (methodName.indexOf("get") != 0) {
return null;
}
return method.getReturnType();
}
private Annotation[] addTypeConverterAdapter(Annotation[] annotations, boolean isField) {
int len = 0;
Annotation[] newAnnotations = new Annotation[1];
if (annotations != null) {
len = annotations.length;
newAnnotations = new Annotation[len + 1];
System.arraycopy(annotations, 0, newAnnotations, 0, len);
}
Annotation xmlAdapter = m_adapterAnnotation;
newAnnotations[len] = xmlAdapter;
return newAnnotations;
}
@Override
public boolean equals(Object o) {
if (null == o) {
return false;
}
if (!(o instanceof JAXBInlineAnnotationReader)) {
return false;
}
JAXBInlineAnnotationReader other = (JAXBInlineAnnotationReader)o;
return m_boundTypes.equals(other.m_boundTypes)
&& m_xmlSchemaAnnoBuilder.equals(other.m_xmlSchemaAnnoBuilder);
}
}