METAGEN-18 Support @ElementCollection.targetClass + its XMl equivalent

This commit is contained in:
Hardy Ferentschik 2010-01-26 21:42:19 +00:00 committed by Strong Liu
parent 19d68006f3
commit 149f8ae8f4
12 changed files with 1619 additions and 1571 deletions

View File

@ -1,6 +1,4 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>

View File

@ -17,8 +17,6 @@
*/
package org.hibernate.jpamodelgen;
import org.hibernate.jpamodelgen.MetaAttribute;
/**
* @author Hardy Ferentschik
*/

View File

@ -17,30 +17,25 @@
*/
package org.hibernate.jpamodelgen.annotation;
import org.hibernate.jpamodelgen.MetaCollection;
import javax.lang.model.element.Element;
import org.hibernate.jpamodelgen.MetaCollection;
/**
*
* @author Max Andersen
* @author Hardy Ferentschik
* @author Emmanuel Bernard
*/
public class AnnotationMetaCollection extends AnnotationMetaAttribute implements MetaCollection {
private String collectionType;
private String collectionType;
public AnnotationMetaCollection(AnnotationMetaEntity parent, Element element, String collectionType, String elementType) {
super(parent, element, elementType);
this.collectionType = collectionType;
super( parent, element, elementType );
this.collectionType = collectionType;
}
@Override
public String getMetaType() {
public String getMetaType() {
return collectionType;
}
}

View File

@ -22,10 +22,8 @@ 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.Modifier;
import javax.lang.model.element.Name;
import javax.lang.model.element.PackageElement;
@ -34,6 +32,7 @@ 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.util.ElementFilter;
import javax.lang.model.util.SimpleTypeVisitor6;
@ -312,8 +311,11 @@ public class AnnotationMetaEntity implements MetaEntity {
class TypeVisitor extends SimpleTypeVisitor6<AnnotationMetaAttribute, Element> {
AnnotationMetaEntity parent;
//if null, process all members as implicit
//if not null, only process members marked as @Access(explicitAccessType)
/*
* If {@code explicitAccessType == null}, process all members as implicit. If {@code explicitAccessType != null}
* only process members marked as {@code @Access(explicitAccessType)}.
*/
private AccessType explicitAccessType;
TypeVisitor(AnnotationMetaEntity parent, AccessType explicitAccessType) {
@ -366,20 +368,19 @@ public class AnnotationMetaEntity implements MetaEntity {
}
@Override
public AnnotationMetaAttribute visitDeclared(DeclaredType t, Element element) {
public AnnotationMetaAttribute visitDeclared(DeclaredType declaredType, Element element) {
//FIXME consider XML
if ( isPersistent( element ) ) {
TypeElement returnedElement = ( TypeElement ) context.getProcessingEnvironment()
.getTypeUtils()
.asElement( t );
String fqElementName = returnedElement.getQualifiedName()
.toString(); // WARNING: .toString() is necessary here since Name equals does not compare to String
.asElement( declaredType );
// WARNING: .toString() is necessary here since Name equals does not compare to String
String fqElementName = returnedElement.getQualifiedName().toString();
String collection = COLLECTIONS.get( fqElementName );
String targetEntity = getTargetEntity( element.getAnnotationMirrors() );
if ( collection != null ) {
if ( TypeUtils.containsAnnotation( element, ElementCollection.class ) ) {
//FIXME I don't understand why this code is different between Elementcollection and a regular collection but it needs to take targetClass into account
TypeMirror collectionElementType = getCollectionElementType( t, fqElementName );
TypeMirror collectionElementType = getCollectionElementType( declaredType, fqElementName );
final TypeElement collectionElement = ( TypeElement ) context.getProcessingEnvironment()
.getTypeUtils()
.asElement( collectionElementType );
@ -391,12 +392,16 @@ public class AnnotationMetaEntity implements MetaEntity {
if ( collection.equals( "javax.persistence.metamodel.MapAttribute" ) ) {
return new AnnotationMetaMap(
//FIXME support targetEntity for map's key @MapKeyClass
parent, element, collection, getKeyType( t ), getElementType( t, targetEntity )
parent,
element,
collection,
getKeyType( declaredType ),
getElementType( declaredType, targetEntity )
);
}
else {
return new AnnotationMetaCollection(
parent, element, collection, getElementType( t, targetEntity )
parent, element, collection, getElementType( declaredType, targetEntity )
);
}
}
@ -446,39 +451,40 @@ public class AnnotationMetaEntity implements MetaEntity {
}
/**
* @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<? extends AnnotationMirror> annotations) {
String fullyQualifiedTargetEntityName = null;
for ( AnnotationMirror mirror : annotations ) {
if ( TypeUtils.isAnnotationMirrorOfType( mirror, ElementCollection.class )
|| TypeUtils.isAnnotationMirrorOfType( mirror, OneToMany.class )
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 ) ) {
final String targetEntity = getTargetEntity( mirror );
if ( targetEntity != null ) {
return targetEntity;
}
fullyQualifiedTargetEntityName = getFullyQualifiedClassNameOfTargetEntity( mirror, "targetEntity" );
}
}
return null;
return fullyQualifiedTargetEntityName;
}
private String getTargetEntity(AnnotationMirror mirror) {
String targetEntity = null;
final Map<? extends ExecutableElement, ? extends AnnotationValue> attributes = mirror.getElementValues();
for ( Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : attributes.entrySet() ) {
final String simpleName = entry.getKey().getSimpleName().toString();
if ( "targetEntity".equals( simpleName ) ) {
targetEntity = entry.getValue().toString();
break;
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();
}
}
targetEntity = ( "void.class".equals( targetEntity ) ) ? null : targetEntity;
return targetEntity != null && targetEntity.endsWith( ".class" ) ?
targetEntity.substring( 0, targetEntity.length() - 6 ) : null;
return targetEntityName;
}
public String generateImports() {

View File

@ -27,10 +27,10 @@ import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
import org.hibernate.jpamodelgen.Context;
import org.hibernate.jpamodelgen.MetaAttribute;
import org.hibernate.jpamodelgen.ImportContextImpl;
import org.hibernate.jpamodelgen.MetaEntity;
import org.hibernate.jpamodelgen.ImportContext;
import org.hibernate.jpamodelgen.ImportContextImpl;
import org.hibernate.jpamodelgen.MetaAttribute;
import org.hibernate.jpamodelgen.MetaEntity;
import org.hibernate.jpamodelgen.util.TypeUtils;
import org.hibernate.jpamodelgen.xml.jaxb.Attributes;
import org.hibernate.jpamodelgen.xml.jaxb.Basic;
@ -101,29 +101,33 @@ public class XmlMetaEntity implements MetaEntity {
XmlMetaSingleAttribute attribute;
for ( Basic basic : attributes.getBasic() ) {
attribute = new XmlMetaSingleAttribute( this, basic.getName(), getType( basic.getName() ) );
attribute = new XmlMetaSingleAttribute( this, basic.getName(), getType( basic.getName(), null ) );
members.add( attribute );
}
for ( ManyToOne manyToOne : attributes.getManyToOne() ) {
attribute = new XmlMetaSingleAttribute( this, manyToOne.getName(), getType( manyToOne.getName() ) );
attribute = new XmlMetaSingleAttribute(
this, manyToOne.getName(), getType( manyToOne.getName(), manyToOne.getTargetEntity() )
);
members.add( attribute );
}
for ( OneToOne oneToOne : attributes.getOneToOne() ) {
attribute = new XmlMetaSingleAttribute( this, oneToOne.getName(), getType( oneToOne.getName() ) );
attribute = new XmlMetaSingleAttribute(
this, oneToOne.getName(), getType( oneToOne.getName(), oneToOne.getTargetEntity() )
);
members.add( attribute );
}
XmlMetaCollection metaCollection;
for ( OneToMany oneToMany : attributes.getOneToMany() ) {
String[] types = getCollectionType( oneToMany.getName() );
String[] types = getCollectionType( oneToMany.getName(), oneToMany.getTargetEntity() );
metaCollection = new XmlMetaCollection( this, oneToMany.getName(), types[0], types[1] );
members.add( metaCollection );
}
for ( ElementCollection collection : attributes.getElementCollection() ) {
String[] types = getCollectionType( collection.getName() );
String[] types = getCollectionType( collection.getName(), collection.getTargetClass() );
metaCollection = new XmlMetaCollection( this, collection.getName(), types[0], types[1] );
members.add( metaCollection );
}
@ -165,19 +169,37 @@ public class XmlMetaEntity implements MetaEntity {
return element;
}
private String[] getCollectionType(String propertyName) {
private String[] getCollectionType(String propertyName, String explicitTargetEntity) {
String types[] = new String[2];
for ( Element elem : element.getEnclosedElements() ) {
if ( elem.getSimpleName().toString().equals( propertyName ) ) {
DeclaredType type = ( ( DeclaredType ) elem.asType() );
types[0] = TypeUtils.extractClosestRealTypeAsString(type.getTypeArguments().get( 0 ), context);
if ( explicitTargetEntity == null ) {
types[0] = TypeUtils.extractClosestRealTypeAsString( type.getTypeArguments().get( 0 ), context );
}
else {
types[0] = explicitTargetEntity;
}
types[1] = COLLECTIONS.get( type.asElement().toString() );
}
}
return types;
}
private String getType(String propertyName) {
/**
* Returns the entity type for relation.
*
* @param propertyName The property name of the association
* @param explicitTargetEntity The explicitly specified target entity type
*
* @return The entity type for relation/association.
*/
private String getType(String propertyName, String explicitTargetEntity) {
if ( explicitTargetEntity != null ) {
// TODO should there be a check of the target entity class and if it is loadable?
return explicitTargetEntity;
}
String typeName = null;
for ( Element elem : element.getEnclosedElements() ) {
if ( elem.getSimpleName().toString().equals( propertyName ) ) {
@ -225,35 +247,39 @@ public class XmlMetaEntity implements MetaEntity {
// TODO what do we do if there are more than one id nodes?
Id id = attributes.getId().get( 0 );
attribute = new XmlMetaSingleAttribute(
this, id.getName(), getType( id.getName() )
this, id.getName(), getType( id.getName(), null )
);
members.add( attribute );
}
for ( Basic basic : attributes.getBasic() ) {
attribute = new XmlMetaSingleAttribute( this, basic.getName(), getType( basic.getName() ) );
attribute = new XmlMetaSingleAttribute( this, basic.getName(), getType( basic.getName(), null ) );
members.add( attribute );
}
for ( ManyToOne manyToOne : attributes.getManyToOne() ) {
attribute = new XmlMetaSingleAttribute( this, manyToOne.getName(), getType( manyToOne.getName() ) );
attribute = new XmlMetaSingleAttribute(
this, manyToOne.getName(), getType( manyToOne.getName(), manyToOne.getTargetEntity() )
);
members.add( attribute );
}
for ( OneToOne oneToOne : attributes.getOneToOne() ) {
attribute = new XmlMetaSingleAttribute( this, oneToOne.getName(), getType( oneToOne.getName() ) );
attribute = new XmlMetaSingleAttribute(
this, oneToOne.getName(), getType( oneToOne.getName(), oneToOne.getTargetEntity() )
);
members.add( attribute );
}
XmlMetaCollection metaCollection;
for ( OneToMany oneToMany : attributes.getOneToMany() ) {
String[] types = getCollectionType( oneToMany.getName() );
String[] types = getCollectionType( oneToMany.getName(), oneToMany.getTargetEntity() );
metaCollection = new XmlMetaCollection( this, oneToMany.getName(), types[0], types[1] );
members.add( metaCollection );
}
for ( ElementCollection collection : attributes.getElementCollection() ) {
String[] types = getCollectionType( collection.getName() );
String[] types = getCollectionType( collection.getName(), collection.getTargetClass() );
metaCollection = new XmlMetaCollection( this, collection.getName(), types[0], types[1] );
members.add( metaCollection );
}

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
// $Id:$
// $Id$
/*
* JBoss, Home of Professional Open Source
* Copyright 2009, Red Hat, Inc. and/or its affiliates, and individual contributors
@ -29,7 +29,7 @@ import javax.persistence.MapKeyColumn;
public class House {
private Map<String, Room> roomsByName;
@ElementCollection
@ElementCollection(targetClass = Room.class)
@MapKeyColumn(name = "room_name")
public Map<String, Room> getRoomsByName() {
return roomsByName;

View File

@ -56,7 +56,7 @@ public class TestUtil {
assertNotNull( Class.forName( className ) );
}
catch ( ClassNotFoundException e ) {
fail( e.getMessage() );
fail( className + " was not generated." );
}
}
@ -126,7 +126,7 @@ public class TestUtil {
return getField( className, fieldName ) != null;
}
private static Field getField(String className, String fieldName) throws ClassNotFoundException {
public static Field getField(String className, String fieldName) throws ClassNotFoundException {
Class<?> clazz = Class.forName( className );
try {
return clazz.getField( fieldName );

View File

@ -0,0 +1,61 @@
// $Id:$
/*
* JBoss, Home of Professional Open Source
* Copyright 2009, Red Hat, Inc. and/or its affiliates, 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.xmlmapped;
import java.util.List;
/**
* @author Hardy Ferentschik
*/
public class Boy {
private long id;
private String name;
/*
* The mapping in boy.xml specifies as target-class for the element collection java.lang.Integer. This makes no
* sense from a mapping point, but makes it easy to test.
*/
private List<String> nickNames;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<String> getNickNames() {
return nickNames;
}
public void setNickNames(List<String> nickNames) {
this.nickNames = nickNames;
}
}

View File

@ -17,12 +17,18 @@
*/
package org.hibernate.jpamodelgen.test.xmlmapped;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import org.testng.annotations.Test;
import org.hibernate.jpamodelgen.test.util.CompilationTest;
import static junit.framework.Assert.assertTrue;
import static org.hibernate.jpamodelgen.test.util.TestUtil.assertClassGenerated;
import static org.hibernate.jpamodelgen.test.util.TestUtil.assertPresenceOfField;
import static org.hibernate.jpamodelgen.test.util.TestUtil.assertSuperClass;
import static org.hibernate.jpamodelgen.test.util.TestUtil.getField;
/**
* @author Hardy Ferentschik
@ -41,6 +47,17 @@ public class XmlMappingTest extends CompilationTest {
);
}
@Test
public void testXmlConfiguredElementCollection() throws Exception {
assertClassGenerated( Boy.class.getName() + "_" );
assertPresenceOfField(
Boy.class.getName() + "_", "nickNames", "nickNames field should exist"
);
Field field = getField( Boy.class.getName() + "_", "nickNames" );
ParameterizedType type = ( ParameterizedType ) field.getGenericType();
assertTrue( "Wrong target type", type.getActualTypeArguments()[1].equals( Integer.class ) );
}
@Test
public void testClassHierarchy() throws Exception {
assertClassGenerated( Mammal.class.getName() + "_" );

View File

@ -10,5 +10,6 @@
<mapping-file>/org/hibernate/jpamodelgen/test/xmlmapped/address.xml</mapping-file>
<mapping-file>/org/hibernate/jpamodelgen/test/xmlmapped/building.xml</mapping-file>
<mapping-file>/org/hibernate/jpamodelgen/test/xmlmapped/mammal.xml</mapping-file>
<mapping-file>/org/hibernate/jpamodelgen/test/xmlmapped/boy.xml</mapping-file>
</persistence-unit>
</persistence>

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm orm_2_0.xsd"
version="2.0"
>
<package>org.hibernate.jpamodelgen.test.xmlmapped</package>
<entity class="Boy" access="FIELD" metadata-complete="true">
<attributes>
<id name="id"/>
<basic name="name"/>
<element-collection name="nickNames" target-class="java.lang.Integer"/>
</attributes>
</entity>
</entity-mappings>