HHH-17875 validate association mappings in processor
This commit is contained in:
parent
c98cd5e675
commit
c9c0261bfa
|
@ -375,6 +375,9 @@ public class AnnotationMetaEntity extends AnnotationMeta {
|
|||
putMember( "class", new AnnotationMetaType(this) );
|
||||
}
|
||||
|
||||
validatePersistentMembers( fieldsOfClass );
|
||||
validatePersistentMembers( gettersAndSettersOfClass );
|
||||
|
||||
addPersistentMembers( fieldsOfClass, AccessType.FIELD );
|
||||
addPersistentMembers( gettersAndSettersOfClass, AccessType.PROPERTY );
|
||||
}
|
||||
|
@ -391,6 +394,7 @@ public class AnnotationMetaEntity extends AnnotationMeta {
|
|||
}
|
||||
|
||||
private void addMethods(TypeElement element, List<ExecutableElement> methodsOfClass) {
|
||||
//TODO just use Elements.getAllMembers(element) here!
|
||||
for ( TypeMirror typeMirror : element.getInterfaces() ) {
|
||||
final DeclaredType declaredType = (DeclaredType) typeMirror;
|
||||
final TypeElement typeElement = (TypeElement) declaredType.asElement();
|
||||
|
@ -669,6 +673,14 @@ public class AnnotationMetaEntity extends AnnotationMeta {
|
|||
&& returnType.getKind() != TypeKind.VOID;
|
||||
}
|
||||
|
||||
private void validatePersistentMembers(List<? extends Element> membersOfClass) {
|
||||
for ( Element memberOfClass : membersOfClass ) {
|
||||
if ( hasAnnotation(memberOfClass, MANY_TO_ONE, ONE_TO_ONE, ONE_TO_MANY, MANY_TO_MANY) ) {
|
||||
validateAssociation(memberOfClass);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addPersistentMembers(List<? extends Element> membersOfClass, AccessType membersKind) {
|
||||
for ( Element memberOfClass : membersOfClass ) {
|
||||
if ( isPersistent( memberOfClass, membersKind ) ) {
|
||||
|
@ -692,6 +704,133 @@ public class AnnotationMetaEntity extends AnnotationMeta {
|
|||
}
|
||||
}
|
||||
|
||||
private void validateAssociation(Element memberOfClass) {
|
||||
final TypeMirror type = attributeType(memberOfClass);
|
||||
if ( hasAnnotation(memberOfClass, MANY_TO_ONE) ) {
|
||||
final AnnotationMirror annotation =
|
||||
castNonNull(getAnnotationMirror(memberOfClass, MANY_TO_ONE));
|
||||
validateToOneAssociation(memberOfClass, annotation, type);
|
||||
}
|
||||
else if ( hasAnnotation(memberOfClass, ONE_TO_ONE) ) {
|
||||
final AnnotationMirror annotation =
|
||||
castNonNull(getAnnotationMirror(memberOfClass, ONE_TO_ONE));
|
||||
validateToOneAssociation(memberOfClass, annotation, type);
|
||||
}
|
||||
else if ( hasAnnotation(memberOfClass, ONE_TO_MANY) ) {
|
||||
final AnnotationMirror annotation =
|
||||
castNonNull(getAnnotationMirror(memberOfClass, ONE_TO_MANY));
|
||||
validateToManyAssociation(memberOfClass, annotation, type);
|
||||
}
|
||||
else if ( hasAnnotation(memberOfClass, MANY_TO_MANY) ) {
|
||||
final AnnotationMirror annotation =
|
||||
castNonNull(getAnnotationMirror(memberOfClass, MANY_TO_MANY));
|
||||
validateToManyAssociation(memberOfClass, annotation, type);
|
||||
}
|
||||
}
|
||||
|
||||
private static TypeMirror attributeType(Element memberOfClass) {
|
||||
switch ( memberOfClass.getKind() ) {
|
||||
case METHOD:
|
||||
final ExecutableElement method = (ExecutableElement) memberOfClass;
|
||||
return method.getReturnType();
|
||||
case FIELD:
|
||||
return memberOfClass.asType();
|
||||
default:
|
||||
throw new AssertionFailure("should be a field or getter");
|
||||
}
|
||||
}
|
||||
|
||||
private void validateToOneAssociation(Element memberOfClass, AnnotationMirror annotation, TypeMirror type) {
|
||||
final TypeMirror target = (TypeMirror) getAnnotationValue(annotation, "targetEntity");
|
||||
validateAssociation(memberOfClass, annotation, target == null ? type : target);
|
||||
}
|
||||
|
||||
private void validateToManyAssociation(Element memberOfClass, AnnotationMirror annotation, TypeMirror type) {
|
||||
final TypeMirror target = (TypeMirror) getAnnotationValue(annotation, "targetEntity");
|
||||
validateAssociation(memberOfClass, annotation, target == null ? elementType(type) : target);
|
||||
}
|
||||
|
||||
private void validateAssociation(Element memberOfClass, AnnotationMirror annotation, @Nullable TypeMirror typeMirror) {
|
||||
if ( typeMirror != null ) {
|
||||
switch ( typeMirror.getKind() ) {
|
||||
case TYPEVAR:
|
||||
if ( hasAnnotation(element, ENTITY) ) {
|
||||
context.message(memberOfClass, "type '" + typeMirror + "' is a type variable",
|
||||
Diagnostic.Kind.WARNING);
|
||||
}
|
||||
break;
|
||||
case DECLARED:
|
||||
final DeclaredType assocDeclaredType = (DeclaredType) typeMirror;
|
||||
final TypeElement assocTypeElement = (TypeElement) assocDeclaredType.asElement();
|
||||
if ( !hasAnnotation(assocTypeElement, ENTITY) ) {
|
||||
context.message(memberOfClass, "type '" + assocTypeElement.getSimpleName()
|
||||
+ "' is not annotated '@Entity'",
|
||||
Diagnostic.Kind.WARNING);
|
||||
}
|
||||
final String mappedBy = (String) getAnnotationValue(annotation, "mappedBy");
|
||||
if ( mappedBy != null && !mappedBy.isEmpty() ) {
|
||||
if ( mappedBy.equals("<error>") ) {
|
||||
return;
|
||||
// throw new ProcessLaterException();
|
||||
}
|
||||
if ( mappedBy.indexOf('.')>0 ) {
|
||||
//we don't know how to handle paths yet
|
||||
return;
|
||||
}
|
||||
final List<? extends Element> members =
|
||||
context.getElementUtils().getAllMembers(assocTypeElement);
|
||||
final AnnotationValue annotationVal =
|
||||
castNonNull(getAnnotationValueRef(annotation, "mappedBy"));
|
||||
if ( members.stream().noneMatch(m -> propertyName(this, m).contentEquals(mappedBy)) ) {
|
||||
context.message(memberOfClass, annotation,
|
||||
annotationVal,
|
||||
"no matching member in '" + assocTypeElement.getSimpleName() + "'",
|
||||
Diagnostic.Kind.ERROR);
|
||||
}
|
||||
else {
|
||||
final Element member =
|
||||
members.stream()
|
||||
.filter(m -> propertyName(this, m).contentEquals(mappedBy))
|
||||
.findFirst().get();
|
||||
if ( hasAnnotation(member, MANY_TO_ONE) ) {
|
||||
final TypeMirror backType = attributeType(member);
|
||||
if ( !context.getTypeUtils().isSameType(backType, element.asType()) ) {
|
||||
context.message(memberOfClass, annotation, annotationVal,
|
||||
"member '" + member.getSimpleName()
|
||||
+ "' of '" + assocTypeElement.getSimpleName()
|
||||
+ "' is not of type '" + element.getSimpleName() + "'",
|
||||
Diagnostic.Kind.WARNING);
|
||||
}
|
||||
}
|
||||
else if ( hasAnnotation(member, MANY_TO_MANY) ) {
|
||||
final TypeMirror backType = elementType( attributeType(member) );
|
||||
if ( backType != null ) {
|
||||
if ( !context.getTypeUtils().isSameType(backType, element.asType()) ) {
|
||||
context.message(memberOfClass, annotation, annotationVal,
|
||||
"member '" + member.getSimpleName()
|
||||
+ "' of '" + assocTypeElement.getSimpleName()
|
||||
+ "' is not of type '" + element.getSimpleName() + "'",
|
||||
Diagnostic.Kind.WARNING);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
context.message(memberOfClass, annotation, annotationVal,
|
||||
"member '" + member.getSimpleName()
|
||||
+ "' of '" + assocTypeElement.getSimpleName()
|
||||
+ "' is not annotated '@ManyToMany' or '@ManyToOne'",
|
||||
Diagnostic.Kind.WARNING);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
context.message(memberOfClass, "type '" + typeMirror + "' is not an entity type",
|
||||
Diagnostic.Kind.WARNING);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isPersistent(Element memberOfClass, AccessType membersKind) {
|
||||
return ( entityAccessTypeInfo.getAccessType() == membersKind
|
||||
|| determineAnnotationSpecifiedAccessType( memberOfClass ) != null )
|
||||
|
@ -948,6 +1087,27 @@ public class AnnotationMetaEntity extends AnnotationMeta {
|
|||
}
|
||||
}
|
||||
|
||||
private @Nullable TypeMirror elementType(TypeMirror parameterType) {
|
||||
switch ( parameterType.getKind() ) {
|
||||
case DECLARED:
|
||||
final DeclaredType declaredType = (DeclaredType) parameterType;
|
||||
List<? extends TypeMirror> typeArguments = declaredType.getTypeArguments();
|
||||
switch ( typeArguments.size() ) {
|
||||
case 1:
|
||||
return typeArguments.get(0);
|
||||
case 2:
|
||||
return typeArguments.get(1);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
case ARRAY:
|
||||
final ArrayType arrayType = (ArrayType) parameterType;
|
||||
return arrayType.getComponentType();
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static String lifecycleOperation(ExecutableElement method) {
|
||||
if ( hasAnnotation(method, JD_INSERT) ) {
|
||||
return "insert";
|
||||
|
|
|
@ -9,7 +9,6 @@ package org.hibernate.processor.annotation;
|
|||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.hibernate.processor.Context;
|
||||
import org.hibernate.processor.util.Constants;
|
||||
import org.hibernate.processor.util.TypeUtils;
|
||||
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
|
@ -23,6 +22,7 @@ import javax.lang.model.util.Types;
|
|||
|
||||
import static org.hibernate.processor.util.TypeUtils.getTargetEntity;
|
||||
import static org.hibernate.processor.util.TypeUtils.isBasicAttribute;
|
||||
import static org.hibernate.processor.util.TypeUtils.isPropertyGetter;
|
||||
import static org.hibernate.processor.util.TypeUtils.toArrayTypeString;
|
||||
import static org.hibernate.processor.util.TypeUtils.toTypeString;
|
||||
|
||||
|
@ -81,7 +81,7 @@ public class DataMetaAttributeGenerationVisitor extends SimpleTypeVisitor8<@Null
|
|||
|
||||
@Override
|
||||
public @Nullable DataAnnotationMetaAttribute visitExecutable(ExecutableType executable, Element element) {
|
||||
return TypeUtils.isPropertyGetter( executable, element )
|
||||
return isPropertyGetter( executable, element )
|
||||
? executable.getReturnType().accept(this, element)
|
||||
: null;
|
||||
}
|
||||
|
|
|
@ -11,7 +11,6 @@ import org.hibernate.processor.Context;
|
|||
import org.hibernate.processor.util.AccessType;
|
||||
import org.hibernate.processor.util.AccessTypeInformation;
|
||||
import org.hibernate.processor.util.Constants;
|
||||
import org.hibernate.processor.util.TypeUtils;
|
||||
|
||||
import javax.lang.model.element.AnnotationMirror;
|
||||
import javax.lang.model.element.Element;
|
||||
|
@ -40,6 +39,7 @@ import static org.hibernate.processor.util.TypeUtils.getAnnotationMirror;
|
|||
import static org.hibernate.processor.util.TypeUtils.getAnnotationValue;
|
||||
import static org.hibernate.processor.util.TypeUtils.getCollectionElementType;
|
||||
import static org.hibernate.processor.util.TypeUtils.getKeyType;
|
||||
import static org.hibernate.processor.util.TypeUtils.getTargetEntity;
|
||||
import static org.hibernate.processor.util.TypeUtils.hasAnnotation;
|
||||
import static org.hibernate.processor.util.TypeUtils.isBasicAttribute;
|
||||
import static org.hibernate.processor.util.TypeUtils.isPropertyGetter;
|
||||
|
@ -86,7 +86,7 @@ public class MetaAttributeGenerationVisitor extends SimpleTypeVisitor8<@Nullable
|
|||
// WARNING: .toString() is necessary here since Name equals does not compare to String
|
||||
final String returnTypeName = returnedElement.getQualifiedName().toString();
|
||||
final String collection = Constants.COLLECTIONS.get( returnTypeName );
|
||||
final String targetEntity = TypeUtils.getTargetEntity( element.getAnnotationMirrors() );
|
||||
final String targetEntity = getTargetEntity( element.getAnnotationMirrors() );
|
||||
if ( collection != null ) {
|
||||
return createMetaCollectionAttribute( declaredType, element, returnTypeName, collection, targetEntity );
|
||||
}
|
||||
|
@ -103,7 +103,7 @@ public class MetaAttributeGenerationVisitor extends SimpleTypeVisitor8<@Nullable
|
|||
DeclaredType declaredType, Element element, String returnTypeName, String collection,
|
||||
@Nullable String targetEntity) {
|
||||
if ( hasAnnotation( element, ELEMENT_COLLECTION ) ) {
|
||||
final String explicitTargetEntity = TypeUtils.getTargetEntity( element.getAnnotationMirrors() );
|
||||
final String explicitTargetEntity = getTargetEntity( element.getAnnotationMirrors() );
|
||||
final TypeMirror collectionElementType =
|
||||
getCollectionElementType( declaredType, returnTypeName, explicitTargetEntity, context );
|
||||
if ( collectionElementType.getKind() == TypeKind.DECLARED ) {
|
||||
|
|
Loading…
Reference in New Issue