/*******************************************************************************
* Copyright (c) 2011, 2015 Oracle and/or its affiliates. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
* which accompanies this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Rick Barkhouse - 2.1 - Initial implementation
******************************************************************************/
package org.eclipse.persistence.jaxb.javamodel.xjc;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.xml.bind.annotation.XmlEnum;
import org.eclipse.persistence.dynamic.DynamicClassLoader;
import org.eclipse.persistence.exceptions.JAXBException;
import org.eclipse.persistence.internal.oxm.XMLConversionManager;
import org.eclipse.persistence.internal.security.PrivilegedAccessHelper;
import org.eclipse.persistence.jaxb.javamodel.AnnotationProxy;
import org.eclipse.persistence.jaxb.javamodel.JavaAnnotation;
import com.sun.codemodel.JAnnotationArrayMember;
import com.sun.codemodel.JAnnotationUse;
import com.sun.codemodel.JAnnotationValue;
import com.sun.codemodel.JClass;
import com.sun.codemodel.JDefinedClass;
import com.sun.codemodel.JStringLiteral;
/**
* INTERNAL:
* <p>
* <b>Purpose:</b> <code>JavaAnnotation</code> implementation wrapping XJC's <code>JAnnotationUse</code>. Used when
* bootstrapping a <code>DynamicJAXBContext</code> from an XML Schema.
* </p>
*
* <p>
* <b>Responsibilities:</b>
* </p>
* <ul>
* <li>Provide <code>Annotation</code> information from the underlying <code>JAnnotationUse</code>.</li>
* </ul>
*
* @since EclipseLink 2.1
*
* @see org.eclipse.persistence.jaxb.javamodel.JavaAnnotation
*/
public class XJCJavaAnnotationImpl implements JavaAnnotation {
private JAnnotationUse xjcAnnotation;
private DynamicClassLoader dynamicClassLoader;
private static Field JANNOTATIONUSE_CLAZZ = null;
private static Field JANNOTATIONUSE_MEMBERVALUES = null;
private static Field JANNOTATIONARRAYMEMBER_VALUES = null;
static {
try {
JANNOTATIONUSE_CLAZZ = PrivilegedAccessHelper.getDeclaredField(JAnnotationUse.class, "clazz", true);
JANNOTATIONUSE_MEMBERVALUES = PrivilegedAccessHelper.getDeclaredField(JAnnotationUse.class, "memberValues", true);
JANNOTATIONARRAYMEMBER_VALUES = PrivilegedAccessHelper.getDeclaredField(JAnnotationArrayMember.class, "values", true);
} catch (Exception e) {
throw JAXBException.errorCreatingDynamicJAXBContext(e);
}
}
/**
* Construct a new instance of <code>XJCJavaAnnotationImpl</code>.
*
* @param annotation - the XJC <code>JAnnotationUse</code> to be wrapped.
* @param loader - the <code>ClassLoader</code> used to bootstrap the <code>DynamicJAXBContext</code>.
*/
public XJCJavaAnnotationImpl(JAnnotationUse annotation, DynamicClassLoader loader) {
this.xjcAnnotation = annotation;
this.dynamicClassLoader = loader;
}
/**
* Return a Java <code>Annotation</code> representation of this <code>JavaAnnotation</code>.
*
* @return a Java <code>Annotation</code> representation of this <code>JavaAnnotation</code>.
*/
@SuppressWarnings("unchecked")
public Annotation getJavaAnnotation() {
try {
Map<String, Object> components = new HashMap<String, Object>();
// First, get all the default values for this annotation class.
Object xjcRefClass = PrivilegedAccessHelper.getValueFromField(JANNOTATIONUSE_CLAZZ, xjcAnnotation);
// Cannot cache this field because JReferencedClass is a protected class.
Field _classField = PrivilegedAccessHelper.getDeclaredField(xjcRefClass.getClass(), "_class", true);
Class<Annotation> annotationClass = (Class<Annotation>) PrivilegedAccessHelper.getValueFromField(_classField, xjcRefClass);
Method[] methods = annotationClass.getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
components.put(methods[i].getName(), methods[i].getDefaultValue());
}
// Get the property values for this annotation instance.
Map<Object, Object> memberValues = (Map<Object, Object>) PrivilegedAccessHelper.getValueFromField(JANNOTATIONUSE_MEMBERVALUES, xjcAnnotation);
if (memberValues == null) {
// Return an annotation with just the defaults set.
return AnnotationProxy.getProxy(components, annotationClass, dynamicClassLoader, XMLConversionManager.getDefaultManager());
}
// Now overwrite the default values with anything we find in the XJC annotation instance.
for (Object key : memberValues.keySet()) {
JAnnotationValue xjcValue = (JAnnotationValue) memberValues.get(key);
if (xjcValue instanceof JAnnotationArrayMember) {
List<Object> values = (List<Object>) PrivilegedAccessHelper.getValueFromField(JANNOTATIONARRAYMEMBER_VALUES, xjcValue);
Object[] valuesArray = new Object[values.size()];
for (int i = 0; i < values.size(); i++) {
if (values.get(i) instanceof JAnnotationUse) {
JAnnotationUse xjcAnno = (JAnnotationUse) values.get(i);
XJCJavaAnnotationImpl anno = new XJCJavaAnnotationImpl(xjcAnno, dynamicClassLoader);
valuesArray[i] = anno.getJavaAnnotation();
} else {
Field valueField = PrivilegedAccessHelper.getDeclaredField(values.get(i).getClass(), "value", true);
Object value = PrivilegedAccessHelper.getValueFromField(valueField, values.get(i));
if (value instanceof JStringLiteral) {
JStringLiteral strvalue = (JStringLiteral) value;
valuesArray[i] = strvalue.str;
} else {
// XmlSeeAlso.value = Array of JDefinedClasses
Field valClField = PrivilegedAccessHelper.getDeclaredField(value.getClass(), "val$cl", true);
JDefinedClass wrappedValue = (JDefinedClass) PrivilegedAccessHelper.getValueFromField(valClField, value);
Class<?> tempDynClass = dynamicClassLoader.createDynamicClass(wrappedValue.fullName());
valuesArray[i] = tempDynClass;
}
}
}
components.put(key.toString(), valuesArray);
} else if (xjcValue.getClass().getName().contains("JAnnotationStringValue")) {
// JAnnotationStringValue is a package-protected class so need to compare class name.
// Cannot cache this field because JAnnotationStringValue is a protected class.
Field valueField = PrivilegedAccessHelper.getDeclaredField(xjcValue.getClass(), "value", true);
Object objValue = PrivilegedAccessHelper.getValueFromField(valueField, xjcValue);
if (objValue instanceof JStringLiteral) {
JStringLiteral value = (JStringLiteral) objValue;
String stringValue = value.str;
components.put(key.toString(), stringValue);
} else if (objValue.getClass().getName().contains("JAtom")) {
// e.g. XmlElement.required = JAtom
// Cannot cache this field because JAtom is a protected class.
Field whatField = PrivilegedAccessHelper.getDeclaredField(objValue.getClass(), "what", true);
String value = (String) PrivilegedAccessHelper.getValueFromField(whatField, objValue);
components.put(key.toString(), value);
} else if (objValue.getClass().getName().contains("JExpr$1")) {
// XmlJavaTypeAdapter contains a JDefinedClass
Field valClField = PrivilegedAccessHelper.getDeclaredField(objValue.getClass(), "val$cl", true);
JClass wrappedValue = (JClass) PrivilegedAccessHelper.getValueFromField(valClField, objValue);
Object tempDynClass = null;
if (!(wrappedValue.getTypeParameters().isEmpty())) {
// Parameterized type, so get the actual parameter type and create that.
wrappedValue = wrappedValue.getTypeParameters().get(0);
}
try {
// Attempt to look up the class normally
tempDynClass = Class.forName(wrappedValue.fullName());
} catch (Exception e) {
if (annotationClass.equals(XmlEnum.class)) {
tempDynClass = String.class;
} else {
tempDynClass = dynamicClassLoader.createDynamicClass(wrappedValue.fullName());
}
}
components.put(key.toString(), tempDynClass);
}
} else {
// e.g. XmlSchema.elementFormDefault = JAnnotationUse$1
// Cannot cache this field because JAtom is a protected class.
Field valValueField = PrivilegedAccessHelper.getDeclaredField(xjcValue.getClass(), "val$value", true);
Object value = PrivilegedAccessHelper.getValueFromField(valValueField, xjcValue);
components.put(key.toString(), value);
}
}
return AnnotationProxy.getProxy(components, annotationClass, dynamicClassLoader, XMLConversionManager.getDefaultManager());
} catch (Exception e) {
return null;
}
}
/**
* Return the Java <code>Class</code> of the <code>Annotation</code> represented by this <code>JavaAnnotation</code>.
*
* @return the Java <code>Class</code> of this <code>JavaAnnotation's</code> <code>Annotation</code>.
*/
public Class<?> getJavaAnnotationClass() {
try {
Object xjcRefClass = PrivilegedAccessHelper.getValueFromField(JANNOTATIONUSE_CLAZZ, xjcAnnotation);
// Cannot cache this field because JReferencedClass is a protected class.
Field _classField = PrivilegedAccessHelper.getDeclaredField(xjcRefClass.getClass(), "_class", true);
Class<?> annotationClass = (Class<?>) PrivilegedAccessHelper.getValueFromField(_classField, xjcRefClass);
return annotationClass;
} catch (Exception e) {
return null;
}
}
/**
* Not supported.
*/
@Override
public Map<Object, Object> getComponents() {
throw new UnsupportedOperationException("getComponents");
}
@Override
@SuppressWarnings("unchecked")
public String getName() {
try {
Object xjcRefClass = PrivilegedAccessHelper.getValueFromField(JANNOTATIONUSE_CLAZZ, xjcAnnotation);
Field _classField = PrivilegedAccessHelper.getDeclaredField(xjcRefClass.getClass(), "_class", true);
Annotation annotationClass = (Annotation) PrivilegedAccessHelper.getValueFromField(_classField, xjcRefClass);
return annotationClass.annotationType().getName();
} catch (Exception e) {
return null;
}
}
}