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 newPrimitives.stream().forEach(p -> p.setModified(true)); 104 } else { // redo 105 // When redoing this command, we have to add the same objects, otherwise 106 // a subsequent command (e.g. MoveCommand) cannot be redone. 107 for (OsmPrimitive osm : createdPrimitives) { 108 getLayer().data.addPrimitive(osm); 109 } 110 primitivesToSelect = createdPrimitivesToSelect; 111 } 112 113 getLayer().data.setSelected(primitivesToSelect); 114 return true; 115 } 116 117 @Override public void undoCommand() { 118 DataSet ds = getAffectedDataSet(); 119 120 if (createdPrimitives == null) { 121 createdPrimitives = new ArrayList<>(data.size()); 122 createdPrimitivesToSelect = new ArrayList<>(toSelect.size()); 123 124 for (PrimitiveData pd : data) { 125 OsmPrimitive p = ds.getPrimitiveById(pd); 126 createdPrimitives.add(p); 127 if (toSelect.contains(pd)) { 128 createdPrimitivesToSelect.add(p); 129 } 130 } 131 createdPrimitives = PurgeCommand.topoSort(createdPrimitives); 132 133 for (PrimitiveData p : data) { 134 ds.removePrimitive(p); 135 } 136 data = null; 137 toSelect = null; 138 139 } else { 140 for (OsmPrimitive osm : createdPrimitives) { 141 ds.removePrimitive(osm); 142 } 143 } 144 } 145 146 @Override 147 public String getDescriptionText() { 148 int size = data != null ? data.size() : createdPrimitives.size(); 149 return trn("Added {0} object", "Added {0} objects", size, size); 150 } 151 152 @Override 153 public Icon getDescriptionIcon() { 154 return null; 155 } 156 157 @Override 158 public void fillModifiedData(Collection<OsmPrimitive> modified, Collection<OsmPrimitive> deleted, 159 Collection<OsmPrimitive> added) { 160 // Does nothing because we don't want to create OsmPrimitives. 161 } 162 163 @Override 164 public Collection<? extends OsmPrimitive> getParticipatingPrimitives() { 165 if (createdPrimitives != null) 166 return createdPrimitives; 167 168 Collection<OsmPrimitive> prims = new HashSet<>(); 169 for (PrimitiveData d : data) { 170 OsmPrimitive osm = getAffectedDataSet().getPrimitiveById(d); 171 if (osm == null) 172 throw new RuntimeException(); 173 prims.add(osm); 174 } 175 return prims; 176 } 177 178 @Override 179 public int hashCode() { 180 return Objects.hash(super.hashCode(), data, toSelect, createdPrimitives, createdPrimitivesToSelect); 181 } 182 183 @Override 184 public boolean equals(Object obj) { 185 if (this == obj) return true; 186 if (obj == null || getClass() != obj.getClass()) return false; 187 if (!super.equals(obj)) return false; 188 AddPrimitivesCommand that = (AddPrimitivesCommand) obj; 189 return Objects.equals(data, that.data) && 190 Objects.equals(toSelect, that.toSelect) && 191 Objects.equals(createdPrimitives, that.createdPrimitives) && 192 Objects.equals(createdPrimitivesToSelect, that.createdPrimitivesToSelect); 193 } 194}