All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.ebean.querybean.generator.SimpleQueryBeanWriter Maven / Gradle / Ivy

There is a newer version: 15.8.0
Show newest version
package io.ebean.querybean.generator;


import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.TypeMirror;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

/**
 * A simple implementation that generates and writes query beans.
 */
class SimpleQueryBeanWriter {

  private static final String[] javaTypes = {
    "java.lang.String",
    "java.lang.Integer",
    "java.lang.Long",
    "java.lang.Double",
    "java.lang.Float",
    "java.lang.Short",
    "java.lang.Boolean",
    "java.lang.Byte",
    "java.lang.Char"
  };

  private static final String[] kotlinTypes = {
    "kotlin.String",
    "kotlin.Int",
    "kotlin.Long",
    "kotlin.Double",
    "kotlin.Float",
    "kotlin.Short",
    "kotlin.Boolean",
    "kotlin.Byte",
    "kotlin.Char"
  };

  // These are special classes under Kotlin, and are auto-imported, same as
  // java.lang under Java
  private static final Set kotlinBlackListedImports = Collections.unmodifiableSet(
    new HashSet<>(
      Arrays.asList(
        "java.util.ArrayList",
        "java.util.HashMap",
        "java.util.HashSet",
        "java.util.LinkedHashMap",
        "java.util.LinkedHashSet",
        "java.util.List",
        "java.util.Map",
        "java.util.Set"
      )
    )
  );

  private final Set importTypes = new TreeSet<>();
  private final List properties = new ArrayList<>();
  private final TypeElement element;
  private final TypeElement implementsInterface;
  private String implementsInterfaceFullName;
  private String implementsInterfaceShortName;
  private final ProcessingContext processingContext;
  private final boolean isEntity;
  private final boolean embeddable;
  private final String dbName;
  private final String beanFullName;
  private final LangAdapter langAdapter;
  private boolean writingAssocBean;
  private final String generatedSourcesDir;

  private String destPackage;
  private String origDestPackage;
  private String shortName;
  private final String shortInnerName;
  private final String origShortName;
  private Append writer;

  SimpleQueryBeanWriter(TypeElement element, ProcessingContext processingContext) {
    this.langAdapter = new KotlinLangAdapter();
    this.generatedSourcesDir = processingContext.generatedSourcesDir();
    this.element = element;
    this.processingContext = processingContext;
    this.beanFullName = element.getQualifiedName().toString();
    boolean nested = element.getNestingKind().isNested();
    this.destPackage = Util.packageOf(nested, beanFullName) + ".query";
    String sn = Util.shortName(nested, beanFullName);
    this.shortInnerName = Util.shortName(false, sn);
    this.shortName = sn.replace(".", "");
    this.origShortName = shortName;
    this.isEntity = processingContext.isEntity(element);
    this.embeddable = processingContext.isEmbeddable(element);
    this.dbName = findDbName();
    this.implementsInterface = initInterface(element);
  }

  private TypeElement initInterface(TypeElement element) {
    for (TypeMirror anInterface : element.getInterfaces()) {
      TypeElement e = (TypeElement)processingContext.asElement(anInterface);
      String name = e.getQualifiedName().toString();
      if (!name.startsWith("java") && !name.startsWith("io.ebean")) {
        return e;
      }
    }
    return null;
  }

  private String findDbName() {
    return processingContext.findDbName(element);
  }

  private LangAdapter lang() {
    return langAdapter;
  }

  private void gatherPropertyDetails() {
    if (implementsInterface != null) {
      implementsInterfaceFullName = implementsInterface.getQualifiedName().toString();
      boolean nested = implementsInterface.getNestingKind().isNested();
      implementsInterfaceShortName = Util.shortName(nested, implementsInterfaceFullName);
    }
    addClassProperties();
  }

