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