public interface StructureVersionDao extends VersionDao<StructureVersion> {

  StructureVersion create(StructureVersion structureVersion, List<Long> parentIds) throws GroundException;

  @Override
  StructureVersion retrieveFromDatabase(long id) throws GroundException;

  @Override
  default Class<StructureVersion> getType() {
    return StructureVersion.class;
  }
} public class GroundException extends Exception {

  private static final long serialVersionUID = 1L;

  private final String message;
  private final ExceptionType exceptionType;

  public enum ExceptionType {
    DB("Database Exception:", "%s"),
    ITEM_NOT_FOUND("GroundItemNotFoundException", "No %s \'%s\' found."),
    VERSION_NOT_FOUND("GroundVersionNotFoundException", "No %s \'%s\' found."),
    ITEM_ALREADY_EXISTS("GroundItemAlreadyExistsException", "%s %s already exists."),
    OTHER("GroundException", "%s");

    String name;
    String description;

    ExceptionType(String name, String description) {
      this.name = name;
      this.description = description;
    }

    public String format(String... values) {
      return String.format(this.description, values);
    }
  }

  public GroundException(ExceptionType exceptionType, String... values) {
    this.exceptionType = exceptionType;
    this.message = this.exceptionType.format(values);
  }

  public GroundException(Exception exception) {
    this.exceptionType = ExceptionType.OTHER;
    this.message = this.exceptionType.format(exception.getMessage());
  }

  @Override
  public String getMessage() {
    return this.message;
  }

  public ExceptionType getExceptionType() {
    return this.exceptionType;
  }
} public enum ExceptionType {
  DB("Database Exception:", "%s"),
  ITEM_NOT_FOUND("GroundItemNotFoundException", "No %s \'%s\' found."),
  VERSION_NOT_FOUND("GroundVersionNotFoundException", "No %s \'%s\' found."),
  ITEM_ALREADY_EXISTS("GroundItemAlreadyExistsException", "%s %s already exists."),
  OTHER("GroundException", "%s");

  String name;
  String description;

  ExceptionType(String name, String description) {
    this.name = name;
    this.description = description;
  }

  public String format(String... values) {
    return String.format(this.description, values);
  }
} public class StructureVersion extends Version {

  // the id of the Structure containing this Version
  @JsonProperty("structureId")
  private final long structureId;

  // the map of attribute names to types
  @JsonProperty("attributes")
  private final Map<String, GroundType> attributes;

  /**
   * Create a new structure version.
   *
   * @param id the id of the structure version
   * @param structureId the id of the structure containing this version
   * @param attributes the attributes required by this structure version
   */
  @JsonCreator
  public StructureVersion(
                           @JsonProperty("id") long id,
                           @JsonProperty("structureId") long structureId,
                           @JsonProperty("attributes") Map<String, GroundType> attributes) {
    super(id);

    this.structureId = structureId;
    this.attributes = attributes;
  }

  public StructureVersion(long id, StructureVersion other) {
    super(id);

    this.structureId = other.structureId;
    this.attributes = other.attributes;
  }

  public long getStructureId() {
    return this.structureId;
  }

  public Map<String, GroundType> getAttributes() {
    return this.attributes;
  }

  @Override
  public boolean equals(Object other) {
    if (!(other instanceof StructureVersion)) {
      return false;
    }

    StructureVersion otherStructureVersion = (StructureVersion) other;

    return this.structureId == otherStructureVersion.structureId
             && this.attributes.equals(otherStructureVersion.attributes)
             && this.getId() == otherStructureVersion.getId();
  }
} public enum GroundType {
  STRING(String.class, "string", Types.VARCHAR) {
    @Override
    public Object parse(String str) {
      return str;
    }
  },
  INTEGER(Integer.class, "integer", Types.INTEGER) {
    @Override
    public Object parse(String str) {
      return Integer.parseInt(str);
    }
  },
  BOOLEAN(Boolean.class, "boolean", Types.BOOLEAN) {
    @Override
    public Object parse(String str) {
      return Boolean.parseBoolean(str);
    }
  },
  LONG(Long.class, "long", Types.BIGINT) {
    @Override
    public Object parse(String str) {
      return Long.parseLong(str);
    }
  };

  private final Class<?> klass;
  private final String name;
  private final int sqlType;

  GroundType(Class<?> klass, String name, int sqlType) {
    this.klass = klass;
    this.name = name;
    this.sqlType = sqlType;
  }

  /**
   * Returns the SQL type as defined in java.sql.Types corresponding to this GroundType
   *
   * @return an integer the corresponding SQL Type
   */
  public int getSqlType() {
    return sqlType;
  }

  public abstract Object parse(String str);

  public Class<?> getTypeClass() {
    return this.klass;
  }

  /**
   * Return a type based on the string name.
   *
   * @param str the name of the type
   * @return the corresponding GroundType
   * @throws GroundException no such type
   */
  @JsonCreator
  public static GroundType fromString(String str) throws GroundException {
    if (str == null) {
      return null;
    }
    try {
      return GroundType.valueOf(str.toUpperCase());
    } catch (IllegalArgumentException iae) {
      throw new GroundException(ExceptionType.OTHER, String.format("Invalid type: %s.", str));
    }
  }

  @Override
  public String toString() {
    return this.name;
  }
} @Singleton
public class IdGenerator {

