1.1 --- a/test/tools/javac/treepostests/TreePosTest.java Thu Mar 04 13:50:33 2010 -0800 1.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 1.3 @@ -1,762 +0,0 @@ 1.4 -/* 1.5 - * Copyright 2010 Sun Microsystems, Inc. All Rights Reserved. 1.6 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 1.7 - * 1.8 - * This code is free software; you can redistribute it and/or modify it 1.9 - * under the terms of the GNU General Public License version 2 only, as 1.10 - * published by the Free Software Foundation. 1.11 - * 1.12 - * This code is distributed in the hope that it will be useful, but WITHOUT 1.13 - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1.14 - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 1.15 - * version 2 for more details (a copy is included in the LICENSE file that 1.16 - * accompanied this code). 1.17 - * 1.18 - * You should have received a copy of the GNU General Public License version 1.19 - * 2 along with this work; if not, write to the Free Software Foundation, 1.20 - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 1.21 - * 1.22 - * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, 1.23 - * CA 95054 USA or visit www.sun.com if you need additional information or 1.24 - * have any questions. 1.25 - */ 1.26 - 1.27 -import java.awt.BorderLayout; 1.28 -import java.awt.Color; 1.29 -import java.awt.Dimension; 1.30 -import java.awt.EventQueue; 1.31 -import java.awt.Font; 1.32 -import java.awt.GridBagConstraints; 1.33 -import java.awt.GridBagLayout; 1.34 -import java.awt.Rectangle; 1.35 -import java.awt.event.ActionEvent; 1.36 -import java.awt.event.ActionListener; 1.37 -import java.awt.event.MouseAdapter; 1.38 -import java.awt.event.MouseEvent; 1.39 -import java.io.File; 1.40 -import java.io.IOException; 1.41 -import java.io.PrintStream; 1.42 -import java.io.PrintWriter; 1.43 -import java.io.StringWriter; 1.44 -import java.lang.reflect.Field; 1.45 -import java.lang.reflect.Modifier; 1.46 -import java.nio.charset.Charset; 1.47 -import java.util.ArrayList; 1.48 -import java.util.Collections; 1.49 -import java.util.HashMap; 1.50 -import java.util.HashSet; 1.51 -import java.util.Iterator; 1.52 -import java.util.List; 1.53 -import java.util.Map; 1.54 -import java.util.Set; 1.55 -import javax.swing.DefaultComboBoxModel; 1.56 -import javax.swing.JComboBox; 1.57 -import javax.swing.JComponent; 1.58 -import javax.swing.JFrame; 1.59 -import javax.swing.JLabel; 1.60 -import javax.swing.JPanel; 1.61 -import javax.swing.JScrollPane; 1.62 -import javax.swing.JTextArea; 1.63 -import javax.swing.JTextField; 1.64 -import javax.swing.SwingUtilities; 1.65 -import javax.swing.event.CaretEvent; 1.66 -import javax.swing.event.CaretListener; 1.67 -import javax.swing.text.BadLocationException; 1.68 -import javax.swing.text.DefaultHighlighter; 1.69 -import javax.swing.text.Highlighter; 1.70 -import javax.tools.Diagnostic; 1.71 -import javax.tools.DiagnosticListener; 1.72 -import javax.tools.JavaFileObject; 1.73 -import javax.tools.StandardJavaFileManager; 1.74 - 1.75 -import com.sun.source.tree.CompilationUnitTree; 1.76 -import com.sun.source.util.JavacTask; 1.77 -import com.sun.tools.javac.api.JavacTool; 1.78 -import com.sun.tools.javac.code.Flags; 1.79 -import com.sun.tools.javac.tree.JCTree; 1.80 -import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; 1.81 -import com.sun.tools.javac.tree.JCTree.JCNewClass; 1.82 -import com.sun.tools.javac.tree.JCTree.JCVariableDecl; 1.83 -import com.sun.tools.javac.tree.TreeInfo; 1.84 -import com.sun.tools.javac.tree.TreeScanner; 1.85 - 1.86 -import static com.sun.tools.javac.util.Position.NOPOS; 1.87 - 1.88 -/** 1.89 - * Utility and test program to check validity of tree positions for tree nodes. 1.90 - * The program can be run standalone, or as a jtreg test. In standalone mode, 1.91 - * errors can be displayed in a gui viewer. For info on command line args, 1.92 - * run program with no args. 1.93 - * 1.94 - * <p> 1.95 - * jtreg: Note that by using the -r switch in the test description below, this test 1.96 - * will process all java files in the langtools/test directory, thus implicitly 1.97 - * covering any new language features that may be tested in this test suite. 1.98 - */ 1.99 - 1.100 -/* 1.101 - * @test 1.102 - * @bug 6919889 1.103 - * @summary assorted position errors in compiler syntax trees 1.104 - * @run main TreePosTest -q -r -ef ./tools/javac/typeAnnotations -ef ./tools/javap/typeAnnotations -et ANNOTATED_TYPE . 1.105 - */ 1.106 -public class TreePosTest { 1.107 - /** 1.108 - * Main entry point. 1.109 - * If test.src is set, program runs in jtreg mode, and will throw an Error 1.110 - * if any errors arise, otherwise System.exit will be used, unless the gui 1.111 - * viewer is being used. In jtreg mode, the default base directory for file 1.112 - * args is the value of ${test.src}. In jtreg mode, the -r option can be 1.113 - * given to change the default base directory to the root test directory. 1.114 - */ 1.115 - public static void main(String... args) { 1.116 - String testSrc = System.getProperty("test.src"); 1.117 - File baseDir = (testSrc == null) ? null : new File(testSrc); 1.118 - boolean ok = new TreePosTest().run(baseDir, args); 1.119 - if (!ok) { 1.120 - if (testSrc != null) // jtreg mode 1.121 - throw new Error("failed"); 1.122 - else 1.123 - System.exit(1); 1.124 - } 1.125 - } 1.126 - 1.127 - /** 1.128 - * Run the program. A base directory can be provided for file arguments. 1.129 - * In jtreg mode, the -r option can be given to change the default base 1.130 - * directory to the test root directory. For other options, see usage(). 1.131 - * @param baseDir base directory for any file arguments. 1.132 - * @param args command line args 1.133 - * @return true if successful or in gui mode 1.134 - */ 1.135 - boolean run(File baseDir, String... args) { 1.136 - if (args.length == 0) { 1.137 - usage(System.out); 1.138 - return true; 1.139 - } 1.140 - 1.141 - List<File> files = new ArrayList<File>(); 1.142 - for (int i = 0; i < args.length; i++) { 1.143 - String arg = args[i]; 1.144 - if (arg.equals("-encoding") && i + 1 < args.length) 1.145 - encoding = args[++i]; 1.146 - else if (arg.equals("-gui")) 1.147 - gui = true; 1.148 - else if (arg.equals("-q")) 1.149 - quiet = true; 1.150 - else if (arg.equals("-v")) 1.151 - verbose = true; 1.152 - else if (arg.equals("-t") && i + 1 < args.length) 1.153 - tags.add(args[++i]); 1.154 - else if (arg.equals("-ef") && i + 1 < args.length) 1.155 - excludeFiles.add(new File(baseDir, args[++i])); 1.156 - else if (arg.equals("-et") && i + 1 < args.length) 1.157 - excludeTags.add(args[++i]); 1.158 - else if (arg.equals("-r")) { 1.159 - if (excludeFiles.size() > 0) 1.160 - throw new Error("-r must be used before -ef"); 1.161 - File d = baseDir; 1.162 - while (!new File(d, "TEST.ROOT").exists()) { 1.163 - d = d.getParentFile(); 1.164 - if (d == null) 1.165 - throw new Error("cannot find TEST.ROOT"); 1.166 - } 1.167 - baseDir = d; 1.168 - } 1.169 - else if (arg.startsWith("-")) 1.170 - throw new Error("unknown option: " + arg); 1.171 - else { 1.172 - while (i < args.length) 1.173 - files.add(new File(baseDir, args[i++])); 1.174 - } 1.175 - } 1.176 - 1.177 - for (File file: files) { 1.178 - if (file.exists()) 1.179 - test(file); 1.180 - else 1.181 - error("File not found: " + file); 1.182 - } 1.183 - 1.184 - if (fileCount != 1) 1.185 - System.err.println(fileCount + " files read"); 1.186 - if (errors > 0) 1.187 - System.err.println(errors + " errors"); 1.188 - 1.189 - return (gui || errors == 0); 1.190 - } 1.191 - 1.192 - /** 1.193 - * Print command line help. 1.194 - * @param out output stream 1.195 - */ 1.196 - void usage(PrintStream out) { 1.197 - out.println("Usage:"); 1.198 - out.println(" java TreePosTest options... files..."); 1.199 - out.println(""); 1.200 - out.println("where options include:"); 1.201 - out.println("-gui Display returns in a GUI viewer"); 1.202 - out.println("-q Quiet: don't report on inapplicable files"); 1.203 - out.println("-v Verbose: report on files as they are being read"); 1.204 - out.println("-t tag Limit checks to tree nodes with this tag"); 1.205 - out.println(" Can be repeated if desired"); 1.206 - out.println("-ef file Exclude file or directory"); 1.207 - out.println("-et tag Exclude tree nodes with given tag name"); 1.208 - out.println(""); 1.209 - out.println("files may be directories or files"); 1.210 - out.println("directories will be scanned recursively"); 1.211 - out.println("non java files, or java files which cannot be parsed, will be ignored"); 1.212 - out.println(""); 1.213 - } 1.214 - 1.215 - /** 1.216 - * Test a file. If the file is a directory, it will be recursively scanned 1.217 - * for java files. 1.218 - * @param file the file or directory to test 1.219 - */ 1.220 - void test(File file) { 1.221 - if (excludeFiles.contains(file)) { 1.222 - if (!quiet) 1.223 - error("File " + file + " excluded"); 1.224 - return; 1.225 - } 1.226 - 1.227 - if (file.isDirectory()) { 1.228 - for (File f: file.listFiles()) { 1.229 - test(f); 1.230 - } 1.231 - return; 1.232 - } 1.233 - 1.234 - if (file.isFile() && file.getName().endsWith(".java")) { 1.235 - try { 1.236 - if (verbose) 1.237 - System.err.println(file); 1.238 - fileCount++; 1.239 - PosTester p = new PosTester(); 1.240 - p.test(read(file)); 1.241 - } catch (ParseException e) { 1.242 - if (!quiet) { 1.243 - error("Error parsing " + file + "\n" + e.getMessage()); 1.244 - } 1.245 - } catch (IOException e) { 1.246 - error("Error reading " + file + ": " + e); 1.247 - } 1.248 - return; 1.249 - } 1.250 - 1.251 - if (!quiet) 1.252 - error("File " + file + " ignored"); 1.253 - } 1.254 - 1.255 - /** 1.256 - * Read a file. 1.257 - * @param file the file to be read 1.258 - * @return the tree for the content of the file 1.259 - * @throws IOException if any IO errors occur 1.260 - * @throws TreePosTest.ParseException if any errors occur while parsing the file 1.261 - */ 1.262 - JCCompilationUnit read(File file) throws IOException, ParseException { 1.263 - StringWriter sw = new StringWriter(); 1.264 - PrintWriter pw = new PrintWriter(sw); 1.265 - Reporter r = new Reporter(pw); 1.266 - JavacTool tool = JavacTool.create(); 1.267 - Charset cs = (encoding == null ? null : Charset.forName(encoding)); 1.268 - StandardJavaFileManager fm = tool.getStandardFileManager(r, null, null); 1.269 - Iterable<? extends JavaFileObject> files = fm.getJavaFileObjects(file); 1.270 - JavacTask task = tool.getTask(pw, fm, r, Collections.<String>emptyList(), null, files); 1.271 - Iterable<? extends CompilationUnitTree> trees = task.parse(); 1.272 - pw.flush(); 1.273 - if (r.errors > 0) 1.274 - throw new ParseException(sw.toString()); 1.275 - Iterator<? extends CompilationUnitTree> iter = trees.iterator(); 1.276 - if (!iter.hasNext()) 1.277 - throw new Error("no trees found"); 1.278 - JCCompilationUnit t = (JCCompilationUnit) iter.next(); 1.279 - if (iter.hasNext()) 1.280 - throw new Error("too many trees found"); 1.281 - return t; 1.282 - } 1.283 - 1.284 - /** 1.285 - * Report an error. When the program is complete, the program will either 1.286 - * exit or throw an Error if any errors have been reported. 1.287 - * @param msg the error message 1.288 - */ 1.289 - void error(String msg) { 1.290 - System.err.println(msg); 1.291 - errors++; 1.292 - } 1.293 - 1.294 - /** Number of files that have been analyzed. */ 1.295 - int fileCount; 1.296 - /** Number of errors reported. */ 1.297 - int errors; 1.298 - /** Flag: don't report irrelevant files. */ 1.299 - boolean quiet; 1.300 - /** Flag: report files as they are processed. */ 1.301 - boolean verbose; 1.302 - /** Flag: show errors in GUI viewer. */ 1.303 - boolean gui; 1.304 - /** Option: encoding for test files. */ 1.305 - String encoding; 1.306 - /** The GUI viewer for errors. */ 1.307 - Viewer viewer; 1.308 - /** The set of tags for tree nodes to be analyzed; if empty, all tree nodes 1.309 - * are analyzed. */ 1.310 - Set<String> tags = new HashSet<String>(); 1.311 - /** Set of files and directories to be excluded from analysis. */ 1.312 - Set<File> excludeFiles = new HashSet<File>(); 1.313 - /** Set of tag names to be excluded from analysis. */ 1.314 - Set<String> excludeTags = new HashSet<String>(); 1.315 - /** Table of printable names for tree tag values. */ 1.316 - TagNames tagNames = new TagNames(); 1.317 - 1.318 - /** 1.319 - * Main class for testing assertions concerning tree positions for tree nodes. 1.320 - */ 1.321 - private class PosTester extends TreeScanner { 1.322 - void test(JCCompilationUnit tree) { 1.323 - sourcefile = tree.sourcefile; 1.324 - endPosTable = tree.endPositions; 1.325 - encl = new Info(); 1.326 - tree.accept(this); 1.327 - } 1.328 - 1.329 - @Override 1.330 - public void scan(JCTree tree) { 1.331 - if (tree == null) 1.332 - return; 1.333 - 1.334 - Info self = new Info(tree, endPosTable); 1.335 - if (check(encl, self)) { 1.336 - // Modifiers nodes are present throughout the tree even where 1.337 - // there is no corresponding source text. 1.338 - // Redundant semicolons in a class definition can cause empty 1.339 - // initializer blocks with no positions. 1.340 - if ((self.tag == JCTree.MODIFIERS || self.tag == JCTree.BLOCK) 1.341 - && self.pos == NOPOS) { 1.342 - // If pos is NOPOS, so should be the start and end positions 1.343 - check("start == NOPOS", encl, self, self.start == NOPOS); 1.344 - check("end == NOPOS", encl, self, self.end == NOPOS); 1.345 - } else { 1.346 - // For this node, start , pos, and endpos should be all defined 1.347 - check("start != NOPOS", encl, self, self.start != NOPOS); 1.348 - check("pos != NOPOS", encl, self, self.pos != NOPOS); 1.349 - check("end != NOPOS", encl, self, self.end != NOPOS); 1.350 - // The following should normally be ordered 1.351 - // encl.start <= start <= pos <= end <= encl.end 1.352 - // In addition, the position of the enclosing node should be 1.353 - // within this node. 1.354 - // The primary exceptions are for array type nodes, because of the 1.355 - // need to support legacy syntax: 1.356 - // e.g. int a[]; int[] b[]; int f()[] { return null; } 1.357 - // and because of inconsistent nesting of left and right of 1.358 - // array declarations: 1.359 - // e.g. int[][] a = new int[2][]; 1.360 - check("encl.start <= start", encl, self, encl.start <= self.start); 1.361 - check("start <= pos", encl, self, self.start <= self.pos); 1.362 - if (!(self.tag == JCTree.TYPEARRAY 1.363 - && (encl.tag == JCTree.VARDEF || encl.tag == JCTree.TYPEARRAY))) { 1.364 - check("encl.pos <= start || end <= encl.pos", 1.365 - encl, self, encl.pos <= self.start || self.end <= encl.pos); 1.366 - } 1.367 - check("pos <= end", encl, self, self.pos <= self.end); 1.368 - if (!(self.tag == JCTree.TYPEARRAY && encl.tag == JCTree.TYPEARRAY)) { 1.369 - check("end <= encl.end", encl, self, self.end <= encl.end); 1.370 - } 1.371 - } 1.372 - } 1.373 - 1.374 - Info prevEncl = encl; 1.375 - encl = self; 1.376 - tree.accept(this); 1.377 - encl = prevEncl; 1.378 - } 1.379 - 1.380 - @Override 1.381 - public void visitVarDef(JCVariableDecl tree) { 1.382 - // enum member declarations are desugared in the parser and have 1.383 - // ill-defined semantics for tree positions, so for now, we 1.384 - // skip the synthesized bits and just check parts which came from 1.385 - // the original source text 1.386 - if ((tree.mods.flags & Flags.ENUM) != 0) { 1.387 - scan(tree.mods); 1.388 - if (tree.init != null) { 1.389 - if (tree.init.getTag() == JCTree.NEWCLASS) { 1.390 - JCNewClass init = (JCNewClass) tree.init; 1.391 - if (init.args != null && init.args.nonEmpty()) { 1.392 - scan(init.args); 1.393 - } 1.394 - if (init.def != null && init.def.defs != null) { 1.395 - scan(init.def.defs); 1.396 - } 1.397 - } 1.398 - } 1.399 - } else 1.400 - super.visitVarDef(tree); 1.401 - } 1.402 - 1.403 - boolean check(Info encl, Info self) { 1.404 - if (excludeTags.size() > 0) { 1.405 - if (encl != null && excludeTags.contains(tagNames.get(encl.tag)) 1.406 - || excludeTags.contains(tagNames.get(self.tag))) 1.407 - return false; 1.408 - } 1.409 - return tags.size() == 0 || tags.contains(tagNames.get(self.tag)); 1.410 - } 1.411 - 1.412 - void check(String label, Info encl, Info self, boolean ok) { 1.413 - if (!ok) { 1.414 - if (gui) { 1.415 - if (viewer == null) 1.416 - viewer = new Viewer(); 1.417 - viewer.addEntry(sourcefile, label, encl, self); 1.418 - } 1.419 - 1.420 - String s = self.tree.toString(); 1.421 - String msg = sourcefile.getName() + ": " + label + ": " + 1.422 - "encl:" + encl + " this:" + self + "\n" + 1.423 - s.substring(0, Math.min(80, s.length())).replaceAll("[\r\n]+", " "); 1.424 - error(msg); 1.425 - } 1.426 - } 1.427 - 1.428 - JavaFileObject sourcefile; 1.429 - Map<JCTree, Integer> endPosTable; 1.430 - Info encl; 1.431 - 1.432 - } 1.433 - 1.434 - /** 1.435 - * Utility class providing easy access to position and other info for a tree node. 1.436 - */ 1.437 - private class Info { 1.438 - Info() { 1.439 - tree = null; 1.440 - tag = JCTree.ERRONEOUS; 1.441 - start = 0; 1.442 - pos = 0; 1.443 - end = Integer.MAX_VALUE; 1.444 - } 1.445 - 1.446 - Info(JCTree tree, Map<JCTree, Integer> endPosTable) { 1.447 - this.tree = tree; 1.448 - tag = tree.getTag(); 1.449 - start = TreeInfo.getStartPos(tree); 1.450 - pos = tree.pos; 1.451 - end = TreeInfo.getEndPos(tree, endPosTable); 1.452 - } 1.453 - 1.454 - @Override 1.455 - public String toString() { 1.456 - return tagNames.get(tree.getTag()) + "[start:" + start + ",pos:" + pos + ",end:" + end + "]"; 1.457 - } 1.458 - 1.459 - final JCTree tree; 1.460 - final int tag; 1.461 - final int start; 1.462 - final int pos; 1.463 - final int end; 1.464 - } 1.465 - 1.466 - /** 1.467 - * Names for tree tags. 1.468 - * javac does not provide an API to convert tag values to strings, so this class uses 1.469 - * reflection to determine names of public static final int values in JCTree. 1.470 - */ 1.471 - private static class TagNames { 1.472 - String get(int tag) { 1.473 - if (map == null) { 1.474 - map = new HashMap<Integer, String>(); 1.475 - Class c = JCTree.class; 1.476 - for (Field f : c.getDeclaredFields()) { 1.477 - if (f.getType().equals(int.class)) { 1.478 - int mods = f.getModifiers(); 1.479 - if (Modifier.isPublic(mods) && Modifier.isStatic(mods) && Modifier.isFinal(mods)) { 1.480 - try { 1.481 - map.put(f.getInt(null), f.getName()); 1.482 - } catch (IllegalAccessException e) { 1.483 - } 1.484 - } 1.485 - } 1.486 - } 1.487 - } 1.488 - String name = map.get(tag); 1.489 - return (name == null) ? "??" : name; 1.490 - } 1.491 - 1.492 - private Map<Integer, String> map; 1.493 - } 1.494 - 1.495 - /** 1.496 - * Thrown when errors are found parsing a java file. 1.497 - */ 1.498 - private static class ParseException extends Exception { 1.499 - ParseException(String msg) { 1.500 - super(msg); 1.501 - } 1.502 - } 1.503 - 1.504 - /** 1.505 - * DiagnosticListener to report diagnostics and count any errors that occur. 1.506 - */ 1.507 - private static class Reporter implements DiagnosticListener<JavaFileObject> { 1.508 - Reporter(PrintWriter out) { 1.509 - this.out = out; 1.510 - } 1.511 - 1.512 - public void report(Diagnostic<? extends JavaFileObject> diagnostic) { 1.513 - out.println(diagnostic); 1.514 - switch (diagnostic.getKind()) { 1.515 - case ERROR: 1.516 - errors++; 1.517 - } 1.518 - } 1.519 - int errors; 1.520 - PrintWriter out; 1.521 - } 1.522 - 1.523 - /** 1.524 - * GUI viewer for issues found by TreePosTester. The viewer provides a drop 1.525 - * down list for selecting error conditions, a header area providing details 1.526 - * about an error, and a text area with the ranges of text highlighted as 1.527 - * appropriate. 1.528 - */ 1.529 - private class Viewer extends JFrame { 1.530 - /** 1.531 - * Create a viewer. 1.532 - */ 1.533 - Viewer() { 1.534 - initGUI(); 1.535 - } 1.536 - 1.537 - /** 1.538 - * Add another entry to the list of errors. 1.539 - * @param file The file containing the error 1.540 - * @param check The condition that was being tested, and which failed 1.541 - * @param encl the enclosing tree node 1.542 - * @param self the tree node containing the error 1.543 - */ 1.544 - void addEntry(JavaFileObject file, String check, Info encl, Info self) { 1.545 - Entry e = new Entry(file, check, encl, self); 1.546 - DefaultComboBoxModel m = (DefaultComboBoxModel) entries.getModel(); 1.547 - m.addElement(e); 1.548 - if (m.getSize() == 1) 1.549 - entries.setSelectedItem(e); 1.550 - } 1.551 - 1.552 - /** 1.553 - * Initialize the GUI window. 1.554 - */ 1.555 - private void initGUI() { 1.556 - JPanel head = new JPanel(new GridBagLayout()); 1.557 - GridBagConstraints lc = new GridBagConstraints(); 1.558 - GridBagConstraints fc = new GridBagConstraints(); 1.559 - fc.anchor = GridBagConstraints.WEST; 1.560 - fc.fill = GridBagConstraints.HORIZONTAL; 1.561 - fc.gridwidth = GridBagConstraints.REMAINDER; 1.562 - 1.563 - entries = new JComboBox(); 1.564 - entries.addActionListener(new ActionListener() { 1.565 - public void actionPerformed(ActionEvent e) { 1.566 - showEntry((Entry) entries.getSelectedItem()); 1.567 - } 1.568 - }); 1.569 - fc.insets.bottom = 10; 1.570 - head.add(entries, fc); 1.571 - fc.insets.bottom = 0; 1.572 - head.add(new JLabel("check:"), lc); 1.573 - head.add(checkField = createTextField(80), fc); 1.574 - fc.fill = GridBagConstraints.NONE; 1.575 - head.add(setBackground(new JLabel("encl:"), enclColor), lc); 1.576 - head.add(enclPanel = new InfoPanel(), fc); 1.577 - head.add(setBackground(new JLabel("self:"), selfColor), lc); 1.578 - head.add(selfPanel = new InfoPanel(), fc); 1.579 - add(head, BorderLayout.NORTH); 1.580 - 1.581 - body = new JTextArea(); 1.582 - body.setFont(Font.decode(Font.MONOSPACED)); 1.583 - body.addCaretListener(new CaretListener() { 1.584 - public void caretUpdate(CaretEvent e) { 1.585 - int dot = e.getDot(); 1.586 - int mark = e.getMark(); 1.587 - if (dot == mark) 1.588 - statusText.setText("dot: " + dot); 1.589 - else 1.590 - statusText.setText("dot: " + dot + ", mark:" + mark); 1.591 - } 1.592 - }); 1.593 - JScrollPane p = new JScrollPane(body, 1.594 - JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, 1.595 - JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); 1.596 - p.setPreferredSize(new Dimension(640, 480)); 1.597 - add(p, BorderLayout.CENTER); 1.598 - 1.599 - statusText = createTextField(80); 1.600 - add(statusText, BorderLayout.SOUTH); 1.601 - 1.602 - pack(); 1.603 - setLocationRelativeTo(null); // centered on screen 1.604 - setVisible(true); 1.605 - setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 1.606 - } 1.607 - 1.608 - /** Show an entry that has been selected. */ 1.609 - private void showEntry(Entry e) { 1.610 - try { 1.611 - // update simple fields 1.612 - setTitle(e.file.getName()); 1.613 - checkField.setText(e.check); 1.614 - enclPanel.setInfo(e.encl); 1.615 - selfPanel.setInfo(e.self); 1.616 - // show file text with highlights 1.617 - body.setText(e.file.getCharContent(true).toString()); 1.618 - Highlighter highlighter = body.getHighlighter(); 1.619 - highlighter.removeAllHighlights(); 1.620 - addHighlight(highlighter, e.encl, enclColor); 1.621 - addHighlight(highlighter, e.self, selfColor); 1.622 - scroll(body, getMinPos(enclPanel.info, selfPanel.info)); 1.623 - } catch (IOException ex) { 1.624 - body.setText("Cannot read " + e.file.getName() + ": " + e); 1.625 - } 1.626 - } 1.627 - 1.628 - /** Create a test field. */ 1.629 - private JTextField createTextField(int width) { 1.630 - JTextField f = new JTextField(width); 1.631 - f.setEditable(false); 1.632 - f.setBorder(null); 1.633 - return f; 1.634 - } 1.635 - 1.636 - /** Add a highlighted region based on the positions in an Info object. */ 1.637 - private void addHighlight(Highlighter h, Info info, Color c) { 1.638 - int start = info.start; 1.639 - int end = info.end; 1.640 - if (start == -1 && end == -1) 1.641 - return; 1.642 - if (start == -1) 1.643 - start = end; 1.644 - if (end == -1) 1.645 - end = start; 1.646 - try { 1.647 - h.addHighlight(info.start, info.end, 1.648 - new DefaultHighlighter.DefaultHighlightPainter(c)); 1.649 - if (info.pos != -1) { 1.650 - Color c2 = new Color(c.getRed(), c.getGreen(), c.getBlue(), (int)(.4f * 255)); // 40% 1.651 - h.addHighlight(info.pos, info.pos + 1, 1.652 - new DefaultHighlighter.DefaultHighlightPainter(c2)); 1.653 - } 1.654 - } catch (BadLocationException e) { 1.655 - e.printStackTrace(); 1.656 - } 1.657 - } 1.658 - 1.659 - /** Get the minimum valid position in a set of info objects. */ 1.660 - private int getMinPos(Info... values) { 1.661 - int i = Integer.MAX_VALUE; 1.662 - for (Info info: values) { 1.663 - if (info.start >= 0) i = Math.min(i, info.start); 1.664 - if (info.pos >= 0) i = Math.min(i, info.pos); 1.665 - if (info.end >= 0) i = Math.min(i, info.end); 1.666 - } 1.667 - return (i == Integer.MAX_VALUE) ? 0 : i; 1.668 - } 1.669 - 1.670 - /** Set the background on a component. */ 1.671 - private JComponent setBackground(JComponent comp, Color c) { 1.672 - comp.setOpaque(true); 1.673 - comp.setBackground(c); 1.674 - return comp; 1.675 - } 1.676 - 1.677 - /** Scroll a text area to display a given position near the middle of the visible area. */ 1.678 - private void scroll(final JTextArea t, final int pos) { 1.679 - // Using invokeLater appears to give text a chance to sort itself out 1.680 - // before the scroll happens; otherwise scrollRectToVisible doesn't work. 1.681 - // Maybe there's a better way to sync with the text... 1.682 - EventQueue.invokeLater(new Runnable() { 1.683 - public void run() { 1.684 - try { 1.685 - Rectangle r = t.modelToView(pos); 1.686 - JScrollPane p = (JScrollPane) SwingUtilities.getAncestorOfClass(JScrollPane.class, t); 1.687 - r.y = Math.max(0, r.y - p.getHeight() * 2 / 5); 1.688 - r.height += p.getHeight() * 4 / 5; 1.689 - t.scrollRectToVisible(r); 1.690 - } catch (BadLocationException ignore) { 1.691 - } 1.692 - } 1.693 - }); 1.694 - } 1.695 - 1.696 - private JComboBox entries; 1.697 - private JTextField checkField; 1.698 - private InfoPanel enclPanel; 1.699 - private InfoPanel selfPanel; 1.700 - private JTextArea body; 1.701 - private JTextField statusText; 1.702 - 1.703 - private Color selfColor = new Color(0.f, 1.f, 0.f, 0.2f); // 20% green 1.704 - private Color enclColor = new Color(1.f, 0.f, 0.f, 0.2f); // 20% red 1.705 - 1.706 - /** Panel to display an Info object. */ 1.707 - private class InfoPanel extends JPanel { 1.708 - InfoPanel() { 1.709 - add(tagName = createTextField(20)); 1.710 - add(new JLabel("start:")); 1.711 - add(addListener(start = createTextField(6))); 1.712 - add(new JLabel("pos:")); 1.713 - add(addListener(pos = createTextField(6))); 1.714 - add(new JLabel("end:")); 1.715 - add(addListener(end = createTextField(6))); 1.716 - } 1.717 - 1.718 - void setInfo(Info info) { 1.719 - this.info = info; 1.720 - tagName.setText(tagNames.get(info.tag)); 1.721 - start.setText(String.valueOf(info.start)); 1.722 - pos.setText(String.valueOf(info.pos)); 1.723 - end.setText(String.valueOf(info.end)); 1.724 - } 1.725 - 1.726 - JTextField addListener(final JTextField f) { 1.727 - f.addMouseListener(new MouseAdapter() { 1.728 - @Override 1.729 - public void mouseClicked(MouseEvent e) { 1.730 - body.setCaretPosition(Integer.valueOf(f.getText())); 1.731 - body.getCaret().setVisible(true); 1.732 - } 1.733 - }); 1.734 - return f; 1.735 - } 1.736 - 1.737 - Info info; 1.738 - JTextField tagName; 1.739 - JTextField start; 1.740 - JTextField pos; 1.741 - JTextField end; 1.742 - } 1.743 - 1.744 - /** Object to record information about an error to be displayed. */ 1.745 - private class Entry { 1.746 - Entry(JavaFileObject file, String check, Info encl, Info self) { 1.747 - this.file = file; 1.748 - this.check = check; 1.749 - this.encl = encl; 1.750 - this.self= self; 1.751 - } 1.752 - 1.753 - @Override 1.754 - public String toString() { 1.755 - return file.getName() + " " + check + " " + getMinPos(encl, self); 1.756 - } 1.757 - 1.758 - final JavaFileObject file; 1.759 - final String check; 1.760 - final Info encl; 1.761 - final Info self; 1.762 - } 1.763 - } 1.764 -} 1.765 -