<commit_before>package org.avaje.ebean.querybean.generator;

import com.avaje.ebean.annotation.DbArray;
import com.avaje.ebean.annotation.DbJson;
import com.avaje.ebean.annotation.DbJsonB;

import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.ElementFilter;
import javax.lang.model.util.Types;
import javax.persistence.Entity;
import javax.persistence.MappedSuperclass;
import javax.tools.Diagnostic;
import javax.tools.FileObject;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.StandardLocation;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

/**
 * Context for the source generation.
 */
public class ProcessingContext {

  private static final String EBEAN_TYPEQUERY_MF = "ebean-typequery.mf";

  private static final String META_INF = "META-INF";

  private static final String NEWLINE = "\n";

  private final Types typeUtils;

  private final Filer filer;

  private final Messager messager;

  private final PropertyTypeMap propertyTypeMap = new PropertyTypeMap();

  /**
   * The set of packages that query beans are generated into.
   */
  private final Set<String> packages = new LinkedHashSet<>();

  private boolean writeOnce;

  public ProcessingContext(ProcessingEnvironment processingEnv) {
    this.typeUtils = processingEnv.getTypeUtils();
    this.filer = processingEnv.getFiler();
    this.messager = processingEnv.getMessager();
  }

  /**
   * Gather all the fields (properties) for the given bean element.
   */
  public List<VariableElement> allFields(Element element) {

    List<VariableElement> list = new ArrayList<>();
    gatherProperties(list, element);
    return list;
  }

  /**
   * Recursively gather all the fields (properties) for the given bean element.
   */
  protected void gatherProperties(List<VariableElement> fields, Element element) {

    TypeElement typeElement = (TypeElement) element;
    TypeMirror superclass = typeElement.getSuperclass();
    Element mappedSuper = typeUtils.asElement(superclass);
    if (isMappedSuper(mappedSuper)) {
      gatherProperties(fields, mappedSuper);
    }

    List<VariableElement> allFields = ElementFilter.fieldsIn(element.getEnclosedElements());
    for (VariableElement field : allFields) {
      if (!ignoreField(field)) {
        fields.add(field);
      }
    }
  }

  /**
   * Not interested in static, transient or Ebean internal fields.
   */
  private boolean ignoreField(VariableElement field) {
    return isStaticOrTransient(field) || ignoreEbeanInternalFields(field);
  }

  private boolean ignoreEbeanInternalFields(VariableElement field) {
    String fieldName = field.getSimpleName().toString();
    return fieldName.startsWith("_ebean") || fieldName.startsWith("_EBEAN");
  }

  private boolean isStaticOrTransient(VariableElement field) {
    Set<Modifier> modifiers = field.getModifiers();
    return (modifiers.contains(Modifier.STATIC) || modifiers.contains(Modifier.TRANSIENT));
  }

  private boolean isMappedSuper(Element mappedSuper) {
    return mappedSuper.getAnnotation(MappedSuperclass.class) != null;
  }

  private boolean isEntity(Element mappedSuper) {
    return mappedSuper.getAnnotation(Entity.class) != null;
  }

  /**
   * Return true if it is a DbJson field.
   */
  public static boolean dbJsonField(Element field) {
    return (field.getAnnotation(DbJson.class) != null
        || field.getAnnotation(DbJsonB.class) != null);
  }

  /**
   * Return true if it is a DbArray field.
   */
  public static boolean dbArrayField(Element field) {
    return (field.getAnnotation(DbArray.class) != null);
  }

