1 | package org.openstreetmap.josm.actions;
|
---|
2 |
|
---|
3 | import static org.openstreetmap.josm.tools.I18n.tr;
|
---|
4 |
|
---|
5 | import java.awt.event.ActionEvent;
|
---|
6 | import java.io.File;
|
---|
7 | import java.io.FileReader;
|
---|
8 | import java.io.IOException;
|
---|
9 | import java.io.PrintWriter;
|
---|
10 | import java.util.Arrays;
|
---|
11 | import java.util.Collection;
|
---|
12 | import java.util.HashSet;
|
---|
13 | import java.util.Iterator;
|
---|
14 | import java.util.LinkedList;
|
---|
15 | import java.util.Stack;
|
---|
16 |
|
---|
17 | import javax.swing.AbstractAction;
|
---|
18 | import javax.swing.JMenu;
|
---|
19 | import javax.swing.JOptionPane;
|
---|
20 |
|
---|
21 | import org.openstreetmap.josm.Main;
|
---|
22 | import org.openstreetmap.josm.command.AddCommand;
|
---|
23 | import org.openstreetmap.josm.command.ChangeCommand;
|
---|
24 | import org.openstreetmap.josm.command.Command;
|
---|
25 | import org.openstreetmap.josm.command.DeleteCommand;
|
---|
26 | import org.openstreetmap.josm.command.SequenceCommand;
|
---|
27 | import org.openstreetmap.josm.data.Bounds;
|
---|
28 | import org.openstreetmap.josm.data.coor.LatLon;
|
---|
29 | import org.openstreetmap.josm.data.osm.DataSet;
|
---|
30 | import org.openstreetmap.josm.data.osm.Node;
|
---|
31 | import org.openstreetmap.josm.data.osm.OsmPrimitive;
|
---|
32 | import org.openstreetmap.josm.data.osm.Segment;
|
---|
33 | import org.openstreetmap.josm.data.osm.Way;
|
---|
34 | import org.openstreetmap.josm.data.osm.visitor.AddVisitor;
|
---|
35 | import org.openstreetmap.josm.data.osm.visitor.AllNodesVisitor;
|
---|
36 | import org.openstreetmap.josm.data.osm.visitor.CollectBackReferencesVisitor;
|
---|
37 | import org.openstreetmap.josm.gui.PleaseWaitRunnable;
|
---|
38 | import org.openstreetmap.josm.gui.layer.Layer;
|
---|
39 | import org.openstreetmap.josm.gui.layer.RawGpsLayer;
|
---|
40 | import org.openstreetmap.josm.gui.layer.RawGpsLayer.GpsPoint;
|
---|
41 | import org.openstreetmap.josm.io.GpxWriter;
|
---|
42 | import org.openstreetmap.josm.io.OsmReader;
|
---|
43 | import org.openstreetmap.josm.io.OsmWriter;
|
---|
44 | import org.xml.sax.Attributes;
|
---|
45 | import org.xml.sax.SAXException;
|
---|
46 |
|
---|
47 | import uk.co.wilson.xml.MinML2;
|
---|
48 |
|
---|
49 | /**
|
---|
50 | * Launches external tools configured in the preferences.
|
---|
51 | *
|
---|
52 | * @author Imi
|
---|
53 | */
|
---|
54 | public class ExternalToolsAction extends AbstractAction {
|
---|
55 |
|
---|
56 | private final Collection<String> flags;
|
---|
57 | private final Collection<String> input;
|
---|
58 | private final String output;
|
---|
59 | private final String[] exec;
|
---|
60 |
|
---|
61 | private final class ExecuteToolRunner extends PleaseWaitRunnable {
|
---|
62 | private final Process p;
|
---|
63 | private DataSet dataSet;
|
---|
64 | private DataSet fromDataSet;
|
---|
65 | private ExecuteToolRunner(String msg, Process p) {
|
---|
66 | super(msg);
|
---|
67 | this.p = p;
|
---|
68 | currentAction.setText(tr("Executing {0}",getValue(NAME)));
|
---|
69 | }
|
---|
70 |
|
---|
71 | @Override protected void realRun() throws SAXException, IOException {
|
---|
72 | if (!input.isEmpty()) {
|
---|
73 | fromDataSet = new DataSet();
|
---|
74 | final Collection<GpsPoint> gpxPoints = new LinkedList<GpsPoint>();
|
---|
75 | final boolean addOsm = !flags.contains("noosm");
|
---|
76 | final boolean addGpx = flags.contains("gpx");
|
---|
77 |
|
---|
78 | AddVisitor adder = new AddVisitor(fromDataSet);
|
---|
79 | if (flags.contains("include_references")) {
|
---|
80 | adder = new AddVisitor(fromDataSet){
|
---|
81 | @Override public void visit(Node n) {
|
---|
82 | if (!ds.nodes.contains(n))
|
---|
83 | super.visit(n);
|
---|
84 | }
|
---|
85 | @Override public void visit(Segment s) {
|
---|
86 | super.visit(s);
|
---|
87 | if (!s.incomplete) {
|
---|
88 | if (!ds.nodes.contains(s.from))
|
---|
89 | s.from.visit(this);
|
---|
90 | if (!ds.nodes.contains(s.to))
|
---|
91 | s.to.visit(this);
|
---|
92 | }
|
---|
93 | }
|
---|
94 | @Override public void visit(Way w) {
|
---|
95 | super.visit(w);
|
---|
96 | for (Segment s : w.segments)
|
---|
97 | if (!ds.segments.contains(s))
|
---|
98 | s.visit(this);
|
---|
99 | }
|
---|
100 | };
|
---|
101 | }
|
---|
102 | if (input.contains("selection")) {
|
---|
103 | Collection<OsmPrimitive> sel = Main.ds.getSelected();
|
---|
104 | if (addOsm) {
|
---|
105 | for (OsmPrimitive osm : sel)
|
---|
106 | osm.visit(adder);
|
---|
107 | if (flags.contains("include_back_references")) {
|
---|
108 | CollectBackReferencesVisitor v = new CollectBackReferencesVisitor(Main.ds);
|
---|
109 | for (OsmPrimitive osm : sel)
|
---|
110 | osm.visit(v);
|
---|
111 | AddVisitor a = new AddVisitor(fromDataSet);
|
---|
112 | for (OsmPrimitive osm : v.data)
|
---|
113 | osm.visit(a);
|
---|
114 | }
|
---|
115 | }
|
---|
116 | if (addGpx) {
|
---|
117 | AllNodesVisitor v = new AllNodesVisitor();
|
---|
118 | for (OsmPrimitive osm : sel)
|
---|
119 | osm.visit(v);
|
---|
120 | Bounds b = new Bounds();
|
---|
121 | for (Node n : v.nodes)
|
---|
122 | b.extend(n.coor);
|
---|
123 | for (Layer l : Main.map.mapView.getAllLayers()) {
|
---|
124 | if (!(l instanceof RawGpsLayer))
|
---|
125 | continue;
|
---|
126 | RawGpsLayer layer = (RawGpsLayer)l;
|
---|
127 | for (Collection<GpsPoint> c : layer.data)
|
---|
128 | for (GpsPoint p : c)
|
---|
129 | if (p.latlon.isWithin(b))
|
---|
130 | gpxPoints.add(p);
|
---|
131 | }
|
---|
132 | }
|
---|
133 | }
|
---|
134 | if (input.contains("all")) {
|
---|
135 | if (addOsm)
|
---|
136 | for (OsmPrimitive osm : Main.ds.allPrimitives())
|
---|
137 | osm.visit(adder);
|
---|
138 | for (Layer l : Main.map.mapView.getAllLayers())
|
---|
139 | if (l instanceof RawGpsLayer)
|
---|
140 | for (Collection<GpsPoint> c : ((RawGpsLayer)l).data)
|
---|
141 | for (GpsPoint p : c)
|
---|
142 | gpxPoints.add(p);
|
---|
143 | }
|
---|
144 | if (input.contains("screen")) {
|
---|
145 | if (Main.map == null) {
|
---|
146 | errorMessage = tr("The Tool requires some data to be loaded.");
|
---|
147 | cancel();
|
---|
148 | return;
|
---|
149 | }
|
---|
150 | LatLon bottomLeft = Main.map.mapView.getLatLon(0,Main.map.mapView.getHeight());
|
---|
151 | LatLon topRight = Main.map.mapView.getLatLon(Main.map.mapView.getWidth(), 0);
|
---|
152 | Bounds b = new Bounds(bottomLeft, topRight);
|
---|
153 | if (addOsm) {
|
---|
154 | Collection<Node> nodes = new HashSet<Node>();
|
---|
155 | for (Node n : Main.ds.nodes) {
|
---|
156 | if (n.coor.isWithin(b)) {
|
---|
157 | n.visit(adder);
|
---|
158 | nodes.add(n);
|
---|
159 | }
|
---|
160 | }
|
---|
161 | Collection<Segment> segments = new HashSet<Segment>();
|
---|
162 | for (Segment s : Main.ds.segments) {
|
---|
163 | if (nodes.contains(s.from) || nodes.contains(s.to)) {
|
---|
164 | s.visit(adder);
|
---|
165 | segments.add(s);
|
---|
166 | }
|
---|
167 | }
|
---|
168 | for (Way w : Main.ds.ways) {
|
---|
169 | for (Segment s : w.segments) {
|
---|
170 | if (segments.contains(s)) {
|
---|
171 | w.visit(adder);
|
---|
172 | break;
|
---|
173 | }
|
---|
174 | }
|
---|
175 | }
|
---|
176 | }
|
---|
177 | if (addGpx) {
|
---|
178 | for (Layer l : Main.map.mapView.getAllLayers())
|
---|
179 | if (l instanceof RawGpsLayer)
|
---|
180 | for (Collection<GpsPoint> c : ((RawGpsLayer)l).data)
|
---|
181 | for (GpsPoint p : c)
|
---|
182 | if (p.latlon.isWithin(b))
|
---|
183 | gpxPoints.add(p);
|
---|
184 | }
|
---|
185 | }
|
---|
186 | OsmWriter.output(p.getOutputStream(), new OsmWriter.Osm(){
|
---|
187 | public void write(PrintWriter out) {
|
---|
188 | if (addOsm)
|
---|
189 | new OsmWriter.All(fromDataSet, false).write(out);
|
---|
190 | if (addGpx) {
|
---|
191 | Collection<Collection<GpsPoint>> c = new LinkedList<Collection<GpsPoint>>();
|
---|
192 | c.add(gpxPoints);
|
---|
193 | GpxWriter.Trk writer = new GpxWriter.Trk(c);
|
---|
194 | writer.header(out);
|
---|
195 | if (!gpxPoints.isEmpty())
|
---|
196 | writer.write(out);
|
---|
197 | writer.footer(out);
|
---|
198 | }
|
---|
199 | }
|
---|
200 | });
|
---|
201 | }
|
---|
202 | if (output != null)
|
---|
203 | dataSet = OsmReader.parseDataSet(p.getInputStream(), currentAction, progress);
|
---|
204 | }
|
---|
205 |
|
---|
206 | @Override protected void cancel() {
|
---|
207 | p.destroy();
|
---|
208 | }
|
---|
209 |
|
---|
210 | @Override protected void finish() {
|
---|
211 | if (dataSet == null || output == null || output.equals("discard"))
|
---|
212 | return; // user cancelled or no stdout to process
|
---|
213 | Collection<OsmPrimitive> allNew = dataSet.allPrimitives();
|
---|
214 | Collection<OsmPrimitive> allOld = fromDataSet.allPrimitives();
|
---|
215 | if (output.equals("replace")) {
|
---|
216 | Command cmd = createCommand(allOld, allNew);
|
---|
217 | if (cmd != null) {
|
---|
218 | Main.main.editLayer().add(cmd);
|
---|
219 | Main.ds.clearSelection();
|
---|
220 | }
|
---|
221 | } else if (output.equals("selection")) {
|
---|
222 | Collection<OsmPrimitive> sel = new LinkedList<OsmPrimitive>();
|
---|
223 | for (OsmPrimitive osm : Main.ds.allPrimitives())
|
---|
224 | if (allNew.contains(osm))
|
---|
225 | sel.add(osm);
|
---|
226 | Main.ds.setSelected(sel);
|
---|
227 | }
|
---|
228 | }
|
---|
229 |
|
---|
230 | /**
|
---|
231 | * Create a command that replaces all objects in from with those in to. The lists will be
|
---|
232 | * changed by createCommand.
|
---|
233 | */
|
---|
234 | private Command createCommand(Collection<OsmPrimitive> from, Collection<OsmPrimitive> to) {
|
---|
235 | // remove all objects in from/to, that are present in both lists.
|
---|
236 | for (Iterator<OsmPrimitive> toIt = to.iterator(); toIt.hasNext();) {
|
---|
237 | OsmPrimitive osm = toIt.next();
|
---|
238 | for (Iterator<OsmPrimitive> fromIt = from.iterator(); fromIt.hasNext();) {
|
---|
239 | if (fromIt.next().realEqual(osm)) {
|
---|
240 | toIt.remove();
|
---|
241 | fromIt.remove();
|
---|
242 | break;
|
---|
243 | }
|
---|
244 | }
|
---|
245 | }
|
---|
246 |
|
---|
247 | Collection<Command> cmd = new LinkedList<Command>();
|
---|
248 |
|
---|
249 | // extract all objects that have changed
|
---|
250 | for (Iterator<OsmPrimitive> toIt = to.iterator(); toIt.hasNext();) {
|
---|
251 | OsmPrimitive toOsm = toIt.next();
|
---|
252 | for (Iterator<OsmPrimitive> fromIt = from.iterator(); fromIt.hasNext();) {
|
---|
253 | OsmPrimitive fromOsm = fromIt.next();
|
---|
254 | if (fromOsm.equals(toOsm)) {
|
---|
255 | toIt.remove();
|
---|
256 | fromIt.remove();
|
---|
257 | cmd.add(new ChangeCommand(fromOsm, toOsm));
|
---|
258 | break;
|
---|
259 | }
|
---|
260 | }
|
---|
261 | }
|
---|
262 | for (OsmPrimitive fromOsm : Main.ds.allPrimitives()) {
|
---|
263 | for (Iterator<OsmPrimitive> it = to.iterator(); it.hasNext();) {
|
---|
264 | OsmPrimitive toOsm = it.next();
|
---|
265 | if (fromOsm.equals(toOsm)) {
|
---|
266 | it.remove();
|
---|
267 | cmd.add(new ChangeCommand(fromOsm, toOsm));
|
---|
268 | break;
|
---|
269 | }
|
---|
270 | }
|
---|
271 | }
|
---|
272 |
|
---|
273 | // extract all added objects
|
---|
274 | for (OsmPrimitive osm : to)
|
---|
275 | cmd.add(new AddCommand(osm));
|
---|
276 |
|
---|
277 | // extract all deleted objects. Delete references as well.
|
---|
278 | CollectBackReferencesVisitor v = new CollectBackReferencesVisitor(Main.ds);
|
---|
279 | for (OsmPrimitive osm : from)
|
---|
280 | osm.visit(v);
|
---|
281 | v.data.addAll(from);
|
---|
282 | if (!v.data.isEmpty())
|
---|
283 | cmd.add(new DeleteCommand(v.data));
|
---|
284 |
|
---|
285 | if (cmd.isEmpty())
|
---|
286 | return null;
|
---|
287 | return new SequenceCommand(tr("Executing {0}",getValue(NAME)), cmd);
|
---|
288 | }
|
---|
289 | }
|
---|
290 |
|
---|
291 | public ExternalToolsAction(String name, String[] exec, String[] flags, String[] input, String output) {
|
---|
292 | super(name);
|
---|
293 | this.exec = exec;
|
---|
294 | this.flags = Arrays.asList(flags);
|
---|
295 | this.input = Arrays.asList(input);
|
---|
296 | this.output = output;
|
---|
297 | }
|
---|
298 |
|
---|
299 | public void actionPerformed(ActionEvent e) {
|
---|
300 | try {
|
---|
301 | final Process p = new ProcessBuilder(exec).start();
|
---|
302 | PleaseWaitRunnable runner = new ExecuteToolRunner(tr("Executing {0}",getValue(NAME)), p);
|
---|
303 | Main.worker.execute(runner);
|
---|
304 | runner.pleaseWaitDlg.setVisible(true);
|
---|
305 | } catch (IOException e1) {
|
---|
306 | e1.printStackTrace();
|
---|
307 | JOptionPane.showMessageDialog(Main.parent, tr("Could not execute command: {0}", exec[0]));
|
---|
308 | }
|
---|
309 | }
|
---|
310 |
|
---|
311 | /**
|
---|
312 | * @return All external tools configured so far as array.
|
---|
313 | */
|
---|
314 | public static JMenu buildMenu() {
|
---|
315 | if (!new File(Main.pref.getPreferencesDir()+"external_tools").exists())
|
---|
316 | return null;
|
---|
317 |
|
---|
318 | final JMenu main = new JMenu(tr("Tools"));
|
---|
319 | main.setMnemonic('T');
|
---|
320 | MinML2 parser = new MinML2() {
|
---|
321 | Stack<JMenu> current = new Stack<JMenu>();
|
---|
322 | @Override public void startDocument() {
|
---|
323 | current.push(main);
|
---|
324 | }
|
---|
325 | @Override public void startElement(String ns, String lname, String qname, Attributes a) throws SAXException {
|
---|
326 | if (qname.equals("group")) {
|
---|
327 | JMenu m = current.peek();
|
---|
328 | current.push(new JMenu(a.getValue(("name"))));
|
---|
329 | m.add(current.peek());
|
---|
330 | }
|
---|
331 | if (!qname.equals("tool"))
|
---|
332 | return;
|
---|
333 | String flagValue = a.getValue("flags");
|
---|
334 | String[] flags = flagValue==null ? new String[]{} : flagValue.split(",");
|
---|
335 | String output = a.getValue("out");
|
---|
336 | String[] exec = a.getValue("exec").split(" ");
|
---|
337 | if (exec.length < 1)
|
---|
338 | throw new SAXException("Execute attribute must not be empty");
|
---|
339 | String inValue = a.getValue("in");
|
---|
340 | String[] input = inValue==null ? new String[]{} : inValue.split(",");
|
---|
341 |
|
---|
342 | current.peek().add(new ExternalToolsAction(a.getValue("name"), exec, flags, input, output));
|
---|
343 | }
|
---|
344 | @Override public void endElement(String ns, String lname, String qname) {
|
---|
345 | if (qname.equals("group"))
|
---|
346 | current.pop();
|
---|
347 | }
|
---|
348 | };
|
---|
349 | try {
|
---|
350 | parser.parse(new FileReader(Main.pref.getPreferencesDir()+"external_tools"));
|
---|
351 | } catch (Exception e) {
|
---|
352 | e.printStackTrace();
|
---|
353 | JOptionPane.showMessageDialog(Main.parent, tr("Could not read external tool configuration."));
|
---|
354 | }
|
---|
355 | return main;
|
---|
356 | }
|
---|
357 | }
|
---|