//License: GPL. Copyright 2007 by Immanuel Scholz and others
package org.openstreetmap.josm.command;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
//import javax.swing.tree.DefaultMutableTreeNode;
//import javax.swing.tree.MutableTreeNode;
import org.openstreetmap.josm.Main;
import org.openstreetmap.josm.data.osm.Node;
import org.openstreetmap.josm.data.osm.OsmPrimitive;
import org.openstreetmap.josm.data.osm.PrimitiveData;
import org.openstreetmap.josm.data.osm.Relation;
import org.openstreetmap.josm.data.osm.Way;
import org.openstreetmap.josm.data.osm.visitor.AbstractVisitor;
import org.openstreetmap.josm.gui.layer.Layer;
import org.openstreetmap.josm.gui.layer.OsmDataLayer;
import org.openstreetmap.josm.tools.CheckParameterUtil;
/**
* GWT
*
* TODO
* excluded getDescription() and description() for now, since it uses swing classes
*/
/**
* Classes implementing Command modify a dataset in a specific way. A command is
* one atomic action on a specific dataset, such as move or delete.
*
* The command remembers the {@see OsmDataLayer} it is operating on.
*
* @author imi
*/
abstract public class Command extends PseudoCommand {
private static final class CloneVisitor extends AbstractVisitor {
public final Map<OsmPrimitive, PrimitiveData> orig = new LinkedHashMap<OsmPrimitive, PrimitiveData>();
public void visit(Node n) {
orig.put(n, n.save());
}
public void visit(Way w) {
orig.put(w, w.save());
}
public void visit(Relation e) {
orig.put(e, e.save());
}
}
/** the map of OsmPrimitives in the original state to OsmPrimitives in cloned state */
private Map<OsmPrimitive, PrimitiveData> cloneMap = new HashMap<OsmPrimitive, PrimitiveData>();
/** the layer which this command is applied to */
private OsmDataLayer layer;
public Command() {
this.layer = Main.map.mapView.getEditLayer();
}
/**
* Creates a new command in the context of a specific data layer
*
* @param layer the data layer. Must not be null.
* @throws IllegalArgumentException thrown if layer is null
*/
public Command(OsmDataLayer layer) throws IllegalArgumentException {
CheckParameterUtil.ensureParameterNotNull(layer, "layer");
this.layer = layer;
}
/**
* Executes the command on the dataset. This implementation will remember all
* primitives returned by fillModifiedData for restoring them on undo.
*/
public boolean executeCommand() {
CloneVisitor visitor = new CloneVisitor();
Collection<OsmPrimitive> all = new ArrayList<OsmPrimitive>();
fillModifiedData(all, all, all);
for (OsmPrimitive osm : all) {
osm.visit(visitor);
}
cloneMap = visitor.orig;
return true;
}
/**
* Undoes the command.
* It can be assumed that all objects are in the same state they were before.
* It can also be assumed that executeCommand was called exactly once before.
*
* This implementation undoes all objects stored by a former call to executeCommand.
*/
public void undoCommand() {
for (Entry<OsmPrimitive, PrimitiveData> e : cloneMap.entrySet()) {
OsmPrimitive primitive = e.getKey();
if (primitive.getDataSet() != null) {
e.getKey().load(e.getValue());
}
}
}
/**
* Called when a layer has been removed to have the command remove itself from
* any buffer if it is not longer applicable to the dataset (e.g. it was part of
* the removed layer)
*
* @param oldLayer the old layer
* @return true if this command
*/
public boolean invalidBecauselayerRemoved(Layer oldLayer) {
if (!(oldLayer instanceof OsmDataLayer))
return false;
return layer == oldLayer;
}
/**
* Lets other commands access the original version
* of the object. Usually for undoing.
*/
public PrimitiveData getOrig(OsmPrimitive osm) {
PrimitiveData o = cloneMap.get(osm);
if (o != null)
return o;
Main.debug("unable to find osm with id: " + osm.getId() + " hashCode: " + osm.hashCode());
for (OsmPrimitive t : cloneMap.keySet()) {
PrimitiveData to = cloneMap.get(t);
Main.debug("now: " + t.getId() + " hashCode: " + t.hashCode());
Main.debug("orig: " + to.getUniqueId() + " hashCode: " + to.hashCode());
}
return o;
}
/**
* Replies the layer this command is (or was) applied to.
*
*/
protected OsmDataLayer getLayer() {
return layer;
}
/**
* Fill in the changed data this command operates on.
* Add to the lists, don't clear them.
*
* @param modified The modified primitives
* @param deleted The deleted primitives
* @param added The added primitives
*/
abstract public void fillModifiedData(Collection<OsmPrimitive> modified,
Collection<OsmPrimitive> deleted,
Collection<OsmPrimitive> added);
/**
* Return the primitives that take part in this command.
*/
@Override public Collection<? extends OsmPrimitive> getParticipatingPrimitives() {
return cloneMap.keySet();
}
/**
* Provide a description that can be presented in a list or tree view.
* This override will be removed when
* <code>description()</code> is removed.
*/
@Override public Object getDescription() {
throw new UnsupportedOperationException("gwt not supported yet");
// return ((DefaultMutableTreeNode) description()).getUserObject();
}
// /**
// * @deprecated use getDescription() and getChildren() instead
// */
// @Deprecated
// public MutableTreeNode description() {
// return null;
// }
}