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:
Pinaki Poddar 2009-04-28 17:50:25 +00:00
parent 9d52998c1e
commit 7adb7932e8
8 changed files with 1761 additions and 669 deletions

View File

@ -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);
}
}
}

View File

@ -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;
}
}

View File

@ -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&lt;java.lang.String&gt;</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();
}
}

View File

@ -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());
}
}
}

View File

@ -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);
}

View File

@ -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) {
}
}
}

View File

@ -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&lt;java.lang.String&gt;</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()));
}
}

View File

@ -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();
}
}
}