  private final long prefix;
  private long versionCounter;
  private long successorCounter;
  private long itemCounter;

  // If true, only one counter will be used. If false, all three counters will be used.
  private final boolean globallyUnique;

  public IdGenerator() {
    this.prefix = 0;
    this.versionCounter = 1;
    this.successorCounter = 1;
    this.itemCounter = 1;
    this.globallyUnique = true;
  }

  /**
   * Create a unique id generator.
   *
   * @param machineId the id of this machine
   * @param numMachines the total number of machines
   * @param globallyUnique if true, only one counter will be used for all version
   */
  public IdGenerator(long machineId, long numMachines, boolean globallyUnique) {
    long machineBits = 1;
    long fence = 2;

    while (fence < numMachines) {
      fence = fence * 2;
      machineBits++;
    }

    this.prefix = machineId << (64 - machineBits);

    // NOTE: Do not change this. The version counter is set to start a 1 because 0 is the default
    // empty version.
    this.versionCounter = 1;
    this.successorCounter = 1;
    this.itemCounter = 1;

    this.globallyUnique = globallyUnique;
  }

  public synchronized long generateVersionId() {
    return prefix | this.versionCounter++;
  }

  /**
   * Generate an id for version successors.
   *
   * @return a new id
   */
  public synchronized long generateSuccessorId() {
    if (this.globallyUnique) {
      return prefix | this.versionCounter++;
    } else {
      return prefix | this.successorCounter++;
    }
  }

