/******************************************************************************* * 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.2 - Initial implementation ******************************************************************************/ package org.eclipse.persistence.jaxb.javamodel.oxm; import java.lang.reflect.GenericArrayType; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.WildcardType; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import javax.xml.bind.JAXBElement; import org.eclipse.persistence.internal.security.PrivilegedAccessHelper; import org.eclipse.persistence.jaxb.compiler.XMLProcessor; import org.eclipse.persistence.jaxb.javamodel.*; import org.eclipse.persistence.jaxb.xmlmodel.JavaAttribute; import org.eclipse.persistence.jaxb.xmlmodel.JavaType; import org.eclipse.persistence.jaxb.xmlmodel.JavaType.JavaAttributes; import org.eclipse.persistence.jaxb.xmlmodel.XmlAnyAttribute; import org.eclipse.persistence.jaxb.xmlmodel.XmlAnyElement; import org.eclipse.persistence.jaxb.xmlmodel.XmlAttribute; import org.eclipse.persistence.jaxb.xmlmodel.XmlElement; import org.eclipse.persistence.jaxb.xmlmodel.XmlElementRef; import org.eclipse.persistence.jaxb.xmlmodel.XmlElements; import org.eclipse.persistence.jaxb.xmlmodel.XmlInverseReference; import org.eclipse.persistence.jaxb.xmlmodel.XmlJoinNodes; import org.eclipse.persistence.jaxb.xmlmodel.XmlValue; /** * INTERNAL: * <p> * <b>Purpose:</b> <code>JavaClass</code> implementation wrapping MOXy's <code>xmlmodel.JavaType</code>. * Used when bootstrapping a <code>DynamicJAXBContext</code> from XML Bindings. * </p> * * <p> * <b>Responsibilities:</b> * </p> * <ul> * <li>Provide Class information from the underlying <code>JavaType</code>.</li> * </ul> * * @since EclipseLink 2.2 * * @see org.eclipse.persistence.jaxb.javamodel.JavaClass * @see org.eclipse.persistence.jaxb.xmlmodel.JavaType */ public class OXMJavaClassImpl implements JavaClass { private JavaType javaType; private String javaName; private List<String> enumValues; private JavaModel javaModel; /** * Construct a new instance of <code>OXMJavaClassImpl</code>. * * @param aJavaType - the XJC <code>JavaType</code> to be wrapped. */ public OXMJavaClassImpl(JavaType aJavaType) { this.javaType = aJavaType; } /** * Construct a new instance of <code>OXMJavaClassImpl</code>. * * @param aJavaTypeName - the name of the JavaType to create. */ public OXMJavaClassImpl(String aJavaTypeName) { this.javaName = aJavaTypeName; } /** * Construct a new instance of <code>OXMJavaClassImpl</code> * representing a Java <code>enum</code>. * * @param aJavaTypeName - the name of the JavaType to create. * @param enumValues - the list of values for this <code>enum</code>. */ public OXMJavaClassImpl(String aJavaTypeName, List<String> enumValues) { this.javaName = aJavaTypeName; this.enumValues = enumValues; } // ======================================================================== /** * Return the "actual" type from a parameterized type. For example, if this * <code>JavaClass</code> represents <code>List<Employee</code>, this method will return the * <code>Employee</code> <code>JavaClass</code>. * * @return a <code>Collection</code> containing the actual type's <code>JavaClass</code>. */ public Collection<JavaClass> getActualTypeArguments() { Object jType = null; if (this.javaType != null) { jType = this.javaType; } else { try { Class<?> jTypeClass = PrivilegedAccessHelper.getClassForName(this.javaName); jType = PrivilegedAccessHelper.newInstanceFromClass(jTypeClass); } catch (Exception e) { return new ArrayList<JavaClass>(); } } ArrayList<JavaClass> argCollection = new ArrayList<JavaClass>(); if (jType instanceof ParameterizedType) { ParameterizedType pType = (ParameterizedType) jType; Type[] params = pType.getActualTypeArguments(); for (Type type : params) { if (type instanceof ParameterizedType) { ParameterizedType pt = (ParameterizedType) type; argCollection.add(this.javaModel.getClass(pt.getRawType().getClass())); } else if (type instanceof WildcardType) { Type[] upperTypes = ((WildcardType) type).getUpperBounds(); if (upperTypes.length >0) { Type upperType = upperTypes[0]; if (upperType instanceof Class<?>) { argCollection.add(this.javaModel.getClass(upperType.getClass())); } } } else if (type instanceof Class<?>) { argCollection.add(this.javaModel.getClass(type.getClass())); } else if (type instanceof GenericArrayType) { Class<?> genericTypeClass = (Class<?>) ((GenericArrayType) type).getGenericComponentType(); genericTypeClass = java.lang.reflect.Array.newInstance(genericTypeClass, 0).getClass(); argCollection.add(this.javaModel.getClass(genericTypeClass.getClass())); } } } return argCollection; } /** * If this <code>JavaClass</code> is an array type, return the type of the * array components. * * @return always returns <code>null</code>, as <code>JavaTypes</code> do not represent arrays. */ public JavaClass getComponentType() { return null; } /** * Return the <code>JavaConstructor</code> for this <code>JavaClass</code> that has the * provided parameter types. * * @param parameterTypes the parameter list used to identify the constructor. * * @return the <code>JavaConstructor</code> with the signature matching parameterTypes. */ public JavaConstructor getConstructor(JavaClass[] parameterTypes) { return new OXMJavaConstructorImpl(this); } /** * Return all of the <code>JavaConstructors</code> for this JavaClass. * * @return A <code>Collection</code> containing this <code>JavaClass'</code> <code>JavaConstructors</code>. */ public Collection<JavaConstructor> getConstructors() { ArrayList<JavaConstructor> constructors = new ArrayList<JavaConstructor>(1); constructors.add(new OXMJavaConstructorImpl(this)); return constructors; } /** * Return this <code>JavaClass'</code> inner classes. * * @return always returns an empty <code>ArrayList</code> as <code>JavaTypes</code> do not represent inner classes. */ public Collection<JavaClass> getDeclaredClasses() { return new ArrayList<JavaClass>(); } /** * Return the declared <code>JavaConstructor</code> for this <code>JavaClass</code>. * * @return the <code>JavaConstructor</code> for this <code>JavaClass</code>. */ public JavaConstructor getDeclaredConstructor(JavaClass[] parameterTypes) { return new OXMJavaConstructorImpl(this); } /** * Return all of the declared <code>JavaConstructors</code> for this <code>JavaClass</code>. * * @return A <code>Collection</code> containing this <code>JavaClass'</code> <code>JavaConstructors</code>. */ public Collection<JavaConstructor> getDeclaredConstructors() { ArrayList<JavaConstructor> constructors = new ArrayList<JavaConstructor>(1); constructors.add(new OXMJavaConstructorImpl(this)); return constructors; } /** * Return the declared <code>JavaField</code> for this <code>JavaClass</code>, identified * by <code>fieldName</code>. * * @param name the name of the <code>JavaField</code> to return. * * @return the <code>JavaField</code> named <code>fieldName</code> from this <code>JavaClass</code>. */ public JavaField getDeclaredField(String name) { Collection<JavaField> allFields = getDeclaredFields(); for (Iterator<JavaField> iterator = allFields.iterator(); iterator.hasNext();) { JavaField field = iterator.next(); if (field.getName().equals(name)) { return field; } } return null; } /** * Return all of the declared <code>JavaFields</code> for this <code>JavaClass</code>. * * @return A <code>Collection</code> containing this <code>JavaClass'</code> <code>JavaFields</code>. */ public Collection<JavaField> getDeclaredFields() { List<JavaField> fieldsToReturn = new ArrayList<JavaField>(); if (this.enumValues != null) { for (Iterator<String> iterator = this.enumValues.iterator(); iterator.hasNext();) { fieldsToReturn.add(new OXMJavaFieldImpl(iterator.next(), JAVA_LANG_OBJECT, this)); } } else { JavaAttributes javaAttributes = this.javaType.getJavaAttributes(); if(null != javaAttributes) { List<JAXBElement<? extends JavaAttribute>> fields = javaAttributes.getJavaAttribute(); for (Iterator<JAXBElement<? extends JavaAttribute>> iterator = fields.iterator(); iterator.hasNext();) { JAXBElement<? extends JavaAttribute> jaxbElement = iterator.next(); JavaAttribute att = (JavaAttribute) jaxbElement.getValue(); if (att instanceof XmlElement) { XmlElement xme = (XmlElement) att; String fieldName = xme.getJavaAttribute(); String fieldType = xme.getType(); fieldsToReturn.add(new OXMJavaFieldImpl(fieldName, fieldType, this)); } else if (att instanceof XmlElements) { XmlElements xmes = (XmlElements) att; String fieldName = xmes.getJavaAttribute(); String fieldType = JAVA_LANG_OBJECT; fieldsToReturn.add(new OXMJavaFieldImpl(fieldName, fieldType, this)); } else if (att instanceof XmlElementRef) { XmlElementRef xmer = (XmlElementRef) att; String fieldName = xmer.getJavaAttribute(); String fieldType = xmer.getType(); fieldsToReturn.add(new OXMJavaFieldImpl(fieldName, fieldType, this)); } else if (att instanceof XmlAttribute) { XmlAttribute xma = (XmlAttribute) att; String fieldName = xma.getJavaAttribute(); String fieldType = xma.getType(); fieldsToReturn.add(new OXMJavaFieldImpl(fieldName, fieldType, this)); } else if (att instanceof XmlValue) { XmlValue xmv = (XmlValue) att; String fieldName = xmv.getJavaAttribute(); String fieldType = xmv.getType(); fieldsToReturn.add(new OXMJavaFieldImpl(fieldName, fieldType, this)); } else if (att instanceof XmlAnyElement) { XmlAnyElement xmae = (XmlAnyElement) att; String fieldName = xmae.getJavaAttribute(); String fieldType = JAVA_LANG_OBJECT; fieldsToReturn.add(new OXMJavaFieldImpl(fieldName, fieldType, this)); } else if (att instanceof XmlAnyAttribute) { XmlAnyAttribute xmaa = (XmlAnyAttribute) att; String fieldName = xmaa.getJavaAttribute(); String fieldType = JAVA_UTIL_MAP; fieldsToReturn.add(new OXMJavaFieldImpl(fieldName, fieldType, this)); } else if (att instanceof XmlJoinNodes) { XmlJoinNodes xmjn = (XmlJoinNodes) att; String fieldName = xmjn.getJavaAttribute(); String fieldType = xmjn.getType(); fieldsToReturn.add(new OXMJavaFieldImpl(fieldName, fieldType, this)); } else if (att instanceof XmlInverseReference) { XmlInverseReference xmir = (XmlInverseReference) att; String fieldName = xmir.getJavaAttribute(); String fieldType = xmir.getType(); fieldsToReturn.add(new OXMJavaFieldImpl(fieldName, fieldType, this)); } } } } return fieldsToReturn; } /** * Return the declared <code>JavaMethod</code> for this <code>JavaClass</code>, * identified by <code>name</code>, with the signature matching <code>args</code>. * * @param name the name of the <code>JavaMethod</code> to return. * @param args the parameter list used to identify the method. * * @return always returns <code>null</code>, as <code>JavaTypes</code> do not have methods. */ public JavaMethod getDeclaredMethod(String name, JavaClass[] args) { return null; } /** * Return all of the declared <code>JavaMethods</code> for this <code>JavaClass</code>. * * @return always returns an empty <code>ArrayList</code>, as <code>JavaTypes</code> do not have methods. */ public Collection<JavaMethod> getDeclaredMethods() { return new ArrayList<JavaMethod>(); } /** * Return the <code>JavaMethod</code> for this <code>JavaClass</code>, * identified by <code>name</code>, with the signature matching <code>args</code>. * * @param name the name of the <code>JavaMethod</code> to return. * @param args the parameter list used to identify the method. * * @return always returns <code>null</code>, as <code>JavaTypes</code> do not have methods. */ public JavaMethod getMethod(String name, JavaClass[] args) { return null; } /** * Return all of the <code>JavaMethods</code> for this <code>JavaClass</code>. * * @return always returns an empty <code>ArrayList</code>, as <code>JavaTypes</code> do not have methods. */ public Collection<JavaMethod> getMethods() { return new ArrayList<JavaMethod>(); } /** * Returns the Java language modifiers for this <code>JavaClass</code>, encoded in an integer. * * @return always returns <code>0</code> as <code>JavaTypes</code> do not have modifiers. * * @see java.lang.reflect.Modifier */ public int getModifiers() { return 0; } /** * Returns the name of this <code>JavaClass</code>. * * @return the <code>String</code> name of this <code>JavaClass</code>. */ public String getName() { if (this.javaType != null) { return this.javaType.getName(); } return this.javaName; } /** * Returns the <code>JavaPackage</code> that this <code>JavaClass</code> belongs to. * * @return the <code>JavaPackage</code> of this <code>JavaClass</code>. */ public JavaPackage getPackage() { return new OXMJavaPackageImpl(getPackageName()); } /** * Returns the package name of this <code>JavaClass</code>. * * @return the <code>String</code> name of this <code>JavaClass'</code> <code>JavaPackage</code>. */ public String getPackageName() { int lastDotIndex = getQualifiedName().lastIndexOf(DOT); if (lastDotIndex == -1) { return EMPTY_STRING; } return getQualifiedName().substring(0, lastDotIndex); } /** * Returns the fully-qualified name of this <code>JavaClass</code>. * * @return the <code>String</code> name of this <code>JavaClass</code>. */ public String getQualifiedName() { return getName(); } /** * Returns the raw name of this <code>JavaClass</code>. Array types will * have "[]" appended to the name. * * @return the <code>String</code> raw name of this <code>JavaClass</code>. */ public String getRawName() { return getName(); } /** * Returns the super class of this <code>JavaClass</code>. * * @return <code>JavaClass</code> representing the super class of this <code>JavaClass</code>. */ public JavaClass getSuperclass() { if (this.javaModel == null) { return null; } if (this.javaType != null) { if (!(this.javaType.getSuperType().equals(XMLProcessor.DEFAULT))) { return this.javaModel.getClass(javaType.getSuperType()); } } return this.javaModel.getClass(JAVA_LANG_OBJECT); } @Override public Type[] getGenericInterfaces() { return new Type[0]; } public Type getGenericSuperclass() { return null; } /** * Indicates if this <code>JavaClass</code> has actual type arguments, i.e. is a * parameterized type (for example, <code>List<Employee</code>). * * @return always returns <code>false</code> as <code>JavaTypes</code> are not parameterized. */ public boolean hasActualTypeArguments() { return false; } /** * Indicates if this <code>JavaClass</code> is <code>abstract</code>. * * @return always returns <code>false</code> as <code>JavaTypes</code> are never <code>abstract</code>. */ public boolean isAbstract() { return false; } /** * Indicates if this <code>JavaClass</code> is an <code>Annotation</code>. * * @return always returns <code>false</code> as <code>JavaTypes</code> are never <code>Annotations</code>. */ public boolean isAnnotation() { return false; } /** * Indicates if this <code>JavaClass</code> is an Array type. * * @return always returns <code>false</code>, as <code>JavaTypes</code> do not represent arrays. */ public boolean isArray() { return false; } /** * Indicates if this <code>JavaClass</code> is either the same as, or is a superclass of, * the <code>javaClass</code> argument. * * @param arg0 the <code>Class</code> to test. * * @return <code>true</code> if this <code>JavaClass</code> is assignable from * <code>javaClass</code>, otherwise <code>false</code>. * * @see java.lang.Class#isAssignableFrom(Class) */ @SuppressWarnings("unchecked") public boolean isAssignableFrom(JavaClass arg0) { String thisJavaName = EMPTY_STRING; String argJavaName = arg0.getName(); if (this.javaName != null) { thisJavaName = this.javaName; } else { thisJavaName = this.javaType.getName(); } if (thisJavaName.startsWith(JAVA) && argJavaName.startsWith(JAVA)) { // Only try class lookup if this is a JDK class, because // we won't ever find classes for dynamically generated types. try { Class thisClass = PrivilegedAccessHelper.getClassForName(thisJavaName); Class argClass = PrivilegedAccessHelper.getClassForName(argJavaName); return thisClass.isAssignableFrom(argClass); } catch (Exception e) { return false; } } else { return thisJavaName.equals(argJavaName); } } /** * Indicates if this <code>JavaClass</code> is an <code>enum</code>. * * @return <code>true</code> if this <code>JavaClass</code> is an <code>enum</code>, otherwise <code>false</code>. */ public boolean isEnum() { return this.enumValues != null; } /** * Indicates if this <code>JavaClass</code> is <code>final</code>. * * @return <code>true</code> if this <code>JavaClass</code> is <code>final</code>, otherwise <code>false</code>. */ public boolean isFinal() { return false; } /** * Indicates if this <code>JavaClass</code> is an <code>interface</code>. * * @return <code>true</code> if this <code>JavaClass</code> is an <code>interface</code>, otherwise <code>false</code>. */ public boolean isInterface() { return false; } /** * Indicates if this <code>JavaClass</code> is an inner <code>Class</code>. * * @return <code>true</code> if this <code>JavaClass</code> is an inner <code>Class</code>, otherwise <code>false</code>. */ public boolean isMemberClass() { return false; } /** * Indicates if this <code>JavaClass</code> represents a primitive type. * * @return <code>true</code> if this <code>JavaClass</code> represents a primitive type, otherwise <code>false</code>. */ public boolean isPrimitive() { return false; } /** * Indicates if this <code>JavaClass</code> is <code>private</code>. * * @return <code>true</code> if this <code>JavaClass</code> is <code>private</code>, otherwise <code>false</code>. */ public boolean isPrivate() { return false; } /** * Indicates if this <code>JavaClass</code> is <code>protected</code>. * * @return <code>true</code> if this <code>JavaClass</code> is <code>protected</code>, otherwise <code>false</code>. */ public boolean isProtected() { return false; } /** * Indicates if this <code>JavaClass</code> is <code>public</code>. * * @return <code>true</code> if this <code>JavaClass</code> is <code>public</code>, otherwise <code>false</code>. */ public boolean isPublic() { return false; } /** * Indicates if this <code>JavaClass</code> is <code>static</code>. * * @return <code>true</code> if this <code>JavaClass</code> is <code>static</code>, otherwise <code>false</code>. */ public boolean isStatic() { return false; } /** * Not supported. */ public boolean isSynthetic() { throw new UnsupportedOperationException("isSynthetic"); } @Override public JavaClassInstanceOf instanceOf() { return JavaClassInstanceOf.OXM_JAVA_CLASS_IMPL; } /** * If this <code>JavaClass</code> is annotated with an <code>Annotation</code> matching <code>aClass</code>, * return its <code>JavaAnnotation</code> representation. * * @param aClass a <code>JavaClass</code> representing the <code>Annotation</code> to look for. * * @return always returns <code>null</code>, as <code>JavaTypes</code> do not have <code>Annotations</code>. */ public JavaAnnotation getAnnotation(JavaClass aClass) { return null; } /** * Return all of the <code>Annotations</code> for this <code>JavaClass</code>. * * @return always returns an empty <code>ArrayList</code>, as <code>JavaTypes</code> do not have <code>Annotations</code>. */ public Collection<JavaAnnotation> getAnnotations() { return new ArrayList<JavaAnnotation>(); } /** * If this <code>JavaClass</code> declares an <code>Annotation</code> matching <code>aClass</code>, * return its <code>JavaAnnotation</code> representation. * * @param arg0 a <code>JavaClass</code> representing the <code>Annotation</code> to look for. * * @return always returns <code>null</code>, as <code>JavaTypes</code> do not have <code>Annotations</code>. */ public JavaAnnotation getDeclaredAnnotation(JavaClass arg0) { return null; } /** * Return all of the declared <code>Annotations</code> for this <code>JavaClass</code>. * * @return always returns an empty <code>ArrayList</code>, as <code>JavaTypes</code> do not have <code>Annotations</code>. */ public Collection<JavaAnnotation> getDeclaredAnnotations() { return new ArrayList<JavaAnnotation>(); } /** * Set this <code>JavaClass'</code> <code>JavaModel</code>. * * @param model The <code>JavaModel</code> to set. */ public void setJavaModel(JavaModel model) { this.javaModel = model; } /** * Get this <code>JavaClass'</code> <code>JavaModel</code>. * * @return The <code>JavaModel</code> associated with this <code>JavaClass</code>. */ public JavaModel getJavaModel() { return this.javaModel; } // ======================================================================== private static String EMPTY_STRING = ""; private static String JAVA = "java"; private static String DOT = "."; private static String JAVA_LANG_OBJECT = "java.lang.Object"; private static String JAVA_UTIL_MAP = "java.util.Map"; }