From 1720d85543929bc715b2392dd4d4003f4b4934b6 Mon Sep 17 00:00:00 2001 From: Hardy Ferentschik Date: Thu, 7 Oct 2010 08:04:03 +0000 Subject: [PATCH] METAGEN-37 fixed TypeUtils.extractClosestRealTypeAsString and added test --- .../org/hibernate/jpamodelgen/Version.java | 5 +- .../annotation/AnnotationMetaEntity.java | 303 +--------------- .../MetaAttributeGenerationVisitor.java | 325 ++++++++++++++++++ .../hibernate/jpamodelgen/util/TypeUtils.java | 27 +- .../AttachmentGroup.java | 33 ++ .../AttachmentGroupInTopic.java | 27 ++ .../AttachmentGroupPost.java | 27 ++ .../AttachmentGroupPostInTopic.java | 24 ++ .../test/typedmappedsuperclass/Post.java | 25 ++ .../TypesMappedSuperclassTest.java | 42 +++ .../test/typedmappedsuperclass/UserRole.java | 25 ++ 11 files changed, 553 insertions(+), 310 deletions(-) create mode 100644 tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/MetaAttributeGenerationVisitor.java create mode 100644 tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/typedmappedsuperclass/AttachmentGroup.java create mode 100644 tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/typedmappedsuperclass/AttachmentGroupInTopic.java create mode 100644 tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/typedmappedsuperclass/AttachmentGroupPost.java create mode 100644 tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/typedmappedsuperclass/AttachmentGroupPostInTopic.java create mode 100644 tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/typedmappedsuperclass/Post.java create mode 100644 tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/typedmappedsuperclass/TypesMappedSuperclassTest.java create mode 100644 tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/typedmappedsuperclass/UserRole.java diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/Version.java b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/Version.java index 93d3f86cdb..0dda0c5113 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/Version.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/Version.java @@ -24,7 +24,10 @@ package org.hibernate.jpamodelgen; * * @author Hardy Ferentschik */ -public class Version { +public final class Version { + private Version() { + } + public static String getVersionString() { return "[WORKING]"; } diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/AnnotationMetaEntity.java b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/AnnotationMetaEntity.java index 6f8f62d4c0..62cf62a567 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/AnnotationMetaEntity.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/AnnotationMetaEntity.java @@ -24,33 +24,14 @@ import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; -import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.Element; -import javax.lang.model.element.ElementKind; import javax.lang.model.element.Modifier; import javax.lang.model.element.Name; import javax.lang.model.element.PackageElement; import javax.lang.model.element.TypeElement; -import javax.lang.model.type.ArrayType; -import javax.lang.model.type.DeclaredType; -import javax.lang.model.type.ExecutableType; -import javax.lang.model.type.PrimitiveType; -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.SimpleTypeVisitor6; import javax.persistence.AccessType; -import javax.persistence.Basic; -import javax.persistence.ElementCollection; -import javax.persistence.Embeddable; -import javax.persistence.ManyToMany; -import javax.persistence.ManyToOne; -import javax.persistence.MapKeyClass; -import javax.persistence.OneToMany; -import javax.persistence.OneToOne; import javax.persistence.Transient; -import javax.tools.Diagnostic; import org.hibernate.jpamodelgen.AccessTypeInformation; import org.hibernate.jpamodelgen.Context; @@ -58,8 +39,6 @@ import org.hibernate.jpamodelgen.ImportContextImpl; import org.hibernate.jpamodelgen.model.ImportContext; import org.hibernate.jpamodelgen.model.MetaAttribute; import org.hibernate.jpamodelgen.model.MetaEntity; -import org.hibernate.jpamodelgen.util.Constants; -import org.hibernate.jpamodelgen.util.StringUtil; import org.hibernate.jpamodelgen.util.TypeUtils; /** @@ -92,6 +71,10 @@ public class AnnotationMetaEntity implements MetaEntity { } } + public AccessTypeInformation getEntityAccessTypeInfo() { + return entityAccessTypeInfo; + } + public Context getContext() { return context; } @@ -133,7 +116,7 @@ public class AnnotationMetaEntity implements MetaEntity { sb.append( '}' ); return sb.toString(); } - + private void addPersistentMembers(List membersOfClass, AccessType membersKind) { for ( Element memberOfClass : membersOfClass ) { AccessType forcedAccessType = TypeUtils.determineAnnotationSpecifiedAccessType( memberOfClass ); @@ -147,7 +130,7 @@ public class AnnotationMetaEntity implements MetaEntity { continue; } - TypeVisitor visitor = new TypeVisitor( this ); + MetaAttributeGenerationVisitor visitor = new MetaAttributeGenerationVisitor( this, context ); AnnotationMetaAttribute result = memberOfClass.asType().accept( visitor, memberOfClass ); if ( result != null ) { members.put( result.getPropertyName(), result ); @@ -155,7 +138,7 @@ public class AnnotationMetaEntity implements MetaEntity { } } - protected void init() { + protected final void init() { TypeUtils.determineAccessTypeForHierarchy( element, context ); entityAccessTypeInfo = context.getAccessTypeInfo( getQualifiedName() ); @@ -166,285 +149,23 @@ public class AnnotationMetaEntity implements MetaEntity { addPersistentMembers( methodsOfClass, AccessType.PROPERTY ); } - public String generateImports() { + public final String generateImports() { return importContext.generateImports(); } - public String importType(String fqcn) { + public final String importType(String fqcn) { return importContext.importType( fqcn ); } - public String staticImport(String fqcn, String member) { + public final String staticImport(String fqcn, String member) { return importContext.staticImport( fqcn, member ); } - public String importType(Name qualifiedName) { + public final String importType(Name qualifiedName) { return importType( qualifiedName.toString() ); } - public TypeElement getTypeElement() { + public final TypeElement getTypeElement() { return element; } - - class TypeVisitor extends SimpleTypeVisitor6 { - - /** - * FQCN of the Hibernate specific @Target annotation. We do not use the class directly to avoid depending on Hibernate - * Core. - */ - private static final String ORG_HIBERNATE_ANNOTATIONS_TARGET = "org.hibernate.annotations.Target"; - - AnnotationMetaEntity parent; - - TypeVisitor(AnnotationMetaEntity parent) { - this.parent = parent; - } - - @Override - public AnnotationMetaAttribute visitPrimitive(PrimitiveType t, Element element) { - return new AnnotationMetaSingleAttribute( parent, element, TypeUtils.toTypeString( t ) ); - } - - @Override - public AnnotationMetaAttribute visitArray(ArrayType t, Element element) { - // METAGEN-2 - For now we handle arrays as SingularAttribute - // The code below is an attempt to be closer to the spec and only allow byte[], Byte[], char[] and Character[] -// AnnotationMetaSingleAttribute attribute = null; -// TypeMirror componentMirror = t.getComponentType(); -// if ( TypeKind.CHAR.equals( componentMirror.getKind() ) -// || TypeKind.BYTE.equals( componentMirror.getKind() ) ) { -// attribute = new AnnotationMetaSingleAttribute( parent, element, TypeUtils.toTypeString( t ) ); -// } -// else if ( TypeKind.DECLARED.equals( componentMirror.getKind() ) ) { -// TypeElement componentElement = ( TypeElement ) context.getProcessingEnvironment() -// .getTypeUtils() -// .asElement( componentMirror ); -// if ( BASIC_ARRAY_TYPES.contains( componentElement.getQualifiedName().toString() ) ) { -// attribute = new AnnotationMetaSingleAttribute( parent, element, TypeUtils.toTypeString( t ) ); -// } -// } -// return attribute; - return new AnnotationMetaSingleAttribute( parent, element, TypeUtils.toTypeString( t ) ); - } - - public AnnotationMetaAttribute visitTypeVariable(TypeVariable t, Element element) { - // METAGEN-29 - for a type variable we use the upper bound - TypeMirror mirror = t.getUpperBound(); - TypeMirror erasedType = context.getTypeUtils().erasure( mirror ); - return new AnnotationMetaSingleAttribute( - parent, element, erasedType.toString() - ); - } - - @Override - public AnnotationMetaAttribute visitDeclared(DeclaredType declaredType, Element element) { - AnnotationMetaAttribute metaAttribute = null; - TypeElement returnedElement = ( TypeElement ) context.getTypeUtils().asElement( declaredType ); - // WARNING: .toString() is necessary here since Name equals does not compare to String - String fqNameOfReturnType = returnedElement.getQualifiedName().toString(); - String collection = Constants.COLLECTIONS.get( fqNameOfReturnType ); - String targetEntity = getTargetEntity( element.getAnnotationMirrors() ); - if ( collection != null ) { - return createMetaCollectionAttribute( - declaredType, element, fqNameOfReturnType, collection, targetEntity - ); - } - else if ( isBasicAttribute( element, returnedElement ) ) { - String type = targetEntity != null ? targetEntity : returnedElement.getQualifiedName().toString(); - return new AnnotationMetaSingleAttribute( parent, element, type ); - } - return metaAttribute; - } - - private AnnotationMetaAttribute createMetaCollectionAttribute(DeclaredType declaredType, Element element, String fqNameOfReturnType, String collection, String targetEntity) { - if ( TypeUtils.containsAnnotation( element, ElementCollection.class ) ) { - String explicitTargetEntity = getTargetEntity( element.getAnnotationMirrors() ); - TypeMirror collectionElementType = TypeUtils.getCollectionElementType( - declaredType, fqNameOfReturnType, explicitTargetEntity, context - ); - final TypeElement collectionElement = ( TypeElement ) context.getTypeUtils() - .asElement( collectionElementType ); - AccessTypeInformation accessTypeInfo = context.getAccessTypeInfo( collectionElement.getQualifiedName().toString() ); - if ( accessTypeInfo == null ) { - AccessType explicitAccessType = TypeUtils.determineAnnotationSpecifiedAccessType( - collectionElement - ); - accessTypeInfo = new AccessTypeInformation( - collectionElement.getQualifiedName().toString(), - explicitAccessType, - entityAccessTypeInfo.getAccessType() - ); - context.addAccessTypeInformation( - collectionElement.getQualifiedName().toString(), accessTypeInfo - ); - } - else { - accessTypeInfo.setDefaultAccessType( entityAccessTypeInfo.getAccessType() ); - } - } - if ( collection.equals( "javax.persistence.metamodel.MapAttribute" ) ) { - return createAnnotationMetaAttributeForMap( declaredType, element, collection, targetEntity ); - } - else { - return new AnnotationMetaCollection( - parent, element, collection, getElementType( declaredType, targetEntity ) - ); - } - } - - @Override - public AnnotationMetaAttribute 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 ); - } - - private boolean isBasicAttribute(Element element, Element returnedElement) { - if ( TypeUtils.containsAnnotation( element, Basic.class ) - || TypeUtils.containsAnnotation( element, OneToOne.class ) - || TypeUtils.containsAnnotation( element, ManyToOne.class ) ) { - return true; - } - - BasicAttributeVisitor basicVisitor = new BasicAttributeVisitor(); - return returnedElement.asType().accept( basicVisitor, returnedElement ); - } - - private AnnotationMetaAttribute createAnnotationMetaAttributeForMap(DeclaredType declaredType, Element element, String collection, String targetEntity) { - String keyType; - if ( TypeUtils.containsAnnotation( element, MapKeyClass.class ) ) { - TypeMirror typeMirror = ( TypeMirror ) TypeUtils.getAnnotationValue( - TypeUtils.getAnnotationMirror( - element, MapKeyClass.class - ), TypeUtils.DEFAULT_ANNOTATION_PARAMETER_NAME - ); - keyType = typeMirror.toString(); - } - else { - keyType = TypeUtils.getKeyType( declaredType, context ); - } - return new AnnotationMetaMap( - parent, - element, - collection, - keyType, - getElementType( declaredType, targetEntity ) - ); - } - - private String getElementType(DeclaredType declaredType, String targetEntity) { - if ( targetEntity != null ) { - return targetEntity; - } - final List mirrors = declaredType.getTypeArguments(); - if ( mirrors.size() == 1 ) { - final TypeMirror type = mirrors.get( 0 ); - return TypeUtils.extractClosestRealTypeAsString( type, context ); - } - else if ( mirrors.size() == 2 ) { - return TypeUtils.extractClosestRealTypeAsString( mirrors.get( 1 ), context ); - } - else { - //for 0 or many - //0 is expected, many is not - if ( mirrors.size() > 2 ) { - context.logMessage( - Diagnostic.Kind.WARNING, "Unable to find the closest solid type" + declaredType - ); - } - return "?"; - } - } - - /** - * @param annotations list of annotation mirrors. - * - * @return target entity class name as string or {@code null} if no targetEntity is here or if equals to void - */ - private String getTargetEntity(List annotations) { - String fullyQualifiedTargetEntityName = null; - for ( AnnotationMirror mirror : annotations ) { - if ( TypeUtils.isAnnotationMirrorOfType( mirror, ElementCollection.class ) ) { - fullyQualifiedTargetEntityName = getFullyQualifiedClassNameOfTargetEntity( mirror, "targetClass" ); - } - else if ( TypeUtils.isAnnotationMirrorOfType( mirror, OneToMany.class ) - || TypeUtils.isAnnotationMirrorOfType( mirror, ManyToMany.class ) - || TypeUtils.isAnnotationMirrorOfType( mirror, ManyToOne.class ) - || TypeUtils.isAnnotationMirrorOfType( mirror, OneToOne.class ) ) { - fullyQualifiedTargetEntityName = getFullyQualifiedClassNameOfTargetEntity( mirror, "targetEntity" ); - } - else if ( TypeUtils.isAnnotationMirrorOfType( mirror, ORG_HIBERNATE_ANNOTATIONS_TARGET ) ) { - fullyQualifiedTargetEntityName = getFullyQualifiedClassNameOfTargetEntity( mirror, "value" ); - } - } - return fullyQualifiedTargetEntityName; - } - - private String getFullyQualifiedClassNameOfTargetEntity(AnnotationMirror mirror, String parameterName) { - assert mirror != null; - assert parameterName != null; - - String targetEntityName = null; - Object parameterValue = TypeUtils.getAnnotationValue( mirror, parameterName ); - if ( parameterValue != null ) { - TypeMirror parameterType = ( TypeMirror ) parameterValue; - if ( !parameterType.getKind().equals( TypeKind.VOID ) ) { - targetEntityName = parameterType.toString(); - } - } - return targetEntityName; - } - } - - /** - * Checks whether the visited type is a basic attribute according to the JPA 2 spec - * ( section 2.8 Mapping Defaults for Non-Relationship Fields or Properties) - */ - class BasicAttributeVisitor extends SimpleTypeVisitor6 { - @Override - public Boolean visitPrimitive(PrimitiveType t, Element element) { - return Boolean.TRUE; - } - - @Override - public Boolean visitArray(ArrayType t, Element element) { - TypeMirror componentMirror = t.getComponentType(); - TypeElement componentElement = ( TypeElement ) context.getTypeUtils().asElement( componentMirror ); - - return Constants.BASIC_ARRAY_TYPES.contains( componentElement.getQualifiedName().toString() ); - } - - @Override - public Boolean visitDeclared(DeclaredType declaredType, Element element) { - if ( ElementKind.ENUM.equals( element.getKind() ) ) { - return Boolean.TRUE; - } - - if ( ElementKind.CLASS.equals( element.getKind() ) || ElementKind.INTERFACE.equals( element.getKind() ) ) { - TypeElement typeElement = ( ( TypeElement ) element ); - String typeName = typeElement.getQualifiedName().toString(); - if ( Constants.BASIC_TYPES.contains( typeName ) ) { - return Boolean.TRUE; - } - if ( TypeUtils.containsAnnotation( element, Embeddable.class ) ) { - return Boolean.TRUE; - } - for ( TypeMirror mirror : typeElement.getInterfaces() ) { - TypeElement interfaceElement = ( TypeElement ) context.getTypeUtils().asElement( mirror ); - if ( "java.io.Serializable".equals( interfaceElement.getQualifiedName().toString() ) ) { - return Boolean.TRUE; - } - } - } - return Boolean.FALSE; - } - } } diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/MetaAttributeGenerationVisitor.java b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/MetaAttributeGenerationVisitor.java new file mode 100644 index 0000000000..9d2c4cdbba --- /dev/null +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/annotation/MetaAttributeGenerationVisitor.java @@ -0,0 +1,325 @@ +/* + * 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. + */ + +// $Id$ +package org.hibernate.jpamodelgen.annotation; + +import java.util.List; +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.TypeElement; +import javax.lang.model.type.ArrayType; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.ExecutableType; +import javax.lang.model.type.PrimitiveType; +import javax.lang.model.type.TypeKind; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.type.TypeVariable; +import javax.lang.model.util.SimpleTypeVisitor6; +import javax.persistence.AccessType; +import javax.persistence.Basic; +import javax.persistence.ElementCollection; +import javax.persistence.Embeddable; +import javax.persistence.ManyToMany; +import javax.persistence.ManyToOne; +import javax.persistence.MapKeyClass; +import javax.persistence.OneToMany; +import javax.persistence.OneToOne; +import javax.tools.Diagnostic; + +import org.hibernate.jpamodelgen.AccessTypeInformation; +import org.hibernate.jpamodelgen.Context; +import org.hibernate.jpamodelgen.util.Constants; +import org.hibernate.jpamodelgen.util.StringUtil; +import org.hibernate.jpamodelgen.util.TypeUtils; + +/** + * @author Hardy Ferentschik + */ +public class MetaAttributeGenerationVisitor extends SimpleTypeVisitor6 { + + /** + * FQCN of the Hibernate specific @Target annotation. We do not use the class directly to avoid depending on Hibernate + * Core. + */ + private static final String ORG_HIBERNATE_ANNOTATIONS_TARGET = "org.hibernate.annotations.Target"; + + private final AnnotationMetaEntity entity; + private final Context context; + + MetaAttributeGenerationVisitor(AnnotationMetaEntity entity, Context context) { + this.entity = entity; + this.context = context; + } + + @Override + public AnnotationMetaAttribute visitPrimitive(PrimitiveType t, Element element) { + return new AnnotationMetaSingleAttribute( entity, element, TypeUtils.toTypeString( t ) ); + } + + @Override + public AnnotationMetaAttribute visitArray(ArrayType t, Element element) { + // METAGEN-2 - For now we handle arrays as SingularAttribute + // The code below is an attempt to be closer to the spec and only allow byte[], Byte[], char[] and Character[] +// AnnotationMetaSingleAttribute attribute = null; +// TypeMirror componentMirror = t.getComponentType(); +// if ( TypeKind.CHAR.equals( componentMirror.getKind() ) +// || TypeKind.BYTE.equals( componentMirror.getKind() ) ) { +// attribute = new AnnotationMetaSingleAttribute( entity, element, TypeUtils.toTypeString( t ) ); +// } +// else if ( TypeKind.DECLARED.equals( componentMirror.getKind() ) ) { +// TypeElement componentElement = ( TypeElement ) context.getProcessingEnvironment() +// .getTypeUtils() +// .asElement( componentMirror ); +// if ( BASIC_ARRAY_TYPES.contains( componentElement.getQualifiedName().toString() ) ) { +// attribute = new AnnotationMetaSingleAttribute( entity, element, TypeUtils.toTypeString( t ) ); +// } +// } +// return attribute; + return new AnnotationMetaSingleAttribute( entity, element, TypeUtils.toTypeString( t ) ); + } + + public AnnotationMetaAttribute visitTypeVariable(TypeVariable t, Element element) { + // METAGEN-29 - for a type variable we use the upper bound + TypeMirror mirror = t.getUpperBound(); + TypeMirror erasedType = context.getTypeUtils().erasure( mirror ); + return new AnnotationMetaSingleAttribute( + entity, element, erasedType.toString() + ); + } + + @Override + public AnnotationMetaAttribute visitDeclared(DeclaredType declaredType, Element element) { + AnnotationMetaAttribute metaAttribute = null; + TypeElement returnedElement = (TypeElement) context.getTypeUtils().asElement( declaredType ); + // WARNING: .toString() is necessary here since Name equals does not compare to String + String fqNameOfReturnType = returnedElement.getQualifiedName().toString(); + String collection = Constants.COLLECTIONS.get( fqNameOfReturnType ); + String targetEntity = getTargetEntity( element.getAnnotationMirrors() ); + if ( collection != null ) { + return createMetaCollectionAttribute( + declaredType, element, fqNameOfReturnType, collection, targetEntity + ); + } + else if ( isBasicAttribute( element, returnedElement ) ) { + String type = targetEntity != null ? targetEntity : returnedElement.getQualifiedName().toString(); + return new AnnotationMetaSingleAttribute( entity, element, type ); + } + return metaAttribute; + } + + private AnnotationMetaAttribute createMetaCollectionAttribute(DeclaredType declaredType, Element element, String fqNameOfReturnType, String collection, String targetEntity) { + if ( TypeUtils.containsAnnotation( element, ElementCollection.class ) ) { + String explicitTargetEntity = getTargetEntity( element.getAnnotationMirrors() ); + TypeMirror collectionElementType = TypeUtils.getCollectionElementType( + declaredType, fqNameOfReturnType, explicitTargetEntity, context + ); + final TypeElement collectionElement = (TypeElement) context.getTypeUtils() + .asElement( collectionElementType ); + AccessTypeInformation accessTypeInfo = context.getAccessTypeInfo( collectionElement.getQualifiedName().toString() ); + if ( accessTypeInfo == null ) { + AccessType explicitAccessType = TypeUtils.determineAnnotationSpecifiedAccessType( + collectionElement + ); + accessTypeInfo = new AccessTypeInformation( + collectionElement.getQualifiedName().toString(), + explicitAccessType, + entity.getEntityAccessTypeInfo().getAccessType() + ); + context.addAccessTypeInformation( + collectionElement.getQualifiedName().toString(), accessTypeInfo + ); + } + else { + accessTypeInfo.setDefaultAccessType( entity.getEntityAccessTypeInfo().getAccessType() ); + } + } + if ( collection.equals( "javax.persistence.metamodel.MapAttribute" ) ) { + return createAnnotationMetaAttributeForMap( declaredType, element, collection, targetEntity ); + } + else { + return new AnnotationMetaCollection( + entity, element, collection, getElementType( declaredType, targetEntity ) + ); + } + } + + @Override + public AnnotationMetaAttribute 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 ); + } + + private boolean isBasicAttribute(Element element, Element returnedElement) { + if ( TypeUtils.containsAnnotation( element, Basic.class ) + || TypeUtils.containsAnnotation( element, OneToOne.class ) + || TypeUtils.containsAnnotation( element, ManyToOne.class ) ) { + return true; + } + + BasicAttributeVisitor basicVisitor = new BasicAttributeVisitor( context ); + return returnedElement.asType().accept( basicVisitor, returnedElement ); + } + + private AnnotationMetaAttribute createAnnotationMetaAttributeForMap(DeclaredType declaredType, Element element, String collection, String targetEntity) { + String keyType; + if ( TypeUtils.containsAnnotation( element, MapKeyClass.class ) ) { + TypeMirror typeMirror = (TypeMirror) TypeUtils.getAnnotationValue( + TypeUtils.getAnnotationMirror( + element, MapKeyClass.class + ), TypeUtils.DEFAULT_ANNOTATION_PARAMETER_NAME + ); + keyType = typeMirror.toString(); + } + else { + keyType = TypeUtils.getKeyType( declaredType, context ); + } + return new AnnotationMetaMap( + entity, + element, + collection, + keyType, + getElementType( declaredType, targetEntity ) + ); + } + + private String getElementType(DeclaredType declaredType, String targetEntity) { + if ( targetEntity != null ) { + return targetEntity; + } + final List mirrors = declaredType.getTypeArguments(); + if ( mirrors.size() == 1 ) { + final TypeMirror type = mirrors.get( 0 ); + return TypeUtils.extractClosestRealTypeAsString( type, context ); + } + else if ( mirrors.size() == 2 ) { + return TypeUtils.extractClosestRealTypeAsString( mirrors.get( 1 ), context ); + } + else { + //for 0 or many + //0 is expected, many is not + if ( mirrors.size() > 2 ) { + context.logMessage( + Diagnostic.Kind.WARNING, "Unable to find the closest solid type" + declaredType + ); + } + return "?"; + } + } + + /** + * @param annotations list of annotation mirrors. + * + * @return target entity class name as string or {@code null} if no targetEntity is here or if equals to void + */ + private String getTargetEntity(List annotations) { + String fullyQualifiedTargetEntityName = null; + for ( AnnotationMirror mirror : annotations ) { + if ( TypeUtils.isAnnotationMirrorOfType( mirror, ElementCollection.class ) ) { + fullyQualifiedTargetEntityName = getFullyQualifiedClassNameOfTargetEntity( mirror, "targetClass" ); + } + else if ( TypeUtils.isAnnotationMirrorOfType( mirror, OneToMany.class ) + || TypeUtils.isAnnotationMirrorOfType( mirror, ManyToMany.class ) + || TypeUtils.isAnnotationMirrorOfType( mirror, ManyToOne.class ) + || TypeUtils.isAnnotationMirrorOfType( mirror, OneToOne.class ) ) { + fullyQualifiedTargetEntityName = getFullyQualifiedClassNameOfTargetEntity( mirror, "targetEntity" ); + } + else if ( TypeUtils.isAnnotationMirrorOfType( mirror, ORG_HIBERNATE_ANNOTATIONS_TARGET ) ) { + fullyQualifiedTargetEntityName = getFullyQualifiedClassNameOfTargetEntity( mirror, "value" ); + } + } + return fullyQualifiedTargetEntityName; + } + + private String getFullyQualifiedClassNameOfTargetEntity(AnnotationMirror mirror, String parameterName) { + assert mirror != null; + assert parameterName != null; + + String targetEntityName = null; + Object parameterValue = TypeUtils.getAnnotationValue( mirror, parameterName ); + if ( parameterValue != null ) { + TypeMirror parameterType = (TypeMirror) parameterValue; + if ( !parameterType.getKind().equals( TypeKind.VOID ) ) { + targetEntityName = parameterType.toString(); + } + } + return targetEntityName; + } +} + +/** + * Checks whether the visited type is a basic attribute according to the JPA 2 spec + * ( section 2.8 Mapping Defaults for Non-Relationship Fields or Properties) + */ +class BasicAttributeVisitor extends SimpleTypeVisitor6 { + + private final Context context; + + public BasicAttributeVisitor(Context context) { + this.context = context; + } + + @Override + public Boolean visitPrimitive(PrimitiveType t, Element element) { + return Boolean.TRUE; + } + + @Override + public Boolean visitArray(ArrayType t, Element element) { + TypeMirror componentMirror = t.getComponentType(); + TypeElement componentElement = (TypeElement) context.getTypeUtils().asElement( componentMirror ); + + return Constants.BASIC_ARRAY_TYPES.contains( componentElement.getQualifiedName().toString() ); + } + + @Override + public Boolean visitDeclared(DeclaredType declaredType, Element element) { + if ( ElementKind.ENUM.equals( element.getKind() ) ) { + return Boolean.TRUE; + } + + if ( ElementKind.CLASS.equals( element.getKind() ) || ElementKind.INTERFACE.equals( element.getKind() ) ) { + TypeElement typeElement = ( (TypeElement) element ); + String typeName = typeElement.getQualifiedName().toString(); + if ( Constants.BASIC_TYPES.contains( typeName ) ) { + return Boolean.TRUE; + } + if ( TypeUtils.containsAnnotation( element, Embeddable.class ) ) { + return Boolean.TRUE; + } + for ( TypeMirror mirror : typeElement.getInterfaces() ) { + TypeElement interfaceElement = (TypeElement) context.getTypeUtils().asElement( mirror ); + if ( "java.io.Serializable".equals( interfaceElement.getQualifiedName().toString() ) ) { + return Boolean.TRUE; + } + } + } + return Boolean.FALSE; + } +} + + diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/util/TypeUtils.java b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/util/TypeUtils.java index 02e12ce4f9..d9b45491cc 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/util/TypeUtils.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/jpamodelgen/util/TypeUtils.java @@ -38,7 +38,6 @@ 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.lang.model.util.Types; import javax.persistence.Access; import javax.persistence.AccessType; import javax.persistence.Embeddable; @@ -59,7 +58,7 @@ import org.hibernate.jpamodelgen.MetaModelGenerationException; * @author Hardy Ferentschik * @author Emmanuel Bernard */ -public class TypeUtils { +public final class TypeUtils { public static final String DEFAULT_ANNOTATION_PARAMETER_NAME = "value"; private static final Map PRIMITIVES = new HashMap(); @@ -94,8 +93,8 @@ public class TypeUtils { //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; + final Element superClassElement = ( (DeclaredType) superClass ).asElement(); + return (TypeElement) superClassElement; } else { return null; @@ -104,19 +103,11 @@ public class TypeUtils { public static String extractClosestRealTypeAsString(TypeMirror type, Context context) { if ( type instanceof TypeVariable ) { - final TypeMirror compositeUpperBound = ( ( TypeVariable ) type ).getUpperBound(); - final Types types = context.getTypeUtils(); - final List upperBounds = types.directSupertypes( compositeUpperBound ); - if ( upperBounds.size() == 0 ) { - return compositeUpperBound.toString(); - } - else { - //take the first one - return extractClosestRealTypeAsString( upperBounds.get( 0 ), context ); - } + final TypeMirror compositeUpperBound = ( (TypeVariable) type ).getUpperBound(); + return extractClosestRealTypeAsString( compositeUpperBound, context ); } else { - return type.toString(); + return context.getTypeUtils().erasure( type ).toString(); } } @@ -345,7 +336,7 @@ public class TypeUtils { List entityAnnotations = context.getElementUtils().getAllAnnotationMirrors( subElement ); for ( Object entityAnnotation : entityAnnotations ) { - AnnotationMirror annotationMirror = ( AnnotationMirror ) entityAnnotation; + AnnotationMirror annotationMirror = (AnnotationMirror) entityAnnotation; if ( isIdAnnotation( annotationMirror ) ) { defaultAccessType = getAccessTypeOfIdAnnotation( subElement ); break; @@ -373,7 +364,7 @@ public class TypeUtils { final AnnotationMirror accessAnnotationMirror = TypeUtils.getAnnotationMirror( element, Access.class ); AccessType forcedAccessType = null; if ( accessAnnotationMirror != null ) { - Element accessElement = ( Element ) TypeUtils.getAnnotationValue( + Element accessElement = (Element) TypeUtils.getAnnotationValue( accessAnnotationMirror, DEFAULT_ANNOTATION_PARAMETER_NAME ); @@ -415,7 +406,7 @@ public class TypeUtils { @Override public String visitDeclared(DeclaredType declaredType, Element element) { - TypeElement returnedElement = ( TypeElement ) context.getTypeUtils().asElement( declaredType ); + TypeElement returnedElement = (TypeElement) context.getTypeUtils().asElement( declaredType ); String fqNameOfReturnType = null; if ( containsAnnotation( returnedElement, Embeddable.class ) ) { fqNameOfReturnType = returnedElement.getQualifiedName().toString(); diff --git a/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/typedmappedsuperclass/AttachmentGroup.java b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/typedmappedsuperclass/AttachmentGroup.java new file mode 100644 index 0000000000..822b698ea9 --- /dev/null +++ b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/typedmappedsuperclass/AttachmentGroup.java @@ -0,0 +1,33 @@ +/* + * 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.test.typedmappedsuperclass; + +import java.util.Set; +import javax.persistence.Id; +import javax.persistence.MappedSuperclass; +import javax.persistence.OneToMany; + +@MappedSuperclass +public abstract class AttachmentGroup, UserRoleType extends UserRole> { + @OneToMany(mappedBy = "parentGroup") + protected Set posts; + + @Id + long id; +} + diff --git a/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/typedmappedsuperclass/AttachmentGroupInTopic.java b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/typedmappedsuperclass/AttachmentGroupInTopic.java new file mode 100644 index 0000000000..384e6e2e4b --- /dev/null +++ b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/typedmappedsuperclass/AttachmentGroupInTopic.java @@ -0,0 +1,27 @@ +/* + * 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.test.typedmappedsuperclass; + +import javax.persistence.Entity; + +@Entity +public class AttachmentGroupInTopic + extends AttachmentGroup { + public AttachmentGroupInTopic() { + } +} diff --git a/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/typedmappedsuperclass/AttachmentGroupPost.java b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/typedmappedsuperclass/AttachmentGroupPost.java new file mode 100644 index 0000000000..ab59d1c3a0 --- /dev/null +++ b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/typedmappedsuperclass/AttachmentGroupPost.java @@ -0,0 +1,27 @@ +/* + * 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.test.typedmappedsuperclass; + + +import javax.persistence.MappedSuperclass; + +@MappedSuperclass +public abstract class AttachmentGroupPost + extends Post { +} + diff --git a/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/typedmappedsuperclass/AttachmentGroupPostInTopic.java b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/typedmappedsuperclass/AttachmentGroupPostInTopic.java new file mode 100644 index 0000000000..be3ffa62a8 --- /dev/null +++ b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/typedmappedsuperclass/AttachmentGroupPostInTopic.java @@ -0,0 +1,24 @@ +/* + * 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.test.typedmappedsuperclass; + +import javax.persistence.Entity; + +@Entity +public class AttachmentGroupPostInTopic extends AttachmentGroupPost { +} diff --git a/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/typedmappedsuperclass/Post.java b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/typedmappedsuperclass/Post.java new file mode 100644 index 0000000000..8cc221d33e --- /dev/null +++ b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/typedmappedsuperclass/Post.java @@ -0,0 +1,25 @@ +/* + * 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.test.typedmappedsuperclass; + +import javax.persistence.MappedSuperclass; + +@MappedSuperclass +public abstract class Post { +} + diff --git a/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/typedmappedsuperclass/TypesMappedSuperclassTest.java b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/typedmappedsuperclass/TypesMappedSuperclassTest.java new file mode 100644 index 0000000000..a88fe2114c --- /dev/null +++ b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/typedmappedsuperclass/TypesMappedSuperclassTest.java @@ -0,0 +1,42 @@ +/* + * 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. + */ + +// $Id$ + +package org.hibernate.jpamodelgen.test.typedmappedsuperclass; + +import org.testng.annotations.Test; + +import org.hibernate.jpamodelgen.test.util.CompilationTest; + +import static org.hibernate.jpamodelgen.test.util.TestUtil.assertMetamodelClassGeneratedFor; + +/** + * @author Hardy Ferentschik + * @see METAGEN-37 + */ +public class TypesMappedSuperclassTest extends CompilationTest { + @Test + public void testExtractClosestRealType() { + assertMetamodelClassGeneratedFor( AttachmentGroup.class ); + } + + @Override + protected String getPackageNameOfCurrentTest() { + return TypesMappedSuperclassTest.class.getPackage().getName(); + } +} \ No newline at end of file diff --git a/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/typedmappedsuperclass/UserRole.java b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/typedmappedsuperclass/UserRole.java new file mode 100644 index 0000000000..afce2bad35 --- /dev/null +++ b/tooling/metamodel-generator/src/test/java/org/hibernate/jpamodelgen/test/typedmappedsuperclass/UserRole.java @@ -0,0 +1,25 @@ +/* + * 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.test.typedmappedsuperclass; + +import javax.persistence.Entity; + +@Entity +public class UserRole { +} +