Ejemplo n.º 1
0
  /**
   * Validates that a table definition (annotated, interface, or both) matches the current state of
   * the table and indexes in the database. Results are returned as a list of validation remarks
   * which includes recommendations, warnings, and errors about the model. The caller may choose to
   * have validate throw an exception on any validation ERROR.
   *
   * @param def the table definition
   * @param throwError whether or not to throw an exception if an error was found
   * @return a list if validation remarks
   */
  <T> List<ValidationRemark> validate(TableDefinition<T> def, boolean throwError) {
    List<ValidationRemark> remarks = Utils.newArrayList();

    // model class definition validation
    if (!Modifier.isPublic(def.getModelClass().getModifiers())) {
      remarks.add(
          error(
                  table,
                  "SCHEMA",
                  format("Class {0} MUST BE PUBLIC!", def.getModelClass().getCanonicalName()))
              .throwError(throwError));
    }

    // Schema Validation
    if (!isNullOrEmpty(schema)) {
      if (isNullOrEmpty(def.schemaName)) {
        remarks.add(
            consider(
                table, "SCHEMA", format("@{0}(\"{1}\")", IQSchema.class.getSimpleName(), schema)));
      } else if (!schema.equalsIgnoreCase(def.schemaName)) {
        remarks.add(
            error(
                    table,
                    "SCHEMA",
                    format(
                        "@{0}(\"{1}\") != {2}",
                        IQSchema.class.getSimpleName(), def.schemaName, schema))
                .throwError(throwError));
      }
    }

    // index validation
    for (IndexInspector index : indexes.values()) {
      validate(remarks, def, index, throwError);
    }

    // field column validation
    for (FieldDefinition fieldDef : def.getFields()) {
      validate(remarks, fieldDef, throwError);
    }
    return remarks;
  }
Ejemplo n.º 2
0
  /**
   * Reads the DatabaseMetaData for the details of this table including primary keys and indexes.
   *
   * @param metaData the database meta data
   */
  void read(DatabaseMetaData metaData) throws SQLException {
    ResultSet rs = null;

    // primary keys
    try {
      rs = metaData.getPrimaryKeys(null, schema, table);
      while (rs.next()) {
        String c = rs.getString("COLUMN_NAME");
        primaryKeys.add(c);
      }
      closeSilently(rs);

      // indexes
      rs = metaData.getIndexInfo(null, schema, table, false, true);
      indexes = Utils.newHashMap();
      while (rs.next()) {
        IndexInspector info = new IndexInspector(rs);
        if (info.type.equals(IndexType.UNIQUE)) {
          String name = info.name.toLowerCase();
          if (name.startsWith("primary")
              || name.startsWith("sys_idx_sys_pk")
              || name.startsWith("sql")
              || name.endsWith("_pkey")) {
            // skip primary key indexes
            continue;
          }
        }
        if (indexes.containsKey(info.name)) {
          indexes.get(info.name).addColumn(rs);
        } else {
          indexes.put(info.name, info);
        }
      }
      closeSilently(rs);

      // columns
      rs = metaData.getColumns(null, schema, table, null);
      columns = Utils.newHashMap();
      while (rs.next()) {
        ColumnInspector col = new ColumnInspector();
        col.name = rs.getString("COLUMN_NAME");
        col.type = rs.getString("TYPE_NAME");
        col.clazz = ModelUtils.getClassForSqlType(col.type, dateTimeClass);
        col.size = rs.getInt("COLUMN_SIZE");
        col.nullable = rs.getInt("NULLABLE") == DatabaseMetaData.columnNullable;
        try {
          Object autoIncrement = rs.getObject("IS_AUTOINCREMENT");
          if (autoIncrement instanceof Boolean) {
            col.isAutoIncrement = (Boolean) autoIncrement;
          } else if (autoIncrement instanceof String) {
            String val = autoIncrement.toString().toLowerCase();
            col.isAutoIncrement = val.equals("true") | val.equals("yes");
          } else if (autoIncrement instanceof Number) {
            Number n = (Number) autoIncrement;
            col.isAutoIncrement = n.intValue() > 0;
          }
        } catch (SQLException s) {
          //					throw s;
        }
        if (primaryKeys.size() == 1) {
          if (col.name.equalsIgnoreCase(primaryKeys.get(0))) {
            col.isPrimaryKey = true;
          }
        }
        if (!col.isAutoIncrement) {
          col.defaultValue = rs.getString("COLUMN_DEF");
        }
        columns.put(col.name.toLowerCase(), col);
      }
    } finally {
      closeSilently(rs);
    }
  }
