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