Package org.hibernate.jpamodelgen.annotation

Source Code of org.hibernate.jpamodelgen.annotation.AnnotationMetaEntity$TypeVisitor

// $Id: AnnotationMetaEntity.java 18639 2010-01-26 21:42:19Z hardy.ferentschik $
/*
* JBoss, Home of Professional Open Source
* Copyright 2008, Red Hat Middleware LLC, and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.hibernate.jpamodelgen.annotation;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
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.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.util.ElementFilter;
import javax.lang.model.util.SimpleTypeVisitor6;
import javax.persistence.Access;
import javax.persistence.AccessType;
import javax.persistence.ElementCollection;
import javax.persistence.Embeddable;
import javax.persistence.Embedded;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.MappedSuperclass;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.Transient;
import javax.tools.Diagnostic;

import org.hibernate.jpamodelgen.Context;
import org.hibernate.jpamodelgen.ImportContext;
import org.hibernate.jpamodelgen.ImportContextImpl;
import org.hibernate.jpamodelgen.MetaAttribute;
import org.hibernate.jpamodelgen.MetaEntity;
import org.hibernate.jpamodelgen.util.TypeUtils;

/**
* @author Max Andersen
* @author Hardy Ferentschik
* @author Emmanuel Bernard
*/
public class AnnotationMetaEntity implements MetaEntity {

  private static final String DEFAULT_ANNOTATION_PARAMETER_NAME = "value";
  static Map<String, String> COLLECTIONS = new HashMap<String, String>();

  static {
    COLLECTIONS.put( "java.util.Collection", "javax.persistence.metamodel.CollectionAttribute" );
    COLLECTIONS.put( "java.util.Set", "javax.persistence.metamodel.SetAttribute" );
    COLLECTIONS.put( "java.util.List", "javax.persistence.metamodel.ListAttribute" );
    COLLECTIONS.put( "java.util.Map", "javax.persistence.metamodel.MapAttribute" );
  }

  private final TypeElement element;
  private final ImportContext importContext;
  private Context context;
  //used to propagate the access type of the root entity over to subclasses, superclasses and embeddable
  private AccessType defaultAccessTypeForHierarchy;
  private AccessType defaultAccessTypeForElement;

  public AnnotationMetaEntity(TypeElement element, Context context) {
    this.element = element;
    this.context = context;
    importContext = new ImportContextImpl( getPackageName() );
  }

  public AnnotationMetaEntity(TypeElement element, Context context, AccessType accessType) {
    this( element, context );
    this.defaultAccessTypeForHierarchy = accessType;
  }

  public Context getContext() {
    return context;
  }

  public String getSimpleName() {
    return element.getSimpleName().toString();
  }

  public String getQualifiedName() {
    return element.getQualifiedName().toString();
  }

  public String getPackageName() {
    PackageElement packageOf = context.getProcessingEnvironment().getElementUtils().getPackageOf( element );
    return context.getProcessingEnvironment().getElementUtils().getName( packageOf.getQualifiedName() ).toString();
  }

  public List<MetaAttribute> getMembers() {
    List<MetaAttribute> membersFound = new ArrayList<MetaAttribute>();
    final AccessType elementAccessType = getAccessTypeForElement();

    List<? extends Element> fieldsOfClass = ElementFilter.fieldsIn( element.getEnclosedElements() );
    addPersistentMembers( membersFound, elementAccessType, fieldsOfClass, AccessType.FIELD );

    List<? extends Element> methodsOfClass = ElementFilter.methodsIn( element.getEnclosedElements() );
    addPersistentMembers( membersFound, elementAccessType, methodsOfClass, AccessType.PROPERTY );

    //process superclasses
    for ( TypeElement superclass = TypeUtils.getSuperclassTypeElement( element );
        superclass != null;
        superclass = TypeUtils.getSuperclassTypeElement( superclass ) ) {
      if ( TypeUtils.containsAnnotation( superclass, Entity.class ) ) {
        break; //will be handled or has been handled already
      }
      else if ( TypeUtils.containsAnnotation( superclass, MappedSuperclass.class ) ) {
        //FIXME use the class default access type
        context.processElement( superclass, defaultAccessTypeForHierarchy );
      }
    }
    return membersFound;
  }

