/**
* $Id: mxSharedGraphModel.java,v 1.1 2012/11/15 13:26:50 gaudenz Exp $
* Copyright (c) 2007-2012, JGraph Ltd
*/
package com.mxgraph.sharing;
import com.mxgraph.io.mxCodec;
import com.mxgraph.model.mxGraphModel;
import com.mxgraph.model.mxGraphModel.mxChildChange;
import com.mxgraph.model.mxICell;
import com.mxgraph.model.mxIGraphModel.mxAtomicGraphModelChange;
import com.mxgraph.util.mxEvent;
import com.mxgraph.util.mxEventObject;
import com.mxgraph.util.mxUndoableEdit;
import com.mxgraph.util.mxXmlUtils;
import org.w3c.dom.Node;
import java.util.LinkedList;
/**
* Implements a diagram that may be shared among multiple sessions.
*/
public class mxSharedGraphModel extends mxSharedState {
/**
*
*/
protected mxGraphModel model;
/**
*
*/
protected mxCodec codec = new mxCodec() {
public Object lookup(String id) {
return model.getCell(id);
}
};
/**
* Whether remote changes should be significant in the
* local command history. Default is true.
*/
protected boolean significantRemoteChanges = true;
/**
* Constructs a new diagram with the given model.
*
* @param model Initial model of the diagram.
*/
public mxSharedGraphModel(mxGraphModel model) {
super(null); // Overrides getState
this.model = model;
}
/**
* @return the model
*/
public mxGraphModel getModel() {
return model;
}
/**
* @return the significantRemoteChanges
*/
public boolean isSignificantRemoteChanges() {
return significantRemoteChanges;
}
/**
* @param significantRemoteChanges the significantRemoteChanges to set
*/
public void setSignificantRemoteChanges(boolean significantRemoteChanges) {
this.significantRemoteChanges = significantRemoteChanges;
}
/**
* Returns the initial state of the diagram.
*/
public String getState() {
return mxXmlUtils.getXml(codec.encode(model));
}
/**
*
*/
public synchronized void addDelta(String edits) {
// Edits are not added to the history. They are sent straight out to
// all sessions and the model is updated so the next session will get
// these edits via the new state of the model in getState.
}
/**
*
*/
protected String processEdit(Node node) {
mxAtomicGraphModelChange[] changes = decodeChanges(node.getFirstChild());
if (changes.length > 0) {
mxUndoableEdit edit = createUndoableEdit(changes);
// No notify event here to avoid the edit from being encoded and transmitted
// LATER: Remove changes property (deprecated)
model.fireEvent(new mxEventObject(mxEvent.CHANGE, "edit", edit, "changes", changes));
model.fireEvent(new mxEventObject(mxEvent.UNDO, "edit", edit));
fireEvent(new mxEventObject(mxEvent.FIRED, "edit", edit));
}
return super.processEdit(node);
}
/**
* Creates a new mxUndoableEdit that implements the notify function to fire
* a change and notify event via the model.
*/
protected mxUndoableEdit createUndoableEdit(mxAtomicGraphModelChange[] changes) {
mxUndoableEdit edit = new mxUndoableEdit(this, significantRemoteChanges) {
public void dispatch() {
// LATER: Remove changes property (deprecated)
((mxGraphModel)source).fireEvent(new mxEventObject(mxEvent.CHANGE, "edit", this, "changes", changes));
((mxGraphModel)source).fireEvent(new mxEventObject(mxEvent.NOTIFY, "edit", this, "changes", changes));
}
};
for (int i = 0; i < changes.length; i++) {
edit.add(changes[i]);
}
return edit;
}
/**
* Adds removed cells to the codec object lookup for references to the removed
* cells after this point in time.
*/
protected mxAtomicGraphModelChange[] decodeChanges(Node node) {
// Updates the document in the existing codec
codec.setDocument(node.getOwnerDocument());
LinkedList<mxAtomicGraphModelChange> changes = new LinkedList<mxAtomicGraphModelChange>();
while (node != null) {
Object change;
if (node.getNodeName().equals("mxRootChange")) {
// Handles the special case were no ids should be
// resolved in the existing model. This change will
// replace all registered ids and cells from the
// model and insert a new cell hierarchy instead.
mxCodec tmp = new mxCodec(node.getOwnerDocument());
change = tmp.decode(node);
}
else {
change = codec.decode(node);
}
if (change instanceof mxAtomicGraphModelChange) {
mxAtomicGraphModelChange ac = (mxAtomicGraphModelChange)change;
ac.setModel(model);
ac.execute();
// Workaround for references not being resolved if cells have
// been removed from the model prior to being referenced. This
// adds removed cells in the codec object lookup table.
if (ac instanceof mxChildChange && ((mxChildChange)ac).getParent() == null) {
cellRemoved(((mxChildChange)ac).getChild());
}
changes.add(ac);
}
node = node.getNextSibling();
}
return changes.toArray(new mxAtomicGraphModelChange[changes.size()]);
}
/**
* Adds removed cells to the codec object lookup for references to the removed
* cells after this point in time.
*/
public void cellRemoved(Object cell) {
codec.putObject(((mxICell)cell).getId(), cell);
int childCount = model.getChildCount(cell);
for (int i = 0; i < childCount; i++) {
cellRemoved(model.getChildAt(cell, i));
}
}
}