Ejemplo n.º 3
0
/**
 * Class to inspect the contents of a particular table including its indexes. This class does the
 * bulk of the work in terms of model generation and model validation.
 */
public class TableInspector {

  private String schema;
  private String table;
  private Class<? extends java.util.Date> dateTimeClass;
  private List<String> primaryKeys = Utils.newArrayList();
  private Map<String, IndexInspector> indexes;
  private Map<String, ColumnInspector> columns;
  private final String eol = "\n";

  TableInspector(String schema, String table, Class<? extends java.util.Date> dateTimeClass) {
    this.schema = schema;
    this.table = table;
    this.dateTimeClass = dateTimeClass;
  }

  /**
   * Tests to see if this TableInspector represents schema.table.
   *
   * <p>
   *
   * @param schema the schema name
   * @param table the table name
   * @return true if the table matches
   */
  boolean matches(String schema, String table) {
    if (isNullOrEmpty(schema)) {
      // table name matching
      return this.table.equalsIgnoreCase(table);
    } else if (isNullOrEmpty(table)) {
      // schema name matching
      return this.schema.equalsIgnoreCase(schema);
    } else {
      // exact table matching
      return this.schema.equalsIgnoreCase(schema) && this.table.equalsIgnoreCase(table);
    }
  }

  /**
   * Reads the DatabaseMetaData for the details of this table including primary keys and indexes.
   *
   * @param metaData the database meta data
   */
  void read(DatabaseMetaData metaData) throws SQLException {
    ResultSet rs = null;

    // primary keys
    try {
      rs = metaData.getPrimaryKeys(null, schema, table);
      while (rs.next()) {
        String c = rs.getString("COLUMN_NAME");
        primaryKeys.add(c);
      }
      closeSilently(rs);

      // indexes
      rs = metaData.getIndexInfo(null, schema, table, false, true);
      indexes = Utils.newHashMap();
      while (rs.next()) {
        IndexInspector info = new IndexInspector(rs);
        if (info.type.equals(IndexType.UNIQUE)) {
          String name = info.name.toLowerCase();
          if (name.startsWith("primary")
              || name.startsWith("sys_idx_sys_pk")
              || name.startsWith("sql")
              || name.endsWith("_pkey")) {
            // skip primary key indexes
            continue;
          }
        }
        if (indexes.containsKey(info.name)) {
          indexes.get(info.name).addColumn(rs);
        } else {
          indexes.put(info.name, info);
        }
      }
      closeSilently(rs);

      // columns
      rs = metaData.getColumns(null, schema, table, null);
      columns = Utils.newHashMap();
      while (rs.next()) {
        ColumnInspector col = new ColumnInspector();
        col.name = rs.getString("COLUMN_NAME");
        col.type = rs.getString("TYPE_NAME");
        col.clazz = ModelUtils.getClassForSqlType(col.type, dateTimeClass);
        col.size = rs.getInt("COLUMN_SIZE");
        col.nullable = rs.getInt("NULLABLE") == DatabaseMetaData.columnNullable;
        try {
          Object autoIncrement = rs.getObject("IS_AUTOINCREMENT");
          if (autoIncrement instanceof Boolean) {
            col.isAutoIncrement = (Boolean) autoIncrement;
          } else if (autoIncrement instanceof String) {
            String val = autoIncrement.toString().toLowerCase();
            col.isAutoIncrement = val.equals("true") | val.equals("yes");
          } else if (autoIncrement instanceof Number) {
            Number n = (Number) autoIncrement;
            col.isAutoIncrement = n.intValue() > 0;
          }
        } catch (SQLException s) {
          //					throw s;
        }
        if (primaryKeys.size() == 1) {
          if (col.name.equalsIgnoreCase(primaryKeys.get(0))) {
            col.isPrimaryKey = true;
          }
        }
        if (!col.isAutoIncrement) {
          col.defaultValue = rs.getString("COLUMN_DEF");
        }
        columns.put(col.name.toLowerCase(), col);
      }
    } finally {
      closeSilently(rs);
    }
  }

