test/tools/javac/failover/CheckAttributedTree.java

Tue, 08 Nov 2011 17:06:58 -0800

author
jjg
date
Tue, 08 Nov 2011 17:06:58 -0800
changeset 1136
ae361e7f435a
parent 1127
ca49d50318dc
child 1138
7375d4979bd3
permissions
-rw-r--r--

7108669: cleanup Log methods for direct printing to streams
Reviewed-by: mcimadamore

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

mercurial