CheckAttributedTree.java revision 2599:50b448c5be54
1/* 2 * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24/* 25 * @test 26 * @bug 6970584 8006694 27 * @summary assorted position errors in compiler syntax trees 28 * temporarily workaround combo tests are causing time out in several platforms 29 * @library ../lib 30 * @build JavacTestingAbstractThreadedTest 31 * @run main/othervm CheckAttributedTree -q -r -et ERRONEOUS . 32 */ 33 34// use /othervm to avoid jtreg timeout issues (CODETOOLS-7900047) 35// see JDK-8006746 36 37import java.awt.BorderLayout; 38import java.awt.Color; 39import java.awt.Dimension; 40import java.awt.EventQueue; 41import java.awt.Font; 42import java.awt.GridBagConstraints; 43import java.awt.GridBagLayout; 44import java.awt.Rectangle; 45import java.awt.event.ActionEvent; 46import java.awt.event.ActionListener; 47import java.awt.event.MouseAdapter; 48import java.awt.event.MouseEvent; 49import java.io.File; 50import java.io.IOException; 51import java.io.PrintStream; 52import java.io.PrintWriter; 53import java.io.StringWriter; 54import java.lang.reflect.Field; 55import java.util.ArrayList; 56import java.util.Arrays; 57import java.util.HashSet; 58import java.util.List; 59import java.util.Set; 60import java.util.concurrent.atomic.AtomicInteger; 61 62import javax.lang.model.element.Element; 63import javax.swing.DefaultComboBoxModel; 64import javax.swing.JComboBox; 65import javax.swing.JComponent; 66import javax.swing.JFrame; 67import javax.swing.JLabel; 68import javax.swing.JPanel; 69import javax.swing.JScrollPane; 70import javax.swing.JTextArea; 71import javax.swing.JTextField; 72import javax.swing.SwingUtilities; 73import javax.swing.event.CaretEvent; 74import javax.swing.event.CaretListener; 75import javax.swing.text.BadLocationException; 76import javax.swing.text.DefaultHighlighter; 77import javax.swing.text.Highlighter; 78import javax.tools.Diagnostic; 79import javax.tools.DiagnosticListener; 80import javax.tools.JavaFileObject; 81 82import com.sun.source.tree.CompilationUnitTree; 83import com.sun.source.util.JavacTask; 84import com.sun.source.util.TaskEvent; 85import com.sun.source.util.TaskListener; 86import com.sun.tools.javac.api.JavacTaskImpl; 87import com.sun.tools.javac.code.Symbol; 88import com.sun.tools.javac.code.Type; 89import com.sun.tools.javac.main.JavaCompiler; 90import com.sun.tools.javac.tree.EndPosTable; 91import com.sun.tools.javac.tree.JCTree; 92import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; 93import com.sun.tools.javac.tree.JCTree.JCImport; 94import com.sun.tools.javac.tree.TreeInfo; 95import com.sun.tools.javac.tree.TreeScanner; 96import com.sun.tools.javac.util.Pair; 97 98import static com.sun.tools.javac.tree.JCTree.Tag.*; 99 100/** 101 * Utility and test program to check validity of tree positions for tree nodes. 102 * The program can be run standalone, or as a jtreg test. In standalone mode, 103 * errors can be displayed in a gui viewer. For info on command line args, 104 * run program with no args. 105 * 106 * <p> 107 * jtreg: Note that by using the -r switch in the test description below, this test 108 * will process all java files in the langtools/test directory, thus implicitly 109 * covering any new language features that may be tested in this test suite. 110 */ 111 112public class CheckAttributedTree extends JavacTestingAbstractThreadedTest { 113 /** 114 * Main entry point. 115 * If test.src is set, program runs in jtreg mode, and will throw an Error 116 * if any errors arise, otherwise System.exit will be used, unless the gui 117 * viewer is being used. In jtreg mode, the default base directory for file 118 * args is the value of ${test.src}. In jtreg mode, the -r option can be 119 * given to change the default base directory to the root test directory. 120 */ 121 public static void main(String... args) throws Exception { 122 String testSrc = System.getProperty("test.src"); 123 File baseDir = (testSrc == null) ? null : new File(testSrc); 124 throwAssertionOnError = false; 125 boolean ok = new CheckAttributedTree().run(baseDir, args); 126 if (!ok) { 127 if (testSrc != null) // jtreg mode 128 throw new Error("failed"); 129 else 130 System.exit(1); 131 } 132 } 133 134 /** 135 * Run the program. A base directory can be provided for file arguments. 136 * In jtreg mode, the -r option can be given to change the default base 137 * directory to the test root directory. For other options, see usage(). 138 * @param baseDir base directory for any file arguments. 139 * @param args command line args 140 * @return true if successful or in gui mode 141 */ 142 boolean run(File baseDir, String... args) throws Exception { 143 if (args.length == 0) { 144 usage(System.out); 145 return true; 146 } 147 148 List<File> files = new ArrayList<File>(); 149 for (int i = 0; i < args.length; i++) { 150 String arg = args[i]; 151 if (arg.equals("-encoding") && i + 1 < args.length) 152 encoding = args[++i]; 153 else if (arg.equals("-gui")) 154 gui = true; 155 else if (arg.equals("-q")) 156 quiet = true; 157 else if (arg.equals("-v")) { 158 verbose = true; 159 printAll = true; 160 } 161 else if (arg.equals("-t") && i + 1 < args.length) 162 tags.add(args[++i]); 163 else if (arg.equals("-ef") && i + 1 < args.length) 164 excludeFiles.add(new File(baseDir, args[++i])); 165 else if (arg.equals("-et") && i + 1 < args.length) 166 excludeTags.add(args[++i]); 167 else if (arg.equals("-r")) { 168 if (excludeFiles.size() > 0) 169 throw new Error("-r must be used before -ef"); 170 File d = baseDir; 171 while (!new File(d, "TEST.ROOT").exists()) { 172 if (d == null) 173 throw new Error("cannot find TEST.ROOT"); 174 d = d.getParentFile(); 175 } 176 baseDir = d; 177 } 178 else if (arg.startsWith("-")) 179 throw new Error("unknown option: " + arg); 180 else { 181 while (i < args.length) 182 files.add(new File(baseDir, args[i++])); 183 } 184 } 185 186 for (File file: files) { 187 if (file.exists()) 188 test(file); 189 else 190 error("File not found: " + file); 191 } 192 193 if (fileCount.get() != 1) 194 errWriter.println(fileCount + " files read"); 195 checkAfterExec(false); 196 197 return (gui || errCount.get() == 0); 198 } 199 200 /** 201 * Print command line help. 202 * @param out output stream 203 */ 204 void usage(PrintStream out) { 205 out.println("Usage:"); 206 out.println(" java CheckAttributedTree options... files..."); 207 out.println(""); 208 out.println("where options include:"); 209 out.println("-q Quiet: don't report on inapplicable files"); 210 out.println("-gui Display returns in a GUI viewer"); 211 out.println("-v Verbose: report on files as they are being read"); 212 out.println("-t tag Limit checks to tree nodes with this tag"); 213 out.println(" Can be repeated if desired"); 214 out.println("-ef file Exclude file or directory"); 215 out.println("-et tag Exclude tree nodes with given tag name"); 216 out.println(""); 217 out.println("files may be directories or files"); 218 out.println("directories will be scanned recursively"); 219 out.println("non java files, or java files which cannot be parsed, will be ignored"); 220 out.println(""); 221 } 222 223 /** 224 * Test a file. If the file is a directory, it will be recursively scanned 225 * for java files. 226 * @param file the file or directory to test 227 */ 228 void test(final File file) { 229 if (excludeFiles.contains(file)) { 230 if (!quiet) 231 error("File " + file + " excluded"); 232 return; 233 } 234 235 if (file.isDirectory()) { 236 for (File f: file.listFiles()) { 237 test(f); 238 } 239 return; 240 } 241 242 if (file.isFile() && file.getName().endsWith(".java")) { 243 pool.execute(new Runnable() { 244 @Override 245 public void run() { 246 try { 247 if (verbose) 248 errWriter.println(file); 249 fileCount.incrementAndGet(); 250 NPETester p = new NPETester(); 251 p.test(read(file)); 252 } catch (AttributionException e) { 253 if (!quiet) { 254 error("Error attributing " + file + "\n" + e.getMessage()); 255 } 256 } catch (IOException e) { 257 error("Error reading " + file + ": " + e); 258 } 259 } 260 }); 261 return; 262 } 263 264 if (!quiet) 265 error("File " + file + " ignored"); 266 } 267 268 // See CR: 6982992 Tests CheckAttributedTree.java, JavacTreeScannerTest.java, and SourceTreeeScannerTest.java timeout 269 StringWriter sw = new StringWriter(); 270 PrintWriter pw = new PrintWriter(sw); 271 Reporter r = new Reporter(pw); 272 273 /** 274 * Read a file. 275 * @param file the file to be read 276 * @return the tree for the content of the file 277 * @throws IOException if any IO errors occur 278 * @throws AttributionException if any errors occur while analyzing the file 279 */ 280 List<Pair<JCCompilationUnit, JCTree>> read(File file) throws IOException, AttributionException { 281 r.errors = 0; 282 Iterable<? extends JavaFileObject> files = fm.get().getJavaFileObjects(file); 283 String[] opts = { "-XDshouldStopPolicy=ATTR", "-XDverboseCompilePolicy" }; 284 JavacTask task = (JavacTask)comp.getTask(pw, fm.get(), r, Arrays.asList(opts), null, files); 285 final List<Element> analyzedElems = new ArrayList<>(); 286 task.setTaskListener(new TaskListener() { 287 public void started(TaskEvent e) { 288 if (e.getKind() == TaskEvent.Kind.ANALYZE) 289 analyzedElems.add(e.getTypeElement()); 290 } 291 public void finished(TaskEvent e) { } 292 }); 293 294 try { 295 Iterable<? extends CompilationUnitTree> trees = task.parse(); 296// JavaCompiler c = JavaCompiler.instance(((JavacTaskImpl) task).getContext()); 297// System.err.println("verboseCompilePolicy: " + c.verboseCompilePolicy); 298// System.err.println("shouldStopIfError: " + c.shouldStopPolicyIfError); 299// System.err.println("shouldStopIfNoError: " + c.shouldStopPolicyIfNoError); 300 Iterable<? extends Element> elems = task.analyze(); 301 if (!elems.iterator().hasNext()) 302 throw new AttributionException("No results from analyze"); 303 List<Pair<JCCompilationUnit, JCTree>> res = new ArrayList<>(); 304 //System.err.println("Try to add pairs. Elems are " + analyzedElems); 305 for (CompilationUnitTree t : trees) { 306 JCCompilationUnit cu = (JCCompilationUnit)t; 307 for (JCTree def : cu.defs) { 308 if (def.hasTag(CLASSDEF) && 309 analyzedElems.contains(((JCTree.JCClassDecl)def).sym)) { 310 //System.err.println("Adding pair..." + cu.sourcefile + " " + ((JCTree.JCClassDecl) def).name); 311 res.add(new Pair<>(cu, def)); 312 } 313 } 314 } 315 return res; 316 } 317 catch (Throwable t) { 318 throw new AttributionException("Exception while attributing file: " + file); 319 } 320 } 321 322 /** 323 * Report an error. When the program is complete, the program will either 324 * exit or throw an Error if any errors have been reported. 325 * @param msg the error message 326 */ 327 void error(String msg) { 328 System.err.println(); 329 System.err.println(msg); 330 System.err.println(); 331 errCount.incrementAndGet(); 332 } 333 334 /** Flag: don't report irrelevant files. */ 335 boolean quiet; 336 /** Flag: show errors in GUI viewer. */ 337 boolean gui; 338 /** The GUI viewer for errors. */ 339 Viewer viewer; 340 /** Flag: report files as they are processed. */ 341 boolean verbose; 342 /** Option: encoding for test files. */ 343 String encoding; 344 /** The set of tags for tree nodes to be analyzed; if empty, all tree nodes 345 * are analyzed. */ 346 Set<String> tags = new HashSet<String>(); 347 /** Set of files and directories to be excluded from analysis. */ 348 Set<File> excludeFiles = new HashSet<File>(); 349 /** Set of tag names to be excluded from analysis. */ 350 Set<String> excludeTags = new HashSet<String>(); 351 /** Utility class for trees */ 352 TreeUtil treeUtil = new TreeUtil(); 353 354 /** 355 * Main class for testing assertions concerning types/symbol 356 * left uninitialized after attribution 357 */ 358 private class NPETester extends TreeScanner { 359 void test(List<Pair<JCCompilationUnit, JCTree>> trees) { 360 for (Pair<JCCompilationUnit, JCTree> p : trees) { 361// System.err.println("checking " + p.fst.sourcefile); 362 sourcefile = p.fst.sourcefile; 363 endPosTable = p.fst.endPositions; 364 encl = new Info(p.snd, endPosTable); 365 p.snd.accept(this); 366 } 367 } 368 369 @Override 370 public void scan(JCTree tree) { 371 if (tree == null || 372 excludeTags.contains(treeUtil.nameFromTag(tree.getTag()))) { 373 return; 374 } 375 376 Info self = new Info(tree, endPosTable); 377 if (mandatoryType(tree)) { 378 check(tree.type != null, 379 "'null' field 'type' found in tree ", self); 380 if (tree.type==null) 381 Thread.dumpStack(); 382 } 383 384 Field errField = checkFields(tree); 385 if (errField!=null) { 386 check(false, 387 "'null' field '" + errField.getName() + "' found in tree ", self); 388 } 389 390 Info prevEncl = encl; 391 encl = self; 392 tree.accept(this); 393 encl = prevEncl; 394 } 395 396 private boolean mandatoryType(JCTree that) { 397 return that instanceof JCTree.JCExpression || 398 that.hasTag(VARDEF) || 399 that.hasTag(METHODDEF) || 400 that.hasTag(CLASSDEF); 401 } 402 403 private final List<String> excludedFields = Arrays.asList("varargsElement", "targetType"); 404 405 void check(boolean ok, String label, Info self) { 406 if (!ok) { 407 if (gui) { 408 if (viewer == null) 409 viewer = new Viewer(); 410 viewer.addEntry(sourcefile, label, encl, self); 411 } 412 error(label + self.toString() + " encl: " + encl.toString() + 413 " in file: " + sourcefile + " " + self.tree); 414 } 415 } 416 417 Field checkFields(JCTree t) { 418 List<Field> fieldsToCheck = treeUtil.getFieldsOfType(t, 419 excludedFields, 420 Symbol.class, 421 Type.class); 422 for (Field f : fieldsToCheck) { 423 try { 424 if (f.get(t) == null) { 425 return f; 426 } 427 } 428 catch (IllegalAccessException e) { 429 System.err.println("Cannot read field: " + f); 430 //swallow it 431 } 432 } 433 return null; 434 } 435 436 @Override 437 public void visitImport(JCImport tree) { } 438 439 @Override 440 public void visitTopLevel(JCCompilationUnit tree) { 441 scan(tree.defs); 442 } 443 444 JavaFileObject sourcefile; 445 EndPosTable endPosTable; 446 Info encl; 447 } 448 449 /** 450 * Utility class providing easy access to position and other info for a tree node. 451 */ 452 private class Info { 453 Info() { 454 tree = null; 455 tag = ERRONEOUS; 456 start = 0; 457 pos = 0; 458 end = Integer.MAX_VALUE; 459 } 460 461 Info(JCTree tree, EndPosTable endPosTable) { 462 this.tree = tree; 463 tag = tree.getTag(); 464 start = TreeInfo.getStartPos(tree); 465 pos = tree.pos; 466 end = TreeInfo.getEndPos(tree, endPosTable); 467 } 468 469 @Override 470 public String toString() { 471 return treeUtil.nameFromTag(tree.getTag()) + "[start:" + start + ",pos:" + pos + ",end:" + end + "]"; 472 } 473 474 final JCTree tree; 475 final JCTree.Tag tag; 476 final int start; 477 final int pos; 478 final int end; 479 } 480 481 /** 482 * Names for tree tags. 483 */ 484 private static class TreeUtil { 485 String nameFromTag(JCTree.Tag tag) { 486 String name = tag.name(); 487 return (name == null) ? "??" : name; 488 } 489 490 List<Field> getFieldsOfType(JCTree t, List<String> excludeNames, Class<?>... types) { 491 List<Field> buf = new ArrayList<Field>(); 492 for (Field f : t.getClass().getDeclaredFields()) { 493 if (!excludeNames.contains(f.getName())) { 494 for (Class<?> type : types) { 495 if (type.isAssignableFrom(f.getType())) { 496 f.setAccessible(true); 497 buf.add(f); 498 break; 499 } 500 } 501 } 502 } 503 return buf; 504 } 505 } 506 507 /** 508 * Thrown when errors are found parsing a java file. 509 */ 510 private static class ParseException extends Exception { 511 ParseException(String msg) { 512 super(msg); 513 } 514 } 515 516 private static class AttributionException extends Exception { 517 AttributionException(String msg) { 518 super(msg); 519 } 520 } 521 522 /** 523 * DiagnosticListener to report diagnostics and count any errors that occur. 524 */ 525 private static class Reporter implements DiagnosticListener<JavaFileObject> { 526 Reporter(PrintWriter out) { 527 this.out = out; 528 } 529 530 public void report(Diagnostic<? extends JavaFileObject> diagnostic) { 531 //out.println(diagnostic); 532 switch (diagnostic.getKind()) { 533 case ERROR: 534 errors++; 535 } 536 } 537 int errors; 538 PrintWriter out; 539 } 540 541 /** 542 * GUI viewer for issues found by TreePosTester. The viewer provides a drop 543 * down list for selecting error conditions, a header area providing details 544 * about an error, and a text area with the ranges of text highlighted as 545 * appropriate. 546 */ 547 private class Viewer extends JFrame { 548 /** 549 * Create a viewer. 550 */ 551 Viewer() { 552 initGUI(); 553 } 554 555 /** 556 * Add another entry to the list of errors. 557 * @param file The file containing the error 558 * @param check The condition that was being tested, and which failed 559 * @param encl the enclosing tree node 560 * @param self the tree node containing the error 561 */ 562 void addEntry(JavaFileObject file, String check, Info encl, Info self) { 563 Entry e = new Entry(file, check, encl, self); 564 DefaultComboBoxModel m = (DefaultComboBoxModel) entries.getModel(); 565 m.addElement(e); 566 if (m.getSize() == 1) 567 entries.setSelectedItem(e); 568 } 569 570 /** 571 * Initialize the GUI window. 572 */ 573 private void initGUI() { 574 JPanel head = new JPanel(new GridBagLayout()); 575 GridBagConstraints lc = new GridBagConstraints(); 576 GridBagConstraints fc = new GridBagConstraints(); 577 fc.anchor = GridBagConstraints.WEST; 578 fc.fill = GridBagConstraints.HORIZONTAL; 579 fc.gridwidth = GridBagConstraints.REMAINDER; 580 581 entries = new JComboBox(); 582 entries.addActionListener(new ActionListener() { 583 public void actionPerformed(ActionEvent e) { 584 showEntry((Entry) entries.getSelectedItem()); 585 } 586 }); 587 fc.insets.bottom = 10; 588 head.add(entries, fc); 589 fc.insets.bottom = 0; 590 head.add(new JLabel("check:"), lc); 591 head.add(checkField = createTextField(80), fc); 592 fc.fill = GridBagConstraints.NONE; 593 head.add(setBackground(new JLabel("encl:"), enclColor), lc); 594 head.add(enclPanel = new InfoPanel(), fc); 595 head.add(setBackground(new JLabel("self:"), selfColor), lc); 596 head.add(selfPanel = new InfoPanel(), fc); 597 add(head, BorderLayout.NORTH); 598 599 body = new JTextArea(); 600 body.setFont(Font.decode(Font.MONOSPACED)); 601 body.addCaretListener(new CaretListener() { 602 public void caretUpdate(CaretEvent e) { 603 int dot = e.getDot(); 604 int mark = e.getMark(); 605 if (dot == mark) 606 statusText.setText("dot: " + dot); 607 else 608 statusText.setText("dot: " + dot + ", mark:" + mark); 609 } 610 }); 611 JScrollPane p = new JScrollPane(body, 612 JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, 613 JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); 614 p.setPreferredSize(new Dimension(640, 480)); 615 add(p, BorderLayout.CENTER); 616 617 statusText = createTextField(80); 618 add(statusText, BorderLayout.SOUTH); 619 620 pack(); 621 setLocationRelativeTo(null); // centered on screen 622 setVisible(true); 623 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 624 } 625 626 /** Show an entry that has been selected. */ 627 private void showEntry(Entry e) { 628 try { 629 // update simple fields 630 setTitle(e.file.getName()); 631 checkField.setText(e.check); 632 enclPanel.setInfo(e.encl); 633 selfPanel.setInfo(e.self); 634 // show file text with highlights 635 body.setText(e.file.getCharContent(true).toString()); 636 Highlighter highlighter = body.getHighlighter(); 637 highlighter.removeAllHighlights(); 638 addHighlight(highlighter, e.encl, enclColor); 639 addHighlight(highlighter, e.self, selfColor); 640 scroll(body, getMinPos(enclPanel.info, selfPanel.info)); 641 } catch (IOException ex) { 642 body.setText("Cannot read " + e.file.getName() + ": " + e); 643 } 644 } 645 646 /** Create a test field. */ 647 private JTextField createTextField(int width) { 648 JTextField f = new JTextField(width); 649 f.setEditable(false); 650 f.setBorder(null); 651 return f; 652 } 653 654 /** Add a highlighted region based on the positions in an Info object. */ 655 private void addHighlight(Highlighter h, Info info, Color c) { 656 int start = info.start; 657 int end = info.end; 658 if (start == -1 && end == -1) 659 return; 660 if (start == -1) 661 start = end; 662 if (end == -1) 663 end = start; 664 try { 665 h.addHighlight(info.start, info.end, 666 new DefaultHighlighter.DefaultHighlightPainter(c)); 667 if (info.pos != -1) { 668 Color c2 = new Color(c.getRed(), c.getGreen(), c.getBlue(), (int)(.4f * 255)); // 40% 669 h.addHighlight(info.pos, info.pos + 1, 670 new DefaultHighlighter.DefaultHighlightPainter(c2)); 671 } 672 } catch (BadLocationException e) { 673 e.printStackTrace(); 674 } 675 } 676 677 /** Get the minimum valid position in a set of info objects. */ 678 private int getMinPos(Info... values) { 679 int i = Integer.MAX_VALUE; 680 for (Info info: values) { 681 if (info.start >= 0) i = Math.min(i, info.start); 682 if (info.pos >= 0) i = Math.min(i, info.pos); 683 if (info.end >= 0) i = Math.min(i, info.end); 684 } 685 return (i == Integer.MAX_VALUE) ? 0 : i; 686 } 687 688 /** Set the background on a component. */ 689 private JComponent setBackground(JComponent comp, Color c) { 690 comp.setOpaque(true); 691 comp.setBackground(c); 692 return comp; 693 } 694 695 /** Scroll a text area to display a given position near the middle of the visible area. */ 696 private void scroll(final JTextArea t, final int pos) { 697 // Using invokeLater appears to give text a chance to sort itself out 698 // before the scroll happens; otherwise scrollRectToVisible doesn't work. 699 // Maybe there's a better way to sync with the text... 700 EventQueue.invokeLater(new Runnable() { 701 public void run() { 702 try { 703 Rectangle r = t.modelToView(pos); 704 JScrollPane p = (JScrollPane) SwingUtilities.getAncestorOfClass(JScrollPane.class, t); 705 r.y = Math.max(0, r.y - p.getHeight() * 2 / 5); 706 r.height += p.getHeight() * 4 / 5; 707 t.scrollRectToVisible(r); 708 } catch (BadLocationException ignore) { 709 } 710 } 711 }); 712 } 713 714 private JComboBox entries; 715 private JTextField checkField; 716 private InfoPanel enclPanel; 717 private InfoPanel selfPanel; 718 private JTextArea body; 719 private JTextField statusText; 720 721 private Color selfColor = new Color(0.f, 1.f, 0.f, 0.2f); // 20% green 722 private Color enclColor = new Color(1.f, 0.f, 0.f, 0.2f); // 20% red 723 724 /** Panel to display an Info object. */ 725 private class InfoPanel extends JPanel { 726 InfoPanel() { 727 add(tagName = createTextField(20)); 728 add(new JLabel("start:")); 729 add(addListener(start = createTextField(6))); 730 add(new JLabel("pos:")); 731 add(addListener(pos = createTextField(6))); 732 add(new JLabel("end:")); 733 add(addListener(end = createTextField(6))); 734 } 735 736 void setInfo(Info info) { 737 this.info = info; 738 tagName.setText(treeUtil.nameFromTag(info.tag)); 739 start.setText(String.valueOf(info.start)); 740 pos.setText(String.valueOf(info.pos)); 741 end.setText(String.valueOf(info.end)); 742 } 743 744 JTextField addListener(final JTextField f) { 745 f.addMouseListener(new MouseAdapter() { 746 @Override 747 public void mouseClicked(MouseEvent e) { 748 body.setCaretPosition(Integer.valueOf(f.getText())); 749 body.getCaret().setVisible(true); 750 } 751 }); 752 return f; 753 } 754 755 Info info; 756 JTextField tagName; 757 JTextField start; 758 JTextField pos; 759 JTextField end; 760 } 761 762 /** Object to record information about an error to be displayed. */ 763 private class Entry { 764 Entry(JavaFileObject file, String check, Info encl, Info self) { 765 this.file = file; 766 this.check = check; 767 this.encl = encl; 768 this.self= self; 769 } 770 771 @Override 772 public String toString() { 773 return file.getName() + " " + check + " " + getMinPos(encl, self); 774 } 775 776 final JavaFileObject file; 777 final String check; 778 final Info encl; 779 final Info self; 780 } 781 } 782 783 /** Number of files that have been analyzed. */ 784 static AtomicInteger fileCount = new AtomicInteger(); 785 786} 787