  /**
   * Generates a model (class definition) from this table. The model includes indexes, primary keys,
   * default values, lengths, and nullables. information.
   *
   * <p>The caller may optionally set a destination package name, whether or not to include the
   * schema name (setting schema can be a problem when using the model between databases), and if to
   * automatically trim strings for those that have a maximum length.
   *
   * <p>
   *
   * @param packageName
   * @param annotateSchema
   * @param trimStrings
   * @return a complete model (class definition) for this table as a string
   */
  String generateModel(String packageName, boolean annotateSchema, boolean trimStrings) {

    // import statements
    Set<String> imports = Utils.newHashSet();
    imports.add(Serializable.class.getCanonicalName());
    imports.add(IQSchema.class.getCanonicalName());
    imports.add(IQTable.class.getCanonicalName());
    imports.add(IQIndexes.class.getCanonicalName());
    imports.add(IQIndex.class.getCanonicalName());
    imports.add(IQColumn.class.getCanonicalName());
    imports.add(IndexType.class.getCanonicalName());

    // fields
    StringBuilder fields = new StringBuilder();
    List<ColumnInspector> sortedColumns = Utils.newArrayList(columns.values());
    Collections.sort(sortedColumns);
    for (ColumnInspector col : sortedColumns) {
      fields.append(generateColumn(imports, col, trimStrings));
    }

    // build complete class definition
    StringBuilder model = new StringBuilder();
    if (!isNullOrEmpty(packageName)) {
      // package
      model.append("package " + packageName + ";");
      model.append(eol).append(eol);
    }

    // imports
    List<String> sortedImports = new ArrayList<String>(imports);
    Collections.sort(sortedImports);
    for (String imp : sortedImports) {
      model.append("import ").append(imp).append(';').append(eol);
    }
    model.append(eol);

    // @IQSchema
    if (annotateSchema && !isNullOrEmpty(schema)) {
      model.append('@').append(IQSchema.class.getSimpleName());
      model.append('(');
      AnnotationBuilder ap = new AnnotationBuilder();
      ap.addParameter(null, schema);
      model.append(ap);
      model.append(')').append(eol);
    }

    // @IQTable
    model.append('@').append(IQTable.class.getSimpleName());
    model.append('(');

    // IQTable annotation parameters
    AnnotationBuilder ap = new AnnotationBuilder();
    ap.addParameter("name", table);

    if (primaryKeys.size() > 1) {
      ap.addParameter("primaryKey", primaryKeys);
    }

    // finish @IQTable annotation
    model.append(ap);
    model.append(')').append(eol);

    // @IQIndexes
    // @IQIndex
    String indexAnnotations = generateIndexAnnotations();
    if (!StringUtils.isNullOrEmpty(indexAnnotations)) {
      model.append(indexAnnotations);
    }

    // class declaration
    String clazzName = ModelUtils.convertTableToClassName(table);
    model.append(format("public class {0} implements Serializable '{'", clazzName)).append(eol);
    model.append(eol);
    model.append("\tprivate static final long serialVersionUID = 1L;").append(eol);
    model.append(eol);

    // field declarations
    model.append(fields);

    // default constructor
    model.append("\t" + "public ").append(clazzName).append("() {").append(eol);
    model.append("\t}").append(eol);

    // end of class body
    model.append('}');
    model.trimToSize();
    return model.toString();
  }

  /**
   * Generates the specified index annotation.
   *
   * @param ap
   */
  String generateIndexAnnotations() {
    if (indexes == null || indexes.size() == 0) {
      // no matching indexes
      return null;
    }
    AnnotationBuilder ap = new AnnotationBuilder();
    if (indexes.size() == 1) {
      // single index
      IndexInspector index = indexes.values().toArray(new IndexInspector[1])[0];
      ap.append(generateIndexAnnotation(index));
      ap.append(eol);
    } else {
      // multiple indexes
      ap.append('@').append(IQIndexes.class.getSimpleName());
      ap.append("({");
      ap.resetCount();
      for (IndexInspector index : indexes.values()) {
        ap.appendExceptFirst(", ");
        ap.append(generateIndexAnnotation(index));
      }
      ap.append("})").append(eol);
    }
    return ap.toString();
  }

