source: subversion/applications/editors/josm/plugins/undelete/src/org/openstreetmap/josm/plugins/undelete/UndeleteAction.java

Last change on this file was 34957, checked in by gerdp, 3 weeks ago

fix checkstyle issue (tabs instead of blanks), no new dist needed

File size: 12.5 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.plugins.undelete;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.event.ActionEvent;
7import java.awt.event.KeyEvent;
8import java.util.ArrayList;
9import java.util.Collections;
10import java.util.LinkedHashSet;
11import java.util.List;
12import java.util.Set;
13
14import javax.swing.JOptionPane;
15
16import org.openstreetmap.josm.actions.AutoScaleAction;
17import org.openstreetmap.josm.actions.AutoScaleAction.AutoScaleMode;
18import org.openstreetmap.josm.actions.JosmAction;
19import org.openstreetmap.josm.data.osm.DataSet;
20import org.openstreetmap.josm.data.osm.Node;
21import org.openstreetmap.josm.data.osm.NodeData;
22import org.openstreetmap.josm.data.osm.OsmPrimitive;
23import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
24import org.openstreetmap.josm.data.osm.PrimitiveId;
25import org.openstreetmap.josm.data.osm.Relation;
26import org.openstreetmap.josm.data.osm.RelationMember;
27import org.openstreetmap.josm.data.osm.RelationMemberData;
28import org.openstreetmap.josm.data.osm.SimplePrimitiveId;
29import org.openstreetmap.josm.data.osm.Way;
30import org.openstreetmap.josm.data.osm.history.History;
31import org.openstreetmap.josm.data.osm.history.HistoryDataSet;
32import org.openstreetmap.josm.data.osm.history.HistoryNode;
33import org.openstreetmap.josm.data.osm.history.HistoryOsmPrimitive;
34import org.openstreetmap.josm.data.osm.history.HistoryRelation;
35import org.openstreetmap.josm.data.osm.history.HistoryWay;
36import org.openstreetmap.josm.gui.MainApplication;
37import org.openstreetmap.josm.gui.Notification;
38import org.openstreetmap.josm.gui.history.HistoryLoadTask;
39import org.openstreetmap.josm.gui.io.DownloadPrimitivesTask;
40import org.openstreetmap.josm.gui.layer.OsmDataLayer;
41import org.openstreetmap.josm.gui.util.GuiHelper;
42import org.openstreetmap.josm.spi.preferences.Config;
43import org.openstreetmap.josm.tools.Logging;
44import org.openstreetmap.josm.tools.Shortcut;
45
46/**
47 * Undelete one or more objects.
48 */
49public class UndeleteAction extends JosmAction {
50
51    private final class Worker implements Runnable {
52        private final OsmPrimitive parent;
53
54        private final OsmDataLayer layer;
55
56        private final List<PrimitiveId> ids;
57
58        private Set<OsmPrimitive> restored;
59
60        private Worker(OsmPrimitive parent, OsmDataLayer layer, List<PrimitiveId> ids, Set<OsmPrimitive> restored) {
61            this.parent = parent;
62            this.layer = layer;
63            this.ids = ids;
64            this.restored = restored != null ? restored : new LinkedHashSet<>();
65        }
66
67        @Override
68        public void run() {
69            List<Node> nodes = new ArrayList<>();
70            for (PrimitiveId pid : ids) {
71                OsmPrimitive primitive = layer.data.getPrimitiveById(pid);
72                if (primitive == null) {
73                    try {
74                        final Long id = pid.getUniqueId();
75                        final OsmPrimitiveType type = pid.getType();
76
77                        History h = HistoryDataSet.getInstance().getHistory(id, type);
78
79                        if (h == null) {
80                            Logging.warn("Cannot find history for " + type + " " + id);
81                            return;
82                        }
83
84                        HistoryOsmPrimitive hPrimitive1 = h.getLatest();
85                        HistoryOsmPrimitive hPrimitive2 = null;
86
87                        boolean visible = hPrimitive1.isVisible();
88
89                        if (visible) {
90                            if (type == OsmPrimitiveType.NODE) {
91                                // we have all needed information in the history object
92                                primitive = new Node(hPrimitive1.getId());
93                                HistoryNode hNode = (HistoryNode) hPrimitive1;
94                                primitive.load(hNode.fillPrimitiveData(new NodeData()));
95                                layer.data.addPrimitive(primitive);
96                            } else {
97                                // If the way or relation is not deleted we get the real object
98                                DownloadPrimitivesTask download = new DownloadPrimitivesTask(layer, Collections.singletonList(pid), true);
99                                download.setZoom(false);
100                                download.run();
101                                primitive = layer.data.getPrimitiveById(id, type);
102                            }
103                            restored.add(primitive);
104                        } else {
105                            // We search n-1 version with redaction robustness
106                            long idx = 1;
107                            long n = hPrimitive1.getVersion();
108                            while (hPrimitive2 == null && idx < n) {
109                                hPrimitive2 = h.getByVersion(n - idx++);
110                            }
111                            if (type == OsmPrimitiveType.NODE) {
112                                // We get version and user from the latest version,
113                                // coordinates and tags from n-1 version
114                                Node node = new Node(id, (int) hPrimitive1.getVersion());
115
116                                HistoryNode hNode = (HistoryNode) hPrimitive2;
117                                if (hNode != null) {
118                                    node.setCoor(hNode.getCoords());
119                                }
120
121                                primitive = node;
122                            } else if (type == OsmPrimitiveType.WAY) {
123                                // We get version and user from the latest version,
124                                // nodes and tags from n-1 version
125                                hPrimitive1 = h.getLatest();
126
127                                Way way = new Way(id, (int) hPrimitive1.getVersion());
128
129                                HistoryWay hWay = (HistoryWay) hPrimitive2;
130                                List<PrimitiveId> nodeIds = new ArrayList<>();
131                                if (hWay != null) {
132                                    for (Long i : hWay.getNodes()) {
133                                        nodeIds.add(new SimplePrimitiveId(i, OsmPrimitiveType.NODE));
134                                    }
135                                }
136                                undelete(false, nodeIds, way, restored);
137
138                                primitive = way;
139                            } else {
140                                primitive = new Relation();
141                                hPrimitive1 = h.getLatest();
142
143                                Relation rel = new Relation(id, (int) hPrimitive1.getVersion());
144                                HistoryRelation hRel = (HistoryRelation) hPrimitive2;
145
146                                if (hRel != null) {
147                                    List<RelationMember> members = new ArrayList<>(hRel.getNumMembers());
148                                    for (RelationMemberData m : hRel.getMembers()) {
149                                        OsmPrimitive p = layer.data.getPrimitiveById(m.getMemberId(), m.getMemberType());
150                                        if (p == null) {
151                                            switch (m.getMemberType()) {
152                                            case NODE:
153                                                p = new Node(m.getMemberId());
154                                                break;
155                                            case CLOSEDWAY:
156                                            case WAY:
157                                                p = new Way(m.getMemberId());
158                                                break;
159                                            case MULTIPOLYGON:
160                                            case RELATION:
161                                                p = new Relation(m.getMemberId());
162                                                break;
163                                            }
164                                            layer.data.addPrimitive(p);
165                                            restored.add(p);
166                                        }
167                                        members.add(new RelationMember(m.getRole(), p));
168                                    }
169                                    rel.setMembers(members);
170                                }
171                                primitive = rel;
172                            }
173
174                            if (hPrimitive2 != null) {
175                                primitive.setChangesetId((int) hPrimitive1.getChangesetId());
176                                primitive.setTimestamp(hPrimitive1.getTimestamp());
177                                primitive.setUser(hPrimitive1.getUser());
178                                primitive.setVisible(hPrimitive1.isVisible());
179                                primitive.setKeys(hPrimitive2.getTags());
180                                primitive.setModified(true);
181
182                                layer.data.addPrimitive(primitive);
183                                restored.add(primitive);
184                            } else {
185                              final String msg = OsmPrimitiveType.NODE == type
186                                  ? tr("Unable to undelete node {0}. Object has likely been redacted", id)
187                                  : OsmPrimitiveType.WAY == type
188                                  ? tr("Unable to undelete way {0}. Object has likely been redacted", id)
189                                  : OsmPrimitiveType.RELATION == type
190                                  ? tr("Unable to undelete relation {0}. Object has likely been redacted", id)
191                                  : null;
192                                GuiHelper.runInEDT(() -> new Notification(msg).setIcon(JOptionPane.WARNING_MESSAGE).show());
193                                Logging.warn(msg);
194                            }
195                        }
196                    } catch (Exception e) {
197                        Logging.error(e);
198                    }
199                }
200                if (parent != null && primitive instanceof Node) {
201                    nodes.add((Node) primitive);
202                }
203            }
204            if (parent instanceof Way && !nodes.isEmpty()) {
205                ((Way) parent).setNodes(nodes);
206            }
207            if (!restored.isEmpty()) {
208                GuiHelper.runInEDT(() -> {
209                    layer.data.setSelected(restored);
210                    AutoScaleAction.autoScale(AutoScaleMode.SELECTION);
211                });
212            }
213        }
214    }
215
216    /**
217     * Create undelete action.
218     */
219    public UndeleteAction() {
220        super(tr("Undelete object..."), "undelete", tr("Undelete object by id"),
221                Shortcut.registerShortcut("tools:undelete", tr("File: {0}", tr("Undelete object...")), KeyEvent.VK_U, Shortcut.ALT_SHIFT), true);
222    }
223
224    @Override
225    public void actionPerformed(ActionEvent e) {
226        UndeleteDialog dialog = new UndeleteDialog(MainApplication.getMainFrame());
227        if (dialog.showDialog().getValue() != 1)
228            return;
229        Config.getPref().putBoolean("undelete.newlayer", dialog.isNewLayerSelected());
230        Config.getPref().put("undelete.osmid", dialog.getOsmIdsString());
231        undelete(dialog.isNewLayerSelected(), dialog.getOsmIds(), null);
232    }
233
234    public void undelete(boolean newLayer, final List<PrimitiveId> ids, final OsmPrimitive parent) {
235        undelete(newLayer, ids, parent, new LinkedHashSet<>());
236    }
237
238    private void undelete(boolean newLayer, final List<PrimitiveId> ids, final OsmPrimitive parent, Set<OsmPrimitive> restored) {
239
240        // TODO: undelete relation members if necessary
241        Logging.info("Undeleting "+ids+(parent == null ? "" : " with parent "+parent));
242
243        OsmDataLayer tmpLayer = MainApplication.getLayerManager().getEditLayer();
244        if ((tmpLayer == null) || newLayer) {
245            tmpLayer = new OsmDataLayer(new DataSet(), OsmDataLayer.createNewName(), null);
246            MainApplication.getLayerManager().addLayer(tmpLayer);
247        }
248
249        final OsmDataLayer layer = tmpLayer;
250
251        HistoryLoadTask task = new HistoryLoadTask();
252        task.setChangesetDataNeeded(false);
253        for (PrimitiveId id : ids) {
254            task.add(id);
255        }
256
257        MainApplication.worker.execute(task);
258        MainApplication.worker.submit(new Worker(parent, layer, ids, restored));
259    }
260}
Note: See TracBrowser for help on using the repository browser.