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