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     * @param objects objects to fetch nodes from
049     */
050    public TransformNodesCommand(Collection<OsmPrimitive> objects) {
051        this.nodes = AllNodesVisitor.getAllNodes(objects);
052        storeOldState();
053    }
054
055    /**
056     * Handling of a mouse event (e.g. dragging event).
057     * @param currentEN the current world position of the mouse
058     */
059    public abstract void handleEvent(EastNorth currentEN);
060
061    /**
062     * Implementation for the nodes transformation.
063     * No parameters are given here, you should handle the user input in handleEvent()
064     * and store it internally.
065     */
066    protected abstract void transformNodes();
067
068    /**
069     * Finally apply the transformation of the nodes.
070     * This is called when the user is happy with the current state of the command
071     * and its effects.
072     */
073    @Override
074    public boolean executeCommand() {
075        transformNodes();
076        flagNodesAsModified();
077        return true;
078    }
079
080    /**
081     * Flag all nodes as modified.
082     */
083    public void flagNodesAsModified() {
084        for (Node n : nodes) {
085            n.setModified(true);
086        }
087    }
088
089    /**
090     * Restore the state of the nodes from the backup.
091     */
092    @Override
093    public void undoCommand() {
094        for (Node n : nodes) {
095            OldNodeState os = oldStates.get(n);
096            n.setCoor(os.getLatlon());
097            n.setModified(os.isModified());
098        }
099    }
100
101    @Override
102    public void fillModifiedData(Collection<OsmPrimitive> modified, Collection<OsmPrimitive> deleted, Collection<OsmPrimitive> added) {
103    }
104
105    @Override
106    public Collection<? extends OsmPrimitive> getParticipatingPrimitives() {
107        return nodes;
108    }
109
110    @Override
111    public String getDescriptionText() {
112        return trn("Transform {0} node", "Transform {0} nodes", nodes.size(), nodes.size());
113    }
114
115    @Override
116    public Icon getDescriptionIcon() {
117        return ImageProvider.get("data", "node");
118    }
119
120    /**
121     * Get the nodes with the current transformation applied.
122     * @return nodes with the current transformation applied
123     */
124    public Collection<Node> getTransformedNodes() {
125        return nodes;
126    }
127
128    /**
129     * Get the center of the nodes under modification.
130     * It's just the barycenter.
131     * @return center east/north of the nodes under modification
132     * @see org.openstreetmap.josm.tools.Geometry#getCentroid(java.util.List)
133     */
134    public EastNorth getNodesCenter() {
135        EastNorth sum = new EastNorth(0, 0);
136
137        for (Node n : nodes) {
138            EastNorth en = n.getEastNorth();
139            sum = sum.add(en.east(), en.north());
140        }
141        return new EastNorth(sum.east()/this.nodes.size(), sum.north()/this.nodes.size());
142
143    }
144
145    @Override
146    public int hashCode() {
147        final int prime = 31;
148        int result = super.hashCode();
149        result = prime * result + ((nodes == null) ? 0 : nodes.hashCode());
150        result = prime * result + ((oldStates == null) ? 0 : oldStates.hashCode());
151        return result;
152    }
153
154    @Override
155    public boolean equals(Object obj) {
156        if (this == obj)
157            return true;
158        if (!super.equals(obj))
159            return false;
160        if (getClass() != obj.getClass())
161            return false;
162        TransformNodesCommand other = (TransformNodesCommand) obj;
163        if (nodes == null) {
164            if (other.nodes != null)
165                return false;
166        } else if (!nodes.equals(other.nodes))
167            return false;
168        if (oldStates == null) {
169            if (other.oldStates != null)
170                return false;
171        } else if (!oldStates.equals(other.oldStates))
172            return false;
173        return true;
174    }
175}