001/*
002// $Id: //open/util/resgen/src/org/eigenbase/resgen/JavaBaseGenerator.java#4 $
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) 2001-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*/
023package org.eigenbase.resgen;
024
025import org.apache.tools.ant.BuildException;
026
027import java.io.PrintWriter;
028import java.io.File;
029import java.lang.reflect.Constructor;
030import java.util.HashSet;
031import java.util.Set;
032
033/**
034 * Generates a Java class for the base locale.
035 *
036 * @author jhyde
037 * @since 19 September, 2005
038 * @version $Id: //open/util/resgen/src/org/eigenbase/resgen/JavaBaseGenerator.java#4 $
039 */
040class JavaBaseGenerator extends AbstractJavaGenerator
041{
042    protected final Set warnedClasses = new HashSet();
043
044    JavaBaseGenerator(
045        File srcFile,
046        File file,
047        String className,
048        String baseClassName,
049        ResourceDef.ResourceBundle resourceBundle)
050    {
051        super(srcFile, file, className, resourceBundle, baseClassName);
052    }
053
054    public void generateModule(
055        ResourceGen generator,
056        ResourceDef.ResourceBundle resourceList, PrintWriter pw)
057    {
058        generateHeader(pw);
059        String className = getClassName();
060        final String classNameSansPackage = Util.removePackage(className);
061        pw.print("public class " + classNameSansPackage);
062        final String baseClass = getBaseClassName();
063        if (baseClass != null) {
064            pw.print(" extends " + baseClass);
065        }
066        pw.println(" {");
067        pw.println("    public " + classNameSansPackage + "() throws IOException {");
068        pw.println("    }");
069        pw.println("    private static final String baseName = " + Util.quoteForJava(getClassName()) + ";");
070        pw.println("    /**");
071        pw.println("     * Retrieves the singleton instance of {@link " + classNameSansPackage + "}. If");
072        pw.println("     * the application has called {@link #setThreadLocale}, returns the");
073        pw.println("     * resource for the thread's locale.");
074        pw.println("     */");
075        pw.println("    public static synchronized " + classNameSansPackage + " instance() {");
076        pw.println("        return (" + classNameSansPackage + ") instance(baseName, getThreadOrDefaultLocale(), ResourceBundle.getBundle(baseName, getThreadOrDefaultLocale()));");
077        pw.println("    }");
078        pw.println("    /**");
079        pw.println("     * Retrieves the instance of {@link " + classNameSansPackage + "} for the given locale.");
080        pw.println("     */");
081        pw.println("    public static synchronized " + classNameSansPackage + " instance(Locale locale) {");
082        pw.println("        return (" + classNameSansPackage + ") instance(baseName, locale, ResourceBundle.getBundle(baseName, locale));");
083        pw.println("    }");
084        if (resourceList.code != null) {
085            pw.println("    // begin of included code");
086            pw.print(resourceList.code.cdata);
087            pw.println("    // end of included code");
088        }
089
090        for (int j = 0; j < resourceList.resources.length; j++) {
091            ResourceDef.Resource resource = resourceList.resources[j];
092            generateResource(resource, pw);
093        }
094        pw.println("");
095        postModule(pw);
096        pw.println("}");
097    }
098
099    protected void postModule(PrintWriter pw)
100    {
101    }
102
103    public void generateResource(ResourceDef.Resource resource, PrintWriter pw)
104    {
105        if (resource.text == null) {
106            throw new BuildException(
107                    "Resource '" + resource.name + "' has no message");
108        }
109        String text = resource.text.cdata;
110        String comment = ResourceGen.getComment(resource);
111        final String resourceInitcap = ResourceGen.getResourceInitcap(resource);// e.g. "Internal"
112
113        String definitionClass = "org.eigenbase.resgen.ResourceDefinition";
114        String parameterList = getParameterList(text);
115        String argumentList = getArgumentList(text); // e.g. "p0, p1"
116        String argumentArray = argumentList.equals("") ?
117            "emptyObjectArray" :
118            "new Object[] {" + argumentList + "}"; // e.g. "new Object[] {p0, p1}"
119
120        pw.println();
121        Util.generateCommentBlock(pw, resource.name, text, comment);
122
123        pw.println("    public static final " + definitionClass + " " + resourceInitcap + " = new " + definitionClass + "(\"" + resourceInitcap + "\", " + Util.quoteForJava(text) + ");");
124        pw.println("    public String get" + resourceInitcap + "(" + parameterList + ") {");
125        pw.println("        return " + resourceInitcap + ".instantiate(" + addLists("this", argumentArray) + ").toString();");
126        pw.println("    }");
127        if (resource instanceof ResourceDef.Exception) {
128            ResourceDef.Exception exception = (ResourceDef.Exception) resource;
129            String errorClassName = getErrorClass(exception);
130            final ExceptionDescription ed = new ExceptionDescription(errorClassName);
131            if (ed.hasInstCon) {
132                pw.println("    public " + errorClassName + " new" + resourceInitcap + "(" + parameterList + ") {");
133                pw.println("        return new " + errorClassName + "(" + resourceInitcap + ".instantiate(" + addLists("this", argumentArray) + "));");
134                pw.println("    }");
135            } else if (ed.hasInstThrowCon) {
136                pw.println("    public " + errorClassName + " new" + resourceInitcap + "(" + parameterList + ") {");
137                pw.println("        return new " + errorClassName + "(" + resourceInitcap + ".instantiate(" + addLists("this", argumentArray) + "), null);");
138                pw.println("    }");
139            } else if (ed.hasStringCon) {
140                pw.println("    public " + errorClassName + " new" + resourceInitcap + "(" + parameterList + ") {");
141                pw.println("        return new " + errorClassName + "(get" + resourceInitcap + "(" + argumentList + "));");
142                pw.println("    }");
143            } else if (ed.hasStringThrowCon) {
144                pw.println("    public " + errorClassName + " new" + resourceInitcap + "(" + parameterList + ") {");
145                pw.println("        return new " + errorClassName + "(get" + resourceInitcap + "(" + argumentList + "), null);");
146                pw.println("    }");
147            }
148            if (ed.hasInstThrowCon) {
149                pw.println("    public " + errorClassName + " new" + resourceInitcap + "(" + addLists(parameterList, "Throwable err") + ") {");
150                pw.println("        return new " + errorClassName + "(" + resourceInitcap + ".instantiate(" + addLists("this", argumentArray) + "), err);");
151                pw.println("    }");
152            } else if (ed.hasStringThrowCon) {
153                pw.println("    public " + errorClassName + " new" + resourceInitcap + "(" + addLists(parameterList, "Throwable err") + ") {");
154                pw.println("        return new " + errorClassName + "(get" + resourceInitcap + "(" + argumentList + "), err);");
155                pw.println("    }");
156            }
157        }
158    }
159
160    /**
161     * Description of the constructs that an exception class has.
162     */
163    class ExceptionDescription {
164        boolean hasInstCon;
165        boolean hasInstThrowCon;
166        boolean hasStringCon;
167        boolean hasStringThrowCon;
168
169        /**
170         * Figures out what constructors the exception class has. We'd
171         * prefer to use
172         * <code>init(ResourceDefinition rd)</code> or
173         * <code>init(ResourceDefinition rd, Throwable e)</code>
174         * if it has them, but we can use
175         * <code>init(String s)</code> and
176         * <code>init(String s, Throwable e)</code>
177         * as a fall-back.
178         *
179         * Prints a warming message if the class cannot be loaded.
180         *
181         * @param errorClassName Name of exception class
182         */
183        ExceptionDescription(String errorClassName)
184        {
185            hasInstCon = false;
186            hasInstThrowCon = false;
187            hasStringCon = false;
188            hasStringThrowCon = false;
189            try {
190                Class errorClass;
191                try {
192                    errorClass = Class.forName(errorClassName);
193                } catch (ClassNotFoundException e) {
194                    // Might be in the java.lang package, for which we
195                    // allow them to omit the package name.
196                    errorClass = Class.forName("java.lang." + errorClassName);
197                }
198                Constructor[] constructors = errorClass.getConstructors();
199                for (int i = 0; i < constructors.length; i++) {
200                    Constructor constructor = constructors[i];
201                    Class[] types = constructor.getParameterTypes();
202                    if (types.length == 1 &&
203                        ResourceInstance.class.isAssignableFrom(types[0])) {
204                        hasInstCon = true;
205                    }
206                    if (types.length == 1 &&
207                        String.class.isAssignableFrom(types[0])) {
208                        hasStringCon = true;
209                    }
210                    if (types.length == 2 &&
211                        ResourceInstance.class.isAssignableFrom(types[0]) &&
212                        Throwable.class.isAssignableFrom(types[1])) {
213                        hasInstThrowCon = true;
214                    }
215                    if (types.length == 2 &&
216                        String.class.isAssignableFrom(types[0]) &&
217                        Throwable.class.isAssignableFrom(types[1])) {
218                        hasStringThrowCon = true;
219                    }
220                }
221            } catch (ClassNotFoundException e) {
222                if (warnedClasses.add(errorClassName)) {
223                    System.out.println("Warning: Could not find exception " +
224                        "class '" + errorClassName + "' on classpath. " +
225                        "Exception factory methods will not be generated.");
226                }
227            }
228        }
229    }
230
231    // helper
232    protected static String addLists(String x, String y) {
233        if (x == null || x.equals("")) {
234            if (y == null || y.equals("")) {
235                return "";
236            } else {
237                return y;
238            }
239        } else if (y == null || y.equals("")) {
240            return x;
241        } else {
242            return x + ", " + y;
243        }
244    }
245
246    protected static String addLists(String x, String y, String z) {
247        return addLists(x, addLists(y, z));
248    }
249}
250
251// End JavaBaseGenerator.java