METAGEN-8

Made sure that the right element type is extracted in case the is a ElementCollection on Map.
I think however, there are more problems. We only cover the java.util.Map case. We need to take care of subclasses as well.
This commit is contained in:
Hardy Ferentschik 2009-11-06 18:23:48 +00:00 committed by Strong Liu
parent fe0038ce14
commit 41c79d533c
12 changed files with 251 additions and 83 deletions

View File

@ -1,19 +0,0 @@
- Find host for project
- Implement XML overriding of annotation metadata
- Implement access Type rules for XML metadata
- Investigate why it is not possible to use the Filer API to load non class resources, eg
/META-INF/orm.xml. The API throws a FilerException with the message "Illegal name /META-INF".
The call is processingEnv.getFiler().getResource((StandardLocation.CLASS_OUTPUT, "/META-INF", "orm.xml" )
Currently we work around this by using Class.getResourceAsStream()
- Optimize XML parsing by only processing XML files if they have changed since last run (maybe write
a tmp file against which to compare the last modified time stamp)
- Reduce the amount of logging and make logging configurable using the possibility to pass arguments to
the processor using -Akey[=value]
- Write IDE specific plugins which allow to cache processing steps

View File

@ -31,10 +31,15 @@
<licenses>
<license>
<name>Apache License, Version 2.0</name>
<url>license.txt</url>
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
</license>
</licenses>
<issueManagement>
<system>JIRA</system>
<url>http://opensource.atlassian.com/projects/hibernate/browse/METAGEN</url>
</issueManagement>
<scm>
<connection>scm:svn:https://svn.jboss.org/repos/hibernate/jpamodelgen/trunk</connection>
<url>http://fisheye.jboss.org/browse/Hibernate/jpamodelgen/trunk</url>
@ -231,9 +236,10 @@
<distributionManagement>
<repository>
<!-- Copy the dist to the local checkout of the JBoss maven2 repo ${maven.repository.root} -->
<!-- It is anticipated that ${maven.repository.root} be set in user's settings.xml -->
<id>repository.jboss.org</id>
<name>JBoss Release Repository</name>
<url>scm:svn:https://svn.jboss.org/repos/repository.jboss.org/maven2</url>
<url>file://${maven.repository.root}</url>
</repository>
<snapshotRepository>
<id>snapshots.jboss.org</id>

View File

@ -109,7 +109,7 @@ public class Context {
logMessage( Diagnostic.Kind.WARNING, "Element already processed (ignoring): " + element );
return;
}
ClassWriter.writeFile( new AnnotationMetaEntity( pe, element, this, defaultAccessTypeForHierarchy ), this );
ClassWriter.writeFile( new AnnotationMetaEntity( element, this, defaultAccessTypeForHierarchy ), this );
elementsAlreadyProcessed.add( element.getQualifiedName().toString() );
}

View File

@ -315,17 +315,13 @@ public class JPAMetaModelEntityProcessor extends AbstractProcessor {
if ( element.getKind() == ElementKind.CLASS ) {
if ( annotationType.equals( ENTITY_ANN ) ) {
AnnotationMetaEntity metaEntity = new AnnotationMetaEntity(
processingEnv, ( TypeElement ) element, context
);
AnnotationMetaEntity metaEntity = new AnnotationMetaEntity( ( TypeElement ) element, context );
// TODO instead of just adding the entity we have to do some merging.
context.getMetaEntitiesToProcess().put( metaEntity.getQualifiedName(), metaEntity );
}
else if ( annotationType.equals( MAPPED_SUPERCLASS_ANN )
|| annotationType.equals( EMBEDDABLE_ANN ) ) {
AnnotationMetaEntity metaEntity = new AnnotationMetaEntity(
processingEnv, ( TypeElement ) element, context
);
AnnotationMetaEntity metaEntity = new AnnotationMetaEntity( ( TypeElement ) element, context );
// TODO instead of just adding the entity we have to do some merging.
context.getMetaSuperclassAndEmbeddableToProcess().put( metaEntity.getQualifiedName(), metaEntity );

View File

@ -17,16 +17,14 @@
*/
package org.hibernate.jpamodelgen.annotation;
import org.hibernate.jpamodelgen.MetaAttribute;
import java.beans.Introspector;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.util.Elements;
import org.hibernate.jpamodelgen.MetaAttribute;
/**
*
* @author Max Andersen
* @author Hardy Ferentschik
* @author Emmanuel Bernard
@ -35,40 +33,42 @@ public abstract class AnnotationMetaAttribute implements MetaAttribute {
final protected Element element;
final protected AnnotationMetaEntity parent;
final protected ProcessingEnvironment pe;
private final String type;
public AnnotationMetaAttribute(AnnotationMetaEntity parent, Element element, String type) {
this.element = element;
this.parent = parent;
this.type = type;
this.pe = parent.pe;
}
public String getDeclarationString() {
return "public static volatile " + parent.importType(getMetaType()) + "<" + parent.importType(parent.getQualifiedName()) + ", " + parent.importType(getTypeDeclaration()) + "> " + getPropertyName() + ";";
return "public static volatile " + parent.importType( getMetaType() ) + "<" + parent.importType( parent.getQualifiedName() ) + ", " + parent
.importType( getTypeDeclaration() ) + "> " + getPropertyName() + ";";
}
public String getPropertyName() {
if(element.getKind()==ElementKind.FIELD) {
Elements elementsUtil = parent.getContext().getProcessingEnvironment().getElementUtils();
if ( element.getKind() == ElementKind.FIELD ) {
return element.getSimpleName().toString();
} else if (element.getKind()==ElementKind.METHOD) {
}
else if ( element.getKind() == ElementKind.METHOD ) {
String name = element.getSimpleName().toString();
if(name.startsWith("get")) {
return pe.getElementUtils().getName(Introspector.decapitalize(name.substring("get".length()))).toString();
} else if(name.startsWith("is")) {
return (pe.getElementUtils().getName(Introspector.decapitalize(name.substring("is".length())))).toString();
if ( name.startsWith( "get" ) ) {
return elementsUtil.getName( Introspector.decapitalize( name.substring( "get".length() ) ) ).toString();
}
return pe.getElementUtils().getName(Introspector.decapitalize(name)).toString();
} else {
return pe.getElementUtils().getName(element.getSimpleName() + "/* " + element.getKind() + " */").toString();
else if ( name.startsWith( "is" ) ) {
return ( elementsUtil.getName( Introspector.decapitalize( name.substring( "is".length() ) ) ) ).toString();
}
return elementsUtil.getName( Introspector.decapitalize( name ) ).toString();
}
else {
return elementsUtil.getName( element.getSimpleName() + "/* " + element.getKind() + " */" ).toString();
}
}
abstract public String getMetaType();
public String getTypeDeclaration() {
return type;
return type;
}
}

View File

@ -21,7 +21,6 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
@ -62,27 +61,37 @@ import org.hibernate.jpamodelgen.TypeUtils;
*/
public class AnnotationMetaEntity implements MetaEntity {
final TypeElement element;
final protected ProcessingEnvironment pe;
static Map<String, String> COLLECTIONS = new HashMap<String, String>();
final ImportContext importContext;
static {
COLLECTIONS.put( "java.util.Collection", "javax.persistence.metamodel.CollectionAttribute" );
COLLECTIONS.put( "java.util.Set", "javax.persistence.metamodel.SetAttribute" );
COLLECTIONS.put( "java.util.List", "javax.persistence.metamodel.ListAttribute" );
COLLECTIONS.put( "java.util.Map", "javax.persistence.metamodel.MapAttribute" );
}
private final TypeElement element;
private final ImportContext importContext;
private Context context;
//used to propagate the access type of the root entity over to subclasses, superclasses and embeddable
private AccessType defaultAccessTypeForHierarchy;
private AccessType defaultAccessTypeForElement;
public AnnotationMetaEntity(ProcessingEnvironment pe, TypeElement element, Context context) {
public AnnotationMetaEntity(TypeElement element, Context context) {
this.element = element;
this.pe = pe;
importContext = new ImportContextImpl( getPackageName() );
this.context = context;
importContext = new ImportContextImpl( getPackageName() );
}
public AnnotationMetaEntity(ProcessingEnvironment pe, TypeElement element, Context context, AccessType accessType) {
this( pe, element, context );
public AnnotationMetaEntity(TypeElement element, Context context, AccessType accessType) {
this( element, context );
this.defaultAccessTypeForHierarchy = accessType;
}
public Context getContext() {
return context;
}
public String getSimpleName() {
return element.getSimpleName().toString();
}
@ -92,8 +101,8 @@ public class AnnotationMetaEntity implements MetaEntity {
}
public String getPackageName() {
PackageElement packageOf = pe.getElementUtils().getPackageOf( element );
return pe.getElementUtils().getName( packageOf.getQualifiedName() ).toString();
PackageElement packageOf = context.getProcessingEnvironment().getElementUtils().getPackageOf( element );
return context.getProcessingEnvironment().getElementUtils().getName( packageOf.getQualifiedName() ).toString();
}
public List<MetaAttribute> getMembers() {
@ -153,10 +162,9 @@ public class AnnotationMetaEntity implements MetaEntity {
accessType = this.defaultAccessTypeForHierarchy;
}
if ( accessType == null ) {
//we dont' know
//if an enity go up
//we don't know if an entity go up
//
//superclasses alre always treated after their entities
//superclasses are always treated after their entities
//and their access type are discovered
//FIXME is it really true if only the superclass is changed
TypeElement superClass = element;
@ -220,7 +228,7 @@ public class AnnotationMetaEntity implements MetaEntity {
List<? extends Element> myMembers = searchedElement.getEnclosedElements();
for ( Element subElement : myMembers ) {
List<? extends AnnotationMirror> entityAnnotations =
pe.getElementUtils().getAllAnnotationMirrors( subElement );
context.getProcessingEnvironment().getElementUtils().getAllAnnotationMirrors( subElement );
for ( Object entityAnnotation : entityAnnotations ) {
AnnotationMirror annotationMirror = ( AnnotationMirror ) entityAnnotation;
@ -274,15 +282,6 @@ public class AnnotationMetaEntity implements MetaEntity {
return sb.toString();
}
static Map<String, String> COLLECTIONS = new HashMap<String, String>();
static {
COLLECTIONS.put( "java.util.Collection", "javax.persistence.metamodel.CollectionAttribute" );
COLLECTIONS.put( "java.util.Set", "javax.persistence.metamodel.SetAttribute" );
COLLECTIONS.put( "java.util.List", "javax.persistence.metamodel.ListAttribute" );
COLLECTIONS.put( "java.util.Map", "javax.persistence.metamodel.MapAttribute" );
}
class TypeVisitor extends SimpleTypeVisitor6<AnnotationMetaAttribute, Element> {
AnnotationMetaEntity parent;
@ -343,15 +342,18 @@ public class AnnotationMetaEntity implements MetaEntity {
public AnnotationMetaAttribute visitDeclared(DeclaredType t, Element element) {
//FIXME consider XML
if ( isPersistent( element ) ) {
TypeElement returnedElement = ( TypeElement ) pe.getTypeUtils().asElement( t );
String collection = COLLECTIONS.get( returnedElement.getQualifiedName().toString() ); // WARNING: .toString() is necessary here since Name equals does not compare to String
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
String collection = COLLECTIONS.get( fqElementName );
if ( collection != null ) {
//collection of element
if ( element.getAnnotation( ElementCollection.class ) != null ) {
final TypeMirror collectionType = t.getTypeArguments().get( 0 );
final TypeElement collectionElement = ( TypeElement ) pe.getTypeUtils()
.asElement( collectionType );
TypeMirror collectionElementType = getCollectionElementType( t, fqElementName );
final TypeElement collectionElement = ( TypeElement ) context.getProcessingEnvironment()
.getTypeUtils()
.asElement( collectionElementType );
this.parent.context.processElement(
collectionElement,
this.parent.defaultAccessTypeForElement
@ -385,6 +387,17 @@ public class AnnotationMetaEntity implements MetaEntity {
}
}
private TypeMirror getCollectionElementType(DeclaredType t, String fqElementName) {
TypeMirror collectionElementType;
if ( Map.class.getCanonicalName().equals( fqElementName ) ) {
collectionElementType = t.getTypeArguments().get( 1 );
}
else {
collectionElementType = t.getTypeArguments().get( 0 );
}
return collectionElementType;
}
@Override
public AnnotationMetaAttribute visitExecutable(ExecutableType t, Element p) {
String string = p.getSimpleName().toString();

View File

@ -0,0 +1,46 @@
// $Id$
/*
* JBoss, Home of Professional Open Source
* Copyright 2008, 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.elementcollection;
import org.testng.annotations.Test;
import org.hibernate.jpamodelgen.test.util.CompilationTest;
import static org.hibernate.jpamodelgen.test.util.TestUtil.assertClassGenerated;
import static org.hibernate.jpamodelgen.test.util.TestUtil.assertClassNotFound;
import static org.hibernate.jpamodelgen.test.util.TestUtil.assertNoGeneratedSourceFile;
/**
* @author Hardy Ferentschik
*/
public class ElementCollectionTest extends CompilationTest {
/**
* METAGEN-8
*/
@Test
public void testElementCollectionOnMap() {
assertClassGenerated( House.class.getName() + "_" );
assertClassGenerated( House.class.getName() + "_" );
// side effect of METAGEN-8 was that a meta class for String was created!
assertNoGeneratedSourceFile( String.class.getName() + "_" );
}
@Override
protected String getTestPackage() {
return House.class.getPackage().getName();
}
}

View File

@ -0,0 +1,43 @@
// $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.elementcollection;
import java.util.Map;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.MapKeyColumn;
/**
* @author Hardy Ferentschik
*/
@Entity
public class House {
private Map<String, Room> roomsByName;
@ElementCollection
@MapKeyColumn(name = "room_name")
public Map<String, Room> getRoomsByName() {
return roomsByName;
}
public void setRoomsByName(Map<String, Room> roomsByName) {
this.roomsByName = roomsByName;
}
}

View File

@ -0,0 +1,48 @@
// $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.elementcollection;
import java.math.BigDecimal;
import javax.persistence.Embeddable;
/**
* @author Hardy Ferentschik
*/
@Embeddable
public class Room {
private BigDecimal length;
private BigDecimal width;
public BigDecimal getLength() {
return length;
}
public void setLength(BigDecimal length) {
this.length = length;
}
public BigDecimal getWidth() {
return width;
}
public void setWidth(BigDecimal width) {
this.width = width;
}
}

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
@ -22,7 +22,6 @@ import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.tools.Diagnostic;
import javax.tools.DiagnosticCollector;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
@ -112,7 +111,7 @@ public abstract class CompilationTest {
FilenameFilter javaFileFilter = new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return name.endsWith( ".java" );
return name.endsWith( ".java" ) && !name.endsWith( "Test.java" );
}
};
for ( File file : packageDir.listFiles( javaFileFilter ) ) {

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
@ -17,13 +17,18 @@
*/
package org.hibernate.jpamodelgen.test.util;
import java.io.File;
import java.io.FilenameFilter;
import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.Vector;
import org.testng.Assert;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertNotNull;
import static org.testng.FileAssert.fail;
@ -32,6 +37,17 @@ import static org.testng.FileAssert.fail;
*/
public class TestUtil {
private static final String PATH_SEPARATOR = System.getProperty( "file.separator" );
private static final String outBaseDir;
static {
String tmp = System.getProperty( "outBaseDir" );
if ( tmp == null ) {
fail( "The system property outBaseDir has to be set and point to the base directory of the test output directory." );
}
outBaseDir = tmp;
}
private TestUtil() {
}
@ -44,6 +60,25 @@ public class TestUtil {
}
}
public static void assertClassNotFound(String className) {
try {
Class.forName( className );
fail( "Class " + className + " should not have been found." );
}
catch ( ClassNotFoundException e ) {
// success
}
}
public static void assertNoGeneratedSourceFile(String className) {
// generate the file name
String fileName = className.replace( ".", PATH_SEPARATOR );
fileName = fileName.concat( ".java" );
File sourceFile = new File(outBaseDir + PATH_SEPARATOR + fileName);
assertFalse(sourceFile.exists(), "There should be no source file: " + fileName);
}
public static void assertAbsenceOfField(String className, String fieldName) throws ClassNotFoundException {
assertAbsenceOfField( className, fieldName, "field should not be persistent" );
}

View File

@ -5,6 +5,7 @@
<packages>
<package name="org.hibernate.jpamodelgen.test.accesstype"/>
<package name="org.hibernate.jpamodelgen.test.arraytype"/>
<package name="org.hibernate.jpamodelgen.test.elementcollection"/>
<package name="org.hibernate.jpamodelgen.test.inheritance"/>
<package name="org.hibernate.jpamodelgen.test.xmlmapped"/>
</packages>