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>&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     **/
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 &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