/******************************************************************************* * Copyright 2012 University of Southern California * * 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. * * This code was developed by the Information Integration Group as part * of the Karma project at the Information Sciences Institute of the * University of Southern California. For more information, publications, * and related projects, please see: http://www.isi.edu/integration ******************************************************************************/ package edu.isi.karma.controller.command.alignment; import org.jgrapht.graph.DirectedWeightedMultigraph; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import edu.isi.karma.controller.command.Command; import edu.isi.karma.controller.command.CommandException; import edu.isi.karma.controller.command.CommandType; import edu.isi.karma.controller.command.WorksheetCommand; import edu.isi.karma.controller.command.alignment.ChangeInternalNodeLinksCommandFactory.Arguments; import edu.isi.karma.controller.history.WorksheetCommandHistoryExecutor; import edu.isi.karma.controller.update.UpdateContainer; import edu.isi.karma.modeling.alignment.Alignment; import edu.isi.karma.modeling.alignment.AlignmentManager; import edu.isi.karma.modeling.alignment.LinkIdFactory; import edu.isi.karma.modeling.ontology.OntologyManager; import edu.isi.karma.rep.Worksheet; import edu.isi.karma.rep.Workspace; import edu.isi.karma.rep.alignment.DefaultLink; import edu.isi.karma.rep.alignment.LabeledLink; import edu.isi.karma.rep.alignment.Node; public class ChangeInternalNodeLinksCommand extends WorksheetCommand { private final String alignmentId; private JSONArray initialEdges; private JSONArray newEdges; // Required for undo private Alignment oldAlignment; private DirectedWeightedMultigraph<Node, DefaultLink> oldGraph; private StringBuilder addDescStr = new StringBuilder(); private StringBuilder delDescStr = new StringBuilder(); public enum LinkJsonKeys { edgeSourceId, edgeId, edgeTargetId, edgeSourceUri, edgeTargetUri, isProvenance } private static Logger logger = LoggerFactory.getLogger(ChangeInternalNodeLinksCommand.class); public ChangeInternalNodeLinksCommand(String id, String model, String worksheetId, String alignmentId, JSONArray initialEdges, JSONArray newEdges) { super(id, model, worksheetId); this.alignmentId = alignmentId; this.initialEdges = initialEdges; this.newEdges = newEdges; addTag(CommandTag.Modeling); } @Override public String getCommandName() { return this.getClass().getSimpleName(); } @Override public String getTitle() { return "Change Links"; } @Override public String getDescription() { String addStr = addDescStr.toString(); String delStr = delDescStr.toString(); String res = ""; if(addStr.length() > 0) res += "Add: " + addStr; if(delStr.length() > 0) { if(res.length() > 0) res += ", "; res += "Delete: " + delStr; } return res; } @Override public CommandType getCommandType() { return CommandType.notInHistory; } @SuppressWarnings("unchecked") @Override public UpdateContainer doIt(Workspace workspace) throws CommandException { Alignment alignment = AlignmentManager.Instance().getAlignment( alignmentId); Worksheet worksheet = workspace.getWorksheet(worksheetId); OntologyManager ontMgr = workspace.getOntologyManager(); // Save the original alignment for undo oldAlignment = alignment.getAlignmentClone(); oldGraph = (DirectedWeightedMultigraph<Node, DefaultLink>) alignment .getGraph().clone(); UpdateContainer uc = new UpdateContainer(); WorksheetCommandHistoryExecutor histExecutor = new WorksheetCommandHistoryExecutor( worksheetId, workspace); // First delete the links that are not present in newEdges and present // in intialEdges try { refineInitialEdges(alignment); uc.append(deleteLinks(histExecutor, workspace, worksheet, alignment)); uc.append(addNewLinks(histExecutor, workspace, alignment, ontMgr)); } catch (JSONException e) { e.printStackTrace(); } return uc; } private void refineInitialEdges(Alignment alignment) { int j = initialEdges.length() - 1; while (j >= 0) { JSONObject initialEdge = initialEdges.getJSONObject(j); String edgeUri = initialEdge.getString(LinkJsonKeys.edgeId.name()); String sourceId = initialEdge.getString(LinkJsonKeys.edgeSourceId.name()); String targetId = initialEdge.getString(LinkJsonKeys.edgeTargetId.name()); String linkId = LinkIdFactory.getLinkId(edgeUri, sourceId, targetId); if (alignment.getLinkById(linkId) == null) { // the link is not even in the graph initialEdges.remove(j); } j--; } } private UpdateContainer addNewLinks(WorksheetCommandHistoryExecutor histExecutor, Workspace workspace, Alignment alignment, OntologyManager ontMgr) throws JSONException { UpdateContainer uc = new UpdateContainer(); boolean saveToHistory = !this.isExecutedInBatch(); for (int i = 0; i < newEdges.length(); i++) { JSONObject newEdge = newEdges.getJSONObject(i); try { Command cmd = (new AddLinkCommandFactory()).createCommand(worksheetId, alignmentId, newEdge, model, workspace); cmd.setExecutedInBatch(this.isExecutedInBatch()); uc.append(workspace.getCommandHistory().doCommand(cmd, workspace, saveToHistory && i==newEdges.length()-1)); } catch(CommandException e) { logger.error("Error adding a new link: " + newEdge.toString(), e); } } return uc; } private UpdateContainer deleteLinks(WorksheetCommandHistoryExecutor histExecutor, Workspace workspace, Worksheet worksheet, Alignment alignment) throws JSONException { UpdateContainer uc = new UpdateContainer(); boolean saveToHistory = !this.isExecutedInBatch(); for (int i = 0; i < initialEdges.length(); i++) { JSONObject initialEdge = initialEdges.getJSONObject(i); int newEdgeIdx = -1; for (int j = 0; j < newEdges.length(); j++) { JSONObject newEdge = newEdges.getJSONObject(j); if ( initialEdge.has(LinkJsonKeys.edgeId.name()) && newEdge.has(LinkJsonKeys.edgeId.name()) && initialEdge.getString(LinkJsonKeys.edgeId.name()).equals(newEdge.getString(LinkJsonKeys.edgeId.name())) && initialEdge.has(LinkJsonKeys.edgeSourceId.name()) && newEdge.has(LinkJsonKeys.edgeSourceId.name()) && initialEdge.getString(LinkJsonKeys.edgeSourceId.name()).equals(newEdge.getString(LinkJsonKeys.edgeSourceId.name())) && initialEdge.has(LinkJsonKeys.edgeTargetId.name()) && (newEdge.has(LinkJsonKeys.edgeTargetId.name()) && initialEdge.getString(LinkJsonKeys.edgeTargetId.name()).equals(newEdge.getString(LinkJsonKeys.edgeTargetId.name()))) ) newEdgeIdx = j; } if(newEdgeIdx != -1) { JSONObject newEdge = newEdges.getJSONObject(newEdgeIdx); boolean newEdgeProv = (newEdge.has(LinkJsonKeys.isProvenance.name())) ? newEdge.getBoolean(LinkJsonKeys.isProvenance.name()): false; boolean initialEdgeProv = (initialEdge.has(LinkJsonKeys.isProvenance.name())) ? initialEdge.getBoolean(LinkJsonKeys.isProvenance.name()): false; if(initialEdgeProv && !newEdgeProv) { //Are removing the provenance, then remove from the "Main" link instead String linkId = LinkIdFactory.getLinkId( newEdge.getString(LinkJsonKeys.edgeId.name()), newEdge.getString(LinkJsonKeys.edgeSourceId.name()), newEdge.getString(LinkJsonKeys.edgeTargetId.name())); LabeledLink link = alignment.getLinkById(linkId); if(!link.isMainProvenanceLink()) { link = getMainProvenanceLink(alignment, link); if(link != null) { initialEdge.put(LinkJsonKeys.edgeSourceId.name(), link.getSource().getId()); initialEdge.put(LinkJsonKeys.edgeSourceUri.name(), link.getSource().getUri()); newEdge.put(LinkJsonKeys.edgeSourceId.name(), link.getSource().getId()); newEdge.put(LinkJsonKeys.edgeSourceUri.name(), link.getSource().getUri()); } } } } try { Command cmd = (new DeleteLinkCommandFactory()).createCommand(worksheetId, alignmentId, initialEdge, model, workspace); cmd.setExecutedInBatch(this.isExecutedInBatch()); uc.append(workspace.getCommandHistory().doCommand(cmd, workspace, saveToHistory && i==initialEdges.length()-1)); } catch(Exception e) { logger.error("Error removing a link: " + initialEdge.toString(), e); } } return uc; } private LabeledLink getMainProvenanceLink(Alignment alignment, LabeledLink link) { String edgeUri = link.getUri(); for(LabeledLink otherLink : alignment.getCurrentIncomingLinksToNode(link.getTarget().getId())) { if(otherLink.getUri().equals(edgeUri) && otherLink.isMainProvenanceLink()) return otherLink; } return null; } @Override public UpdateContainer undoIt(Workspace workspace) { // Revert to the old alignment AlignmentManager.Instance() .addAlignmentToMap(alignmentId, oldAlignment); oldAlignment.setGraph(oldGraph); return this.computeAlignmentAndSemanticTypesAndCreateUpdates(workspace); } @Override protected JSONObject getArgsJSON(Workspace workspace) { JSONObject args = new JSONObject(); try { args.put("command", getTitle()) .put(Arguments.alignmentId.name(), alignmentId) .put(Arguments.initialEdges.name(), initialEdges) .put(Arguments.worksheetId.name(), formatWorsheetId(workspace, worksheetId)) .put(Arguments.newEdges.name(), newEdges); } catch (JSONException e) { e.printStackTrace(); } return args; } }