You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ScannerReportViewerApp.java 22KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2018 SonarSource SA
  4. * mailto:info AT sonarsource DOT com
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 3 of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public License
  17. * along with this program; if not, write to the Free Software Foundation,
  18. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  19. */
  20. package org.sonar.scanner.protocol.viewer;
  21. import java.awt.BorderLayout;
  22. import java.awt.Cursor;
  23. import java.awt.Dimension;
  24. import java.awt.EventQueue;
  25. import java.io.File;
  26. import java.io.IOException;
  27. import java.io.InputStream;
  28. import java.io.PrintWriter;
  29. import java.io.StringWriter;
  30. import java.nio.charset.StandardCharsets;
  31. import java.sql.Date;
  32. import java.text.SimpleDateFormat;
  33. import java.util.List;
  34. import java.util.Map;
  35. import java.util.Scanner;
  36. import javax.annotation.CheckForNull;
  37. import javax.swing.JEditorPane;
  38. import javax.swing.JFileChooser;
  39. import javax.swing.JFrame;
  40. import javax.swing.JOptionPane;
  41. import javax.swing.JPanel;
  42. import javax.swing.JScrollPane;
  43. import javax.swing.JSplitPane;
  44. import javax.swing.JTabbedPane;
  45. import javax.swing.JTree;
  46. import javax.swing.UIManager;
  47. import javax.swing.UIManager.LookAndFeelInfo;
  48. import javax.swing.event.TreeSelectionEvent;
  49. import javax.swing.event.TreeSelectionListener;
  50. import javax.swing.tree.DefaultMutableTreeNode;
  51. import javax.swing.tree.DefaultTreeModel;
  52. import javax.swing.tree.TreeSelectionModel;
  53. import org.apache.commons.io.FileUtils;
  54. import org.apache.commons.lang.StringUtils;
  55. import org.sonar.core.util.CloseableIterator;
  56. import org.sonar.scanner.protocol.output.FileStructure.Domain;
  57. import org.sonar.scanner.protocol.output.ScannerReport;
  58. import org.sonar.scanner.protocol.output.ScannerReport.Changesets;
  59. import org.sonar.scanner.protocol.output.ScannerReport.Changesets.Changeset;
  60. import org.sonar.scanner.protocol.output.ScannerReport.Component;
  61. import org.sonar.scanner.protocol.output.ScannerReport.Issue;
  62. import org.sonar.scanner.protocol.output.ScannerReport.Metadata;
  63. import org.sonar.scanner.protocol.output.ScannerReport.Metadata.Plugin;
  64. import org.sonar.scanner.protocol.output.ScannerReport.Metadata.QProfile;
  65. import org.sonar.scanner.protocol.output.ScannerReportReader;
  66. public class ScannerReportViewerApp {
  67. private JFrame frame;
  68. private ScannerReportReader reader;
  69. private Metadata metadata;
  70. private SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
  71. private JTree componentTree;
  72. private JSplitPane splitPane;
  73. private JTabbedPane tabbedPane;
  74. private JScrollPane treeScrollPane;
  75. private JScrollPane componentDetailsTab;
  76. private JScrollPane highlightingTab;
  77. private JScrollPane symbolTab;
  78. private JEditorPane componentEditor;
  79. private JEditorPane highlightingEditor;
  80. private JEditorPane symbolEditor;
  81. private JScrollPane sourceTab;
  82. private JEditorPane sourceEditor;
  83. private JScrollPane coverageTab;
  84. private JEditorPane coverageEditor;
  85. private JScrollPane testsTab;
  86. private JEditorPane testsEditor;
  87. private TextLineNumber textLineNumber;
  88. private JScrollPane duplicationTab;
  89. private JEditorPane duplicationEditor;
  90. private JScrollPane issuesTab;
  91. private JEditorPane issuesEditor;
  92. private JScrollPane measuresTab;
  93. private JEditorPane measuresEditor;
  94. private JScrollPane scmTab;
  95. private JEditorPane scmEditor;
  96. private JScrollPane activeRuleTab;
  97. private JEditorPane activeRuleEditor;
  98. private JScrollPane qualityProfileTab;
  99. private JEditorPane qualityProfileEditor;
  100. private JScrollPane pluginTab;
  101. private JEditorPane pluginEditor;
  102. private JScrollPane cpdTextBlocksTab;
  103. private JEditorPane cpdTextBlocksEditor;
  104. private JScrollPane significantCodeTab;
  105. private JEditorPane significantCodeEditor;
  106. /**
  107. * Create the application.
  108. */
  109. public ScannerReportViewerApp() {
  110. initialize();
  111. }
  112. /**
  113. * Launch the application.
  114. */
  115. public static void main(String[] args) {
  116. EventQueue.invokeLater(new Runnable() {
  117. @Override
  118. public void run() {
  119. try {
  120. ScannerReportViewerApp window = new ScannerReportViewerApp();
  121. window.frame.setVisible(true);
  122. window.loadReport();
  123. } catch (Exception e) {
  124. e.printStackTrace();
  125. }
  126. }
  127. });
  128. }
  129. private void loadReport() {
  130. final JFileChooser fc = new JFileChooser();
  131. fc.setDialogTitle("Choose scanner report directory");
  132. File lastReport = getLastUsedReport();
  133. if (lastReport != null) {
  134. fc.setCurrentDirectory(lastReport);
  135. }
  136. fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
  137. fc.setFileHidingEnabled(false);
  138. fc.setApproveButtonText("Open scanner report");
  139. int returnVal = fc.showOpenDialog(frame);
  140. if (returnVal == JFileChooser.APPROVE_OPTION) {
  141. File file = fc.getSelectedFile();
  142. try {
  143. setLastUsedReport(file);
  144. loadReport(file);
  145. } catch (Exception e) {
  146. JOptionPane.showMessageDialog(frame, e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
  147. exit();
  148. }
  149. } else {
  150. exit();
  151. }
  152. }
  153. @CheckForNull
  154. private File getLastUsedReport() {
  155. File f = new File(System.getProperty("java.io.tmpdir"), ".last_batch_report_dir");
  156. if (f.exists()) {
  157. String path;
  158. try {
  159. path = FileUtils.readFileToString(f, StandardCharsets.UTF_8);
  160. } catch (IOException e) {
  161. return null;
  162. }
  163. File lastReport = new File(path);
  164. if (lastReport.exists() && lastReport.isDirectory()) {
  165. return lastReport;
  166. }
  167. }
  168. return null;
  169. }
  170. private void setLastUsedReport(File lastReport) throws IOException {
  171. File f = new File(System.getProperty("java.io.tmpdir"), ".last_batch_report_dir");
  172. String fullPath = lastReport.getAbsolutePath();
  173. FileUtils.write(f, fullPath, StandardCharsets.UTF_8);
  174. }
  175. private void exit() {
  176. frame.setVisible(false);
  177. frame.dispose();
  178. }
  179. private void loadReport(File file) {
  180. reader = new ScannerReportReader(file);
  181. metadata = reader.readMetadata();
  182. updateTitle();
  183. loadComponents();
  184. updateActiveRules();
  185. updateQualityProfiles();
  186. updatePlugins();
  187. }
  188. private void loadComponents() {
  189. int rootComponentRef = metadata.getRootComponentRef();
  190. Component component = reader.readComponent(rootComponentRef);
  191. DefaultMutableTreeNode project = createNode(component);
  192. loadChildren(component, project);
  193. getComponentTree().setModel(new DefaultTreeModel(project));
  194. }
  195. private static DefaultMutableTreeNode createNode(Component component) {
  196. return new DefaultMutableTreeNode(component) {
  197. @Override
  198. public String toString() {
  199. return getNodeName((Component) getUserObject());
  200. }
  201. };
  202. }
  203. private static String getNodeName(Component component) {
  204. switch (component.getType()) {
  205. case PROJECT:
  206. case MODULE:
  207. return component.getName();
  208. case DIRECTORY:
  209. case FILE:
  210. return component.getPath();
  211. default:
  212. throw new IllegalArgumentException("Unknow component type: " + component.getType());
  213. }
  214. }
  215. private void loadChildren(Component parentComponent, DefaultMutableTreeNode parentNode) {
  216. for (int ref : parentComponent.getChildRefList()) {
  217. Component child = reader.readComponent(ref);
  218. DefaultMutableTreeNode childNode = createNode(child);
  219. parentNode.add(childNode);
  220. loadChildren(child, childNode);
  221. }
  222. }
  223. private void updateTitle() {
  224. frame.setTitle(metadata.getProjectKey() + (StringUtils.isNotEmpty(metadata.getBranchName()) ? (" (" + metadata.getBranchName() + ")") : "") + " "
  225. + sdf.format(new Date(metadata.getAnalysisDate())));
  226. }
  227. private void updateDetails(Component component) {
  228. componentEditor.setText(component.toString());
  229. updateHighlighting(component);
  230. updateSymbols(component);
  231. updateSource(component);
  232. updateCoverage(component);
  233. updateTests(component);
  234. updateDuplications(component);
  235. updateIssues(component);
  236. updateMeasures(component);
  237. updateScm(component);
  238. updateCpdTextBlocks(component);
  239. updateSignificantCode(component);
  240. }
  241. private void updateCpdTextBlocks(Component component) {
  242. cpdTextBlocksEditor.setText("");
  243. if (reader.hasCoverage(component.getRef())) {
  244. try (CloseableIterator<ScannerReport.CpdTextBlock> it = reader.readCpdTextBlocks(component.getRef())) {
  245. while (it.hasNext()) {
  246. ScannerReport.CpdTextBlock textBlock = it.next();
  247. cpdTextBlocksEditor.getDocument().insertString(cpdTextBlocksEditor.getDocument().getEndPosition().getOffset(), textBlock + "\n", null);
  248. }
  249. } catch (Exception e) {
  250. throw new IllegalStateException("Can't read CPD text blocks for " + getNodeName(component), e);
  251. }
  252. }
  253. }
  254. private void updateSignificantCode(Component component) {
  255. significantCodeEditor.setText("");
  256. if (reader.hasCoverage(component.getRef())) {
  257. try (CloseableIterator<ScannerReport.LineSgnificantCode> it = reader.readComponentSignificantCode(component.getRef())) {
  258. if (it != null) {
  259. while (it.hasNext()) {
  260. ScannerReport.LineSgnificantCode textBlock = it.next();
  261. significantCodeEditor.getDocument().insertString(significantCodeEditor.getDocument().getEndPosition().getOffset(), textBlock + "\n", null);
  262. }
  263. }
  264. } catch (Exception e) {
  265. throw new IllegalStateException("Can't read significant code for " + getNodeName(component), e);
  266. }
  267. }
  268. }
  269. private void updateDuplications(Component component) {
  270. duplicationEditor.setText("");
  271. if (reader.hasCoverage(component.getRef())) {
  272. try (CloseableIterator<ScannerReport.Duplication> it = reader.readComponentDuplications(component.getRef())) {
  273. while (it.hasNext()) {
  274. ScannerReport.Duplication dup = it.next();
  275. duplicationEditor.getDocument().insertString(duplicationEditor.getDocument().getEndPosition().getOffset(), dup + "\n", null);
  276. }
  277. } catch (Exception e) {
  278. throw new IllegalStateException("Can't read duplications for " + getNodeName(component), e);
  279. }
  280. }
  281. }
  282. private void updateIssues(Component component) {
  283. issuesEditor.setText("");
  284. try (CloseableIterator<Issue> it = reader.readComponentIssues(component.getRef())) {
  285. while (it.hasNext()) {
  286. Issue issue = it.next();
  287. int offset = issuesEditor.getDocument().getEndPosition().getOffset();
  288. issuesEditor.getDocument().insertString(offset, issue.toString(), null);
  289. }
  290. } catch (Exception e) {
  291. throw new IllegalStateException("Can't read issues for " + getNodeName(component), e);
  292. }
  293. }
  294. private void updateCoverage(Component component) {
  295. coverageEditor.setText("");
  296. try (CloseableIterator<ScannerReport.LineCoverage> it = reader.readComponentCoverage(component.getRef())) {
  297. while (it.hasNext()) {
  298. ScannerReport.LineCoverage coverage = it.next();
  299. coverageEditor.getDocument().insertString(coverageEditor.getDocument().getEndPosition().getOffset(), coverage + "\n", null);
  300. }
  301. } catch (Exception e) {
  302. throw new IllegalStateException("Can't read code coverage for " + getNodeName(component), e);
  303. }
  304. }
  305. private void updateTests(Component component) {
  306. testsEditor.setText("");
  307. File tests = reader.readTests(component.getRef());
  308. if (tests == null) {
  309. return;
  310. }
  311. try (InputStream inputStream = FileUtils.openInputStream(tests)) {
  312. ScannerReport.Test test = ScannerReport.Test.parser().parseDelimitedFrom(inputStream);
  313. while (test != null) {
  314. testsEditor.getDocument().insertString(testsEditor.getDocument().getEndPosition().getOffset(), test + "\n", null);
  315. test = ScannerReport.Test.parser().parseDelimitedFrom(inputStream);
  316. }
  317. } catch (Exception e) {
  318. throw new IllegalStateException(e);
  319. }
  320. }
  321. private void updateSource(Component component) {
  322. File sourceFile = reader.getFileStructure().fileFor(Domain.SOURCE, component.getRef());
  323. sourceEditor.setText("");
  324. if (sourceFile.exists()) {
  325. try (Scanner s = new Scanner(sourceFile, StandardCharsets.UTF_8.name()).useDelimiter("\\Z")) {
  326. if (s.hasNext()) {
  327. sourceEditor.setText(s.next());
  328. }
  329. } catch (IOException ex) {
  330. StringWriter errors = new StringWriter();
  331. ex.printStackTrace(new PrintWriter(errors));
  332. sourceEditor.setText(errors.toString());
  333. }
  334. }
  335. }
  336. private void updateActiveRules() {
  337. activeRuleEditor.setText("");
  338. StringBuilder builder = new StringBuilder();
  339. try (CloseableIterator<ScannerReport.ActiveRule> activeRuleCloseableIterator = reader.readActiveRules()) {
  340. while (activeRuleCloseableIterator.hasNext()) {
  341. builder.append(activeRuleCloseableIterator.next().toString()).append("\n");
  342. }
  343. activeRuleEditor.setText(builder.toString());
  344. }
  345. }
  346. private void updateQualityProfiles() {
  347. qualityProfileEditor.setText("");
  348. StringBuilder builder = new StringBuilder();
  349. for (Map.Entry<String, QProfile> qp : metadata.getQprofilesPerLanguage().entrySet()) {
  350. builder.append(qp.getKey()).append(":\n").append(qp.getValue()).append("\n\n");
  351. }
  352. qualityProfileEditor.setText(builder.toString());
  353. }
  354. private void updatePlugins() {
  355. pluginEditor.setText("");
  356. StringBuilder builder = new StringBuilder();
  357. for (Map.Entry<String, Plugin> p : metadata.getPluginsByKey().entrySet()) {
  358. builder.append(p.getKey()).append(":\n").append(p.getValue()).append("\n\n");
  359. }
  360. pluginEditor.setText(builder.toString());
  361. }
  362. private void updateHighlighting(Component component) {
  363. highlightingEditor.setText("");
  364. try (CloseableIterator<ScannerReport.SyntaxHighlightingRule> it = reader.readComponentSyntaxHighlighting(component.getRef())) {
  365. while (it.hasNext()) {
  366. ScannerReport.SyntaxHighlightingRule rule = it.next();
  367. highlightingEditor.getDocument().insertString(highlightingEditor.getDocument().getEndPosition().getOffset(), rule + "\n", null);
  368. }
  369. } catch (Exception e) {
  370. throw new IllegalStateException("Can't read syntax highlighting for " + getNodeName(component), e);
  371. }
  372. }
  373. private void updateMeasures(Component component) {
  374. measuresEditor.setText("");
  375. try (CloseableIterator<ScannerReport.Measure> it = reader.readComponentMeasures(component.getRef())) {
  376. while (it.hasNext()) {
  377. ScannerReport.Measure measure = it.next();
  378. measuresEditor.getDocument().insertString(measuresEditor.getDocument().getEndPosition().getOffset(), measure + "\n", null);
  379. }
  380. } catch (Exception e) {
  381. throw new IllegalStateException("Can't read measures for " + getNodeName(component), e);
  382. }
  383. }
  384. private void updateScm(Component component) {
  385. scmEditor.setText("");
  386. Changesets changesets = reader.readChangesets(component.getRef());
  387. if (changesets == null) {
  388. return;
  389. }
  390. List<Integer> changesetIndexByLine = changesets.getChangesetIndexByLineList();
  391. try {
  392. int index = 0;
  393. for (Changeset changeset : changesets.getChangesetList()) {
  394. scmEditor.getDocument().insertString(scmEditor.getDocument().getEndPosition().getOffset(), Integer.toString(index) + "\n", null);
  395. scmEditor.getDocument().insertString(scmEditor.getDocument().getEndPosition().getOffset(), changeset + "\n", null);
  396. index++;
  397. }
  398. scmEditor.getDocument().insertString(scmEditor.getDocument().getEndPosition().getOffset(), "\n", null);
  399. int line = 1;
  400. for (Integer idx : changesetIndexByLine) {
  401. scmEditor.getDocument().insertString(scmEditor.getDocument().getEndPosition().getOffset(), Integer.toString(line) + ": " + idx + "\n", null);
  402. line++;
  403. }
  404. } catch (Exception e) {
  405. throw new IllegalStateException("Can't read SCM for " + getNodeName(component), e);
  406. }
  407. }
  408. private void updateSymbols(Component component) {
  409. symbolEditor.setText("");
  410. try (CloseableIterator<ScannerReport.Symbol> it = reader.readComponentSymbols(component.getRef())) {
  411. while (it.hasNext()) {
  412. ScannerReport.Symbol symbol = it.next();
  413. symbolEditor.getDocument().insertString(symbolEditor.getDocument().getEndPosition().getOffset(), symbol + "\n", null);
  414. }
  415. } catch (Exception e) {
  416. throw new IllegalStateException("Can't read symbol references for " + getNodeName(component), e);
  417. }
  418. }
  419. /**
  420. * Initialize the contents of the frame.
  421. */
  422. private void initialize() {
  423. try {
  424. for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
  425. if ("Nimbus".equals(info.getName())) {
  426. UIManager.setLookAndFeel(info.getClassName());
  427. break;
  428. }
  429. }
  430. } catch (Exception e) {
  431. // If Nimbus is not available, you can set the GUI to another look and feel.
  432. }
  433. frame = new JFrame();
  434. frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  435. splitPane = new JSplitPane();
  436. frame.getContentPane().add(splitPane, BorderLayout.CENTER);
  437. tabbedPane = new JTabbedPane(JTabbedPane.TOP);
  438. tabbedPane.setPreferredSize(new Dimension(500, 7));
  439. splitPane.setRightComponent(tabbedPane);
  440. componentDetailsTab = new JScrollPane();
  441. tabbedPane.addTab("Component details", null, componentDetailsTab, null);
  442. componentEditor = new JEditorPane();
  443. componentDetailsTab.setViewportView(componentEditor);
  444. sourceTab = new JScrollPane();
  445. tabbedPane.addTab("Source", null, sourceTab, null);
  446. sourceEditor = createSourceEditor();
  447. sourceEditor.setEditable(false);
  448. sourceTab.setViewportView(sourceEditor);
  449. textLineNumber = createTextLineNumber();
  450. sourceTab.setRowHeaderView(textLineNumber);
  451. highlightingTab = new JScrollPane();
  452. tabbedPane.addTab("Highlighting", null, highlightingTab, null);
  453. highlightingEditor = new JEditorPane();
  454. highlightingTab.setViewportView(highlightingEditor);
  455. symbolTab = new JScrollPane();
  456. tabbedPane.addTab("Symbol references", null, symbolTab, null);
  457. symbolEditor = new JEditorPane();
  458. symbolTab.setViewportView(symbolEditor);
  459. coverageTab = new JScrollPane();
  460. tabbedPane.addTab("Coverage", null, coverageTab, null);
  461. coverageEditor = new JEditorPane();
  462. coverageTab.setViewportView(coverageEditor);
  463. duplicationTab = new JScrollPane();
  464. tabbedPane.addTab("Duplications", null, duplicationTab, null);
  465. duplicationEditor = new JEditorPane();
  466. duplicationTab.setViewportView(duplicationEditor);
  467. testsTab = new JScrollPane();
  468. tabbedPane.addTab("Tests", null, testsTab, null);
  469. testsEditor = new JEditorPane();
  470. testsTab.setViewportView(testsEditor);
  471. issuesTab = new JScrollPane();
  472. tabbedPane.addTab("Issues", null, issuesTab, null);
  473. issuesEditor = new JEditorPane();
  474. issuesTab.setViewportView(issuesEditor);
  475. measuresTab = new JScrollPane();
  476. tabbedPane.addTab("Measures", null, measuresTab, null);
  477. measuresEditor = new JEditorPane();
  478. measuresTab.setViewportView(measuresEditor);
  479. scmTab = new JScrollPane();
  480. tabbedPane.addTab("SCM", null, scmTab, null);
  481. scmEditor = new JEditorPane();
  482. scmTab.setViewportView(scmEditor);
  483. activeRuleTab = new JScrollPane();
  484. tabbedPane.addTab("Active Rules", null, activeRuleTab, null);
  485. activeRuleEditor = new JEditorPane();
  486. activeRuleTab.setViewportView(activeRuleEditor);
  487. qualityProfileTab = new JScrollPane();
  488. tabbedPane.addTab("Quality Profiles", null, qualityProfileTab, null);
  489. qualityProfileEditor = new JEditorPane();
  490. qualityProfileTab.setViewportView(qualityProfileEditor);
  491. pluginTab = new JScrollPane();
  492. tabbedPane.addTab("Plugins", null, pluginTab, null);
  493. pluginEditor = new JEditorPane();
  494. pluginTab.setViewportView(pluginEditor);
  495. cpdTextBlocksTab = new JScrollPane();
  496. tabbedPane.addTab("CPD Text Blocks", null, cpdTextBlocksTab, null);
  497. cpdTextBlocksEditor = new JEditorPane();
  498. cpdTextBlocksTab.setViewportView(cpdTextBlocksEditor);
  499. significantCodeTab = new JScrollPane();
  500. tabbedPane.addTab("Significant Code Ranges", null, significantCodeTab, null);
  501. significantCodeEditor = new JEditorPane();
  502. significantCodeTab.setViewportView(significantCodeEditor);
  503. treeScrollPane = new JScrollPane();
  504. treeScrollPane.setPreferredSize(new Dimension(200, 400));
  505. splitPane.setLeftComponent(treeScrollPane);
  506. componentTree = new JTree();
  507. componentTree.setModel(new DefaultTreeModel(
  508. new DefaultMutableTreeNode("empty") {
  509. {
  510. }
  511. }));
  512. treeScrollPane.setViewportView(componentTree);
  513. componentTree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
  514. componentTree.addTreeSelectionListener(new TreeSelectionListener() {
  515. @Override
  516. public void valueChanged(TreeSelectionEvent e) {
  517. DefaultMutableTreeNode node = (DefaultMutableTreeNode) componentTree.getLastSelectedPathComponent();
  518. if (node == null) {
  519. // Nothing is selected.
  520. return;
  521. }
  522. frame.setCursor(new Cursor(Cursor.WAIT_CURSOR));
  523. updateDetails((Component) node.getUserObject());
  524. frame.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
  525. }
  526. });
  527. frame.pack();
  528. }
  529. public JTree getComponentTree() {
  530. return componentTree;
  531. }
  532. /**
  533. * @wbp.factory
  534. */
  535. public static JPanel createComponentPanel() {
  536. JPanel panel = new JPanel();
  537. return panel;
  538. }
  539. protected JEditorPane getComponentEditor() {
  540. return componentEditor;
  541. }
  542. /**
  543. * @wbp.factory
  544. */
  545. public static JEditorPane createSourceEditor() {
  546. JEditorPane editorPane = new JEditorPane();
  547. return editorPane;
  548. }
  549. /**
  550. * @wbp.factory
  551. */
  552. public TextLineNumber createTextLineNumber() {
  553. TextLineNumber textLineNumber = new TextLineNumber(sourceEditor);
  554. return textLineNumber;
  555. }
  556. }