/*
* Copyright 2007 - 2017 the original author or authors.
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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 net.sf.jailer.subsetting;
import java.io.FileWriter;
import java.io.IOException;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.apache.log4j.Logger;
import net.sf.jailer.ExecutionContext;
import net.sf.jailer.database.SQLDialect;
import net.sf.jailer.database.Session;
import net.sf.jailer.datamodel.Association;
import net.sf.jailer.datamodel.Column;
import net.sf.jailer.datamodel.DataModel;
import net.sf.jailer.datamodel.RowIdSupport;
import net.sf.jailer.datamodel.Table;
import net.sf.jailer.entitygraph.EntityGraph;
import net.sf.jailer.util.CellContentConverter;
import net.sf.jailer.util.Quoting;
/**
* Creates a listing of all association-paths of exported entities
* back to a subject in an entity-graph (the 'explain.log' file)
*
* @author Ralf Wisser
*/
public class ExplainTool {
/**
* The logger.
*/
private static final Logger _log = Logger.getLogger(ExplainTool.class);
/**
* Creates a listing of all association-paths of exported entities
* back to a subject in an entity-graph (the 'explain.log' file)
*
* @param graph
* the entity-graph to explain
* @param session
* for executing SQL-statements
*/
public static void explain(final EntityGraph graph, final Session session, final ExecutionContext executionContext) throws SQLException, IOException {
_log.info("generating explain.log...");
final Quoting quoting = new Quoting(session);
StringBuffer succEqualsE = new StringBuffer();
for (Column column: graph.getUniversalPrimaryKey().getColumns()) {
if (succEqualsE.length() > 0) {
succEqualsE.append(" and ");
}
succEqualsE.append("Succ.PRE_" + column.name + "=E." + column.name);
}
final FileWriter writer = new FileWriter("explain.log");
final RowIdSupport rowIdSupport = new RowIdSupport(graph.getDatamodel(), session.dbms, executionContext);
String selectLeafs = "Select type, " + graph.getUniversalPrimaryKey().columnList(null) + " From " + SQLDialect.dmlTableReference(EntityGraph.ENTITY, session, executionContext) + " E Where E.r_entitygraph=" + graph.graphID +
" and not exists (Select * from " + SQLDialect.dmlTableReference(EntityGraph.ENTITY, session, executionContext) + " Succ Where Succ.r_entitygraph=" + graph.graphID + " and Succ.PRE_TYPE=E.type and " + succEqualsE + ")";
session.executeQuery(selectLeafs, new Session.AbstractResultSetReader() {
public void readCurrentRow(ResultSet resultSet) throws SQLException {
int o = resultSet.getInt(1);
String type = null;
if (!resultSet.wasNull()) {
Table tableByTypeName = graph.getDatamodel().getTableByOrdinal(o);
type = tableByTypeName.getName();
}
List<String> keys = new ArrayList<String>();
int i = 2;
CellContentConverter cellContentConverter = getCellContentConverter(resultSet, session, session.dbms);
for (@SuppressWarnings("unused") Column column: graph.getUniversalPrimaryKey().getColumns()) {
keys.add(cellContentConverter.toSql(cellContentConverter.getObject(resultSet, i++)));
}
try {
writer.append(path(graph, session, type, keys, graph.getDatamodel(), rowIdSupport, quoting, executionContext));
writer.append(".\n");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
});
writer.append("\n");
List<String> associations = new ArrayList<String>();
for (Map.Entry<Association, Integer> e: graph.explainIdOfAssociation.entrySet()) {
String nr = "" + e.getValue();
while (nr.length() < 5) {
nr = " " + nr;
}
String sourceName = e.getKey().source.getName();
while (sourceName.length() < 24) {
sourceName = sourceName + " ";
}
associations.add("#" + nr + " " + sourceName + " -> " + e.getKey());
}
Collections.sort(associations);
for (String line: associations) {
writer.append(line);
writer.append("\n");
}
writer.close();
}
/**
* Stringifies the path of predecessors of a given entity.
* @param rowIdSupport
* @throws SQLException
*/
private static String path(final EntityGraph graph, final Session session, String type, List<String> keys, DataModel datamodel, RowIdSupport rowIdSupport, Quoting quoting, ExecutionContext executionContext) throws SQLException {
String where = "";
int i = 0;
for (Column column: graph.getUniversalPrimaryKey().getColumns()) {
if (where.length() > 0) {
where += " and ";
}
String value = keys.get(i++);
where += quoting.requote(column.name) + (value == null || value.equals("null")? " is null" : ("=" + value));
}
String selectPredecessor = "Select PRE_TYPE, association, " + graph.getUniversalPrimaryKey().columnList("PRE_") + " From " + SQLDialect.dmlTableReference(EntityGraph.ENTITY, session, executionContext) + " E Where E.r_entitygraph=" + graph.graphID +
" and type=" + datamodel.getTable(type).getOrdinal() + " and " + where;
final String preType[] = new String[1];
final List<String> preKeys = new ArrayList<String>();
final Integer associationID[] = new Integer[1];
session.executeQuery(selectPredecessor, new Session.AbstractResultSetReader() {
public void readCurrentRow(ResultSet resultSet) throws SQLException {
int o = resultSet.getInt(1);
if (!resultSet.wasNull()) {
Table tableByTypeName = graph.getDatamodel().getTableByOrdinal(o);
preType[0] = tableByTypeName.getName();
}
associationID[0] = resultSet.getInt(2);
int i = 3;
CellContentConverter cellContentConverter = getCellContentConverter(resultSet, session, session.dbms);
for (@SuppressWarnings("unused") Column column: graph.getUniversalPrimaryKey().getColumns()) {
preKeys.add(cellContentConverter.toSql(cellContentConverter.getObject(resultSet, i++)));
}
}
});
String thePath = entityAsString(type, keys, datamodel, rowIdSupport, session);
if (preType[0] != null) {
thePath = path(graph, session, preType[0], preKeys, datamodel, rowIdSupport, quoting, executionContext) + " --" + associationID[0] + "--> " + thePath;
}
return thePath;
}
/**
* Stringifies an entity.
* @param session
*/
private static String entityAsString(String type, List<String> keys, DataModel datamodel, RowIdSupport rowIdSupport, Session session) {
StringBuffer sb = new StringBuffer(type + "(");
int i = 0, ki = 0;
Map<Column, Column> match = rowIdSupport.getUniversalPrimaryKey().match(rowIdSupport.getPrimaryKey(datamodel.getTable(type)));
for (Column column: rowIdSupport.getUniversalPrimaryKey().getColumns()) {
if (match.get(column) != null) {
if (i > 0) {
sb.append(", ");
}
++i;
sb.append(keys.get(ki));
}
++ki;
}
sb.append(")");
return sb.toString();
}
}