  /**
   * Generate an id for items.
   *
   * @return a new id
   */
  public synchronized long generateItemId() {
    if (this.globallyUnique) {
      return prefix | this.versionCounter++;
    } else {
      return prefix | this.itemCounter++;
    }
  }
} public class SqlConstants {

  /* General insert statements */
  public static final String INSERT_GENERIC_ITEM_WITH_NAME = "INSERT INTO %s (item_id, source_key, name) VALUES (%d, \'%s\', \'%s\');";
  public static final String INSERT_GENERIC_ITEM_WITHOUT_NAME = "INSERT INTO %s (item_id, source_key, name) VALUES (%d, \'%s\', null);";

  /* General select statements */
  public static final String SELECT_STAR_BY_SOURCE_KEY = "SELECT * FROM %s WHERE source_key = \'%s\'";
  public static final String SELECT_STAR_ITEM_BY_ID = "SELECT * FROM %s WHERE item_id = %d;";
  public static final String SELECT_STAR_BY_ID = "SELECT * FROM %s WHERE id = %d;";
  public static final String DELETE_BY_ID = "DELETE FROM %s WHERE id = %d";

  /* Version-specific statements */
  public static final String INSERT_VERSION = "INSERT INTO version (id) VALUES (%d);";

  /* Version Successor-specific statements */
  public static final String INSERT_VERSION_SUCCESSOR = "INSERT INTO version_successor (id, from_version_id, to_version_id) VALUES (%d, %d, %d);";
  public static final String SELECT_VERSION_SUCCESSOR = "SELECT * FROM version_successor where id = %d;";
  public static final String SELECT_VERSION_SUCCESSOR_BY_ENDPOINT = "SELECT * FROM version_successor WHERE to_version_id = %d;";
  public static final String DELETE_VERSION_SUCCESSOR = "DELETE FROM version_successor WHERE id = %d;";

  /* Version History DAG-specific statements */
  public static final String INSERT_VERSION_HISTORY_DAG_EDGE = "INSERT INTO version_history_dag (item_id, version_successor_id) VALUES (%d, %d);";
  public static final String SELECT_VERSION_HISTORY_DAG = "SELECT * FROM version_history_dag WHERE item_id = %d;";
  public static final String DELETE_SUCCESSOR_FROM_DAG = "DELETE FROM version_history_dag WHERE version_successor_id = %d;";

  /* Item-specific statements */
  public static final String INSERT_ITEM = "INSERT INTO ITEM (id) VALUES (%d);";
  public static final String INSERT_ITEM_TAG_WITH_VALUE =
    "INSERT INTO item_tag (item_id, key, value, type) VALUES (%d, " + "\'%s\', \'%s\', \'%s\');";
  public static final String INSERT_ITEM_TAG_NO_VALUE = "INSERT INTO item_tag (item_id, key, value, type) VALUES (%d, \'%s\', null, null);";
  public static final String SELECT_ITEM_TAGS = "SELECT * FROM item_tag WHERE item_id = %d;";
  public static final String SELECT_ITEM_TAGS_BY_KEY = "SELECT * FROM item_tag WHERE key = \'%s\';";

  /* Edge-specific statements */
  public static final String INSERT_EDGE_WITH_NAME =
    "INSERT INTO edge (item_id, source_key, from_node_id, to_node_id, name) VALUES (%d, \'%s\', %d, %d, \'%s\');";
  public static final String INSERT_EDGE_WITHOUT_NAME =
    "INSERT INTO edge (item_id, source_key, from_node_id, to_node_id, name) VALUES (%d, \'%s\', %d, %d, null);";
  public static final String INSERT_EDGE_VERSION = "INSERT INTO edge_version (id, edge_id, from_node_version_start_id, from_node_version_end_id, "
                                                     + "to_node_version_start_id, to_node_version_end_id) VALUES (%d, %d, %d, %d, %d, %d);";
  public static final String UPDATE_EDGE_VERSION = "UPDATE edge_version SET from_node_version_end_id = %d, to_node_version_end_id = %d WHERE id = "
                                                     + "%d;";

  /* Graph-specific statements */
  public static final String INSERT_GRAPH_VERSION = "INSERT INTO graph_version (id, graph_id) VALUES (%d, %d);";
  public static final String INSERT_GRAPH_VERSION_EDGE = "INSERT INTO graph_version_edge (graph_version_id, edge_version_id) VALUES (%d, %d);";
  public static final String SELECT_GRAPH_VERSION_EDGES = "SELECT * FROM graph_version_edge WHERE graph_version_id = %d;";
  public static final String DELETE_ALL_GRAPH_VERSION_EDGES = "DELETE FROM %s WHERE %s_version_id = %d";

  /* Node-specific statements */
  public static final String INSERT_NODE_VERSION = "INSERT INTO node_version (id, node_id) VALUES (%d, %d);";
  public static final String SELECT_NODE_VERSION_ADJACENT_LINEAGE = "SELECT * FROM lineage_edge_version WHERE from_rich_version_id = %d;";

  /* Rich Version-specific statements */
  public static final String INSERT_RICH_VERSION_WITH_REFERENCE = "INSERT INTO rich_version (id, structure_version_id, reference) VALUES (%d, %d, "
                                                                    + "\'%s\');";
  public static final String INSERT_RICH_VERSION_WITHOUT_REFERENCE = "INSERT INTO rich_version (id, structure_version_id, reference) VALUES (%d, %d, "
                                                                       + "null);";
  public static final String INSERT_RICH_VERSION_TAG_WITH_VALUE = "INSERT INTO rich_version_tag (rich_version_id, key, value, type) VALUES (%d, "
                                                                    + "\'%s\', \'%s\', \'%s\');";
  public static final String INSERT_RICH_VERSION_TAG_NO_VALUE = "INSERT INTO rich_version_tag (rich_version_id, key, value, type) VALUES (%d, "
                                                                  + "\'%s\', null, null);";
  public static final String INSERT_RICH_VERSION_EXTERNAL_PARAMETER = "INSERT INTO rich_version_external_parameter (rich_version_id, key, value) "
                                                                        + "VALUES (%d, \'%s\', \'%s\');";
  public static final String SELECT_RICH_VERSION_EXTERNAL_PARAMETERS = "SELECT * FROM rich_version_external_parameter WHERE rich_version_id = %d;";
  public static final String SELECT_RICH_VERSION_TAGS = "SELECT * FROM rich_version_tag WHERE rich_version_id = %d;";
  public static final String SELECT_RICH_VERSION_TAGS_BY_KEY = "SELECT * FROM rich_version_tag WHERE key = \'%s\';";
  public static final String DELETE_RICH_VERSION_TAGS = "DELETE FROM rich_version_tag WHERE rich_version_id = %d";
  public static final String DELETE_RICH_EXTERNAL_PARAMETERS = "DELETE FROM rich_version_external_parameter WHERE rich_version_id = %d";

  /* Structure-specific statements */
  public static final String INSERT_STRUCTURE_VERSION = "INSERT INTO structure_version (id, structure_id) VALUES (%d, %d);";
  public static final String INSERT_STRUCTURE_VERSION_ATTRIBUTE = "INSERT INTO structure_version_attribute (structure_version_id, key, type) "
                                                                    + "VALUES (%d, \'%s\', \'%s\');";
  public static final String SELECT_STRUCTURE_VERSION_ATTRIBUTES = "SELECT * FROM structure_version_attribute WHERE structure_version_id = %d;";
  public static final String DELETE_STRUCTURE_VERSION_ATTRIBUTES = "DELETE FROM structure_version_attribute WHERE structure_version_id = %d";

  /* Lineage Edge-specific statements */
  public static final String INSERT_LINEAGE_EDGE_VERSION = "INSERT INTO lineage_edge_version (id, lineage_edge_id, from_rich_version_id, "
                                                             + "to_rich_version_id, principal_id) VALUES (%d, %d, %d, %d, %d);";

  /* Lineage Graph-specific statements */
  public static final String INSERT_LINEAGE_GRAPH_VERSION = "INSERT INTO lineage_graph_version (id, lineage_graph_id) VALUES (%d, %d);";
  public static final String INSERT_LINEAGE_GRAPH_VERSION_EDGE = "INSERT INTO lineage_graph_version_edge (lineage_graph_version_id, "
                                                                   + "lineage_edge_version_id) VALUES (%d, %d);";
  public static final String SELECT_LINEAGE_GRAPH_VERSION_EDGES = "SELECT * FROM lineage_graph_version_edge WHERE lineage_graph_version_id = %d;";
} public abstract class PostgresVersionDao<T extends Version> implements VersionDao<T> {

  protected Database dbSource;
  protected IdGenerator idGenerator;

  public PostgresVersionDao(Database dbSource, IdGenerator idGenerator) {
    this.dbSource = dbSource;
    this.idGenerator = idGenerator;
  }

  @Override
  public PostgresStatements insert(T version) throws GroundException {
    PostgresStatements statements = new PostgresStatements();

    statements.append(String.format(SqlConstants.INSERT_VERSION, version.getId()));
    return statements;
  }

  @Override
  public PostgresStatements delete(long id) {
    PostgresStatements statements = new PostgresStatements();
    statements.append(String.format(SqlConstants.DELETE_BY_ID, "version", id));

    return statements;
  }
} public class PostgresStatements implements DbStatements<String> {

  List<String> statements;

  public PostgresStatements() {
    this.statements = new ArrayList<>();
  }

  public PostgresStatements(List<String> statements) {
    this.statements = statements;
  }

  @Override
  public void append(String statement) {
    this.statements.add(statement);
  }

  @Override
  public void merge(DbStatements other) {
    this.statements.addAll(other.getAllStatements());
  }

  @Override
  public List<String> getAllStatements() {
    return this.statements;
  }
} public final class PostgresUtils {

  private PostgresUtils() {
  }

  public static Executor getDbSourceHttpContext(final ActorSystem actorSystem) {
    return HttpExecution.fromThread((Executor) actorSystem.dispatchers().lookup("ground.db.context"));
  }

  public static String executeQueryToJson(Database dbSource, String sql) throws GroundException {
    Logger.debug("executeQueryToJson: {}", sql);

    try {
      Connection con = dbSource.getConnection();
      Statement stmt = con.createStatement();

      final ResultSet resultSet = stmt.executeQuery(sql);
      final long columnCount = resultSet.getMetaData().getColumnCount();
      final List<Map<String, Object>> objList = new ArrayList<>();

      while (resultSet.next()) {
        final Map<String, Object> rowData = new HashMap<>();

        for (int column = 1; column <= columnCount; column++) {
          String key = CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, resultSet.getMetaData().getColumnLabel(column));
          rowData.put(key, resultSet.getObject(column));
        }

        objList.add(rowData);
      }

      stmt.close();
      con.close();
      return GroundUtils.listToJson(objList);
    } catch (SQLException e) {
      Logger.error("ERROR:  executeQueryToJson  SQL : {} Message: {} Trace: {}", sql, e.getMessage(), e.getStackTrace());
      throw new GroundException(e);
    }
  }

  public static void executeSqlList(final Database dbSource, final PostgresStatements statements) throws GroundException {
    try {
      Connection con = dbSource.getConnection();
      con.setAutoCommit(false);
      Statement stmt = con.createStatement();

      for (final String sql : statements.getAllStatements()) {
        Logger.debug("executeSqlList sql : {}", sql);

        try {
          stmt.execute(sql);
        } catch (final SQLException e) {
          con.rollback();
          Logger.error("error:  Message: {} Trace: {}", e.getMessage(), e.getStackTrace());

          throw new GroundException(e);
        }
      }

      stmt.close();
      con.commit();
      con.close();
    } catch (SQLException e) {
      Logger.error("error:  executeSqlList SQL : {} Message: {} Trace: {}", statements.getAllStatements(), e.getMessage(), e.getStackTrace());

      throw new GroundException(e);
    }
  }
}
import com.fasterxml.jackson.databind.JsonNode;
import edu.berkeley.ground.common.dao.core.StructureVersionDao;
import edu.berkeley.ground.common.exception.GroundException;
import edu.berkeley.ground.common.exception.GroundException.ExceptionType;
import edu.berkeley.ground.common.model.core.StructureVersion;
import edu.berkeley.ground.common.model.version.GroundType;
import edu.berkeley.ground.common.util.IdGenerator;
import edu.berkeley.ground.postgres.dao.SqlConstants;
import edu.berkeley.ground.postgres.dao.version.PostgresVersionDao;
import edu.berkeley.ground.postgres.util.PostgresStatements;
import edu.berkeley.ground.postgres.util.PostgresUtils;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import play.db.Database;
import play.libs.Json;
/**
 * 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
 *
 * <p>http://www.apache.org/licenses/LICENSE-2.0
 *
 * <p>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 edu.berkeley.ground.postgres.dao.core;


public class PostgresStructureVersionDao extends PostgresVersionDao<StructureVersion> implements StructureVersionDao {

  private PostgresStructureDao postgresStructureDao;

  public PostgresStructureVersionDao(Database dbSource, IdGenerator idGenerator) {
    super(dbSource, idGenerator);
    this.postgresStructureDao = new PostgresStructureDao(dbSource, idGenerator);
  }

  @Override
  public final StructureVersion create(final StructureVersion structureVersion, List<Long> parentIds) throws GroundException {

    long uniqueId = idGenerator.generateItemId();
    StructureVersion newStructureVersion = new StructureVersion(uniqueId, structureVersion);
    PostgresStatements updateVersionList = this.postgresStructureDao
                                             .update(newStructureVersion.getStructureId(), newStructureVersion.getId(), parentIds);

    try {
      PostgresStatements statements = super.insert(newStructureVersion);
      statements.append(String.format(SqlConstants.INSERT_STRUCTURE_VERSION, uniqueId, structureVersion.getStructureId()));