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