001 /*
002 // $Id: //open/util/resgen/src/org/eigenbase/resgen/ResourceGenTask.java#7 $
003 // Package org.eigenbase.resgen is an i18n resource generator.
004 // Copyright (C) 2005-2005 The Eigenbase Project
005 // Copyright (C) 2005-2005 Disruptive Tech
006 // Copyright (C) 2005-2005 LucidEra, Inc.
007 // Portions Copyright (C) 2002-2005 Kana Software, Inc. and others.
008 //
009 // This library is free software; you can redistribute it and/or modify it
010 // under the terms of the GNU Lesser General Public License as published by the
011 // Free Software Foundation; either version 2 of the License, or (at your
012 // option) any later version approved by The Eigenbase Project.
013 //
014 // This library is distributed in the hope that it will be useful,
015 // but WITHOUT ANY WARRANTY; without even the implied warranty of
016 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
017 // GNU Lesser General Public License for more details.
018 //
019 // You should have received a copy of the GNU Lesser General Public License
020 // along with this library; if not, write to the Free Software
021 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
022 //
023 // jhyde, Oct 8, 2002
024 */
025
026 package org.eigenbase.resgen;
027
028 import org.apache.tools.ant.BuildException;
029 import org.apache.tools.ant.Task;
030
031 import java.io.File;
032 import java.io.IOException;
033 import java.util.ArrayList;
034
035 /**
036 * A <code>ResourceGenTask</code> is an ANT task to invoke the Eigenbase
037 * Resource Generator.
038 *
039 * <p>Example:<blockquote>
040 *
041 * <pre><resgen srcdir="source" locales="en_US">
042 * <include name="happy/BirthdayResource.xml"/>
043 *</resgen></pre>
044 *
045 * </blockquote>generates<blockquote>
046 *
047 * <pre>source/happy/BirthdayResource.properties
048 *source/happy/BirthdayResource_en_US.properties
049 *source/happy/BirthdayResource.java
050 *source/happy/BirthdayResource_en_US.java</pre>
051 *
052 * </blockquote>
053 *
054 * <p>C++ Example:<blockquote>
055 *
056 * <pre><resgen mode="c++" srcdir="source" locales="en_US">
057 * <include name="happy/BirthdayResource.xml"/>
058 *</resgen></pre>
059 *
060 * </blockquote>generates<blockquote>
061 *
062 * <pre>source/happy/BirthdayResource.resources
063 *source/happy/BirthdayResource_en_US.resources
064 *source/happy/BirthdayResource.h
065 *source/happy/BirthdayResource.cpp</pre></blockquote>
066 *
067 * <p>Files are not generated if there is an existing newer one.</p>
068 *
069 * <p>The output path is determined by 'destdir' (or 'resdir' for .properties
070 * files) and the package-name (derived from the XML file's path relative to
071 * 'srcdir'). Since the Java runtime environment searches for resource bundles
072 * on the classpath, it is typical to set srcdir="src", destdir="src",
073 * resdir="classes".</p>
074 *
075 * <h2>Element <resourceGen></h2>
076 *
077 * <table border="2">
078 * <tr>
079 * <th>Attribute</th>
080 * <th>Description</th>
081 * <th>Required</th>
082 * </tr>
083 *
084 * <tr>
085 * <td><a name="mode">mode</a></td>
086 * <td>Generation mode. Acceptable values are "java", "c++" or "all".
087 * The default is "java".</td>
088 * <td>No</td>
089 * </tr>
090 *
091 * <tr>
092 * <td><a name="srcdir">srcdir</a></td>
093 * <td>Source directory. The paths of resource files, and hence the
094 * package names of generated Java classes, are relative to this
095 * directory.</td>
096 * <td>Yes</td>
097 * </tr>
098 *
099 * <tr>
100 * <td><a name="destdir">destdir</a></td>
101 * <td>Destination directory. Output .java files are generated relative to this
102 * directory. If not specified, has the same value as
103 * <a href="#srcdir">srcdir</a>.</td>
104 * <td>No</td>
105 * </tr>
106 *
107 * <tr>
108 * <td><a name="resdir">resdir</a></td>
109 * <td>Resource directory. Output .properties files are generated relative to
110 * this directory. If not specified, has the same value as
111 * <a href="#destdir">destdir</a>.</td>
112 * <td>No</td>
113 * </tr>
114 *
115 * <tr>
116 * <td><a name="locales">locales</a></td>
117 * <td>Comma-separated list of locales to generate files for.
118 * If not specified, uses the locale of the resource file.</td>
119 * <td>No</td>
120 * </tr>
121 *
122 * <tr>
123 * <td><a name="style">style</a></td>
124 * <td>Code-generation style. Values are "dynamic" or "functor".
125 * Default is "dynamic": generate several non-static methods for each
126 * resource.
127 * In the "functor" style, there is one member per resource, which has
128 * several methods.</td>
129 * <td>No</td>
130 * </tr>
131 *
132 * <tr>
133 * <td><a name="force">force</a></td>
134 * <td>Whether to generate files even if they do not appear to be out of
135 * date. Default is false.</td>
136 * <td>No</td>
137 * </tr>
138 *
139 * <tr>
140 * <td><a name="commentstyle">commentstyle</a></td>
141 * <td>Generated comment style. Values are "normal" and "scm-safe". The
142 * default is "normal": generates comments that indicate the source file's
143 * original path and states that the file should not be checked into source
144 * control systems. The "scm-safe" comment style modifies the comments
145 * to make storage of the output files in an SCM more palatable. It omits
146 * the source file's path and states that the file was generated and should
147 * not be edited manually.</td>
148 * <td>No</td>
149 * </table>
150 *
151 * Nested element: <{@link Include include}>.
152 *
153 * @author jhyde
154 * @since Oct 8, 2002
155 * @version $Id: //open/util/resgen/src/org/eigenbase/resgen/ResourceGenTask.java#7 $
156 **/
157 public class ResourceGenTask extends Task
158 {
159 private ArrayList resources = new ArrayList();
160 int mode = MODE_JAVA;
161 File src;
162 File dest;
163 File res;
164 int style = STYLE_DYNAMIC;
165 String locales;
166 boolean force;
167 int commentStyle = COMMENT_STYLE_NORMAL;
168
169 private static final int MODE_UNKNOWN = -1;
170 private static final int MODE_JAVA = 1;
171 private static final int MODE_CPP = 2;
172 private static final int MODE_ALL = 3;
173
174 public static final int STYLE_DYNAMIC = 1;
175 public static final int STYLE_FUNCTOR = 2;
176
177 public static final int COMMENT_STYLE_NORMAL = 1;
178 public static final int COMMENT_STYLE_SCM_SAFE = 2;
179
180 public ResourceGenTask()
181 {
182 }
183
184 public void execute() throws BuildException
185 {
186 validate();
187 try {
188 new ResourceGen().run(this);
189 } catch (IOException e) {
190 throw new BuildException(e);
191 }
192 }
193
194 /** Called by ANT. **/
195 public void addInclude(Include resourceArgs)
196 {
197 resources.add(resourceArgs);
198 resourceArgs.root = this;
199 }
200
201 void validate()
202 {
203 if (mode != MODE_JAVA && mode != MODE_CPP && mode != MODE_ALL) {
204 throw new BuildException("You must specify a value mode: java, c++, or all");
205 }
206
207 if (src == null) {
208 throw new BuildException("You must specify 'srcdir'");
209 }
210 if (dest == null) {
211 dest = src;
212 }
213 if (res == null) {
214 res = dest;
215 }
216 final Include[] args = getIncludes();
217 for (int i = 0; i < args.length; i++) {
218 args[i].validate();
219 }
220 }
221
222 Include[] getIncludes()
223 {
224 return (Include[]) resources.toArray(new Include[0]);
225 }
226
227 /** Sets <a href="#mode">mode</a>. **/
228 public void setMode(String mode)
229 throws BuildException
230 {
231 if ("java".equals(mode)) {
232 this.mode = MODE_JAVA;
233 } else if ("c++".equals(mode)) {
234 this.mode = MODE_CPP;
235 } else if ("all".equals(mode)) {
236 this.mode = MODE_ALL;
237 } else {
238 this.mode = MODE_UNKNOWN;
239 }
240 }
241
242 /** Sets <a href="#srcdir">srcdir</a>. **/
243 public void setSrcdir(File srcDir)
244 {
245 this.src = srcDir;
246 }
247
248 /** Returns <a href="#srcdir">srcdir</a>. **/
249 public File getSrcdir()
250 {
251 return src;
252 }
253
254 /** Sets <a href="#destdir">destdir</a>. **/
255 public void setDestdir(File destDir)
256 {
257 this.dest = destDir;
258 }
259
260 /** Returns <a href="#destdir">destdir</a>. **/
261 public File getDestdir()
262 {
263 return dest;
264 }
265
266 /** Sets <a href="#resdir">resdir</a>. **/
267 public void setResdir(File resDir)
268 {
269 this.res = resDir;
270 }
271
272 /** Sets <a href="#style">style</a>. */
273 public void setStyle(String style) throws BuildException
274 {
275 if (style.equals("dynamic")) {
276 this.style = STYLE_DYNAMIC;
277 } else if (style.equals("functor")) {
278 this.style = STYLE_FUNCTOR;
279 } else {
280 throw new BuildException("Invalid style '" + style + "'");
281 }
282 }
283
284 /** Sets <a href="#locales">locales</a>. **/
285 public void setLocales(String locales) throws BuildException
286 {
287 this.locales = locales;
288 }
289
290 /** Sets <a href="#force">force</a>. **/
291 public void setForce(boolean force)
292 {
293 this.force = force;
294 }
295
296 /** Sets <a href="#commentstyle">commentstyle</a>. */
297 public void setCommentStyle(String commentStyle) throws BuildException
298 {
299 if (commentStyle.equals("normal")) {
300 this.commentStyle = COMMENT_STYLE_NORMAL;
301 } else if (commentStyle.equals("scm-safe")) {
302 this.commentStyle = COMMENT_STYLE_SCM_SAFE;
303 } else {
304 throw new BuildException(
305 "Invalid commentstyle '" + commentStyle + "'");
306 }
307 }
308
309 /**
310 * <code>Include</code> implements <include> element nested
311 * within a <resgen> task (see {@link ResourceGenTask}).
312 *
313 * <table border="2">
314 * <tr>
315 * <th>Attribute</th>
316 * <th>Description</th>
317 * <th>Required</th>
318 * </tr>
319 *
320 * <tr>
321 * <td><a name="name">name</a></td>
322 * <td>The name, relative to <a href="#srcdir">srcdir</a>, of the XML file
323 * which defines the resources.</td>
324 * <td>Yes</td>
325 * </tr>
326 *
327 * <tr>
328 * <td><a name="className">className</a></td>
329 * <td>The name of the class to be generated, including the package, but
330 * not including any locale suffix. By default, the class name is
331 * derived from the name of the source file, for example
332 * <code>happy/BirthdayResource_en_US.xml</code> becomes class
333 * <code>happy.BirthdayResource</code>.</td>
334 * <td>No</td>
335 * </tr>
336 * <tr>
337 *
338 * <td><a name="cppClassName">cppClassName</a></td>
339 * <td>The name of the C++ class to be generated. By default, the class
340 * name is derived from the name of the source file, for example
341 * <code>happy/BirthdayResource_en_US.xml</code> becomes class
342 * <code>happy.BirthdayResource</code>.</td>
343 * <td>No</td>
344 * </tr>
345 *
346 * <tr>
347 * <td><a name="baseClassName">baseClassName</a></td>
348 * <td>The fully-qualified name of the base class of the resource bundle.
349 * Defaults to "org.eigenbase.resgen.ShadowResourceBundle".</td>
350 * <td>No</td>
351 * </tr>
352 *
353 * <tr>
354 * <td><a name="cppBaseClassName">cppBaseClassName</a></td>
355 * <td>The fully-qualified name of the base class of the resource bundle
356 * for C++. Defaults to "ResourceBundle".</td>
357 * <td>No</td>
358 * </tr>
359 *
360 * </table>
361 */
362 public static class Include
363 {
364 public Include()
365 {
366 }
367 ResourceGenTask root;
368 /** Name of source file, relative to 'srcdir'. **/
369 String fileName;
370 /** Class name. **/
371 String className;
372 /** Base class. */
373 String baseClassName;
374
375 /** C++ Class name. **/
376 String cppClassName;
377 /** C++ Base class. */
378 String cppBaseClassName;
379
380 void validate() throws BuildException
381 {
382 if (fileName == null) {
383 throw new BuildException("You must specify attribute 'name'");
384 }
385 }
386
387 void process(ResourceGen generator) throws BuildException
388 {
389
390 boolean outputJava = (root.mode != ResourceGenTask.MODE_CPP);
391 boolean outputCpp = (root.mode != ResourceGenTask.MODE_JAVA);
392
393 FileTask task;
394 if (fileName.endsWith(".xml")) {
395 task = generator.createXmlTask(this, fileName,
396 className, baseClassName, outputJava,
397 cppClassName, cppBaseClassName,
398 outputCpp);
399 } else if (fileName.endsWith(".properties")) {
400 task = generator.createPropertiesTask(this, fileName);
401 } else {
402 throw new BuildException(
403 "File '" + fileName + "' is not of a supported " +
404 "type (.java or .properties)");
405 }
406 try {
407 task.process(generator);
408 } catch (IOException e) {
409 e.printStackTrace();
410 throw new BuildException(
411 "Failed while processing '" + fileName + "'", e);
412 }
413 }
414
415 /** Sets <a href="#name">name</a>. **/
416 public void setName(String name)
417 {
418 this.fileName = name;
419 }
420
421 /** Sets <a href="#className">className</a>. **/
422 public void setClassName(String className)
423 {
424 this.className = className;
425 }
426
427 /** Sets <a href="#baseClassName">baseClassName</a>. **/
428 public void setBaseClassName(String baseClassName)
429 {
430 this.baseClassName = baseClassName;
431 }
432
433 String getBaseClassName()
434 {
435 return baseClassName;
436 }
437
438 /** Sets <a href="#cppClassName">cppClassName</a>. **/
439 public void setCppClassName(String className)
440 {
441 this.cppClassName = className;
442 }
443
444 /** Sets <a href="#cppBaseClassName">cppBaseClassName</a>. **/
445 public void setCppBaseClassName(String baseClassName)
446 {
447 this.cppBaseClassName = baseClassName;
448 }
449
450 String getCppBaseClassName()
451 {
452 return cppBaseClassName;
453 }
454 }
455 }
456
457 // End ResourceGenTask.java