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}