minor bug fixes and code cleanups to Metamodel Generator

This commit is contained in:
Gavin King 2023-07-06 14:08:27 +02:00
parent 84714ed585
commit 677b9848a2
26 changed files with 817 additions and 661 deletions

View File

@ -95,34 +95,34 @@ public final class ClassWriter {
* @return body content
*/
private static StringBuffer generateBody(Metamodel entity, Context context) {
StringWriter sw = new StringWriter();
final StringWriter sw = new StringWriter();
try ( PrintWriter pw = new PrintWriter(sw) ) {
if ( entity.getElement() instanceof TypeElement ) {
pw.println(writeStaticMetaModelAnnotation(entity));
pw.println( writeStaticMetaModelAnnotation(entity) );
}
if (context.addGeneratedAnnotation()) {
pw.println(writeGeneratedAnnotation(entity, context));
if ( context.addGeneratedAnnotation() ) {
pw.println( writeGeneratedAnnotation(entity, context) );
}
if (context.isAddSuppressWarningsAnnotation()) {
pw.println(writeSuppressWarnings());
if ( context.isAddSuppressWarningsAnnotation() ) {
pw.println( writeSuppressWarnings() );
}
printClassDeclaration(entity, pw, context);
printClassDeclaration( entity, pw, context );
pw.println();
List<MetaAttribute> members = entity.getMembers();
for (MetaAttribute metaMember : members) {
if (metaMember.hasTypedAttribute()) {
final List<MetaAttribute> members = entity.getMembers();
for ( MetaAttribute metaMember : members ) {
if ( metaMember.hasTypedAttribute() ) {
metaMember.getAttributeDeclarationString().lines()
.forEach(line -> pw.println(" " + line));
}
}
pw.println();
for (MetaAttribute metaMember : members) {
pw.println(" " + metaMember.getAttributeNameDeclarationString());
for ( MetaAttribute metaMember : members ) {
pw.println( " " + metaMember.getAttributeNameDeclarationString() );
}
pw.println();

View File

@ -212,23 +212,19 @@ public final class Context {
}
public void logMessage(Diagnostic.Kind type, String message) {
if ( !logDebug && type.equals( Diagnostic.Kind.OTHER ) ) {
return;
if ( logDebug || type != Diagnostic.Kind.OTHER ) {
pe.getMessager().printMessage( type, message );
}
pe.getMessager().printMessage( type, message );
}
public boolean isFullyXmlConfigured() {
return fullyXmlConfigured != null && fullyXmlConfigured.booleanValue();
return fullyXmlConfigured != null && fullyXmlConfigured;
}
public void mappingDocumentFullyXmlConfigured(boolean fullyXmlConfigured) {
if ( this.fullyXmlConfigured == null ) {
this.fullyXmlConfigured = fullyXmlConfigured;
}
else {
this.fullyXmlConfigured = this.fullyXmlConfigured && fullyXmlConfigured;
}
this.fullyXmlConfigured = this.fullyXmlConfigured == null
? fullyXmlConfigured
: this.fullyXmlConfigured && fullyXmlConfigured;
}
public AccessType getPersistenceUnitDefaultAccessType() {

View File

@ -8,6 +8,7 @@ package org.hibernate.jpamodelgen;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.TreeSet;
@ -21,13 +22,13 @@ import org.hibernate.jpamodelgen.model.ImportContext;
*/
public class ImportContextImpl implements ImportContext {
private Set<String> imports = new TreeSet<String>();
private Set<String> staticImports = new TreeSet<String>();
private Map<String, String> simpleNames = new HashMap<String, String>();
private final Set<String> imports = new TreeSet<>();
private final Set<String> staticImports = new TreeSet<>();
private final Map<String, String> simpleNames = new HashMap<>();
private String basePackage = "";
private static final Map<String, String> PRIMITIVES = new HashMap<String, String>();
private static final Map<String, String> PRIMITIVES = new HashMap<>();
static {
PRIMITIVES.put( "char", "Character" );
@ -56,61 +57,79 @@ public class ImportContextImpl implements ImportContext {
* {@code java.util.Collection<org.marvel.Hulk>} imports {@code java.util.Collection} and returns {@code Collection}
* {@code org.marvel.Hulk[]} imports {@code org.marvel.Hulk} and returns {@code Hulk}
*
* @param fqcn Fully qualified class name
* @param typeExpression A type expression
*
* @return import string
*/
public String importType(String fqcn) {
String result = fqcn;
public String importType(String typeExpression) {
String result = typeExpression;
//if(fqcn==null) return "/** (null) **/";
String additionalTypePart = null;
if ( fqcn.indexOf( '<' ) >= 0 ) {
additionalTypePart = result.substring( fqcn.indexOf( '<' ) );
result = result.substring( 0, fqcn.indexOf( '<' ) );
fqcn = result;
}
else if ( fqcn.indexOf( '[' ) >= 0 ) {
additionalTypePart = result.substring( fqcn.indexOf( '[' ) );
result = result.substring( 0, fqcn.indexOf( '[' ) );
fqcn = result;
}
else if ( fqcn.endsWith( "..." ) ) {
additionalTypePart = "...";
result = result.substring( 0, fqcn.indexOf( "..." ) );
fqcn = result;
// strip off type annotations and '? super' or '? extends'
String preamble = "";
if ( result.startsWith("@") || result.startsWith("?") ) {
int index = result.lastIndexOf(' ');
if ( index > 0 ) {
preamble = result.substring( 0, index+1 );
result = result.substring( index+1 );
}
}
String pureFqcn = fqcn.replace( '$', '.' );
String appendices = "";
if ( result.indexOf( '<' ) >= 0 ) {
int startIndex = result.indexOf('<');
int endIndex = result.lastIndexOf('>');
appendices = '<' + importTypes( result.substring( startIndex + 1, endIndex ) ) + '>'
+ result.substring( endIndex + 1 );
result = result.substring( 0, startIndex );
}
else if ( result.indexOf( '[' ) >= 0 ) {
int index = result.indexOf('[');
appendices = result.substring( index );
result = result.substring( 0, index );
}
else if ( result.endsWith( "..." ) ) {
appendices = "...";
int index = result.indexOf("...");
result = result.substring( 0, index );
}
return ( preamble + unqualifyName( result ) + appendices )
.replace( '$', '.' );
}
private String unqualifyName(String qualifiedName) {
final String sourceQualifiedName = qualifiedName.replace( '$', '.' );
final String simpleName = unqualify( qualifiedName );
boolean canBeSimple;
String simpleName = unqualify( fqcn );
if ( simpleNames.containsKey( simpleName ) ) {
String existingFqcn = simpleNames.get( simpleName );
canBeSimple = existingFqcn.equals( pureFqcn );
String existing = simpleNames.get( simpleName );
canBeSimple = existing.equals( sourceQualifiedName );
}
else {
canBeSimple = true;
simpleNames.put( simpleName, pureFqcn );
imports.add( pureFqcn );
simpleNames.put( simpleName, sourceQualifiedName );
imports.add( sourceQualifiedName );
}
if ( inSamePackage( fqcn ) || ( imports.contains( pureFqcn ) && canBeSimple ) ) {
result = unqualify( result );
if ( inSamePackage( qualifiedName ) || inJavaLang( qualifiedName )
|| canBeSimple && imports.contains( sourceQualifiedName ) ) {
return unqualify( qualifiedName );
}
else if ( inJavaLang( fqcn ) ) {
result = result.substring( "java.lang.".length() );
else {
return qualifiedName;
}
}
if ( additionalTypePart != null ) {
result = result + additionalTypePart;
private String importTypes(String originalArgList) {
String[] args = originalArgList.split(",");
StringBuilder argList = new StringBuilder();
for ( String arg : args ) {
if ( argList.length() > 0 ) {
argList.append(',');
}
argList.append( importType( arg ) );
}
result = result.replace( '$', '.' );
return result;
return argList.toString();
}
public String staticImport(String fqcn, String member) {
@ -135,9 +154,7 @@ public class ImportContextImpl implements ImportContext {
}
private boolean inSamePackage(String className) {
String other = qualifier( className );
return other == basePackage
|| ( other != null && other.equals( basePackage ) );
return Objects.equals( qualifier( className ), basePackage );
}
private boolean inJavaLang(String className) {

View File

@ -8,7 +8,6 @@ package org.hibernate.jpamodelgen;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.ProcessingEnvironment;
@ -26,7 +25,7 @@ import javax.lang.model.type.ExecutableType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.ElementFilter;
import javax.lang.model.util.SimpleTypeVisitor6;
import javax.lang.model.util.SimpleTypeVisitor8;
import javax.tools.Diagnostic;
import org.hibernate.jpamodelgen.annotation.AnnotationMetaEntity;
@ -42,6 +41,7 @@ import org.checkerframework.checker.nullness.qual.Nullable;
import static org.hibernate.jpamodelgen.util.Constants.HQL;
import static org.hibernate.jpamodelgen.util.Constants.SQL;
import static org.hibernate.jpamodelgen.util.TypeUtils.containsAnnotation;
import static org.hibernate.jpamodelgen.util.TypeUtils.isAnnotationMirrorOfType;
/**
* Main annotation processor.
@ -149,8 +149,19 @@ public class JPAMetaModelEntityProcessor extends AbstractProcessor {
return ALLOW_OTHER_PROCESSORS_TO_CLAIM_ANNOTATIONS;
}
Set<? extends Element> elements = roundEnvironment.getRootElements();
for ( Element element : elements ) {
try {
processClasses( roundEnvironment );
createMetaModelClasses();
}
catch (Exception e) {
context.logMessage( Diagnostic.Kind.ERROR, "Error generating JPA metamodel:" + e.getMessage() );
}
return ALLOW_OTHER_PROCESSORS_TO_CLAIM_ANNOTATIONS;
}
private void processClasses(RoundEnvironment roundEnvironment) {
for ( Element element : roundEnvironment.getRootElements() ) {
if ( isJPAEntity( element ) ) {
context.logMessage( Diagnostic.Kind.OTHER, "Processing annotated class " + element.toString() );
handleRootElementAnnotationMirrors( element );
@ -162,7 +173,7 @@ public class JPAMetaModelEntityProcessor extends AbstractProcessor {
else if ( element instanceof TypeElement ) {
for ( Element enclosedElement : element.getEnclosedElements() ) {
if ( containsAnnotation( enclosedElement, HQL, SQL ) ) {
AnnotationMetaEntity metaEntity =
final AnnotationMetaEntity metaEntity =
AnnotationMetaEntity.create( (TypeElement) element, context, false );
context.addMetaAuxiliary( metaEntity.getQualifiedName(), metaEntity );
break;
@ -170,9 +181,6 @@ public class JPAMetaModelEntityProcessor extends AbstractProcessor {
}
}
}
createMetaModelClasses();
return ALLOW_OTHER_PROCESSORS_TO_CLAIM_ANNOTATIONS;
}
private void createMetaModelClasses() {
@ -197,9 +205,9 @@ public class JPAMetaModelEntityProcessor extends AbstractProcessor {
// we cannot process the delayed entities in any order. There might be dependencies between them.
// we need to process the top level entities first
Collection<Metamodel> toProcessEntities = context.getMetaEmbeddables();
final Collection<Metamodel> toProcessEntities = context.getMetaEmbeddables();
while ( !toProcessEntities.isEmpty() ) {
Set<Metamodel> processedEntities = new HashSet<Metamodel>();
final Set<Metamodel> processedEntities = new HashSet<>();
int toProcessCountBeforeLoop = toProcessEntities.size();
for ( Metamodel entity : toProcessEntities ) {
// see METAGEN-36
@ -227,7 +235,7 @@ public class JPAMetaModelEntityProcessor extends AbstractProcessor {
}
private boolean modelGenerationNeedsToBeDeferred(Collection<Metamodel> entities, Metamodel containedEntity) {
Element element = containedEntity.getElement();
final Element element = containedEntity.getElement();
if ( element instanceof TypeElement ) {
ContainsAttributeTypeVisitor visitor = new ContainsAttributeTypeVisitor( (TypeElement) element, context );
for ( Metamodel entity : entities ) {
@ -236,22 +244,18 @@ public class JPAMetaModelEntityProcessor extends AbstractProcessor {
}
for ( Element subElement : ElementFilter.fieldsIn( entity.getElement().getEnclosedElements() ) ) {
TypeMirror mirror = subElement.asType();
if ( !TypeKind.DECLARED.equals( mirror.getKind() ) ) {
continue;
}
boolean contains = mirror.accept( visitor, subElement );
if ( contains ) {
return true;
if ( TypeKind.DECLARED == mirror.getKind() ) {
if ( mirror.accept( visitor, subElement ) ) {
return true;
}
}
}
for ( Element subElement : ElementFilter.methodsIn( entity.getElement().getEnclosedElements() ) ) {
TypeMirror mirror = subElement.asType();
if ( !TypeKind.DECLARED.equals( mirror.getKind() ) ) {
continue;
}
boolean contains = mirror.accept( visitor, subElement );
if ( contains ) {
return true;
if ( TypeKind.DECLARED == mirror.getKind() ) {
if ( mirror.accept( visitor, subElement ) ) {
return true;
}
}
}
}
@ -291,33 +295,30 @@ public class JPAMetaModelEntityProcessor extends AbstractProcessor {
}
private void handleRootElementAnnotationMirrors(final Element element) {
List<? extends AnnotationMirror> annotationMirrors = element.getAnnotationMirrors();
for ( AnnotationMirror mirror : annotationMirrors ) {
if ( !element.getKind().isClass() || ElementKind.ENUM.equals( element.getKind() ) ) {
continue;
}
for ( AnnotationMirror mirror : element.getAnnotationMirrors() ) {
if ( element.getKind() == ElementKind.CLASS ) {
final String fqn = ( (TypeElement) element ).getQualifiedName().toString();
final Metamodel alreadyExistingMetaEntity = tryGettingExistingEntityFromContext( mirror, fqn );
if ( alreadyExistingMetaEntity != null && alreadyExistingMetaEntity.isMetaComplete() ) {
String msg = "Skipping processing of annotations for " + fqn + " since xml configuration is metadata complete.";
context.logMessage( Diagnostic.Kind.OTHER, msg );
}
else {
boolean requiresLazyMemberInitialization = false;
AnnotationMetaEntity metaEntity;
if ( containsAnnotation( element, Constants.EMBEDDABLE ) ||
containsAnnotation( element, Constants.MAPPED_SUPERCLASS ) ) {
requiresLazyMemberInitialization = true;
}
String fqn = ( (TypeElement) element ).getQualifiedName().toString();
Metamodel alreadyExistingMetaEntity = tryGettingExistingEntityFromContext( mirror, fqn );
if ( alreadyExistingMetaEntity != null && alreadyExistingMetaEntity.isMetaComplete() ) {
String msg = "Skipping processing of annotations for " + fqn + " since xml configuration is metadata complete.";
context.logMessage( Diagnostic.Kind.OTHER, msg );
continue;
}
metaEntity = AnnotationMetaEntity.create( (TypeElement) element, context, requiresLazyMemberInitialization );
boolean requiresLazyMemberInitialization = false;
AnnotationMetaEntity metaEntity;
if ( containsAnnotation( element, Constants.EMBEDDABLE ) ||
containsAnnotation( element, Constants.MAPPED_SUPERCLASS ) ) {
requiresLazyMemberInitialization = true;
if ( alreadyExistingMetaEntity != null ) {
metaEntity.mergeInMembers( alreadyExistingMetaEntity );
}
addMetaEntityToContext( mirror, metaEntity );
}
}
metaEntity = AnnotationMetaEntity.create( (TypeElement) element, context, requiresLazyMemberInitialization );
if ( alreadyExistingMetaEntity != null ) {
metaEntity.mergeInMembers( alreadyExistingMetaEntity );
}
addMetaEntityToContext( mirror, metaEntity );
}
}
@ -337,30 +338,30 @@ public class JPAMetaModelEntityProcessor extends AbstractProcessor {
private @Nullable Metamodel tryGettingExistingEntityFromContext(AnnotationMirror mirror, String fqn) {
Metamodel alreadyExistingMetaEntity = null;
if ( TypeUtils.isAnnotationMirrorOfType( mirror, Constants.ENTITY )
|| TypeUtils.isAnnotationMirrorOfType( mirror, Constants.MAPPED_SUPERCLASS ) ) {
if ( isAnnotationMirrorOfType( mirror, Constants.ENTITY )
|| isAnnotationMirrorOfType( mirror, Constants.MAPPED_SUPERCLASS ) ) {
alreadyExistingMetaEntity = context.getMetaEntity( fqn );
}
else if ( TypeUtils.isAnnotationMirrorOfType( mirror, Constants.EMBEDDABLE ) ) {
else if ( isAnnotationMirrorOfType( mirror, Constants.EMBEDDABLE ) ) {
alreadyExistingMetaEntity = context.getMetaEmbeddable( fqn );
}
return alreadyExistingMetaEntity;
}
private void addMetaEntityToContext(AnnotationMirror mirror, AnnotationMetaEntity metaEntity) {
if ( TypeUtils.isAnnotationMirrorOfType( mirror, Constants.ENTITY ) ) {
if ( isAnnotationMirrorOfType( mirror, Constants.ENTITY ) ) {
context.addMetaEntity( metaEntity.getQualifiedName(), metaEntity );
}
else if ( TypeUtils.isAnnotationMirrorOfType( mirror, Constants.MAPPED_SUPERCLASS ) ) {
else if ( isAnnotationMirrorOfType( mirror, Constants.MAPPED_SUPERCLASS ) ) {
context.addMetaEntity( metaEntity.getQualifiedName(), metaEntity );
}
else if ( TypeUtils.isAnnotationMirrorOfType( mirror, Constants.EMBEDDABLE ) ) {
else if ( isAnnotationMirrorOfType( mirror, Constants.EMBEDDABLE ) ) {
context.addMetaEmbeddable( metaEntity.getQualifiedName(), metaEntity );
}
}
static class ContainsAttributeTypeVisitor extends SimpleTypeVisitor6<Boolean, Element> {
static class ContainsAttributeTypeVisitor extends SimpleTypeVisitor8<Boolean, Element> {
private Context context;
private TypeElement type;

View File

@ -9,7 +9,6 @@ package org.hibernate.jpamodelgen.annotation;
import org.hibernate.jpamodelgen.model.MetaAttribute;
import org.hibernate.jpamodelgen.model.Metamodel;
import org.hibernate.jpamodelgen.util.Constants;
import org.hibernate.jpamodelgen.util.TypeUtils;
import org.hibernate.jpamodelgen.validation.ProcessorSessionFactory;
import org.hibernate.jpamodelgen.validation.Validation;
@ -17,6 +16,8 @@ import javax.lang.model.element.AnnotationMirror;
import java.util.List;
import static java.util.Collections.emptySet;
import static org.hibernate.jpamodelgen.util.TypeUtils.containsAnnotation;
import static org.hibernate.jpamodelgen.util.TypeUtils.getAnnotationMirror;
public abstract class AnnotationMeta implements Metamodel {
@ -41,47 +42,45 @@ public abstract class AnnotationMeta implements Metamodel {
}
void checkNamedQueries() {
if ( TypeUtils.containsAnnotation( getElement(), Constants.CHECK_HQL )
|| TypeUtils.containsAnnotation( getElement().getEnclosingElement(), Constants.CHECK_HQL ) ) {
checkNamedQueriesForAnnotation( Constants.NAMED_QUERY );
checkNamedQueriesForRepeatableAnnotation( Constants.NAMED_QUERIES );
checkNamedQueriesForAnnotation( Constants.HIB_NAMED_QUERY );
checkNamedQueriesForRepeatableAnnotation( Constants.HIB_NAMED_QUERIES );
}
boolean checkHql = containsAnnotation( getElement(), Constants.CHECK_HQL )
|| containsAnnotation( getElement().getEnclosingElement(), Constants.CHECK_HQL );
checkNamedQueriesForAnnotation( Constants.NAMED_QUERY, checkHql );
checkNamedQueriesForRepeatableAnnotation( Constants.NAMED_QUERIES, checkHql );
checkNamedQueriesForAnnotation( Constants.HIB_NAMED_QUERY, checkHql );
checkNamedQueriesForRepeatableAnnotation( Constants.HIB_NAMED_QUERIES, checkHql );
}
private void checkNamedQueriesForAnnotation(String annotationName) {
AnnotationMirror mirror = TypeUtils.getAnnotationMirror(getElement(), annotationName);
private void checkNamedQueriesForAnnotation(String annotationName, boolean checkHql) {
final AnnotationMirror mirror = getAnnotationMirror( getElement(), annotationName );
if ( mirror != null ) {
checkNamedQueriesForMirror( mirror );
checkNamedQueriesForMirror( mirror, checkHql );
}
}
private void checkNamedQueriesForRepeatableAnnotation(String annotationName) {
AnnotationMirror mirror = TypeUtils.getAnnotationMirror(getElement(), annotationName);
private void checkNamedQueriesForRepeatableAnnotation(String annotationName, boolean checkHql) {
final AnnotationMirror mirror = getAnnotationMirror( getElement(), annotationName );
if ( mirror != null ) {
mirror.getElementValues().forEach((key, value) -> {
if ( key.getSimpleName().contentEquals("value") ) {
List<? extends AnnotationMirror> values =
(List<? extends AnnotationMirror>) value.getValue();
for ( AnnotationMirror annotationMirror : values ) {
checkNamedQueriesForMirror( annotationMirror );
checkNamedQueriesForMirror( annotationMirror, checkHql );
}
}
});
}
}
private void checkNamedQueriesForMirror(AnnotationMirror mirror) {
private void checkNamedQueriesForMirror(AnnotationMirror mirror, boolean checkHql) {
mirror.getElementValues().forEach((key, value) -> {
if ( key.getSimpleName().contentEquals("query") ) {
String hql = value.getValue().toString();
final String hql = value.getValue().toString();
Validation.validate(
hql,
false,
false, checkHql,
emptySet(), emptySet(),
new ErrorHandler( getElement(), mirror, hql, getContext()),
new ErrorHandler( getElement(), mirror, hql, getContext() ),
ProcessorSessionFactory.create( getContext().getProcessingEnvironment() )
);
}
@ -89,7 +88,7 @@ public abstract class AnnotationMeta implements Metamodel {
}
private void addAuxiliaryMembersForRepeatableAnnotation(String annotationName, String prefix) {
AnnotationMirror mirror = TypeUtils.getAnnotationMirror( getElement(), annotationName );
final AnnotationMirror mirror = getAnnotationMirror( getElement(), annotationName );
if ( mirror != null ) {
mirror.getElementValues().forEach((key, value) -> {
if ( key.getSimpleName().contentEquals("value") ) {
@ -104,7 +103,7 @@ public abstract class AnnotationMeta implements Metamodel {
}
private void addAuxiliaryMembersForAnnotation(String annotationName, String prefix) {
AnnotationMirror mirror = TypeUtils.getAnnotationMirror( getElement(), annotationName);
final AnnotationMirror mirror = getAnnotationMirror( getElement(), annotationName );
if ( mirror != null ) {
addAuxiliaryMembersForMirror( mirror, prefix );
}
@ -113,8 +112,9 @@ public abstract class AnnotationMeta implements Metamodel {
private void addAuxiliaryMembersForMirror(AnnotationMirror mirror, String prefix) {
mirror.getElementValues().forEach((key, value) -> {
if ( key.getSimpleName().contentEquals("name") ) {
String name = value.getValue().toString();
putMember( prefix + name, new NameMetaAttribute( this, name, prefix ) );
final String name = value.getValue().toString();
putMember( prefix + name,
new NameMetaAttribute( this, name, prefix ) );
}
});
}

View File

@ -24,8 +24,8 @@ import org.hibernate.jpamodelgen.util.StringUtil;
*/
public abstract class AnnotationMetaAttribute implements MetaAttribute {
private final Element element;
private final AnnotationMetaEntity parent;
final Element element;
final AnnotationMetaEntity parent;
private final String type;
public AnnotationMetaAttribute(AnnotationMetaEntity parent, Element element, String type) {
@ -43,10 +43,10 @@ public abstract class AnnotationMetaAttribute implements MetaAttribute {
public String getAttributeDeclarationString() {
return new StringBuilder()
.append("\n/**\n * @see ")
.append(parent.getQualifiedName())
.append("#")
.append(element.getSimpleName())
.append("\n **/\n")
.append( parent.getQualifiedName() )
.append( "#")
.append( element.getSimpleName() )
.append( "\n **/\n" )
.append( "public static volatile " )
.append( parent.importType( getMetaType() ) )
.append( "<" )
@ -61,7 +61,8 @@ public abstract class AnnotationMetaAttribute implements MetaAttribute {
@Override
public String getAttributeNameDeclarationString(){
return new StringBuilder().append("public static final ")
return new StringBuilder()
.append("public static final ")
.append(parent.importType(String.class.getName()))
.append(" ")
.append(StringUtil.getUpperUnderscoreCaseFromLowerCamelCase(getPropertyName()))
@ -109,11 +110,10 @@ public abstract class AnnotationMetaAttribute implements MetaAttribute {
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append( "AnnotationMetaAttribute" );
sb.append( "{element=" ).append( element );
sb.append( ", type='" ).append( type ).append( '\'' );
sb.append( '}' );
return sb.toString();
return new StringBuilder()
.append( "AnnotationMetaAttribute" )
.append( "{element=" ).append( element )
.append( ", type='" ).append( type ).append( '\'' )
.append( '}' ).toString();
}
}

View File

@ -15,10 +15,12 @@ import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
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.DeclaredType;
import javax.lang.model.type.ExecutableType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.ElementFilter;
import javax.tools.Diagnostic;
@ -32,8 +34,6 @@ import org.hibernate.jpamodelgen.model.Metamodel;
import org.hibernate.jpamodelgen.util.AccessType;
import org.hibernate.jpamodelgen.util.AccessTypeInformation;
import org.hibernate.jpamodelgen.util.Constants;
import org.hibernate.jpamodelgen.util.NullnessUtil;
import org.hibernate.jpamodelgen.util.TypeUtils;
import org.hibernate.jpamodelgen.validation.ProcessorSessionFactory;
import org.hibernate.jpamodelgen.validation.Validation;
@ -41,7 +41,9 @@ import static java.util.Collections.emptySet;
import static java.util.stream.Collectors.toList;
import static org.hibernate.jpamodelgen.annotation.QueryMethod.isOrderParam;
import static org.hibernate.jpamodelgen.annotation.QueryMethod.isPageParam;
import static org.hibernate.jpamodelgen.util.NullnessUtil.castNonNull;
import static org.hibernate.jpamodelgen.util.TypeUtils.containsAnnotation;
import static org.hibernate.jpamodelgen.util.TypeUtils.determineAccessTypeForHierarchy;
import static org.hibernate.jpamodelgen.util.TypeUtils.determineAnnotationSpecifiedAccessType;
import static org.hibernate.jpamodelgen.util.TypeUtils.getAnnotationMirror;
import static org.hibernate.jpamodelgen.util.TypeUtils.getAnnotationValue;
@ -191,28 +193,26 @@ public class AnnotationMetaEntity extends AnnotationMeta {
@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();
return new StringBuilder()
.append( "AnnotationMetaEntity" )
.append( "{element=" )
.append( element )
.append( ", members=" )
.append( members )
.append( '}' )
.toString();
}
protected final void init() {
getContext().logMessage( Diagnostic.Kind.OTHER, "Initializing type " + getQualifiedName() + "." );
TypeUtils.determineAccessTypeForHierarchy( element, context );
AccessTypeInformation accessTypeInfo = context.getAccessTypeInfo( getQualifiedName() );
entityAccessTypeInfo = NullnessUtil.castNonNull( accessTypeInfo );
List<? extends Element> fieldsOfClass = ElementFilter.fieldsIn( element.getEnclosedElements() );
addPersistentMembers( fieldsOfClass, AccessType.FIELD );
List<? extends Element> methodsOfClass = ElementFilter.methodsIn( element.getEnclosedElements() );
List<Element> gettersAndSettersOfClass = new ArrayList<>();
List<ExecutableElement> queryMethods = new ArrayList<>();
determineAccessTypeForHierarchy( element, context );
entityAccessTypeInfo = castNonNull( context.getAccessTypeInfo( getQualifiedName() ) );
final List<? extends Element> fieldsOfClass = ElementFilter.fieldsIn( element.getEnclosedElements() );
final List<? extends Element> methodsOfClass = ElementFilter.methodsIn( element.getEnclosedElements() );
final List<Element> gettersAndSettersOfClass = new ArrayList<>();
final List<ExecutableElement> queryMethods = new ArrayList<>();
for ( Element rawMethodOfClass: methodsOfClass ) {
if ( isGetterOrSetter( rawMethodOfClass ) ) {
gettersAndSettersOfClass.add( rawMethodOfClass );
@ -222,6 +222,8 @@ public class AnnotationMetaEntity extends AnnotationMeta {
queryMethods.add( (ExecutableElement) rawMethodOfClass );
}
}
addPersistentMembers( fieldsOfClass, AccessType.FIELD );
addPersistentMembers( gettersAndSettersOfClass, AccessType.PROPERTY );
addAuxiliaryMembers();
@ -241,48 +243,48 @@ public class AnnotationMetaEntity extends AnnotationMeta {
* @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();
return isSetter(methodSimpleName, methodParameterTypes, returnType)
|| isGetter(methodSimpleName, methodParameterTypes, returnType);
final ExecutableType methodType = (ExecutableType) methodOfClass.asType();
final Name methodSimpleName = methodOfClass.getSimpleName();
final List<? extends TypeMirror> methodParameterTypes = methodType.getParameterTypes();
final TypeMirror returnType = methodType.getReturnType();
return isSetter( methodSimpleName, methodParameterTypes, returnType )
|| isGetter( methodSimpleName, methodParameterTypes, returnType );
}
private static boolean isGetter(String methodSimpleName, List<? extends TypeMirror> methodParameterTypes, TypeMirror returnType) {
return (methodSimpleName.startsWith("get") || methodSimpleName.startsWith("is"))
private static boolean isGetter(Name methodSimpleName, List<? extends TypeMirror> methodParameterTypes, TypeMirror returnType) {
return ( methodSimpleName.subSequence(0,3).toString().equals("get")
|| methodSimpleName.subSequence(0,2).toString().equals("is") )
&& methodParameterTypes.isEmpty()
&& !"void".equalsIgnoreCase(returnType.toString());
&& returnType.getKind() != TypeKind.VOID;
}
private static boolean isSetter(String methodSimpleName, List<? extends TypeMirror> methodParameterTypes, TypeMirror returnType) {
return methodSimpleName.startsWith("set")
private static boolean isSetter(Name methodSimpleName, List<? extends TypeMirror> methodParameterTypes, TypeMirror returnType) {
return methodSimpleName.subSequence(0,3).toString().equals("set")
&& methodParameterTypes.size() == 1
&& "void".equalsIgnoreCase(returnType.toString());
&& returnType.getKind() != TypeKind.VOID;
}
private void addPersistentMembers(List<? extends Element> membersOfClass, AccessType membersKind) {
for ( Element memberOfClass : membersOfClass ) {
AccessType forcedAccessType = determineAnnotationSpecifiedAccessType( memberOfClass );
if ( entityAccessTypeInfo.getAccessType() != membersKind && forcedAccessType == null ) {
continue;
}
if ( 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 );
if ( isPersistent( memberOfClass, membersKind ) ) {
final AnnotationMetaAttribute result =
memberOfClass.asType()
.accept( new MetaAttributeGenerationVisitor(this, context ), memberOfClass );
if ( result != null ) {
members.put( result.getPropertyName(), result );
}
}
}
}
private boolean isPersistent(Element memberOfClass, AccessType membersKind) {
return ( entityAccessTypeInfo.getAccessType() == membersKind
|| determineAnnotationSpecifiedAccessType( memberOfClass ) != null )
&& !containsAnnotation( memberOfClass, Constants.TRANSIENT )
&& !memberOfClass.getModifiers().contains( Modifier.TRANSIENT )
&& !memberOfClass.getModifiers().contains( Modifier.STATIC );
}
private void addQueryMethods(List<ExecutableElement> queryMethods) {
for ( ExecutableElement method : queryMethods) {
addQueryMethod( method );
@ -317,11 +319,15 @@ public class AnnotationMetaEntity extends AnnotationMeta {
addQueryMethod( method, methodName, returnTypeName, containerTypeName );
}
else {
context.message( method, "incorrect return type '" + containerTypeName + "'", Diagnostic.Kind.ERROR );
context.message( method,
"incorrect return type '" + containerTypeName + "'",
Diagnostic.Kind.ERROR );
}
}
else {
context.message( method, "incorrect return type '" + declaredType + "'", Diagnostic.Kind.ERROR );
context.message( method,
"incorrect return type '" + declaredType + "'",
Diagnostic.Kind.ERROR );
}
}
}
@ -392,9 +398,9 @@ public class AnnotationMetaEntity extends AnnotationMeta {
// checkHqlSyntax( method, mirror, hql );
Validation.validate(
hql,
false,
false, true,
emptySet(), emptySet(),
new ErrorHandler( method, mirror, hql, context),
new ErrorHandler( method, mirror, hql, context ),
ProcessorSessionFactory.create( context.getProcessingEnvironment() )
);
}
@ -404,15 +410,21 @@ public class AnnotationMetaEntity extends AnnotationMeta {
private void checkParameters(ExecutableElement method, List<String> paramNames, List<String> paramTypes, AnnotationMirror mirror, String hql) {
for (int i = 1; i <= paramNames.size(); i++) {
final String param = paramNames.get(i-1);
final String ptype = paramTypes.get(i-1);
if ( !hql.contains(":" + param) && !hql.contains("?" + i)
&& !isPageParam(ptype) && !isOrderParam(ptype)) {
final String type = paramTypes.get(i-1);
if ( parameterIsMissing( hql, i, param, type ) ) {
context.message( method, mirror, "missing query parameter for '" + param
+ "' (no parameter named :" + param + " or ?" + i + ")", Diagnostic.Kind.ERROR );
}
}
}
private static boolean parameterIsMissing(String hql, int i, String param, String type) {
return !hql.contains(":" + param)
&& !hql.contains("?" + i)
&& !isPageParam(type)
&& !isOrderParam(type);
}
// private void checkHqlSyntax(ExecutableElement method, AnnotationMirror mirror, String queryString) {
// final ANTLRErrorListener errorListener = new ErrorHandler( method, mirror, queryString );
// final HqlLexer hqlLexer = HqlParseTreeBuilder.INSTANCE.buildHqlLexer( queryString );

View File

@ -25,10 +25,23 @@ public class AnnotationMetaMap extends AnnotationMetaCollection {
@Override
public String getAttributeDeclarationString() {
return "public static volatile " + getHostingEntity().importType( getMetaType() )
+ "<" + getHostingEntity().importType( getHostingEntity().getQualifiedName() )
+ ", " + getHostingEntity().importType( keyType ) + ", "
+ getHostingEntity().importType( getTypeDeclaration() ) + "> "
+ getPropertyName() + ";";
return new StringBuilder()
.append("\n/**\n * @see ")
.append( parent.getQualifiedName() )
.append("#")
.append( element.getSimpleName() )
.append("\n **/\n")
.append("public static volatile ")
.append( parent.importType( getMetaType() ) )
.append("<")
.append( parent.importType( parent.getQualifiedName() ) )
.append(", ")
.append( parent.importType(keyType) )
.append(", ")
.append( parent.importType( getTypeDeclaration() ) )
.append("> ")
.append( getPropertyName() )
.append(";")
.toString();
}
}

View File

@ -106,11 +106,12 @@ public class AnnotationMetaPackage extends AnnotationMeta {
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append( "AnnotationMetaPackage" );
sb.append( "{element=" ).append( element );
sb.append( '}' );
return sb.toString();
return new StringBuilder()
.append( "AnnotationMetaPackage" )
.append( "{element=" )
.append( element )
.append( '}' )
.toString();
}
protected final void init() {

View File

@ -18,23 +18,33 @@ 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.lang.model.util.SimpleTypeVisitor8;
import javax.tools.Diagnostic;
import org.hibernate.jpamodelgen.Context;
import org.hibernate.jpamodelgen.util.AccessType;
import org.hibernate.jpamodelgen.util.AccessTypeInformation;
import org.hibernate.jpamodelgen.util.Constants;
import org.hibernate.jpamodelgen.util.NullnessUtil;
import org.hibernate.jpamodelgen.util.StringUtil;
import org.hibernate.jpamodelgen.util.TypeUtils;
import org.checkerframework.checker.nullness.qual.Nullable;
import static org.hibernate.jpamodelgen.util.NullnessUtil.castNonNull;
import static org.hibernate.jpamodelgen.util.StringUtil.isProperty;
import static org.hibernate.jpamodelgen.util.TypeUtils.DEFAULT_ANNOTATION_PARAMETER_NAME;
import static org.hibernate.jpamodelgen.util.TypeUtils.containsAnnotation;
import static org.hibernate.jpamodelgen.util.TypeUtils.determineAnnotationSpecifiedAccessType;
import static org.hibernate.jpamodelgen.util.TypeUtils.extractClosestRealTypeAsString;
import static org.hibernate.jpamodelgen.util.TypeUtils.getAnnotationMirror;
import static org.hibernate.jpamodelgen.util.TypeUtils.getAnnotationValue;
import static org.hibernate.jpamodelgen.util.TypeUtils.getCollectionElementType;
import static org.hibernate.jpamodelgen.util.TypeUtils.getKeyType;
import static org.hibernate.jpamodelgen.util.TypeUtils.isAnnotationMirrorOfType;
import static org.hibernate.jpamodelgen.util.TypeUtils.toArrayTypeString;
import static org.hibernate.jpamodelgen.util.TypeUtils.toTypeString;
/**
* @author Hardy Ferentschik
*/
public class MetaAttributeGenerationVisitor extends SimpleTypeVisitor6<@Nullable AnnotationMetaAttribute, Element> {
public class MetaAttributeGenerationVisitor extends SimpleTypeVisitor8<@Nullable AnnotationMetaAttribute, Element> {
/**
* FQCN of the Hibernate-specific {@code @Target} annotation.
@ -58,7 +68,7 @@ public class MetaAttributeGenerationVisitor extends SimpleTypeVisitor6<@Nullable
@Override
public @Nullable AnnotationMetaAttribute visitPrimitive(PrimitiveType t, Element element) {
return new AnnotationMetaSingleAttribute( entity, element, TypeUtils.toTypeString( t ) );
return new AnnotationMetaSingleAttribute( entity, element, toTypeString( t ) );
}
@Override
@ -80,167 +90,150 @@ public class MetaAttributeGenerationVisitor extends SimpleTypeVisitor6<@Nullable
// }
// }
// return attribute;
return new AnnotationMetaSingleAttribute( entity, element, TypeUtils.toArrayTypeString( t, context ) );
return new AnnotationMetaSingleAttribute( entity, element, toArrayTypeString( t, context ) );
}
@Override
public @Nullable 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()
);
final TypeMirror erasedType = context.getTypeUtils().erasure( t.getUpperBound() );
return new AnnotationMetaSingleAttribute( entity, element, erasedType.toString() );
}
@Override
public @Nullable AnnotationMetaAttribute visitDeclared(DeclaredType declaredType, Element element) {
AnnotationMetaAttribute metaAttribute = null;
TypeElement returnedElement = (TypeElement) context.getTypeUtils().asElement( declaredType );
final 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() );
final String fqNameOfReturnType = returnedElement.getQualifiedName().toString();
final String collection = Constants.COLLECTIONS.get( fqNameOfReturnType );
final String targetEntity = getTargetEntity( element.getAnnotationMirrors() );
if ( collection != null ) {
return createMetaCollectionAttribute(
declaredType, element, fqNameOfReturnType, collection, targetEntity
);
return createMetaCollectionAttribute( declaredType, element, fqNameOfReturnType, collection, targetEntity );
}
else if ( isBasicAttribute( element, returnedElement ) ) {
String type = targetEntity != null ? targetEntity : returnedElement.getQualifiedName().toString();
final String type = targetEntity != null ? targetEntity : returnedElement.getQualifiedName().toString();
return new AnnotationMetaSingleAttribute( entity, element, type );
}
return metaAttribute;
else {
return null;
}
}
private @Nullable AnnotationMetaAttribute createMetaCollectionAttribute(DeclaredType declaredType, Element element, String fqNameOfReturnType, String collection, @Nullable String targetEntity) {
if ( TypeUtils.containsAnnotation( element, Constants.ELEMENT_COLLECTION ) ) {
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(
collectionElementType.toString() );
if ( accessTypeInfo == null ) {
AccessType explicitAccessType = null;
if ( collectionElement != null ) {
explicitAccessType = TypeUtils.determineAnnotationSpecifiedAccessType(
collectionElement
);
}
accessTypeInfo = new AccessTypeInformation(
collectionElementType.toString(),
explicitAccessType,
entity.getEntityAccessTypeInfo().getAccessType()
private AnnotationMetaAttribute createMetaCollectionAttribute(
DeclaredType declaredType, Element element, String fqNameOfReturnType, String collection,
@Nullable String targetEntity) {
if ( containsAnnotation( element, Constants.ELEMENT_COLLECTION ) ) {
final String explicitTargetEntity = getTargetEntity( element.getAnnotationMirrors() );
final TypeMirror collectionElementType =
getCollectionElementType( declaredType, fqNameOfReturnType, explicitTargetEntity, context );
if ( collectionElementType.getKind() == TypeKind.DECLARED ) {
final TypeElement collectionElement = (TypeElement)
context.getTypeUtils().asElement( collectionElementType );
setAccessType( collectionElementType, collectionElement );
}
}
return createMetaAttribute( declaredType, element, collection, targetEntity );
}
private AnnotationMetaAttribute createMetaAttribute(
DeclaredType declaredType, Element element, String collection, @Nullable String targetEntity) {
if ( containsAnnotation( element,
Constants.ONE_TO_MANY, Constants.MANY_TO_MANY,
Constants.MANY_TO_ANY, Constants.ELEMENT_COLLECTION ) ) {
if ( collection.equals( Constants.MAP_ATTRIBUTE ) ) { //TODO: pretty fragile!
return new AnnotationMetaMap(
entity,
element,
collection,
getMapKeyType( declaredType, element ),
getElementType( declaredType, targetEntity )
);
context.addAccessTypeInformation( collectionElementType.toString(), accessTypeInfo );
}
else {
accessTypeInfo.setDefaultAccessType( entity.getEntityAccessTypeInfo().getAccessType() );
return new AnnotationMetaCollection(
entity,
element,
collection,
getElementType( declaredType, targetEntity )
);
}
}
if ( TypeUtils.containsAnnotation(
element,
Constants.BASIC,
Constants.CONVERT,
Constants.HIBERNATE_TYPE
) && !TypeUtils.containsAnnotation(
element,
Constants.ONE_TO_MANY,
Constants.MANY_TO_MANY,
Constants.ELEMENT_COLLECTION
) ) {
return new AnnotationMetaSingleAttribute(
entity,
element,
TypeUtils.toTypeString( declaredType )
else {
final String typeWithVariablesErased = extractClosestRealTypeAsString( declaredType, context );
return new AnnotationMetaSingleAttribute( entity, element, typeWithVariablesErased );
}
}
private void setAccessType(TypeMirror collectionElementType, TypeElement collectionElement) {
final AccessTypeInformation accessTypeInfo = context.getAccessTypeInfo( collectionElementType.toString() );
if ( accessTypeInfo == null ) {
final AccessTypeInformation newAccessTypeInfo = new AccessTypeInformation(
collectionElementType.toString(),
collectionElement == null ? null : determineAnnotationSpecifiedAccessType( collectionElement ),
entity.getEntityAccessTypeInfo().getAccessType()
);
context.addAccessTypeInformation( collectionElementType.toString(), newAccessTypeInfo );
}
if ( collection.equals( Constants.MAP_ATTRIBUTE ) ) {
return createAnnotationMetaAttributeForMap( declaredType, element, collection, targetEntity );
else {
accessTypeInfo.setDefaultAccessType( entity.getEntityAccessTypeInfo().getAccessType() );
}
return new AnnotationMetaCollection(
entity, element, collection, getElementType( declaredType, targetEntity )
);
}
@Override
public @Nullable AnnotationMetaAttribute visitExecutable(ExecutableType t, Element p) {
if ( !p.getKind().equals( ElementKind.METHOD ) ) {
if ( p.getKind() == ElementKind.METHOD
&& isProperty( p.getSimpleName().toString(), toTypeString( t.getReturnType() ) ) ) {
return t.getReturnType().accept( this, p );
}
else {
return null;
}
String string = p.getSimpleName().toString();
if ( !StringUtil.isProperty( string, TypeUtils.toTypeString( t.getReturnType() ) ) ) {
return null;
}
TypeMirror returnType = t.getReturnType();
return returnType.accept( this, p );
}
private boolean isBasicAttribute(Element element, Element returnedElement) {
if ( TypeUtils.containsAnnotation( element, Constants.BASIC )
|| TypeUtils.containsAnnotation( element, Constants.ONE_TO_ONE )
|| TypeUtils.containsAnnotation( element, Constants.MANY_TO_ONE )
|| TypeUtils.containsAnnotation( element, Constants.EMBEDDED_ID )
|| TypeUtils.containsAnnotation( element, Constants.ID ) ) {
if ( containsAnnotation( element, Constants.BASIC )
|| containsAnnotation( element, Constants.ONE_TO_ONE )
|| containsAnnotation( element, Constants.MANY_TO_ONE )
|| containsAnnotation( element, Constants.EMBEDDED_ID )
|| containsAnnotation( element, Constants.ID ) ) {
return true;
}
// METAGEN-28
if ( TypeUtils.getAnnotationMirror( element, ORG_HIBERNATE_ANNOTATIONS_TYPE ) != null ) {
if ( getAnnotationMirror( element, ORG_HIBERNATE_ANNOTATIONS_TYPE ) != null ) {
return true;
}
BasicAttributeVisitor basicVisitor = new BasicAttributeVisitor( context );
return returnedElement.asType().accept( basicVisitor, returnedElement );
return returnedElement.asType().accept( new BasicAttributeVisitor( context ), returnedElement );
}
private @Nullable AnnotationMetaAttribute createAnnotationMetaAttributeForMap(DeclaredType declaredType, Element element, String collection, @Nullable String targetEntity) {
final AnnotationMirror annotationMirror = TypeUtils.getAnnotationMirror( element, Constants.MAP_KEY_CLASS );
String keyType;
if ( annotationMirror != null ) {
TypeMirror typeMirror = (TypeMirror) NullnessUtil.castNonNull(
TypeUtils.getAnnotationValue( annotationMirror, 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 getMapKeyType(DeclaredType declaredType, Element element) {
final AnnotationMirror annotationMirror = getAnnotationMirror(element, Constants.MAP_KEY_CLASS );
return annotationMirror == null
? getKeyType( declaredType, context )
: castNonNull( getAnnotationValue( annotationMirror, DEFAULT_ANNOTATION_PARAMETER_NAME ) ).toString();
}
private String getElementType(DeclaredType declaredType, @Nullable String targetEntity) {
if ( targetEntity != null ) {
return targetEntity;
}
final List<? extends TypeMirror> 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
);
final List<? extends TypeMirror> mirrors = declaredType.getTypeArguments();
switch ( mirrors.size() ) {
case 0:
return "?";
case 1:
return extractClosestRealTypeAsString( mirrors.get( 0 ), context );
case 2:
return extractClosestRealTypeAsString( mirrors.get( 1 ), context );
default:
context.logMessage(
Diagnostic.Kind.WARNING,
"Unable to find the closest solid type" + declaredType
);
return "?";
}
return "?";
}
}
@ -250,37 +243,35 @@ public class MetaAttributeGenerationVisitor extends SimpleTypeVisitor6<@Nullable
* @return target entity class name as string or {@code null} if no targetEntity is here or if equals to void
*/
private @Nullable String getTargetEntity(List<? extends AnnotationMirror> annotations) {
String fullyQualifiedTargetEntityName = null;
for ( AnnotationMirror mirror : annotations ) {
if ( TypeUtils.isAnnotationMirrorOfType( mirror, Constants.ELEMENT_COLLECTION ) ) {
fullyQualifiedTargetEntityName = getFullyQualifiedClassNameOfTargetEntity( mirror, "targetClass" );
if ( isAnnotationMirrorOfType( mirror, Constants.ELEMENT_COLLECTION ) ) {
return getFullyQualifiedClassNameOfTargetEntity( mirror, "targetClass" );
}
else if ( TypeUtils.isAnnotationMirrorOfType( mirror, Constants.ONE_TO_MANY )
|| TypeUtils.isAnnotationMirrorOfType( mirror, Constants.MANY_TO_MANY )
|| TypeUtils.isAnnotationMirrorOfType( mirror, Constants.MANY_TO_ONE )
|| TypeUtils.isAnnotationMirrorOfType( mirror, Constants.ONE_TO_ONE ) ) {
fullyQualifiedTargetEntityName = getFullyQualifiedClassNameOfTargetEntity( mirror, "targetEntity" );
else if ( isAnnotationMirrorOfType( mirror, Constants.ONE_TO_MANY )
|| isAnnotationMirrorOfType( mirror, Constants.MANY_TO_MANY )
|| isAnnotationMirrorOfType( mirror, Constants.MANY_TO_ONE )
|| isAnnotationMirrorOfType( mirror, Constants.ONE_TO_ONE ) ) {
return getFullyQualifiedClassNameOfTargetEntity( mirror, "targetEntity" );
}
else if ( TypeUtils.isAnnotationMirrorOfType( mirror, ORG_HIBERNATE_ANNOTATIONS_TARGET ) ) {
fullyQualifiedTargetEntityName = getFullyQualifiedClassNameOfTargetEntity( mirror, "value" );
else if ( isAnnotationMirrorOfType( mirror, ORG_HIBERNATE_ANNOTATIONS_TARGET ) ) {
return getFullyQualifiedClassNameOfTargetEntity( mirror, "value" );
}
}
return fullyQualifiedTargetEntityName;
return null;
}
private @Nullable String getFullyQualifiedClassNameOfTargetEntity(AnnotationMirror mirror, String parameterName) {
assert mirror != null;
assert parameterName != null;
String targetEntityName = null;
Object parameterValue = TypeUtils.getAnnotationValue( mirror, parameterName );
final Object parameterValue = getAnnotationValue( mirror, parameterName );
if ( parameterValue != null ) {
TypeMirror parameterType = (TypeMirror) parameterValue;
if ( !parameterType.getKind().equals( TypeKind.VOID ) ) {
targetEntityName = parameterType.toString();
final TypeMirror parameterType = (TypeMirror) parameterValue;
if ( parameterType.getKind() != TypeKind.VOID ) {
return parameterType.toString();
}
}
return targetEntityName;
return null;
}
}
@ -288,51 +279,49 @@ public class MetaAttributeGenerationVisitor extends SimpleTypeVisitor6<@Nullable
* 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<Boolean, Element> {
class BasicAttributeVisitor extends SimpleTypeVisitor8<Boolean, Element> {
private final Context context;
public BasicAttributeVisitor(Context context) {
super( Boolean.FALSE );
super( false );
this.context = context;
}
@Override
public Boolean visitPrimitive(PrimitiveType t, Element element) {
return Boolean.TRUE;
return true;
}
@Override
public Boolean visitArray(ArrayType t, Element element) {
TypeMirror componentMirror = t.getComponentType();
TypeElement componentElement = (TypeElement) context.getTypeUtils().asElement( componentMirror );
final TypeElement componentElement = (TypeElement)
context.getTypeUtils().asElement( t.getComponentType() );
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;
return true;
}
if ( ( element.getKind().isClass() && !ElementKind.ENUM.equals( element.getKind() ) ) ||
ElementKind.INTERFACE.equals( element.getKind() ) ) {
TypeElement typeElement = ( (TypeElement) element );
String typeName = typeElement.getQualifiedName().toString();
if ( element.getKind() == ElementKind.CLASS || element.getKind() == ElementKind.INTERFACE ) {
final TypeElement typeElement = (TypeElement) element;
final String typeName = typeElement.getQualifiedName().toString();
if ( Constants.BASIC_TYPES.contains( typeName ) ) {
return Boolean.TRUE;
return true;
}
if ( TypeUtils.containsAnnotation( element, Constants.EMBEDDABLE ) ) {
return Boolean.TRUE;
if ( containsAnnotation( element, Constants.EMBEDDABLE ) ) {
return true;
}
for ( TypeMirror mirror : typeElement.getInterfaces() ) {
TypeElement interfaceElement = (TypeElement) context.getTypeUtils().asElement( mirror );
if ( "java.io.Serializable".equals( interfaceElement.getQualifiedName().toString() ) ) {
return Boolean.TRUE;
}
final TypeMirror serializableType =
context.getElementUtils().getTypeElement("java.io.Serializable").asType();
if ( context.getTypeUtils().isSubtype( typeElement.asType(), serializableType) ) {
return true;
}
}
return Boolean.FALSE;
return false;
}
}

View File

@ -62,7 +62,7 @@ public class QueryMethod implements MetaAttribute {
@Override
public String getAttributeDeclarationString() {
List<String> paramTypes = this.paramTypes.stream()
.map(ptype->isOrderParam(ptype) && ptype.endsWith("[]") ? ptype.replace("[]", "...") : ptype)
.map(ptype->isOrderParam(ptype) && ptype.endsWith("[]") ? ptype.substring(0, ptype.length()-2) + "..." : ptype)
.collect(toList());
StringBuilder declaration = new StringBuilder();
declaration

View File

@ -25,6 +25,7 @@ public final class Constants {
public static final String ONE_TO_MANY = "jakarta.persistence.OneToMany";
public static final String MANY_TO_ONE = "jakarta.persistence.ManyToOne";
public static final String MANY_TO_MANY = "jakarta.persistence.ManyToMany";
public static final String MANY_TO_ANY = "org.hibernate.annotations.ManyToAny";
public static final String MAP_KEY_CLASS = "jakarta.persistence.MapKeyClass";
public static final String ELEMENT_COLLECTION = "jakarta.persistence.ElementCollection";
public static final String ACCESS = "jakarta.persistence.Access";

View File

@ -50,7 +50,8 @@ public final class StringUtil {
return true;
}
if ( isValidPropertyName( methodName, PROPERTY_PREFIX_IS ) || isValidPropertyName( methodName, PROPERTY_PREFIX_HAS ) ) {
if ( isValidPropertyName( methodName, PROPERTY_PREFIX_IS )
|| isValidPropertyName( methodName, PROPERTY_PREFIX_HAS ) ) {
return isBooleanGetter( returnTypeAsString );
}

View File

@ -6,9 +6,7 @@
*/
package org.hibernate.jpamodelgen.util;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@ -24,9 +22,9 @@ import javax.lang.model.type.ExecutableType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.type.TypeVariable;
import javax.lang.model.type.WildcardType;
import javax.lang.model.util.ElementFilter;
import javax.lang.model.util.Elements;
import javax.lang.model.util.SimpleTypeVisitor6;
import javax.lang.model.util.SimpleTypeVisitor8;
import javax.tools.Diagnostic;
import org.hibernate.jpamodelgen.Context;
@ -34,6 +32,9 @@ import org.hibernate.jpamodelgen.MetaModelGenerationException;
import org.checkerframework.checker.nullness.qual.Nullable;
import static org.hibernate.jpamodelgen.util.NullnessUtil.castNonNull;
import static org.hibernate.jpamodelgen.util.StringUtil.isProperty;
/**
* Utility class.
*
@ -44,8 +45,8 @@ import org.checkerframework.checker.nullness.qual.Nullable;
public final class TypeUtils {
public static final String DEFAULT_ANNOTATION_PARAMETER_NAME = "value";
private static final Map<TypeKind, String> PRIMITIVE_WRAPPERS = new HashMap<TypeKind, String>();
private static final Map<TypeKind, String> PRIMITIVES = new HashMap<TypeKind, String>();
private static final Map<TypeKind, String> PRIMITIVE_WRAPPERS = new HashMap<>();
private static final Map<TypeKind, String> PRIMITIVES = new HashMap<>();
static {
PRIMITIVE_WRAPPERS.put( TypeKind.CHAR, "Character" );
@ -74,35 +75,34 @@ public final class TypeUtils {
}
public static String toTypeString(TypeMirror type) {
if ( type.getKind().isPrimitive() ) {
return NullnessUtil.castNonNull( PRIMITIVE_WRAPPERS.get( type.getKind() ) );
}
return TypeRenderingVisitor.toString( type );
return type.getKind().isPrimitive()
? castNonNull( PRIMITIVE_WRAPPERS.get( type.getKind() ) )
: TypeRenderingVisitor.toString( type );
}
public static String toArrayTypeString(ArrayType type, Context context) {
TypeMirror componentType = type.getComponentType();
final TypeMirror componentType = type.getComponentType();
if ( componentType.getKind().isPrimitive() ) {
return PRIMITIVES.get( componentType.getKind() ) + "[]";
}
// When an ArrayType is annotated with an annotation which uses TYPE_USE targets,
// we cannot simply take the TypeMirror returned by #getComponentType because it
// itself is an AnnotatedType.
//
// The simplest approach here to get the TypeMirror for both ArrayType use cases
// is to use the visitor to retrieve the underlying TypeMirror.
TypeMirror component = componentType.accept(
new SimpleTypeVisitor6<TypeMirror, Void>() {
@Override
protected TypeMirror defaultAction(TypeMirror e, Void aVoid) {
return e;
}
},
null
);
return extractClosestRealTypeAsString( component, context ) + "[]";
else {
// When an ArrayType is annotated with an annotation which uses TYPE_USE targets,
// we cannot simply take the TypeMirror returned by #getComponentType because it
// itself is an AnnotatedType.
//
// The simplest approach here to get the TypeMirror for both ArrayType use cases
// is to use the visitor to retrieve the underlying TypeMirror.
final TypeMirror component = componentType.accept(
new SimpleTypeVisitor8<TypeMirror, Void>() {
@Override
protected TypeMirror defaultAction(TypeMirror e, Void aVoid) {
return e;
}
},
null
);
return extractClosestRealTypeAsString( component, context ) + "[]";
}
}
public static @Nullable TypeElement getSuperclassTypeElement(TypeElement element) {
@ -119,31 +119,53 @@ public final class TypeUtils {
}
public static String extractClosestRealTypeAsString(TypeMirror type, Context context) {
if ( type instanceof TypeVariable ) {
final TypeMirror compositeUpperBound = ( (TypeVariable) type ).getUpperBound();
return extractClosestRealTypeAsString( compositeUpperBound, context );
final TypeMirror mirror = extractClosestRealType( type, context );
return mirror == null ? "?" : mirror.toString();
}
private static @Nullable TypeMirror lowerBound(TypeMirror bound) {
return bound.getKind() == TypeKind.NULL ? null : bound;
}
private static @Nullable TypeMirror upperBound(TypeMirror bound) {
return bound.getKind() == TypeKind.DECLARED && bound.toString().equals("java.lang.Object") ? null : bound;
}
@SuppressWarnings("nullness")
public static TypeMirror extractClosestRealType(TypeMirror type, Context context) {
if ( type == null ) {
return null;
}
else {
final TypeMirror erasureType = context.getTypeUtils().erasure( type );
if ( TypeKind.ARRAY.equals( erasureType.getKind() ) ) {
// keep old behavior here for arrays since #asElement returns null for them.
return erasureType.toString();
}
else {
return ( (TypeElement) context.getTypeUtils().asElement( erasureType ) ).getQualifiedName().toString();
}
switch ( type.getKind() ) {
case TYPEVAR:
final TypeVariable typeVariable = (TypeVariable) type;
return context.getTypeUtils().getWildcardType(
upperBound( extractClosestRealType( typeVariable.getUpperBound(), context ) ),
lowerBound( extractClosestRealType( typeVariable.getLowerBound(), context ) )
);
case WILDCARD:
final WildcardType wildcardType = (WildcardType) type;
return context.getTypeUtils().getWildcardType(
extractClosestRealType( wildcardType.getExtendsBound(), context ),
extractClosestRealType( wildcardType.getSuperBound(), context )
);
case DECLARED:
final DeclaredType declaredType = (DeclaredType) type;
final TypeElement typeElement = (TypeElement) declaredType.asElement();
return context.getTypeUtils().getDeclaredType( typeElement,
declaredType.getTypeArguments().stream()
.map( arg -> extractClosestRealType( arg, context ) )
.toArray( TypeMirror[]::new ) );
default:
return context.getTypeUtils().erasure( type );
}
}
public static boolean containsAnnotation(Element element, String... annotations) {
assert element != null;
assert annotations != null;
Set<String> annotationClassNames = new HashSet<>();
Collections.addAll( annotationClassNames, annotations );
List<? extends AnnotationMirror> annotationMirrors = element.getAnnotationMirrors();
for ( AnnotationMirror mirror : annotationMirrors ) {
final Set<String> annotationClassNames = Set.of(annotations);
for ( AnnotationMirror mirror : element.getAnnotationMirrors() ) {
if ( annotationClassNames.contains( mirror.getAnnotationType().toString() ) ) {
return true;
}
@ -164,9 +186,7 @@ public final class TypeUtils {
public static boolean isAnnotationMirrorOfType(AnnotationMirror annotationMirror, String fqcn) {
assert annotationMirror != null;
assert fqcn != null;
String annotationClassName = annotationMirror.getAnnotationType().toString();
return annotationClassName.equals( fqcn );
return annotationMirror.getAnnotationType().toString().equals( fqcn );
}
/**
@ -181,121 +201,114 @@ public final class TypeUtils {
public static @Nullable AnnotationMirror getAnnotationMirror(Element element, String fqcn) {
assert element != null;
assert fqcn != null;
AnnotationMirror mirror = null;
for ( AnnotationMirror am : element.getAnnotationMirrors() ) {
if ( isAnnotationMirrorOfType( am, fqcn ) ) {
mirror = am;
break;
for ( AnnotationMirror mirror : element.getAnnotationMirrors() ) {
if ( isAnnotationMirrorOfType( mirror, fqcn ) ) {
return mirror;
}
}
return mirror;
return null;
}
public static @Nullable Object getAnnotationValue(AnnotationMirror annotationMirror, String parameterValue) {
assert annotationMirror != null;
assert parameterValue != null;
Object returnValue = null;
for ( Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : annotationMirror.getElementValues()
.entrySet() ) {
for ( Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry
: annotationMirror.getElementValues().entrySet() ) {
if ( parameterValue.equals( entry.getKey().getSimpleName().toString() ) ) {
returnValue = entry.getValue().getValue();
break;
return entry.getValue().getValue();
}
}
return returnValue;
return null;
}
public static void determineAccessTypeForHierarchy(TypeElement searchedElement, Context context) {
String fqcn = searchedElement.getQualifiedName().toString();
final String fqcn = searchedElement.getQualifiedName().toString();
context.logMessage( Diagnostic.Kind.OTHER, "Determining access type for " + fqcn );
AccessTypeInformation accessTypeInfo = context.getAccessTypeInfo( fqcn );
final AccessTypeInformation accessTypeInfo = context.getAccessTypeInfo( fqcn );
if ( accessTypeInfo != null && accessTypeInfo.isAccessTypeResolved() ) {
context.logMessage(
Diagnostic.Kind.OTHER,
"AccessType for " + searchedElement.toString() + " found in cache: " + accessTypeInfo
"AccessType for " + searchedElement + " found in cache: " + accessTypeInfo
);
return;
}
// check for explicit access type
AccessType forcedAccessType = determineAnnotationSpecifiedAccessType( searchedElement );
if ( forcedAccessType != null ) {
context.logMessage(
Diagnostic.Kind.OTHER, "Explicit access type on " + searchedElement + ":" + forcedAccessType
);
accessTypeInfo = new AccessTypeInformation( fqcn, forcedAccessType, null );
context.addAccessTypeInformation( fqcn, accessTypeInfo );
updateEmbeddableAccessType( searchedElement, context, forcedAccessType );
return;
}
// need to find the default access type for this class
// let's check first if this entity is the root of the class hierarchy and defines an id. If so the
// placement of the id annotation determines the access type
AccessType defaultAccessType = getAccessTypeInCaseElementIsRoot( searchedElement, context );
if ( defaultAccessType != null ) {
accessTypeInfo = new AccessTypeInformation( fqcn, null, defaultAccessType );
context.addAccessTypeInformation( fqcn, accessTypeInfo );
updateEmbeddableAccessType( searchedElement, context, defaultAccessType );
setDefaultAccessTypeForMappedSuperclassesInHierarchy( searchedElement, defaultAccessType, context );
return;
}
// if we end up here we need to recursively look for superclasses
defaultAccessType = getDefaultAccessForHierarchy( searchedElement, context );
if ( defaultAccessType == null ) {
defaultAccessType = AccessType.PROPERTY;
}
accessTypeInfo = new AccessTypeInformation( fqcn, null, defaultAccessType );
context.addAccessTypeInformation( fqcn, accessTypeInfo );
updateEmbeddableAccessType( searchedElement, context, defaultAccessType );
}
public static TypeMirror getCollectionElementType(DeclaredType t, String fqNameOfReturnedType, @Nullable String explicitTargetEntityName, Context context) {
TypeMirror collectionElementType;
if ( explicitTargetEntityName != null ) {
Elements elements = context.getElementUtils();
TypeElement element = elements.getTypeElement( explicitTargetEntityName );
collectionElementType = element.asType();
}
else {
List<? extends TypeMirror> typeArguments = t.getTypeArguments();
// check for explicit access type
final AccessType forcedAccessType = determineAnnotationSpecifiedAccessType( searchedElement) ;
if ( forcedAccessType != null ) {
context.logMessage(
Diagnostic.Kind.OTHER,
"Explicit access type on " + searchedElement + ":" + forcedAccessType
);
final AccessTypeInformation newAccessTypeInfo =
new AccessTypeInformation( fqcn, forcedAccessType, null );
context.addAccessTypeInformation( fqcn, newAccessTypeInfo );
updateEmbeddableAccessType( searchedElement, context, forcedAccessType );
}
else {
// need to find the default access type for this class
// let's check first if this entity is the root of the class hierarchy and defines an id. If so the
// placement of the id annotation determines the access type
final AccessType defaultAccessType = getAccessTypeInCaseElementIsRoot( searchedElement, context );
if ( defaultAccessType != null ) {
final AccessTypeInformation newAccessTypeInfo =
new AccessTypeInformation(fqcn, null, defaultAccessType);
context.addAccessTypeInformation( fqcn, newAccessTypeInfo );
updateEmbeddableAccessType( searchedElement, context, defaultAccessType );
setDefaultAccessTypeForMappedSuperclassesInHierarchy( searchedElement, defaultAccessType, context );
}
else {
// if we end up here we need to recursively look for superclasses
AccessType newDefaultAccessType = getDefaultAccessForHierarchy( searchedElement, context );
if ( newDefaultAccessType == null ) {
//TODO: this default is arbitrary and very questionable!
newDefaultAccessType = AccessType.PROPERTY;
}
final AccessTypeInformation newAccessTypeInfo =
new AccessTypeInformation( fqcn, null, newDefaultAccessType );
context.addAccessTypeInformation( fqcn, newAccessTypeInfo );
updateEmbeddableAccessType( searchedElement, context, newDefaultAccessType );
}
}
}
}
public static TypeMirror getCollectionElementType(
DeclaredType t, String fqNameOfReturnedType, @Nullable String explicitTargetEntityName, Context context) {
if ( explicitTargetEntityName != null ) {
return context.getElementUtils().getTypeElement( explicitTargetEntityName ).asType();
}
else {
final List<? extends TypeMirror> typeArguments = t.getTypeArguments();
if ( typeArguments.size() == 0 ) {
throw new MetaModelGenerationException( "Unable to determine collection type" );
}
else if ( Map.class.getCanonicalName().equals( fqNameOfReturnedType ) ) {
collectionElementType = t.getTypeArguments().get( 1 );
return t.getTypeArguments().get( 1 );
}
else {
collectionElementType = t.getTypeArguments().get( 0 );
return t.getTypeArguments().get( 0 );
}
}
return collectionElementType;
}
private static void updateEmbeddableAccessType(TypeElement element, Context context, AccessType defaultAccessType) {
List<? extends Element> fieldsOfClass = ElementFilter.fieldsIn( element.getEnclosedElements() );
for ( Element field : fieldsOfClass ) {
for ( Element field : ElementFilter.fieldsIn( element.getEnclosedElements() ) ) {
updateEmbeddableAccessTypeForMember( context, defaultAccessType, field );
}
List<? extends Element> methodOfClass = ElementFilter.methodsIn( element.getEnclosedElements() );
for ( Element method : methodOfClass ) {
for ( Element method : ElementFilter.methodsIn( element.getEnclosedElements() ) ) {
updateEmbeddableAccessTypeForMember( context, defaultAccessType, method );
}
}
private static void updateEmbeddableAccessTypeForMember(Context context, AccessType defaultAccessType, Element member) {
EmbeddedAttributeVisitor visitor = new EmbeddedAttributeVisitor( context );
String embeddedClassName = member.asType().accept( visitor, member );
final String embeddedClassName = member.asType().accept( new EmbeddedAttributeVisitor( context ), member );
if ( embeddedClassName != null ) {
AccessTypeInformation accessTypeInfo = context.getAccessTypeInfo( embeddedClassName );
final AccessTypeInformation accessTypeInfo = context.getAccessTypeInfo( embeddedClassName );
if ( accessTypeInfo == null ) {
accessTypeInfo = new AccessTypeInformation( embeddedClassName, null, defaultAccessType );
context.addAccessTypeInformation( embeddedClassName, accessTypeInfo );
final AccessTypeInformation newAccessTypeInfo =
new AccessTypeInformation( embeddedClassName, null, defaultAccessType );
context.addAccessTypeInformation( embeddedClassName, newAccessTypeInfo );
}
else {
accessTypeInfo.setDefaultAccessType( defaultAccessType );
@ -307,18 +320,19 @@ public final class TypeUtils {
AccessType defaultAccessType = null;
TypeElement superClass = element;
do {
superClass = TypeUtils.getSuperclassTypeElement( superClass );
superClass = getSuperclassTypeElement( superClass );
if ( superClass != null ) {
String fqcn = superClass.getQualifiedName().toString();
AccessTypeInformation accessTypeInfo = context.getAccessTypeInfo( fqcn );
final String fqcn = superClass.getQualifiedName().toString();
final AccessTypeInformation accessTypeInfo = context.getAccessTypeInfo( fqcn );
if ( accessTypeInfo != null && accessTypeInfo.getDefaultAccessType() != null ) {
return accessTypeInfo.getDefaultAccessType();
}
if ( TypeUtils.containsAnnotation( superClass, Constants.ENTITY, Constants.MAPPED_SUPERCLASS ) ) {
if ( containsAnnotation( superClass, Constants.ENTITY, Constants.MAPPED_SUPERCLASS ) ) {
defaultAccessType = getAccessTypeInCaseElementIsRoot( superClass, context );
if ( defaultAccessType != null ) {
accessTypeInfo = new AccessTypeInformation( fqcn, null, defaultAccessType );
context.addAccessTypeInformation( fqcn, accessTypeInfo );
final AccessTypeInformation newAccessTypeInfo
= new AccessTypeInformation( fqcn, null, defaultAccessType );
context.addAccessTypeInformation( fqcn, newAccessTypeInfo );
// we found an id within the class hierarchy and determined a default access type
// there cannot be any super entity classes (otherwise it would be a configuration error)
@ -344,18 +358,15 @@ public final class TypeUtils {
private static void setDefaultAccessTypeForMappedSuperclassesInHierarchy(TypeElement element, AccessType defaultAccessType, Context context) {
TypeElement superClass = element;
do {
superClass = TypeUtils.getSuperclassTypeElement( superClass );
superClass = getSuperclassTypeElement( superClass );
if ( superClass != null ) {
String fqcn = superClass.getQualifiedName().toString();
if ( TypeUtils.containsAnnotation( superClass, Constants.MAPPED_SUPERCLASS ) ) {
AccessTypeInformation accessTypeInfo;
AccessType forcedAccessType = determineAnnotationSpecifiedAccessType( superClass );
if ( forcedAccessType != null ) {
accessTypeInfo = new AccessTypeInformation( fqcn, null, forcedAccessType );
}
else {
accessTypeInfo = new AccessTypeInformation( fqcn, null, defaultAccessType );
}
final String fqcn = superClass.getQualifiedName().toString();
if ( containsAnnotation( superClass, Constants.MAPPED_SUPERCLASS ) ) {
final AccessType forcedAccessType = determineAnnotationSpecifiedAccessType( superClass );
final AccessTypeInformation accessTypeInfo =
forcedAccessType != null
? new AccessTypeInformation( fqcn, null, forcedAccessType )
: new AccessTypeInformation( fqcn, null, defaultAccessType );
context.addAccessTypeInformation( fqcn, accessTypeInfo );
}
}
@ -374,13 +385,10 @@ public final class TypeUtils {
* {@code null} is returned.
*/
private static @Nullable AccessType getAccessTypeInCaseElementIsRoot(TypeElement searchedElement, Context context) {
List<? extends Element> myMembers = searchedElement.getEnclosedElements();
for ( Element subElement : myMembers ) {
List<? extends AnnotationMirror> entityAnnotations =
context.getElementUtils().getAllAnnotationMirrors( subElement );
for ( Object entityAnnotation : entityAnnotations ) {
AnnotationMirror annotationMirror = (AnnotationMirror) entityAnnotation;
if ( isIdAnnotation( annotationMirror ) ) {
for ( Element subElement : searchedElement.getEnclosedElements() ) {
for ( AnnotationMirror entityAnnotation :
context.getElementUtils().getAllAnnotationMirrors( subElement ) ) {
if ( isIdAnnotation( entityAnnotation ) ) {
return getAccessTypeOfIdAnnotation( subElement );
}
}
@ -389,57 +397,54 @@ public final class TypeUtils {
}
private static @Nullable AccessType getAccessTypeOfIdAnnotation(Element element) {
AccessType accessType = null;
final ElementKind kind = element.getKind();
if ( kind == ElementKind.FIELD || kind == ElementKind.METHOD ) {
accessType = kind == ElementKind.FIELD ? AccessType.FIELD : AccessType.PROPERTY;
return kind == ElementKind.FIELD ? AccessType.FIELD : AccessType.PROPERTY;
}
else {
return null;
}
return accessType;
}
private static boolean isIdAnnotation(AnnotationMirror annotationMirror) {
return TypeUtils.isAnnotationMirrorOfType( annotationMirror, Constants.ID )
|| TypeUtils.isAnnotationMirrorOfType( annotationMirror, Constants.EMBEDDED_ID );
return isAnnotationMirrorOfType( annotationMirror, Constants.ID )
|| isAnnotationMirrorOfType( annotationMirror, Constants.EMBEDDED_ID );
}
public static @Nullable AccessType determineAnnotationSpecifiedAccessType(Element element) {
final AnnotationMirror accessAnnotationMirror = TypeUtils.getAnnotationMirror( element, Constants.ACCESS );
AccessType forcedAccessType = null;
if ( accessAnnotationMirror != null ) {
Element accessElement = (Element) NullnessUtil.castNonNull(
TypeUtils.getAnnotationValue( accessAnnotationMirror, DEFAULT_ANNOTATION_PARAMETER_NAME )
);
if ( accessElement.getKind().equals( ElementKind.ENUM_CONSTANT ) ) {
if ( accessElement.getSimpleName().toString().equals( AccessType.PROPERTY.toString() ) ) {
forcedAccessType = AccessType.PROPERTY;
final Element accessElement = (Element)
castNonNull( getAnnotationValue( accessAnnotationMirror, DEFAULT_ANNOTATION_PARAMETER_NAME ) );
if ( accessElement.getKind() == ElementKind.ENUM_CONSTANT ) {
if ( accessElement.getSimpleName().contentEquals( AccessType.PROPERTY.toString() ) ) {
return AccessType.PROPERTY;
}
else if ( accessElement.getSimpleName().toString().equals( AccessType.FIELD.toString() ) ) {
forcedAccessType = AccessType.FIELD;
else if ( accessElement.getSimpleName().contentEquals( AccessType.FIELD.toString() ) ) {
return AccessType.FIELD;
}
}
}
return forcedAccessType;
return null;
}
public static ElementKind getElementKindForAccessType(AccessType accessType) {
if ( AccessType.FIELD.equals( accessType ) ) {
return ElementKind.FIELD;
return accessType == AccessType.FIELD ? ElementKind.FIELD : ElementKind.METHOD;
}
public static String getKeyType(DeclaredType type, Context context) {
final List<? extends TypeMirror> typeArguments = type.getTypeArguments();
if ( typeArguments.isEmpty() ) {
context.logMessage( Diagnostic.Kind.ERROR, "Unable to determine type argument for " + type );
return "java.lang.Object";
}
else {
return ElementKind.METHOD;
return extractClosestRealTypeAsString( typeArguments.get( 0 ), context );
}
}
public static String getKeyType(DeclaredType t, Context context) {
List<? extends TypeMirror> typeArguments = t.getTypeArguments();
if ( typeArguments.size() == 0 ) {
context.logMessage( Diagnostic.Kind.ERROR, "Unable to determine type argument for " + t );
}
return extractClosestRealTypeAsString( typeArguments.get( 0 ), context );
}
static class EmbeddedAttributeVisitor extends SimpleTypeVisitor6<@Nullable String, Element> {
private Context context;
static class EmbeddedAttributeVisitor extends SimpleTypeVisitor8<@Nullable String, Element> {
private final Context context;
EmbeddedAttributeVisitor(Context context) {
this.context = context;
@ -447,27 +452,24 @@ public final class TypeUtils {
@Override
public @Nullable String visitDeclared(DeclaredType declaredType, Element element) {
TypeElement returnedElement = (TypeElement) context.getTypeUtils().asElement( declaredType );
String fqNameOfReturnType = null;
if ( containsAnnotation( returnedElement, Constants.EMBEDDABLE ) ) {
fqNameOfReturnType = returnedElement.getQualifiedName().toString();
}
return fqNameOfReturnType;
final TypeElement returnedElement = (TypeElement)
context.getTypeUtils().asElement( declaredType );
return containsAnnotation( returnedElement, Constants.EMBEDDABLE )
? returnedElement.getQualifiedName().toString()
: null;
}
@Override
public @Nullable String visitExecutable(ExecutableType t, Element p) {
if ( !p.getKind().equals( ElementKind.METHOD ) ) {
if ( p.getKind().equals( ElementKind.METHOD ) ) {
String string = p.getSimpleName().toString();
return isProperty( string, toTypeString( t.getReturnType() ) )
? t.getReturnType().accept(this, p)
: null;
}
else {
return null;
}
String string = p.getSimpleName().toString();
if ( !StringUtil.isProperty( string, TypeUtils.toTypeString( t.getReturnType() ) ) ) {
return null;
}
TypeMirror returnType = t.getReturnType();
return returnType.accept( this, p );
}
}
}

View File

@ -525,12 +525,11 @@ public abstract class ProcessorSessionFactory extends MockSessionFactory {
return true;
}
else {
TypeMirror type = member.asType();
final TypeMirror type = member.asType();
if (type.getKind() == TypeKind.DECLARED) {
DeclaredType declaredType = (DeclaredType) type;
TypeElement typeElement = (TypeElement) declaredType.asElement();
//TODO: something better here!
return typeElement.getSuperclass().toString().startsWith("java.lang.Enum");
final DeclaredType declaredType = (DeclaredType) type;
final TypeElement typeElement = (TypeElement) declaredType.asElement();
return typeElement.getKind() == ElementKind.ENUM;
}
else {
return false;
@ -547,7 +546,7 @@ public abstract class ProcessorSessionFactory extends MockSessionFactory {
return true;
}
else {
TypeMirror type = member.asType();
final TypeMirror type = member.asType();
return type.getKind() == TypeKind.DECLARED
&& hasAnnotation(type, "Embeddable");
}

View File

@ -14,6 +14,7 @@ import org.antlr.v4.runtime.atn.PredictionMode;
import org.antlr.v4.runtime.misc.ParseCancellationException;
import org.hibernate.PropertyNotFoundException;
import org.hibernate.QueryException;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.grammars.hql.HqlLexer;
import org.hibernate.grammars.hql.HqlParser;
import org.hibernate.query.hql.internal.HqlParseTreeBuilder;
@ -46,68 +47,32 @@ public class Validation {
}
public static void validate(
String hql, boolean checkParams,
String hql,
boolean checkParams, boolean checkTyping,
Set<Integer> setParameterLabels,
Set<String> setParameterNames,
Handler handler,
MockSessionFactory factory) {
validate(hql, checkParams, setParameterLabels, setParameterNames, handler, factory, 0);
SessionFactoryImplementor factory) {
validate( hql, checkParams, checkTyping, setParameterLabels, setParameterNames, handler, factory, 0 );
}
public static void validate(
String hql, boolean checkParams,
String hql,
boolean checkParams, boolean checkTyping,
Set<Integer> setParameterLabels,
Set<String> setParameterNames,
Handler handler,
MockSessionFactory factory,
SessionFactoryImplementor factory,
int errorOffset) {
// handler = new Filter(handler, errorOffset);
try {
final HqlLexer hqlLexer = HqlParseTreeBuilder.INSTANCE.buildHqlLexer( hql );
final HqlParser hqlParser = HqlParseTreeBuilder.INSTANCE.buildHqlParser( hql, hqlLexer );
hqlLexer.addErrorListener( handler );
hqlParser.getInterpreter().setPredictionMode( PredictionMode.SLL );
hqlParser.removeErrorListeners();
hqlParser.addErrorListener( handler );
hqlParser.setErrorHandler( new BailErrorStrategy() );
HqlParser.StatementContext statementContext;
try {
statementContext = hqlParser.statement();
final HqlParser.StatementContext statementContext = parseAndCheckSyntax( hql, handler );
if ( checkTyping && handler.getErrorCount() == 0 ) {
checkTyping( hql, handler, factory, errorOffset, statementContext );
}
catch ( ParseCancellationException e) {
// reset the input token stream and parser state
hqlLexer.reset();
hqlParser.reset();
// fall back to LL(k)-based parsing
hqlParser.getInterpreter().setPredictionMode( PredictionMode.LL );
hqlParser.setErrorHandler( new DefaultErrorStrategy() );
statementContext = hqlParser.statement();
}
if (handler.getErrorCount() == 0) {
try {
new SemanticQueryBuilder<>(Object[].class, () -> false, factory)
.visitStatement( statementContext );
}
catch (JdbcTypeRecommendationException ignored) {
// just squash these for now
}
catch (QueryException | PathElementException | TerminalPathException | EntityTypeException
| PropertyNotFoundException se) { //TODO is this one really thrown by core? It should not be!
String message = se.getMessage();
if ( message != null ) {
handler.error(-errorOffset+1, -errorOffset + hql.length(), message);
}
}
}
if (checkParams) {
checkParameterBinding(hql, setParameterLabels, setParameterNames, handler, errorOffset);
if ( checkParams ) {
checkParameterBinding( hql, setParameterLabels, setParameterNames, handler, errorOffset );
}
}
catch (Exception e) {
@ -115,6 +80,53 @@ public class Validation {
}
}
private static void checkTyping(
String hql,
Handler handler,
SessionFactoryImplementor factory,
int errorOffset,
HqlParser.StatementContext statementContext) {
try {
new SemanticQueryBuilder<>( Object[].class, () -> false, factory )
.visitStatement( statementContext );
}
catch ( JdbcTypeRecommendationException ignored ) {
// just squash these for now
}
catch ( QueryException | PathElementException | TerminalPathException | EntityTypeException
| PropertyNotFoundException se ) { //TODO is this one really thrown by core? It should not be!
String message = se.getMessage();
if ( message != null ) {
handler.error( -errorOffset +1, -errorOffset + hql.length(), message );
}
}
}
private static HqlParser.StatementContext parseAndCheckSyntax(String hql, Handler handler) {
final HqlLexer hqlLexer = HqlParseTreeBuilder.INSTANCE.buildHqlLexer( hql );
final HqlParser hqlParser = HqlParseTreeBuilder.INSTANCE.buildHqlParser( hql, hqlLexer );
hqlLexer.addErrorListener( handler );
hqlParser.getInterpreter().setPredictionMode( PredictionMode.SLL );
hqlParser.removeErrorListeners();
hqlParser.addErrorListener( handler );
hqlParser.setErrorHandler( new BailErrorStrategy() );
try {
return hqlParser.statement();
}
catch ( ParseCancellationException e) {
// reset the input token stream and parser state
hqlLexer.reset();
hqlParser.reset();
// fall back to LL(k)-based parsing
hqlParser.getInterpreter().setPredictionMode( PredictionMode.LL );
hqlParser.setErrorHandler( new DefaultErrorStrategy() );
return hqlParser.statement();
}
}
private static void checkParameterBinding(
String hql,
Set<Integer> setParameterLabels,
@ -143,10 +155,10 @@ public class Validation {
String text = next.getText();
switch (tokenType) {
case HqlLexer.COLON:
if (!text.isEmpty()
&& isJavaIdentifierStart(text.codePointAt(0))) {
if ( !text.isEmpty()
&& isJavaIdentifierStart( text.codePointAt(0) ) ) {
names.add(text);
if (setParameterNames.contains(text)) {
if ( setParameterNames.contains(text) ) {
continue;
}
}
@ -155,7 +167,7 @@ public class Validation {
}
break;
case HqlLexer.QUESTION_MARK:
if (next.getType() == HqlLexer.INTEGER_LITERAL) {
if ( next.getType() == HqlLexer.INTEGER_LITERAL ) {
int label;
try {
label = parseInt(text);
@ -164,7 +176,7 @@ public class Validation {
continue;
}
labels.add(label);
if (setParameterLabels.contains(label)) {
if ( setParameterLabels.contains(label) ) {
continue;
}
}
@ -186,34 +198,38 @@ public class Validation {
break;
}
}
if (unsetParams != null) {
handler.warn(start-errorOffset+1, end-errorOffset, parameters + unsetParams + notSet);
if ( unsetParams != null ) {
handler.warn( start-errorOffset+1, end-errorOffset, parameters + unsetParams + notSet );
}
setParameterNames.removeAll(names);
setParameterLabels.removeAll(labels);
int count = setParameterNames.size() + setParameterLabels.size();
if (count > 0) {
String missingParams =
concat(setParameterNames.stream().map(name -> ":" + name),
setParameterLabels.stream().map(label -> "?" + label))
.reduce((x, y) -> x + ", " + y)
.orElse(null);
String params =
count == 1 ?
"Parameter " :
"Parameters ";
String notOccur =
count == 1 ?
" does not occur in the query" :
" do not occur in the query";
handler.warn(0, 0, params + missingParams + notOccur);
}
reportMissingParams( setParameterLabels, setParameterNames, handler );
}
finally {
setParameterNames.clear();
setParameterLabels.clear();
}
}
private static void reportMissingParams(Set<Integer> setParameterLabels, Set<String> setParameterNames, Handler handler) {
final int count = setParameterNames.size() + setParameterLabels.size();
if (count > 0) {
final String missingParams =
concat( setParameterNames.stream().map(name -> ":" + name),
setParameterLabels.stream().map(label -> "?" + label) )
.reduce((x, y) -> x + ", " + y)
.orElse(null);
final String params =
count == 1 ?
"Parameter " :
"Parameters ";
final String notOccur =
count == 1 ?
" does not occur in the query" :
" do not occur in the query";
handler.warn(0, 0, params + missingParams + notOccur);
}
}
}

View File

@ -8,13 +8,11 @@ package org.hibernate.jpamodelgen.test.elementcollection;
import org.hibernate.jpamodelgen.test.util.CompilationTest;
import org.hibernate.jpamodelgen.test.util.TestForIssue;
import org.hibernate.jpamodelgen.test.util.TestUtil;
import org.hibernate.jpamodelgen.test.util.WithClasses;
import org.junit.Test;
import static org.hibernate.jpamodelgen.test.util.TestUtil.assertListAttributeTypeInMetaModelFor;
import static org.hibernate.jpamodelgen.test.util.TestUtil.assertMapAttributesInMetaModelFor;
import static org.hibernate.jpamodelgen.test.util.TestUtil.assertMetamodelClassGeneratedFor;
import static org.hibernate.jpamodelgen.test.util.TestUtil.assertSetAttributeTypeInMetaModelFor;
import static org.hibernate.jpamodelgen.test.util.TestUtil.*;
/**
* @author Chris Cranford
@ -24,6 +22,7 @@ public class ElementCollectionTypeUseTest extends CompilationTest {
@TestForIssue(jiraKey = "HHH-12612")
@WithClasses(OfficeBuildingValidated.class)
public void testAnnotatedCollectionElements() {
System.out.println( TestUtil.getMetaModelSourceAsString( OfficeBuildingValidated.class ) );
assertMetamodelClassGeneratedFor( OfficeBuildingValidated.class );
assertMapAttributesInMetaModelFor(

View File

@ -9,6 +9,8 @@ package org.hibernate.jpamodelgen.test.elementcollection;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import jakarta.persistence.ElementCollection;
import jakarta.persistence.Entity;
/**
@ -19,6 +21,7 @@ public class Homework {
private List<String> paths;
@ElementCollection
public List<String> getPaths() {
return paths;
}

View File

@ -14,13 +14,15 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import jakarta.persistence.Access;
import jakarta.persistence.AccessType;
import jakarta.persistence.ElementCollection;
import jakarta.persistence.Entity;
/**
* @author Chris Cranford
*/
@Entity
@Entity @Access(AccessType.FIELD)
public class OfficeBuildingValidated {
// mock a bean validation annotation using TYPE_USE

View File

@ -8,13 +8,11 @@ package org.hibernate.jpamodelgen.test.embeddable.generics;
import org.hibernate.jpamodelgen.test.util.CompilationTest;
import org.hibernate.jpamodelgen.test.util.TestForIssue;
import org.hibernate.jpamodelgen.test.util.TestUtil;
import org.hibernate.jpamodelgen.test.util.WithClasses;
import org.junit.Test;
import static org.hibernate.jpamodelgen.test.util.TestUtil.assertMetamodelClassGeneratedFor;
import static org.hibernate.jpamodelgen.test.util.TestUtil.assertSetAttributeTypeInMetaModelFor;
import static org.hibernate.jpamodelgen.test.util.TestUtil.assertSuperClassRelationShipInMetamodel;
import static java.lang.System.out;
import static org.hibernate.jpamodelgen.test.util.TestUtil.*;
/**
* @author Chris Cranford
@ -24,15 +22,16 @@ public class EmbeddableGenericsTest extends CompilationTest {
@Test
@WithClasses({ ChildEmbeddable.class, ParentEmbeddable.class })
public void testGeneratingEmbeddablesWithGenerics() {
out.println( getMetaModelSourceAsString(ParentEmbeddable.class) );
assertMetamodelClassGeneratedFor( ChildEmbeddable.class );
assertMetamodelClassGeneratedFor( ParentEmbeddable.class );
assertSetAttributeTypeInMetaModelFor(
ParentEmbeddable.class,
"fields",
MyTypeInterface.class,
"Expected Set<MyTypeInterface> for attribute named 'fields'"
);
// assertAttributeTypeInMetaModelFor(
// ParentEmbeddable.class,
// "fields",
// "java.util.Set<? extends MyTypeInterface>",
// "Expected Set for attribute named 'fields'"
// );
assertSuperClassRelationShipInMetamodel(
ChildEmbeddable.class,

View File

@ -0,0 +1,18 @@
package org.hibernate.jpamodelgen.test.generictype;
import jakarta.persistence.*;
import org.hibernate.annotations.ManyToAny;
import java.util.List;
import java.util.Map;
import java.util.Set;
@Entity
public class Generic<T extends Number> {
@Id Long id;
@ManyToOne Generic<T> parent;
@OneToMany Set<Generic<T>> children;
@ElementCollection List<T> list;
@ElementCollection Map<T,T> map;
@ManyToAny Set<T> set;
}

View File

@ -0,0 +1,28 @@
/*
* 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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.jpamodelgen.test.generictype;
import org.hibernate.jpamodelgen.test.util.CompilationTest;
import org.hibernate.jpamodelgen.test.util.WithClasses;
import org.junit.Test;
import static java.lang.System.out;
import static org.hibernate.jpamodelgen.test.util.TestUtil.assertMetamodelClassGeneratedFor;
import static org.hibernate.jpamodelgen.test.util.TestUtil.getMetaModelSourceAsString;
/**
* @author Hardy Ferentschik
*/
public class GenericEntityTest extends CompilationTest {
@Test
@WithClasses(Generic.class)
public void testGeneric() {
out.println( getMetaModelSourceAsString( Generic.class ) );
assertMetamodelClassGeneratedFor( Generic.class );
}
}

View File

@ -7,8 +7,6 @@
package org.hibernate.jpamodelgen.test.util;
import java.io.File;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

View File

@ -0,0 +1,15 @@
/*
* 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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.jpamodelgen.test.wildcard;
public interface Property<T> {
String getName();
T getValue();
}

View File

@ -0,0 +1,20 @@
package org.hibernate.jpamodelgen.test.wildcard;
import jakarta.persistence.DiscriminatorType;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import org.hibernate.annotations.AnyDiscriminator;
import org.hibernate.annotations.ManyToAny;
import java.util.ArrayList;
import java.util.List;
@Entity
public class PropertyRepo {
@Id
private Long id;
@ManyToAny
@AnyDiscriminator(DiscriminatorType.STRING)
private List<Property<?>> properties = new ArrayList<>();
}

View File

@ -0,0 +1,26 @@
/*
* 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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.jpamodelgen.test.wildcard;
import org.hibernate.jpamodelgen.test.util.CompilationTest;
import org.hibernate.jpamodelgen.test.util.TestUtil;
import org.hibernate.jpamodelgen.test.util.WithClasses;
import org.junit.Test;
import static org.hibernate.jpamodelgen.test.util.TestUtil.assertMetamodelClassGeneratedFor;
/**
* @author Gavin King
*/
public class WildcardTest extends CompilationTest {
@Test
@WithClasses({ PropertyRepo.class })
public void testGeneratedAnnotationNotGenerated() {
System.out.println( TestUtil.getMetaModelSourceAsString( PropertyRepo.class ) );
assertMetamodelClassGeneratedFor( PropertyRepo.class );
}
}