  private void addPersistentMembers(
      List<MetaAttribute> membersFound,
      AccessType elementAccessType,
      List<? extends Element> membersOfClass,
      AccessType membersKind) {
    AccessType explicitAccessType;
    if ( elementAccessType == membersKind ) {
      //all membersKind considered
      explicitAccessType = null;
    }
    else {
      //use membersKind only if marked with @Access(membersKind)
      explicitAccessType = membersKind;
    }
    for ( Element memberOfClass : membersOfClass ) {

      TypeVisitor visitor = new TypeVisitor( this, explicitAccessType );
      AnnotationMetaAttribute result = memberOfClass.asType().accept( visitor, memberOfClass );
      if ( result != null ) {
        membersFound.add( result );
      }
    }
  }

  private AccessType getAccessTypeForElement() {

    //get local strategy
    AccessType accessType = getAccessTypeForClass( element );
    if ( accessType == null ) {
      accessType = this.defaultAccessTypeForHierarchy;
    }
    if ( accessType == null ) {
      //we don't know if an entity go up
      //
      //superclasses are always treated after their entities
      //and their access type are discovered
      //FIXME is it really true if only the superclass is changed
      TypeElement superClass = element;
      do {
        superClass = TypeUtils.getSuperclassTypeElement( superClass );
        if ( superClass != null ) {
          if ( TypeUtils.containsAnnotation( superClass, Entity.class, MappedSuperclass.class ) ) {
            //FIXME make it work for XML
            AccessType superClassAccessType = getAccessTypeForClass( superClass );
            //we've reach the root entity and resolved Ids
            if ( superClassAccessType != null && defaultAccessTypeForHierarchy != null ) {
              break; //we've found it
            }
          }
          else {
            break; //neither @Entity nor @MappedSuperclass
          }
        }
      }
      while ( superClass != null );
    }

    if ( accessType == null ) {
      accessType = AccessType.PROPERTY; //default to property
      this.defaultAccessTypeForElement = accessType;
    }
    //this is a subclass so caching is OK
    //this.defaultAccessTypeForHierarchy = accessType;
    context.addAccessType( this.element, accessType );
    this.defaultAccessTypeForElement = accessType;
    return accessType;
  }

  private AccessType getAccessTypeForClass(TypeElement searchedElement) {
    context.logMessage( Diagnostic.Kind.OTHER, "check class " + searchedElement );
    AccessType accessType = context.getAccessType( searchedElement );

    if ( defaultAccessTypeForHierarchy == null ) {
      this.defaultAccessTypeForHierarchy = context.getDefaultAccessTypeForHerarchy( searchedElement );
    }
    if ( accessType != null ) {
      context.logMessage( Diagnostic.Kind.OTHER, "Found in cache" + searchedElement + ":" + accessType );
      return accessType;
    }

    /**
     * when forcing access type, we can only override the defaultAccessTypeForHierarchy
     * if we are the entity root (identified by having @Id or @EmbeddedId
     */
    AccessType forcedAccessType = determineAnnotationSpecifiedAccessType( searchedElement );
    if ( forcedAccessType != null ) {
      context.logMessage( Diagnostic.Kind.OTHER, "access type " + searchedElement + ":" + forcedAccessType );
      context.addAccessType( searchedElement, forcedAccessType );
    }

    //continue nevertheless to check if we are root and if defaultAccessTypeForHierarchy
    //should be overridden
    if ( forcedAccessType == null || defaultAccessTypeForHierarchy == null ) {
      List<? extends Element> myMembers = searchedElement.getEnclosedElements();
      for ( Element subElement : myMembers ) {
        List<? extends AnnotationMirror> entityAnnotations =
            context.getProcessingEnvironment().getElementUtils().getAllAnnotationMirrors( subElement );

        for ( Object entityAnnotation : entityAnnotations ) {
          AnnotationMirror annotationMirror = ( AnnotationMirror ) entityAnnotation;

          //FIXME consider XML
          if ( TypeUtils.isAnnotationMirrorOfType( annotationMirror, Id.class )
              || TypeUtils.isAnnotationMirrorOfType( annotationMirror, EmbeddedId.class ) ) {
            context.logMessage( Diagnostic.Kind.OTHER, "Found id on" + searchedElement );
            final ElementKind kind = subElement.getKind();
            if ( kind == ElementKind.FIELD || kind == ElementKind.METHOD ) {
              accessType = kind == ElementKind.FIELD ? AccessType.FIELD : AccessType.PROPERTY;
              //FIXME enlever in niveau
              if ( defaultAccessTypeForHierarchy == null ) {
                this.defaultAccessTypeForHierarchy = context.getDefaultAccessTypeForHerarchy(
                    searchedElement
                );
                //we've discovered the class hierarchy, let's cache it
                if ( defaultAccessTypeForHierarchy == null ) {
                  this.defaultAccessTypeForHierarchy = accessType;
                  context.addAccessTypeForHierarchy( searchedElement, defaultAccessTypeForHierarchy );
                  //FIXME should we add
                  //context.addAccessTypeForHierarchy( element, defaultAccessTypeForHierarchy );
                }
              }
              if ( forcedAccessType == null ) {
                context.addAccessType( searchedElement, accessType );
                context.logMessage(
                    Diagnostic.Kind.OTHER, "access type " + searchedElement + ":" + accessType
                );
                return accessType;
              }
              else {
                return forcedAccessType;
              }
            }
          }
        }
      }
    }
    return forcedAccessType;
  }