  private String generateIndexAnnotation(IndexInspector index) {
    AnnotationBuilder ap = new AnnotationBuilder();
    ap.append('@').append(IQIndex.class.getSimpleName());
    ap.append('(');
    ap.resetCount();
    if (!StringUtils.isNullOrEmpty(index.name)) {
      ap.addParameter("name", index.name);
    }
    if (!index.type.equals(IndexType.STANDARD)) {
      ap.addEnum("type", index.type);
    }
    if (ap.getCount() > 0) {
      // multiple fields specified
      ap.addParameter("value", index.columns);
    } else {
      // default value
      ap.addParameter(null, index.columns);
    }
    ap.append(')');
    return ap.toString();
  }

  private StatementBuilder generateColumn(
      Set<String> imports, ColumnInspector col, boolean trimStrings) {
    StatementBuilder sb = new StatementBuilder();
    Class<?> clazz = col.clazz;
    String column = ModelUtils.convertColumnToFieldName(col.name.toLowerCase());
    sb.append('\t');
    if (clazz == null) {
      // unsupported type
      clazz = Object.class;
      sb.append("// unsupported type " + col.type);
    } else {
      // Imports
      // don't import primitives, java.lang classes, or byte []
      if (clazz.getPackage() == null) {
      } else if (clazz.getPackage().getName().equals("java.lang")) {
      } else if (clazz.equals(byte[].class)) {
      } else {
        imports.add(clazz.getCanonicalName());
      }
      // @IQColumn
      sb.append('@').append(IQColumn.class.getSimpleName());

      // IQColumn annotation parameters
      AnnotationBuilder ap = new AnnotationBuilder();

      // IQColumn.name
      if (!col.name.equalsIgnoreCase(column)) {
        ap.addParameter("name", col.name);
      }

      // IQColumn.primaryKey
      // composite primary keys are annotated on the table
      if (col.isPrimaryKey && primaryKeys.size() == 1) {
        ap.addParameter("primaryKey=true");
      }

      // IQColumn.length
      if ((clazz == String.class) && (col.size > 0) && (col.size < Integer.MAX_VALUE)) {
        ap.addParameter("length", col.size);

        // IQColumn.trim
        if (trimStrings) {
          ap.addParameter("trim=true");
        }
      } else {
        // IQColumn.AutoIncrement
        if (col.isAutoIncrement) {
          ap.addParameter("autoIncrement=true");
        }
      }

      // IQColumn.nullable
      if (!col.nullable) {
        ap.addParameter("nullable=false");
      }

      // IQColumn.defaultValue
      if (!isNullOrEmpty(col.defaultValue)) {
        ap.addParameter("defaultValue=\"" + col.defaultValue + "\"");
      }

      // add leading and trailing ()
      if (ap.length() > 0) {
        ap.insert(0, '(');
        ap.append(')');
      }
      sb.append(ap);
    }
    sb.append(eol);

    // variable declaration
    sb.append("\t" + "public ");
    sb.append(clazz.getSimpleName());
    sb.append(' ');
    sb.append(column);
    sb.append(';');
    sb.append(eol).append(eol);
    return sb;
  }

  /**
   * Validates that a table definition (annotated, interface, or both) matches the current state of
   * the table and indexes in the database. Results are returned as a list of validation remarks
   * which includes recommendations, warnings, and errors about the model. The caller may choose to
   * have validate throw an exception on any validation ERROR.
   *
   * @param def the table definition
   * @param throwError whether or not to throw an exception if an error was found
   * @return a list if validation remarks
   */
  <T> List<ValidationRemark> validate(TableDefinition<T> def, boolean throwError) {
    List<ValidationRemark> remarks = Utils.newArrayList();

    // model class definition validation
    if (!Modifier.isPublic(def.getModelClass().getModifiers())) {
      remarks.add(
          error(
                  table,
                  "SCHEMA",
                  format("Class {0} MUST BE PUBLIC!", def.getModelClass().getCanonicalName()))
              .throwError(throwError));
    }

    // Schema Validation
    if (!isNullOrEmpty(schema)) {
      if (isNullOrEmpty(def.schemaName)) {
        remarks.add(
            consider(
                table, "SCHEMA", format("@{0}(\"{1}\")", IQSchema.class.getSimpleName(), schema)));
      } else if (!schema.equalsIgnoreCase(def.schemaName)) {
        remarks.add(
            error(
                    table,
                    "SCHEMA",
                    format(
                        "@{0}(\"{1}\") != {2}",
                        IQSchema.class.getSimpleName(), def.schemaName, schema))
                .throwError(throwError));
      }
    }

    // index validation
    for (IndexInspector index : indexes.values()) {
      validate(remarks, def, index, throwError);
    }

    // field column validation
    for (FieldDefinition fieldDef : def.getFields()) {
      validate(remarks, fieldDef, throwError);
    }
    return remarks;
  }

