001: /*
002: * Copyright 2007 Google Inc.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005: * use this file except in compliance with the License. You may obtain a copy of
006: * the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012: * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013: * License for the specific language governing permissions and limitations under
014: * the License.
015: */
016: package com.google.gwt.i18n.tools;
017:
018: import com.google.gwt.i18n.client.Constants;
019: import com.google.gwt.i18n.client.ConstantsWithLookup;
020: import com.google.gwt.i18n.client.Messages;
021: import com.google.gwt.i18n.rebind.LocalizableGenerator;
022: import com.google.gwt.i18n.rebind.util.AbstractLocalizableInterfaceCreator;
023: import com.google.gwt.i18n.rebind.util.ConstantsInterfaceCreator;
024: import com.google.gwt.i18n.rebind.util.MessagesInterfaceCreator;
025: import com.google.gwt.util.tools.ArgHandlerExtra;
026: import com.google.gwt.util.tools.ArgHandlerFlag;
027: import com.google.gwt.util.tools.ArgHandlerString;
028: import com.google.gwt.util.tools.ToolBase;
029:
030: import java.io.File;
031: import java.io.FileNotFoundException;
032: import java.io.IOException;
033: import java.net.URL;
034:
035: /**
036: * Common public access point for localization support methods.
037: */
038: public class I18NSync extends ToolBase {
039:
040: private class classNameArgHandler extends ArgHandlerExtra {
041:
042: @Override
043: public boolean addExtraArg(String str) {
044: if (classNameArg != null) {
045: System.err.println("Too many arguments.");
046: return false;
047: }
048: // We wish to use the same sets of checks for validity whether the user
049: // calls the static method to create localizable fields or uses the
050: // command line, as the java call must throw IOException, here we must
051: // catch it and convert it to a System.err message.
052: try {
053: File resourceFile = urlToResourceFile(str);
054: checkValidResourceInputFile(resourceFile);
055: classNameArg = str;
056: } catch (IOException e) {
057: System.err.println("Error: " + e.getMessage());
058: return false;
059: }
060: return true;
061: }
062:
063: @Override
064: public String[] getDefaultArgs() {
065: return null;
066: }
067:
068: @Override
069: public String getPurpose() {
070: return "Identifies the Constants/Messages class to be created. For example com.google.sample.i18n.client.Colors";
071: }
072:
073: @Override
074: public String[] getTagArgs() {
075: String[] interfaceArg = { "name of the Constants/Messages interface to create" };
076: return interfaceArg;
077: }
078:
079: @Override
080: public boolean isRequired() {
081: return true;
082: }
083: }
084:
085: private class outDirHandler extends ArgHandlerString {
086:
087: @Override
088: public String[] getDefaultArgs() {
089: return null;
090: }
091:
092: @Override
093: public String getPurpose() {
094: return "Java source directory, defaults to the resource's class path.";
095: }
096:
097: @Override
098: public String getTag() {
099: return "-out";
100: }
101:
102: @Override
103: public String[] getTagArgs() {
104: String[] resourceArgs = { "fileName" };
105: return resourceArgs;
106: }
107:
108: @Override
109: public boolean isRequired() {
110: return false;
111: }
112:
113: @Override
114: public boolean setString(String str) {
115:
116: // We wish to use the same sets of checks for validity whether the user
117: // calls the static method to create localizable classes or uses the
118: // command line, as the java call must throw IOException, here we must
119: // catch it and convert it to a System.err message.
120: outDirArg = new File(str);
121: try {
122: checkValidSourceDir(outDirArg);
123: } catch (IOException e) {
124: System.err.println("Error: " + e.getMessage());
125: return false;
126: }
127: return true;
128: }
129: }
130:
131: /**
132: * Created Key.
133: */
134: public static final String ID = "@" + LocalizableGenerator.GWT_KEY
135: + " ";
136:
137: /**
138: * Creates a <code>Constants</code> interface from a class name. The
139: * resource file needed to create the class must be on your class path.
140: *
141: * @param className the name of the Constants class to be created
142: * @param outDir source dir root
143: * @throws IOException
144: */
145: public static void createConstantsInterfaceFromClassName(
146: String className, File outDir) throws IOException {
147: createConstantsInterfaceFromClassName(className, outDir,
148: Constants.class);
149: }
150:
151: /**
152: * Creates a <code>ConstantsWithLookup</code> interface from a class name.
153: * The resource file needed to create the class must be on your class path.
154: *
155: * @param className the name of the Constants class to be created
156: * @throws IOException
157: */
158: public static void createConstantsWithLookupInterfaceFromClassName(
159: String className) throws IOException {
160: createConstantsInterfaceFromClassName(className, null,
161: ConstantsWithLookup.class);
162: }
163:
164: /**
165: * Creates a <code>ConstantsWithLookup</code> interface from a class name.
166: * The resource file needed to create the class must be on your class path.
167: *
168: * @param className the name of the Constants class to be created
169: * @param outDir source dir root
170: * @throws IOException
171: */
172: public static void createConstantsWithLookupInterfaceFromClassName(
173: String className, File outDir) throws IOException {
174: createConstantsInterfaceFromClassName(className, outDir,
175: ConstantsWithLookup.class);
176: }
177:
178: /**
179: * Creates a <code>Messages</code> interface from a class name. The resource
180: * file needed to create the class must be on your class path.
181: *
182: * @param className the name of the Constants class to be created
183: * @throws IOException
184: */
185: public static void createMessagesInterfaceFromClassName(
186: String className) throws IOException {
187: createMessagesInterfaceFromClassName(className, null,
188: Messages.class);
189: }
190:
191: /**
192: * Creates a <code>Messages</code> interface from a class name. The resource
193: * file needed to create the class must be on your class path.
194: *
195: * @param className the name of the Constants class to be created
196: * @param outDir source dir root
197: * @throws IOException
198: */
199: public static void createMessagesInterfaceFromClassName(
200: String className, File outDir) throws IOException {
201: createMessagesInterfaceFromClassName(className, outDir,
202: Messages.class);
203: }
204:
205: /**
206: * Creates Messages and Constants java source files.
207: *
208: * @param args arguements for generation
209: */
210: public static void main(String[] args) {
211: I18NSync creator = new I18NSync();
212: if (creator.processArgs(args)) {
213: if (creator.run()) {
214: return;
215: }
216: }
217: System.exit(1);
218: }
219:
220: static void checkValidJavaSourceOutputFile(File targetSource)
221: throws IOException {
222:
223: if (targetSource.isDirectory()) {
224: throw new IOException("Output file'" + targetSource
225: + "' exists and is a directory; cannot overwrite");
226: }
227: if (targetSource.getParentFile().isDirectory() == false) {
228: throw new IOException("The target source's directory '"
229: + targetSource.getParent()
230: + "' must be an existing directory");
231: }
232: }
233:
234: static void checkValidResourceInputFile(File resource)
235: throws IOException {
236: if (!resource.getPath().endsWith(".properties")) {
237: throw new IOException("Properties files " + resource
238: + " should end with '.properties'");
239: }
240: if (!resource.exists() || !resource.isFile()) {
241: throw new IOException("Properties file not found: "
242: + resource);
243: }
244: }
245:
246: private static void checkValidSourceDir(File outDir)
247: throws IOException {
248: if (outDir.isDirectory() == false) {
249: throw new IOException(
250: outDir
251: + " must be an existing directory. Current path is "
252: + new File(".").getCanonicalPath());
253: }
254: }
255:
256: private static void createConstantsInterfaceFromClassName(
257: String className, File sourceDir,
258: Class<? extends Constants> interfaceClass)
259: throws FileNotFoundException, IOException {
260: File resource = urlToResourceFile(className);
261: File source;
262: if (sourceDir == null) {
263: source = synthesizeSourceFile(resource);
264: } else {
265: checkValidSourceDir(sourceDir);
266: String sourcePath = className.replace('.',
267: File.separatorChar);
268: sourcePath = sourceDir.getCanonicalFile() + File.separator
269: + sourcePath + ".java";
270: source = new File(sourcePath);
271: }
272: // Need both source path and class name for this check
273: checkValidJavaSourceOutputFile(source);
274: checkValidResourceInputFile(resource);
275:
276: int classDiv = className.lastIndexOf(".");
277: String packageName = className.substring(0, classDiv);
278: String name = className.substring(classDiv + 1);
279: AbstractLocalizableInterfaceCreator creator = new ConstantsInterfaceCreator(
280: name, packageName, resource, source, interfaceClass);
281: creator.generate();
282: }
283:
284: private static void createMessagesInterfaceFromClassName(
285: String className, File sourceDir,
286: Class<? extends Messages> interfaceClass)
287: throws FileNotFoundException, IOException {
288: File resource = urlToResourceFile(className);
289: File source;
290: if (sourceDir == null) {
291: source = synthesizeSourceFile(resource);
292: } else {
293: checkValidSourceDir(sourceDir);
294: String sourcePath = className.replace('.',
295: File.separatorChar);
296: sourcePath = sourceDir.getCanonicalFile() + File.separator
297: + sourcePath + ".java";
298: source = new File(sourcePath);
299: }
300: // Need both source path and class name for this check
301: checkValidJavaSourceOutputFile(source);
302: checkValidResourceInputFile(resource);
303:
304: int classDiv = className.lastIndexOf(".");
305: String packageName = className.substring(0, classDiv);
306: String name = className.substring(classDiv + 1);
307: AbstractLocalizableInterfaceCreator creator = new MessagesInterfaceCreator(
308: name, packageName, resource, source);
309: creator.generate();
310: }
311:
312: private static File synthesizeSourceFile(File resource) {
313: String javaPath = resource.getName();
314: javaPath = javaPath.substring(0, javaPath.lastIndexOf("."));
315: javaPath = resource.getParentFile().getPath() + File.separator
316: + javaPath + ".java";
317: File targetClassFile = new File(javaPath);
318: return targetClassFile;
319: }
320:
321: private static File urlToResourceFile(String className)
322: throws FileNotFoundException, IOException {
323: if (className.endsWith(".java")
324: || className.endsWith(".properties")
325: || className.endsWith(".class")
326: || className.indexOf(File.separator) > 0) {
327: throw new IllegalArgumentException(
328: "class '"
329: + className
330: + "'should not contain an extension. \"com.google.gwt.SomeClass\" is an example of a correctly formed class string");
331: }
332: String resourcePath = className.replace('.', '/')
333: + ".properties";
334: URL r = ClassLoader.getSystemResource(resourcePath);
335: if (r == null) {
336: throw new FileNotFoundException(
337: "Could not find the resource '"
338: + resourcePath
339: + " matching '"
340: + className
341: + "' did you remember to add it to your classpath?");
342: }
343: File resourceFile = new File(r.getFile());
344: return resourceFile;
345: }
346:
347: private String classNameArg;
348:
349: private boolean createMessagesArg = false;
350:
351: private File outDirArg;
352:
353: private I18NSync() {
354: registerHandler(new classNameArgHandler());
355: registerHandler(new outDirHandler());
356: registerHandler(new ArgHandlerFlag() {
357:
358: @Override
359: public String getPurpose() {
360: return "create Messages interface rather than Constants interface.";
361: }
362:
363: @Override
364: public String getTag() {
365: return "-createMessages";
366: }
367:
368: @Override
369: public boolean setFlag() {
370: createMessagesArg = true;
371: return true;
372: }
373: });
374: }
375:
376: /**
377: * Creates the interface.
378: *
379: * @return whether the interface was created
380: */
381: protected boolean run() {
382: try {
383: if (createMessagesArg) {
384: createMessagesInterfaceFromClassName(classNameArg,
385: outDirArg, Messages.class);
386: } else {
387: createConstantsInterfaceFromClassName(classNameArg,
388: outDirArg, Constants.class);
389: }
390: return true;
391: } catch (Throwable e) {
392: System.err.println(e.getMessage());
393: return false;
394: }
395: }
396: }
|