org.hibernate.jpamodelgen.annotation.AnnotationMetaEntity Maven / Gradle / Ivy
Show all versions of hibernate-jpamodelgen-jakarta Show documentation
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or .
*/
package org.hibernate.jpamodelgen.annotation;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.ExecutableType;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.ElementFilter;
import javax.tools.Diagnostic;
import org.hibernate.jpamodelgen.Context;
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.AccessType;
import org.hibernate.jpamodelgen.util.AccessTypeInformation;
import org.hibernate.jpamodelgen.util.Constants;
import org.hibernate.jpamodelgen.util.TypeUtils;
/**
* Class used to collect meta information about an annotated type (entity, embeddable or mapped superclass).
*
* @author Max Andersen
* @author Hardy Ferentschik
* @author Emmanuel Bernard
*/
public class AnnotationMetaEntity implements MetaEntity {
private final ImportContext importContext;
private final TypeElement element;
private final Map members;
private final Context context;
private AccessTypeInformation entityAccessTypeInfo;
/**
* Whether the members of this type have already been initialized or not.
*
* 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.
*
* 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;
/**
* Another meta entity for the same type which should be merged lazily with this meta entity. Doing the merge
* lazily is required for embeddedables and mapped supertypes to only pull in those members matching the access
* type as configured via the embedding entity or subclass (also see METAGEN-85).
*/
private MetaEntity entityToMerge;
public AnnotationMetaEntity(TypeElement element, Context context, boolean lazilyInitialised) {
this.element = element;
this.context = context;
this.members = new HashMap();
this.importContext = new ImportContextImpl( getPackageName() );
if ( !lazilyInitialised ) {
init();
}
}
public AccessTypeInformation getEntityAccessTypeInfo() {
return entityAccessTypeInfo;
}
public final Context getContext() {
return context;
}
public final String getSimpleName() {
return element.getSimpleName().toString();
}
public final String getQualifiedName() {
return element.getQualifiedName().toString();
}
public final String getPackageName() {
PackageElement packageOf = context.getElementUtils().getPackageOf( element );
return context.getElementUtils().getName( packageOf.getQualifiedName() ).toString();
}
public List getMembers() {
if ( !initialized ) {
init();
if ( entityToMerge != null ) {
mergeInMembers( entityToMerge.getMembers() );
}
}
return new ArrayList( members.values() );
}
@Override
public boolean isMetaComplete() {
return false;
}
private void mergeInMembers(Collection attributes) {
for ( MetaAttribute attribute : attributes ) {
// propagate types to be imported
importType( attribute.getMetaType() );
importType( attribute.getTypeDeclaration() );
members.put( attribute.getPropertyName(), attribute );
}
}
public void mergeInMembers(MetaEntity other) {
// store the entity in order do the merge lazily in case of a non-initialized embeddedable or mapped superclass
if ( !initialized ) {
this.entityToMerge = other;
}
else {
mergeInMembers( other.getMembers() );
}
}
public final String generateImports() {
return importContext.generateImports();
}
public final String importType(String fqcn) {
return importContext.importType( fqcn );
}
public final String staticImport(String fqcn, String member) {
return importContext.staticImport( fqcn, member );
}
public final TypeElement getTypeElement() {
return element;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append( "AnnotationMetaEntity" );
sb.append( "{element=" ).append( element );
sb.append( ", members=" ).append( members );
sb.append( '}' );
return sb.toString();
}
protected TypeElement getElement() {
return element;
}
protected final void init() {
getContext().logMessage( Diagnostic.Kind.OTHER, "Initializing type " + getQualifiedName() + "." );
TypeUtils.determineAccessTypeForHierarchy( element, context );
entityAccessTypeInfo = context.getAccessTypeInfo( getQualifiedName() );
List extends Element> fieldsOfClass = ElementFilter.fieldsIn( element.getEnclosedElements() );
addPersistentMembers( fieldsOfClass, AccessType.FIELD );
List extends Element> methodsOfClass = ElementFilter.methodsIn( element.getEnclosedElements() );
List gettersAndSettersOfClass = new ArrayList<>();
for (Element rawMethodOfClass: methodsOfClass) {
if ( isGetterOrSetter( rawMethodOfClass)) {
gettersAndSettersOfClass.add(rawMethodOfClass);
}
}
addPersistentMembers( gettersAndSettersOfClass, AccessType.PROPERTY );
initialized = true;
}
/**
* Check if method respects Java Bean conventions for getter and setters.
*
* @param methodOfClass method element
*
* @return whether method respects Java Bean conventions.
*/
private boolean isGetterOrSetter(Element methodOfClass) {
ExecutableType methodType = (ExecutableType) methodOfClass.asType();
String methodSimpleName = methodOfClass.getSimpleName().toString();
List extends TypeMirror> methodParameterTypes = methodType.getParameterTypes();
TypeMirror returnType = methodType.getReturnType();
if(
methodSimpleName.startsWith("set") &&
methodParameterTypes.size() == 1 &&
"void".equalsIgnoreCase( returnType.toString() ) ) {
return true;
}
else if(
( methodSimpleName.startsWith("get") || methodSimpleName.startsWith("is") ) &&
methodParameterTypes.isEmpty() &&
!"void".equalsIgnoreCase( returnType.toString() ) ) {
return true;
}
else {
return false;
}
}
private void addPersistentMembers(List extends Element> membersOfClass, AccessType membersKind) {
for ( Element memberOfClass : membersOfClass ) {
AccessType forcedAccessType = TypeUtils.determineAnnotationSpecifiedAccessType( memberOfClass );
if ( entityAccessTypeInfo.getAccessType() != membersKind && forcedAccessType == null ) {
continue;
}
if ( TypeUtils.containsAnnotation( memberOfClass, Constants.TRANSIENT )
|| memberOfClass.getModifiers().contains( Modifier.TRANSIENT )
|| memberOfClass.getModifiers().contains( Modifier.STATIC ) ) {
continue;
}
MetaAttributeGenerationVisitor visitor = new MetaAttributeGenerationVisitor( this, context );
AnnotationMetaAttribute result = memberOfClass.asType().accept( visitor, memberOfClass );
if ( result != null ) {
members.put( result.getPropertyName(), result );
}
}
}
}