mirror of https://github.com/apache/openjpa.git
OPENJPA-1008: Facade OpenJPA metamodel to JPA 2.0
git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@769478 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
9d52998c1e
commit
7adb7932e8
|
@ -46,6 +46,7 @@ import org.apache.openjpa.kernel.Broker;
|
|||
import org.apache.openjpa.meta.ClassMetaData;
|
||||
import org.apache.openjpa.persistence.JPAFacadeHelper;
|
||||
import org.apache.openjpa.persistence.OpenJPAEntityManagerFactorySPI;
|
||||
import org.apache.openjpa.persistence.OpenJPAPersistence;
|
||||
|
||||
/**
|
||||
* Base test class providing persistence utilities.
|
||||
|
@ -527,12 +528,13 @@ public abstract class PersistenceTestCase
|
|||
}
|
||||
|
||||
public int hashCode() {
|
||||
return unit.hashCode() + config.hashCode();
|
||||
return (unit != null ? unit.hashCode() : 0) + config.hashCode();
|
||||
}
|
||||
|
||||
public boolean equals(Object other) {
|
||||
EMFKey that = (EMFKey)other;
|
||||
return unit.equals(that.unit) && config.equals(that.config);
|
||||
return (unit != null ? unit.equals(that.unit) : that.unit == null)
|
||||
&& config.equals(that.config);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,6 +46,8 @@ import org.apache.openjpa.lib.conf.ProductDerivations;
|
|||
import org.apache.openjpa.lib.conf.Value;
|
||||
import org.apache.openjpa.lib.util.Localizer;
|
||||
import org.apache.openjpa.lib.util.Closeable;
|
||||
import org.apache.openjpa.meta.MetaDataRepository;
|
||||
import org.apache.openjpa.persistence.meta.MetamodelImpl;
|
||||
import org.apache.openjpa.persistence.query.OpenJPAQueryBuilder;
|
||||
import org.apache.openjpa.persistence.query.QueryBuilderImpl;
|
||||
import org.apache.openjpa.util.OpenJPAException;
|
||||
|
@ -69,7 +71,8 @@ public class EntityManagerFactoryImpl
|
|||
private transient Constructor<FetchPlan> _plan = null;
|
||||
private transient StoreCache _cache = null;
|
||||
private transient QueryResultCache _queryCache = null;
|
||||
|
||||
private transient MetamodelImpl _metaModel;
|
||||
|
||||
/**
|
||||
* Default constructor provided for auto-instantiation.
|
||||
*/
|
||||
|
@ -354,8 +357,7 @@ public class EntityManagerFactoryImpl
|
|||
}
|
||||
|
||||
public QueryBuilder getQueryBuilder() {
|
||||
throw new UnsupportedOperationException(
|
||||
"JPA 2.0 - Method not yet implemented");
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public OpenJPAQueryBuilder getDynamicQueryBuilder() {
|
||||
|
@ -366,8 +368,11 @@ public class EntityManagerFactoryImpl
|
|||
return _factory.getSupportedProperties();
|
||||
}
|
||||
|
||||
public Metamodel getMetamodel() {
|
||||
throw new UnsupportedOperationException(
|
||||
"JPA 2.0 - Method not yet implemented");
|
||||
public MetamodelImpl getMetamodel() {
|
||||
if (_metaModel == null) {
|
||||
_metaModel = new MetamodelImpl(getConfiguration()
|
||||
.getMetaDataRepositoryInstance());
|
||||
}
|
||||
return _metaModel;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,14 +5,9 @@ import static javax.lang.model.SourceVersion.RELEASE_6;
|
|||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.annotation.Generated;
|
||||
|
@ -23,52 +18,37 @@ import javax.annotation.processing.RoundEnvironment;
|
|||
import javax.annotation.processing.SupportedAnnotationTypes;
|
||||
import javax.annotation.processing.SupportedOptions;
|
||||
import javax.annotation.processing.SupportedSourceVersion;
|
||||
import javax.lang.model.element.AnnotationMirror;
|
||||
import javax.lang.model.element.AnnotationValue;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.ElementKind;
|
||||
import javax.lang.model.element.ExecutableElement;
|
||||
import javax.lang.model.element.Modifier;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.lang.model.element.VariableElement;
|
||||
import javax.lang.model.type.ArrayType;
|
||||
import javax.lang.model.type.DeclaredType;
|
||||
import javax.lang.model.type.PrimitiveType;
|
||||
import javax.lang.model.type.TypeKind;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
import javax.lang.model.util.Elements;
|
||||
import javax.lang.model.util.Types;
|
||||
import javax.persistence.Access;
|
||||
import javax.persistence.AccessType;
|
||||
import javax.persistence.Transient;
|
||||
import javax.persistence.metamodel.TypesafeMetamodel;
|
||||
import javax.tools.Diagnostic;
|
||||
import javax.tools.JavaFileObject;
|
||||
|
||||
import org.apache.openjpa.lib.util.Localizer;
|
||||
import org.apache.openjpa.persistence.util.SourceCode;
|
||||
import org.apache.openjpa.util.OpenJPAException;
|
||||
import org.apache.openjpa.util.UserException;
|
||||
|
||||
/**
|
||||
* Annotation processing tool to generate/instantiate a meta-model given the
|
||||
* annotated source code of persistent domain model.
|
||||
* Annotation processing tool generates souce code for a meta-model class given
|
||||
* the annotated source code of persistent entity.
|
||||
* <p>
|
||||
* This tool is invoked as part of compilation phase for JDK6 compiler provided
|
||||
* OpenJPA and JPA class libraries are specified in the compiler
|
||||
* <code>-processorpath</code> option. <br>
|
||||
* For example<br>
|
||||
* <code>$ javac -processorpath path/to/openjpa.jar;/path/to/jpa.jar
|
||||
* src/mypackage/MyClass.java</code>
|
||||
* <br>
|
||||
* will generate source code for canonical meta-model for
|
||||
* <code>mypackage.MyClass</code> (if it is annotated with persistence
|
||||
* annotation <code>Entity or Embedded or MappedSuperclass</code>) to produce a
|
||||
* file <code>mypackage/MyClass_.java</code>.
|
||||
* This tool is invoked during compilation for JDK6 compiler if OpenJPA and JPA
|
||||
* libraries are specified in the compiler <code>-processorpath</code> option.
|
||||
* <br>
|
||||
* For example,<br>
|
||||
* <center><code>$ javac -processorpath path/to/openjpa;/path/to/jpa
|
||||
* -s src -Alog mypackage/MyClass.java</code></center>
|
||||
* <p>
|
||||
* will generate source code for canonical meta-model class at
|
||||
* <code>src/mypackage/MyClass_.java</code>.
|
||||
* <p>
|
||||
* The generated source code is written relative to the source path root which
|
||||
* is, by default, the current directory or as specified by -s option to
|
||||
* <code>javac</code> compiler.
|
||||
* <p>
|
||||
* Currently the only recognized option is <code>-Alog</code> specified as shown
|
||||
* in the <code>javac</code> command above.
|
||||
*
|
||||
* @author Pinaki Poddar
|
||||
*
|
||||
|
@ -83,31 +63,11 @@ import org.apache.openjpa.util.UserException;
|
|||
@SupportedSourceVersion(RELEASE_6)
|
||||
|
||||
public class AnnotationProcessor6 extends AbstractProcessor {
|
||||
private static final String UNDERSCORE = "_";
|
||||
private SourceAnnotationHandler handler;
|
||||
|
||||
|
||||
/**
|
||||
* Set of Inclusion Filters based on member type, access type or transient
|
||||
* annotations. Used to determine the subset of available field/method that
|
||||
* are persistent.
|
||||
*/
|
||||
static AccessFilter propertyAccessFilter =
|
||||
new AccessFilter(AccessType.PROPERTY);
|
||||
static AccessFilter fieldAccessFilter =
|
||||
new AccessFilter(AccessType.FIELD);
|
||||
|
||||
static KindFilter fieldFilter = new KindFilter(ElementKind.FIELD);
|
||||
static KindFilter methodFilter = new KindFilter(ElementKind.METHOD);
|
||||
|
||||
static GetterFilter getterFilter = new GetterFilter();
|
||||
static SetterFilter setterFilter = new SetterFilter();
|
||||
|
||||
static NonTransientMemberFilter nonTransientFilter =
|
||||
new NonTransientMemberFilter();
|
||||
static AnnotatedMemberFilter annotatedFilter = new AnnotatedMemberFilter();
|
||||
|
||||
private static Localizer _loc =
|
||||
Localizer.forPackage(AnnotationProcessor6.class);
|
||||
private static final String UNDERSCORE = "_";
|
||||
|
||||
/**
|
||||
* Category of members as per JPA 2.0 type system.
|
||||
|
@ -129,7 +89,6 @@ public class AnnotationProcessor6 extends AbstractProcessor {
|
|||
public String getMetaModelType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -184,6 +143,7 @@ public class AnnotationProcessor6 extends AbstractProcessor {
|
|||
super.init(processingEnv);
|
||||
processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE,
|
||||
_loc.get("mmg-tool-banner").getMessage());
|
||||
handler = new SourceAnnotationHandler(processingEnv);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -207,7 +167,7 @@ public class AnnotationProcessor6 extends AbstractProcessor {
|
|||
* @return true if code is generated for the given element. false otherwise.
|
||||
*/
|
||||
private boolean process(TypeElement e) {
|
||||
if (!isAnnotatedAsEntity(e)) {
|
||||
if (!handler.isAnnotatedAsEntity(e)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -219,18 +179,18 @@ public class AnnotationProcessor6 extends AbstractProcessor {
|
|||
SourceCode source = new SourceCode(metaClass);
|
||||
comment(source);
|
||||
annotate(source, originalClass);
|
||||
TypeElement supCls = getPCSuperclass(e);
|
||||
TypeElement supCls = handler.getPersistentSupertype(e);
|
||||
if (supCls != null)
|
||||
source.getTopLevelClass().setSuper(supCls.toString() + UNDERSCORE);
|
||||
try {
|
||||
PrintWriter writer = createSourceFile(metaClass, e);
|
||||
SourceCode.Class modelClass = source.getTopLevelClass();
|
||||
List<? extends Element> members = getPersistentMembers(e);
|
||||
Set<? extends Element> members = handler.getPersistentMembers(e);
|
||||
|
||||
for (Element m : members) {
|
||||
TypeMirror decl = getDeclaredType(m);
|
||||
String fieldName = getPersistentMemberName(m);
|
||||
String fieldType = getDeclaredTypeName(decl, true);
|
||||
TypeMirror decl = handler.getDeclaredType(m);
|
||||
String fieldName = handler.getPersistentMemberName(m);
|
||||
String fieldType = handler.getDeclaredTypeName(decl);
|
||||
TypeCategory typeCategory = toMetaModelTypeCategory(fieldType);
|
||||
String metaModelType = typeCategory.getMetaModelType();
|
||||
SourceCode.Field modelField = null;
|
||||
|
@ -243,17 +203,17 @@ public class AnnotationProcessor6 extends AbstractProcessor {
|
|||
case COLLECTION:
|
||||
case LIST:
|
||||
case SET:
|
||||
TypeMirror param = getTypeParameter(decl, 0);
|
||||
String elementType = getDeclaredTypeName(param, true);
|
||||
TypeMirror param = handler.getTypeParameter(decl, 0);
|
||||
String elementType = handler.getDeclaredTypeName(param);
|
||||
modelField = modelClass.addField(fieldName, metaModelType);
|
||||
modelField.addParameter(originalSimpleClass)
|
||||
.addParameter(elementType);
|
||||
break;
|
||||
case MAP:
|
||||
TypeMirror key = getTypeParameter(decl, 0);
|
||||
TypeMirror value = getTypeParameter(decl, 1);
|
||||
String keyType = getDeclaredTypeName(key, true);
|
||||
String valueType = getDeclaredTypeName(value, true);
|
||||
TypeMirror key = handler.getTypeParameter(decl, 0);
|
||||
TypeMirror value = handler.getTypeParameter(decl, 1);
|
||||
String keyType = handler.getDeclaredTypeName(key);
|
||||
String valueType = handler.getDeclaredTypeName(value);
|
||||
modelField = modelClass.addField(fieldName, metaModelType);
|
||||
modelField.addParameter(originalSimpleClass)
|
||||
.addParameter(keyType)
|
||||
|
@ -295,602 +255,10 @@ public class AnnotationProcessor6 extends AbstractProcessor {
|
|||
PrintWriter writer = new PrintWriter(out);
|
||||
return writer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get access type of the given class, if specified explicitly.
|
||||
* null otherwise.
|
||||
*
|
||||
* @param type
|
||||
* @return FIELD or PROPERTY
|
||||
*/
|
||||
AccessType getExplicitAccessType(TypeElement type) {
|
||||
Object access = getAnnotationValue(type, Access.class);
|
||||
if (equalsByValue(AccessType.FIELD, access))
|
||||
return AccessType.FIELD;
|
||||
if (equalsByValue(AccessType.PROPERTY, access))
|
||||
return AccessType.PROPERTY;
|
||||
return null;
|
||||
}
|
||||
|
||||
String getPersistentMemberName(Element e) {
|
||||
return isMethod(e) ? extractFieldName((ExecutableElement)e)
|
||||
: e.getSimpleName().toString();
|
||||
}
|
||||
|
||||
String extractFieldName(ExecutableElement method) {
|
||||
String name = method.getSimpleName().toString();
|
||||
String head = isNormalGetter(method) ? "get" : "is";
|
||||
name = name.substring(head.length());
|
||||
return Character.toLowerCase(name.charAt(0)) + name.substring(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of persistent fields and/or methods for the given type.
|
||||
*
|
||||
* Scans relevant @AccessType annotation and field/method as per JPA
|
||||
* specification to determine the candidate set of field/methods.
|
||||
*/
|
||||
private List<Element> getPersistentMembers(TypeElement type) {
|
||||
AccessType access = getExplicitAccessType(type);
|
||||
boolean isExplicit = access != null;
|
||||
|
||||
return isExplicit ? access == AccessType.FIELD
|
||||
? getFieldAccessPersistentMembers(type)
|
||||
: getPropertyAccessPersistentMembers(type)
|
||||
: getDefaultAccessPersistentMembers(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect members for the given type which uses explicit field access.
|
||||
*/
|
||||
private List<Element> getFieldAccessPersistentMembers(TypeElement type) {
|
||||
List<? extends Element> allMembers = type.getEnclosedElements();
|
||||
Set<VariableElement> allFields = (Set<VariableElement>)
|
||||
filter(allMembers, fieldFilter, nonTransientFilter);
|
||||
Set<ExecutableElement> allMethods = (Set<ExecutableElement>)
|
||||
filter(allMembers, methodFilter, nonTransientFilter);
|
||||
Set<ExecutableElement> getters = filter(allMethods, getterFilter,
|
||||
propertyAccessFilter, annotatedFilter);
|
||||
Set<ExecutableElement> setters = filter(allMethods, setterFilter);
|
||||
getters = matchGetterAndSetter(getters, setters);
|
||||
|
||||
return merge(getters, allFields);
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect members for the given type which uses explicit field access.
|
||||
*/
|
||||
private List<Element> getPropertyAccessPersistentMembers(TypeElement type)
|
||||
{
|
||||
List<? extends Element> allMembers = type.getEnclosedElements();
|
||||
Set<ExecutableElement> allMethods = (Set<ExecutableElement>)
|
||||
filter(allMembers, methodFilter, nonTransientFilter);
|
||||
|
||||
Set<ExecutableElement> getters = filter(allMethods, getterFilter);
|
||||
Set<ExecutableElement> setters = filter(allMethods, setterFilter);
|
||||
getters = matchGetterAndSetter(getters, setters);
|
||||
|
||||
return merge(filter(allMembers, fieldFilter, nonTransientFilter,
|
||||
fieldAccessFilter), getters);
|
||||
}
|
||||
|
||||
|
||||
private List<Element> getDefaultAccessPersistentMembers(TypeElement type) {
|
||||
List<Element> result = new ArrayList<Element>();
|
||||
List<? extends Element> allMembers = type.getEnclosedElements();
|
||||
Set<VariableElement> allFields = (Set<VariableElement>)
|
||||
filter(allMembers, fieldFilter, nonTransientFilter);
|
||||
Set<ExecutableElement> allMethods = (Set<ExecutableElement>)
|
||||
filter(allMembers, methodFilter, nonTransientFilter);
|
||||
|
||||
Set<VariableElement> annotatedFields = filter(allFields,
|
||||
annotatedFilter);
|
||||
Set<ExecutableElement> getters = filter(allMethods, getterFilter,
|
||||
annotatedFilter);
|
||||
Set<ExecutableElement> setters = filter(allMethods, setterFilter);
|
||||
getters = matchGetterAndSetter(getters, setters);
|
||||
|
||||
boolean isFieldAccess = !annotatedFields.isEmpty();
|
||||
boolean isPropertyAccess = !getters.isEmpty();
|
||||
|
||||
if (isFieldAccess && isPropertyAccess) {
|
||||
throw new UserException(_loc.get("access-mixed", type,
|
||||
toString(annotatedFields), toString(getters)));
|
||||
}
|
||||
if (isFieldAccess) {
|
||||
result.addAll(annotatedFields);
|
||||
} else if (isPropertyAccess) {
|
||||
result.addAll(getters);
|
||||
} else {
|
||||
warn(_loc.get("access-none", type).toString());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
List<Element> merge(Set<? extends Element> a, Set<? extends Element> b) {
|
||||
List<Element> result = new ArrayList<Element>();
|
||||
result.addAll(a);
|
||||
for (Element e1 : b) {
|
||||
boolean hide = false;
|
||||
String key = getPersistentMemberName(e1);
|
||||
for (Element e2 : a) {
|
||||
if (getPersistentMemberName(e2).equals(key)) {
|
||||
hide = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!hide) {
|
||||
result.add(e1);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// =========================================================================
|
||||
// Annotation processing utilities
|
||||
// =========================================================================
|
||||
|
||||
/**
|
||||
* Affirms if the given element is annotated with <em>any</em>
|
||||
* <code>javax.persistence.*</code> or <code>org.apache.openjpa.*</code>
|
||||
* annotation.
|
||||
*/
|
||||
private static boolean isAnnotated(Element e) {
|
||||
return isAnnotatedWith(e, (Set<String>)null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Affirms if the given declaration has the given annotation.
|
||||
*/
|
||||
private static boolean isAnnotatedWith(Element e,
|
||||
Class<? extends Annotation> anno) {
|
||||
return e != null && e.getAnnotation(anno) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Affirms if the given element is annotated with any of the given
|
||||
* annotations.
|
||||
*
|
||||
* @param annos null checks for any annotation that starts with
|
||||
* 'javax.persistence.' or 'openjpa.*'.
|
||||
*
|
||||
*/
|
||||
private static boolean isAnnotatedWith(Element e, Set<String> annos) {
|
||||
if (e == null)
|
||||
return false;
|
||||
List<? extends AnnotationMirror> mirrors = e.getAnnotationMirrors();
|
||||
if (annos == null) {
|
||||
for (AnnotationMirror mirror : mirrors) {
|
||||
String name = mirror.getAnnotationType().toString();
|
||||
if (startsWith(name, "javax.persistence.")
|
||||
|| startsWith(name, "openjpa."))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
for (AnnotationMirror mirror : mirrors) {
|
||||
String name = mirror.getAnnotationType().toString();
|
||||
if (annos.contains(name))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Affirms if the given list contains one of the registered annotation that
|
||||
* designates an entity type.
|
||||
*/
|
||||
private boolean isAnnotatedAsEntity(TypeElement e) {
|
||||
return isAnnotatedWith(e, getSupportedAnnotationTypes());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the element name of the class the given mirror represents. If the
|
||||
* mirror is primitive then returns the corresponding boxed class name.
|
||||
* If the mirror is parameterized returns only the generic type i.e.
|
||||
* if the given declared type is
|
||||
* <code>java.util.Set<java.lang.String></code> this method will
|
||||
* return <code>java.util.Set</code>.
|
||||
*/
|
||||
private String getDeclaredTypeName(TypeMirror mirror, boolean box) {
|
||||
if (mirror.getKind() == TypeKind.ARRAY) {
|
||||
TypeMirror comp = ((ArrayType)mirror).getComponentType();
|
||||
return getDeclaredTypeName(comp, false)+"[]";
|
||||
}
|
||||
mirror = box? box(mirror) : mirror;
|
||||
if (isPrimitive(mirror))
|
||||
return ((PrimitiveType)mirror).toString();
|
||||
return processingEnv.getTypeUtils().asElement(mirror).toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the declared type of the given member. For fields, returns the
|
||||
* declared type while for method returns the return type.
|
||||
*
|
||||
* @param e a field or method.
|
||||
* @exception if given member is neither a field nor a method.
|
||||
*/
|
||||
private TypeMirror getDeclaredType(Element e) {
|
||||
TypeMirror result = null;
|
||||
switch (e.getKind()) {
|
||||
case FIELD:
|
||||
result = e.asType();
|
||||
break;
|
||||
case METHOD:
|
||||
result = ((ExecutableElement) e).getReturnType();
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException(toDetails(e));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Affirms if the given type mirrors a primitive.
|
||||
*/
|
||||
private boolean isPrimitive(TypeMirror mirror) {
|
||||
TypeKind kind = mirror.getKind();
|
||||
return kind == TypeKind.BOOLEAN
|
||||
|| kind == TypeKind.BYTE
|
||||
|| kind == TypeKind.CHAR
|
||||
|| kind == TypeKind.DOUBLE
|
||||
|| kind == TypeKind.FLOAT
|
||||
|| kind == TypeKind.INT
|
||||
|| kind == TypeKind.LONG
|
||||
|| kind == TypeKind.SHORT;
|
||||
}
|
||||
|
||||
TypeMirror box(TypeMirror t) {
|
||||
if (isPrimitive(t))
|
||||
return processingEnv.getTypeUtils()
|
||||
.boxedClass((PrimitiveType)t).asType();
|
||||
return t;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the parameter type argument at the given index of the given type.
|
||||
*
|
||||
* @return if the given type represents a parameterized type, then the
|
||||
* indexed parameter type argument. Otherwise null.
|
||||
*/
|
||||
private TypeMirror getTypeParameter(TypeMirror mirror, int index) {
|
||||
if (mirror.getKind() != TypeKind.DECLARED)
|
||||
return null;
|
||||
List<? extends TypeMirror> params = ((DeclaredType)mirror)
|
||||
.getTypeArguments();
|
||||
return (params == null || params.size() < index+1)
|
||||
? null : params.get(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the nearest super class of the given class which is persistent.
|
||||
*
|
||||
* @return null if no such super class exists.
|
||||
*/
|
||||
private TypeElement getPCSuperclass(TypeElement cls) {
|
||||
TypeMirror sup = cls.getSuperclass();
|
||||
if (sup == null || isRootObject(sup))
|
||||
return null;
|
||||
TypeElement supe =
|
||||
(TypeElement) processingEnv.getTypeUtils().asElement(sup);
|
||||
if (isAnnotatedAsEntity(supe))
|
||||
return supe;
|
||||
return getPCSuperclass(supe);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the value of the given annotation, if present, in the given
|
||||
* declaration. Otherwise, null.
|
||||
*/
|
||||
private static Object getAnnotationValue(Element decl,
|
||||
Class<? extends Annotation> anno) {
|
||||
return getAnnotationValue(decl, anno, "value");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the given attribute of the given annotation, if
|
||||
* present, in the given declaration. Otherwise, null.
|
||||
*/
|
||||
private static Object getAnnotationValue(Element e,
|
||||
Class<? extends Annotation> anno, String attr) {
|
||||
if (e == null || e.getAnnotation(anno) == null)
|
||||
return null;
|
||||
List<? extends AnnotationMirror> annos = e.getAnnotationMirrors();
|
||||
for (AnnotationMirror mirror : annos) {
|
||||
if (mirror.getAnnotationType().toString().equals(anno.getName())) {
|
||||
Map<? extends ExecutableElement, ? extends AnnotationValue>
|
||||
values = mirror.getElementValues();
|
||||
for (ExecutableElement ex : values.keySet()) {
|
||||
if (ex.getSimpleName().toString().equals(attr))
|
||||
return values.get(ex).getValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches the given getters with the given setters. Removes the getters
|
||||
* that do not have a corresponding setter.
|
||||
*/
|
||||
private Set<ExecutableElement> matchGetterAndSetter(
|
||||
Set<ExecutableElement> getters, Set<ExecutableElement> setters) {
|
||||
Collection<ExecutableElement> unmatched =
|
||||
new ArrayList<ExecutableElement>();
|
||||
Types typeUtils = processingEnv.getTypeUtils();
|
||||
|
||||
for (ExecutableElement getter : getters) {
|
||||
String getterName = getter.getSimpleName().toString();
|
||||
TypeMirror getterReturnType = getter.getReturnType();
|
||||
String expectedSetterName = "set" + getterName.substring(
|
||||
(isBooleanGetter(getter) ? "is" : "get").length());
|
||||
boolean matched = false;
|
||||
for (ExecutableElement setter : setters) {
|
||||
TypeMirror setterArgType = setter.getParameters()
|
||||
.iterator().next().asType();
|
||||
String actualSetterName = setter.getSimpleName().toString();
|
||||
matched = actualSetterName.equals(expectedSetterName)
|
||||
&& typeUtils.isSameType(setterArgType, getterReturnType);
|
||||
if (matched)
|
||||
break;
|
||||
}
|
||||
if (!matched) {
|
||||
warn(_loc.get("getter-unmatched", getter,
|
||||
getter.getEnclosingElement()).toString());
|
||||
unmatched.add(getter);
|
||||
}
|
||||
|
||||
}
|
||||
getters.removeAll(unmatched);
|
||||
return getters;
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// Selection Filters select specific elements from a collection.
|
||||
// ========================================================================
|
||||
|
||||
/**
|
||||
* Inclusive element filtering predicate.
|
||||
*
|
||||
*/
|
||||
private static interface InclusiveFilter<T extends Element> {
|
||||
/**
|
||||
* Return true to include the given element.
|
||||
*/
|
||||
boolean includes(T e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the given collection with the conjunction of filters. The given
|
||||
* collection itself is not modified.
|
||||
*/
|
||||
private <T extends Element> Set<T> filter(Collection<T> coll,
|
||||
InclusiveFilter... filters) {
|
||||
Set<T> result = new HashSet<T>();
|
||||
for (T e : coll) {
|
||||
boolean include = true;
|
||||
for (InclusiveFilter f : filters) {
|
||||
if (!f.includes(e)) {
|
||||
include = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (include)
|
||||
result.add(e);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects getter method. A getter method name starts with 'get', returns a
|
||||
* non-void type and has no argument. Or starts with 'is', returns a boolean
|
||||
* and has no argument.
|
||||
*
|
||||
*/
|
||||
static class GetterFilter implements InclusiveFilter<ExecutableElement> {
|
||||
public boolean includes(ExecutableElement method) {
|
||||
return isGetter(method);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects setter method. A setter method name starts with 'set', returns a
|
||||
* void and has single argument.
|
||||
*
|
||||
*/
|
||||
static class SetterFilter implements InclusiveFilter<ExecutableElement> {
|
||||
public boolean includes(ExecutableElement method) {
|
||||
return isSetter(method);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects elements which is annotated with @Access annotation and that
|
||||
* annotation has the given AccessType value.
|
||||
*
|
||||
*/
|
||||
static class AccessFilter implements InclusiveFilter<Element> {
|
||||
final AccessType target;
|
||||
|
||||
public AccessFilter(AccessType target) {
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
public boolean includes(Element obj) {
|
||||
Object value = getAnnotationValue(obj, Access.class);
|
||||
return equalsByValue(target, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects elements of given kind.
|
||||
*
|
||||
*/
|
||||
static class KindFilter implements InclusiveFilter<Element> {
|
||||
final ElementKind target;
|
||||
|
||||
public KindFilter(ElementKind target) {
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
public boolean includes(Element obj) {
|
||||
return obj.getKind() == target;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects all non-transient element.
|
||||
*/
|
||||
static class NonTransientMemberFilter implements InclusiveFilter<Element> {
|
||||
public boolean includes(Element obj) {
|
||||
boolean isTransient = isAnnotatedWith(obj, Transient.class)
|
||||
|| obj.getModifiers().contains(Modifier.TRANSIENT);
|
||||
return !isTransient;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects all annotated element.
|
||||
*/
|
||||
static class AnnotatedMemberFilter implements InclusiveFilter<Element> {
|
||||
public boolean includes(Element obj) {
|
||||
return isAnnotated(obj);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ========================================================================
|
||||
// Utilities
|
||||
// ========================================================================
|
||||
|
||||
/**
|
||||
* Affirms if the given mirror represents a primitive or non-primitive
|
||||
* boolean.
|
||||
*/
|
||||
private static boolean isBoolean(TypeMirror type) {
|
||||
return (type != null && (type.getKind() == TypeKind.BOOLEAN
|
||||
|| "java.lang.Boolean".equals(type.toString())));
|
||||
}
|
||||
|
||||
/**
|
||||
* Affirms if the given mirror represents a void.
|
||||
*/
|
||||
private static boolean isVoid(TypeMirror type) {
|
||||
return (type != null && type.getKind() == TypeKind.VOID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Affirms if the given element represents a method.
|
||||
*/
|
||||
private static boolean isMethod(Element e) {
|
||||
return e != null && ExecutableElement.class.isInstance(e)
|
||||
&& e.getKind() == ElementKind.METHOD;
|
||||
}
|
||||
|
||||
/**
|
||||
* Affirms if the given method matches the following signature
|
||||
* <code> public T getXXX() </code>
|
||||
* where T is any non-void type.
|
||||
*/
|
||||
private static boolean isNormalGetter(ExecutableElement method) {
|
||||
String methodName = method.getSimpleName().toString();
|
||||
return method.getKind() == ElementKind.METHOD
|
||||
&& startsWith(methodName, "get")
|
||||
&& method.getParameters().isEmpty()
|
||||
&& !isVoid(method.getReturnType());
|
||||
}
|
||||
|
||||
/**
|
||||
* Affirms if the given method matches the following signature
|
||||
* <code> public boolean isXXX() </code>
|
||||
* <code> public Boolean isXXX() </code>
|
||||
*/
|
||||
private static boolean isBooleanGetter(ExecutableElement method) {
|
||||
String methodName = method.getSimpleName().toString();
|
||||
return method.getKind() == ElementKind.METHOD
|
||||
&& startsWith(methodName, "is")
|
||||
&& method.getParameters().isEmpty()
|
||||
&& isBoolean(method.getReturnType());
|
||||
}
|
||||
|
||||
private static boolean isGetter(ExecutableElement method) {
|
||||
return isNormalGetter(method) || isBooleanGetter(method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Affirms if the given method matches the following signature
|
||||
* <code> public void setXXX(T t) </code>
|
||||
*/
|
||||
private static boolean isSetter(ExecutableElement method) {
|
||||
String methodName = method.getSimpleName().toString();
|
||||
return method.getKind() == ElementKind.METHOD
|
||||
&& startsWith(methodName, "set")
|
||||
&& method.getParameters().size() == 1
|
||||
&& isVoid(method.getReturnType());
|
||||
}
|
||||
|
||||
/**
|
||||
* Affirms if the given mirror represents root java.lang.Object.
|
||||
*/
|
||||
private static boolean isRootObject(TypeMirror type) {
|
||||
return type != null && "java.lang.Object".equals(type.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Affirms if the given full string starts with the given head.
|
||||
*/
|
||||
private static boolean startsWith(String full, String head) {
|
||||
return full != null && full.startsWith(head)
|
||||
&& full.length() > head.length();
|
||||
}
|
||||
|
||||
/**
|
||||
* Affirms if the given enum equals the given value.
|
||||
*/
|
||||
private static boolean equalsByValue(Enum<?> e, Object v) {
|
||||
if (v == null)
|
||||
return false;
|
||||
return e.toString().equals(v.toString());
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Access rules
|
||||
// =========================================================================
|
||||
|
||||
|
||||
// =========================================================================
|
||||
// Logging
|
||||
// =========================================================================
|
||||
|
||||
private void log(String msg) {
|
||||
if (!processingEnv.getOptions().containsKey("log"))
|
||||
return;
|
||||
processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, msg);
|
||||
}
|
||||
|
||||
private void warn(String msg) {
|
||||
processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING, msg);
|
||||
}
|
||||
|
||||
private static String toString(Collection<? extends Element> elements) {
|
||||
StringBuilder tmp = new StringBuilder();
|
||||
int i = 0;
|
||||
for (Element e : elements) {
|
||||
tmp.append(e.getSimpleName() + (++i == elements.size() ? "" : ","));
|
||||
}
|
||||
return tmp.toString();
|
||||
}
|
||||
|
||||
String toDetails(Element e) {
|
||||
TypeMirror mirror = e.asType();
|
||||
return new StringBuffer(e.getKind().toString()).append(" ")
|
||||
.append(e.toString())
|
||||
.append("Mirror ")
|
||||
.append(mirror.getKind().toString())
|
||||
.append(mirror.toString()).toString();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,246 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.openjpa.persistence.meta;
|
||||
|
||||
import javax.persistence.metamodel.AbstractCollection;
|
||||
import javax.persistence.metamodel.ManagedType;
|
||||
import javax.persistence.metamodel.Type;
|
||||
|
||||
import org.apache.openjpa.meta.FieldMetaData;
|
||||
import org.apache.openjpa.meta.JavaTypes;
|
||||
|
||||
/**
|
||||
* Member according to JPA 2.0 metamodel.
|
||||
*
|
||||
* Implemented as a thin adapter to OpenJPA FieldMetadata.
|
||||
* Mostly immutable.
|
||||
*
|
||||
* @author Pinaki Poddar
|
||||
*
|
||||
* @since 2.0.0
|
||||
*
|
||||
*/
|
||||
public class Members {
|
||||
/**
|
||||
* Root of implementation hierarchy.
|
||||
*
|
||||
* @param <X> the class that owns this member
|
||||
* @param <Y> the class of the value held by this member
|
||||
*/
|
||||
public static abstract class Member<X, Y>
|
||||
implements javax.persistence.metamodel.Member<X, Y> {
|
||||
|
||||
public final Types.Managed<X> owner;
|
||||
public final FieldMetaData fmd;
|
||||
|
||||
protected Member(Types.Managed<X> owner, FieldMetaData fmd) {
|
||||
this.owner = owner;
|
||||
this.fmd = fmd;
|
||||
}
|
||||
|
||||
public final ManagedType<X> getDeclaringType() {
|
||||
return owner.model.type((Class<X>) fmd.getDeclaringType());
|
||||
}
|
||||
|
||||
public final java.lang.reflect.Member getJavaMember() {
|
||||
return fmd.getBackingMember();
|
||||
}
|
||||
|
||||
public final Class<Y> getMemberJavaType() {
|
||||
return (Class<Y>) fmd.getDeclaredType();
|
||||
}
|
||||
|
||||
public final String getName() {
|
||||
return fmd.getName();
|
||||
}
|
||||
|
||||
public final boolean isAssociation() {
|
||||
return fmd.isDeclaredTypePC();
|
||||
}
|
||||
|
||||
public final boolean isCollection() {
|
||||
return fmd.getDeclaredTypeCode() == JavaTypes.COLLECTION
|
||||
|| fmd.getDeclaredTypeCode() == JavaTypes.MAP;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return fmd.getName();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attributes are non-collection members.
|
||||
*
|
||||
* @param <X> the class that owns this member
|
||||
* @param <T> the class of the value held by this member
|
||||
*/
|
||||
public static final class Attribute<X, T> extends Member<X, T> implements
|
||||
javax.persistence.metamodel.Attribute<X, T> {
|
||||
|
||||
public Attribute(Types.Managed<X> owner, FieldMetaData fmd) {
|
||||
super(owner, fmd);
|
||||
}
|
||||
|
||||
public Multiplicity getMultiplicity() {
|
||||
throw new AbstractMethodError();
|
||||
}
|
||||
|
||||
public boolean isId() {
|
||||
return fmd.isPrimaryKey();
|
||||
}
|
||||
|
||||
public boolean isVersion() {
|
||||
return fmd.isVersion();
|
||||
}
|
||||
|
||||
public boolean isOptional() {
|
||||
return fmd.getNullValue() != FieldMetaData.NULL_EXCEPTION;
|
||||
}
|
||||
|
||||
public Type<T> getAttributeType() {
|
||||
return owner.model.type(fmd.getDeclaredType());
|
||||
}
|
||||
|
||||
public BindableType getBindableType() {
|
||||
return fmd.isDeclaredTypePC() ? BindableType.MANAGED_TYPE
|
||||
: BindableType.ATTRIBUTE;
|
||||
}
|
||||
|
||||
public Class<T> getJavaType() {
|
||||
return super.getMemberJavaType();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Root of collection members.
|
||||
*
|
||||
* @param <X> the class that owns this member
|
||||
* @param <C> the container class that holds this member
|
||||
* (e.g. java.util.Set)
|
||||
* @param <E> the class of the element held by this member
|
||||
*/
|
||||
public static abstract class BaseCollection<X, C, E> extends Member<X, C>
|
||||
implements AbstractCollection<X, C, E> {
|
||||
|
||||
public BaseCollection(Types.Managed<X> owner, FieldMetaData fmd) {
|
||||
super(owner, fmd);
|
||||
}
|
||||
|
||||
public final Type<E> getElementType() {
|
||||
return owner.model.getType(fmd.getElement().getDeclaredType());
|
||||
}
|
||||
|
||||
public final BindableType getBindableType() {
|
||||
return BindableType.COLLECTION;
|
||||
}
|
||||
|
||||
public final Class<E> getJavaType() {
|
||||
return fmd.getElement().getDeclaredType();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Members declared as java.util.Collection<E>.
|
||||
*/
|
||||
public static class Collection<X, E> extends
|
||||
BaseCollection<X, java.util.Collection<E>, E> implements
|
||||
javax.persistence.metamodel.Collection<X, E> {
|
||||
|
||||
public Collection(Types.Managed<X> owner, FieldMetaData fmd) {
|
||||
super(owner, fmd);
|
||||
}
|
||||
|
||||
public Multiplicity getMultiplicity() {
|
||||
return Multiplicity.ONE_TO_MANY;
|
||||
}
|
||||
|
||||
public CollectionType getCollectionType() {
|
||||
return CollectionType.COLLECTION;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Members declared as java.util.List<E>.
|
||||
*/
|
||||
public static class List<X, E> extends
|
||||
BaseCollection<X, java.util.List<E>, E> implements
|
||||
javax.persistence.metamodel.List<X, E> {
|
||||
|
||||
public List(Types.Managed<X> owner, FieldMetaData fmd) {
|
||||
super(owner, fmd);
|
||||
}
|
||||
|
||||
public Multiplicity getMultiplicity() {
|
||||
return Multiplicity.ONE_TO_MANY;
|
||||
}
|
||||
|
||||
public CollectionType getCollectionType() {
|
||||
return CollectionType.LIST;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Members declared as java.util.Set<E>.
|
||||
*/
|
||||
public static class Set<X, E> extends
|
||||
BaseCollection<X, java.util.Set<E>, E> implements
|
||||
javax.persistence.metamodel.Set<X, E> {
|
||||
|
||||
public Set(Types.Managed<X> owner, FieldMetaData fmd) {
|
||||
super(owner, fmd);
|
||||
}
|
||||
|
||||
public Multiplicity getMultiplicity() {
|
||||
return Multiplicity.ONE_TO_MANY;
|
||||
}
|
||||
|
||||
public CollectionType getCollectionType() {
|
||||
return CollectionType.SET;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Members declared as java.util.Map<K,V>.
|
||||
*/
|
||||
public static class Map<X, K, V> extends
|
||||
BaseCollection<X, java.util.Map<K, V>, V> implements
|
||||
javax.persistence.metamodel.Map<X, K, V> {
|
||||
|
||||
public Map(Types.Managed<X> owner, FieldMetaData fmd) {
|
||||
super(owner, fmd);
|
||||
}
|
||||
|
||||
public CollectionType getCollectionType() {
|
||||
return CollectionType.MAP;
|
||||
}
|
||||
|
||||
public Multiplicity getMultiplicity() {
|
||||
return Multiplicity.MANY_TO_MANY;
|
||||
}
|
||||
|
||||
public Class<K> getKeyJavaType() {
|
||||
return (Class<K>) fmd.getKey().getDeclaredType();
|
||||
}
|
||||
|
||||
public Type<K> getKeyType() {
|
||||
return owner.model.getType(getKeyJavaType());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
package org.apache.openjpa.persistence.meta;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Collection of generic utility functions for extracting persistence related
|
||||
* metadata from user specified metadata available in various source
|
||||
* environment.
|
||||
* <br>
|
||||
* Persistence metadata needs to be acquired from different sources such as
|
||||
* annotated source code, compiled class files, XML descriptors or combinations
|
||||
* thereof under different invocation and configuration context.
|
||||
* <br>
|
||||
* Specific implementation of this interface is distinguished by the nature of
|
||||
* the source and the representation available for type system in the source.
|
||||
*
|
||||
* @param T the M2 representation of type based on the nature of the source
|
||||
* e.g. {@linkplain TypeElement} for annotation processing of *.java files or
|
||||
* {@link Class} for compiled bytecode.
|
||||
*
|
||||
* @param M the corresponding M2 representation for member of type T
|
||||
*
|
||||
* @author Pinaki Poddar
|
||||
*
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public interface MetadataProcessor<T,M> {
|
||||
/**
|
||||
* Determine the access type of the given type.
|
||||
*
|
||||
* @return an integer denoting the type of access. The integer value
|
||||
* corresponds to {@linkplain ClassMetaData#getAccessType()}.
|
||||
*/
|
||||
public int determineTypeAccess(T t);
|
||||
|
||||
/**
|
||||
* Determine the access type of the given member.
|
||||
*
|
||||
* @return an integer denoting the type of access. The integer value
|
||||
* corresponds to {@linkplain FieldMetaData#getAccessType()}.
|
||||
*/
|
||||
public int determineMemberAccess(M m);
|
||||
|
||||
/**
|
||||
* Get the persistent members of the given type.
|
||||
*
|
||||
*/
|
||||
public Set<M> getPersistentMembers(T t);
|
||||
|
||||
/**
|
||||
* Gets the violations, if any.
|
||||
*
|
||||
* @return null or empty list if no exceptions.
|
||||
*/
|
||||
public List<Exception> validateAccess(T t);
|
||||
|
||||
/**
|
||||
* Affirms if the members of given type are using both field and property
|
||||
* based access.
|
||||
*/
|
||||
public boolean isMixedAccess(T t);
|
||||
|
||||
public T getPersistentSupertype(T t);
|
||||
}
|
|
@ -0,0 +1,200 @@
|
|||
package org.apache.openjpa.persistence.meta;
|
||||
|
||||
import static javax.persistence.metamodel.Type.PersistenceType.BASIC;
|
||||
import static javax.persistence.metamodel.Type.PersistenceType.EMBEDDABLE;
|
||||
import static javax.persistence.metamodel.Type.PersistenceType.ENTITY;
|
||||
import static
|
||||
javax.persistence.metamodel.Type.PersistenceType.MAPPED_SUPERCLASS;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.persistence.metamodel.Embeddable;
|
||||
import javax.persistence.metamodel.Entity;
|
||||
import javax.persistence.metamodel.ManagedType;
|
||||
import javax.persistence.metamodel.MappedSuperclass;
|
||||
import javax.persistence.metamodel.Metamodel;
|
||||
import javax.persistence.metamodel.Type;
|
||||
import javax.persistence.metamodel.AbstractCollection.CollectionType;
|
||||
import javax.persistence.metamodel.Type.PersistenceType;
|
||||
|
||||
import org.apache.openjpa.lib.util.J2DoPrivHelper;
|
||||
import org.apache.openjpa.lib.util.Localizer;
|
||||
import org.apache.openjpa.meta.ClassMetaData;
|
||||
import org.apache.openjpa.meta.MetaDataRepository;
|
||||
import org.apache.openjpa.util.InternalException;
|
||||
|
||||
/**
|
||||
* Adapts JPA Metamodel to OpenJPA meta-data repository.
|
||||
*
|
||||
* @author Pinaki Poddar
|
||||
*
|
||||
*/
|
||||
public class MetamodelImpl implements Metamodel {
|
||||
public final MetaDataRepository repos;
|
||||
Map<Class<?>, Entity<?>> _entities = new HashMap<Class<?>, Entity<?>>();
|
||||
Map<Class<?>, Embeddable<?>> _embeddables =
|
||||
new HashMap<Class<?>, Embeddable<?>>();
|
||||
Map<Class<?>, MappedSuperclass<?>> _mappedsupers =
|
||||
new HashMap<Class<?>, MappedSuperclass<?>>();
|
||||
Map<Class<?>, Type<?>> _basics = new HashMap<Class<?>, Type<?>>();
|
||||
|
||||
private static Localizer _loc = Localizer.forPackage(MetamodelImpl.class);
|
||||
|
||||
public MetamodelImpl(MetaDataRepository repos) {
|
||||
this.repos = repos;
|
||||
ClassMetaData[] metas = repos.getMetaDatas();
|
||||
for (ClassMetaData meta : metas) {
|
||||
PersistenceType type = getPersistenceType(meta);
|
||||
Class<?> cls = meta.getDescribedType();
|
||||
switch (type) {
|
||||
case ENTITY:
|
||||
find(cls, _entities, ENTITY);
|
||||
break;
|
||||
case EMBEDDABLE:
|
||||
find(cls, _embeddables, EMBEDDABLE);
|
||||
break;
|
||||
case MAPPED_SUPERCLASS:
|
||||
find(cls, _mappedsupers, MAPPED_SUPERCLASS);
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public <X> Embeddable<X> embeddable(Class<X> clazz) {
|
||||
return (Embeddable<X>)find(clazz, _embeddables, EMBEDDABLE);
|
||||
}
|
||||
|
||||
public <X> Entity<X> entity(Class<X> clazz) {
|
||||
return (Entity<X>) find(clazz, _entities, ENTITY);
|
||||
}
|
||||
|
||||
public Set<Embeddable<?>> getEmbeddables() {
|
||||
return unmodifiableSet(_embeddables.values());
|
||||
}
|
||||
|
||||
public Set<Entity<?>> getEntities() {
|
||||
return unmodifiableSet(_entities.values());
|
||||
}
|
||||
|
||||
public Set<ManagedType<?>> getManagedTypes() {
|
||||
Set<ManagedType<?>> result = new HashSet<ManagedType<?>>();
|
||||
result.addAll(_entities.values());
|
||||
result.addAll(_embeddables.values());
|
||||
result.addAll(_mappedsupers.values());
|
||||
return result;
|
||||
}
|
||||
|
||||
public <X> ManagedType<X> type(Class<X> clazz) {
|
||||
if (_entities.containsKey(clazz))
|
||||
return (Entity<X>) _entities.get(clazz);
|
||||
if (_embeddables.containsKey(clazz))
|
||||
return (Embeddable<X>) _embeddables.get(clazz);
|
||||
if (_mappedsupers.containsKey(clazz))
|
||||
return (MappedSuperclass<X>) _mappedsupers.get(clazz);
|
||||
throw new IllegalArgumentException(_loc.get("type-not-managed", clazz)
|
||||
.getMessage());
|
||||
}
|
||||
|
||||
public <X> Type<X> getType(Class<X> cls) {
|
||||
try {
|
||||
return type(cls);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
if (_basics.containsKey(cls))
|
||||
return (Type<X>)_basics.get(cls);
|
||||
Type<X> basic = new Types.Basic<X>(cls);
|
||||
_basics.put(cls, basic);
|
||||
return basic;
|
||||
}
|
||||
}
|
||||
|
||||
public static PersistenceType getPersistenceType(ClassMetaData meta) {
|
||||
if (meta == null)
|
||||
return BASIC;
|
||||
if (meta.isEmbeddedOnly())
|
||||
return meta.isAbstract() ? MAPPED_SUPERCLASS : EMBEDDABLE;
|
||||
return ENTITY;
|
||||
}
|
||||
|
||||
private <V extends ManagedType<?>> V find(Class<?> cls,
|
||||
Map<Class<?>,V> container, PersistenceType expected) {
|
||||
if (container.containsKey(cls))
|
||||
return container.get(cls);
|
||||
ClassMetaData meta = repos.getMetaData(cls, null, false);
|
||||
if (meta != null) {
|
||||
instantiate(cls, container, expected);
|
||||
}
|
||||
return container.get(cls);
|
||||
}
|
||||
|
||||
private <X,V extends ManagedType<?>> void instantiate(Class<X> cls,
|
||||
Map<Class<?>,V> container, PersistenceType expected) {
|
||||
ClassMetaData meta = repos.getMetaData(cls, null, true);
|
||||
PersistenceType actual = getPersistenceType(meta);
|
||||
if (actual != expected)
|
||||
throw new IllegalArgumentException(_loc.get("type-wrong-category",
|
||||
cls, actual, expected).getMessage());
|
||||
|
||||
switch (actual) {
|
||||
case EMBEDDABLE:
|
||||
Types.Embeddable<X> embedded = new Types.Embeddable<X>(meta, this);
|
||||
_embeddables.put(cls, embedded);
|
||||
populate(embedded);
|
||||
break;
|
||||
case ENTITY:
|
||||
Types.Entity<X> entity = new Types.Entity<X>(meta, this);
|
||||
_entities.put(cls, entity);
|
||||
populate(entity);
|
||||
break;
|
||||
case MAPPED_SUPERCLASS:
|
||||
Types.MappedSuper<X> mapped = new Types.MappedSuper<X>(meta, this);
|
||||
_mappedsupers.put(cls, mapped);
|
||||
populate(mapped);
|
||||
break;
|
||||
default:
|
||||
throw new InternalException(cls.getName());
|
||||
}
|
||||
}
|
||||
|
||||
public <T> Set<T> unmodifiableSet(Collection<T> coll) {
|
||||
HashSet<T> result = new HashSet<T>();
|
||||
for (T t : coll)
|
||||
result.add(t);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static CollectionType getCollectionType(Class<?> cls) {
|
||||
if (Set.class.isAssignableFrom(cls))
|
||||
return CollectionType.SET;
|
||||
if (List.class.isAssignableFrom(cls))
|
||||
return CollectionType.LIST;
|
||||
if (Collection.class.isAssignableFrom(cls))
|
||||
return CollectionType.COLLECTION;
|
||||
if (Map.class.isAssignableFrom(cls))
|
||||
return CollectionType.MAP;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate the static fields of the canonical type.
|
||||
*/
|
||||
public <X> void populate(Types.Managed<X> type) {
|
||||
try {
|
||||
Class<X> cls = type.getJavaType();
|
||||
Class<?> mcls = J2DoPrivHelper.getForNameAction(cls.getName()+"_",
|
||||
true, cls.getClassLoader()).run();
|
||||
Field[] fields = mcls.getFields();
|
||||
for (Field f : fields) {
|
||||
f.set(null, type.getMember(f.getName()));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,701 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.openjpa.persistence.meta;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.annotation.processing.ProcessingEnvironment;
|
||||
import javax.lang.model.element.AnnotationMirror;
|
||||
import javax.lang.model.element.AnnotationValue;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.ElementKind;
|
||||
import javax.lang.model.element.ExecutableElement;
|
||||
import javax.lang.model.element.Modifier;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.lang.model.element.VariableElement;
|
||||
import javax.lang.model.type.ArrayType;
|
||||
import javax.lang.model.type.DeclaredType;
|
||||
import javax.lang.model.type.PrimitiveType;
|
||||
import javax.lang.model.type.TypeKind;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
import javax.lang.model.util.Types;
|
||||
import javax.persistence.Access;
|
||||
import javax.persistence.AccessType;
|
||||
import static javax.persistence.AccessType.*;
|
||||
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.MappedSuperclass;
|
||||
import javax.persistence.Transient;
|
||||
import javax.tools.Diagnostic;
|
||||
import org.apache.openjpa.lib.util.Localizer;
|
||||
import org.apache.openjpa.util.UserException;
|
||||
|
||||
/**
|
||||
* Extracts persistent metadata information by analyzing available annotation
|
||||
* in *.java source files. Requires JDK6 Annotation Processing environment
|
||||
* available.
|
||||
*
|
||||
* @author Pinaki Poddar
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public class SourceAnnotationHandler
|
||||
implements MetadataProcessor<TypeElement, Element> {
|
||||
|
||||
private final ProcessingEnvironment processingEnv;
|
||||
|
||||
/**
|
||||
* Set of Inclusion Filters based on member type, access type or transient
|
||||
* annotations. Used to determine the subset of available field/method that
|
||||
* are persistent.
|
||||
*/
|
||||
protected AccessFilter propertyAccessFilter = new AccessFilter(PROPERTY);
|
||||
protected AccessFilter fieldAccessFilter = new AccessFilter(FIELD);
|
||||
|
||||
protected KindFilter fieldFilter = new KindFilter(ElementKind.FIELD);
|
||||
protected KindFilter methodFilter = new KindFilter(ElementKind.METHOD);
|
||||
protected TransientFilter nonTransientFilter = new TransientFilter();
|
||||
protected AnnotatedFilter annotatedFilter = new AnnotatedFilter();
|
||||
protected GetterFilter getterFilter = new GetterFilter();
|
||||
protected SetterFilter setterFilter = new SetterFilter();
|
||||
|
||||
private static Localizer _loc = Localizer.forPackage(
|
||||
SourceAnnotationHandler.class);
|
||||
|
||||
/**
|
||||
* Construct with JDK6 annotation processing environment.
|
||||
*
|
||||
*/
|
||||
public SourceAnnotationHandler(ProcessingEnvironment processingEnv) {
|
||||
super();
|
||||
this.processingEnv = processingEnv;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int determineTypeAccess(TypeElement t) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int determineMemberAccess(Element m) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Exception> validateAccess(TypeElement t) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean isMixedAccess(TypeElement t) {
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Gets the list of persistent fields and/or methods for the given type.
|
||||
*
|
||||
* Scans relevant @AccessType annotation and field/method as per JPA
|
||||
* specification to determine the candidate set of field/methods.
|
||||
*/
|
||||
@Override
|
||||
public Set<Element> getPersistentMembers(TypeElement type) {
|
||||
AccessType access = getExplicitAccessType(type);
|
||||
boolean isExplicit = access != null;
|
||||
|
||||
return isExplicit ? access == AccessType.FIELD
|
||||
? getFieldAccessPersistentMembers(type)
|
||||
: getPropertyAccessPersistentMembers(type)
|
||||
: getDefaultAccessPersistentMembers(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect members for the given type which uses explicit field access.
|
||||
*/
|
||||
private Set<Element> getFieldAccessPersistentMembers(TypeElement type) {
|
||||
List<? extends Element> allMembers = type.getEnclosedElements();
|
||||
Set<VariableElement> allFields = (Set<VariableElement>)
|
||||
filter(allMembers, fieldFilter, nonTransientFilter);
|
||||
Set<ExecutableElement> allMethods = (Set<ExecutableElement>)
|
||||
filter(allMembers, methodFilter, nonTransientFilter);
|
||||
Set<ExecutableElement> getters = filter(allMethods, getterFilter,
|
||||
propertyAccessFilter, annotatedFilter);
|
||||
Set<ExecutableElement> setters = filter(allMethods, setterFilter);
|
||||
getters = matchGetterAndSetter(getters, setters);
|
||||
|
||||
return merge(getters, allFields);
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect members for the given type which uses explicit field access.
|
||||
*/
|
||||
private Set<Element> getPropertyAccessPersistentMembers(TypeElement type)
|
||||
{
|
||||
List<? extends Element> allMembers = type.getEnclosedElements();
|
||||
Set<ExecutableElement> allMethods = (Set<ExecutableElement>)
|
||||
filter(allMembers, methodFilter, nonTransientFilter);
|
||||
|
||||
Set<ExecutableElement> getters = filter(allMethods, getterFilter);
|
||||
Set<ExecutableElement> setters = filter(allMethods, setterFilter);
|
||||
getters = matchGetterAndSetter(getters, setters);
|
||||
|
||||
return merge(filter(allMembers, fieldFilter, nonTransientFilter,
|
||||
fieldAccessFilter), getters);
|
||||
}
|
||||
|
||||
|
||||
private Set<Element> getDefaultAccessPersistentMembers(TypeElement type) {
|
||||
Set<Element> result = new HashSet<Element>();
|
||||
List<? extends Element> allMembers = type.getEnclosedElements();
|
||||
Set<VariableElement> allFields = (Set<VariableElement>)
|
||||
filter(allMembers, fieldFilter, nonTransientFilter);
|
||||
Set<ExecutableElement> allMethods = (Set<ExecutableElement>)
|
||||
filter(allMembers, methodFilter, nonTransientFilter);
|
||||
|
||||
Set<VariableElement> annotatedFields = filter(allFields,
|
||||
annotatedFilter);
|
||||
Set<ExecutableElement> getters = filter(allMethods, getterFilter,
|
||||
annotatedFilter);
|
||||
Set<ExecutableElement> setters = filter(allMethods, setterFilter);
|
||||
getters = matchGetterAndSetter(getters, setters);
|
||||
|
||||
boolean isFieldAccess = !annotatedFields.isEmpty();
|
||||
boolean isPropertyAccess = !getters.isEmpty();
|
||||
|
||||
if (isFieldAccess && isPropertyAccess) {
|
||||
throw new UserException(_loc.get("access-mixed", type,
|
||||
toString(annotatedFields), toString(getters)));
|
||||
}
|
||||
if (isFieldAccess) {
|
||||
result.addAll(annotatedFields);
|
||||
} else if (isPropertyAccess) {
|
||||
result.addAll(getters);
|
||||
} else {
|
||||
warn(_loc.get("access-none", type).toString());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Set<Element> merge(Set<? extends Element> a, Set<? extends Element> b) {
|
||||
Set<Element> result = new HashSet<Element>();
|
||||
result.addAll(a);
|
||||
for (Element e1 : b) {
|
||||
boolean hide = false;
|
||||
String key = getPersistentMemberName(e1);
|
||||
for (Element e2 : a) {
|
||||
if (getPersistentMemberName(e2).equals(key)) {
|
||||
hide = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!hide) {
|
||||
result.add(e1);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches the given getters with the given setters. Removes the getters
|
||||
* that do not have a corresponding setter.
|
||||
*/
|
||||
private Set<ExecutableElement> matchGetterAndSetter(
|
||||
Set<ExecutableElement> getters, Set<ExecutableElement> setters) {
|
||||
Collection<ExecutableElement> unmatched =
|
||||
new ArrayList<ExecutableElement>();
|
||||
Types typeUtils = processingEnv.getTypeUtils();
|
||||
|
||||
for (ExecutableElement getter : getters) {
|
||||
String getterName = getter.getSimpleName().toString();
|
||||
TypeMirror getterReturnType = getter.getReturnType();
|
||||
String expectedSetterName = "set" + getterName.substring(
|
||||
(isBooleanGetter(getter) ? "is" : "get").length());
|
||||
boolean matched = false;
|
||||
for (ExecutableElement setter : setters) {
|
||||
TypeMirror setterArgType = setter.getParameters()
|
||||
.iterator().next().asType();
|
||||
String actualSetterName = setter.getSimpleName().toString();
|
||||
matched = actualSetterName.equals(expectedSetterName)
|
||||
&& typeUtils.isSameType(setterArgType, getterReturnType);
|
||||
if (matched)
|
||||
break;
|
||||
}
|
||||
if (!matched) {
|
||||
warn(_loc.get("getter-unmatched", getter,
|
||||
getter.getEnclosingElement()).toString());
|
||||
unmatched.add(getter);
|
||||
}
|
||||
|
||||
}
|
||||
getters.removeAll(unmatched);
|
||||
return getters;
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// Selection Filters select specific elements from a collection.
|
||||
// ========================================================================
|
||||
|
||||
/**
|
||||
* Inclusive element filtering predicate.
|
||||
*
|
||||
*/
|
||||
private static interface InclusiveFilter<T extends Element> {
|
||||
/**
|
||||
* Return true to include the given element.
|
||||
*/
|
||||
boolean includes(T e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the given collection with the conjunction of filters. The given
|
||||
* collection itself is not modified.
|
||||
*/
|
||||
<T extends Element> Set<T> filter(Collection<T> coll,
|
||||
InclusiveFilter... filters) {
|
||||
Set<T> result = new HashSet<T>();
|
||||
for (T e : coll) {
|
||||
boolean include = true;
|
||||
for (InclusiveFilter f : filters) {
|
||||
if (!f.includes(e)) {
|
||||
include = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (include)
|
||||
result.add(e);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects getter method. A getter method name starts with 'get', returns a
|
||||
* non-void type and has no argument. Or starts with 'is', returns a boolean
|
||||
* and has no argument.
|
||||
*
|
||||
*/
|
||||
static class GetterFilter implements InclusiveFilter<ExecutableElement> {
|
||||
public boolean includes(ExecutableElement method) {
|
||||
return isGetter(method);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects setter method. A setter method name starts with 'set', returns a
|
||||
* void and has single argument.
|
||||
*
|
||||
*/
|
||||
static class SetterFilter implements InclusiveFilter<ExecutableElement> {
|
||||
public boolean includes(ExecutableElement method) {
|
||||
return isSetter(method);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects elements which is annotated with @Access annotation and that
|
||||
* annotation has the given AccessType value.
|
||||
*
|
||||
*/
|
||||
static class AccessFilter implements InclusiveFilter<Element> {
|
||||
final AccessType target;
|
||||
|
||||
public AccessFilter(AccessType target) {
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
public boolean includes(Element obj) {
|
||||
Object value = getAnnotationValue(obj, Access.class);
|
||||
return equalsByValue(target, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects elements of given kind.
|
||||
*
|
||||
*/
|
||||
static class KindFilter implements InclusiveFilter<Element> {
|
||||
final ElementKind target;
|
||||
|
||||
public KindFilter(ElementKind target) {
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
public boolean includes(Element obj) {
|
||||
return obj.getKind() == target;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects all non-transient element.
|
||||
*/
|
||||
static class TransientFilter implements InclusiveFilter<Element> {
|
||||
public boolean includes(Element obj) {
|
||||
boolean isTransient = isAnnotatedWith(obj, Transient.class)
|
||||
|| obj.getModifiers().contains(Modifier.TRANSIENT);
|
||||
return !isTransient;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects all annotated element.
|
||||
*/
|
||||
static class AnnotatedFilter implements InclusiveFilter<Element> {
|
||||
public boolean includes(Element obj) {
|
||||
return isAnnotated(obj);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get access type of the given class, if specified explicitly.
|
||||
* null otherwise.
|
||||
*
|
||||
* @param type
|
||||
* @return FIELD or PROPERTY
|
||||
*/
|
||||
AccessType getExplicitAccessType(TypeElement type) {
|
||||
Object access = getAnnotationValue(type, Access.class);
|
||||
if (equalsByValue(AccessType.FIELD, access))
|
||||
return AccessType.FIELD;
|
||||
if (equalsByValue(AccessType.PROPERTY, access))
|
||||
return AccessType.PROPERTY;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the given annotation, if present, in the given
|
||||
* declaration. Otherwise, null.
|
||||
*/
|
||||
public static Object getAnnotationValue(Element decl,
|
||||
Class<? extends Annotation> anno) {
|
||||
return getAnnotationValue(decl, anno, "value");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the given attribute of the given annotation, if
|
||||
* present, in the given declaration. Otherwise, null.
|
||||
*/
|
||||
public static Object getAnnotationValue(Element e,
|
||||
Class<? extends Annotation> anno, String attr) {
|
||||
if (e == null || e.getAnnotation(anno) == null)
|
||||
return null;
|
||||
List<? extends AnnotationMirror> annos = e.getAnnotationMirrors();
|
||||
for (AnnotationMirror mirror : annos) {
|
||||
if (mirror.getAnnotationType().toString().equals(anno.getName())) {
|
||||
Map<? extends ExecutableElement, ? extends AnnotationValue>
|
||||
values = mirror.getElementValues();
|
||||
for (ExecutableElement ex : values.keySet()) {
|
||||
if (ex.getSimpleName().toString().equals(attr))
|
||||
return values.get(ex).getValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String toString(Collection<? extends Element> elements) {
|
||||
StringBuilder tmp = new StringBuilder();
|
||||
int i = 0;
|
||||
for (Element e : elements) {
|
||||
tmp.append(e.getSimpleName() + (++i == elements.size() ? "" : ","));
|
||||
}
|
||||
return tmp.toString();
|
||||
}
|
||||
|
||||
String toDetails(Element e) {
|
||||
TypeMirror mirror = e.asType();
|
||||
return new StringBuffer(e.getKind().toString()).append(" ")
|
||||
.append(e.toString())
|
||||
.append("Mirror ")
|
||||
.append(mirror.getKind().toString())
|
||||
.append(mirror.toString()).toString();
|
||||
}
|
||||
|
||||
private void log(String msg) {
|
||||
if (!processingEnv.getOptions().containsKey("log"))
|
||||
return;
|
||||
processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, msg);
|
||||
}
|
||||
|
||||
private void warn(String msg) {
|
||||
processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING, msg);
|
||||
}
|
||||
|
||||
String getPersistentMemberName(Element e) {
|
||||
return isMethod(e) ? extractFieldName((ExecutableElement)e)
|
||||
: e.getSimpleName().toString();
|
||||
}
|
||||
|
||||
public String extractFieldName(ExecutableElement method) {
|
||||
String name = method.getSimpleName().toString();
|
||||
String head = isNormalGetter(method) ? "get" : "is";
|
||||
name = name.substring(head.length());
|
||||
return Character.toLowerCase(name.charAt(0)) + name.substring(1);
|
||||
}
|
||||
|
||||
|
||||
// =========================================================================
|
||||
// Annotation processing utilities
|
||||
// =========================================================================
|
||||
|
||||
/**
|
||||
* Affirms if the given element is annotated with <em>any</em>
|
||||
* <code>javax.persistence.*</code> or <code>org.apache.openjpa.*</code>
|
||||
* annotation.
|
||||
*/
|
||||
public static boolean isAnnotated(Element e) {
|
||||
return isAnnotatedWith(e, (Set<String>)null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Affirms if the given declaration has the given annotation.
|
||||
*/
|
||||
boolean isAnnotatedAsEntity(Element e) {
|
||||
return isAnnotatedWith(e, Entity.class)
|
||||
|| isAnnotatedWith(e, Embeddable.class)
|
||||
|| isAnnotatedWith(e, MappedSuperclass.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Affirms if the given declaration has the given annotation.
|
||||
*/
|
||||
public static boolean isAnnotatedWith(Element e,
|
||||
Class<? extends Annotation> anno) {
|
||||
return e != null && e.getAnnotation(anno) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Affirms if the given element is annotated with any of the given
|
||||
* annotations.
|
||||
*
|
||||
* @param annos null checks for any annotation that starts with
|
||||
* 'javax.persistence.' or 'openjpa.*'.
|
||||
*
|
||||
*/
|
||||
public static boolean isAnnotatedWith(Element e, Set<String> annos) {
|
||||
if (e == null)
|
||||
return false;
|
||||
List<? extends AnnotationMirror> mirrors = e.getAnnotationMirrors();
|
||||
if (annos == null) {
|
||||
for (AnnotationMirror mirror : mirrors) {
|
||||
String name = mirror.getAnnotationType().toString();
|
||||
if (startsWith(name, "javax.persistence.")
|
||||
|| startsWith(name, "openjpa."))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
for (AnnotationMirror mirror : mirrors) {
|
||||
String name = mirror.getAnnotationType().toString();
|
||||
if (annos.contains(name))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
String getDeclaredTypeName(TypeMirror mirror) {
|
||||
return getDeclaredTypeName(mirror, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the element name of the class the given mirror represents. If the
|
||||
* mirror is primitive then returns the corresponding boxed class name.
|
||||
* If the mirror is parameterized returns only the generic type i.e.
|
||||
* if the given declared type is
|
||||
* <code>java.util.Set<java.lang.String></code> this method will
|
||||
* return <code>java.util.Set</code>.
|
||||
*/
|
||||
String getDeclaredTypeName(TypeMirror mirror, boolean box) {
|
||||
if (mirror.getKind() == TypeKind.ARRAY) {
|
||||
TypeMirror comp = ((ArrayType)mirror).getComponentType();
|
||||
return getDeclaredTypeName(comp, false)+"[]";
|
||||
}
|
||||
mirror = box? box(mirror) : mirror;
|
||||
if (isPrimitive(mirror))
|
||||
return ((PrimitiveType)mirror).toString();
|
||||
return processingEnv.getTypeUtils().asElement(mirror).toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the declared type of the given member. For fields, returns the
|
||||
* declared type while for method returns the return type.
|
||||
*
|
||||
* @param e a field or method.
|
||||
* @exception if given member is neither a field nor a method.
|
||||
*/
|
||||
TypeMirror getDeclaredType(Element e) {
|
||||
TypeMirror result = null;
|
||||
switch (e.getKind()) {
|
||||
case FIELD:
|
||||
result = e.asType();
|
||||
break;
|
||||
case METHOD:
|
||||
result = ((ExecutableElement) e).getReturnType();
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException(toDetails(e));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Affirms if the given type mirrors a primitive.
|
||||
*/
|
||||
private boolean isPrimitive(TypeMirror mirror) {
|
||||
TypeKind kind = mirror.getKind();
|
||||
return kind == TypeKind.BOOLEAN
|
||||
|| kind == TypeKind.BYTE
|
||||
|| kind == TypeKind.CHAR
|
||||
|| kind == TypeKind.DOUBLE
|
||||
|| kind == TypeKind.FLOAT
|
||||
|| kind == TypeKind.INT
|
||||
|| kind == TypeKind.LONG
|
||||
|| kind == TypeKind.SHORT;
|
||||
}
|
||||
|
||||
public TypeMirror box(TypeMirror t) {
|
||||
if (isPrimitive(t))
|
||||
return processingEnv.getTypeUtils()
|
||||
.boxedClass((PrimitiveType)t).asType();
|
||||
return t;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the parameter type argument at the given index of the given type.
|
||||
*
|
||||
* @return if the given type represents a parameterized type, then the
|
||||
* indexed parameter type argument. Otherwise null.
|
||||
*/
|
||||
TypeMirror getTypeParameter(TypeMirror mirror, int index) {
|
||||
if (mirror.getKind() != TypeKind.DECLARED)
|
||||
return null;
|
||||
List<? extends TypeMirror> params = ((DeclaredType)mirror)
|
||||
.getTypeArguments();
|
||||
return (params == null || params.size() < index+1)
|
||||
? null : params.get(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypeElement getPersistentSupertype(TypeElement cls) {
|
||||
TypeMirror sup = cls.getSuperclass();
|
||||
if (sup == null || isRootObject(sup))
|
||||
return null;
|
||||
TypeElement supe =
|
||||
(TypeElement) processingEnv.getTypeUtils().asElement(sup);
|
||||
if (isAnnotatedAsEntity(supe))
|
||||
return supe;
|
||||
return getPersistentSupertype(supe);
|
||||
}
|
||||
|
||||
|
||||
// ========================================================================
|
||||
// Utilities
|
||||
// ========================================================================
|
||||
|
||||
/**
|
||||
* Affirms if the given mirror represents a primitive or non-primitive
|
||||
* boolean.
|
||||
*/
|
||||
public static boolean isBoolean(TypeMirror type) {
|
||||
return (type != null && (type.getKind() == TypeKind.BOOLEAN
|
||||
|| "java.lang.Boolean".equals(type.toString())));
|
||||
}
|
||||
|
||||
/**
|
||||
* Affirms if the given mirror represents a void.
|
||||
*/
|
||||
public static boolean isVoid(TypeMirror type) {
|
||||
return (type != null && type.getKind() == TypeKind.VOID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Affirms if the given element represents a method.
|
||||
*/
|
||||
public static boolean isMethod(Element e) {
|
||||
return e != null && ExecutableElement.class.isInstance(e)
|
||||
&& e.getKind() == ElementKind.METHOD;
|
||||
}
|
||||
|
||||
/**
|
||||
* Affirms if the given method matches the following signature
|
||||
* <code> public T getXXX() </code>
|
||||
* where T is any non-void type.
|
||||
*/
|
||||
public static boolean isNormalGetter(ExecutableElement method) {
|
||||
String methodName = method.getSimpleName().toString();
|
||||
return method.getKind() == ElementKind.METHOD
|
||||
&& startsWith(methodName, "get")
|
||||
&& method.getParameters().isEmpty()
|
||||
&& !isVoid(method.getReturnType());
|
||||
}
|
||||
|
||||
/**
|
||||
* Affirms if the given method matches the following signature
|
||||
* <code> public boolean isXXX() </code>
|
||||
* <code> public Boolean isXXX() </code>
|
||||
*/
|
||||
public static boolean isBooleanGetter(ExecutableElement method) {
|
||||
String methodName = method.getSimpleName().toString();
|
||||
return method.getKind() == ElementKind.METHOD
|
||||
&& startsWith(methodName, "is")
|
||||
&& method.getParameters().isEmpty()
|
||||
&& isBoolean(method.getReturnType());
|
||||
}
|
||||
|
||||
public static boolean isGetter(ExecutableElement method) {
|
||||
return isNormalGetter(method) || isBooleanGetter(method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Affirms if the given method matches the following signature
|
||||
* <code> public void setXXX(T t) </code>
|
||||
*/
|
||||
public static boolean isSetter(ExecutableElement method) {
|
||||
String methodName = method.getSimpleName().toString();
|
||||
return method.getKind() == ElementKind.METHOD
|
||||
&& startsWith(methodName, "set")
|
||||
&& method.getParameters().size() == 1
|
||||
&& isVoid(method.getReturnType());
|
||||
}
|
||||
|
||||
/**
|
||||
* Affirms if the given mirror represents root java.lang.Object.
|
||||
*/
|
||||
public static boolean isRootObject(TypeMirror type) {
|
||||
return type != null && "java.lang.Object".equals(type.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Affirms if the given full string starts with the given head.
|
||||
*/
|
||||
public static boolean startsWith(String full, String head) {
|
||||
return full != null && head != null && full.startsWith(head)
|
||||
&& full.length() > head.length();
|
||||
}
|
||||
|
||||
/**
|
||||
* Affirms if the given enum equals the given value.
|
||||
*/
|
||||
public static boolean equalsByValue(Enum<?> e, Object v) {
|
||||
return e == v
|
||||
|| (v != null && e != null && e.toString().equals(v.toString()));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,505 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.openjpa.persistence.meta;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Locale;
|
||||
|
||||
import javax.persistence.metamodel.AbstractCollection;
|
||||
import javax.persistence.metamodel.Attribute;
|
||||
import javax.persistence.metamodel.Collection;
|
||||
import javax.persistence.metamodel.IdentifiableType;
|
||||
import javax.persistence.metamodel.List;
|
||||
import javax.persistence.metamodel.ManagedType;
|
||||
import javax.persistence.metamodel.Map;
|
||||
import javax.persistence.metamodel.MappedSuperclass;
|
||||
import javax.persistence.metamodel.Member;
|
||||
import javax.persistence.metamodel.Set;
|
||||
import javax.persistence.metamodel.Type;
|
||||
|
||||
import org.apache.openjpa.lib.util.Localizer;
|
||||
import org.apache.openjpa.meta.ClassMetaData;
|
||||
import org.apache.openjpa.meta.FieldMetaData;
|
||||
import org.apache.openjpa.meta.JavaTypes;
|
||||
|
||||
/**
|
||||
* Type according to JPA 2.0.
|
||||
*
|
||||
* Implemented as a thin adapter to OpenJPA metadata system.
|
||||
*
|
||||
* @author Pinaki Poddar
|
||||
*
|
||||
* @since 2.0.0
|
||||
*
|
||||
*/
|
||||
public class Types {
|
||||
protected static Localizer _loc = Localizer.forPackage(Types.class);
|
||||
|
||||
/**
|
||||
* Mirrors a concrete Java type X.
|
||||
*
|
||||
* @param <X> Java class
|
||||
*/
|
||||
private static abstract class BaseType<X> implements Type<X> {
|
||||
public final Class<X> cls;
|
||||
|
||||
protected BaseType(Class<X> cls) {
|
||||
this.cls = cls;
|
||||
}
|
||||
|
||||
public final Class<X> getJavaType() {
|
||||
return cls;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "" + cls;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Basic<X> extends BaseType<X> implements Type<X> {
|
||||
public Basic(Class<X> cls) {
|
||||
super(cls);
|
||||
}
|
||||
|
||||
public PersistenceType getPersistenceType() {
|
||||
return PersistenceType.BASIC;
|
||||
}
|
||||
}
|
||||
|
||||
public static abstract class Managed<X> extends BaseType<X> implements
|
||||
ManagedType<X> {
|
||||
|
||||
public final MetamodelImpl model;
|
||||
public final ClassMetaData meta;
|
||||
|
||||
private java.util.Map<FieldMetaData, Attribute<? super X, ?>> attrs =
|
||||
new HashMap<FieldMetaData, Attribute<? super X, ?>>();
|
||||
|
||||
private java.util.Map<FieldMetaData, AbstractCollection<? super X,?,?>>
|
||||
colls = new HashMap<FieldMetaData, AbstractCollection<? super X,?,?>>();
|
||||
|
||||
public Managed(ClassMetaData meta, MetamodelImpl model) {
|
||||
super((Class<X>)meta.getDescribedType());
|
||||
this.model = model;
|
||||
this.meta = meta;
|
||||
FieldMetaData[] fmds = meta.getFields();
|
||||
for (FieldMetaData f : fmds) {
|
||||
int decCode = f.getDeclaredTypeCode();
|
||||
switch (decCode) {
|
||||
case JavaTypes.BOOLEAN:
|
||||
case JavaTypes.BOOLEAN_OBJ:
|
||||
attrs.put(f, new Members.Attribute<X, Boolean>(this, f));
|
||||
break;
|
||||
case JavaTypes.BYTE:
|
||||
case JavaTypes.BYTE_OBJ:
|
||||
attrs.put(f, new Members.Attribute<X, Byte>(this, f));
|
||||
break;
|
||||
case JavaTypes.CHAR:
|
||||
case JavaTypes.CHAR_OBJ:
|
||||
attrs.put(f, new Members.Attribute<X, Character>(this, f));
|
||||
break;
|
||||
case JavaTypes.DOUBLE:
|
||||
case JavaTypes.DOUBLE_OBJ:
|
||||
attrs.put(f, new Members.Attribute<X, Double>(this, f));
|
||||
break;
|
||||
case JavaTypes.FLOAT:
|
||||
case JavaTypes.FLOAT_OBJ:
|
||||
attrs.put(f, new Members.Attribute<X, Float>(this, f));
|
||||
break;
|
||||
case JavaTypes.INT:
|
||||
case JavaTypes.INT_OBJ:
|
||||
attrs.put(f, new Members.Attribute<X, Integer>(this, f));
|
||||
break;
|
||||
case JavaTypes.LONG:
|
||||
case JavaTypes.LONG_OBJ:
|
||||
attrs.put(f, new Members.Attribute<X, Long>(this, f));
|
||||
break;
|
||||
case JavaTypes.SHORT:
|
||||
case JavaTypes.SHORT_OBJ:
|
||||
attrs.put(f, new Members.Attribute<X, Short>(this, f));
|
||||
break;
|
||||
case JavaTypes.STRING:
|
||||
attrs.put(f, new Members.Attribute<X, String>(this, f));
|
||||
break;
|
||||
case JavaTypes.NUMBER:
|
||||
attrs.put(f, new Members.Attribute<X, Number>(this, f));
|
||||
break;
|
||||
case JavaTypes.DATE:
|
||||
attrs.put(f, new Members.Attribute<X, Date>(this, f));
|
||||
break;
|
||||
case JavaTypes.CALENDAR:
|
||||
attrs.put(f, new Members.Attribute<X, Calendar>(this, f));
|
||||
break;
|
||||
case JavaTypes.BIGDECIMAL:
|
||||
attrs.put(f, new Members.Attribute<X, BigDecimal>(this, f));
|
||||
break;
|
||||
case JavaTypes.BIGINTEGER:
|
||||
attrs.put(f, new Members.Attribute<X, BigInteger>(this, f));
|
||||
break;
|
||||
case JavaTypes.LOCALE:
|
||||
attrs.put(f, new Members.Attribute<X, Locale>(this, f));
|
||||
break;
|
||||
case JavaTypes.PC:
|
||||
attrs.put(f, new Members.Attribute(this, f));
|
||||
break;
|
||||
case JavaTypes.OBJECT:
|
||||
attrs.put(f, new Members.Attribute(this, f));
|
||||
break;
|
||||
case JavaTypes.COLLECTION:
|
||||
switch (model.getCollectionType(f.getDeclaredType())) {
|
||||
case COLLECTION:
|
||||
colls.put(f, new Members.Collection(this, f));
|
||||
break;
|
||||
case LIST:
|
||||
colls.put(f, new Members.List(this, f));
|
||||
break;
|
||||
case SET:
|
||||
colls.put(f, new Members.Set(this, f));
|
||||
break;
|
||||
case MAP:
|
||||
colls.put(f, new Members.Map(this, f));
|
||||
break;
|
||||
}
|
||||
}
|
||||
// TODO: Account for the following codes
|
||||
// case ARRAY = 11;
|
||||
// case PC_UNTYPED = 27;
|
||||
// case OID = 29;
|
||||
// case INPUT_STREAM = 30;
|
||||
// case INPUT_READER = 31;
|
||||
}
|
||||
}
|
||||
|
||||
public Member<? super X,?> getMember(String name) {
|
||||
FieldMetaData fmd = meta.getField(name);
|
||||
if (attrs.containsKey(fmd))
|
||||
return attrs.get(fmd);
|
||||
if (colls.containsKey(fmd))
|
||||
return colls.get(fmd);
|
||||
return null;
|
||||
}
|
||||
|
||||
public <Y> Attribute<? super X, Y> getAttribute(String name,
|
||||
Class<Y> type) {
|
||||
return (Attribute<? super X, Y>) attrs.get(getField(name, type));
|
||||
}
|
||||
|
||||
public <Y> Attribute<X, Y> getDeclaredAttribute(String name,
|
||||
Class<Y> type) {
|
||||
return (Attribute<X, Y>) attrs.get(getField(name, type, true));
|
||||
}
|
||||
|
||||
public <E> Collection<? super X, E> getCollection(String name,
|
||||
Class<E> elementType) {
|
||||
return getCollectionMember(name, java.util.Collection.class,
|
||||
elementType, false);
|
||||
}
|
||||
|
||||
public <E> Set<? super X, E> getSet(String name, Class<E> elementType) {
|
||||
return getCollectionMember(name, java.util.Set.class, elementType,
|
||||
false);
|
||||
}
|
||||
|
||||
public <E> List<? super X, E> getList(String name,
|
||||
Class<E> elementType) {
|
||||
return getCollectionMember(name, java.util.List.class, elementType,
|
||||
false);
|
||||
}
|
||||
|
||||
public <K, V> Map<? super X, K, V> getMap(String name,
|
||||
Class<K> keyType, Class<V> valueType) {
|
||||
return getMapMember(name, valueType, keyType, false);
|
||||
}
|
||||
|
||||
public <E> Collection<X, E> getDeclaredCollection(String name,
|
||||
Class<E> elementType) {
|
||||
return getCollectionMember(name, java.util.Collection.class,
|
||||
elementType, true);
|
||||
}
|
||||
|
||||
public <E> Set<X, E> getDeclaredSet(String name, Class<E> elementType) {
|
||||
return getCollectionMember(name, java.util.Set.class, elementType,
|
||||
true);
|
||||
}
|
||||
|
||||
public <E> List<X, E> getDeclaredList(String name,
|
||||
Class<E> elementType) {
|
||||
return getCollectionMember(name, java.util.List.class, elementType,
|
||||
true);
|
||||
}
|
||||
|
||||
public <K, V> Map<X, K, V> getDeclaredMap(String name,
|
||||
Class<K> keyType, Class<V> valueType) {
|
||||
return getMapMember(name, valueType, keyType, true);
|
||||
}
|
||||
|
||||
public java.util.Set<Attribute<? super X, ?>> getAttributes() {
|
||||
return collect(attrs.values());
|
||||
}
|
||||
|
||||
public java.util.Set<Attribute<X, ?>> getDeclaredAttributes() {
|
||||
return filter(collect(attrs.values()));
|
||||
}
|
||||
|
||||
public java.util.Set<AbstractCollection<? super X, ?, ?>>
|
||||
getCollections() {
|
||||
return collect(colls.values());
|
||||
}
|
||||
|
||||
public java.util.Set<AbstractCollection<X, ?, ?>>
|
||||
getDeclaredCollections() {
|
||||
return filter(collect(colls.values()));
|
||||
}
|
||||
|
||||
<T> java.util.Set<T> collect(java.util.Collection<T> values) {
|
||||
java.util.Set<T> result = new HashSet<T>();
|
||||
result.addAll(values);
|
||||
return result;
|
||||
}
|
||||
|
||||
<T extends Member<X, ?>> java.util.Set<T> filter(
|
||||
java.util.Set<? extends Member<? super X, ?>> values) {
|
||||
java.util.Set<T> result = new HashSet<T>();
|
||||
for (Member<? super X, ?> m : values) {
|
||||
if (isDeclared(m))
|
||||
result.add((T) m);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// relaxed-type: gets the model elements by String arguments.
|
||||
|
||||
public Attribute<? super X, ?> getAttribute(String name) {
|
||||
return getAttribute(name, null);
|
||||
}
|
||||
|
||||
public Attribute<X, ?> getDeclaredAttribute(String name) {
|
||||
return getDeclaredAttribute(name, null);
|
||||
}
|
||||
|
||||
public Collection<? super X, ?> getCollection(String name) {
|
||||
return getCollectionMember(name, java.util.Collection.class, null,
|
||||
false);
|
||||
}
|
||||
|
||||
public Set<? super X, ?> getSet(String name) {
|
||||
return getCollectionMember(name, java.util.Set.class, null, false);
|
||||
}
|
||||
|
||||
public List<? super X, ?> getList(String name) {
|
||||
return getCollectionMember(name, java.util.List.class, null, false);
|
||||
}
|
||||
|
||||
public Map<? super X, ?, ?> getMap(String name) {
|
||||
return getMapMember(name, null, null, false);
|
||||
}
|
||||
|
||||
public Collection<X, ?> getDeclaredCollection(String name) {
|
||||
return getCollectionMember(name, java.util.Collection.class, null,
|
||||
true);
|
||||
}
|
||||
|
||||
public Set<X, ?> getDeclaredSet(String name) {
|
||||
return getCollectionMember(name, java.util.Set.class, null, true);
|
||||
}
|
||||
|
||||
public List<X, ?> getDeclaredList(String name) {
|
||||
return getCollectionMember(name, java.util.List.class, null, true);
|
||||
}
|
||||
|
||||
public Map<X, ?, ?> getDeclaredMap(String name) {
|
||||
return getMapMember(name, null, null, true);
|
||||
}
|
||||
|
||||
public BindableType getBindableType() {
|
||||
return BindableType.MANAGED_TYPE;
|
||||
}
|
||||
|
||||
// =====================================================================
|
||||
// Support functions
|
||||
// =====================================================================
|
||||
|
||||
FieldMetaData getField(String name) {
|
||||
return getField(name, null, null, null, false);
|
||||
}
|
||||
|
||||
FieldMetaData getField(String name, Class type) {
|
||||
return getField(name, type, null, null, false);
|
||||
}
|
||||
|
||||
FieldMetaData getField(String name, Class type, boolean declaredOnly) {
|
||||
return getField(name, type, null, null, declaredOnly);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the field of the given name after validating the conditions. null
|
||||
* value on any condition implies not to validate.
|
||||
*
|
||||
* @param name simple name i.e. without the class name
|
||||
* @param type the expected type of the field.
|
||||
* @param element the expected element type of the field.
|
||||
* @param key the expected key type of the field.
|
||||
* @param declared is this field declared in this receiver
|
||||
*
|
||||
* @exception IllegalArgumentException if any of the validation fails.
|
||||
*
|
||||
*/
|
||||
FieldMetaData getField(String name, Class<?> type, Class<?> elementType,
|
||||
Class<?> keyType, boolean decl) {
|
||||
FieldMetaData fmd =
|
||||
decl ? meta.getDeclaredField(name) : meta.getField(name);
|
||||
|
||||
if (fmd == null) {
|
||||
if (decl && meta.getField(name) != null) {
|
||||
throw new IllegalArgumentException(_loc.get(
|
||||
"field-not-decl", name, cls)
|
||||
.getMessage());
|
||||
} else {
|
||||
throw new IllegalArgumentException(_loc.get(
|
||||
"field-missing", name, meta.getDescribedType())
|
||||
.getMessage());
|
||||
}
|
||||
}
|
||||
assertType("field-type-mismatch", fmd, fmd.getDeclaredType(), type);
|
||||
assertType("field-element-type-mismatch", fmd, fmd.getElement()
|
||||
.getDeclaredType(), elementType);
|
||||
assertType("field-key-type-mismatch", fmd, fmd.getKey()
|
||||
.getDeclaredType(), keyType);
|
||||
return fmd;
|
||||
}
|
||||
|
||||
void assertType(String msg, FieldMetaData fmd, Class<?> actual,
|
||||
Class<?> expected) {
|
||||
if (expected != null && !expected.isAssignableFrom(actual)) {
|
||||
throw new IllegalArgumentException(_loc.get(msg, fmd.getName(),
|
||||
actual, expected).getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
boolean isDeclared(Member<?,?> member) {
|
||||
return member.getDeclaringType() == this;
|
||||
}
|
||||
|
||||
<T extends AbstractCollection, C,E> T getCollectionMember(
|
||||
String name, Class<C> target, Class<E> eType, boolean dec) {
|
||||
FieldMetaData fmd = getField(name, target, eType, null, dec);
|
||||
return (T) colls.get(fmd);
|
||||
}
|
||||
|
||||
<T extends Map, K, V> T getMapMember(String name,
|
||||
Class<V> vType, Class<K> kType, boolean dec) {
|
||||
FieldMetaData fmd = getField(name, java.util.Map.class, vType,
|
||||
kType, dec);
|
||||
return (T) colls.get(fmd);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static abstract class Identifiable<X> extends Managed<X> implements
|
||||
IdentifiableType<X> {
|
||||
|
||||
public Identifiable(ClassMetaData meta, MetamodelImpl model) {
|
||||
super(meta, model);
|
||||
}
|
||||
|
||||
public <Y> Attribute<? super X, Y> getId(Class<Y> type) {
|
||||
FieldMetaData[] pks = meta.getPrimaryKeyFields();
|
||||
Class<?> idType = meta.getObjectIdType();
|
||||
return (Attribute<? super X, Y>) getAttribute(pks[0].getName(),
|
||||
idType);
|
||||
}
|
||||
|
||||
public <Y> Attribute<? super X, Y> getVersion(Class<Y> type) {
|
||||
FieldMetaData vfmd = meta.getVersionField();
|
||||
return (Attribute<? super X, Y>) getAttribute(vfmd.getName());
|
||||
}
|
||||
|
||||
public <Y> Attribute<X, Y> getDeclaredId(Class<Y> type) {
|
||||
FieldMetaData[] pks = meta.getPrimaryKeyFields();
|
||||
Class<?> idType = meta.getObjectIdType();
|
||||
return (Attribute<X, Y>) getDeclaredAttribute(pks[0].getName(),
|
||||
idType);
|
||||
}
|
||||
|
||||
public <Y> Attribute<X, Y> getDeclaredVersion(Class<Y> type) {
|
||||
FieldMetaData vfmd = meta.getVersionField();
|
||||
return (Attribute<X, Y>) getDeclaredAttribute(vfmd.getName());
|
||||
}
|
||||
|
||||
public IdentifiableType<? super X> getSupertype() {
|
||||
return (IdentifiableType<? super X>) model.type(meta
|
||||
.getPCSuperclassMetaData().getDescribedType());
|
||||
}
|
||||
|
||||
public boolean hasIdAttribute() {
|
||||
return meta.getIdentityType() == ClassMetaData.ID_APPLICATION;
|
||||
}
|
||||
|
||||
public Type<?> getIdType() {
|
||||
Class<?> idType = meta.getObjectIdType();
|
||||
return model.type(idType);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Embeddable<X> extends Managed<X>
|
||||
implements javax.persistence.metamodel.Embeddable<X> {
|
||||
public Embeddable(ClassMetaData meta, MetamodelImpl model) {
|
||||
super(meta, model);
|
||||
}
|
||||
|
||||
public PersistenceType getPersistenceType() {
|
||||
return PersistenceType.EMBEDDABLE;
|
||||
}
|
||||
}
|
||||
|
||||
public static class MappedSuper<X> extends Identifiable<X> implements
|
||||
MappedSuperclass<X> {
|
||||
|
||||
public MappedSuper(ClassMetaData meta, MetamodelImpl model) {
|
||||
super(meta, model);
|
||||
}
|
||||
|
||||
public PersistenceType getPersistenceType() {
|
||||
return PersistenceType.MAPPED_SUPERCLASS;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class Entity<X> extends Identifiable<X>
|
||||
implements javax.persistence.metamodel.Entity<X> {
|
||||
|
||||
public Entity(ClassMetaData meta, MetamodelImpl model) {
|
||||
super(meta, model);
|
||||
}
|
||||
|
||||
public PersistenceType getPersistenceType() {
|
||||
return PersistenceType.ENTITY;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return meta.getTypeAlias();
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue