METAGEN-94 Initializing members of mapped superclasses lazily in order to correctly take access type declared by subtypes into account

This commit is contained in:
Gunnar Morling 2013-08-08 11:53:55 +02:00 committed by Strong Liu
parent b61dc5cc98
commit 399bc3542f
7 changed files with 68 additions and 165 deletions

View File

@ -39,7 +39,6 @@ import javax.lang.model.util.ElementFilter;
import javax.lang.model.util.SimpleTypeVisitor6;
import javax.tools.Diagnostic;
import org.hibernate.jpamodelgen.annotation.AnnotationEmbeddable;
import org.hibernate.jpamodelgen.annotation.AnnotationMetaEntity;
import org.hibernate.jpamodelgen.model.MetaEntity;
import org.hibernate.jpamodelgen.util.Constants;
@ -243,14 +242,15 @@ public class JPAMetaModelEntityProcessor extends AbstractProcessor {
continue;
}
boolean requiresLazyMemberInitialization = false;
AnnotationMetaEntity metaEntity;
if ( TypeUtils.containsAnnotation( element, Constants.EMBEDDABLE ) ) {
metaEntity = new AnnotationEmbeddable( (TypeElement) element, context );
}
else {
metaEntity = new AnnotationMetaEntity( (TypeElement) element, context );
if ( TypeUtils.containsAnnotation( element, Constants.EMBEDDABLE ) ||
TypeUtils.containsAnnotation( element, Constants.MAPPED_SUPERCLASS ) ) {
requiresLazyMemberInitialization = true;
}
metaEntity = new AnnotationMetaEntity( (TypeElement) element, context, requiresLazyMemberInitialization );
if ( alreadyExistingMetaEntity != null ) {
metaEntity.mergeInMembers( alreadyExistingMetaEntity.getMembers() );
}

View File

@ -1,68 +0,0 @@
/*
* 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.annotation;
import java.util.List;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
import org.hibernate.jpamodelgen.Context;
import org.hibernate.jpamodelgen.model.MetaAttribute;
/**
* @author Max Andersen
* @author Hardy Ferentschik
* @author Emmanuel Bernard
*/
public class AnnotationEmbeddable extends AnnotationMetaEntity {
// Embeddables needs to be lazily initialized since the access type be determined by the class which is embedding
// the entity. This might not be known until annotations are processed.
// Also note, that if two different classes with different access types embed this entity the access type of the
// embeddable will be the one of the last embedding entity processed. The result is not determined (that's ok
// according to the spec)
private boolean initialized;
public AnnotationEmbeddable(TypeElement element, Context context) {
super( element, context, true );
}
public List<MetaAttribute> getMembers() {
if ( !initialized ) {
getContext().logMessage( Diagnostic.Kind.OTHER, "Entity " + getQualifiedName() + " was lazily initialised." );
init();
initialized = true;
}
return super.getMembers();
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append( "AnnotationEmbeddable" );
sb.append( "{element=" ).append( getElement() );
sb.append( ", members=" );
if ( initialized ) {
sb.append( getMembers() );
}
else {
sb.append( "[un-initalized]" );
}
sb.append( '}' );
return sb.toString();
}
}

View File

@ -26,6 +26,7 @@ import javax.lang.model.element.Modifier;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.ElementFilter;
import javax.tools.Diagnostic;
import org.hibernate.jpamodelgen.Context;
import org.hibernate.jpamodelgen.ImportContextImpl;
@ -38,7 +39,7 @@ import org.hibernate.jpamodelgen.util.Constants;
import org.hibernate.jpamodelgen.util.TypeUtils;
/**
* Class used to collect meta information about an annotated entity.
* Class used to collect meta information about an annotated type (entity, embeddable or mapped superclass).
*
* @author Max Andersen
* @author Hardy Ferentschik
@ -49,15 +50,24 @@ public class AnnotationMetaEntity implements MetaEntity {
private final ImportContext importContext;
private final TypeElement element;
private final Map<String, MetaAttribute> members;
private Context context;
private final Context context;
private AccessTypeInformation entityAccessTypeInfo;
public AnnotationMetaEntity(TypeElement element, Context context) {
this( element, context, false );
}
/**
* Whether the members of this type have already been initialized or not.
* <p>
* Embeddables and mapped super-classes need to be lazily initialized since the access type may be determined by
* the class which is embedding or sub-classing the entity or super-class. This might not be known until
* annotations are processed.
* <p>
* Also note, that if two different classes with different access types embed this entity or extend this mapped
* super-class, the access type of the embeddable/super-class will be the one of the last embedding/sub-classing
* entity processed. The result is not determined (that's ok according to the spec).
*/
private boolean initialized;
protected AnnotationMetaEntity(TypeElement element, Context context, boolean lazilyInitialised) {
public AnnotationMetaEntity(TypeElement element, Context context, boolean lazilyInitialised) {
this.element = element;
this.context = context;
this.members = new HashMap<String, MetaAttribute>();
@ -89,6 +99,10 @@ public class AnnotationMetaEntity implements MetaEntity {
}
public List<MetaAttribute> getMembers() {
if ( !initialized ) {
init();
}
return new ArrayList<MetaAttribute>( members.values() );
}
@ -134,6 +148,8 @@ public class AnnotationMetaEntity implements MetaEntity {
}
protected final void init() {
getContext().logMessage( Diagnostic.Kind.OTHER, "Initializing type " + getQualifiedName() + "." );
TypeUtils.determineAccessTypeForHierarchy( element, context );
entityAccessTypeInfo = context.getAccessTypeInfo( getQualifiedName() );
@ -142,6 +158,8 @@ public class AnnotationMetaEntity implements MetaEntity {
List<? extends Element> methodsOfClass = ElementFilter.methodsIn( element.getEnclosedElements() );
addPersistentMembers( methodsOfClass, AccessType.PROPERTY );
initialized = true;
}
private void addPersistentMembers(List<? extends Element> membersOfClass, AccessType membersKind) {

View File

@ -268,7 +268,7 @@ public class JpaDescriptorParser {
continue;
}
XmlMetaEntity metaEntity = new XmlMetaEmbeddable( embeddable, pkg, getXmlMappedType( fqcn ), context );
XmlMetaEntity metaEntity = new XmlMetaEntity( embeddable, pkg, getXmlMappedType( fqcn ), context );
if ( context.containsMetaEmbeddable( fqcn ) ) {
context.logMessage(
Diagnostic.Kind.WARNING,

View File

@ -1,68 +0,0 @@
/*
* 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.xml;
import java.util.List;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
import org.hibernate.jpamodelgen.Context;
import org.hibernate.jpamodelgen.model.MetaAttribute;
import org.hibernate.jpamodelgen.xml.jaxb.Embeddable;
/**
* @author Hardy Ferentschik
*/
public class XmlMetaEmbeddable extends XmlMetaEntity {
// Embeddables needs to be lazily initialized since the access type be determined by the class which is embedding
// the entity. This might not be known until annotations are processed.
// Also note, that if two different classes with different access types embed this entity the access type of the
// embeddable will be the one of the last embedding entity processed. The result is not determined (that's ok
// according to the spec)
private boolean initialized;
public XmlMetaEmbeddable(Embeddable embeddable, String packageName, TypeElement element, Context context) {
super( embeddable, packageName, element, context );
}
public List<MetaAttribute> getMembers() {
if ( !initialized ) {
context.logMessage( Diagnostic.Kind.OTHER, "Entity " + getQualifiedName() + " was lazily initialised." );
init();
initialized = true;
}
return members;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append( "XmlMetaEmbeddable" );
sb.append( "{accessTypeInfo=" ).append( accessTypeInfo );
sb.append( ", clazzName='" ).append( clazzName ).append( '\'' );
sb.append( ", members=" );
if ( initialized ) {
sb.append( members );
}
else {
sb.append( "[un-initalized]" );
}
sb.append( ", isMetaComplete=" ).append( isMetaComplete );
sb.append( '}' );
return sb.toString();
}
}

View File

@ -55,6 +55,8 @@ import org.hibernate.jpamodelgen.xml.jaxb.OneToMany;
import org.hibernate.jpamodelgen.xml.jaxb.OneToOne;
/**
* Collects XML-based meta information about an annotated type (entity, embeddable or mapped superclass).
*
* @author Hardy Ferentschik
*/
public class XmlMetaEntity implements MetaEntity {
@ -68,20 +70,33 @@ public class XmlMetaEntity implements MetaEntity {
COLLECTIONS.put( "java.util.Map", "javax.persistence.metamodel.MapAttribute" );
}
protected final String clazzName;
protected final String packageName;
protected final String defaultPackageName;
protected final ImportContext importContext;
protected final List<MetaAttribute> members = new ArrayList<MetaAttribute>();
protected final TypeElement element;
protected final Context context;
protected final boolean isMetaComplete;
private final String clazzName;
private final String packageName;
private final String defaultPackageName;
private final ImportContext importContext;
private final List<MetaAttribute> members = new ArrayList<MetaAttribute>();
private final TypeElement element;
private final Context context;
private final boolean isMetaComplete;
private Attributes attributes;
private EmbeddableAttributes embeddableAttributes;
protected AccessTypeInformation accessTypeInfo;
private AccessTypeInformation accessTypeInfo;
public XmlMetaEntity(Entity ormEntity, String defaultPackageName, TypeElement element, Context context) {
/**
* Whether the members of this type have already been initialized or not.
* <p>
* Embeddables and mapped super-classes need to be lazily initialized since the access type may be determined by
* the class which is embedding or sub-classing the entity or super-class. This might not be known until
* annotations are processed.
* <p>
* Also note, that if two different classes with different access types embed this entity or extend this mapped
* super-class, the access type of the embeddable/super-class will be the one of the last embedding/sub-classing
* entity processed. The result is not determined (that's ok according to the spec).
*/
private boolean initialized;
XmlMetaEntity(Entity ormEntity, String defaultPackageName, TypeElement element, Context context) {
this( ormEntity.getClazz(), defaultPackageName, element, context, ormEntity.isMetadataComplete() );
this.attributes = ormEntity.getAttributes();
this.embeddableAttributes = null;
@ -89,7 +104,7 @@ public class XmlMetaEntity implements MetaEntity {
init();
}
protected XmlMetaEntity(MappedSuperclass mappedSuperclass, String defaultPackageName, TypeElement element, Context context) {
XmlMetaEntity(MappedSuperclass mappedSuperclass, String defaultPackageName, TypeElement element, Context context) {
this(
mappedSuperclass.getClazz(),
defaultPackageName,
@ -99,11 +114,9 @@ public class XmlMetaEntity implements MetaEntity {
);
this.attributes = mappedSuperclass.getAttributes();
this.embeddableAttributes = null;
// entities can be directly initialised
init();
}
protected XmlMetaEntity(Embeddable embeddable, String defaultPackageName, TypeElement element, Context context) {
XmlMetaEntity(Embeddable embeddable, String defaultPackageName, TypeElement element, Context context) {
this( embeddable.getClazz(), defaultPackageName, element, context, embeddable.isMetadataComplete() );
this.attributes = null;
this.embeddableAttributes = embeddable.getAttributes();
@ -127,7 +140,9 @@ public class XmlMetaEntity implements MetaEntity {
this.isMetaComplete = initIsMetaComplete( metaComplete );
}
protected final void init() {
private final void init() {
context.logMessage( Diagnostic.Kind.OTHER, "Initializing type " + getQualifiedName() + "." );
this.accessTypeInfo = context.getAccessTypeInfo( getQualifiedName() );
if ( attributes != null ) {
parseAttributes( attributes );
@ -135,6 +150,8 @@ public class XmlMetaEntity implements MetaEntity {
else {
parseEmbeddableAttributes( embeddableAttributes );
}
initialized = true;
}
public String getSimpleName() {
@ -150,6 +167,10 @@ public class XmlMetaEntity implements MetaEntity {
}
public List<MetaAttribute> getMembers() {
if ( !initialized ) {
init();
}
return members;
}