/**
  * INTERNAL: Visit a generic type parameter (either to a field or method) e.g Collection<X>,
  * the type parameter being X.
  */
 @Override
 public MetadataClass visitTypeParameter(
     TypeParameterElement typeParameterElement, MetadataClass metadataClass) {
   metadataClass.setName(typeParameterElement.getSimpleName().toString());
   metadataClass.setType(TypeVisitor.GENERIC_TYPE);
   return metadataClass;
 }
  /** INTERNAL: Visit an executable and create a MetadataMethod object. */
  @Override
  public MetadataMethod visitExecutable(
      ExecutableElement executableElement, MetadataClass metadataClass) {
    MetadataMethod method = new MetadataMethod(metadataClass.getMetadataFactory(), metadataClass);

    // Set the name.
    method.setName(executableElement.getSimpleName().toString());

    // Set the attribute name.
    method.setAttributeName(Helper.getAttributeNameFromMethodName(method.getName()));

    // Set the modifiers.
    method.setModifiers(getModifiers(executableElement.getModifiers()));

    // Visit executable element for the parameters, return type and generic type.
    executableElement.asType().accept(typeVisitor, method);

    // Set the annotations.
    buildMetadataAnnotations(method, executableElement.getAnnotationMirrors());

    // Handle multiple methods with the same name.
    MetadataMethod existing = metadataClass.getMethods().get(method.getName());
    if (existing == null) {
      metadataClass.addMethod(method);
    } else {
      while (existing.getNext() != null) {
        existing = existing.getNext();
      }
      existing.setNext(method);
    }

    return method;
  }
  /** INTERNAL: */
  @Override
  public MetadataClass visitType(TypeElement typeElement, MetadataClass metadataClass) {
    // processingEnv.getMessager().printMessage(Kind.NOTE, "Visiting class: " + typeElement);
    MetadataMirrorFactory factory = ((MetadataMirrorFactory) metadataClass.getMetadataFactory());

    // Set the qualified name.
    metadataClass.setName(typeElement.getQualifiedName().toString());

    // By default, set the type to be the same as the name, which in most
    // cases is correct. For non JDK elements we'll visit the typeElement
    // further (see below). This will further process any generic types,
    // e.g. Employee<Integer>. For the most part I don't think we need to
    // care, certainly not with JDK classes but for round elements we'll
    // set them anyway.
    metadataClass.setType(metadataClass.getName());

    // Add the interfaces.
    for (TypeMirror interfaceCls : typeElement.getInterfaces()) {
      metadataClass.addInterface(factory.getMetadataClass(interfaceCls).getName());
    }

    // Set the superclass name (if there is one)
    TypeMirror superclass = typeElement.getSuperclass();
    if (superclass != null) {
      metadataClass.setSuperclassName(factory.getMetadataClass(superclass).getName());
    }

    // As a performance gain, limit what is visited by JDK elements.
    if (!metadataClass.isJDK()) {
      // Set the modifiers.
      metadataClass.setModifiers(getModifiers(typeElement.getModifiers()));

      // Visit the type element for type and generic type.
      typeElement.asType().accept(typeVisitor, metadataClass);

      // Visit the enclosed elements.
      for (Element enclosedElement : typeElement.getEnclosedElements()) {
        if (enclosedElement.getKind().isClass()) {
          metadataClass.addEnclosedClass(factory.getMetadataClass(enclosedElement));
        } else {
          enclosedElement.accept(this, metadataClass);
        }
      }

      // Visit the annotations only if it is a round element.
      buildMetadataAnnotations(metadataClass, typeElement.getAnnotationMirrors());
    }

    return metadataClass;
  }
 /** INTERNAL: Every converter needs to be able to process themselves. */
 public void process(
     DatabaseMapping mapping,
     MappingAccessor accessor,
     MetadataClass referenceClass,
     boolean isForMapKey) {
   // Set the field classification type on the mapping based on the
   // referenceClass type.
   if (isValidClobType(referenceClass)) {
     setFieldClassification(mapping, java.sql.Clob.class, isForMapKey);
     setConverter(mapping, new TypeConversionConverter(mapping), isForMapKey);
   } else if (isValidBlobType(referenceClass)) {
     setFieldClassification(mapping, java.sql.Blob.class, isForMapKey);
     setConverter(mapping, new TypeConversionConverter(mapping), isForMapKey);
   } else if (referenceClass.extendsInterface(Serializable.class)) {
     setFieldClassification(mapping, java.sql.Blob.class, isForMapKey);
     setConverter(mapping, new SerializedObjectConverter(mapping), isForMapKey);
   } else {
     // The referenceClass is neither a valid BLOB or CLOB attribute.
     throw ValidationException.invalidTypeForLOBAttribute(
         mapping.getAttributeName(), referenceClass, accessor.getJavaClass());
   }
 }
  /** INTERNAL: Visit a variable and create a MetadataField object. */
  @Override
  public MetadataField visitVariable(VariableElement variableElement, MetadataClass metadataClass) {
    MetadataField field = new MetadataField(metadataClass);

    // Set the name.
    field.setName(variableElement.getSimpleName().toString());

    // Set the attribute name (same as name in this case)
    field.setAttributeName(field.getName());

    // Visit the variable element for type and generic type.
    variableElement.asType().accept(typeVisitor, field);

    // Set the modifiers.
    field.setModifiers(getModifiers(variableElement.getModifiers()));

    // Set the annotations.
    buildMetadataAnnotations(field, variableElement.getAnnotationMirrors());

    // Add the field to the class and return the field.
    metadataClass.addField(field);

    return field;
  }
 /** Return if a given class is annotated with @Entity. */
 public static boolean isEntity(MetadataClass candidateClass) {
   return candidateClass.isAnnotationPresent(JPA_ENTITY);
 }
 /** Return if a given class is annotated with @Embeddable. */
 public static boolean isEmbeddable(MetadataClass candidateClass) {
   return candidateClass.isAnnotationPresent(JPA_EMBEDDABLE);
 }
 /** Return if a given class is annotated with @Converter. */
 public static boolean isConverter(MetadataClass candidateClass) {
   return candidateClass.isAnnotationPresent(JPA_CONVERTER);
 }
 /** Return the @StaticMetamodel annotation on the given class. */
 public static MetadataAnnotation getStaticMetamodelAnnotation(MetadataClass candidateClass) {
   return candidateClass.getAnnotation(JPA_STATIC_METAMODEL);
 }
 /** Return if a given class is annotated with @Entity. */
 public static MetadataAnnotation getMappedSuperclassAnnotation(MetadataClass candidateClass) {
   return candidateClass.getAnnotation(JPA_MAPPED_SUPERCLASS);
 }
 /** Return if a given class is annotated with @Entity. */
 public static MetadataAnnotation getEntityAnnotation(MetadataClass candidateClass) {
   return candidateClass.getAnnotation(JPA_ENTITY);
 }
 /** Return if a given class is annotated with @Embeddable. */
 public static MetadataAnnotation getConverterAnnotation(MetadataClass candidateClass) {
   return candidateClass.getAnnotation(JPA_CONVERTER);
 }
 /** INTERNAL: Returns true if the given class is a valid clob type. */
 public static boolean isValidClobType(MetadataClass cls) {
   return cls.equals(char[].class)
       || cls.equals(String.class)
       || cls.equals(Character[].class)
       || cls.equals(java.sql.Clob.class);
 }
 /** INTERNAL: Returns true if the given class is a valid blob type. */
 public static boolean isValidBlobType(MetadataClass cls) {
   return cls.equals(byte[].class) || cls.equals(Byte[].class) || cls.equals(java.sql.Blob.class);
 }
 /** Return if a given class is annotated with @StaticMetamodel. */
 public static boolean isStaticMetamodelClass(MetadataClass candidateClass) {
   return candidateClass.isAnnotationPresent(JPA_STATIC_METAMODEL);
 }
 /** Return if a given class is annotated with @MappedSuperclass. */
 public static boolean isMappedSuperclass(MetadataClass candidateClass) {
   return candidateClass.isAnnotationPresent(JPA_MAPPED_SUPERCLASS);
 }
 /** Return if a given class is annotated with @Embeddable. */
 public static MetadataAnnotation getEmbeddableAnnotation(MetadataClass candidateClass) {
   return candidateClass.getAnnotation(JPA_EMBEDDABLE);
 }