  public PropertyType getPropertyType(VariableElement field) {

    TypeMirror typeMirror = field.asType();

    PropertyType type = propertyTypeMap.getType(typeMirror.toString());
    if (type != null) {
      // simple scalar type
      return type;
    }

    if (dbJsonField(field)) {
      return propertyTypeMap.getDbJsonType();
    }

    if (dbArrayField(field)) {
      // get generic parameter type
      DeclaredType declaredType = (DeclaredType)typeMirror;
      String fullType = declaredType.getTypeArguments().get(0).toString();
      return new PropertyTypeArray(fullType, Split.shortName(fullType));
    }

    Element fieldType = typeUtils.asElement(typeMirror);

    if (fieldType != null) {
      if (fieldType.getKind() == ElementKind.ENUM) {
        String fullType = typeMirror.toString();
        return new PropertyTypeEnum(fullType, Split.shortName(fullType));
      }

      if (isEntity(fieldType)) {
        //  public QAssocContact<QCustomer> contacts;
        return createPropertyTypeAssoc(typeMirror.toString());
      }

      if (typeMirror.getKind() == TypeKind.DECLARED) {
        DeclaredType declaredType = (DeclaredType) typeMirror;
        List<? extends TypeMirror> typeArguments = declaredType.getTypeArguments();
        if (typeArguments.size() == 1) {
          TypeMirror argType = typeArguments.get(0);
          Element argElement = typeUtils.asElement(argType);
          if (isEntity(argElement)) {
            return createPropertyTypeAssoc(argElement.asType().toString());
          }
        }
      }
    }

    return null;
  }

  /**
   * Create the QAssoc PropertyType.
   */
  private PropertyType createPropertyTypeAssoc(String fullName) {

    String[] split = Split.split(fullName);
    String propertyName = "QAssoc" + split[1];
    String packageName = packageAppend(split[0], "query.assoc");
    return new PropertyTypeAssoc(propertyName, packageName);
  }

