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.ArrayList; 007import java.util.Collection; 008import java.util.HashSet; 009import java.util.List; 010import java.util.Objects; 011 012import javax.swing.Icon; 013 014import org.openstreetmap.josm.data.osm.DataSet; 015import org.openstreetmap.josm.data.osm.Node; 016import org.openstreetmap.josm.data.osm.NodeData; 017import org.openstreetmap.josm.data.osm.OsmPrimitive; 018import org.openstreetmap.josm.data.osm.PrimitiveData; 019import org.openstreetmap.josm.gui.layer.OsmDataLayer; 020import org.openstreetmap.josm.tools.CheckParameterUtil; 021 022/** 023 * Add primitives to a data layer. 024 * @since 2305 025 */ 026public class AddPrimitivesCommand extends Command { 027 028 private List<PrimitiveData> data = new ArrayList<>(); 029 private Collection<PrimitiveData> toSelect = new ArrayList<>(); 030 031 // only filled on undo 032 private List<OsmPrimitive> createdPrimitives; 033 private Collection<OsmPrimitive> createdPrimitivesToSelect; 034 035 /** 036 * Constructs a new {@code AddPrimitivesCommand} to add data to the current edit layer. 037 * @param data The OSM primitives data to add. Must not be {@code null} 038 */ 039 public AddPrimitivesCommand(List<PrimitiveData> data) { 040 this(data, data); 041 } 042 043 /** 044 * Constructs a new {@code AddPrimitivesCommand} to add data to the current edit layer. 045 * @param data The OSM primitives to add. Must not be {@code null} 046 * @param toSelect The OSM primitives to select at the end. Can be {@code null} 047 * @since 5953 048 */ 049 public AddPrimitivesCommand(List<PrimitiveData> data, List<PrimitiveData> toSelect) { 050 init(data, toSelect); 051 } 052 053 /** 054 * Constructs a new {@code AddPrimitivesCommand} to add data to the given layer. 055 * @param data The OSM primitives data to add. Must not be {@code null} 056 * @param toSelect The OSM primitives to select at the end. Can be {@code null} 057 * @param layer The target data layer. Must not be {@code null} 058 */ 059 public AddPrimitivesCommand(List<PrimitiveData> data, List<PrimitiveData> toSelect, OsmDataLayer layer) { 060 super(layer); 061 init(data, toSelect); 062 } 063 064 private void init(List<PrimitiveData> data, List<PrimitiveData> toSelect) { 065 CheckParameterUtil.ensureParameterNotNull(data, "data"); 066 this.data.addAll(data); 067 if (toSelect != null) { 068 this.toSelect.addAll(toSelect); 069 } 070 } 071 072 @Override 073 public boolean executeCommand() { 074 Collection<OsmPrimitive> primitivesToSelect; 075 if (createdPrimitives == null) { // first time execution 076 List<OsmPrimitive> newPrimitives = new ArrayList<>(data.size()); 077 primitivesToSelect = new ArrayList<>(toSelect.size()); 078 079 for (PrimitiveData pd : data) { 080 OsmPrimitive primitive = getAffectedDataSet().getPrimitiveById(pd); 081 boolean created = primitive == null; 082 if (created) { 083 primitive = pd.getType().newInstance(pd.getUniqueId(), true); 084 } 085 if (pd instanceof NodeData) { // Load nodes immediately because they can't be added to dataset without coordinates 086 primitive.load(pd); 087 } 088 if (created) { 089 getAffectedDataSet().addPrimitive(primitive); 090 } 091 newPrimitives.add(primitive); 092 if (toSelect.contains(pd)) { 093 primitivesToSelect.add(primitive); 094 } 095 } 096 097 // Then load ways and relations 098 for (int i = 0; i < newPrimitives.size(); i++) { 099 if (!(newPrimitives.get(i) instanceof Node)) { 100 newPrimitives.get(i).load(data.get(i)); 101 } 102 } 103 } else { // redo 104 // When redoing this command, we have to add the same objects, otherwise 105 // a subsequent command (e.g. MoveCommand) cannot be redone. 106 for (OsmPrimitive osm : createdPrimitives) { 107 getLayer().data.addPrimitive(osm); 108 } 109 primitivesToSelect = createdPrimitivesToSelect; 110 } 111 112 getLayer().data.setSelected(primitivesToSelect); 113 return true; 114 } 115 116 @Override public void undoCommand() { 117 DataSet ds = getAffectedDataSet(); 118 119 if (createdPrimitives == null) { 120 createdPrimitives = new ArrayList<>(data.size()); 121 createdPrimitivesToSelect = new ArrayList<>(toSelect.size()); 122 123 for (PrimitiveData pd : data) { 124 OsmPrimitive p = ds.getPrimitiveById(pd); 125 createdPrimitives.add(p); 126 if (toSelect.contains(pd)) { 127 createdPrimitivesToSelect.add(p); 128 } 129 } 130 createdPrimitives = PurgeCommand.topoSort(createdPrimitives); 131 132 for (PrimitiveData p : data) { 133 ds.removePrimitive(p); 134 } 135 data = null; 136 toSelect = null; 137 138 } else { 139 for (OsmPrimitive osm : createdPrimitives) { 140 ds.removePrimitive(osm); 141 } 142 } 143 } 144 145 @Override 146 public String getDescriptionText() { 147 int size = data != null ? data.size() : createdPrimitives.size(); 148 return trn("Added {0} object", "Added {0} objects", size, size); 149 } 150 151 @Override 152 public Icon getDescriptionIcon() { 153 return null; 154 } 155 156 @Override 157 public void fillModifiedData(Collection<OsmPrimitive> modified, Collection<OsmPrimitive> deleted, 158 Collection<OsmPrimitive> added) { 159 // Does nothing because we don't want to create OsmPrimitives. 160 } 161 162 @Override 163 public Collection<? extends OsmPrimitive> getParticipatingPrimitives() { 164 if (createdPrimitives != null) 165 return createdPrimitives; 166 167 Collection<OsmPrimitive> prims = new HashSet<>(); 168 for (PrimitiveData d : data) { 169 OsmPrimitive osm = getAffectedDataSet().getPrimitiveById(d); 170 if (osm == null) 171 throw new RuntimeException(); 172 prims.add(osm); 173 } 174 return prims; 175 } 176 177 @Override 178 public int hashCode() { 179 return Objects.hash(super.hashCode(), data, toSelect, createdPrimitives, createdPrimitivesToSelect); 180 } 181 182 @Override 183 public boolean equals(Object obj) { 184 if (this == obj) return true; 185 if (obj == null || getClass() != obj.getClass()) return false; 186 if (!super.equals(obj)) return false; 187 AddPrimitivesCommand that = (AddPrimitivesCommand) obj; 188 return Objects.equals(data, that.data) && 189 Objects.equals(toSelect, that.toSelect) && 190 Objects.equals(createdPrimitives, that.createdPrimitives) && 191 Objects.equals(createdPrimitivesToSelect, that.createdPrimitivesToSelect); 192 } 193}