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
026package org.eigenbase.resgen;
027
028import org.apache.tools.ant.BuildException;
029import org.apache.tools.ant.Task;
030
031import java.io.File;
032import java.io.IOException;
033import 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>&lt;resgen srcdir="source" locales="en_US"&gt;
042 *    &lt;include name="happy/BirthdayResource.xml"/&gt;
043 *&lt;/resgen&gt;</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>&lt;resgen mode="c++" srcdir="source" locales="en_US"&gt;
057 *    &lt;include name="happy/BirthdayResource.xml"/&gt;
058 *&lt;/resgen&gt;</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 &lt;resourceGen&gt;</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: &lt;{@link Include include}&gt;.
152 *
153 * @author jhyde
154 * @since Oct 8, 2002
155 * @version $Id: //open/util/resgen/src/org/eigenbase/resgen/ResourceGenTask.java#7 $
156 **/
157public 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 &lt;include&gt; element nested
311     * within a &lt;resgen&gt; 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