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