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