1 | /*
|
---|
2 | * Copyright (c) 2002, 2005, 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. Oracle designates this
|
---|
8 | * particular file as subject to the "Classpath" exception as provided
|
---|
9 | * by Oracle in the LICENSE file that accompanied this code.
|
---|
10 | *
|
---|
11 | * This code is distributed in the hope that it will be useful, but WITHOUT
|
---|
12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
---|
13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
---|
14 | * version 2 for more details (a copy is included in the LICENSE file that
|
---|
15 | * accompanied this code).
|
---|
16 | *
|
---|
17 | * You should have received a copy of the GNU General Public License version
|
---|
18 | * 2 along with this work; if not, write to the Free Software Foundation,
|
---|
19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
---|
20 | *
|
---|
21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
---|
22 | * or visit www.oracle.com if you need additional information or have any
|
---|
23 | * questions.
|
---|
24 | */
|
---|
25 |
|
---|
26 | import java.io.BufferedWriter;
|
---|
27 | import java.io.File;
|
---|
28 | import java.io.FileInputStream;
|
---|
29 | import java.io.FileNotFoundException;
|
---|
30 | import java.io.FileOutputStream;
|
---|
31 | import java.io.IOException;
|
---|
32 | import java.io.OutputStreamWriter;
|
---|
33 | import java.io.Writer;
|
---|
34 | import java.text.MessageFormat;
|
---|
35 | import java.util.ArrayList;
|
---|
36 | import java.util.Collections;
|
---|
37 | import java.util.Iterator;
|
---|
38 | import java.util.List;
|
---|
39 | import java.util.Properties;
|
---|
40 |
|
---|
41 | /** Translates a .properties file into a .java file containing the
|
---|
42 | * definition of a java.util.Properties subclass which can then be
|
---|
43 | * compiled with javac. <P>
|
---|
44 | *
|
---|
45 | * Usage: java CompileProperties [path to .properties file] [path to .java file to be output] [super class]
|
---|
46 | *
|
---|
47 | * Infers the package by looking at the common suffix of the two
|
---|
48 | * inputs, eliminating "classes" from it.
|
---|
49 | *
|
---|
50 | * @author Scott Violet
|
---|
51 | * @author Kenneth Russell
|
---|
52 | */
|
---|
53 |
|
---|
54 | public class CompileProperties {
|
---|
55 |
|
---|
56 | public static void main(String[] args) {
|
---|
57 | CompileProperties cp = new CompileProperties();
|
---|
58 | boolean ok = cp.run(args);
|
---|
59 | if ( !ok ) {
|
---|
60 | System.exit(1);
|
---|
61 | }
|
---|
62 | }
|
---|
63 |
|
---|
64 | static interface Log {
|
---|
65 | void info(String msg);
|
---|
66 | void verbose(String msg);
|
---|
67 | void error(String msg, Exception e);
|
---|
68 | }
|
---|
69 |
|
---|
70 | private String propfiles[];
|
---|
71 | private String outfiles[] ;
|
---|
72 | private String supers[] ;
|
---|
73 | private int compileCount = 0;
|
---|
74 | private boolean quiet = false;
|
---|
75 | private Log log;
|
---|
76 |
|
---|
77 | public void setLog(Log log) {
|
---|
78 | this.log = log;
|
---|
79 | }
|
---|
80 |
|
---|
81 | public boolean run(String[] args) {
|
---|
82 | if (log == null) {
|
---|
83 | log = new Log() {
|
---|
84 | public void error(String msg, Exception e) {
|
---|
85 | System.err.println("ERROR: CompileProperties: " + msg);
|
---|
86 | if ( e != null ) {
|
---|
87 | System.err.println("EXCEPTION: " + e.toString());
|
---|
88 | e.printStackTrace();
|
---|
89 | }
|
---|
90 | }
|
---|
91 | public void info(String msg) {
|
---|
92 | System.out.println(msg);
|
---|
93 | }
|
---|
94 | public void verbose(String msg) {
|
---|
95 | if (!quiet)
|
---|
96 | System.out.println(msg);
|
---|
97 | }
|
---|
98 | };
|
---|
99 | }
|
---|
100 |
|
---|
101 | boolean ok = true;
|
---|
102 | /* Original usage */
|
---|
103 | if (args.length == 2 && args[0].charAt(0) != '-' ) {
|
---|
104 | ok = createFile(args[0], args[1], "ListResourceBundle");
|
---|
105 | } else if (args.length == 3) {
|
---|
106 | ok = createFile(args[0], args[1], args[2]);
|
---|
107 | } else if (args.length == 0) {
|
---|
108 | usage(log);
|
---|
109 | ok = false;
|
---|
110 | } else {
|
---|
111 | /* New batch usage */
|
---|
112 | ok = parseOptions(args);
|
---|
113 | if ( ok && compileCount == 0 ) {
|
---|
114 | log.error("options parsed but no files to compile", null);
|
---|
115 | ok = false;
|
---|
116 | }
|
---|
117 | /* Need at least one file. */
|
---|
118 | if ( !ok ) {
|
---|
119 | usage(log);
|
---|
120 | } else {
|
---|
121 | /* Process files */
|
---|
122 | for ( int i = 0; i < compileCount && ok ; i++ ) {
|
---|
123 | ok = createFile(propfiles[i], outfiles[i], supers[i]);
|
---|
124 | }
|
---|
125 | }
|
---|
126 | }
|
---|
127 | return ok;
|
---|
128 | }
|
---|
129 |
|
---|
130 | private boolean parseOptions(String args[]) {
|
---|
131 | boolean ok = true;
|
---|
132 | if ( compileCount > 0 ) {
|
---|
133 | String new_propfiles[] = new String[compileCount + args.length];
|
---|
134 | String new_outfiles[] = new String[compileCount + args.length];
|
---|
135 | String new_supers[] = new String[compileCount + args.length];
|
---|
136 | System.arraycopy(propfiles, 0, new_propfiles, 0, compileCount);
|
---|
137 | System.arraycopy(outfiles, 0, new_outfiles, 0, compileCount);
|
---|
138 | System.arraycopy(supers, 0, new_supers, 0, compileCount);
|
---|
139 | propfiles = new_propfiles;
|
---|
140 | outfiles = new_outfiles;
|
---|
141 | supers = new_supers;
|
---|
142 | } else {
|
---|
143 | propfiles = new String[args.length];
|
---|
144 | outfiles = new String[args.length];
|
---|
145 | supers = new String[args.length];
|
---|
146 | }
|
---|
147 |
|
---|
148 | for ( int i = 0; i < args.length ; i++ ) {
|
---|
149 | if ( "-compile".equals(args[i]) && i+3 < args.length ) {
|
---|
150 | propfiles[compileCount] = args[++i];
|
---|
151 | outfiles[compileCount] = args[++i];
|
---|
152 | supers[compileCount] = args[++i];
|
---|
153 | compileCount++;
|
---|
154 | } else if ( "-optionsfile".equals(args[i]) && i+1 < args.length ) {
|
---|
155 | String filename = args[++i];
|
---|
156 | FileInputStream finput = null;
|
---|
157 | byte contents[] = null;
|
---|
158 | try {
|
---|
159 | finput = new FileInputStream(filename);
|
---|
160 | int byteCount = finput.available();
|
---|
161 | if ( byteCount <= 0 ) {
|
---|
162 | log.error("The -optionsfile file is empty", null);
|
---|
163 | ok = false;
|
---|
164 | } else {
|
---|
165 | contents = new byte[byteCount];
|
---|
166 | int bytesRead = finput.read(contents);
|
---|
167 | if ( byteCount != bytesRead ) {
|
---|
168 | log.error("Cannot read all of -optionsfile file", null);
|
---|
169 | ok = false;
|
---|
170 | }
|
---|
171 | }
|
---|
172 | } catch ( IOException e ) {
|
---|
173 | log.error("cannot open " + filename, e);
|
---|
174 | ok = false;
|
---|
175 | }
|
---|
176 | if ( finput != null ) {
|
---|
177 | try {
|
---|
178 | finput.close();
|
---|
179 | } catch ( IOException e ) {
|
---|
180 | ok = false;
|
---|
181 | log.error("cannot close " + filename, e);
|
---|
182 | }
|
---|
183 | }
|
---|
184 | if ( ok = true && contents != null ) {
|
---|
185 | String tokens[] = (new String(contents)).split("\\s+");
|
---|
186 | if ( tokens.length > 0 ) {
|
---|
187 | ok = parseOptions(tokens);
|
---|
188 | }
|
---|
189 | }
|
---|
190 | if ( !ok ) {
|
---|
191 | break;
|
---|
192 | }
|
---|
193 | } else if ( "-quiet".equals(args[i]) ) {
|
---|
194 | quiet = true;
|
---|
195 | } else {
|
---|
196 | log.error("argument error", null);
|
---|
197 | ok = false;
|
---|
198 | }
|
---|
199 | }
|
---|
200 | return ok;
|
---|
201 | }
|
---|
202 |
|
---|
203 | private boolean createFile(String propertiesPath, String outputPath,
|
---|
204 | String superClass) {
|
---|
205 | boolean ok = true;
|
---|
206 | log.verbose("parsing: " + propertiesPath);
|
---|
207 | Properties p = new Properties();
|
---|
208 | try {
|
---|
209 | p.load(new FileInputStream(propertiesPath));
|
---|
210 | } catch ( FileNotFoundException e ) {
|
---|
211 | ok = false;
|
---|
212 | log.error("Cannot find file " + propertiesPath, e);
|
---|
213 | } catch ( IOException e ) {
|
---|
214 | ok = false;
|
---|
215 | log.error("IO error on file " + propertiesPath, e);
|
---|
216 | }
|
---|
217 | if ( ok ) {
|
---|
218 | String packageName = inferPackageName(propertiesPath, outputPath);
|
---|
219 | log.verbose("inferred package name: " + packageName);
|
---|
220 | List<String> sortedKeys = new ArrayList<String>();
|
---|
221 | for ( Object key : p.keySet() ) {
|
---|
222 | sortedKeys.add((String)key);
|
---|
223 | }
|
---|
224 | Collections.sort(sortedKeys);
|
---|
225 | Iterator keys = sortedKeys.iterator();
|
---|
226 |
|
---|
227 | StringBuffer data = new StringBuffer();
|
---|
228 |
|
---|
229 | while (keys.hasNext()) {
|
---|
230 | Object key = keys.next();
|
---|
231 | data.append(" { \"" + escape((String)key) + "\", \"" +
|
---|
232 | escape((String)p.get(key)) + "\" },\n");
|
---|
233 | }
|
---|
234 |
|
---|
235 | // Get class name from java filename, not the properties filename.
|
---|
236 | // (zh_TW properties might be used to create zh_HK files)
|
---|
237 | File file = new File(outputPath);
|
---|
238 | String name = file.getName();
|
---|
239 | int dotIndex = name.lastIndexOf('.');
|
---|
240 | String className;
|
---|
241 | if (dotIndex == -1) {
|
---|
242 | className = name;
|
---|
243 | } else {
|
---|
244 | className = name.substring(0, dotIndex);
|
---|
245 | }
|
---|
246 |
|
---|
247 | String packageString = "";
|
---|
248 | if (packageName != null && !packageName.equals("")) {
|
---|
249 | packageString = "package " + packageName + ";\n\n";
|
---|
250 | }
|
---|
251 |
|
---|
252 | Writer writer = null;
|
---|
253 | try {
|
---|
254 | writer = new BufferedWriter(
|
---|
255 | new OutputStreamWriter(new FileOutputStream(outputPath), "8859_1"));
|
---|
256 | MessageFormat format = new MessageFormat(FORMAT);
|
---|
257 | writer.write(format.format(new Object[] { packageString, className, superClass, data }));
|
---|
258 | } catch ( IOException e ) {
|
---|
259 | ok = false;
|
---|
260 | log.error("IO error writing to file " + outputPath, e);
|
---|
261 | }
|
---|
262 | if ( writer != null ) {
|
---|
263 | try {
|
---|
264 | writer.flush();
|
---|
265 | } catch ( IOException e ) {
|
---|
266 | ok = false;
|
---|
267 | log.error("IO error flush " + outputPath, e);
|
---|
268 | }
|
---|
269 | try {
|
---|
270 | writer.close();
|
---|
271 | } catch ( IOException e ) {
|
---|
272 | ok = false;
|
---|
273 | log.error("IO error close " + outputPath, e);
|
---|
274 | }
|
---|
275 | }
|
---|
276 | log.verbose("wrote: " + outputPath);
|
---|
277 | }
|
---|
278 | return ok;
|
---|
279 | }
|
---|
280 |
|
---|
281 | private static void usage(Log log) {
|
---|
282 | log.info("usage:");
|
---|
283 | log.info(" java CompileProperties path_to_properties_file path_to_java_output_file [super_class]");
|
---|
284 | log.info(" -OR-");
|
---|
285 | log.info(" java CompileProperties {-compile path_to_properties_file path_to_java_output_file super_class} -or- -optionsfile filename");
|
---|
286 | log.info("");
|
---|
287 | log.info("Example:");
|
---|
288 | log.info(" java CompileProperties -compile test.properties test.java ListResourceBundle");
|
---|
289 | log.info(" java CompileProperties -optionsfile option_file");
|
---|
290 | log.info("option_file contains: -compile test.properties test.java ListResourceBundle");
|
---|
291 | }
|
---|
292 |
|
---|
293 | private static String escape(String theString) {
|
---|
294 | // This is taken from Properties.saveConvert with changes for Java strings
|
---|
295 | int len = theString.length();
|
---|
296 | StringBuffer outBuffer = new StringBuffer(len*2);
|
---|
297 |
|
---|
298 | for(int x=0; x<len; x++) {
|
---|
299 | char aChar = theString.charAt(x);
|
---|
300 | switch(aChar) {
|
---|
301 | case '\\':outBuffer.append('\\'); outBuffer.append('\\');
|
---|
302 | break;
|
---|
303 | case '\t':outBuffer.append('\\'); outBuffer.append('t');
|
---|
304 | break;
|
---|
305 | case '\n':outBuffer.append('\\'); outBuffer.append('n');
|
---|
306 | break;
|
---|
307 | case '\r':outBuffer.append('\\'); outBuffer.append('r');
|
---|
308 | break;
|
---|
309 | case '\f':outBuffer.append('\\'); outBuffer.append('f');
|
---|
310 | break;
|
---|
311 | default:
|
---|
312 | if ((aChar < 0x0020) || (aChar > 0x007e)) {
|
---|
313 | outBuffer.append('\\');
|
---|
314 | outBuffer.append('u');
|
---|
315 | outBuffer.append(toHex((aChar >> 12) & 0xF));
|
---|
316 | outBuffer.append(toHex((aChar >> 8) & 0xF));
|
---|
317 | outBuffer.append(toHex((aChar >> 4) & 0xF));
|
---|
318 | outBuffer.append(toHex( aChar & 0xF));
|
---|
319 | } else {
|
---|
320 | if (specialSaveChars.indexOf(aChar) != -1) {
|
---|
321 | outBuffer.append('\\');
|
---|
322 | }
|
---|
323 | outBuffer.append(aChar);
|
---|
324 | }
|
---|
325 | }
|
---|
326 | }
|
---|
327 | return outBuffer.toString();
|
---|
328 | }
|
---|
329 |
|
---|
330 | private static String inferPackageName(String inputPath, String outputPath) {
|
---|
331 | // Normalize file names
|
---|
332 | inputPath = new File(inputPath).getPath();
|
---|
333 | outputPath = new File(outputPath).getPath();
|
---|
334 | // Split into components
|
---|
335 | String sep;
|
---|
336 | if (File.separatorChar == '\\') {
|
---|
337 | sep = "\\\\";
|
---|
338 | } else {
|
---|
339 | sep = File.separator;
|
---|
340 | }
|
---|
341 | String[] inputs = inputPath.split(sep);
|
---|
342 | String[] outputs = outputPath.split(sep);
|
---|
343 | // Match common names, eliminating first "classes" entry from
|
---|
344 | // each if present
|
---|
345 | int inStart = 0;
|
---|
346 | int inEnd = inputs.length - 2;
|
---|
347 | int outEnd = outputs.length - 2;
|
---|
348 | int i = inEnd;
|
---|
349 | int j = outEnd;
|
---|
350 | while (i >= 0 && j >= 0) {
|
---|
351 | if (!inputs[i].equals(outputs[j]) ||
|
---|
352 | (inputs[i].equals("gensrc") && inputs[j].equals("gensrc"))) {
|
---|
353 | ++i;
|
---|
354 | ++j;
|
---|
355 | break;
|
---|
356 | }
|
---|
357 | --i;
|
---|
358 | --j;
|
---|
359 | }
|
---|
360 | String result;
|
---|
361 | if (i < 0 || j < 0 || i >= inEnd || j >= outEnd) {
|
---|
362 | result = "";
|
---|
363 | } else {
|
---|
364 | if (inputs[i].equals("classes") && outputs[j].equals("classes")) {
|
---|
365 | ++i;
|
---|
366 | }
|
---|
367 | inStart = i;
|
---|
368 | StringBuffer buf = new StringBuffer();
|
---|
369 | for (i = inStart; i <= inEnd; i++) {
|
---|
370 | buf.append(inputs[i]);
|
---|
371 | if (i < inEnd) {
|
---|
372 | buf.append('.');
|
---|
373 | }
|
---|
374 | }
|
---|
375 | result = buf.toString();
|
---|
376 | }
|
---|
377 | return result;
|
---|
378 | }
|
---|
379 |
|
---|
380 | private static final String FORMAT =
|
---|
381 | "{0}" +
|
---|
382 | "import java.util.ListResourceBundle;\n\n" +
|
---|
383 | "public final class {1} extends {2} '{'\n" +
|
---|
384 | " protected final Object[][] getContents() '{'\n" +
|
---|
385 | " return new Object[][] '{'\n" +
|
---|
386 | "{3}" +
|
---|
387 | " };\n" +
|
---|
388 | " }\n" +
|
---|
389 | "}\n";
|
---|
390 |
|
---|
391 | // This comes from Properties
|
---|
392 | private static char toHex(int nibble) {
|
---|
393 | return hexDigit[(nibble & 0xF)];
|
---|
394 | }
|
---|
395 |
|
---|
396 | // This comes from Properties
|
---|
397 | private static final char[] hexDigit = {
|
---|
398 | '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'
|
---|
399 | };
|
---|
400 |
|
---|
401 | // Note: different from that in Properties
|
---|
402 | private static final String specialSaveChars = "\"";
|
---|
403 | }
|
---|