  private AccessType determineAnnotationSpecifiedAccessType(Element element) {
    final AnnotationMirror accessAnnotationMirror = TypeUtils.getAnnotationMirror( element, Access.class );
    AccessType forcedAccessType = null;
    if ( accessAnnotationMirror != null ) {
      Element accessElement = ( Element ) 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;
        }
        else if ( accessElement.getSimpleName().toString().equals( AccessType.FIELD.toString() ) ) {
          forcedAccessType = AccessType.FIELD;

        }
        else {
          context.logMessage( Diagnostic.Kind.ERROR, "Unexpected type for access type" );
        }
      }
    }
    return forcedAccessType;
  }

  @Override
  public String toString() {
    final StringBuilder sb = new StringBuilder();
    sb.append( "MetaEntity" );
    sb.append( "{element=" ).append( element );
    sb.append( '}' );
    return sb.toString();
  }

  class TypeVisitor extends SimpleTypeVisitor6<AnnotationMetaAttribute, Element> {

    AnnotationMetaEntity parent;

    /*
     * If {@code explicitAccessType == null}, process all members as implicit. If  {@code explicitAccessType != null}
     * only process members marked as {@code @Access(explicitAccessType)}.
     */
    private AccessType explicitAccessType;

    TypeVisitor(AnnotationMetaEntity parent, AccessType explicitAccessType) {
      this.parent = parent;
      this.explicitAccessType = explicitAccessType;
    }

    @Override
    protected AnnotationMetaAttribute defaultAction(TypeMirror e, Element p) {
      return super.defaultAction( e, p );
    }

    @Override
    public AnnotationMetaAttribute visitPrimitive(PrimitiveType t, Element element) {
      if ( isPersistent( element ) ) {
        return new AnnotationMetaSingleAttribute( parent, element, TypeUtils.toTypeString( t ) );
      }
      else {
        return null;
      }
    }

    @Override
    public AnnotationMetaAttribute visitArray(ArrayType t, Element element) {
      if ( isPersistent( element ) ) {
        return new AnnotationMetaSingleAttribute( parent, element, TypeUtils.toTypeString( t ) );
      }
      else {
        return null;
      }
    }

    private boolean isPersistent(Element element) {
      //FIXME consider XML
      boolean correctAccessType = false;
      if ( this.explicitAccessType == null ) {
        correctAccessType = true;
      }
      else {
        AccessType annotationAccessType = determineAnnotationSpecifiedAccessType( element );
        if ( explicitAccessType.equals( annotationAccessType ) ) {
          correctAccessType = true;
        }
      }
      return correctAccessType
          && !TypeUtils.containsAnnotation( element, Transient.class )
          && !element.getModifiers().contains( Modifier.TRANSIENT )
          && !element.getModifiers().contains( Modifier.STATIC );

    }

    @Override
    public AnnotationMetaAttribute visitDeclared(DeclaredType declaredType, Element element) {
      //FIXME consider XML
      if ( isPersistent( element ) ) {
        TypeElement returnedElement = ( TypeElement ) context.getProcessingEnvironment()
            .getTypeUtils()
            .asElement( declaredType );
        // WARNING: .toString() is necessary here since Name equals does not compare to String
        String fqElementName = returnedElement.getQualifiedName().toString();
        String collection = COLLECTIONS.get( fqElementName );
        String targetEntity = getTargetEntity( element.getAnnotationMirrors() );
        if ( collection != null ) {
          if ( TypeUtils.containsAnnotation( element, ElementCollection.class ) ) {
            TypeMirror collectionElementType = getCollectionElementType( declaredType, fqElementName );
            final TypeElement collectionElement = ( TypeElement ) context.getProcessingEnvironment()
                .getTypeUtils()
                .asElement( collectionElementType );
            this.parent.context.processElement(
                collectionElement,
                this.parent.defaultAccessTypeForElement
            );
          }
          if ( collection.equals( "javax.persistence.metamodel.MapAttribute" ) ) {
            return new AnnotationMetaMap(
                //FIXME support targetEntity for map's key @MapKeyClass
                parent,
                element,
                collection,
                getKeyType( declaredType ),
                getElementType( declaredType, targetEntity )
            );
          }
          else {
            return new AnnotationMetaCollection(
                parent, element, collection, getElementType( declaredType, targetEntity )
            );
          }
        }
        else {
          //FIXME Consider XML
          if ( TypeUtils.containsAnnotation( returnedElement, Embedded.class, Embeddable.class ) ) {
            this.parent.context.processElement(
                returnedElement,
                this.parent.defaultAccessTypeForElement
            );
          }
          return new AnnotationMetaSingleAttribute(
              parent, element, returnedElement.getQualifiedName().toString()
          );
        }
      }
      else {
        return null;
      }
    }

    private TypeMirror getCollectionElementType(DeclaredType t, String fqElementName) {
      TypeMirror collectionElementType;
      if ( Map.class.getCanonicalName().equals( fqElementName ) ) {
        collectionElementType = t.getTypeArguments().get( 1 );
      }
      else {
        collectionElementType = t.getTypeArguments().get( 0 );
      }
      return collectionElementType;
    }

    @Override
    public AnnotationMetaAttribute visitExecutable(ExecutableType t, Element p) {
      String string = p.getSimpleName().toString();

      // TODO: implement proper property get/is/boolean detection
      if ( string.startsWith( "get" ) || string.startsWith( "is" ) ) {
        TypeMirror returnType = t.getReturnType();

        return returnType.accept( this, p );
      }
      else {
        return null;
      }
    }
  }

  /**
   * @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, ElementCollection.class ) ) {
        fullyQualifiedTargetEntityName = getFullyQualifiedClassNameOfTargetEntity( mirror, "targetClass" );
      }
      else if ( TypeUtils.isAnnotationMirrorOfType( mirror, OneToMany.class )
          || TypeUtils.isAnnotationMirrorOfType( mirror, ManyToMany.class )
          || TypeUtils.isAnnotationMirrorOfType( mirror, ManyToOne.class )
          || TypeUtils.isAnnotationMirrorOfType( mirror, OneToOne.class ) ) {
        fullyQualifiedTargetEntityName = getFullyQualifiedClassNameOfTargetEntity( mirror, "targetEntity" );
      }
    }
    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;
  }

  public String generateImports() {
    return importContext.generateImports();
  }

  public String importType(String fqcn) {
    return importContext.importType( fqcn );
  }

  public String staticImport(String fqcn, String member) {
    return importContext.staticImport( fqcn, member );
  }

  public String importType(Name qualifiedName) {
    return importType( qualifiedName.toString() );
  }

  public TypeElement getTypeElement() {
    return element;
  }

  private String getKeyType(DeclaredType t) {
    return TypeUtils.extractClosestRealTypeAsString( t.getTypeArguments().get( 0 ), context );
  }

  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 "?";
    }
  }
}
TOP

Related Classes of org.hibernate.jpamodelgen.annotation.AnnotationMetaEntity$TypeVisitor

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.