duke@1: /* jjh@1305: * Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved. duke@1: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. duke@1: * duke@1: * This code is free software; you can redistribute it and/or modify it duke@1: * under the terms of the GNU General Public License version 2 only, as ohair@554: * published by the Free Software Foundation. Oracle designates this duke@1: * particular file as subject to the "Classpath" exception as provided ohair@554: * by Oracle in the LICENSE file that accompanied this code. duke@1: * duke@1: * This code is distributed in the hope that it will be useful, but WITHOUT duke@1: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or duke@1: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License duke@1: * version 2 for more details (a copy is included in the LICENSE file that duke@1: * accompanied this code). duke@1: * duke@1: * You should have received a copy of the GNU General Public License version duke@1: * 2 along with this work; if not, write to the Free Software Foundation, duke@1: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. duke@1: * ohair@554: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ohair@554: * or visit www.oracle.com if you need additional information or have any ohair@554: * questions. duke@1: */ duke@1: ohrstrom@1224: package compileproperties; ohrstrom@1224: duke@1: import java.io.BufferedWriter; duke@1: import java.io.File; duke@1: import java.io.FileInputStream; duke@1: import java.io.FileNotFoundException; duke@1: import java.io.FileOutputStream; duke@1: import java.io.IOException; duke@1: import java.io.OutputStreamWriter; duke@1: import java.io.Writer; duke@1: import java.text.MessageFormat; duke@1: import java.util.ArrayList; duke@1: import java.util.Collections; duke@1: import java.util.Iterator; duke@1: import java.util.List; duke@1: import java.util.Properties; duke@1: duke@1: /** Translates a .properties file into a .java file containing the duke@1: * definition of a java.util.Properties subclass which can then be duke@1: * compiled with javac.

duke@1: * duke@1: * Usage: java CompileProperties [path to .properties file] [path to .java file to be output] [super class] duke@1: * duke@1: * Infers the package by looking at the common suffix of the two duke@1: * inputs, eliminating "classes" from it. duke@1: * duke@1: * @author Scott Violet duke@1: * @author Kenneth Russell duke@1: */ duke@1: duke@1: public class CompileProperties { duke@1: duke@1: public static void main(String[] args) { duke@1: CompileProperties cp = new CompileProperties(); duke@1: boolean ok = cp.run(args); duke@1: if ( !ok ) { duke@1: System.exit(1); duke@1: } duke@1: } duke@1: ohrstrom@1224: public static interface Log { duke@1: void info(String msg); duke@1: void verbose(String msg); duke@1: void error(String msg, Exception e); duke@1: } duke@1: duke@1: private String propfiles[]; duke@1: private String outfiles[] ; duke@1: private String supers[] ; duke@1: private int compileCount = 0; duke@1: private boolean quiet = false; ohrstrom@1224: public Log log; duke@1: duke@1: public void setLog(Log log) { duke@1: this.log = log; duke@1: } duke@1: duke@1: public boolean run(String[] args) { duke@1: if (log == null) { duke@1: log = new Log() { duke@1: public void error(String msg, Exception e) { duke@1: System.err.println("ERROR: CompileProperties: " + msg); duke@1: if ( e != null ) { duke@1: System.err.println("EXCEPTION: " + e.toString()); duke@1: e.printStackTrace(); duke@1: } duke@1: } duke@1: public void info(String msg) { duke@1: System.out.println(msg); duke@1: } duke@1: public void verbose(String msg) { duke@1: if (!quiet) duke@1: System.out.println(msg); duke@1: } duke@1: }; duke@1: } duke@1: duke@1: boolean ok = true; duke@1: /* Original usage */ duke@1: if (args.length == 2 && args[0].charAt(0) != '-' ) { jjg@465: ok = createFile(args[0], args[1], "java.util.ListResourceBundle"); duke@1: } else if (args.length == 3) { duke@1: ok = createFile(args[0], args[1], args[2]); duke@1: } else if (args.length == 0) { duke@1: usage(log); duke@1: ok = false; duke@1: } else { duke@1: /* New batch usage */ duke@1: ok = parseOptions(args); duke@1: if ( ok && compileCount == 0 ) { duke@1: log.error("options parsed but no files to compile", null); duke@1: ok = false; duke@1: } duke@1: /* Need at least one file. */ duke@1: if ( !ok ) { duke@1: usage(log); duke@1: } else { duke@1: /* Process files */ duke@1: for ( int i = 0; i < compileCount && ok ; i++ ) { duke@1: ok = createFile(propfiles[i], outfiles[i], supers[i]); duke@1: } duke@1: } duke@1: } duke@1: return ok; duke@1: } duke@1: duke@1: private boolean parseOptions(String args[]) { duke@1: boolean ok = true; duke@1: if ( compileCount > 0 ) { duke@1: String new_propfiles[] = new String[compileCount + args.length]; duke@1: String new_outfiles[] = new String[compileCount + args.length]; duke@1: String new_supers[] = new String[compileCount + args.length]; duke@1: System.arraycopy(propfiles, 0, new_propfiles, 0, compileCount); duke@1: System.arraycopy(outfiles, 0, new_outfiles, 0, compileCount); duke@1: System.arraycopy(supers, 0, new_supers, 0, compileCount); duke@1: propfiles = new_propfiles; duke@1: outfiles = new_outfiles; duke@1: supers = new_supers; duke@1: } else { duke@1: propfiles = new String[args.length]; duke@1: outfiles = new String[args.length]; duke@1: supers = new String[args.length]; duke@1: } duke@1: duke@1: for ( int i = 0; i < args.length ; i++ ) { duke@1: if ( "-compile".equals(args[i]) && i+3 < args.length ) { duke@1: propfiles[compileCount] = args[++i]; duke@1: outfiles[compileCount] = args[++i]; duke@1: supers[compileCount] = args[++i]; duke@1: compileCount++; duke@1: } else if ( "-optionsfile".equals(args[i]) && i+1 < args.length ) { duke@1: String filename = args[++i]; duke@1: FileInputStream finput = null; duke@1: byte contents[] = null; duke@1: try { duke@1: finput = new FileInputStream(filename); duke@1: int byteCount = finput.available(); duke@1: if ( byteCount <= 0 ) { duke@1: log.error("The -optionsfile file is empty", null); duke@1: ok = false; duke@1: } else { duke@1: contents = new byte[byteCount]; duke@1: int bytesRead = finput.read(contents); duke@1: if ( byteCount != bytesRead ) { duke@1: log.error("Cannot read all of -optionsfile file", null); duke@1: ok = false; duke@1: } duke@1: } duke@1: } catch ( IOException e ) { duke@1: log.error("cannot open " + filename, e); duke@1: ok = false; duke@1: } duke@1: if ( finput != null ) { duke@1: try { duke@1: finput.close(); duke@1: } catch ( IOException e ) { duke@1: ok = false; duke@1: log.error("cannot close " + filename, e); duke@1: } duke@1: } duke@1: if ( ok = true && contents != null ) { duke@1: String tokens[] = (new String(contents)).split("\\s+"); duke@1: if ( tokens.length > 0 ) { duke@1: ok = parseOptions(tokens); duke@1: } duke@1: } duke@1: if ( !ok ) { duke@1: break; duke@1: } duke@1: } else if ( "-quiet".equals(args[i]) ) { duke@1: quiet = true; duke@1: } else { duke@1: log.error("argument error", null); duke@1: ok = false; duke@1: } duke@1: } duke@1: return ok; duke@1: } duke@1: duke@1: private boolean createFile(String propertiesPath, String outputPath, duke@1: String superClass) { duke@1: boolean ok = true; duke@1: log.verbose("parsing: " + propertiesPath); duke@1: Properties p = new Properties(); duke@1: try { duke@1: p.load(new FileInputStream(propertiesPath)); duke@1: } catch ( FileNotFoundException e ) { duke@1: ok = false; duke@1: log.error("Cannot find file " + propertiesPath, e); duke@1: } catch ( IOException e ) { duke@1: ok = false; duke@1: log.error("IO error on file " + propertiesPath, e); duke@1: } duke@1: if ( ok ) { duke@1: String packageName = inferPackageName(propertiesPath, outputPath); duke@1: log.verbose("inferred package name: " + packageName); duke@1: List sortedKeys = new ArrayList(); duke@1: for ( Object key : p.keySet() ) { duke@1: sortedKeys.add((String)key); duke@1: } duke@1: Collections.sort(sortedKeys); jjg@1045: Iterator keys = sortedKeys.iterator(); duke@1: duke@1: StringBuffer data = new StringBuffer(); duke@1: duke@1: while (keys.hasNext()) { jjg@1045: String key = keys.next(); jjg@1045: data.append(" { \"" + escape(key) + "\", \"" + duke@1: escape((String)p.get(key)) + "\" },\n"); duke@1: } duke@1: duke@1: // Get class name from java filename, not the properties filename. duke@1: // (zh_TW properties might be used to create zh_HK files) duke@1: File file = new File(outputPath); duke@1: String name = file.getName(); duke@1: int dotIndex = name.lastIndexOf('.'); duke@1: String className; duke@1: if (dotIndex == -1) { duke@1: className = name; duke@1: } else { duke@1: className = name.substring(0, dotIndex); duke@1: } duke@1: duke@1: String packageString = ""; duke@1: if (packageName != null && !packageName.equals("")) { duke@1: packageString = "package " + packageName + ";\n\n"; duke@1: } duke@1: duke@1: Writer writer = null; duke@1: try { duke@1: writer = new BufferedWriter( duke@1: new OutputStreamWriter(new FileOutputStream(outputPath), "8859_1")); duke@1: MessageFormat format = new MessageFormat(FORMAT); duke@1: writer.write(format.format(new Object[] { packageString, className, superClass, data })); duke@1: } catch ( IOException e ) { duke@1: ok = false; duke@1: log.error("IO error writing to file " + outputPath, e); duke@1: } duke@1: if ( writer != null ) { duke@1: try { duke@1: writer.flush(); duke@1: } catch ( IOException e ) { duke@1: ok = false; duke@1: log.error("IO error flush " + outputPath, e); duke@1: } duke@1: try { duke@1: writer.close(); duke@1: } catch ( IOException e ) { duke@1: ok = false; duke@1: log.error("IO error close " + outputPath, e); duke@1: } duke@1: } duke@1: log.verbose("wrote: " + outputPath); duke@1: } duke@1: return ok; duke@1: } duke@1: duke@1: private static void usage(Log log) { duke@1: log.info("usage:"); duke@1: log.info(" java CompileProperties path_to_properties_file path_to_java_output_file [super_class]"); duke@1: log.info(" -OR-"); duke@1: log.info(" java CompileProperties {-compile path_to_properties_file path_to_java_output_file super_class} -or- -optionsfile filename"); duke@1: log.info(""); duke@1: log.info("Example:"); jjg@465: log.info(" java CompileProperties -compile test.properties test.java java.util.ListResourceBundle"); duke@1: log.info(" java CompileProperties -optionsfile option_file"); jjg@465: log.info("option_file contains: -compile test.properties test.java java.util.ListResourceBundle"); duke@1: } duke@1: duke@1: private static String escape(String theString) { duke@1: // This is taken from Properties.saveConvert with changes for Java strings duke@1: int len = theString.length(); duke@1: StringBuffer outBuffer = new StringBuffer(len*2); duke@1: duke@1: for(int x=0; x 0x007e)) { duke@1: outBuffer.append('\\'); duke@1: outBuffer.append('u'); duke@1: outBuffer.append(toHex((aChar >> 12) & 0xF)); duke@1: outBuffer.append(toHex((aChar >> 8) & 0xF)); duke@1: outBuffer.append(toHex((aChar >> 4) & 0xF)); duke@1: outBuffer.append(toHex( aChar & 0xF)); duke@1: } else { duke@1: if (specialSaveChars.indexOf(aChar) != -1) { duke@1: outBuffer.append('\\'); duke@1: } duke@1: outBuffer.append(aChar); duke@1: } duke@1: } duke@1: } duke@1: return outBuffer.toString(); duke@1: } duke@1: duke@1: private static String inferPackageName(String inputPath, String outputPath) { duke@1: // Normalize file names duke@1: inputPath = new File(inputPath).getPath(); duke@1: outputPath = new File(outputPath).getPath(); duke@1: // Split into components duke@1: String sep; duke@1: if (File.separatorChar == '\\') { duke@1: sep = "\\\\"; duke@1: } else { duke@1: sep = File.separator; duke@1: } duke@1: String[] inputs = inputPath.split(sep); duke@1: String[] outputs = outputPath.split(sep); duke@1: // Match common names, eliminating first "classes" entry from duke@1: // each if present duke@1: int inStart = 0; duke@1: int inEnd = inputs.length - 2; duke@1: int outEnd = outputs.length - 2; duke@1: int i = inEnd; duke@1: int j = outEnd; duke@1: while (i >= 0 && j >= 0) { duke@1: if (!inputs[i].equals(outputs[j]) || duke@1: (inputs[i].equals("gensrc") && inputs[j].equals("gensrc"))) { duke@1: ++i; duke@1: ++j; duke@1: break; duke@1: } duke@1: --i; duke@1: --j; duke@1: } duke@1: String result; duke@1: if (i < 0 || j < 0 || i >= inEnd || j >= outEnd) { duke@1: result = ""; duke@1: } else { duke@1: if (inputs[i].equals("classes") && outputs[j].equals("classes")) { duke@1: ++i; duke@1: } duke@1: inStart = i; duke@1: StringBuffer buf = new StringBuffer(); duke@1: for (i = inStart; i <= inEnd; i++) { duke@1: buf.append(inputs[i]); duke@1: if (i < inEnd) { duke@1: buf.append('.'); duke@1: } duke@1: } duke@1: result = buf.toString(); duke@1: } duke@1: return result; duke@1: } duke@1: duke@1: private static final String FORMAT = duke@1: "{0}" + duke@1: "public final class {1} extends {2} '{'\n" + duke@1: " protected final Object[][] getContents() '{'\n" + duke@1: " return new Object[][] '{'\n" + duke@1: "{3}" + duke@1: " };\n" + duke@1: " }\n" + duke@1: "}\n"; duke@1: duke@1: // This comes from Properties duke@1: private static char toHex(int nibble) { duke@1: return hexDigit[(nibble & 0xF)]; duke@1: } duke@1: duke@1: // This comes from Properties duke@1: private static final char[] hexDigit = { duke@1: '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' duke@1: }; duke@1: duke@1: // Note: different from that in Properties duke@1: private static final String specialSaveChars = "\""; duke@1: }