  /**
   * Validates an inspected index from the database against the IndexDefinition within the
   * TableDefinition.
   */
  private <T> void validate(
      List<ValidationRemark> remarks,
      TableDefinition<T> def,
      IndexInspector index,
      boolean throwError) {
    List<IndexDefinition> defIndexes = def.getIndexes();
    if (defIndexes.size() > indexes.size()) {
      remarks.add(
          warn(table, IndexType.STANDARD.name(), "More model indexes  than database indexes"));
    } else if (defIndexes.size() < indexes.size()) {
      remarks.add(warn(table, IndexType.STANDARD.name(), "Model class is missing indexes"));
    }
    // TODO complete index validation.
    // need to actually compare index types and columns within each index.

    // TODO add constraints validation
    List<ConstraintUniqueDefinition> defContraintsU = def.getContraintsUnique();
    List<ConstraintForeignKeyDefinition> defContraintsFK = def.getContraintsForeignKey();
  }

  /**
   * Validates a column against the model's field definition. Checks for existence, supported type,
   * type mapping, default value, defined lengths, primary key, autoincrement.
   */
  private void validate(
      List<ValidationRemark> remarks, FieldDefinition fieldDef, boolean throwError) {
    // unknown field
    if (!columns.containsKey(fieldDef.columnName.toLowerCase())) {
      // unknown column mapping
      remarks.add(error(table, fieldDef, "Does not exist in database!").throwError(throwError));
      return;
    }
    ColumnInspector col = columns.get(fieldDef.columnName.toLowerCase());
    Class<?> fieldClass = fieldDef.field.getType();
    Class<?> jdbcClass = ModelUtils.getClassForSqlType(col.type, dateTimeClass);

    // supported type check
    // iciql maps to VARCHAR for unsupported types.
    if (fieldDef.dataType.equals("VARCHAR") && (fieldClass != String.class)) {
      remarks.add(
          error(
                  table,
                  fieldDef,
                  "iciql does not currently implement support for " + fieldClass.getName())
              .throwError(throwError));
    }
    // number types
    if (!fieldClass.equals(jdbcClass)) {
      if (Number.class.isAssignableFrom(fieldClass)) {
        remarks.add(
            warn(
                table,
                col,
                format(
                    "Precision mismatch: ModelObject={0}, ColumnObject={1}",
                    fieldClass.getSimpleName(), jdbcClass.getSimpleName())));
      } else {
        if (!Date.class.isAssignableFrom(jdbcClass)) {
          remarks.add(
              warn(
                  table,
                  col,
                  format(
                      "Object Mismatch: ModelObject={0}, ColumnObject={1}",
                      fieldClass.getSimpleName(), jdbcClass.getSimpleName())));
        }
      }
    }

    // string types
    if (fieldClass == String.class) {
      if ((fieldDef.length != col.size) && (col.size < Integer.MAX_VALUE)) {
        remarks.add(
            warn(
                table,
                col,
                format(
                    "{0}.length={1}, ColumnMaxLength={2}",
                    IQColumn.class.getSimpleName(), fieldDef.length, col.size)));
      }
      if (fieldDef.length > 0 && !fieldDef.trim) {
        remarks.add(
            consider(
                table,
                col,
                format(
                    "{0}.trim=true will prevent IciqlExceptions on"
                        + " INSERT or UPDATE, but will clip data!",
                    IQColumn.class.getSimpleName())));
      }
    }

    // numeric autoIncrement
    if (fieldDef.isAutoIncrement != col.isAutoIncrement) {
      remarks.add(
          warn(
              table,
              col,
              format(
                  "{0}.autoIncrement={1}" + " while Column autoIncrement={2}",
                  IQColumn.class.getSimpleName(), fieldDef.isAutoIncrement, col.isAutoIncrement)));
    }
    // default value
    if (!col.isAutoIncrement && !col.isPrimaryKey) {
      String defaultValue = null;
      if (fieldDef.defaultValue != null && fieldDef.defaultValue instanceof String) {
        defaultValue = fieldDef.defaultValue.toString();
      }
      // check Model.defaultValue format
      if (!ModelUtils.isProperlyFormattedDefaultValue(defaultValue)) {
        remarks.add(
            error(
                    table,
                    col,
                    format(
                        "{0}.defaultValue=\"{1}\"" + " is improperly formatted!",
                        IQColumn.class.getSimpleName(), defaultValue))
                .throwError(throwError));
        // next field
        return;
      }
      // compare Model.defaultValue to Column.defaultValue
      if (isNullOrEmpty(defaultValue) && !isNullOrEmpty(col.defaultValue)) {
        // Model.defaultValue is NULL, Column.defaultValue is NOT NULL
        remarks.add(
            warn(
                table,
                col,
                format(
                    "{0}.defaultValue=\"\"" + " while column default=\"{1}\"",
                    IQColumn.class.getSimpleName(), col.defaultValue)));
      } else if (!isNullOrEmpty(defaultValue) && isNullOrEmpty(col.defaultValue)) {
        // Column.defaultValue is NULL, Model.defaultValue is NOT NULL
        remarks.add(
            warn(
                table,
                col,
                format(
                    "{0}.defaultValue=\"{1}\"" + " while column default=\"\"",
                    IQColumn.class.getSimpleName(), defaultValue)));
      } else if (!isNullOrEmpty(defaultValue) && !isNullOrEmpty(col.defaultValue)) {
        if (!defaultValue.equals(col.defaultValue)) {
          // Model.defaultValue != Column.defaultValue
          remarks.add(
              warn(
                  table,
                  col,
                  format(
                      "{0}.defaultValue=\"{1}\"" + " while column default=\"{2}\"",
                      IQColumn.class.getSimpleName(), defaultValue, col.defaultValue)));
        }
      }

      // sanity check Model.defaultValue literal value
      if (!ModelUtils.isValidDefaultValue(fieldDef.field.getType(), defaultValue)) {
        remarks.add(
            error(
                table,
                col,
                format(
                    "{0}.defaultValue=\"{1}\" is invalid!",
                    IQColumn.class.getSimpleName(), defaultValue)));
      }
    }
  }

  /** Represents an index as it exists in the database. */
  private static class IndexInspector {

    String name;
    IndexType type;
    private List<String> columns = new ArrayList<String>();

    public IndexInspector(ResultSet rs) throws SQLException {
      name = rs.getString("INDEX_NAME");

      // determine index type
      boolean hash = rs.getInt("TYPE") == DatabaseMetaData.tableIndexHashed;
      boolean unique = !rs.getBoolean("NON_UNIQUE");

      if (!hash && !unique) {
        type = IndexType.STANDARD;
      } else if (hash && unique) {
        type = IndexType.UNIQUE_HASH;
      } else if (unique) {
        type = IndexType.UNIQUE;
      } else if (hash) {
        type = IndexType.HASH;
      }
      columns.add(rs.getString("COLUMN_NAME"));
    }

    public void addColumn(ResultSet rs) throws SQLException {
      columns.add(rs.getString("COLUMN_NAME"));
    }
  }

  /** Represents a column as it exists in the database. */
  static class ColumnInspector implements Comparable<ColumnInspector> {
    String name;
    String type;
    int size;
    boolean nullable;
    Class<?> clazz;
    boolean isPrimaryKey;
    boolean isAutoIncrement;
    String defaultValue;

    public int compareTo(ColumnInspector o) {
      if (isPrimaryKey && o.isPrimaryKey) {
        // both primary sort by name
        return name.compareTo(o.name);
      } else if (isPrimaryKey && !o.isPrimaryKey) {
        // primary first
        return -1;
      } else if (!isPrimaryKey && o.isPrimaryKey) {
        // primary first
        return 1;
      } else {
        // neither primary, sort by name
        return name.compareTo(o.name);
      }
    }
  }

  /** Convenience class based on StatementBuilder for creating the annotation parameter list. */
  private static class AnnotationBuilder extends StatementBuilder {

    AnnotationBuilder() {
      super();
    }

    void addParameter(String parameter) {

      appendExceptFirst(", ");
      append(parameter);
    }

