org.eclipse.persistence.internal.jpa.metadata.accessors.objects.MetadataAsmFactory Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of eclipselink Show documentation
Show all versions of eclipselink Show documentation
EclipseLink build based upon Git transaction 180e602
/*******************************************************************************
* Copyright (c) 1998, 2014 Oracle, Hans Harz, Andrew Rustleund. 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:
* James Sutherland - initial impl
* 05/14/2010-2.1 Guy Pelletier
* - 253083: Add support for dynamic persistence using ORM.xml/eclipselink-orm.xml
* Hans Harz, Andrew Rustleund - Bug 324862 - IndexOutOfBoundsException in
* DatabaseSessionImpl.initializeDescriptors because @MapKey Annotation is not found.
* 04/21/2011-2.3 dclarke: Upgraded to support ASM 3.3.1
* 08/10/2011-2.3 Lloyd Fernandes : Bug 336133 - Validation error during processing on parameterized generic OneToMany Entity relationship from MappedSuperclass
* 10/05/2012-2.4.1 Guy Pelletier
* - 373092: Exceptions using generics, embedded key and entity inheritance
* 19/04/2014-2.6 Lukas Jungmann
* - 429992: JavaSE 8/ASM 5.0.1 support (EclipseLink silently ignores Entity classes with lambda expressions)
******************************************************************************/
package org.eclipse.persistence.internal.jpa.metadata.accessors.objects;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.persistence.internal.helper.Helper;
import org.eclipse.persistence.internal.jpa.metadata.MetadataDescriptor;
import org.eclipse.persistence.internal.jpa.metadata.MetadataLogger;
import org.eclipse.persistence.internal.libraries.asm.AnnotationVisitor;
import org.eclipse.persistence.internal.libraries.asm.Attribute;
import org.eclipse.persistence.internal.libraries.asm.ClassReader;
import org.eclipse.persistence.internal.libraries.asm.ClassVisitor;
import org.eclipse.persistence.internal.libraries.asm.FieldVisitor;
import org.eclipse.persistence.internal.libraries.asm.MethodVisitor;
import org.eclipse.persistence.internal.libraries.asm.Opcodes;
import org.eclipse.persistence.internal.libraries.asm.Type;
/**
* INTERNAL: A metadata factory that uses ASM technology and no reflection
* whatsoever to process the metadata model.
*
* @author James Sutherland
* @since EclipseLink 1.2
*/
public class MetadataAsmFactory extends MetadataFactory {
/** Set of primitive type codes. */
public static final String PRIMITIVES = "VJIBZCSFD";
/** Set of desc token characters. */
public static final String TOKENS = "()<>;";
/**
* INTERNAL:
*/
public MetadataAsmFactory(MetadataLogger logger, ClassLoader loader) {
super(logger, loader);
addMetadataClass("I", new MetadataClass(this, int.class));
addMetadataClass("J", new MetadataClass(this, long.class));
addMetadataClass("S", new MetadataClass(this, short.class));
addMetadataClass("Z", new MetadataClass(this, boolean.class));
addMetadataClass("F", new MetadataClass(this, float.class));
addMetadataClass("D", new MetadataClass(this, double.class));
addMetadataClass("C", new MetadataClass(this, char.class));
addMetadataClass("B", new MetadataClass(this, byte.class));
}
/**
* Build the class metadata for the class name using ASM to read the class
* byte codes.
*/
protected void buildClassMetadata(MetadataClass metadataClass, String className, boolean isLazy) {
ClassMetadataVisitor visitor = new ClassMetadataVisitor(metadataClass, isLazy);
InputStream stream = null;
try {
String resourceString = className.replace('.', '/') + ".class";
stream = m_loader.getResourceAsStream(resourceString);
ClassReader reader = new ClassReader(stream);
Attribute[] attributes = new Attribute[0];
reader.accept(visitor, attributes, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
} catch (Exception exception) {
// Some basic types can't be found, so can just be registered
// (i.e. arrays). Also, VIRTUAL classes may also not exist,
// therefore, tag the MetadataClass as loadable false. This will be
// used to determine if a class will be dynamically created or not.
metadataClass = new MetadataClass(this, className, false);
// If the class is a JDK class, then maybe there is a class loader issues,
// since it is a JDK class, just use reflection.
if ((className.length() > 5) && className.substring(0, 5).equals("java.")) {
try {
Class reflectClass = Class.forName(className);
if (reflectClass.getSuperclass() != null) {
metadataClass.setSuperclassName(reflectClass.getSuperclass().getName());
}
for (Class reflectInterface : reflectClass.getInterfaces()) {
metadataClass.addInterface(reflectInterface.getName());
}
} catch (Exception failed) {
metadataClass.setIsAccessible(false);
}
} else {
metadataClass.setIsAccessible(false);
}
addMetadataClass(metadataClass);
} finally {
try {
if (stream != null) {
stream.close();
}
} catch (IOException ignore) {
// Ignore.
}
}
}
/**
* Return the class metadata for the class name.
*/
public MetadataClass getMetadataClass(String className) {
return getMetadataClass(className, false);
}
/**
* Return the class metadata for the class name.
*/
public MetadataClass getMetadataClass(String className, boolean isLazy) {
if (className == null) {
return null;
}
MetadataClass metaClass = m_metadataClasses.get(className);
if ((metaClass == null) || (!isLazy && metaClass.isLazy())) {
if (metaClass != null) {
metaClass.setIsLazy(false);
}
buildClassMetadata(metaClass, className, isLazy);
metaClass = m_metadataClasses.get(className);
}
return metaClass;
}
/**
* INTERNAL: This method resolves generic types based on the ASM class
* metadata. Unless every other factory (e.g. APT mirror factory) respects
* the generic format as built from ASM this method will not work since it
* is very tied to it.
*/
public void resolveGenericTypes(MetadataClass child, List genericTypes, MetadataClass parent, MetadataDescriptor descriptor) {
// If we have a generic parent we need to grab our generic types
// that may be used (and therefore need to be resolved) to map
// accessors correctly.
if (genericTypes != null) {
// The generic types provided map to its parents generic types. The
// generics also include the superclass, and interfaces. The parent
// generics include the type and ":" and class.
List parentGenericTypes = parent.getGenericType();
if (parentGenericTypes != null) {
List genericParentTemp = new ArrayList(genericTypes);
genericParentTemp.removeAll(child.getInterfaces());
int size = genericParentTemp.size();
int parentIndex = 0;
for (int index = genericTypes.indexOf(parent.getName()) + 1; index < size; index++) {
String actualTypeArgument = genericTypes.get(index);
// Ignore extra types on the end of the child, such as
// interface generics.
if (parentIndex >= parentGenericTypes.size()) {
break;
}
String variable = parentGenericTypes.get(parentIndex);
// if we get as far as the superclass name in the parent generic type list,
// there is nothing more to process. We have processed all the generics in the type definition
if (variable.equals(parent.getSuperclassName())){
break;
}
parentIndex = parentIndex + 3;
// We are building bottom up and need to link up any
// TypeVariables with the actual class from the originating
// entity.
if (actualTypeArgument.length() == 1) {
index++;
actualTypeArgument = genericTypes.get(index);
descriptor.addGenericType(variable, descriptor.getGenericType(actualTypeArgument));
} else {
descriptor.addGenericType(variable, actualTypeArgument);
}
}
}
}
}
/**
* Walk the class byte codes and collect the class info.
*/
public class ClassMetadataVisitor extends ClassVisitor {
private boolean isLazy;
private boolean processedMemeber;
private MetadataClass classMetadata;
ClassMetadataVisitor(MetadataClass metadataClass, boolean isLazy) {
super(Opcodes.ASM5);
this.isLazy = isLazy;
this.classMetadata = metadataClass;
}
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
String className = toClassName(name);
if ((this.classMetadata == null) || !this.classMetadata.getName().equals(className)) {
this.classMetadata = new MetadataClass(MetadataAsmFactory.this, className, isLazy);
addMetadataClass(this.classMetadata);
}
this.classMetadata.setName(className);
this.classMetadata.setSuperclassName(toClassName(superName));
this.classMetadata.setModifiers(access);
this.classMetadata.setGenericType(processDescription(signature, true));
for (String interfaceName : interfaces) {
this.classMetadata.addInterface(toClassName(interfaceName));
}
}
public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
this.processedMemeber = true;
if (this.classMetadata.isLazy()) {
return null;
}
return new MetadataFieldVisitor(this.classMetadata, access, name, desc, signature, value);
}
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
this.processedMemeber = true;
if (this.classMetadata.isLazy() || name.indexOf("init>") != -1) {
return null;
}
return new MetadataMethodVisitor(this.classMetadata, access, name, signature, desc, exceptions);
}
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
if (desc.startsWith("Ljavax/persistence") || desc.startsWith("Lorg/eclipse/persistence")) {
// If an annotation is found, parse the whole class.
if (!this.processedMemeber && this.classMetadata.isLazy()) {
this.classMetadata.setIsLazy(false);
}
return new MetadataAnnotationVisitor(this.classMetadata, desc);
}
return null;
}
}
/**
* {@link AnnotationVisitor} used to process class, field , and method
* annotations populating a {@link MetadataAnnotation} and its nested state.
*
* @see MetadataAnnotationArrayVisitor for population of array attributes
*/
class MetadataAnnotationVisitor extends AnnotationVisitor {
/**
* Element the annotation is being applied to. If this is null the
* {@link MetadataAnnotation} being constructed is a nested annotation
* and is already referenced from its parent.
*/
private MetadataAnnotatedElement element;
/**
* {@link MetadataAnnotation} being populated
*/
private MetadataAnnotation annotation;
MetadataAnnotationVisitor(MetadataAnnotatedElement element, String name) {
super(Opcodes.ASM5);
this.element = element;
this.annotation = new MetadataAnnotation();
this.annotation.setName(processDescription(name, false).get(0));
}
public MetadataAnnotationVisitor(MetadataAnnotation annotation) {
super(Opcodes.ASM5);
this.annotation = annotation;
}
public void visit(String name, Object value) {
this.annotation.addAttribute(name, annotationValue(null, value));
}
public void visitEnum(String name, String desc, String value) {
this.annotation.addAttribute(name, annotationValue(desc, value));
}
public AnnotationVisitor visitAnnotation(String name, String desc) {
MetadataAnnotation mda = new MetadataAnnotation();
mda.setName(processDescription(desc, false).get(0));
this.annotation.addAttribute(name, mda);
return new MetadataAnnotationVisitor(mda);
}
public AnnotationVisitor visitArray(String name) {
return new MetadataAnnotationArrayVisitor(this.annotation, name);
}
public void visitEnd() {
if (this.element != null) {
this.element.addAnnotation(this.annotation);
}
}
}
/**
* Specialized visitor to handle the population of arrays of annotation
* values.
*/
class MetadataAnnotationArrayVisitor extends AnnotationVisitor {
private MetadataAnnotation annotation;
private String attributeName;
private List