1 /*
2  * Copyright (c) 2016 Vivid Solutions.
3  *
4  * All rights reserved. This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * and Eclipse Distribution License v. 1.0 which accompanies this distribution.
7  * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html
8  * and the Eclipse Distribution License is available at
9  *
10  * http://www.eclipse.org/org/documents/edl-v10.php.
11  */
12 package org.locationtech.jtstest.testbuilder;
13 
14 import java.awt.AWTEvent;
15 import java.awt.BorderLayout;
16 import java.awt.Color;
17 import java.awt.Component;
18 import java.awt.Cursor;
19 import java.awt.Dimension;
20 import java.awt.GridBagLayout;
21 import java.awt.GridLayout;
22 import java.awt.Toolkit;
23 import java.awt.event.ActionEvent;
24 import java.awt.event.WindowEvent;
25 import java.io.File;
26 import java.util.Iterator;
27 import java.util.List;
28 
29 import javax.swing.BorderFactory;
30 import javax.swing.ImageIcon;
31 import javax.swing.JFileChooser;
32 import javax.swing.JFrame;
33 import javax.swing.JOptionPane;
34 import javax.swing.JPanel;
35 import javax.swing.JSplitPane;
36 import javax.swing.JTabbedPane;
37 import javax.swing.border.BevelBorder;
38 import javax.swing.border.Border;
39 import javax.swing.border.EmptyBorder;
40 import javax.swing.event.ChangeEvent;
41 import javax.swing.event.ChangeListener;
42 
43 import org.locationtech.jts.geom.Coordinate;
44 import org.locationtech.jts.geom.Geometry;
45 import org.locationtech.jts.geom.util.LinearComponentExtracter;
46 import org.locationtech.jts.io.ParseException;
47 import org.locationtech.jts.util.Assert;
48 import org.locationtech.jtstest.clean.CleanDuplicatePoints;
49 import org.locationtech.jtstest.testbuilder.controller.ResultController;
50 import org.locationtech.jtstest.testbuilder.event.SpatialFunctionPanelEvent;
51 import org.locationtech.jtstest.testbuilder.event.SpatialFunctionPanelListener;
52 import org.locationtech.jtstest.testbuilder.io.HtmlWriter;
53 import org.locationtech.jtstest.testbuilder.io.JavaTestWriter;
54 import org.locationtech.jtstest.testbuilder.io.XMLTestWriter;
55 import org.locationtech.jtstest.testbuilder.model.DisplayParameters;
56 import org.locationtech.jtstest.testbuilder.model.GeometryEvent;
57 import org.locationtech.jtstest.testbuilder.model.TestBuilderModel;
58 import org.locationtech.jtstest.testbuilder.model.TestCaseEdit;
59 import org.locationtech.jtstest.testbuilder.ui.ImageUtil;
60 import org.locationtech.jtstest.testbuilder.ui.SwingUtil;
61 import org.locationtech.jtstest.testbuilder.ui.dnd.FileDrop;
62 import org.locationtech.jtstest.testbuilder.ui.tools.DeleteVertexTool;
63 import org.locationtech.jtstest.testbuilder.ui.tools.EditVertexTool;
64 import org.locationtech.jtstest.testbuilder.ui.tools.ExtractComponentTool;
65 import org.locationtech.jtstest.testbuilder.ui.tools.InfoTool;
66 import org.locationtech.jtstest.testbuilder.ui.tools.LineStringTool;
67 import org.locationtech.jtstest.testbuilder.ui.tools.PanTool;
68 import org.locationtech.jtstest.testbuilder.ui.tools.PointTool;
69 import org.locationtech.jtstest.testbuilder.ui.tools.RectangleTool;
70 import org.locationtech.jtstest.testbuilder.ui.tools.StreamPolygonTool;
71 import org.locationtech.jtstest.testbuilder.ui.tools.Tool;
72 import org.locationtech.jtstest.testbuilder.ui.tools.ZoomTool;
73 import org.locationtech.jtstest.testrunner.GuiUtil;
74 import org.locationtech.jtstest.util.FileUtil;
75 import org.locationtech.jtstest.util.StringUtil;
76 
77 
78 /**
79  * The main frame for the JTS Test Builder.
80  *
81  * @version 1.7
82  */
83 public class JTSTestBuilderFrame extends JFrame
84 {
85 
86   private static JTSTestBuilderFrame singleton = null;
87   static boolean isShowingIndicators = true;
88 
89   TestBuilderModel tbModel;
90 
91   private ResultController resultController = new ResultController(this);
92   private JTSTestBuilderMenuBar tbMenuBar = new JTSTestBuilderMenuBar(this);
93   private JTSTestBuilderToolBar tbToolBar = new JTSTestBuilderToolBar(this);
94   //---------------------------------------------
95   JPanel contentPane;
96   BorderLayout contentLayout = new BorderLayout();
97   Border border4;
98   JSplitPane jSplitPane1 = new JSplitPane();
99   JPanel panelTop = new JPanel();
100   BorderLayout borderLayout2 = new BorderLayout();
101   TestCasePanel testCasePanel = new TestCasePanel();
102   JPanel panelBottom = new JPanel();
103   JTabbedPane inputTabbedPane = new JTabbedPane();
104   BorderLayout borderLayout3 = new BorderLayout();
105   JPanel testPanel = new JPanel();
106   WKTPanel wktPanel = new WKTPanel(this);
107   CommandPanel commandPanel = new CommandPanel();
108   InspectorPanel inspectPanel = new InspectorPanel();
109   TestListPanel testListPanel = new TestListPanel(this);
110   //LayerListPanel layerListPanel = new LayerListPanel();
111   LayerListPanel layerListPanel = new LayerListPanel();
112   GridBagLayout gridBagLayout2 = new GridBagLayout();
113   GridLayout gridLayout1 = new GridLayout();
114   ResultWKTPanel resultWKTPanel = new ResultWKTPanel();
115   ResultValuePanel resultValuePanel = new ResultValuePanel();
116   StatsPanel statsPanel = new StatsPanel();
117   InfoPanel logPanel = new InfoPanel();
118 
119   private JFileChooser fileChooser = new JFileChooser();
120   private JFileChooser pngFileChooser;
121   private JFileChooser fileAndDirectoryChooser = new JFileChooser();
122   private JFileChooser directoryChooser = new JFileChooser();
123 
124   /**
125    *  Construct the frame
126    */
JTSTestBuilderFrame()127   public JTSTestBuilderFrame() {
128     try {
129       Assert.isTrue(singleton == null);
130       singleton = this;
131       enableEvents(AWTEvent.WINDOW_EVENT_MASK);
132       setIconImage(AppIcons.APP.getImage());
133       jbInit();
134 
135       testCasePanel.spatialFunctionPanel.addSpatialFunctionPanelListener(
136           new SpatialFunctionPanelListener() {
137             public void functionExecuted(SpatialFunctionPanelEvent e) {
138             	resultController.spatialFunctionPanel_functionExecuted(e);
139             }
140           });
141       testCasePanel.scalarFunctionPanel.addSpatialFunctionPanelListener(
142           new SpatialFunctionPanelListener() {
143             public void functionExecuted(SpatialFunctionPanelEvent e) {
144             	resultController.executeScalarFunction();
145             }
146           });
147       testCasePanel.cbRevealTopo.addActionListener(
148           new java.awt.event.ActionListener() {
149             public void actionPerformed(ActionEvent e) {
150               displayRevealTopo();
151             }
152           });
153       //testCasePanel.editCtlPanel.stretchDist
154       testCasePanel.spStretchDist
155       .addChangeListener(new javax.swing.event.ChangeListener() {
156         public void stateChanged(javax.swing.event.ChangeEvent e) {
157           displayRevealTopo();
158         }
159       });
160 
161       showGeomsTab();
162       initFileDrop(testCasePanel);
163       testCasePanel.getGeometryEditPanel().setCurrentTool(RectangleTool.getInstance());
164     }
165     catch (Exception e) {
166       e.printStackTrace();
167     }
168   }
169 
initFileDrop(Component comp)170   private void initFileDrop(Component comp) {
171     new FileDrop(comp, new FileDrop.Listener() {
172       public void filesDropped(java.io.File[] files) {
173         try {
174           openXmlFilesAndDirectories(files);
175         } catch (Exception ex) {
176           SwingUtil.reportException(null, ex);
177         }
178       }
179     });
180   }
initFileChoosers()181   private void initFileChoosers() {
182     if (pngFileChooser == null) {
183       pngFileChooser = new JFileChooser();
184       pngFileChooser.addChoosableFileFilter(SwingUtil.PNG_FILE_FILTER);
185       pngFileChooser.setDialogTitle("Save PNG");
186       pngFileChooser.setSelectedFile(new File("geoms.png"));
187     }
188   }
189 
instance()190   public static JTSTestBuilderFrame instance() {
191     if (singleton == null) {
192       new JTSTestBuilderFrame();
193     }
194     return singleton;
195   }
196   /**
197    * Tests if the TestBuilder is running.
198    * Useful to allow functions to decide whether to show indicators
199    * (if functions are running under JtsOpCmd, they should not show indicators
200    * since that seriously impacts performance).
201    *
202    * @return true if there is a TestBuilder instance running
203    */
isRunning()204   public static boolean isRunning() {
205     return singleton != null;
206   }
isShowingIndicators()207   public static boolean isShowingIndicators() {
208     return isRunning() && isShowingIndicators;
209   }
210 
getGeometryEditPanel()211   public static GeometryEditPanel getGeometryEditPanel()
212   {
213     return instance().getTestCasePanel().getGeometryEditPanel();
214   }
215 
getModel()216   public TestBuilderModel getModel()
217   {
218     return tbModel;
219   }
220 
setModel(TestBuilderModel model)221   public void setModel(TestBuilderModel model)
222   {
223   	tbModel = model;
224     testCasePanel.setModel(tbModel);
225     wktPanel.setModel(model);
226     inspectPanel.setModel(model);
227     resultWKTPanel.setModel(model);
228     resultValuePanel.setModel(model);
229     statsPanel.setModel(model);
230 
231     model.getGeometryEditModel().addGeometryListener(
232         new org.locationtech.jtstest.testbuilder.model.GeometryListener() {
233           public void geometryChanged(GeometryEvent e) {
234             model_geometryChanged(e);
235           }
236         });
237 
238     testListPanel.populateList();
239     //layerListPanel.init(getModel().getLayers());
240     layerListPanel.populateList();
241     updateTestCaseView();
242     updatePrecisionModelDescription();
243   }
244 
reportException(Exception e)245   public static void reportException(Exception e) {
246   	SwingUtil.reportException(instance(), e);
247   }
248 
setCursorWait()249   public void setCursorWait() {
250     setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
251   }
252 
setCursorNormal()253   public void setCursorNormal() {
254     setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
255   }
256 
setCurrentTestCase(TestCaseEdit testCase)257   public void setCurrentTestCase(TestCaseEdit testCase) {
258     tbModel.cases().setCurrent(testCase);
259     updateTestCaseView();
260     JTSTestBuilder.controller().zoomToInput();
261   }
262 
getTestCasePanel()263   public TestCasePanel getTestCasePanel() {
264     return testCasePanel;
265   }
266 
getResultWKTPanel()267   public ResultWKTPanel getResultWKTPanel() {
268     return resultWKTPanel;
269   }
270 
getResultValuePanel()271   public ResultValuePanel getResultValuePanel() {
272     return resultValuePanel;
273   }
274 
getLogPanel()275   public InfoPanel getLogPanel() {
276     return logPanel;
277   }
278 
getCommandPanel()279   public CommandPanel getCommandPanel() {
280     return commandPanel;
281   }
282 
283   /**
284    *  File | Exit action performed
285    */
actionExit()286   public void actionExit() {
287     System.exit(0);
288   }
289 
showTab(String name)290   public void showTab(String name)
291   {
292     inputTabbedPane.setSelectedIndex(inputTabbedPane.indexOfTab(name));
293   }
294 
showGeomsTab()295   public void showGeomsTab()
296   {
297     showTab(AppStrings.TAB_LABEL_INPUT);
298   }
299 
showResultWKTTab()300   public void showResultWKTTab()
301   {
302     showTab(AppStrings.TAB_LABEL_RESULT);
303   }
showResultValueTab()304   public void showResultValueTab()
305   {
306     showTab(AppStrings.TAB_LABEL_VALUE);
307   }
308 
showInfoTab()309   public void showInfoTab()
310   {
311     showTab(AppStrings.TAB_LABEL_LOG);
312   }
313 
openXmlFilesAndDirectories(File[] files)314   public void openXmlFilesAndDirectories(File[] files) throws Exception {
315     if (files.length == 1) {
316       fileChooser.setSelectedFile(files[0]);
317     }
318     tbModel.openXmlFilesAndDirectories(files);
319     reportProblemsParsingXmlTestFile(tbModel.getParsingProblems());
320     updateTestCaseView();
321     testListPanel.populateList();
322     updatePrecisionModelDescription();
323     JTSTestBuilder.controller().zoomToInput();
324   }
325 
326   /**
327    *  Overridden so we can exit when window is closed
328    */
processWindowEvent(WindowEvent e)329   protected void processWindowEvent(WindowEvent e) {
330     super.processWindowEvent(e);
331     if (e.getID() == WindowEvent.WINDOW_CLOSING) {
332       actionExit();
333     }
334   }
335 
model_geometryChanged(GeometryEvent e)336   void model_geometryChanged(GeometryEvent e) {
337     //testCasePanel.relatePanel.clearResults();
338     JTSTestBuilder.controller().geometryViewChanged();
339     updateWktPanel();
340   }
341 
currentCase()342   TestCaseEdit currentCase() {
343     return tbModel.cases().getCurrentCase();
344   }
updateTestCases()345   public void updateTestCases()
346   {
347     testListPanel.populateList();
348     updateTestCaseView();
349   }
350 
copyResultToTest()351   public void copyResultToTest()
352   {
353     Object currResult = tbModel.getResult();
354     if (! (currResult instanceof Geometry))
355       return;
356     tbModel.addCase(new Geometry[] { (Geometry) currResult, null },
357         "Result of " + tbModel.getOpName());
358     updateTestCaseView();
359     testListPanel.populateList();
360   }
361 
inspectResult()362   public void inspectResult()
363   {
364     Object currResult = tbModel.getResult();
365     if (! (currResult instanceof Geometry))
366       return;
367     inspectGeometry((Geometry) currResult, 0, "R");
368   }
369 
inspectGeometry()370   public void inspectGeometry() {
371     int geomIndex = tbModel.getGeometryEditModel().getGeomIndex();
372     String tag = geomIndex == 0 ? AppStrings.GEOM_LABEL_A : AppStrings.GEOM_LABEL_B;
373     Geometry geometry = currentCase().getGeometry(geomIndex);
374     inspectGeometry(geometry, geomIndex, tag);
375   }
376 
inspectGeometry(Geometry geometry, int geomIndex, String tag)377   private void inspectGeometry(Geometry geometry, int geomIndex, String tag) {
378     inspectPanel.setGeometry( tag, geometry, geomIndex);
379     showTab(AppStrings.TAB_LABEL_INSPECT);
380   }
381 
menuLoadXmlTestFile_actionPerformed(ActionEvent e)382   void menuLoadXmlTestFile_actionPerformed(ActionEvent e) {
383     try {
384       fileChooser.removeChoosableFileFilter(SwingUtil.JAVA_FILE_FILTER);
385       fileChooser.addChoosableFileFilter(SwingUtil.XML_FILE_FILTER);
386       fileChooser.setDialogTitle("Open XML Test File(s)");
387       fileChooser.setMultiSelectionEnabled(true);
388       if (JFileChooser.APPROVE_OPTION == fileChooser.showOpenDialog(this)) {
389         File[] files = fileChooser.getSelectedFiles();
390         if (files.length == 0) {
391           files = new File[]{fileChooser.getSelectedFile()};
392         }
393         openXmlFilesAndDirectories(files);
394       }
395     }
396     catch (Exception x) {
397       SwingUtil.reportException(this, x);
398     }
399   }
400 
getRunXml()401   public String getRunXml()
402   {
403   	return XMLTestWriter.getRunXml(tbModel.getTestCaseList(), tbModel.getPrecisionModel());
404   }
405 
actionDeleteAllTestCases()406   void actionDeleteAllTestCases() {
407     tbModel.cases().init();
408     updateTestCaseView();
409     testListPanel.populateList();
410   }
411 
actionLoadXmlTestFolder()412   void actionLoadXmlTestFolder() {
413     try {
414       directoryChooser.removeChoosableFileFilter(SwingUtil.JAVA_FILE_FILTER);
415       directoryChooser.setDialogTitle("Open Folder(s) Containing XML Test Files");
416       directoryChooser.setMultiSelectionEnabled(true);
417       if (JFileChooser.APPROVE_OPTION == directoryChooser.showOpenDialog(this)) {
418         File[] files = directoryChooser.getSelectedFiles();
419         if (files.length == 0) {
420           files = new File[]{fileChooser.getSelectedFile()};
421         }
422         openXmlFilesAndDirectories(files);
423       }
424     }
425     catch (Exception x) {
426       reportException(x);
427     }
428   }
429 
displayRevealTopo()430   private void displayRevealTopo() {
431     DisplayParameters.setRevealingTopology(testCasePanel.cbRevealTopo.isSelected());
432     DisplayParameters.setTopologyStretchSize(testCasePanel.getStretchSize());
433     JTSTestBuilder.controller().geometryViewChanged();
434   }
435 
436   /**
437    *  Component initialization
438    */
jbInit()439   private void jbInit() throws Exception {
440     this.setSize(new Dimension(800, 800));
441     this.setTitle("JTS TestBuilder");
442     this.setJMenuBar(tbMenuBar.getMenuBar());
443 
444     fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
445     fileChooser.setMultiSelectionEnabled(false);
446     fileAndDirectoryChooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
447     fileAndDirectoryChooser.setMultiSelectionEnabled(true);
448     directoryChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
449     directoryChooser.setMultiSelectionEnabled(false);
450     //Center the window
451     //Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
452 
453     //---------------------------------------------------
454 
455     wktPanel.setMinimumSize(new Dimension(111, 0));
456     wktPanel.setPreferredSize(new Dimension(600, 100));
457     testPanel.setLayout(gridBagLayout2);
458     gridLayout1.setRows(4);
459     gridLayout1.setColumns(1);
460 
461     panelTop.setLayout(borderLayout2);
462     panelTop.setMinimumSize(new Dimension(431, 0));
463     panelTop.add(testCasePanel, BorderLayout.CENTER);
464 
465     panelBottom.setLayout(borderLayout3);
466     panelBottom.setMinimumSize(new Dimension(431, 0));
467     panelBottom.add(tbToolBar.getToolBar(), BorderLayout.NORTH);
468     panelBottom.add(inputTabbedPane, BorderLayout.CENTER);
469 
470     //---- Input tabs
471     inputTabbedPane.setTabPlacement(JTabbedPane.LEFT);
472     inputTabbedPane.add(testListPanel, AppStrings.TAB_LABEL_CASES);
473     inputTabbedPane.add(wktPanel,  AppStrings.TAB_LABEL_INPUT);
474     inputTabbedPane.add(resultWKTPanel, AppStrings.TAB_LABEL_RESULT);
475     inputTabbedPane.add(resultValuePanel, AppStrings.TAB_LABEL_VALUE);
476     inputTabbedPane.add(commandPanel,  AppStrings.TAB_LABEL_COMMAND);
477     inputTabbedPane.add(inspectPanel,  AppStrings.TAB_LABEL_INSPECT);
478     inputTabbedPane.add(statsPanel, AppStrings.TAB_LABEL_STATS);
479     inputTabbedPane.add(logPanel, AppStrings.TAB_LABEL_LOG);
480     inputTabbedPane.add(layerListPanel, AppStrings.TAB_LABEL_LAYERS);
481     inputTabbedPane.setSelectedIndex(1);
482     inputTabbedPane.addChangeListener(new ChangeListener() {
483       public void stateChanged(ChangeEvent e)
484       {
485         updateStatsPanelIfVisible();
486         }
487     });
488 
489     //--- main frame
490 
491     jSplitPane1.setOrientation(JSplitPane.VERTICAL_SPLIT);
492     jSplitPane1.setPreferredSize(new Dimension(601, 690));
493     jSplitPane1.setBorder(new EmptyBorder(2,2,2,2));
494     jSplitPane1.setResizeWeight(1);
495     jSplitPane1.setDividerLocation(500);
496     jSplitPane1.add(panelTop, JSplitPane.TOP);
497     jSplitPane1.add(panelBottom, JSplitPane.BOTTOM);
498 
499     /*
500     border4 = BorderFactory.createBevelBorder(BevelBorder.LOWERED, Color.white,
501         Color.white, new Color(93, 93, 93), new Color(134, 134, 134));
502         */
503     contentPane = (JPanel) this.getContentPane();
504     contentPane.setLayout(contentLayout);
505     contentPane.setPreferredSize(new Dimension(601, 690));
506     contentPane.add(jSplitPane1, BorderLayout.CENTER);
507 
508   }
509 
getToolbar()510   public JTSTestBuilderToolBar getToolbar()
511   {
512     return tbToolBar;
513   }
514 
updateStatsPanelIfVisible()515   private void updateStatsPanelIfVisible()
516   {
517     int index = inputTabbedPane.getSelectedIndex();
518     if (index < 0) return;
519     if (inputTabbedPane.getComponent(index) == statsPanel) {
520       statsPanel.refresh();
521     }
522   }
523 
geometryChanged()524   public void geometryChanged() {
525     testCasePanel.relatePanel.clearResults();
526     testCasePanel.setTestCase(currentCase());
527     updateWktPanel();
528   }
529 
updateWktPanel()530   private void updateWktPanel() {
531     Geometry g0 = tbModel.getGeometryEditModel().getGeometry(0);
532     wktPanel.setText(g0, 0);
533     Geometry g1 = tbModel.getGeometryEditModel().getGeometry(1);
534     wktPanel.setText(g1, 1);
535   }
536 
updatePrecisionModelDescription()537   void updatePrecisionModelDescription() {
538     testCasePanel.setPrecisionModelDescription(tbModel.getPrecisionModel().toString());
539   }
540 
updateTestCaseView()541   public void updateTestCaseView() {
542     testCasePanel.setTestCase(currentCase());
543     getTestCasePanel().setCurrentTestCaseIndex(tbModel.getCurrentCaseIndex() + 1);
544     getTestCasePanel().setMaxTestCaseIndex(tbModel.getCasesSize());
545     updateWktPanel();
546     updateStatsPanelIfVisible();
547   }
548 
updateLayerList()549   public void updateLayerList() {
550     layerListPanel.updateList();
551   }
552 
reportProblemsParsingXmlTestFile(List parsingProblems)553   private void reportProblemsParsingXmlTestFile(List parsingProblems) {
554     if (parsingProblems.isEmpty()) {
555       return;
556     }
557     for (Iterator i = parsingProblems.iterator(); i.hasNext(); ) {
558       String problem = (String) i.next();
559       System.out.println(problem);
560     }
561     JOptionPane.showMessageDialog(this, StringUtil.wrap(parsingProblems.size()
562          + " problems occurred parsing the XML test file."
563          + " The first problem was: " + parsingProblems.get(0), 80),
564         "Error", JOptionPane.ERROR_MESSAGE);
565   }
566 
pickOffset(Geometry a, Geometry b)567   private Coordinate pickOffset(Geometry a, Geometry b) {
568     if (a != null && ! a.isEmpty()) {
569       return a.getCoordinates()[0];
570     }
571     if (b != null && ! b.isEmpty()) {
572       return b.getCoordinates()[0];
573     }
574     return null;
575   }
576 
577 }
578 
579