  /**
   * Prepend the package to the suffix taking null into account.
   */
  private String packageAppend(String origPackage, String suffix) {
    if (origPackage == null) {
      return suffix;
    } else {
      return origPackage + "." + suffix;
    }<commit_msg>#8 - Support @EmbeddedId properties ... (these are not generated)<commit_after>package org.avaje.ebean.querybean.generator;

import com.avaje.ebean.annotation.DbArray;
import com.avaje.ebean.annotation.DbJson;
import com.avaje.ebean.annotation.DbJsonB;

import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.ElementFilter;
import javax.lang.model.util.Types;
import javax.persistence.Embeddable;
import javax.persistence.Entity;
import javax.persistence.MappedSuperclass;
import javax.tools.Diagnostic;
import javax.tools.FileObject;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.StandardLocation;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

/**
 * Context for the source generation.
 */
public class ProcessingContext {

  private static final String EBEAN_TYPEQUERY_MF = "ebean-typequery.mf";

  private static final String META_INF = "META-INF";

  private static final String NEWLINE = "\n";

  private final Types typeUtils;

  private final Filer filer;

  private final Messager messager;

  private final PropertyTypeMap propertyTypeMap = new PropertyTypeMap();

  /**
   * The set of packages that query beans are generated into.
   */
  private final Set<String> packages = new LinkedHashSet<>();

  private boolean writeOnce;

  public ProcessingContext(ProcessingEnvironment processingEnv) {
    this.typeUtils = processingEnv.getTypeUtils();
    this.filer = processingEnv.getFiler();
    this.messager = processingEnv.getMessager();
  }

  /**
   * Gather all the fields (properties) for the given bean element.
   */
  public List<VariableElement> allFields(Element element) {

    List<VariableElement> list = new ArrayList<>();
    gatherProperties(list, element);
    return list;
  }

  /**
   * Recursively gather all the fields (properties) for the given bean element.
   */
  protected void gatherProperties(List<VariableElement> fields, Element element) {

    TypeElement typeElement = (TypeElement) element;
    TypeMirror superclass = typeElement.getSuperclass();
    Element mappedSuper = typeUtils.asElement(superclass);
    if (isMappedSuper(mappedSuper)) {
      gatherProperties(fields, mappedSuper);
    }

    List<VariableElement> allFields = ElementFilter.fieldsIn(element.getEnclosedElements());
    for (VariableElement field : allFields) {
      if (!ignoreField(field)) {
        fields.add(field);
      }
    }
  }

  /**
   * Not interested in static, transient or Ebean internal fields.
   */
  private boolean ignoreField(VariableElement field) {
    return isStaticOrTransient(field) || ignoreEbeanInternalFields(field);
  }

  private boolean ignoreEbeanInternalFields(VariableElement field) {
    String fieldName = field.getSimpleName().toString();
    return fieldName.startsWith("_ebean") || fieldName.startsWith("_EBEAN");
  }

  private boolean isStaticOrTransient(VariableElement field) {
    Set<Modifier> modifiers = field.getModifiers();
    return (modifiers.contains(Modifier.STATIC) || modifiers.contains(Modifier.TRANSIENT));
  }

  private boolean isMappedSuper(Element mappedSuper) {
    return mappedSuper.getAnnotation(MappedSuperclass.class) != null;
  }

  private boolean isEntityOrEmbedded(Element mappedSuper) {
    return mappedSuper.getAnnotation(Entity.class) != null
        || mappedSuper.getAnnotation(Embeddable.class) != null;
  }

  /**
   * Return true if it is a DbJson field.
   */
  public static boolean dbJsonField(Element field) {
    return (field.getAnnotation(DbJson.class) != null
        || field.getAnnotation(DbJsonB.class) != null);
  }

  /**
   * Return true if it is a DbArray field.
   */
  public static boolean dbArrayField(Element field) {
    return (field.getAnnotation(DbArray.class) != null);
  }

  public PropertyType getPropertyType(VariableElement field) {

    TypeMirror typeMirror = field.asType();

    PropertyType type = propertyTypeMap.getType(typeMirror.toString());
    if (type != null) {
      // simple scalar type
      return type;
    }

    if (dbJsonField(field)) {
      return propertyTypeMap.getDbJsonType();
    }

    if (dbArrayField(field)) {
      // get generic parameter type
      DeclaredType declaredType = (DeclaredType)typeMirror;
      String fullType = declaredType.getTypeArguments().get(0).toString();
      return new PropertyTypeArray(fullType, Split.shortName(fullType));
    }

    Element fieldType = typeUtils.asElement(typeMirror);

    if (fieldType != null) {
      if (fieldType.getKind() == ElementKind.ENUM) {
        String fullType = typeMirror.toString();
        return new PropertyTypeEnum(fullType, Split.shortName(fullType));
      }

      if (isEntityOrEmbedded(fieldType)) {
        //  public QAssocContact<QCustomer> contacts;
        return createPropertyTypeAssoc(typeMirror.toString());
      }

      if (typeMirror.getKind() == TypeKind.DECLARED) {
        DeclaredType declaredType = (DeclaredType) typeMirror;
        List<? extends TypeMirror> typeArguments = declaredType.getTypeArguments();
        if (typeArguments.size() == 1) {
          TypeMirror argType = typeArguments.get(0);
          Element argElement = typeUtils.asElement(argType);
          if (isEntityOrEmbedded(argElement)) {
            return createPropertyTypeAssoc(argElement.asType().toString());
          }
        }
      }
    }

    return null;
  }

  /**
   * Create the QAssoc PropertyType.
   */
  private PropertyType createPropertyTypeAssoc(String fullName) {

    String[] split = Split.split(fullName);
    String propertyName = "QAssoc" + split[1];
    String packageName = packageAppend(split[0], "query.assoc");
    return new PropertyTypeAssoc(propertyName, packageName);
  }

  /**
   * Prepend the package to the suffix taking null into account.
   */
  private String packageAppend(String origPackage, String suffix) {
    if (origPackage == null) {
      return suffix;
    } else {
      return origPackage + "." + suffix;
    }
{'fraction_non_alphanumeric': 0.10455534825870647, 'fraction_numerical': 0.0008550995024875622, 'mean_word_length': 3.85105580693816, 'pattern_counts': {'":': 0, '<': 23, '<?xml version=': 0, '>': 23, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1696042', 'n_tokens_mistral': 3693, 'n_tokens_neox': 3586, 'n_words': 841}