001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.command;
003
004import static org.openstreetmap.josm.tools.I18n.trn;
005
006import java.util.Collection;
007import java.util.HashMap;
008import java.util.LinkedList;
009import java.util.Map;
010
011import javax.swing.Icon;
012
013import org.openstreetmap.josm.data.coor.EastNorth;
014import org.openstreetmap.josm.data.osm.Node;
015import org.openstreetmap.josm.data.osm.OsmPrimitive;
016import org.openstreetmap.josm.data.osm.visitor.AllNodesVisitor;
017import org.openstreetmap.josm.tools.ImageProvider;
018
019/**
020 * Abstract class with common services for nodes rotation and scaling commands.
021 *
022 * @author Olivier Croquette <ocroquette@free.fr>
023 */
024public abstract class TransformNodesCommand extends Command {
025
026    /**
027     * The nodes to transform.
028     */
029    protected Collection<Node> nodes = new LinkedList<>();
030
031    /**
032     * List of all old states of the nodes.
033     */
034    protected Map<Node, OldNodeState> oldStates = new HashMap<>();
035
036    /**
037     * Stores the state of the nodes before the command.
038     */
039    protected final void storeOldState() {
040        for (Node n : this.nodes) {
041            oldStates.put(n, new OldNodeState(n));
042        }
043    }
044
045    /**
046     * Creates a TransformNodesObject.
047     * Find out the impacted nodes and store their initial state.
048     */
049    public TransformNodesCommand(Collection<OsmPrimitive> objects) {
050        this.nodes = AllNodesVisitor.getAllNodes(objects);
051        storeOldState();
052    }
053
054    /**
055     * Handling of a mouse event (e.g. dragging event).
056     * @param currentEN the current world position of the mouse
057     */
058    public abstract void handleEvent(EastNorth currentEN);
059
060    /**
061     * Implementation for the nodes transformation.
062     * No parameters are given here, you should handle the user input in handleEvent()
063     * and store it internally.
064     */
065    protected abstract void transformNodes();
066
067    /**
068     * Finally apply the transformation of the nodes.
069     * This is called when the user is happy with the current state of the command
070     * and its effects.
071     */
072    @Override
073    public boolean executeCommand() {
074        transformNodes();
075        flagNodesAsModified();
076        return true;
077    }
078
079    /**
080     * Flag all nodes as modified.
081     */
082    public void flagNodesAsModified() {
083        for (Node n : nodes) {
084            n.setModified(true);
085        }
086    }
087
088    /**
089     * Restore the state of the nodes from the backup.
090     */
091    @Override
092    public void undoCommand() {
093        for (Node n : nodes) {
094            OldNodeState os = oldStates.get(n);
095            n.setCoor(os.getLatlon());
096            n.setModified(os.isModified());
097        }
098    }
099
100    @Override
101    public void fillModifiedData(Collection<OsmPrimitive> modified, Collection<OsmPrimitive> deleted, Collection<OsmPrimitive> added) {
102    }
103
104    @Override
105    public Collection<? extends OsmPrimitive> getParticipatingPrimitives() {
106        return nodes;
107    }
108
109    @Override
110    public String getDescriptionText() {
111        return trn("Transform {0} node", "Transform {0} nodes", nodes.size(), nodes.size());
112    }
113
114    @Override
115    public Icon getDescriptionIcon() {
116        return ImageProvider.get("data", "node");
117    }
118
119    /**
120     * Get the nodes with the current transformation applied.
121     * @return nodes with the current transformation applied
122     */
123    public Collection<Node> getTransformedNodes() {
124        return nodes;
125    }
126
127    /**
128     * Get the center of the nodes under modification.
129     * It's just the barycenter.
130     * @return center east/north of the nodes under modification
131     * @see org.openstreetmap.josm.tools.Geometry#getCentroid(java.util.List)
132     */
133    public EastNorth getNodesCenter() {
134        EastNorth sum = new EastNorth(0, 0);
135
136        for (Node n : nodes) {
137            EastNorth en = n.getEastNorth();
138            sum = sum.add(en.east(), en.north());
139        }
140        return new EastNorth(sum.east()/this.nodes.size(), sum.north()/this.nodes.size());
141
142    }
143
144    @Override
145    public int hashCode() {
146        final int prime = 31;
147        int result = super.hashCode();
148        result = prime * result + ((nodes == null) ? 0 : nodes.hashCode());
149        result = prime * result + ((oldStates == null) ? 0 : oldStates.hashCode());
150        return result;
151    }
152
153    @Override
154    public boolean equals(Object obj) {
155        if (this == obj)
156            return true;
157        if (!super.equals(obj))
158            return false;
159        if (getClass() != obj.getClass())
160            return false;
161        TransformNodesCommand other = (TransformNodesCommand) obj;
162        if (nodes == null) {
163            if (other.nodes != null)
164                return false;
165        } else if (!nodes.equals(other.nodes))
166            return false;
167        if (oldStates == null) {
168            if (other.oldStates != null)
169                return false;
170        } else if (!oldStates.equals(other.oldStates))
171            return false;
172        return true;
173    }
174}