package edu.isi.karma.controller.command.worksheet;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.json.JSONArray;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import edu.isi.karma.controller.command.CommandException;
import edu.isi.karma.controller.command.CommandType;
import edu.isi.karma.controller.command.WorksheetSelectionCommand;
import edu.isi.karma.controller.command.selection.SuperSelection;
import edu.isi.karma.controller.update.ErrorUpdate;
import edu.isi.karma.controller.update.UpdateContainer;
import edu.isi.karma.controller.update.WorksheetListUpdate;
import edu.isi.karma.controller.update.WorksheetUpdateFactory;
import edu.isi.karma.er.helper.CloneTableUtils;
import edu.isi.karma.rep.HNode;
import edu.isi.karma.rep.HNode.HNodeType;
import edu.isi.karma.rep.HTable;
import edu.isi.karma.rep.Node;
import edu.isi.karma.rep.RepFactory;
import edu.isi.karma.rep.Row;
import edu.isi.karma.rep.Table;
import edu.isi.karma.rep.Worksheet;
import edu.isi.karma.rep.Workspace;
import edu.isi.karma.util.Util;
public class GlueCommand extends WorksheetSelectionCommand {
private String hNodeId;
private String newhNodeId;
private String hNodeIdList;
private GlueMethod implMethod;
private enum GlueMethod{
Longest, Shortest, CrossProduct
}
List<String> hNodeName;
private String hNodeParentName;
private static Logger logger = LoggerFactory
.getLogger(GlueCommand.class);
protected GlueCommand(String id, String model, String worksheetId,
String hNodeId, String selectionId,
String hNodeIdList, String implMethod) {
super(id, model, worksheetId, selectionId);
this.hNodeId = hNodeId;
this.hNodeIdList = hNodeIdList;
if (implMethod != null && !implMethod.isEmpty()) {
this.implMethod = GlueMethod.valueOf(implMethod);
}
else {
this.implMethod = GlueMethod.Longest;
}
hNodeName = new ArrayList<>();
addTag(CommandTag.Transformation);
}
@Override
public String getCommandName() {
return GlueCommand.class.getSimpleName();
}
@Override
public String getTitle() {
return "Glue";
}
@Override
public String getDescription() {
StringBuilder builder = new StringBuilder(hNodeParentName);
String sep = " > ";
for (String name : hNodeName) {
builder.append(sep + name);
sep = ", ";
}
return builder.substring(0, builder.length());
}
@Override
public CommandType getCommandType() {
return CommandType.undoable;
}
@Override
public UpdateContainer doIt(Workspace workspace) throws CommandException {
inputColumns.clear();
outputColumns.clear();
hNodeName.clear();
RepFactory factory = workspace.getFactory();
Worksheet oldws = workspace.getWorksheet(worksheetId);
List<HNode> hnodes = new ArrayList<>();
JSONArray checked = new JSONArray(hNodeIdList);
HTable ht;
if (hNodeId.compareTo("") != 0)
ht = factory.getHTable(factory.getHNode(hNodeId).getHTableId());
else
ht = oldws.getHeaders();
for (int i = 0; i < checked.length(); i++) {
JSONObject t = checked.getJSONObject(i);
HNode hNode = ht.getHNode(t.getString("value"));
if(i == 0)
this.hNodeParentName = hNode.getParentColumnName(factory);
if (hNode != null) {
hnodes.add(hNode);
hNodeName.add(hNode.getColumnName());
}
}
if (ht != oldws.getHeaders())
glueNestedTable(oldws, workspace, ht, hnodes, factory);
else
glueTopLevel(oldws, workspace, hnodes, factory);
try{
UpdateContainer c = new UpdateContainer();
c.add(new WorksheetListUpdate());
c.append(WorksheetUpdateFactory.createRegenerateWorksheetUpdates(oldws.getId(), getSuperSelection(oldws), workspace.getContextId()));
c.append(computeAlignmentAndSemanticTypesAndCreateUpdates(workspace));
WorksheetUpdateFactory.detectSelectionStatusChange(worksheetId, workspace, this);
return c;
} catch (Exception e) {
logger.error("Error in GlueCommand" + e.toString());
e.printStackTrace();
Util.logException(logger, e);
return new UpdateContainer(new ErrorUpdate(e.getMessage()));
}
}
@Override
public UpdateContainer undoIt(Workspace workspace) {
Worksheet worksheet = workspace.getWorksheet(worksheetId);
UpdateContainer uc = new UpdateContainer();
HNode ndid = workspace.getFactory().getHNode(newhNodeId);
HTable currentTable = workspace.getFactory().getHTable(ndid.getHTableId());
ndid.removeNestedTable();
//remove the new column
currentTable.removeHNode(newhNodeId, worksheet);
uc.append(WorksheetUpdateFactory.createRegenerateWorksheetUpdates(worksheetId, getSuperSelection(worksheet), workspace.getContextId()));
uc.append(computeAlignmentAndSemanticTypesAndCreateUpdates(workspace));
return uc;
}
private void glueNestedTable(Worksheet worksheet, Workspace workspace, HTable ht, List<HNode> hnodes, RepFactory factory) {
SuperSelection selection = getSuperSelection(worksheet);
HTable parentHT = ht.getParentHNode().getHTable(factory);
List<Table> parentTables = new ArrayList<>();
CloneTableUtils.getDatatable(worksheet.getDataTable(), parentHT,parentTables, selection);
ArrayList<Row> parentRows = new ArrayList<>();
for (Table tmp : parentTables) {
for (Row row : tmp.getRows(0, tmp.getNumRows(), selection)) {
parentRows.add(row);
}
}
HNode newNode = ht.addHNode(ht.getNewColumnName("Glue"), HNodeType.Transformation, worksheet, factory);
outputColumns.add(newNode.getId());
newhNodeId = newNode.getId();
HTable newht = newNode.addNestedTable(newNode.getColumnName(), worksheet, factory);
List<HNode> childHNodes = new ArrayList<>();
for (HNode hnode : hnodes) {
if (hnode.hasNestedTable()) {
for (HNode hn : hnode.getNestedTable().getHNodes()) {
childHNodes.add(hn);
}
}
}
Map<String, String> mapping = CloneTableUtils.cloneHTable(newht, worksheet, factory, childHNodes, true);
for (Entry<String, String> entry : mapping.entrySet()) {
outputColumns.add(entry.getValue());
}
for (Row parentRow : parentRows) {
Table t = null;
for (Node node : parentRow.getNodes()) {
if (node.hasNestedTable() && node.getNestedTable().getHTableId().compareTo(ht.getId()) == 0) {
t = node.getNestedTable();
break;
}
}
ArrayList<Row> rows = t.getRows(0, t.getNumRows(), selection);
for (Row row : rows) {
Table nestedTable = row.getNeighbor(newNode.getId()).getNestedTable();
generateRows(hnodes, selection, row, nestedTable, factory, mapping, childHNodes, newht);
}
}
}
private void glueTopLevel(Worksheet oldws, Workspace workspace, List<HNode> hnodes, RepFactory factory) {
HTable parentHT = oldws.getHeaders();
SuperSelection selection = getSuperSelection(oldws);
HNode newNode = parentHT.addHNode(parentHT.getNewColumnName("Glue"), HNodeType.Transformation, oldws, factory);
newhNodeId = newNode.getId();
outputColumns.add(newhNodeId);
HTable newht = newNode.addNestedTable(newNode.getColumnName(), oldws, factory);
List<HNode> childHNodes = new ArrayList<>();
for (HNode hnode : hnodes) {
if (hnode.hasNestedTable()) {
for (HNode hn : hnode.getNestedTable().getHNodes()) {
childHNodes.add(hn);
}
}
}
Map<String, String> mapping = CloneTableUtils.cloneHTable(newht, oldws, factory, childHNodes, true);
for (Entry<String, String> entry : mapping.entrySet()) {
outputColumns.add(entry.getValue());
}
ArrayList<Row> rows = oldws.getDataTable().getRows(0, oldws.getDataTable().getNumRows(), selection);
for (Row row : rows) {
Table nestedTable = row.getNeighbor(newNode.getId()).getNestedTable();
generateRows(hnodes, selection, row, nestedTable, factory, mapping, childHNodes, newht);
}
}
private void generateRows(List<HNode> hNodes, SuperSelection selection, Row row,
Table nestedTable, RepFactory factory, Map<String, String> mapping,
List<HNode> childHNodes, HTable newHTable) {
if (implMethod != GlueMethod.CrossProduct) {
int max = implMethod == GlueMethod.Longest ? Integer.MIN_VALUE : Integer.MAX_VALUE;
for (HNode hnode : hNodes) {
if (!hnode.hasNestedTable())
continue;
Node tmp = row.getNeighbor(hnode.getId());
int size = tmp.getNestedTable().getRows(0, tmp.getNestedTable().getNumRows(), selection).size();
if (size > max && implMethod == GlueMethod.Longest) {
max = size;
}
if (size < max && implMethod == GlueMethod.Shortest) {
max = size;
}
}
List<Row> newRows = new ArrayList<>();
for (int i = 0; i < max; i++)
newRows.add(nestedTable.addRow(factory));
for (HNode hnode : hNodes) {
if (!hnode.hasNestedTable())
continue;
Node tmp = row.getNeighbor(hnode.getId());
int i = 0;
for (Row nestedRow : tmp.getNestedTable().getRows(0, tmp.getNestedTable().getNumRows(), selection)) {
if (i >= max) {
break;
}
CloneTableUtils.cloneDataTableExistingRow(nestedRow, newRows.get(i), childHNodes, factory, mapping, selection);
i++;
}
}
}
else {
List<List<Row>> tablesToCross = new ArrayList<>();
for (HNode hnode : hNodes) {
if (!hnode.hasNestedTable())
continue;
Node tmp = row.getNeighbor(hnode.getId());
ArrayList<Row> rowsInTable = new ArrayList<>();
tablesToCross.add(rowsInTable);
for (Row nestedRow : tmp.getNestedTable().getRows(0, tmp.getNestedTable().getNumRows(), selection)) {
rowsInTable.add(nestedRow);
}
}
int size = tablesToCross.size();
int enumeration[] = new int[size + 1];
while(enumeration[size] != 1) {
Row r = nestedTable.addRow(factory);
for (int i = 0; i < tablesToCross.size(); i++) {
Row nestedRow = tablesToCross.get(i).get(enumeration[i]);
CloneTableUtils.cloneDataTableExistingRow(nestedRow, r, childHNodes, factory, mapping, selection);
}
enumeration[0]++;
for (int i = 0; i < tablesToCross.size(); i++) {
if (enumeration[i] == tablesToCross.get(i).size()) {
enumeration[i + 1]++;
enumeration[i] = 0;
}
}
}
System.out.println(nestedTable);
}
}
}