  /**
   * Recursively add properties from the inheritance hierarchy.
   * 

* Includes properties from mapped super classes and usual inheritance. *

*/ private void addClassProperties() { for (VariableElement field : processingContext.allFields(element)) { PropertyType type = processingContext.getPropertyType(field); if (type != null) { type.addImports(importTypes); properties.add(new PropertyMeta(field.getSimpleName().toString(), type)); } } } /** * Write the type query bean (root bean). */ void writeRootBean() throws IOException { gatherPropertyDetails(); if (isEmbeddable()) { processingContext.addEntity(beanFullName, dbName); } else if (isEntity()) { processingContext.addEntity(beanFullName, dbName); writer = new Append(createFileWriter()); translateKotlinImportTypes(); writePackage(); writeImports(); writeClass(); writeAlias(); writeFields(); writeConstructors(); //writeStaticAliasClass(); writeClassEnd(); writer.close(); } } /** * Translate the base types (String, Integer etc) to Kotlin types. */ private void translateKotlinImportTypes() { for (int i = 0; i < javaTypes.length; i++) { if (importTypes.remove(javaTypes[i])) { importTypes.add(kotlinTypes[i]); } } importTypes.removeAll(kotlinBlackListedImports); } private boolean isEntity() { return isEntity; } private boolean isEmbeddable() { return embeddable; } /** * Write the type query assoc bean. */ void writeAssocBean() throws IOException { writingAssocBean = true; origDestPackage = destPackage; destPackage = destPackage + ".assoc"; shortName = "Assoc" + shortName; prepareAssocBeanImports(); writer = new Append(createFileWriter()); writePackage(); writeImports(); writeClass(); writeFields(); writeConstructors(); writeClassEnd(); writer.close(); } /** * Prepare the imports for writing assoc bean. */ private void prepareAssocBeanImports() { if (embeddable) { importTypes.add(Constants.TQASSOC); } else { importTypes.add(Constants.TQASSOCBEAN); } if (isEntity()) { importTypes.add(Constants.TQPROPERTY); importTypes.add(origDestPackage + ".Q" + origShortName); //if (implementsInterface != null) { // importTypes.add(Constants.AVAJE_LANG_NULLABLE); // importTypes.add(Constants.JAVA_COLLECTION); // importTypes.add(implementsInterfaceFullName); //} } // remove imports for the same package Iterator importsIterator = importTypes.iterator(); String checkImportStart = destPackage + ".QAssoc"; while (importsIterator.hasNext()) { String importType = importsIterator.next(); if (importType.startsWith(checkImportStart)) { importsIterator.remove(); } } } /** * Write constructors. */ private void writeConstructors() { if (writingAssocBean) { writeAssocBeanFetch(); writeAssocBeanConstructor(); } else { writeRootBeanConstructor(); } } /** * Write the constructors for 'root' type query bean. */ private void writeRootBeanConstructor() { lang().rootBeanConstructor(writer, shortName, dbName, beanFullName); } private void writeAssocBeanFetch() { if (isEntity()) { //if (implementsInterface != null) { // writeAssocBeanExpression(false, "eq", "Is equal to by ID property."); // writeAssocBeanExpression(true, "eqIfPresent", "Is equal to by ID property if the value is not null, if null no expression is added."); // writeAssocBeanExpression(false, "in", "IN the given values.", implementsInterfaceShortName + "...", "in"); // writeAssocBeanExpression(false, "inBy", "IN the given interface values.", "Collection<" + implementsInterfaceShortName + ">", "in"); // writeAssocBeanExpression(true, "inOrEmptyBy", "IN the given interface values if the collection is not empty. No expression is added if the collection is empty..", "Collection<" + implementsInterfaceShortName + ">", "inOrEmpty"); //} } } private void writeAssocBeanExpression(boolean nullable,String expression, String comment) { writeAssocBeanExpression(nullable, expression, comment, implementsInterfaceShortName, expression); } private void writeAssocBeanExpression(boolean nullable, String expression, String comment, String param, String actualExpression) { final String nullableAnnotation = nullable ? "@Nullable " : ""; String values = expression.startsWith("in") ? "values" : "value"; writer.append(" /**").eol(); writer.append(" * ").append(comment).eol(); writer.append(" */").eol(); writer.append(" fun %s(%s%s %s): R {", expression, nullableAnnotation, param, values).eol(); writer.append(" expr().%s(_name, %s);", actualExpression, values).eol(); writer.append(" return _root;").eol(); writer.append(" }").eol(); writer.eol(); } /** * Write constructor for 'assoc' type query bean. */ private void writeAssocBeanConstructor() { lang().assocBeanConstructor(writer, shortName); } /** * Write all the fields. */ private void writeFields() { for (PropertyMeta property : properties) { String typeDefn = property.getTypeDefn(shortName, writingAssocBean); lang().fieldDefn(writer, property.getName(), typeDefn); writer.eol(); } writer.eol(); } /** * Write the class definition. */ private void writeClass() { if (writingAssocBean) { writer.append("/**").eol(); writer.append(" * Association query bean for %s.", shortName).eol(); writer.append(" * ").eol(); writer.append(" * THIS IS A GENERATED OBJECT, DO NOT MODIFY THIS CLASS.").eol(); writer.append(" */").eol(); writer.append(Constants.AT_GENERATED).eol(); writer.append(Constants.AT_TYPEQUERYBEAN).eol(); if (embeddable) { writer.append("class Q%s : TQAssoc<%s,R> {", shortName, beanFullName).eol(); } else { writer.append("class Q%s : TQAssocBean<%s,R,Q%s> {", shortName, beanFullName, origShortName).eol(); } } else { writer.append("/**").eol(); writer.append(" * Query bean for %s.", shortName).eol(); writer.append(" * ").eol(); writer.append(" * THIS IS A GENERATED OBJECT, DO NOT MODIFY THIS CLASS.").eol(); writer.append(" */").eol(); writer.append(Constants.AT_GENERATED).eol(); writer.append(Constants.AT_TYPEQUERYBEAN).eol(); writer.append("class Q%s : io.ebean.typequery.QueryBean<%s, Q%s> {", shortName, beanFullName, shortName).eol(); } writer.eol(); } private void writeAlias() { if (!writingAssocBean) { lang().alias(writer, shortName, beanFullName); } } private void writeClassEnd() { writer.append("}").eol(); } /** * Write all the imports. */ private void writeImports() { for (String importType : importTypes) { writer.append("import %s;", importType).eol(); } writer.eol(); } private void writePackage() { writer.append("package %s;", destPackage).eol().eol(); } private Writer createFileWriter() throws IOException { String relPath = destPackage.replace('.', '/'); File absDir = new File(generatedSourcesDir, relPath); if (!absDir.exists() && !absDir.mkdirs()) { processingContext.logNote("failed to create directories for:" + absDir.getAbsolutePath()); } String fullPath = relPath + "/Q" + shortName + ".kt"; File absFile = new File(generatedSourcesDir, fullPath); return new FileWriter(absFile); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy