Package org.hibernate.jpamodelgen.annotation

Source Code of org.hibernate.jpamodelgen.annotation.BasicAttributeVisitor

/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2010, Red Hat, Inc. and/or its affiliates or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors.  All third-party contributions are
* distributed under license by Red Hat, Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA  02110-1301  USA
*/
package org.hibernate.jpamodelgen.annotation;

import java.util.List;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.ArrayType;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.ExecutableType;
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.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.StringUtil;
import org.hibernate.jpamodelgen.util.TypeUtils;

/**
* @author Hardy Ferentschik
*/
public class MetaAttributeGenerationVisitor extends SimpleTypeVisitor6<AnnotationMetaAttribute, Element> {

  /**
   * FQCN of the Hibernate specific @Target annotation. We do not use the class directly to avoid depending on Hibernate
   * Core.
   */
  private static final String ORG_HIBERNATE_ANNOTATIONS_TARGET = "org.hibernate.annotations.Target";

  /**
   * FQCN of the Hibernate specific @Type annotation. We do not use the class directly to avoid depending on Hibernate
   * Core.
   */
  private static final String ORG_HIBERNATE_ANNOTATIONS_TYPE = "org.hibernate.annotations.Type";

  private final AnnotationMetaEntity entity;
  private final Context context;

  MetaAttributeGenerationVisitor(AnnotationMetaEntity entity, Context context) {
    this.entity = entity;
    this.context = context;
  }

  @Override
  public AnnotationMetaAttribute visitPrimitive(PrimitiveType t, Element element) {
    return new AnnotationMetaSingleAttribute( entity, element, TypeUtils.toTypeString( t ) );
  }

  @Override
  public AnnotationMetaAttribute visitArray(ArrayType t, Element element) {
    // METAGEN-2 - For now we handle arrays as SingularAttribute
    // The code below is an attempt to be closer to the spec and only allow byte[], Byte[], char[] and Character[]
//      AnnotationMetaSingleAttribute attribute = null;
//      TypeMirror componentMirror = t.getComponentType();
//      if ( TypeKind.CHAR.equals( componentMirror.getKind() )
//          || TypeKind.BYTE.equals( componentMirror.getKind() ) ) {
//        attribute = new AnnotationMetaSingleAttribute( entity, element, TypeUtils.toTypeString( t ) );
//      }
//      else if ( TypeKind.DECLARED.equals( componentMirror.getKind() ) ) {
//        TypeElement componentElement = ( TypeElement ) context.getProcessingEnvironment()
//            .getTypeUtils()
//            .asElement( componentMirror );
//        if ( BASIC_ARRAY_TYPES.contains( componentElement.getQualifiedName().toString() ) ) {
//          attribute = new AnnotationMetaSingleAttribute( entity, element, TypeUtils.toTypeString( t ) );
//        }
//      }
//      return attribute;
    return new AnnotationMetaSingleAttribute( entity, element, TypeUtils.toTypeString( t ) );
  }

  @Override
  public 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()
    );
  }

  @Override
  public AnnotationMetaAttribute visitDeclared(DeclaredType declaredType, Element element) {
    AnnotationMetaAttribute metaAttribute = null;
    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() );
    if ( collection != null ) {
      return createMetaCollectionAttribute(
          declaredType, element, fqNameOfReturnType, collection, targetEntity
      );
    }
    else if ( isBasicAttribute( element, returnedElement ) ) {
      String type = targetEntity != null ? targetEntity : returnedElement.getQualifiedName().toString();
      return new AnnotationMetaSingleAttribute( entity, element, type );
    }
    return metaAttribute;
  }

  private AnnotationMetaAttribute createMetaCollectionAttribute(DeclaredType declaredType, Element element, String fqNameOfReturnType, String collection, 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(
          collectionElement.getQualifiedName()
              .toString()
      );
      if ( accessTypeInfo == null ) {
        AccessType explicitAccessType = TypeUtils.determineAnnotationSpecifiedAccessType(
            collectionElement
        );
        accessTypeInfo = new AccessTypeInformation(
            collectionElement.getQualifiedName().toString(),
            explicitAccessType,
            entity.getEntityAccessTypeInfo().getAccessType()
        );
        context.addAccessTypeInformation(
            collectionElement.getQualifiedName().toString(), accessTypeInfo
        );
      }
      else {
        accessTypeInfo.setDefaultAccessType( entity.getEntityAccessTypeInfo().getAccessType() );
      }
    }
    if ( collection.equals( "javax.persistence.metamodel.MapAttribute" ) ) {
      return createAnnotationMetaAttributeForMap( declaredType, element, collection, targetEntity );
    }
    else {
      return new AnnotationMetaCollection(
          entity, element, collection, getElementType( declaredType, targetEntity )
      );
    }
  }

  @Override
  public AnnotationMetaAttribute visitExecutable(ExecutableType t, Element p) {
    if ( !p.getKind().equals( ElementKind.METHOD ) ) {
      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 ) ) {
      return true;
    }

    // METAGEN-28
    if ( TypeUtils.getAnnotationMirror( element, ORG_HIBERNATE_ANNOTATIONS_TYPE ) != null ) {
      return true;
    }

    BasicAttributeVisitor basicVisitor = new BasicAttributeVisitor( context );
    return returnedElement.asType().accept( basicVisitor, returnedElement );
  }

  private AnnotationMetaAttribute createAnnotationMetaAttributeForMap(DeclaredType declaredType, Element element, String collection, String targetEntity) {
    String keyType;
    if ( TypeUtils.containsAnnotation( element, Constants.MAP_KEY_CLASS ) ) {
      TypeMirror typeMirror = (TypeMirror) TypeUtils.getAnnotationValue(
          TypeUtils.getAnnotationMirror(
              element, Constants.MAP_KEY_CLASS
          ), 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 getElementType(DeclaredType declaredType, 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
        );
      }
      return "?";
    }
  }

  /**
   * @param annotations list of annotation mirrors.
   *
   * @return target entity class name as string or {@code null} if no targetEntity is here or if equals to void
   */
  private String getTargetEntity(List<? extends AnnotationMirror> annotations) {
    String fullyQualifiedTargetEntityName = null;
    for ( AnnotationMirror mirror : annotations ) {
      if ( TypeUtils.isAnnotationMirrorOfType( mirror, Constants.ELEMENT_COLLECTION ) ) {
        fullyQualifiedTargetEntityName = 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 ( TypeUtils.isAnnotationMirrorOfType( mirror, ORG_HIBERNATE_ANNOTATIONS_TARGET ) ) {
        fullyQualifiedTargetEntityName = getFullyQualifiedClassNameOfTargetEntity( mirror, "value" );
      }
    }
    return fullyQualifiedTargetEntityName;
  }

  private String getFullyQualifiedClassNameOfTargetEntity(AnnotationMirror mirror, String parameterName) {
    assert mirror != null;
    assert parameterName != null;

    String targetEntityName = null;
    Object parameterValue = TypeUtils.getAnnotationValue( mirror, parameterName );
    if ( parameterValue != null ) {
      TypeMirror parameterType = (TypeMirror) parameterValue;
      if ( !parameterType.getKind().equals( TypeKind.VOID ) ) {
        targetEntityName = parameterType.toString();
      }
    }
    return targetEntityName;
  }
}

/**
* 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> {

  private final Context context;

  public BasicAttributeVisitor(Context context) {
    this.context = context;
  }

  @Override
  public Boolean visitPrimitive(PrimitiveType t, Element element) {
    return Boolean.TRUE;
  }

  @Override
  public Boolean visitArray(ArrayType t, Element element) {
    TypeMirror componentMirror = t.getComponentType();
    TypeElement componentElement = (TypeElement) context.getTypeUtils().asElement( componentMirror );

    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;
    }

    if ( ElementKind.CLASS.equals( element.getKind() ) || ElementKind.INTERFACE.equals( element.getKind() ) ) {
      TypeElement typeElement = ( (TypeElement) element );
      String typeName = typeElement.getQualifiedName().toString();
      if ( Constants.BASIC_TYPES.contains( typeName ) ) {
        return Boolean.TRUE;
      }
      if ( TypeUtils.containsAnnotation( element, Constants.EMBEDDABLE ) ) {
        return Boolean.TRUE;
      }
      for ( TypeMirror mirror : typeElement.getInterfaces() ) {
        TypeElement interfaceElement = (TypeElement) context.getTypeUtils().asElement( mirror );
        if ( "java.io.Serializable".equals( interfaceElement.getQualifiedName().toString() ) ) {
          return Boolean.TRUE;
        }
      }
    }
    return Boolean.FALSE;
  }
}

TOP

Related Classes of org.hibernate.jpamodelgen.annotation.BasicAttributeVisitor

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.