METAGEN-81 Fixed the propagation of the access type to the @Embeddable

entities via the @Embedded link even if the access type is determined from the containing entity hierarchy
This commit is contained in:
Guillaume Smet 2012-03-11 01:54:03 +01:00 committed by Strong Liu
parent f946f60471
commit 7bb932f505
5 changed files with 571 additions and 438 deletions

View File

@ -1,438 +1,439 @@
/*
* JBoss, Home of Professional Open Source
* Copyright 2010, Red Hat Middleware LLC, and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* Licensed 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.hibernate.jpamodelgen.util;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
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.TypeElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.ExecutableType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.type.TypeVariable;
import javax.lang.model.util.ElementFilter;
import javax.lang.model.util.Elements;
import javax.lang.model.util.SimpleTypeVisitor6;
import javax.tools.Diagnostic;
import org.hibernate.jpamodelgen.Context;
import org.hibernate.jpamodelgen.MetaModelGenerationException;
/**
* Utility class.
*
* @author Max Andersen
* @author Hardy Ferentschik
* @author Emmanuel Bernard
*/
public final class TypeUtils {
public static final String DEFAULT_ANNOTATION_PARAMETER_NAME = "value";
private static final Map<String, String> PRIMITIVES = new HashMap<String, String>();
static {
PRIMITIVES.put( "char", "Character" );
PRIMITIVES.put( "byte", "Byte" );
PRIMITIVES.put( "short", "Short" );
PRIMITIVES.put( "int", "Integer" );
PRIMITIVES.put( "long", "Long" );
PRIMITIVES.put( "boolean", "Boolean" );
PRIMITIVES.put( "float", "Float" );
PRIMITIVES.put( "double", "Double" );
}
private TypeUtils() {
}
public static String toTypeString(TypeMirror type) {
if ( type.getKind().isPrimitive() ) {
return PRIMITIVES.get( type.toString() );
}
return type.toString();
}
public static TypeElement getSuperclassTypeElement(TypeElement element) {
final TypeMirror superClass = element.getSuperclass();
//superclass of Object is of NoType which returns some other kind
if ( superClass.getKind() == TypeKind.DECLARED ) {
//F..king Ch...t Have those people used their horrible APIs even once?
final Element superClassElement = ( (DeclaredType) superClass ).asElement();
return (TypeElement) superClassElement;
}
else {
return null;
}
}
public static String extractClosestRealTypeAsString(TypeMirror type, Context context) {
if ( type instanceof TypeVariable ) {
final TypeMirror compositeUpperBound = ( (TypeVariable) type ).getUpperBound();
return extractClosestRealTypeAsString( compositeUpperBound, context );
}
else {
return context.getTypeUtils().erasure( type ).toString();
}
}
public static boolean containsAnnotation(Element element, String... annotations) {
assert element != null;
assert annotations != null;
List<String> annotationClassNames = new ArrayList<String>();
Collections.addAll( annotationClassNames, annotations );
List<? extends AnnotationMirror> annotationMirrors = element.getAnnotationMirrors();
for ( AnnotationMirror mirror : annotationMirrors ) {
if ( annotationClassNames.contains( mirror.getAnnotationType().toString() ) ) {
return true;
}
}
return false;
}
/**
* Returns {@code true} if the provided annotation type is of the same type as the provided class, {@code false} otherwise.
* This method uses the string class names for comparison. See also
* <a href="http://www.retep.org/2009/02/getting-class-values-from-annotations.html">getting-class-values-from-annotations</a>.
*
* @param annotationMirror The annotation mirror
* @param fqcn the fully qualified class name to check against
*
* @return {@code true} if the provided annotation type is of the same type as the provided class, {@code false} otherwise.
*/
public static boolean isAnnotationMirrorOfType(AnnotationMirror annotationMirror, String fqcn) {
assert annotationMirror != null;
assert fqcn != null;
String annotationClassName = annotationMirror.getAnnotationType().toString();
return annotationClassName.equals( fqcn );
}
/**
* Checks whether the {@code Element} hosts the annotation with the given fully qualified class name.
*
* @param element the element to check for the hosted annotation
* @param fqcn the fully qualified class name of the annotation to check for
*
* @return the annotation mirror for the specified annotation class from the {@code Element} or {@code null} in case
* the {@code TypeElement} does not host the specified annotation.
*/
public static AnnotationMirror getAnnotationMirror(Element element, String fqcn) {
assert element != null;
assert fqcn != null;
AnnotationMirror mirror = null;
for ( AnnotationMirror am : element.getAnnotationMirrors() ) {
if ( isAnnotationMirrorOfType( am, fqcn ) ) {
mirror = am;
break;
}
}
return mirror;
}
public static Object getAnnotationValue(AnnotationMirror annotationMirror, String parameterValue) {
assert annotationMirror != null;
assert parameterValue != null;
Object returnValue = null;
for ( Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : annotationMirror.getElementValues()
.entrySet() ) {
if ( parameterValue.equals( entry.getKey().getSimpleName().toString() ) ) {
returnValue = entry.getValue().getValue();
break;
}
}
return returnValue;
}
public static void determineAccessTypeForHierarchy(TypeElement searchedElement, Context context) {
String fqcn = searchedElement.getQualifiedName().toString();
context.logMessage( Diagnostic.Kind.OTHER, "Determining access type for " + fqcn );
AccessTypeInformation accessTypeInfo = context.getAccessTypeInfo( fqcn );
if ( accessTypeInfo != null && accessTypeInfo.isAccessTypeResolved() ) {
context.logMessage(
Diagnostic.Kind.OTHER,
"AccessType for " + searchedElement.toString() + " found in cache: " + accessTypeInfo
);
return;
}
// check for explicit access type
AccessType forcedAccessType = determineAnnotationSpecifiedAccessType( searchedElement );
if ( forcedAccessType != null ) {
context.logMessage(
Diagnostic.Kind.OTHER, "Explicit access type on " + searchedElement + ":" + forcedAccessType
);
accessTypeInfo = new AccessTypeInformation( fqcn, forcedAccessType, null );
context.addAccessTypeInformation( fqcn, accessTypeInfo );
updateEmbeddableAccessType( searchedElement, context, forcedAccessType );
return;
}
// need to find the default access type for this class
// let's check first if this entity is the root of the class hierarchy and defines an id. If so the
// placement of the id annotation determines the access type
AccessType defaultAccessType = getAccessTypeInCaseElementIsRoot( searchedElement, context );
if ( defaultAccessType != null ) {
accessTypeInfo = new AccessTypeInformation( fqcn, null, defaultAccessType );
context.addAccessTypeInformation( fqcn, accessTypeInfo );
updateEmbeddableAccessType( searchedElement, context, defaultAccessType );
setDefaultAccessTypeForMappedSuperclassesInHierarchy( searchedElement, defaultAccessType, context );
return;
}
// if we end up here we need to recursively look for superclasses
defaultAccessType = getDefaultAccessForHierarchy( searchedElement, context );
if ( defaultAccessType == null ) {
defaultAccessType = AccessType.PROPERTY;
}
accessTypeInfo = new AccessTypeInformation( fqcn, null, defaultAccessType );
context.addAccessTypeInformation( fqcn, accessTypeInfo );
}
public static TypeMirror getCollectionElementType(DeclaredType t, String fqNameOfReturnedType, String explicitTargetEntityName, Context context) {
TypeMirror collectionElementType;
if ( explicitTargetEntityName != null ) {
Elements elements = context.getElementUtils();
TypeElement element = elements.getTypeElement( explicitTargetEntityName );
collectionElementType = element.asType();
}
else {
List<? extends TypeMirror> typeArguments = t.getTypeArguments();
if ( typeArguments.size() == 0 ) {
throw new MetaModelGenerationException( "Unable to determine collection type" );
}
else if ( Map.class.getCanonicalName().equals( fqNameOfReturnedType ) ) {
collectionElementType = t.getTypeArguments().get( 1 );
}
else {
collectionElementType = t.getTypeArguments().get( 0 );
}
}
return collectionElementType;
}
private static void updateEmbeddableAccessType(TypeElement element, Context context, AccessType defaultAccessType) {
List<? extends Element> fieldsOfClass = ElementFilter.fieldsIn( element.getEnclosedElements() );
for ( Element field : fieldsOfClass ) {
updateEmbeddableAccessTypeForMember( context, defaultAccessType, field );
}
List<? extends Element> methodOfClass = ElementFilter.methodsIn( element.getEnclosedElements() );
for ( Element method : methodOfClass ) {
updateEmbeddableAccessTypeForMember( context, defaultAccessType, method );
}
}
private static void updateEmbeddableAccessTypeForMember(Context context, AccessType defaultAccessType, Element member) {
EmbeddedAttributeVisitor visitor = new EmbeddedAttributeVisitor( context );
String embeddedClassName = member.asType().accept( visitor, member );
if ( embeddedClassName != null ) {
AccessTypeInformation accessTypeInfo = context.getAccessTypeInfo( embeddedClassName );
if ( accessTypeInfo == null ) {
accessTypeInfo = new AccessTypeInformation( embeddedClassName, null, defaultAccessType );
context.addAccessTypeInformation( embeddedClassName, accessTypeInfo );
}
else {
accessTypeInfo.setDefaultAccessType( defaultAccessType );
}
}
}
private static AccessType getDefaultAccessForHierarchy(TypeElement element, Context context) {
AccessType defaultAccessType = null;
TypeElement superClass = element;
do {
superClass = TypeUtils.getSuperclassTypeElement( superClass );
if ( superClass != null ) {
String fqcn = superClass.getQualifiedName().toString();
AccessTypeInformation accessTypeInfo = context.getAccessTypeInfo( fqcn );
if ( accessTypeInfo != null && accessTypeInfo.getDefaultAccessType() != null ) {
return accessTypeInfo.getDefaultAccessType();
}
if ( TypeUtils.containsAnnotation( superClass, Constants.ENTITY, Constants.MAPPED_SUPERCLASS ) ) {
defaultAccessType = getAccessTypeInCaseElementIsRoot( superClass, context );
if ( defaultAccessType != null ) {
accessTypeInfo = new AccessTypeInformation( fqcn, null, defaultAccessType );
context.addAccessTypeInformation( fqcn, accessTypeInfo );
// we found an id within the class hierarchy and determined a default access type
// there cannot be any super entity classes (otherwise it would be a configuration error)
// but there might be mapped super classes
// Also note, that if two different class hierarchies with different access types ave a common
// mapped super class, the access type of the mapped super class will be the one of the last
// hierarchy processed. The result is not determined which is od with the spec
setDefaultAccessTypeForMappedSuperclassesInHierarchy( superClass, defaultAccessType, context );
// we found a default access type, o need to look further
break;
}
else {
defaultAccessType = getDefaultAccessForHierarchy( superClass, context );
}
}
}
}
while ( superClass != null );
return defaultAccessType;
}
private static void setDefaultAccessTypeForMappedSuperclassesInHierarchy(TypeElement element, AccessType defaultAccessType, Context context) {
TypeElement superClass = element;
do {
superClass = TypeUtils.getSuperclassTypeElement( superClass );
if ( superClass != null ) {
String fqcn = superClass.getQualifiedName().toString();
if ( TypeUtils.containsAnnotation( superClass, Constants.MAPPED_SUPERCLASS ) ) {
AccessTypeInformation accessTypeInfo;
AccessType forcedAccessType = determineAnnotationSpecifiedAccessType( superClass );
if ( forcedAccessType != null ) {
accessTypeInfo = new AccessTypeInformation( fqcn, null, forcedAccessType );
}
else {
accessTypeInfo = new AccessTypeInformation( fqcn, null, defaultAccessType );
}
context.addAccessTypeInformation( fqcn, accessTypeInfo );
}
}
}
while ( superClass != null );
}
/**
* Iterates all elements of a type to check whether they contain the id annotation. If so the placement of this
* annotation determines the access type
*
* @param searchedElement the type to be searched
* @param context The global execution context
*
* @return returns the access type of the element annotated with the id annotation. If no element is annotated
* {@code null} is returned.
*/
private static AccessType getAccessTypeInCaseElementIsRoot(TypeElement searchedElement, Context context) {
List<? extends Element> myMembers = searchedElement.getEnclosedElements();
for ( Element subElement : myMembers ) {
List<? extends AnnotationMirror> entityAnnotations =
context.getElementUtils().getAllAnnotationMirrors( subElement );
for ( Object entityAnnotation : entityAnnotations ) {
AnnotationMirror annotationMirror = (AnnotationMirror) entityAnnotation;
if ( isIdAnnotation( annotationMirror ) ) {
return getAccessTypeOfIdAnnotation( subElement );
}
}
}
return null;
}
private static AccessType getAccessTypeOfIdAnnotation(Element element) {
AccessType accessType = null;
final ElementKind kind = element.getKind();
if ( kind == ElementKind.FIELD || kind == ElementKind.METHOD ) {
accessType = kind == ElementKind.FIELD ? AccessType.FIELD : AccessType.PROPERTY;
}
return accessType;
}
private static boolean isIdAnnotation(AnnotationMirror annotationMirror) {
return TypeUtils.isAnnotationMirrorOfType( annotationMirror, Constants.ID )
|| TypeUtils.isAnnotationMirrorOfType( annotationMirror, Constants.EMBEDDED_ID );
}
public static AccessType determineAnnotationSpecifiedAccessType(Element element) {
final AnnotationMirror accessAnnotationMirror = TypeUtils.getAnnotationMirror( element, Constants.ACCESS );
AccessType forcedAccessType = null;
if ( accessAnnotationMirror != null ) {
Element accessElement = (Element) TypeUtils.getAnnotationValue(
accessAnnotationMirror,
DEFAULT_ANNOTATION_PARAMETER_NAME
);
if ( accessElement.getKind().equals( ElementKind.ENUM_CONSTANT ) ) {
if ( accessElement.getSimpleName().toString().equals( AccessType.PROPERTY.toString() ) ) {
forcedAccessType = AccessType.PROPERTY;
}
else if ( accessElement.getSimpleName().toString().equals( AccessType.FIELD.toString() ) ) {
forcedAccessType = AccessType.FIELD;
}
}
}
return forcedAccessType;
}
public static ElementKind getElementKindForAccessType(AccessType accessType) {
if ( AccessType.FIELD.equals( accessType ) ) {
return ElementKind.FIELD;
}
else {
return ElementKind.METHOD;
}
}
public static String getKeyType(DeclaredType t, Context context) {
List<? extends TypeMirror> typeArguments = t.getTypeArguments();
if ( typeArguments.size() == 0 ) {
context.logMessage( Diagnostic.Kind.ERROR, "Unable to determine type argument for " + t );
}
return extractClosestRealTypeAsString( typeArguments.get( 0 ), context );
}
static class EmbeddedAttributeVisitor extends SimpleTypeVisitor6<String, Element> {
private Context context;
EmbeddedAttributeVisitor(Context context) {
this.context = context;
}
@Override
public String visitDeclared(DeclaredType declaredType, Element element) {
TypeElement returnedElement = (TypeElement) context.getTypeUtils().asElement( declaredType );
String fqNameOfReturnType = null;
if ( containsAnnotation( returnedElement, Constants.EMBEDDABLE ) ) {
fqNameOfReturnType = returnedElement.getQualifiedName().toString();
}
return fqNameOfReturnType;
}
@Override
public String visitExecutable(ExecutableType t, Element p) {
if ( !p.getKind().equals( ElementKind.METHOD ) ) {
return null;
}
String string = p.getSimpleName().toString();
if ( !StringUtil.isPropertyName( string ) ) {
return null;
}
TypeMirror returnType = t.getReturnType();
return returnType.accept( this, p );
}
}
}
/*
* JBoss, Home of Professional Open Source
* Copyright 2010, Red Hat Middleware LLC, and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* Licensed 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.hibernate.jpamodelgen.util;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
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.TypeElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.ExecutableType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.type.TypeVariable;
import javax.lang.model.util.ElementFilter;
import javax.lang.model.util.Elements;
import javax.lang.model.util.SimpleTypeVisitor6;
import javax.tools.Diagnostic;
import org.hibernate.jpamodelgen.Context;
import org.hibernate.jpamodelgen.MetaModelGenerationException;
/**
* Utility class.
*
* @author Max Andersen
* @author Hardy Ferentschik
* @author Emmanuel Bernard
*/
public final class TypeUtils {
public static final String DEFAULT_ANNOTATION_PARAMETER_NAME = "value";
private static final Map<String, String> PRIMITIVES = new HashMap<String, String>();
static {
PRIMITIVES.put( "char", "Character" );
PRIMITIVES.put( "byte", "Byte" );
PRIMITIVES.put( "short", "Short" );
PRIMITIVES.put( "int", "Integer" );
PRIMITIVES.put( "long", "Long" );
PRIMITIVES.put( "boolean", "Boolean" );
PRIMITIVES.put( "float", "Float" );
PRIMITIVES.put( "double", "Double" );
}
private TypeUtils() {
}
public static String toTypeString(TypeMirror type) {
if ( type.getKind().isPrimitive() ) {
return PRIMITIVES.get( type.toString() );
}
return type.toString();
}
public static TypeElement getSuperclassTypeElement(TypeElement element) {
final TypeMirror superClass = element.getSuperclass();
//superclass of Object is of NoType which returns some other kind
if ( superClass.getKind() == TypeKind.DECLARED ) {
//F..king Ch...t Have those people used their horrible APIs even once?
final Element superClassElement = ( (DeclaredType) superClass ).asElement();
return (TypeElement) superClassElement;
}
else {
return null;
}
}
public static String extractClosestRealTypeAsString(TypeMirror type, Context context) {
if ( type instanceof TypeVariable ) {
final TypeMirror compositeUpperBound = ( (TypeVariable) type ).getUpperBound();
return extractClosestRealTypeAsString( compositeUpperBound, context );
}
else {
return context.getTypeUtils().erasure( type ).toString();
}
}
public static boolean containsAnnotation(Element element, String... annotations) {
assert element != null;
assert annotations != null;
List<String> annotationClassNames = new ArrayList<String>();
Collections.addAll( annotationClassNames, annotations );
List<? extends AnnotationMirror> annotationMirrors = element.getAnnotationMirrors();
for ( AnnotationMirror mirror : annotationMirrors ) {
if ( annotationClassNames.contains( mirror.getAnnotationType().toString() ) ) {
return true;
}
}
return false;
}
/**
* Returns {@code true} if the provided annotation type is of the same type as the provided class, {@code false} otherwise.
* This method uses the string class names for comparison. See also
* <a href="http://www.retep.org/2009/02/getting-class-values-from-annotations.html">getting-class-values-from-annotations</a>.
*
* @param annotationMirror The annotation mirror
* @param fqcn the fully qualified class name to check against
*
* @return {@code true} if the provided annotation type is of the same type as the provided class, {@code false} otherwise.
*/
public static boolean isAnnotationMirrorOfType(AnnotationMirror annotationMirror, String fqcn) {
assert annotationMirror != null;
assert fqcn != null;
String annotationClassName = annotationMirror.getAnnotationType().toString();
return annotationClassName.equals( fqcn );
}
/**
* Checks whether the {@code Element} hosts the annotation with the given fully qualified class name.
*
* @param element the element to check for the hosted annotation
* @param fqcn the fully qualified class name of the annotation to check for
*
* @return the annotation mirror for the specified annotation class from the {@code Element} or {@code null} in case
* the {@code TypeElement} does not host the specified annotation.
*/
public static AnnotationMirror getAnnotationMirror(Element element, String fqcn) {
assert element != null;
assert fqcn != null;
AnnotationMirror mirror = null;
for ( AnnotationMirror am : element.getAnnotationMirrors() ) {
if ( isAnnotationMirrorOfType( am, fqcn ) ) {
mirror = am;
break;
}
}
return mirror;
}
public static Object getAnnotationValue(AnnotationMirror annotationMirror, String parameterValue) {
assert annotationMirror != null;
assert parameterValue != null;
Object returnValue = null;
for ( Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : annotationMirror.getElementValues()
.entrySet() ) {
if ( parameterValue.equals( entry.getKey().getSimpleName().toString() ) ) {
returnValue = entry.getValue().getValue();
break;
}
}
return returnValue;
}
public static void determineAccessTypeForHierarchy(TypeElement searchedElement, Context context) {
String fqcn = searchedElement.getQualifiedName().toString();
context.logMessage( Diagnostic.Kind.OTHER, "Determining access type for " + fqcn );
AccessTypeInformation accessTypeInfo = context.getAccessTypeInfo( fqcn );
if ( accessTypeInfo != null && accessTypeInfo.isAccessTypeResolved() ) {
context.logMessage(
Diagnostic.Kind.OTHER,
"AccessType for " + searchedElement.toString() + " found in cache: " + accessTypeInfo
);
return;
}
// check for explicit access type
AccessType forcedAccessType = determineAnnotationSpecifiedAccessType( searchedElement );
if ( forcedAccessType != null ) {
context.logMessage(
Diagnostic.Kind.OTHER, "Explicit access type on " + searchedElement + ":" + forcedAccessType
);
accessTypeInfo = new AccessTypeInformation( fqcn, forcedAccessType, null );
context.addAccessTypeInformation( fqcn, accessTypeInfo );
updateEmbeddableAccessType( searchedElement, context, forcedAccessType );
return;
}
// need to find the default access type for this class
// let's check first if this entity is the root of the class hierarchy and defines an id. If so the
// placement of the id annotation determines the access type
AccessType defaultAccessType = getAccessTypeInCaseElementIsRoot( searchedElement, context );
if ( defaultAccessType != null ) {
accessTypeInfo = new AccessTypeInformation( fqcn, null, defaultAccessType );
context.addAccessTypeInformation( fqcn, accessTypeInfo );
updateEmbeddableAccessType( searchedElement, context, defaultAccessType );
setDefaultAccessTypeForMappedSuperclassesInHierarchy( searchedElement, defaultAccessType, context );
return;
}
// if we end up here we need to recursively look for superclasses
defaultAccessType = getDefaultAccessForHierarchy( searchedElement, context );
if ( defaultAccessType == null ) {
defaultAccessType = AccessType.PROPERTY;
}
accessTypeInfo = new AccessTypeInformation( fqcn, null, defaultAccessType );
context.addAccessTypeInformation( fqcn, accessTypeInfo );
updateEmbeddableAccessType( searchedElement, context, defaultAccessType );
}
public static TypeMirror getCollectionElementType(DeclaredType t, String fqNameOfReturnedType, String explicitTargetEntityName, Context context) {
TypeMirror collectionElementType;
if ( explicitTargetEntityName != null ) {
Elements elements = context.getElementUtils();
TypeElement element = elements.getTypeElement( explicitTargetEntityName );
collectionElementType = element.asType();
}
else {
List<? extends TypeMirror> typeArguments = t.getTypeArguments();
if ( typeArguments.size() == 0 ) {
throw new MetaModelGenerationException( "Unable to determine collection type" );
}
else if ( Map.class.getCanonicalName().equals( fqNameOfReturnedType ) ) {
collectionElementType = t.getTypeArguments().get( 1 );
}
else {
collectionElementType = t.getTypeArguments().get( 0 );
}
}
return collectionElementType;
}
private static void updateEmbeddableAccessType(TypeElement element, Context context, AccessType defaultAccessType) {
List<? extends Element> fieldsOfClass = ElementFilter.fieldsIn( element.getEnclosedElements() );
for ( Element field : fieldsOfClass ) {
updateEmbeddableAccessTypeForMember( context, defaultAccessType, field );
}
List<? extends Element> methodOfClass = ElementFilter.methodsIn( element.getEnclosedElements() );
for ( Element method : methodOfClass ) {
updateEmbeddableAccessTypeForMember( context, defaultAccessType, method );
}
}
private static void updateEmbeddableAccessTypeForMember(Context context, AccessType defaultAccessType, Element member) {
EmbeddedAttributeVisitor visitor = new EmbeddedAttributeVisitor( context );
String embeddedClassName = member.asType().accept( visitor, member );
if ( embeddedClassName != null ) {
AccessTypeInformation accessTypeInfo = context.getAccessTypeInfo( embeddedClassName );
if ( accessTypeInfo == null ) {
accessTypeInfo = new AccessTypeInformation( embeddedClassName, null, defaultAccessType );
context.addAccessTypeInformation( embeddedClassName, accessTypeInfo );
}
else {
accessTypeInfo.setDefaultAccessType( defaultAccessType );
}
}
}
private static AccessType getDefaultAccessForHierarchy(TypeElement element, Context context) {
AccessType defaultAccessType = null;
TypeElement superClass = element;
do {
superClass = TypeUtils.getSuperclassTypeElement( superClass );
if ( superClass != null ) {
String fqcn = superClass.getQualifiedName().toString();
AccessTypeInformation accessTypeInfo = context.getAccessTypeInfo( fqcn );
if ( accessTypeInfo != null && accessTypeInfo.getDefaultAccessType() != null ) {
return accessTypeInfo.getDefaultAccessType();
}
if ( TypeUtils.containsAnnotation( superClass, Constants.ENTITY, Constants.MAPPED_SUPERCLASS ) ) {
defaultAccessType = getAccessTypeInCaseElementIsRoot( superClass, context );
if ( defaultAccessType != null ) {
accessTypeInfo = new AccessTypeInformation( fqcn, null, defaultAccessType );
context.addAccessTypeInformation( fqcn, accessTypeInfo );
// we found an id within the class hierarchy and determined a default access type
// there cannot be any super entity classes (otherwise it would be a configuration error)
// but there might be mapped super classes
// Also note, that if two different class hierarchies with different access types have a common
// mapped super class, the access type of the mapped super class will be the one of the last
// hierarchy processed. The result is not determined which is odd with the spec
setDefaultAccessTypeForMappedSuperclassesInHierarchy( superClass, defaultAccessType, context );
// we found a default access type, no need to look further
break;
}
else {
defaultAccessType = getDefaultAccessForHierarchy( superClass, context );
}
}
}
}
while ( superClass != null );
return defaultAccessType;
}
private static void setDefaultAccessTypeForMappedSuperclassesInHierarchy(TypeElement element, AccessType defaultAccessType, Context context) {
TypeElement superClass = element;
do {
superClass = TypeUtils.getSuperclassTypeElement( superClass );
if ( superClass != null ) {
String fqcn = superClass.getQualifiedName().toString();
if ( TypeUtils.containsAnnotation( superClass, Constants.MAPPED_SUPERCLASS ) ) {
AccessTypeInformation accessTypeInfo;
AccessType forcedAccessType = determineAnnotationSpecifiedAccessType( superClass );
if ( forcedAccessType != null ) {
accessTypeInfo = new AccessTypeInformation( fqcn, null, forcedAccessType );
}
else {
accessTypeInfo = new AccessTypeInformation( fqcn, null, defaultAccessType );
}
context.addAccessTypeInformation( fqcn, accessTypeInfo );
}
}
}
while ( superClass != null );
}
/**
* Iterates all elements of a type to check whether they contain the id annotation. If so the placement of this
* annotation determines the access type
*
* @param searchedElement the type to be searched
* @param context The global execution context
*
* @return returns the access type of the element annotated with the id annotation. If no element is annotated
* {@code null} is returned.
*/
private static AccessType getAccessTypeInCaseElementIsRoot(TypeElement searchedElement, Context context) {
List<? extends Element> myMembers = searchedElement.getEnclosedElements();
for ( Element subElement : myMembers ) {
List<? extends AnnotationMirror> entityAnnotations =
context.getElementUtils().getAllAnnotationMirrors( subElement );
for ( Object entityAnnotation : entityAnnotations ) {
AnnotationMirror annotationMirror = (AnnotationMirror) entityAnnotation;
if ( isIdAnnotation( annotationMirror ) ) {
return getAccessTypeOfIdAnnotation( subElement );
}
}
}
return null;
}
private static AccessType getAccessTypeOfIdAnnotation(Element element) {
AccessType accessType = null;
final ElementKind kind = element.getKind();
if ( kind == ElementKind.FIELD || kind == ElementKind.METHOD ) {
accessType = kind == ElementKind.FIELD ? AccessType.FIELD : AccessType.PROPERTY;
}
return accessType;
}
private static boolean isIdAnnotation(AnnotationMirror annotationMirror) {
return TypeUtils.isAnnotationMirrorOfType( annotationMirror, Constants.ID )
|| TypeUtils.isAnnotationMirrorOfType( annotationMirror, Constants.EMBEDDED_ID );
}
public static AccessType determineAnnotationSpecifiedAccessType(Element element) {
final AnnotationMirror accessAnnotationMirror = TypeUtils.getAnnotationMirror( element, Constants.ACCESS );
AccessType forcedAccessType = null;
if ( accessAnnotationMirror != null ) {
Element accessElement = (Element) TypeUtils.getAnnotationValue(
accessAnnotationMirror,
DEFAULT_ANNOTATION_PARAMETER_NAME
);
if ( accessElement.getKind().equals( ElementKind.ENUM_CONSTANT ) ) {
if ( accessElement.getSimpleName().toString().equals( AccessType.PROPERTY.toString() ) ) {
forcedAccessType = AccessType.PROPERTY;
}
else if ( accessElement.getSimpleName().toString().equals( AccessType.FIELD.toString() ) ) {
forcedAccessType = AccessType.FIELD;
}
}
}
return forcedAccessType;
}
public static ElementKind getElementKindForAccessType(AccessType accessType) {
if ( AccessType.FIELD.equals( accessType ) ) {
return ElementKind.FIELD;
}
else {
return ElementKind.METHOD;
}
}
public static String getKeyType(DeclaredType t, Context context) {
List<? extends TypeMirror> typeArguments = t.getTypeArguments();
if ( typeArguments.size() == 0 ) {
context.logMessage( Diagnostic.Kind.ERROR, "Unable to determine type argument for " + t );
}
return extractClosestRealTypeAsString( typeArguments.get( 0 ), context );
}
static class EmbeddedAttributeVisitor extends SimpleTypeVisitor6<String, Element> {
private Context context;
EmbeddedAttributeVisitor(Context context) {
this.context = context;
}
@Override
public String visitDeclared(DeclaredType declaredType, Element element) {
TypeElement returnedElement = (TypeElement) context.getTypeUtils().asElement( declaredType );
String fqNameOfReturnType = null;
if ( containsAnnotation( returnedElement, Constants.EMBEDDABLE ) ) {
fqNameOfReturnType = returnedElement.getQualifiedName().toString();
}
return fqNameOfReturnType;
}
@Override
public String visitExecutable(ExecutableType t, Element p) {
if ( !p.getKind().equals( ElementKind.METHOD ) ) {
return null;
}
String string = p.getSimpleName().toString();
if ( !StringUtil.isPropertyName( string ) ) {
return null;
}
TypeMirror returnType = t.getReturnType();
return returnType.accept( this, p );
}
}
}

