aoqi@0: /* aoqi@0: * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. aoqi@0: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. aoqi@0: * aoqi@0: * This code is free software; you can redistribute it and/or modify it aoqi@0: * under the terms of the GNU General Public License version 2 only, as aoqi@0: * published by the Free Software Foundation. aoqi@0: * aoqi@0: * This code is distributed in the hope that it will be useful, but WITHOUT aoqi@0: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or aoqi@0: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License aoqi@0: * version 2 for more details (a copy is included in the LICENSE file that aoqi@0: * accompanied this code). aoqi@0: * aoqi@0: * You should have received a copy of the GNU General Public License version aoqi@0: * 2 along with this work; if not, write to the Free Software Foundation, aoqi@0: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. aoqi@0: * aoqi@0: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA aoqi@0: * or visit www.oracle.com if you need additional information or have any aoqi@0: * questions. aoqi@0: */ aoqi@0: aoqi@0: /** aoqi@0: * @test aoqi@0: * @bug 7013272 7127924 aoqi@0: * @summary Automatically generate info about how compiler resource keys are used aoqi@0: * @build Example ArgTypeCompilerFactory MessageFile MessageInfo aoqi@0: * @run main/othervm MessageInfo aoqi@0: */ aoqi@0: /* aoqi@0: * See CR 7127924 for info on why othervm is used. aoqi@0: */ aoqi@0: aoqi@0: import java.io.*; aoqi@0: import java.text.SimpleDateFormat; aoqi@0: import java.util.*; aoqi@0: aoqi@0: /** aoqi@0: * Utility to manipulate compiler.properties, and suggest info comments based aoqi@0: * on information derived from running examples. aoqi@0: * aoqi@0: * Options: aoqi@0: * -examples dir location of examples directory aoqi@0: * -o file output file aoqi@0: * -check just check message file aoqi@0: * -ensureNewlines ensure newline after each entry aoqi@0: * -fixIndent fix indentation of continuation lines aoqi@0: * -sort sort messages aoqi@0: * -verbose verbose output aoqi@0: * -replace replace comments instead of merging comments aoqi@0: * file javac compiler.properties file aoqi@0: * aoqi@0: */ aoqi@0: public class MessageInfo { aoqi@0: public static void main(String... args) throws Exception { aoqi@0: jtreg = (System.getProperty("test.src") != null); aoqi@0: File tmpDir; aoqi@0: if (jtreg) { aoqi@0: // use standard jtreg scratch directory: the current directory aoqi@0: tmpDir = new File(System.getProperty("user.dir")); aoqi@0: } else { aoqi@0: tmpDir = new File(System.getProperty("java.io.tmpdir"), aoqi@0: MessageInfo.class.getName() aoqi@0: + (new SimpleDateFormat("yyMMddHHmmss")).format(new Date())); aoqi@0: } aoqi@0: Example.setTempDir(tmpDir); aoqi@0: Example.Compiler.factory = new ArgTypeCompilerFactory(); aoqi@0: aoqi@0: MessageInfo mi = new MessageInfo(); aoqi@0: aoqi@0: try { aoqi@0: if (mi.run(args)) aoqi@0: return; aoqi@0: } finally { aoqi@0: /* VERY IMPORTANT NOTE. In jtreg mode, tmpDir is set to the aoqi@0: * jtreg scratch directory, which is the current directory. aoqi@0: * In case someone is faking jtreg mode, make sure to only aoqi@0: * clean tmpDir when it is reasonable to do so. aoqi@0: */ aoqi@0: if (tmpDir.isDirectory() && aoqi@0: tmpDir.getName().startsWith(MessageInfo.class.getName())) { aoqi@0: if (clean(tmpDir)) aoqi@0: tmpDir.delete(); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: if (jtreg) aoqi@0: throw new Exception(mi.errors + " errors occurred"); aoqi@0: else aoqi@0: System.exit(1); aoqi@0: } aoqi@0: aoqi@0: void usage() { aoqi@0: System.out.println("Usage:"); aoqi@0: System.out.println(" java MessageInfo [options] [file]"); aoqi@0: System.out.println("where options include"); aoqi@0: System.out.println(" -examples dir location of examples directory"); aoqi@0: System.out.println(" -o file output file"); aoqi@0: System.out.println(" -check just check message file"); aoqi@0: System.out.println(" -ensureNewlines ensure newline after each entry"); aoqi@0: System.out.println(" -fixIndent fix indentation of continuation lines"); aoqi@0: System.out.println(" -sort sort messages"); aoqi@0: System.out.println(" -verbose verbose output"); aoqi@0: System.out.println(" -replace replace comments instead of merging comments"); aoqi@0: System.out.println(" file javac compiler.properties file"); aoqi@0: } aoqi@0: aoqi@0: boolean run(String... args) { aoqi@0: File testSrc = new File(System.getProperty("test.src", ".")); aoqi@0: File examplesDir = new File(testSrc, "examples"); aoqi@0: File notYetFile = null; aoqi@0: File msgFile = null; aoqi@0: File outFile = null; aoqi@0: boolean verbose = false; aoqi@0: boolean ensureNewlines = false; aoqi@0: boolean fixIndent = false; aoqi@0: boolean sort = false; aoqi@0: boolean replace = false; aoqi@0: boolean check = jtreg; // default true in jtreg mode aoqi@0: aoqi@0: for (int i = 0; i < args.length; i++) { aoqi@0: String arg = args[i]; aoqi@0: if (arg.equals("-examples") && (i + 1) < args.length) aoqi@0: examplesDir = new File(args[++i]); aoqi@0: else if(arg.equals("-notyet") && (i + 1) < args.length) aoqi@0: notYetFile = new File(args[++i]); aoqi@0: else if (arg.equals("-ensureNewlines")) aoqi@0: ensureNewlines = true; aoqi@0: else if (arg.equals("-fixIndent")) aoqi@0: fixIndent = true; aoqi@0: else if (arg.equals("-sort")) aoqi@0: sort = true; aoqi@0: else if (arg.equals("-verbose")) aoqi@0: verbose = true; aoqi@0: else if (arg.equals("-replace")) aoqi@0: replace = true; aoqi@0: else if (arg.equals("-check")) aoqi@0: check = true; aoqi@0: else if (arg.equals("-o") && (i + 1) < args.length) aoqi@0: outFile = new File(args[++i]); aoqi@0: else if (arg.startsWith("-")) { aoqi@0: error("unknown option: " + arg); aoqi@0: return false; aoqi@0: } else if (i == args.length - 1) { aoqi@0: msgFile = new File(arg); aoqi@0: } else { aoqi@0: error("unknown arg: " + arg); aoqi@0: return false; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: if (!check && outFile == null) { aoqi@0: usage(); aoqi@0: return true; aoqi@0: } aoqi@0: aoqi@0: if ((ensureNewlines || fixIndent || sort) && outFile == null) { aoqi@0: error("must set output file for these options"); aoqi@0: return false; aoqi@0: } aoqi@0: aoqi@0: if (notYetFile == null) { aoqi@0: notYetFile = new File(examplesDir.getParentFile(), "examples.not-yet.txt"); aoqi@0: } aoqi@0: aoqi@0: if (msgFile == null) { aoqi@0: for (File d = testSrc; d != null; d = d.getParentFile()) { aoqi@0: if (new File(d, "TEST.ROOT").exists()) { aoqi@0: d = d.getParentFile(); aoqi@0: File f = new File(d, "src/share/classes/com/sun/tools/javac/resources/compiler.properties"); aoqi@0: if (f.exists()) { aoqi@0: msgFile = f; aoqi@0: break; aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: if (msgFile == null) { aoqi@0: if (jtreg) { aoqi@0: System.err.println("Warning: no message file available, test skipped"); aoqi@0: return true; aoqi@0: } aoqi@0: error("no message file available"); aoqi@0: return false; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: MessageFile mf; aoqi@0: try { aoqi@0: mf = new MessageFile(msgFile); aoqi@0: } catch (IOException e) { aoqi@0: error("problem reading message file: " + e); aoqi@0: return false; aoqi@0: } aoqi@0: aoqi@0: Map> msgInfo = runExamples(examplesDir, verbose); aoqi@0: aoqi@0: if (ensureNewlines) aoqi@0: ensureNewlines(mf); aoqi@0: aoqi@0: if (fixIndent) aoqi@0: fixIndent(mf); aoqi@0: aoqi@0: if (sort) aoqi@0: sort(mf, true); aoqi@0: aoqi@0: for (Map.Entry> e: msgInfo.entrySet()) { aoqi@0: String k = e.getKey(); aoqi@0: Set suggestions = e.getValue(); aoqi@0: MessageFile.Message m = mf.messages.get(k); aoqi@0: if (m == null) { aoqi@0: error("Can't find message for " + k + " in message file"); aoqi@0: continue; aoqi@0: } aoqi@0: aoqi@0: MessageFile.Info info = m.getInfo(); aoqi@0: Set placeholders = m.getPlaceholders(); aoqi@0: MessageFile.Info suggestedInfo = new MessageFile.Info(suggestions); aoqi@0: suggestedInfo.markUnused(placeholders); aoqi@0: aoqi@0: if (!info.isEmpty()) { aoqi@0: if (info.contains(suggestedInfo)) aoqi@0: continue; aoqi@0: if (!replace) { aoqi@0: if (info.fields.size() != suggestedInfo.fields.size()) aoqi@0: error("Cannot merge info for " + k); aoqi@0: else aoqi@0: suggestedInfo.merge(info); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: if (outFile == null) { aoqi@0: System.err.println("suggest for " + k); aoqi@0: System.err.println(suggestedInfo.toComment()); aoqi@0: } else aoqi@0: m.setInfo(suggestedInfo); aoqi@0: } aoqi@0: aoqi@0: if (check) aoqi@0: check(mf, notYetFile); aoqi@0: aoqi@0: try { aoqi@0: if (outFile != null) aoqi@0: mf.write(outFile); aoqi@0: } catch (IOException e) { aoqi@0: error("problem writing file: " + e); aoqi@0: return false; aoqi@0: } aoqi@0: aoqi@0: return (errors == 0); aoqi@0: } aoqi@0: aoqi@0: void check(MessageFile mf, File notYetFile) { aoqi@0: Set notYetList = null; aoqi@0: for (Map.Entry e: mf.messages.entrySet()) { aoqi@0: String key = e.getKey(); aoqi@0: MessageFile.Message m = e.getValue(); aoqi@0: if (m.needInfo() && m.getInfo().isEmpty()) { aoqi@0: if (notYetList == null) aoqi@0: notYetList = getNotYetList(notYetFile); aoqi@0: if (notYetList.contains(key)) aoqi@0: System.err.println("Warning: no info for " + key); aoqi@0: else aoqi@0: error("no info for " + key); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: } aoqi@0: aoqi@0: void ensureNewlines(MessageFile mf) { aoqi@0: for (MessageFile.Message m: mf.messages.values()) { aoqi@0: MessageFile.Line l = m.firstLine; aoqi@0: while (l.text.endsWith("\\")) aoqi@0: l = l.next; aoqi@0: if (l.next != null && !l.next.text.isEmpty()) aoqi@0: l.insertAfter(""); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: void fixIndent(MessageFile mf) { aoqi@0: for (MessageFile.Message m: mf.messages.values()) { aoqi@0: MessageFile.Line l = m.firstLine; aoqi@0: while (l.text.endsWith("\\") && l.next != null) { aoqi@0: if (!l.next.text.matches("^ \\S.*")) aoqi@0: l.next.text = " " + l.next.text.trim(); aoqi@0: l = l.next; aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: void sort(MessageFile mf, boolean includePrecedingNewlines) { aoqi@0: for (MessageFile.Message m: mf.messages.values()) { aoqi@0: for (MessageFile.Line l: m.getLines(includePrecedingNewlines)) { aoqi@0: l.remove(); aoqi@0: mf.lastLine.insertAfter(l); aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: Map> runExamples(File examplesDir, boolean verbose) { aoqi@0: Map> map = new TreeMap>(); aoqi@0: for (Example e: getExamples(examplesDir)) { aoqi@0: StringWriter sw = new StringWriter(); aoqi@0: PrintWriter pw = new PrintWriter(sw); aoqi@0: e.run(pw, true, verbose); aoqi@0: pw.close(); aoqi@0: String[] lines = sw.toString().split("\n"); aoqi@0: for (String line: lines) { aoqi@0: if (!line.startsWith("compiler.")) aoqi@0: continue; aoqi@0: int colon = line.indexOf(":"); aoqi@0: if (colon == -1) aoqi@0: continue; aoqi@0: String key = line.substring(0, colon); aoqi@0: StringBuilder sb = new StringBuilder(); aoqi@0: sb.append("# "); aoqi@0: int i = 0; aoqi@0: String[] descs = line.substring(colon + 1).split(", *"); aoqi@0: for (String desc: descs) { aoqi@0: if (i > 0) sb.append(", "); aoqi@0: sb.append(i++); aoqi@0: sb.append(": "); aoqi@0: sb.append(desc.trim()); aoqi@0: } aoqi@0: Set set = map.get(key); aoqi@0: if (set == null) aoqi@0: map.put(key, set = new TreeSet()); aoqi@0: set.add(sb.toString()); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: return map; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Get the complete set of examples to be checked. aoqi@0: */ aoqi@0: Set getExamples(File examplesDir) { aoqi@0: Set results = new TreeSet(); aoqi@0: for (File f: examplesDir.listFiles()) { aoqi@0: if (isValidExample(f)) aoqi@0: results.add(new Example(f)); aoqi@0: } aoqi@0: return results; aoqi@0: } aoqi@0: aoqi@0: boolean isValidExample(File f) { aoqi@0: return (f.isDirectory() && (!jtreg || f.list().length > 0)) || aoqi@0: (f.isFile() && f.getName().endsWith(".java")); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Get the contents of the "not-yet" list. aoqi@0: */ aoqi@0: Set getNotYetList(File file) { aoqi@0: Set results = new TreeSet(); aoqi@0: try { aoqi@0: String[] lines = read(file).split("[\r\n]"); aoqi@0: for (String line: lines) { aoqi@0: int hash = line.indexOf("#"); aoqi@0: if (hash != -1) aoqi@0: line = line.substring(0, hash).trim(); aoqi@0: if (line.matches("[A-Za-z0-9-_.]+")) aoqi@0: results.add(line); aoqi@0: } aoqi@0: } catch (IOException e) { aoqi@0: throw new Error(e); aoqi@0: } aoqi@0: return results; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Read the contents of a file. aoqi@0: */ aoqi@0: String read(File f) throws IOException { aoqi@0: byte[] bytes = new byte[(int) f.length()]; aoqi@0: DataInputStream in = new DataInputStream(new FileInputStream(f)); aoqi@0: try { aoqi@0: in.readFully(bytes); aoqi@0: } finally { aoqi@0: in.close(); aoqi@0: } aoqi@0: return new String(bytes); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Report an error. aoqi@0: */ aoqi@0: void error(String msg) { aoqi@0: System.err.println("Error: " + msg); aoqi@0: errors++; aoqi@0: } aoqi@0: aoqi@0: static boolean jtreg; aoqi@0: aoqi@0: int errors; aoqi@0: aoqi@0: /** aoqi@0: * Clean the contents of a directory. aoqi@0: */ aoqi@0: static boolean clean(File dir) { aoqi@0: boolean ok = true; aoqi@0: for (File f: dir.listFiles()) { aoqi@0: if (f.isDirectory()) aoqi@0: ok &= clean(f); aoqi@0: ok &= f.delete(); aoqi@0: } aoqi@0: return ok; aoqi@0: } aoqi@0: aoqi@0: }