org.nuiton.topia.service.script.table.TopiaSqlTables Maven / Gradle / Ivy
package org.nuiton.topia.service.script.table;
/*
* #%L
* ObServe Toolkit :: ToPIA Script service
* %%
* Copyright (C) 2017 - 2018 IRD, Ultreia.io
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program. If not, see
* .
* #L%
*/
import com.google.common.base.MoreObjects;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.TreeMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.nuiton.topia.persistence.TopiaEntityEnum;
import org.nuiton.topia.persistence.metadata.TopiaMetadataEntity;
import org.nuiton.topia.persistence.metadata.TopiaMetadataModel;
/**
* A container of {@link TopiaSqlTable}.
*
* Created on 01/01/16.
*
* @author Tony Chemit - [email protected]
* @since 3.0.1
*/
public class TopiaSqlTables implements Iterable {
/** Logger. */
private static final Log log = LogFactory.getLog(TopiaSqlTables.class);
private ImmutableMap tablesByFullyTableName;
private ImmutableSet orderedTables;
public TopiaSqlTables(ImmutableMap tablesByFullyTableName,
ImmutableSet orderedTables) {
this.tablesByFullyTableName = tablesByFullyTableName;
this.orderedTables = orderedTables;
}
public static Builder builder(TopiaMetadataModel metadataModel, TopiaMetadataEntity metadataEntity) {
return new BuilderImpl(metadataModel, metadataEntity);
}
public TopiaSqlTable getTable(String key) {
return tablesByFullyTableName.get(key);
}
@SuppressWarnings("NullableProblems")
@Override
public Iterator iterator() {
return orderedTables.iterator();
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("orderedTables", orderedTables)
.toString();
}
public TopiaSqlTables reverse() {
List reverseList = new LinkedList<>();
for (TopiaSqlTable orderedTable : orderedTables) {
reverseList.add(0, orderedTable);
}
ImmutableSet reverseSet = ImmutableSet.copyOf(reverseList);
return new TopiaSqlTables(tablesByFullyTableName, reverseSet);
}
public void replaceTable(String tableName, TopiaSqlTable table) {
ImmutableMap.Builder tablesByFullyTableName2 = ImmutableMap.builder();
ImmutableSet.Builder orderedTables2 = ImmutableSet.builder();
for (Map.Entry entry : tablesByFullyTableName.entrySet()) {
String name = entry.getKey();
TopiaSqlTable topiaSqlTable = entry.getValue();
if (tableName.equals(name)) {
topiaSqlTable = table;
}
tablesByFullyTableName2.put(name, topiaSqlTable);
}
for (TopiaSqlTable orderedTable : orderedTables) {
if (tableName.equals(orderedTable.getFullyTableName())) {
orderedTable = table;
}
orderedTables2.add(orderedTable);
}
tablesByFullyTableName = tablesByFullyTableName2.build();
orderedTables = orderedTables2.build();
}
public int size() {
return tablesByFullyTableName.size();
}
/**
* Created on 02/01/16.
*
* @author Tony Chemit - [email protected]
*/
public interface Builder {
BuilderStepOnTable addMainTable(TopiaEntityEnum entityEnum);
TopiaSqlTables build();
}
public interface BuilderStepOnTable extends Builder {
BuilderStepOnTable addJoinTable(TopiaEntityEnum entityEnum);
BuilderStepOnTable addAndEnterJoinTable(TopiaEntityEnum entityEnum);
BuilderStepOnTable addReverseJoinTable(TopiaEntityEnum entityEnum);
BuilderStepOnTable addAndEnterReverseJoinTable(TopiaEntityEnum entityEnum);
BuilderStepOnTable addAssociationTable(TopiaEntityEnum entityEnum, String associationName);
BuilderStepOnTable backToParent();
BuilderStepOnTable backToTable(TopiaEntityEnum entityEnum);
BuilderStepOnTable checkCurrentTable(TopiaEntityEnum entityEnum);
}
/**
* Created on 01/01/16.
*
* @author Tony Chemit - [email protected]
*/
protected static class BuilderImpl implements Builder {
private final TreeMap tablesByFullyTableName;
private final TreeMap tablesByOrder;
private final TopiaMetadataModel metadataModel;
private final TopiaMetadataEntity metadataEntity;
private int internalOrder;
BuilderImpl(TopiaMetadataModel metadataModel, TopiaMetadataEntity metadataEntity) {
this.metadataModel = metadataModel;
this.metadataEntity = metadataEntity;
this.tablesByFullyTableName = new TreeMap<>();
this.tablesByOrder = new TreeMap<>();
}
@Override
public BuilderStepOnTable addMainTable(TopiaEntityEnum entityEnum) {
String schemaName = entityEnum.dbSchemaName().toLowerCase();
String tableName = entityEnum.dbTableName().toLowerCase();
//TODO check that this table is not already registred
String whereClauseAlias = tableName + ".topiaid";
String fromClause = schemaName + "." + tableName + " " + tableName;
registerTable(entityEnum, schemaName, tableName, whereClauseAlias, fromClause, ImmutableSet.of());
return new BuilderStepOnTableImpl(null, entityEnum);
}
@Override
public TopiaSqlTables build() {
List orders = Lists.newArrayList(tablesByOrder.keySet());
Collections.sort(orders);
ImmutableSet.Builder orderedTablesBuilder = ImmutableSet.builder();
for (Integer order : orders) {
orderedTablesBuilder.add(tablesByOrder.get(order));
}
return new TopiaSqlTables(ImmutableMap.copyOf(tablesByFullyTableName),
orderedTablesBuilder.build());
}
Builder registerTable(TopiaEntityEnum entityEnum,
String schemaName,
String tableName,
String whereClauseAlias,
String fromClause,
ImmutableSet joinClauses) {
return registerTable(entityEnum, null, schemaName, tableName, whereClauseAlias, fromClause, joinClauses, null);
}
Builder registerAssociationTable(TopiaEntityEnum entityEnum,
TopiaMetadataEntity associationEntityEnum,
String schemaName,
String tableName,
String whereClauseAlias,
String fromClause,
ImmutableSet joinClauses,
String joinColumnName) {
return registerTable(entityEnum, associationEntityEnum, schemaName, tableName, whereClauseAlias, fromClause, joinClauses, joinColumnName);
}
private Builder registerTable(TopiaEntityEnum entityEnum,
TopiaMetadataEntity associationMetadataEntity,
String schemaName,
String tableName,
String whereClauseAlias,
String fromClause,
ImmutableSet joinClauses,
String joinColumnName) {
//TODO check that this table is not already registered
TopiaMetadataEntity metadataEntity = metadataModel.getEntity(entityEnum.name());
TopiaSqlTable table = new TopiaSqlTable(
metadataEntity,
associationMetadataEntity,
schemaName,
tableName,
fromClause,
whereClauseAlias,
joinClauses,
joinColumnName);
log.debug("new TopiaTable: " + table);
tablesByFullyTableName.put(table.getFullyTableName(), table);
tablesByOrder.put(internalOrder++, table);
return this;
}
protected TopiaSqlTable getTable(String key) {
return tablesByFullyTableName.get(key);
}
protected TopiaSqlTable getTable(TopiaEntityEnum entityEnum) {
String key = getFullyTableName(entityEnum);
return tablesByFullyTableName.get(key);
}
String getFullyTableName(TopiaEntityEnum entityEnum) {
return entityEnum.dbSchemaName().toLowerCase() + "." + entityEnum.dbTableName().toLowerCase();
}
String getAssociationTableName(String tableName, String parentTableName) {
String associationTableName;
if (tableName.compareTo(parentTableName) < 0) {
associationTableName = tableName + "_" + parentTableName;
} else {
associationTableName = parentTableName + "_" + tableName;
}
return associationTableName;
}
protected class BuilderStepOnTableImpl implements Builder, BuilderStepOnTable {
protected final BuilderStepOnTableImpl parent;
final TopiaEntityEnum tableEntityEnum;
BuilderStepOnTableImpl(BuilderStepOnTableImpl parent, TopiaEntityEnum tableEntityEnum) {
this.parent = parent;
this.tableEntityEnum = tableEntityEnum;
}
@Override
public BuilderStepOnTable addMainTable(TopiaEntityEnum entityEnum) {
return BuilderImpl.this.addMainTable(entityEnum);
}
@Override
public TopiaSqlTables build() {
return BuilderImpl.this.build();
}
@Override
public BuilderStepOnTable addJoinTable(TopiaEntityEnum entityEnum) {
TopiaSqlTable parentTable = getTable();
String schemaName = entityEnum.dbSchemaName().toLowerCase();
String tableName = entityEnum.dbTableName().toLowerCase();
String parentTableName = parentTable.getTableName();
String whereClauseAlias;
String fromClause;
ImmutableSet joinClauses;
if (parent == null) {
// parent table is main (no join on it)
// we can directly use the target table to join
whereClauseAlias = tableName + "." + parentTableName;
fromClause = schemaName + "." + tableName + " " + tableName;
joinClauses = ImmutableSet.of();
} else {
// simple join table
whereClauseAlias = parentTable.getWhereClauseAlias();
fromClause = parentTable.getFromClause();
String joinClause = " INNER JOIN " + schemaName + "." + tableName + " " + tableName + " ON " + tableName + "." + parentTableName + " = " + parentTableName + ".topiaId";
joinClauses = addJoinCause(parentTable.getJoinClauses(), joinClause);
}
registerTable(entityEnum, schemaName, tableName, whereClauseAlias, fromClause, joinClauses);
return this;
}
@Override
public BuilderStepOnTable addAndEnterJoinTable(TopiaEntityEnum entityEnum) {
addJoinTable(entityEnum);
return new BuilderStepOnTableImpl(this, entityEnum);
}
@Override
public BuilderStepOnTable addReverseJoinTable(TopiaEntityEnum entityEnum) {
TopiaSqlTable parentTable = getTable();
String schemaName = entityEnum.dbSchemaName().toLowerCase();
String tableName = entityEnum.dbTableName().toLowerCase();
String whereClauseAlias = parentTable.getWhereClauseAlias();
String fromClause = parentTable.getFromClause();
String parentTableName = parentTable.getTableName();
String joinClause = " INNER JOIN " + schemaName + "." + tableName + " " + tableName + " ON " + tableName + ".topiaId = " + parentTableName + "." + tableName;
ImmutableSet joinClauses = addJoinCause(parentTable.getJoinClauses(), joinClause);
registerTable(entityEnum, schemaName, tableName, whereClauseAlias, fromClause, joinClauses);
invertOrderWithParent(parentTable, entityEnum);
return this;
}
@Override
public BuilderStepOnTable addAndEnterReverseJoinTable(TopiaEntityEnum entityEnum) {
addReverseJoinTable(entityEnum);
return new BuilderStepOnTableImpl(this, entityEnum);
}
@Override
public BuilderStepOnTable addAssociationTable(TopiaEntityEnum entityEnum, String associationName) {
TopiaSqlTable parentTable = getTable();
String schemaName = tableEntityEnum.dbSchemaName().toLowerCase();
String tableName = getAssociationTableName(associationName.toLowerCase(), parentTable.getTableName());
String whereClauseAlias = parentTable.getWhereClauseAlias();
String fromClause = parentTable.getFromClause();
if (parentTable.getJoinClauses().isEmpty()) {
fromClause = schemaName + "." + tableName;
}
ImmutableSet joinClauses;
boolean addInnerJoin = parent != null;
if (addInnerJoin) {
String parentTableName = parentTable.getTableName();
String joinClause = " INNER JOIN " + schemaName + "." + tableName + " " + tableName + " ON " + tableName + "." + parentTableName + " = " + parentTableName + ".topiaId";
joinClauses = addJoinCause(parentTable.getJoinClauses(), joinClause);
} else {
joinClauses = parentTable.getJoinClauses();
}
String joinColumnName = parentTable.getTableName();
registerAssociationTable(entityEnum,
parentTable.getMetadataEntity(),
schemaName,
tableName,
whereClauseAlias,
fromClause,
joinClauses,
joinColumnName);
return this;
}
@Override
public BuilderStepOnTable backToParent() {
Preconditions.checkState(parent != null, "Could not find a parent table");
return parent;
}
@Override
public BuilderStepOnTable backToTable(TopiaEntityEnum entityEnum) {
BuilderStepOnTable table;
if (Objects.equals(tableEntityEnum, entityEnum)) {
table = this;
} else {
Preconditions.checkState(parent != null, "Could not find a perent table of type: " + entityEnum);
table = parent.backToTable(entityEnum);
}
return table;
}
@Override
public BuilderStepOnTable checkCurrentTable(TopiaEntityEnum entityEnum) {
Preconditions.checkState(tableEntityEnum.equals(entityEnum), "Current table should be " + entityEnum + ", but was " + tableEntityEnum);
return this;
}
protected TopiaSqlTable getTable() {
return BuilderImpl.this.getTable(tableEntityEnum);
}
int getTableOrder(TopiaSqlTable table) {
for (Map.Entry entry : tablesByOrder.entrySet()) {
if (table.equals(entry.getValue())) {
return entry.getKey();
}
}
throw new IllegalStateException("Could not find table " + table.getFullyTableName());
}
void invertOrderWithParent(TopiaSqlTable parentTable, TopiaEntityEnum entityEnum) {
int parentTableOrder = getTableOrder(parentTable);
TopiaSqlTable table = BuilderImpl.this.getTable(entityEnum);
int tableOrder = getTableOrder(table);
tablesByOrder.put(parentTableOrder, table);
tablesByOrder.put(tableOrder, parentTable);
}
ImmutableSet addJoinCause(ImmutableSet joinClauses, String joinClause) {
return ImmutableSet
.builder()
.addAll(joinClauses)
.add(joinClause)
.build();
}
}
}
}