View File

@ -67,6 +67,12 @@ public class AccessTypeTest extends CompilationTest {
assertAbsenceOfFieldInMetamodelFor(
Pet.class, "nonPersistent", "Collection of embeddable not taken care of"
);
// see METAGEN-81
// access type should be inherited from the position of the @Id field annotation from the root entity
// via the @Embedded annotation
assertPresenceOfFieldInMetamodelFor( Hotel.class, "webmaster",
"Access type should be inherited from the hierarchy" );
}
@Test

View File

@ -0,0 +1,42 @@
/*
* JBoss, Home of Professional Open Source
* Copyright 2012 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @authors tag. All rights reserved.
* See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU Lesser General Public License, v. 2.1.
* This program is distributed in the hope that it will be useful, but WITHOUT A
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public License,
* v.2.1 along with this distribution; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
package org.hibernate.jpamodelgen.test.accesstype;
import javax.persistence.Embeddable;
import javax.persistence.OneToOne;
/**
* @author gsmet
*/
@Embeddable
public class Hotel {
@OneToOne
private User webmaster;
public User getWebmaster() {
return webmaster;
}
public void setWebmaster(User webmaster) {
this.webmaster = webmaster;
}
}

View File

@ -0,0 +1,42 @@
/*
* JBoss, Home of Professional Open Source
* Copyright 2012 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @authors tag. All rights reserved.
* See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU Lesser General Public License, v. 2.1.
* This program is distributed in the hope that it will be useful, but WITHOUT A
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public License,
* v.2.1 along with this distribution; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
package org.hibernate.jpamodelgen.test.accesstype;
import javax.persistence.Embedded;
import javax.persistence.Entity;
/**
* @author gsmet
*/
@Entity
public class HotelRoom extends Room {
@Embedded
private Hotel hotel;
public Hotel getHotel() {
return hotel;
}
public void setHotel(Hotel hotel) {
this.hotel = hotel;
}
}

View File

@ -0,0 +1,42 @@
/*
* JBoss, Home of Professional Open Source
* Copyright 2012 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @authors tag. All rights reserved.
* See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU Lesser General Public License, v. 2.1.
* This program is distributed in the hope that it will be useful, but WITHOUT A
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public License,
* v.2.1 along with this distribution; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
package org.hibernate.jpamodelgen.test.accesstype;
import javax.persistence.Entity;
import javax.persistence.Id;
/**
* @author gsmet
*/
@Entity
public class Room {
@Id
private Integer id;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
}