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}