    <T> void addParameter(String parameter, T value) {
      appendExceptFirst(", ");
      if (!StringUtils.isNullOrEmpty(parameter)) {
        append(parameter);
        append('=');
      }
      if (value instanceof List) {
        append("{ ");
        List<?> list = (List<?>) value;
        StatementBuilder flat = new StatementBuilder();
        for (Object o : list) {
          flat.appendExceptFirst(", ");
          if (o instanceof String) {
            flat.append('\"');
          }
          // TODO escape string
          flat.append(o.toString().trim());
          if (o instanceof String) {
            flat.append('\"');
          }
        }
        append(flat);
        append(" }");
      } else {
        if (value instanceof String) {
          append('\"');
        }
        // TODO escape
        append(value.toString().trim());
        if (value instanceof String) {
          append('\"');
        }
      }
    }

    void addEnum(String parameter, Enum<?> value) {
      appendExceptFirst(", ");
      if (!StringUtils.isNullOrEmpty(parameter)) {
        append(parameter);
        append('=');
      }
      append(value.getClass().getSimpleName() + "." + value.name());
    }
  }
}
Ejemplo n.º 4
0
  /**
   * Generates a model (class definition) from this table. The model includes indexes, primary keys,
   * default values, lengths, and nullables. information.
   *
   * <p>The caller may optionally set a destination package name, whether or not to include the
   * schema name (setting schema can be a problem when using the model between databases), and if to
   * automatically trim strings for those that have a maximum length.
   *
   * <p>
   *
   * @param packageName
   * @param annotateSchema
   * @param trimStrings
   * @return a complete model (class definition) for this table as a string
   */
  String generateModel(String packageName, boolean annotateSchema, boolean trimStrings) {

    // import statements
    Set<String> imports = Utils.newHashSet();
    imports.add(Serializable.class.getCanonicalName());
    imports.add(IQSchema.class.getCanonicalName());
    imports.add(IQTable.class.getCanonicalName());
    imports.add(IQIndexes.class.getCanonicalName());
    imports.add(IQIndex.class.getCanonicalName());
    imports.add(IQColumn.class.getCanonicalName());
    imports.add(IndexType.class.getCanonicalName());

    // fields
    StringBuilder fields = new StringBuilder();
    List<ColumnInspector> sortedColumns = Utils.newArrayList(columns.values());
    Collections.sort(sortedColumns);
    for (ColumnInspector col : sortedColumns) {
      fields.append(generateColumn(imports, col, trimStrings));
    }

    // build complete class definition
    StringBuilder model = new StringBuilder();
    if (!isNullOrEmpty(packageName)) {
      // package
      model.append("package " + packageName + ";");
      model.append(eol).append(eol);
    }

    // imports
    List<String> sortedImports = new ArrayList<String>(imports);
    Collections.sort(sortedImports);
    for (String imp : sortedImports) {
      model.append("import ").append(imp).append(';').append(eol);
    }
    model.append(eol);

    // @IQSchema
    if (annotateSchema && !isNullOrEmpty(schema)) {
      model.append('@').append(IQSchema.class.getSimpleName());
      model.append('(');
      AnnotationBuilder ap = new AnnotationBuilder();
      ap.addParameter(null, schema);
      model.append(ap);
      model.append(')').append(eol);
    }

    // @IQTable
    model.append('@').append(IQTable.class.getSimpleName());
    model.append('(');

    // IQTable annotation parameters
    AnnotationBuilder ap = new AnnotationBuilder();
    ap.addParameter("name", table);

    if (primaryKeys.size() > 1) {
      ap.addParameter("primaryKey", primaryKeys);
    }

    // finish @IQTable annotation
    model.append(ap);
    model.append(')').append(eol);

    // @IQIndexes
    // @IQIndex
    String indexAnnotations = generateIndexAnnotations();
    if (!StringUtils.isNullOrEmpty(indexAnnotations)) {
      model.append(indexAnnotations);
    }

    // class declaration
    String clazzName = ModelUtils.convertTableToClassName(table);
    model.append(format("public class {0} implements Serializable '{'", clazzName)).append(eol);
    model.append(eol);
    model.append("\tprivate static final long serialVersionUID = 1L;").append(eol);
    model.append(eol);

    // field declarations
    model.append(fields);

    // default constructor
    model.append("\t" + "public ").append(clazzName).append("() {").append(eol);
    model.append("\t}").append(eol);

    // end of class body
    model.append('}');
    model.trimToSize();
    return model.toString();
  }