resgen/ 0000755 0001750 0001750 00000000000 11543216467 012246 5 ustar drazzib drazzib resgen/build.xml 0000444 0001750 0001750 00000017717 11543216467 014102 0 ustar drazzib drazzib
DTD Name:
Example: C++ Example: Files are not generated if there is an existing newer one. The output path is determined by 'destdir' (or 'resdir' for .properties
* files) and the package-name (derived from the XML file's path relative to
* 'srcdir'). Since the Java runtime environment searches for resource bundles
* on the classpath, it is typical to set srcdir="src", destdir="src",
* resdir="classes". For example, {@code count("foobar", 'o')} returns 2.
*
* @param s String
* @param c Character
* @return Number of occurrences
*/
private int count(String s, char c) {
int count = 0;
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) == c) {
++count;
}
}
return count;
}
/**
* Generates a properties file for a given locale. If there is a source
* file for the locale, it is copied. Otherwise generates a file with
* headers but no resources.
*
* @param pw Output file writer
* @param targetFile the locale-specific output file
* @param srcFile The locale-specific properties file, e.g.
* "source/happy/BirthdayResource_fr-FR.properties". It may not exist,
* but if it does, we copy it.
* @param locale Locale, never null
* @pre locale != null
*/
private void generateProperties(
PrintWriter pw,
File targetFile,
File srcFile,
Locale locale)
{
if (srcFile.exists() && srcFile.canRead() && !targetFile.equals(srcFile)) {
try {
final FileReader reader = new FileReader(srcFile);
final char[] cbuf = new char[1000];
int charsRead;
while ((charsRead = reader.read(cbuf)) > 0) {
pw.write(cbuf, 0, charsRead);
}
return;
} catch (IOException e) {
throw new BuildException("Error while copying from '" +
srcFile + "'");
}
}
// Generate an empty file.
String fullClassName = getClassName(locale);
pw.println("# This file contains the resources for");
pw.println("# class '" + fullClassName + "' and locale '" + locale + "'.");
pw.println("# It was generated by " + ResourceGen.class);
pw.println("# from " + getFileForComments());
if (include.root.commentStyle !=
ResourceGenTask.COMMENT_STYLE_SCM_SAFE)
{
pw.println("# on " + new Date().toString() + ".");
}
pw.println();
pw.println("# This file is intentionally blank. Add property values");
pw.println("# to this file to override the translations in the base");
String basePropertiesFileName = Util.getClassNameSansPackage(className, locale) + ".properties";
pw.println("# properties file, " + basePropertiesFileName);
pw.println();
pw.println("# End " + fullClassName + ".properties");
}
private String getClassName(Locale locale) {
String s = className;
if (locale != null) {
s += '_' + locale.toString();
}
return s;
}
protected void generateCpp(
ResourceGen generator,
ResourceDef.ResourceBundle resourceList)
{
String defaultExceptionClass = resourceList.cppExceptionClassName;
String defaultExceptionLocation = resourceList.cppExceptionClassLocation;
if (defaultExceptionClass != null &&
defaultExceptionLocation == null) {
throw new BuildException(
"C++ exception class is defined without a header file location in "
+ getFile());
}
for (int i = 0; i < resourceList.resources.length; i++) {
ResourceDef.Resource resource = resourceList.resources[i];
if (resource.text == null) {
throw new BuildException(
"Resource '" + resource.name + "' has no message");
}
if (resource instanceof ResourceDef.Exception) {
ResourceDef.Exception exception =
(ResourceDef.Exception)resource;
if (exception.cppClassName != null &&
(exception.cppClassLocation == null &&
defaultExceptionLocation == null)) {
throw new BuildException(
"C++ exception class specified for "
+ exception.name
+ " without specifiying a header location in "
+ getFile());
}
if (defaultExceptionClass == null &&
exception.cppClassName == null) {
throw new BuildException(
"No exception class specified for "
+ exception.name
+ " in "
+ getFile());
}
}
}
String hFilename = cppClassName + ".h";
String cppFileName = cppClassName + ".cpp";
File hFile = new File(include.root.dest, hFilename);
File cppFile = new File(include.root.dest, cppFileName);
boolean allUpToDate = true;
if (!checkUpToDate(generator, hFile)) {
allUpToDate = false;
}
if (!checkUpToDate(generator, cppFile)) {
allUpToDate = false;
}
if (allUpToDate && !include.root.force) {
return;
}
generator.comment("Generating " + hFile);
final FileOutputStream hOut;
try {
makeParentDirs(hFile);
hOut = new FileOutputStream(hFile);
} catch (FileNotFoundException e) {
throw new BuildException("Error while writing " + hFile, e);
}
String className = Util.removePackage(this.className);
String baseClassName = Util.removePackage(this.cppBaseClassName);
PrintWriter pw = new PrintWriter(hOut);
try {
final CppHeaderGenerator gen =
new CppHeaderGenerator(getFile(), hFile,
className, baseClassName, defaultExceptionClass);
configureCommentStyle(gen);
gen.generateModule(generator, resourceList, pw);
} finally {
pw.close();
}
generator.comment("Generating " + cppFile);
final FileOutputStream cppOut;
try {
makeParentDirs(cppFile);
cppOut = new FileOutputStream(cppFile);
} catch (FileNotFoundException e) {
throw new BuildException("Error while writing " + cppFile, e);
}
pw = new PrintWriter(cppOut);
try {
final CppGenerator gen =
new CppGenerator(getFile(), cppFile, className, baseClassName,
defaultExceptionClass, hFilename);
configureCommentStyle(gen);
gen.generateModule(generator, resourceList, pw);
} finally {
pw.close();
}
}
}
// End XmlFileTask.java
resgen/src/org/eigenbase/resgen/ShadowResourceBundle.java 0000444 0001750 0001750 00000024253 11543216467 023767 0 ustar drazzib drazzib /*
// $Id: //open/util/resgen/src/org/eigenbase/resgen/ShadowResourceBundle.java#5 $
// Package org.eigenbase.resgen is an i18n resource generator.
// Copyright (C) 2005-2005 The Eigenbase Project
// Copyright (C) 2005-2005 Disruptive Tech
// Copyright (C) 2005-2005 LucidEra, Inc.
// Portions Copyright (C) 2002-2005 Kana Software, Inc. and others.
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published by the
// Free Software Foundation; either version 2 of the License, or (at your
// option) any later version approved by The Eigenbase Project.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// jhyde, 19 September, 2002
*/
package org.eigenbase.resgen;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
/**
* In the standard scheme (see {@link ResourceBundle}),
* if you call You should create a class as follows: This method should be called from a derived class, with the proper
* casting: This method should be called from a derived class, with the proper
* casting: This method should be called from a derived class, with the proper
* casting:XOM Meta Model Instance
Class Name:
Root Element:
Version:
Overview
Imports
Element Summary
Classes
Element
Class
Attributes
Attribute Type Default
Description
Content
Element Java Name Constraints Description
Plugin
Class
Attributes
Attribute Type Default
Description
defPackage String
TODO
The defPackage attribute, available to all Plugins, specifies
the package of the Java Class used to parse all plugin contents.
defClass String
TODO
The defClass attribute, available to all Plugins, specifies
the class name of the Java Class used to parse all plugin contents.
Content
Any from defClass
Class
Superclass
Attributes
Attribute Type Default
Description
Content
Element Java Name Constraints Description
StringElement
Attributes
none
Content
Text
Package:
Import
Class:
DTD:
org.eigenbase.resgen
* package, all them static
and package-private.
*
* @author jhyde
* @since 3 December, 2001
* @version $Id: //open/util/resgen/src/org/eigenbase/resgen/Util.java#6 $
**/
abstract class Util {
private static final Throwable[] emptyThrowableArray = new Throwable[0];
/** loads URL into Document and returns set of resources **/
static ResourceDef.ResourceBundle load(URL url)
throws IOException
{
return load(url.openStream());
}
/** loads InputStream and returns set of resources **/
static ResourceDef.ResourceBundle load(InputStream inStream)
throws IOException
{
try {
Parser parser = XOMUtil.createDefaultParser();
DOMWrapper def = parser.parse(inStream);
ResourceDef.ResourceBundle xmlResourceList = new
ResourceDef.ResourceBundle(def);
return xmlResourceList;
} catch (XOMException err) {
throw new IOException(err.toString());
}
}
/**
* Left-justify a block of text. Line breaks are preserved, but long lines
* are broken.
*
* @param pw where to output the formatted text
* @param text the text to be written
* @param linePrefix a string to prepend to each output line
* @param lineSuffix a string to append to each output line
* @param maxTextPerLine the maximum number of characters to place on
* each line, not counting the prefix and suffix. If this is -1,
* never break lines.
**/
static void fillText(
PrintWriter pw, String text, String linePrefix, String lineSuffix,
int maxTextPerLine)
{
int i = 0;
for (;;) {
int end = text.length();
if (end <= i) {
// Nothing left. We're done.
break;
}
if (i > 0) {
// End the previous line and start another.
pw.println(lineSuffix);
pw.print(linePrefix);
}
int nextCR = text.indexOf("\r", i);
if (nextCR >= 0 && nextCR < end) {
end = nextCR;
}
int nextLF = text.indexOf("\n", i);
if (nextLF >= 0 && nextLF < end) {
end = nextLF;
}
if (maxTextPerLine > 0 && i + maxTextPerLine <= end) {
// More than a line left. Break at the last space before the
// line limit.
end = text.lastIndexOf(" ",i + maxTextPerLine);
if (end < i) {
// No space exists before the line limit; look beyond it.
end = text.indexOf(" ",i);
if (end < 0) {
// No space anywhere in the line. Take the whole line.
end = text.length();
}
}
}
pw.print(text.substring(i, end));
// The line is short enough. Print it, and find where the next one
// starts.
i = end;
while (i < text.length() &&
(text.charAt(i) == ' ' ||
text.charAt(i) == '\r' ||
text.charAt(i) == '\n')) {
i++;
}
}
}
static URL stringToUrl(String strFile) throws IOException
{
try {
File f = new File(strFile);
return convertPathToURL(f);
} catch (Throwable err) {
throw new IOException(err.toString());
}
}
/**
* Creates a file-protocol URL for the given filename.
**/
static URL convertPathToURL(File file)
{
try {
String path = file.getAbsolutePath();
// This is a bunch of weird code that is required to
// make a valid URL on the Windows platform, due
// to inconsistencies in what getAbsolutePath returns.
String fs = System.getProperty("file.separator");
if (fs.length() == 1)
{
char sep = fs.charAt(0);
if (sep != '/')
path = path.replace(sep, '/');
if (path.charAt(0) != '/')
path = '/' + path;
}
path = "file://" + path;
return new URL(path);
} catch (MalformedURLException e) {
throw new java.lang.Error(e.getMessage());
}
}
static String formatError(String template, Object[] args)
{
String s = template;
for (int i = 0; i < args.length; i++) {
String arg = args[i].toString();
s = replace(s, "%" + (i + 1), arg);
s = replace(s, "%i" + (i + 1), arg);
}
return s;
}
/** Returns s
with every instance of find
* converted to replace
. */
static String replace(String s,String find,String replace) {
// let's be optimistic
int found = s.indexOf(find);
if (found == -1) {
return s;
}
StringBuffer sb = new StringBuffer(s.length());
int start = 0;
for (;;) {
for (; start < found; start++) {
sb.append(s.charAt(start));
}
if (found == s.length()) {
break;
}
sb.append(replace);
start += find.length();
found = s.indexOf(find,start);
if (found == -1) {
found = s.length();
}
}
return sb.toString();
}
/** Return val
in double-quotes, suitable as a string in a
* Java or JScript program.
*
* @param val the value
* @param nullMeansNull whether to print a null value as null
* (the default), as opposed to ""
*/
static String quoteForJava(String val,boolean nullMeansNull)
{
if (val == null) {
return nullMeansNull ? "null" : "";
}
String s0;
s0 = replace(val, "\\", "\\\\");
s0 = replace(val, "\"", "\\\"");
s0 = replace(s0, "\n\r", "\\n");
s0 = replace(s0, "\n", "\\n");
s0 = replace(s0, "\r", "\\r");
return "\"" + s0 + "\"";
}
static String quoteForJava(String val)
{
return quoteForJava(val,true);
}
/**
* Returns a string quoted so that it can appear in a resource file.
*/
static String quoteForProperties(String val) {
String s0;
s0 = replace(val, "\\", "\\\\");
// s0 = replace(val, "\"", "\\\"");
// s0 = replace(s0, "'", "''");
s0 = replace(s0, "\n\r", "\\n");
s0 = replace(s0, "\n", "\\n");
s0 = replace(s0, "\r", "\\r");
s0 = replace(s0, "\t", "\\t");
return s0;
}
static final char fileSep = System.getProperty("file.separator").charAt(0);
static String fileNameToClassName(String fileName, String suffix) {
String s = fileName;
s = removeSuffix(s, suffix);
s = s.replace(fileSep, '.');
s = s.replace('/', '.');
int score = s.indexOf('_');
if (score >= 0) {
s = s.substring(0,score);
}
return s;
}
static String fileNameToCppClassName(String fileName, String suffix) {
String s = fileName;
s = removeSuffix(s, suffix);
int pos = s.lastIndexOf(fileSep);
if (pos >= 0) {
s = s.substring(pos + 1);
}
int score = s.indexOf('_');
if (score >= 0) {
s = s.substring(0, score);
}
return s;
}
static String removeSuffix(String s, final String suffix) {
if (s.endsWith(suffix)) {
s = s.substring(0,s.length()-suffix.length());
}
return s;
}
/**
* Given happy/BirthdayResource_en_US.xml
,
* returns the locale "en_US".
*/
static Locale fileNameToLocale(String fileName, String suffix) {
String s = removeSuffix(fileName, suffix);
int score = s.indexOf('_');
if (score <= 0) {
return null;
} else {
String localeName = s.substring(score + 1);
return parseLocale(localeName);
}
}
/**
* Parses 'localeName' into a locale.
*/
static Locale parseLocale(String localeName) {
int score1 = localeName.indexOf('_');
String language, country = "", variant = "";
if (score1 < 0) {
language = localeName;
} else {
language = localeName.substring(0, score1);
if (language.length() != 2) {
return null;
}
int score2 = localeName.indexOf('_',score1 + 1);
if (score2 < 0) {
country = localeName.substring(score1 + 1);
if (country.length() != 2) {
return null;
}
} else {
country = localeName.substring(score1 + 1, score2);
if (country.length() != 2) {
return null;
}
variant = localeName.substring(score2 + 1);
}
}
return new Locale(language,country,variant);
}
/**
* Given "happy/BirthdayResource_fr_FR.properties" and ".properties",
* returns "happy/BirthdayResource".
*/
static String fileNameSansLocale(String fileName, String suffix) {
String s = removeSuffix(fileName, suffix);
// If there are directory names, start reading after the last one.
int from = s.lastIndexOf(fileSep);
if (from < 0) {
from = 0;
}
while (from < s.length()) {
// See whether the rest of the filename after the current
// underscore is a valid locale name. If it is, return the
// segment of the filename before the current underscore.
int score = s.indexOf('_', from);
Locale locale = parseLocale(s.substring(score+1));
if (locale != null) {
return s.substring(0,score);
}
from = score + 1;
}
return s;
}
/**
* Converts a chain of {@link Throwable}s into an array.
**/
static Throwable[] toArray(Throwable err)
{
ArrayList list = new ArrayList();
while (err != null) {
list.add(err);
err = getCause(err);
}
return (Throwable[]) list.toArray(emptyThrowableArray);
}
private static final Class[] emptyClassArray = new Class[0];
private static Throwable getCause(Throwable err) {
if (err instanceof InvocationTargetException) {
return ((InvocationTargetException) err).getTargetException();
}
try {
Method method = err.getClass().getMethod(
"getCause", emptyClassArray);
if (Throwable.class.isAssignableFrom(method.getReturnType())) {
return (Throwable) method.invoke(err, new Object[0]);
}
} catch (NoSuchMethodException e) {
} catch (SecurityException e) {
} catch (IllegalAccessException e) {
} catch (IllegalArgumentException e) {
} catch (InvocationTargetException e) {
}
try {
Method method = err.getClass().getMethod(
"getNestedThrowable", emptyClassArray);
if (Throwable.class.isAssignableFrom(method.getReturnType())) {
return (Throwable) method.invoke(err, new Object[0]);
}
} catch (NoSuchMethodException e) {
} catch (SecurityException e) {
} catch (IllegalAccessException e) {
} catch (IllegalArgumentException e) {
} catch (InvocationTargetException e) {
}
return null;
}
/**
* Formats an error, which may have chained errors, as a string.
*/
static String toString(Throwable err)
{
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
Throwable[] throwables = toArray(err);
for (int i = 0; i < throwables.length; i++) {
Throwable throwable = throwables[i];
if (i > 0) {
pw.println();
pw.print("Caused by: ");
}
pw.print(throwable.toString());
}
return sw.toString();
}
static void printStackTrace(Throwable throwable, PrintWriter s) {
Throwable[] stack = Util.toArray(throwable);
PrintWriter pw = new DummyPrintWriter(s);
for (int i = 0; i < stack.length; i++) {
if (i > 0) {
pw.println("caused by");
}
stack[i].printStackTrace(pw);
}
pw.flush();
}
static void printStackTrace(Throwable throwable, PrintStream s) {
Throwable[] stack = Util.toArray(throwable);
PrintStream ps = new DummyPrintStream(s);
for (int i = 0; i < stack.length; i++) {
if (i > 0) {
ps.println("caused by");
}
stack[i].printStackTrace(ps);
}
ps.flush();
}
static void generateCommentBlock(
PrintWriter pw,
String name,
String text,
String comment)
{
final String indent = " ";
pw.println(indent + "/**");
if (comment != null) {
fillText(pw, comment, indent + " * ", "", 70);
pw.println();
pw.println(indent + " *");
}
pw.print(indent + " * ");
fillText(
pw,
"" + name + "
is '"
+ StringEscaper.xmlEscaper.escapeString(text) + "
'",
indent + " * ", "", -1);
pw.println();
pw.println(indent + " */");
}
/**
* Returns the class name without its package name but with a locale
* extension, if applicable.
* For example, if class name is happy.BirthdayResource
,
* and locale is en_US
,
* returns BirthdayResource_en_US
.
*/
static String getClassNameSansPackage(String className, Locale locale) {
String s = className;
int lastDot = className.lastIndexOf('.');
if (lastDot >= 0) {
s = s.substring(lastDot + 1);
}
if (locale != null) {
s += '_' + locale.toString();
}
return s;
}
protected static String removePackage(String s)
{
int lastDot = s.lastIndexOf('.');
if (lastDot >= 0) {
s = s.substring(lastDot + 1);
}
return s;
}
/**
* So we know to avoid recursively calling
* {@link Util#printStackTrace(Throwable,java.io.PrintWriter)}.
*/
static class DummyPrintWriter extends PrintWriter {
public DummyPrintWriter(Writer out) {
super(out);
}
}
/**
* So we know to avoid recursively calling
* {@link Util#printStackTrace(Throwable,PrintStream)}.
*/
static class DummyPrintStream extends PrintStream {
public DummyPrintStream(OutputStream out) {
super(out);
}
}
}
// End Util.java
resgen/src/org/eigenbase/resgen/AbstractGenerator.java 0000444 0001750 0001750 00000014116 11543216467 023307 0 ustar drazzib drazzib /*
// $Id: //open/util/resgen/src/org/eigenbase/resgen/AbstractGenerator.java#3 $
// Package org.eigenbase.resgen is an i18n resource generator.
// Copyright (C) 2005-2005 The Eigenbase Project
// Copyright (C) 2005-2005 Disruptive Tech
// Copyright (C) 2005-2005 LucidEra, Inc.
// Portions Copyright (C) 2001-2005 Kana Software, Inc. and others.
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published by the
// Free Software Foundation; either version 2 of the License, or (at your
// option) any later version approved by The Eigenbase Project.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.eigenbase.resgen;
import java.io.PrintWriter;
import java.io.File;
import java.util.Date;
import java.text.Format;
import java.text.MessageFormat;
import java.text.NumberFormat;
import java.text.DateFormat;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;
/**
* Abstract base for all generators.
*
* @author jhyde
* @since 19 September, 2005
* @version $Id: //open/util/resgen/src/org/eigenbase/resgen/AbstractGenerator.java#3 $
*/
abstract class AbstractGenerator implements Generator
{
private final File srcFile;
private final File file;
private Boolean scmSafeComments = null;
public AbstractGenerator(File srcFile, File file)
{
this.srcFile = srcFile;
this.file = file;
}
public void setScmSafeComments(boolean enabled)
{
if (scmSafeComments != null) {
throw new AssertionError(
"SCM safe comment style may only be configured once.");
}
scmSafeComments = enabled ? Boolean.TRUE : Boolean.FALSE;
}
protected boolean useScmSafeComments()
{
return scmSafeComments != null && scmSafeComments.booleanValue();
}
/**
* Generates code for a particular resource.
*/
protected abstract void generateResource(
ResourceDef.Resource resource,
PrintWriter pw);
protected void generateDoNotModifyHeader(PrintWriter pw) {
if (useScmSafeComments()) {
pw.println(
"// This class is generated. Do NOT modify it manually.");
} else {
pw.println("// This class is generated. Do NOT modify it, or");
pw.println("// add it to source control.");
}
pw.println();
}
protected void generateGeneratedByBlock(PrintWriter pw) {
pw.println("/**");
pw.println(" * This class was generated");
pw.println(" * by " + ResourceGen.class);
String file = getSrcFileForComment();
pw.println(" * from " + file);
if (!useScmSafeComments()) {
pw.println(" * on " + new Date().toString() + ".");
}
pw.println(" * It contains a list of messages, and methods to");
pw.println(" * retrieve and format those messages.");
pw.println(" */");
pw.println();
}
/**
* Returns the generator's output file. e.g., "BirthdayResource.java"
*/
protected File getFile()
{
return file;
}
/**
* Returns the XML or .properties source file, in a manner suitable
* for use in source code comments. Path information is stripped if
* SCM-safe comment style is enabled.
*
* @see #setScmSafeComments(boolean)
*/
protected String getSrcFileForComment()
{
String filename = srcFile.toString().replace('\\', '/');
if (useScmSafeComments()) {
int slashPos = filename.lastIndexOf('/');
if (slashPos > 0) {
filename = "..." + filename.substring(slashPos);
}
}
return filename;
}
/**
* Returns the fully-qualified name of the class being generated,
* for example "happy.BirthdayResource_en_US".
*/
protected abstract String getClassName();
/**
* Returns the fully-qualified name of the base class.
*/
protected abstract String getBaseClassName();
/**
* Returns a parameter list string, e.g. "String p0, int p1".
*/
protected String getParameterList(String message) {
final String [] types = getArgTypes(message);
if (types.length == 0) {
return "";
}
StringBuffer sb = new StringBuffer();
for (int i = 0; i < types.length; i++) {
String type = types[i];
if (i > 0) {
sb.append(", ");
}
sb.append(type);
// If this is a C++ pointer type, say "const char *", don't put
// a space between it and the variable name.
if (!type.endsWith("&") && !type.endsWith("*")) {
sb.append(" ");
}
sb.append("p");
sb.append(Integer.toString(i));
}
return sb.toString();
}
/**
* Returns the number and types of parameters in the given error message,
* expressed as an array of Strings (legal values are
* currently "String", "Number", "java.util.Date", and null) ordered by
* parameter number.
*/
protected abstract String [] getArgTypes(String message);
protected String getArgumentList(String message)
{
final String [] types = getArgTypes(message);
if (types.length == 0) {
return "";
}
StringBuffer sb = new StringBuffer();
for (int i = 0; i < types.length; i++) {
if (i > 0) {
sb.append(", ");
}
sb.append("p");
sb.append(Integer.toString(i));
}
return sb.toString();
}
}
// End AbstractGenerator.java
resgen/src/org/eigenbase/resgen/CppHeaderGenerator.java 0000444 0001750 0001750 00000020371 11543216467 023377 0 ustar drazzib drazzib /*
// $Id: //open/util/resgen/src/org/eigenbase/resgen/CppHeaderGenerator.java#3 $
// Package org.eigenbase.resgen is an i18n resource generator.
// Copyright (C) 2005-2005 The Eigenbase Project
// Copyright (C) 2005-2005 Disruptive Tech
// Copyright (C) 2005-2005 LucidEra, Inc.
// Portions Copyright (C) 2001-2005 Kana Software, Inc. and others.
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published by the
// Free Software Foundation; either version 2 of the License, or (at your
// option) any later version approved by The Eigenbase Project.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.eigenbase.resgen;
import java.io.File;
import java.io.PrintWriter;
/**
* Generates a C++ header file containing resource definitions.
*
* @author jhyde
* @since 19 September, 2005
* @version $Id: //open/util/resgen/src/org/eigenbase/resgen/CppHeaderGenerator.java#3 $
*/
public class CppHeaderGenerator extends CppGenerator
{
/**
* Creates a C++ header generator.
*
* @param srcFile
* @param file
* @param className
* @param baseClassName Name of base class, must not be null, typically
* @param defaultExceptionClassName
*/
public CppHeaderGenerator(
File srcFile,
File file,
String className,
String baseClassName,
String defaultExceptionClassName)
{
super(srcFile, file, className, baseClassName,
defaultExceptionClassName, null);
}
public void generateModule(
ResourceGen generator,
ResourceDef.ResourceBundle resourceList,
PrintWriter pw)
{
generateDoNotModifyHeader(pw);
generateGeneratedByBlock(pw);
StringBuffer ifndef = new StringBuffer();
String fileName = getFile().getName();
ifndef.append(fileName.substring(0, fileName.length() - 2));
ifndef.append("_Included");
if (resourceList.cppNamespace != null) {
ifndef.insert(0, '_');
ifndef.insert(0, resourceList.cppNamespace.substring(1));
ifndef.insert(0, Character.toUpperCase(resourceList
.cppNamespace
.charAt(0)));
}
pw.println("#ifndef " + ifndef.toString());
pw.println("#define " + ifndef.toString());
pw.println();
pw.println("#include ResourceGenTask
is an ANT task to invoke the Eigenbase
* Resource Generator.
*
*
*
*
generates<resgen srcdir="source" locales="en_US">
* <include name="happy/BirthdayResource.xml"/>
*</resgen>
*
*
*
*
*
* source/happy/BirthdayResource.properties
*source/happy/BirthdayResource_en_US.properties
*source/happy/BirthdayResource.java
*source/happy/BirthdayResource_en_US.java
*
*
*
*
generates<resgen mode="c++" srcdir="source" locales="en_US">
* <include name="happy/BirthdayResource.xml"/>
*</resgen>
*
*
*
*
*
* source/happy/BirthdayResource.resources
*source/happy/BirthdayResource_en_US.resources
*source/happy/BirthdayResource.h
*source/happy/BirthdayResource.cpp
Element <resourceGen>
*
*
*
*
* Nested element: <{@link Include include}>.
*
* @author jhyde
* @since Oct 8, 2002
* @version $Id: //open/util/resgen/src/org/eigenbase/resgen/ResourceGenTask.java#7 $
**/
public class ResourceGenTask extends Task
{
private ArrayList resources = new ArrayList();
int mode = MODE_JAVA;
File src;
File dest;
File res;
int style = STYLE_DYNAMIC;
String locales;
boolean force;
int commentStyle = COMMENT_STYLE_NORMAL;
private static final int MODE_UNKNOWN = -1;
private static final int MODE_JAVA = 1;
private static final int MODE_CPP = 2;
private static final int MODE_ALL = 3;
public static final int STYLE_DYNAMIC = 1;
public static final int STYLE_FUNCTOR = 2;
public static final int COMMENT_STYLE_NORMAL = 1;
public static final int COMMENT_STYLE_SCM_SAFE = 2;
public ResourceGenTask()
{
}
public void execute() throws BuildException
{
validate();
try {
new ResourceGen().run(this);
} catch (IOException e) {
throw new BuildException(e);
}
}
/** Called by ANT. **/
public void addInclude(Include resourceArgs)
{
resources.add(resourceArgs);
resourceArgs.root = this;
}
void validate()
{
if (mode != MODE_JAVA && mode != MODE_CPP && mode != MODE_ALL) {
throw new BuildException("You must specify a value mode: java, c++, or all");
}
if (src == null) {
throw new BuildException("You must specify 'srcdir'");
}
if (dest == null) {
dest = src;
}
if (res == null) {
res = dest;
}
final Include[] args = getIncludes();
for (int i = 0; i < args.length; i++) {
args[i].validate();
}
}
Include[] getIncludes()
{
return (Include[]) resources.toArray(new Include[0]);
}
/** Sets mode. **/
public void setMode(String mode)
throws BuildException
{
if ("java".equals(mode)) {
this.mode = MODE_JAVA;
} else if ("c++".equals(mode)) {
this.mode = MODE_CPP;
} else if ("all".equals(mode)) {
this.mode = MODE_ALL;
} else {
this.mode = MODE_UNKNOWN;
}
}
/** Sets srcdir. **/
public void setSrcdir(File srcDir)
{
this.src = srcDir;
}
/** Returns srcdir. **/
public File getSrcdir()
{
return src;
}
/** Sets destdir. **/
public void setDestdir(File destDir)
{
this.dest = destDir;
}
/** Returns destdir. **/
public File getDestdir()
{
return dest;
}
/** Sets resdir. **/
public void setResdir(File resDir)
{
this.res = resDir;
}
/** Sets style. */
public void setStyle(String style) throws BuildException
{
if (style.equals("dynamic")) {
this.style = STYLE_DYNAMIC;
} else if (style.equals("functor")) {
this.style = STYLE_FUNCTOR;
} else {
throw new BuildException("Invalid style '" + style + "'");
}
}
/** Sets locales. **/
public void setLocales(String locales) throws BuildException
{
this.locales = locales;
}
/** Sets force. **/
public void setForce(boolean force)
{
this.force = force;
}
/** Sets commentstyle. */
public void setCommentStyle(String commentStyle) throws BuildException
{
if (commentStyle.equals("normal")) {
this.commentStyle = COMMENT_STYLE_NORMAL;
} else if (commentStyle.equals("scm-safe")) {
this.commentStyle = COMMENT_STYLE_SCM_SAFE;
} else {
throw new BuildException(
"Invalid commentstyle '" + commentStyle + "'");
}
}
/**
*
*
*
* Attribute
* Description
* Required
*
*
*
* mode
* Generation mode. Acceptable values are "java", "c++" or "all".
* The default is "java".
* No
*
*
*
* srcdir
* Source directory. The paths of resource files, and hence the
* package names of generated Java classes, are relative to this
* directory.
* Yes
*
*
*
* destdir
* Destination directory. Output .java files are generated relative to this
* directory. If not specified, has the same value as
* srcdir.
* No
*
*
*
* resdir
* Resource directory. Output .properties files are generated relative to
* this directory. If not specified, has the same value as
* destdir.
* No
*
*
*
* locales
* Comma-separated list of locales to generate files for.
* If not specified, uses the locale of the resource file.
* No
*
*
*
* style
* Code-generation style. Values are "dynamic" or "functor".
* Default is "dynamic": generate several non-static methods for each
* resource.
* In the "functor" style, there is one member per resource, which has
* several methods.
* No
*
*
*
* force
* Whether to generate files even if they do not appear to be out of
* date. Default is false.
* No
*
* commentstyle
* Generated comment style. Values are "normal" and "scm-safe". The
* default is "normal": generates comments that indicate the source file's
* original path and states that the file should not be checked into source
* control systems. The "scm-safe" comment style modifies the comments
* to make storage of the output files in an SCM more palatable. It omits
* the source file's path and states that the file was generated and should
* not be edited manually.
* No
* Include
implements <include> element nested
* within a <resgen> task (see {@link ResourceGenTask}).
*
*
*
*/
public static class Include
{
public Include()
{
}
ResourceGenTask root;
/** Name of source file, relative to 'srcdir'. **/
String fileName;
/** Class name. **/
String className;
/** Base class. */
String baseClassName;
/** C++ Class name. **/
String cppClassName;
/** C++ Base class. */
String cppBaseClassName;
void validate() throws BuildException
{
if (fileName == null) {
throw new BuildException("You must specify attribute 'name'");
}
}
void process(ResourceGen generator) throws BuildException
{
boolean outputJava = (root.mode != ResourceGenTask.MODE_CPP);
boolean outputCpp = (root.mode != ResourceGenTask.MODE_JAVA);
FileTask task;
if (fileName.endsWith(".xml")) {
task = generator.createXmlTask(this, fileName,
className, baseClassName, outputJava,
cppClassName, cppBaseClassName,
outputCpp);
} else if (fileName.endsWith(".properties")) {
task = generator.createPropertiesTask(this, fileName);
} else {
throw new BuildException(
"File '" + fileName + "' is not of a supported " +
"type (.java or .properties)");
}
try {
task.process(generator);
} catch (IOException e) {
e.printStackTrace();
throw new BuildException(
"Failed while processing '" + fileName + "'", e);
}
}
/** Sets name. **/
public void setName(String name)
{
this.fileName = name;
}
/** Sets className. **/
public void setClassName(String className)
{
this.className = className;
}
/** Sets baseClassName. **/
public void setBaseClassName(String baseClassName)
{
this.baseClassName = baseClassName;
}
String getBaseClassName()
{
return baseClassName;
}
/** Sets cppClassName. **/
public void setCppClassName(String className)
{
this.cppClassName = className;
}
/** Sets cppBaseClassName. **/
public void setCppBaseClassName(String baseClassName)
{
this.cppBaseClassName = baseClassName;
}
String getCppBaseClassName()
{
return cppBaseClassName;
}
}
}
// End ResourceGenTask.java
resgen/src/org/eigenbase/resgen/XmlFileTask.java 0000444 0001750 0001750 00000035401 11543216467 022060 0 ustar drazzib drazzib /*
// $Id: //open/util/resgen/src/org/eigenbase/resgen/XmlFileTask.java#12 $
// Package org.eigenbase.resgen is an i18n resource generator.
// Copyright (C) 2005-2008 The Eigenbase Project
// Copyright (C) 2005-2008 Disruptive Tech
// Copyright (C) 2005-2008 LucidEra, Inc.
// Portions Copyright (C) 2001-2005 Kana Software, Inc. and others.
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published by the
// Free Software Foundation; either version 2 of the License, or (at your
// option) any later version approved by The Eigenbase Project.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.eigenbase.resgen;
import org.apache.tools.ant.BuildException;
import java.io.*;
import java.net.URL;
import java.util.*;
/**
* Ant task which processes an XML file and generates a C++ or Java class from
* the resources in it.
*
* @author jhyde
* @since 19 September, 2005
* @version $Id: //open/util/resgen/src/org/eigenbase/resgen/XmlFileTask.java#12 $
*/
class XmlFileTask extends FileTask
{
final String baseClassName;
final String cppBaseClassName;
XmlFileTask(ResourceGenTask.Include include, String fileName,
String className, String baseClassName, boolean outputJava,
String cppClassName, String cppBaseClassName, boolean outputCpp)
{
this.include = include;
this.fileName = fileName;
this.outputJava = outputJava;
if (className == null) {
className = Util.fileNameToClassName(fileName, ".xml");
}
this.className = className;
if (baseClassName == null) {
baseClassName = "org.eigenbase.resgen.ShadowResourceBundle";
}
this.baseClassName = baseClassName;
this.outputCpp = outputCpp;
if (cppClassName == null) {
cppClassName = Util.fileNameToCppClassName(fileName, ".xml");
}
this.cppClassName = cppClassName;
if (cppBaseClassName == null) {
cppBaseClassName = "ResourceBundle";
}
this.cppBaseClassName = cppBaseClassName;
}
void process(ResourceGen generator) throws IOException {
URL url = Util.convertPathToURL(getFile());
ResourceDef.ResourceBundle resourceList = Util.load(url);
if (resourceList.locale == null) {
throw new BuildException(
"Resource file " + url + " must have locale");
}
ArrayList localeNames = new ArrayList();
if (include.root.locales == null) {
localeNames.add(resourceList.locale);
} else {
StringTokenizer tokenizer = new StringTokenizer(include.root.locales,",");
while (tokenizer.hasMoreTokens()) {
String token = tokenizer.nextToken();
localeNames.add(token);
}
}
if (!localeNames.contains(resourceList.locale)) {
throw new BuildException(
"Resource file " + url + " has locale '" +
resourceList.locale +
"' which is not in the 'locales' list");
}
Locale[] locales = new Locale[localeNames.size()];
for (int i = 0; i < locales.length; i++) {
String localeName = (String) localeNames.get(i);
locales[i] = Util.parseLocale(localeName);
if (locales[i] == null) {
throw new BuildException(
"Invalid locale " + localeName);
}
}
if (outputJava) {
generateJava(generator, resourceList, null);
}
generateProperties(generator, resourceList, null);
for (int i = 0; i < locales.length; i++) {
Locale locale = locales[i];
if (outputJava) {
generateJava(generator, resourceList, locale);
}
generateProperties(generator, resourceList, locale);
}
if (outputCpp) {
generateCpp(generator, resourceList);
}
}
private void generateProperties(
ResourceGen generator,
ResourceDef.ResourceBundle resourceList,
Locale locale) {
String fileName = Util.getClassNameSansPackage(className, locale) + ".properties";
File file = new File(getResourceDirectory(), fileName);
File srcFile = locale == null ?
getFile() :
new File(getSrcDirectory(), fileName);
if (file.exists()) {
if (locale != null) {
if (file.equals(srcFile)) {
// The locale.properties file already exists, and the
// source and target locale.properties files are the
// same. No need to create it, or even to issue a warning.
// We were only going to create an empty file, anyway.
return;
}
}
if (file.lastModified() >= srcFile.lastModified()) {
generator.comment(file + " is up to date");
return;
}
if (!file.canWrite()) {
generator.comment(file + " is read-only");
return;
}
}
generator.comment("Generating " + file);
final FileOutputStream out;
try {
if (file.getParentFile() != null) {
file.getParentFile().mkdirs();
}
out = new FileOutputStream(file);
} catch (FileNotFoundException e) {
throw new BuildException("Error while writing " + file, e);
}
PrintWriter pw = new PrintWriter(out);
try {
if (locale == null) {
generateBaseProperties(resourceList, pw);
} else {
generateProperties(pw, file, srcFile, locale);
}
} finally {
pw.close();
}
}
/**
* Generates a properties file containing a line for each resource.
*/
private void generateBaseProperties(
ResourceDef.ResourceBundle resourceList,
PrintWriter pw)
{
String fullClassName = getClassName(null);
pw.println("# This file contains the resources for");
pw.println("# class '" + fullClassName + "'; the base locale is '" +
resourceList.locale + "'.");
pw.println("# It was generated by " + ResourceGen.class);
pw.println("# from " + getFileForComments());
if (include.root.commentStyle !=
ResourceGenTask.COMMENT_STYLE_SCM_SAFE)
{
pw.println("# on " + new Date().toString() + ".");
}
pw.println();
for (int i = 0; i < resourceList.resources.length; i++) {
ResourceDef.Resource resource = resourceList.resources[i];
final String name = resource.name;
if (resource.text == null) {
throw new BuildException(
"Resource '" + name + "' has no message");
}
final String message = resource.text.cdata;
if (message == null) {
continue;
}
if (count(resource.text.cdata, '\'') % 2 != 0) {
System.out.println(
"WARNING: The message for resource '" + resource.name
+ "' has an odd number of single-quotes. These should"
+ " probably be doubled (to include an single-quote in"
+ " a message) or closed (to include a literal string"
+ " in a message).");
}
pw.println(name + "=" + Util.quoteForProperties(message));
}
pw.println("# End " + fullClassName + ".properties");
}
/**
* Returns the number of occurrences of a given character in a string.
*
*
*
*
* Attribute
* Description
* Required
*
*
*
* name
* The name, relative to srcdir, of the XML file
* which defines the resources.
* Yes
*
*
* className
* The name of the class to be generated, including the package, but
* not including any locale suffix. By default, the class name is
* derived from the name of the source file, for example
*
* happy/BirthdayResource_en_US.xml
becomes class
* happy.BirthdayResource
.No
*
*
*
*
* cppClassName
* The name of the C++ class to be generated. By default, the class
* name is derived from the name of the source file, for example
*
* happy/BirthdayResource_en_US.xml
becomes class
* happy.BirthdayResource
.No
*
*
*
* baseClassName
* The fully-qualified name of the base class of the resource bundle.
* Defaults to "org.eigenbase.resgen.ShadowResourceBundle".
* No
*
*
*
* cppBaseClassName
* The fully-qualified name of the base class of the resource bundle
* for C++. Defaults to "ResourceBundle".
* No
* ShadowResourceBundle
is an abstract base class for
* {@link ResourceBundle} classes which are backed by a properties file. When
* the class is created, it loads a properties file with the same name as the
* class.
*
* {@link ResourceBundle#getBundle}("foo.MyResource")
,
* it first looks for a class called foo.MyResource
, then
* looks for a file called foo/MyResource.properties
. If it finds
* the file, it creates a {@link PropertyResourceBundle} and loads the class.
* The problem is if you want to load the .properties
file
* into a dedicated class; ShadowResourceBundle
helps with this
* case.
*
*
*
*
Then when you call
* {@link ResourceBundle#getBundle ResourceBundle.getBundle("foo.MyResource")},
* it will find the class before the properties file, but still automatically
* load the properties file based upon the name of the class.
*/
public abstract class ShadowResourceBundle extends ResourceBundle {
private PropertyResourceBundle bundle;
private static final ThreadLocal mapThreadToLocale = new ThreadLocal();
protected static final Object[] emptyObjectArray = new Object[0];
/**
* Creates a package foo;
*class MyResource extends org.eigenbase.resgen.ShadowResourceBundle {
* public MyResource() throws java.io.IOException {
* }
*}
*
* ShadowResourceBundle
, and reads resources from
* a .properties
file with the same name as the current class.
* For example, if the class is called foo.MyResource_en_US
,
* reads from foo/MyResource_en_US.properties
, then
* foo/MyResource_en.properties
, then
* foo/MyResource.properties
.
*/
protected ShadowResourceBundle() throws IOException {
super();
Class clazz = getClass();
InputStream stream = openPropertiesFile(clazz);
if (stream == null) {
throw new IOException("could not open properties file for " + getClass());
}
MyPropertyResourceBundle previousBundle =
new MyPropertyResourceBundle(stream);
bundle = previousBundle;
stream.close();
// Now load properties files for parent locales, which we deduce from
// the names of our super-class, and its super-class.
while (true) {
clazz = clazz.getSuperclass();
if (clazz == null ||
clazz == ShadowResourceBundle.class ||
!ResourceBundle.class.isAssignableFrom(clazz)) {
break;
}
stream = openPropertiesFile(clazz);
if (stream == null) {
continue;
}
MyPropertyResourceBundle newBundle =
new MyPropertyResourceBundle(stream);
stream.close();
if (previousBundle != null) {
previousBundle.setParentTrojan(newBundle);
} else {
bundle = newBundle;
}
previousBundle = newBundle;
}
}
static class MyPropertyResourceBundle extends PropertyResourceBundle {
public MyPropertyResourceBundle(InputStream stream) throws IOException {
super(stream);
}
void setParentTrojan(ResourceBundle parent) {
super.setParent(parent);
}
}
/**
* Opens the properties file corresponding to a given class. The code is
* copied from {@link ResourceBundle}.
*/
private static InputStream openPropertiesFile(Class clazz) {
final ClassLoader loader = clazz.getClassLoader();
final String resName = clazz.getName().replace('.', '/') + ".properties";
return (InputStream)java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction() {
public Object run() {
if (loader != null) {
return loader.getResourceAsStream(resName);
} else {
return ClassLoader.getSystemResourceAsStream(resName);
}
}
}
);
}
public Enumeration getKeys() {
return bundle.getKeys();
}
protected Object handleGetObject(String key)
throws MissingResourceException {
return bundle.getObject(key);
}
/**
* Returns the instance of the baseName
resource bundle for
* the current thread's locale. For example, if called with
* "mondrian.olap.MondrianResource", from a thread which has called {@link
* #setThreadLocale}({@link Locale#FRENCH}), will get an instance of
* "mondrian.olap.MondrianResource_FR" from the cache.
*
*
*
*
*
* @deprecated This method does not work correctly in dynamically
* loaded jars.
*/
protected static ResourceBundle instance(String baseName) {
return instance(baseName, getThreadLocale());
}
/**
* Returns the instance of the class MyResource extends ShadowResourceBundle {
* ...
* /**
* * Retrieves the instance of {@link MyResource} appropriate
* * to the current locale. If this thread has specified a locale
* * by calling {@link #setThreadLocale}, this locale is used,
* * otherwise the default locale is used.
* **/
* public static MyResource instance() {
* return (MyResource) instance(MyResource.class.getName());
* }
* ...
* }
baseName
resource bundle
* for the given locale.
*
*
*
*
*
* @deprecated This method does not work correctly in dynamically
* loaded jars.
*/
protected static ShadowResourceBundle instance(
String baseName, Locale locale) {
if (locale == null) {
locale = Locale.getDefault();
}
ResourceBundle bundle = ResourceBundle.getBundle(baseName, locale);
return instance(baseName, locale, bundle);
}
/**
* Returns the instance of the class MyResource extends ShadowResourceBundle {
* ...
*
* /**
* * Retrieves the instance of {@link MyResource} appropriate
* * to the given locale.
* **/
* public static MyResource instance(Locale locale) {
* return (MyResource) instance(MyResource.class.getName(), locale);
* }
* ...
* }
baseName
resource bundle
* for the given locale.
*
*
*
*
*/
protected static ShadowResourceBundle instance(
String baseName, Locale locale, ResourceBundle bundle)
{
if (bundle instanceof PropertyResourceBundle) {
throw new ClassCastException(
"ShadowResourceBundle.instance('" + baseName + "','" +
locale + "') found " +
baseName + "_" + locale + ".properties but not " +
baseName + "_" + locale + ".class");
}
return (ShadowResourceBundle) bundle;
}
/** Returns the preferred locale of the current thread, or
* the default locale if the current thread has not called {@link
* #setThreadLocale}. **/
protected static Locale getThreadOrDefaultLocale() {
Locale locale = getThreadLocale();
if (locale == null) {
return Locale.getDefault();
} else {
return locale;
}
}
/** Sets the locale for the current thread. Used by {@link
* #instance(String,Locale)}. **/
public static void setThreadLocale(Locale locale) {
mapThreadToLocale.set(locale);
}
/** Returns the preferred locale of the current thread, or null if the
* thread has not called {@link #setThreadLocale}. **/
public static Locale getThreadLocale() {
return (Locale) mapThreadToLocale.get();
}
}
// End ShadowResourceBundle.java
resgen/src/org/eigenbase/resgen/CppGenerator.java 0000444 0001750 0001750 00000022052 11543216467 022264 0 ustar drazzib drazzib /*
// $Id: //open/util/resgen/src/org/eigenbase/resgen/CppGenerator.java#3 $
// Package org.eigenbase.resgen is an i18n resource generator.
// Copyright (C) 2005-2005 The Eigenbase Project
// Copyright (C) 2005-2005 Disruptive Tech
// Copyright (C) 2005-2005 LucidEra, Inc.
// Portions Copyright (C) 2001-2005 Kana Software, Inc. and others.
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published by the
// Free Software Foundation; either version 2 of the License, or (at your
// option) any later version approved by The Eigenbase Project.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.eigenbase.resgen;
import java.io.File;
import java.io.PrintWriter;
/**
* Generates a C++ class containing resource definitions.
*
* @author jhyde
* @since 19 September, 2005
* @version $Id: //open/util/resgen/src/org/eigenbase/resgen/CppGenerator.java#3 $
*/
class CppGenerator extends AbstractGenerator
{
private final String className;
private final String defaultExceptionClassName;
private final String headerFilename;
private final String baseClassName;
private static final String CPP_STRING = "const std::string &";
private static final String CPP_NUMBER = "int";
private static final String CPP_DATE_TIME = "time_t";
private static final String[] CPP_TYPE_NAMES =
{CPP_STRING, CPP_NUMBER, CPP_DATE_TIME, CPP_DATE_TIME};
/**
* Creates a C++ header generator.
*
* @param srcFile
* @param file
* @param className
* @param baseClassName Name of base class, must not be null, typically
* @param defaultExceptionClassName
*/
CppGenerator(
File srcFile,
File file,
String className,
String baseClassName,
String defaultExceptionClassName,
String headerFilename)
{
super(srcFile, file);
assert className.indexOf('.') < 0 :
"C++ class name must not contain '.': " + className;
this.className = className;
this.defaultExceptionClassName = defaultExceptionClassName;
this.headerFilename = headerFilename;
assert baseClassName != null;
this.baseClassName = baseClassName;
}
protected String getClassName()
{
return className;
}
protected String getBaseClassName()
{
return baseClassName;
}
protected String[] getArgTypes(String message) {
return ResourceDefinition.getArgTypes(message, CPP_TYPE_NAMES);
}
public void generateModule(ResourceGen generator, ResourceDef.ResourceBundle resourceList, PrintWriter pw)
{
generateDoNotModifyHeader(pw);
generateGeneratedByBlock(pw);
String className = getClassName();
String bundleCacheClassName = className + "BundleCache";
String baseClass = getBaseClassName();
if (resourceList.cppCommonInclude != null) {
pw.println(
"// begin common include specified by "
+ getSrcFileForComment());
pw.println("#include \"" + resourceList.cppCommonInclude + "\"");
pw.println(
"// end common include specified by "
+ getSrcFileForComment());
}
pw.println("#include \"" + headerFilename + "\"");
pw.println("#include \"ResourceBundle.h\"");
pw.println("#include \"Locale.h\"");
pw.println();
pw.println("#include class MyResource extends ShadowResourceBundle {
* ...
*
* /**
* * Retrieves the instance of {@link MyResource} appropriate
* * to the given locale.
* **/
* public static MyResource instance(Locale locale) {
* return (MyResource) instance(
* MyResource.class.getName(), locale,
* ResourceBundle.getBundle(MyResource.class.getName(), locale));
* }
* ...
* }
This will create class MyResource
, with a
* function corresponding to each error message in
* MyResource_en.xml
.
See also the ANT Task, {@link ResourceGenTask}.
* * @author jhyde * @since 3 December, 2001 * @version $Id: //open/util/resgen/src/org/eigenbase/resgen/ResourceGen.java#7 $ */ public class ResourceGen { public static void main(String [] args) throws IOException { ResourceGenTask rootArgs = parse(args); new ResourceGen().run(rootArgs); } static ResourceGenTask parse(String[] args) { ResourceGenTask rootArgs = new ResourceGenTask(); for (int i = 0; i < args.length; i++) { String arg = args[i]; if (arg.equals("-mode") && i + 1 < args.length) { rootArgs.setMode(args[++i]); } else if (arg.equals("-srcdir") && i + 1 < args.length) { rootArgs.setSrcdir(new File(args[++i])); } else if (arg.equals("-destdir") && i + 1 < args.length) { rootArgs.setDestdir(new File(args[++i])); } else if (arg.equals("-resdir") && i + 1 < args.length) { rootArgs.setResdir(new File(args[++i])); } else if (arg.equals("-locales") && i + 1 < args.length) { rootArgs.setLocales(args[++i]); } else if (arg.equals("-style") && i + 1 < args.length) { rootArgs.setStyle(args[++i]); } else if (arg.equals("-force")) { rootArgs.setForce(true); } else if (arg.equals("-commentstyle")) { rootArgs.setCommentStyle(args[++i]); } else { ResourceGenTask.Include resourceArgs = new ResourceGenTask.Include(); rootArgs.addInclude(resourceArgs); resourceArgs.setName(arg); } } if (rootArgs.getIncludes().length == 0) { throw new java.lang.Error("No input file specified."); } if (rootArgs.getDestdir() == null) { rootArgs.setDestdir(rootArgs.getSrcdir()); } return rootArgs; } void run(ResourceGenTask rootArgs) throws IOException { rootArgs.validate(); final ResourceGenTask.Include[] includes = rootArgs.getIncludes(); for (int i = 0; i < includes.length; i++) { includes[i].process(this); } } /** * Prints a message to the output stream. */ void comment(String message) { System.out.println(message); } /** * Returns the name of the resource with the first letter capitalized, * suitable for use in method names. For example, "MyErrorMessage". */ static String getResourceInitcap(ResourceDef.Resource resource) { String name = resource.name; if (name.equals(name.toUpperCase())) { return "_" + name; } else { return name.substring(0,1).toUpperCase() + name.substring(1); } } /** * Returns any comment relating to the message. */ static String getComment(ResourceDef.Resource resource) { DOMWrapper[] children = resource.getDef().getChildren(); for (int i = 0; i < children.length; i++) { DOMWrapper child = children[i]; if (child.getType() == DOMWrapper.COMMENT) { return child.getText(); // first comment only } } return null; // no comment } FileTask createXmlTask( ResourceGenTask.Include include, String fileName, String className, String baseClassName, boolean outputJava, String cppClassName, String cppBaseClassName, boolean outputCpp) { return new XmlFileTask( include, fileName, className, baseClassName, outputJava, cppClassName, cppBaseClassName, outputCpp); } FileTask createPropertiesTask( ResourceGenTask.Include include, String fileName) { return new PropertiesFileTask(include, fileName); } } // End ResourceGen.java resgen/src/org/eigenbase/resgen/Generator.java 0000444 0001750 0001750 00000004016 11543216467 021621 0 ustar drazzib drazzib /* // $Id: //open/util/resgen/src/org/eigenbase/resgen/Generator.java#3 $ // Package org.eigenbase.resgen is an i18n resource generator. // Copyright (C) 2005-2005 The Eigenbase Project // Copyright (C) 2005-2005 Disruptive Tech // Copyright (C) 2005-2005 LucidEra, Inc. // Portions Copyright (C) 2001-2005 Kana Software, Inc. and others. // // This library is free software; you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License as published by the // Free Software Foundation; either version 2 of the License, or (at your // option) any later version approved by The Eigenbase Project. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package org.eigenbase.resgen; import java.io.PrintWriter; /** * A generator converts a set of resource definitions to a piece of code. * * @author jhyde * @since 19 September, 2005 * @version $Id: //open/util/resgen/src/org/eigenbase/resgen/Generator.java#3 $ */ interface Generator { /** * Configures whether this generator will output comments that may be * submitted to a source code management system. In general, it * squelches comments indicating the file should not be checked in as * well as comments change with each generation of the file (thereby * avoiding merge conflicts). * * @param enabled */ void setScmSafeComments(boolean enabled); /** * Generates a class containing a line for each resource. */ void generateModule( ResourceGen generator, ResourceDef.ResourceBundle resourceList, PrintWriter pw); } // End Generator.java resgen/src/org/eigenbase/resgen/JavaBaseGenerator.java 0000444 0001750 0001750 00000026402 11543216467 023221 0 ustar drazzib drazzib /* // $Id: //open/util/resgen/src/org/eigenbase/resgen/JavaBaseGenerator.java#4 $ // Package org.eigenbase.resgen is an i18n resource generator. // Copyright (C) 2005-2005 The Eigenbase Project // Copyright (C) 2005-2005 Disruptive Tech // Copyright (C) 2005-2005 LucidEra, Inc. // Portions Copyright (C) 2001-2005 Kana Software, Inc. and others. // // This library is free software; you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License as published by the // Free Software Foundation; either version 2 of the License, or (at your // option) any later version approved by The Eigenbase Project. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package org.eigenbase.resgen; import org.apache.tools.ant.BuildException; import java.io.PrintWriter; import java.io.File; import java.lang.reflect.Constructor; import java.util.HashSet; import java.util.Set; /** * Generates a Java class for the base locale. * * @author jhyde * @since 19 September, 2005 * @version $Id: //open/util/resgen/src/org/eigenbase/resgen/JavaBaseGenerator.java#4 $ */ class JavaBaseGenerator extends AbstractJavaGenerator { protected final Set warnedClasses = new HashSet(); JavaBaseGenerator( File srcFile, File file, String className, String baseClassName, ResourceDef.ResourceBundle resourceBundle) { super(srcFile, file, className, resourceBundle, baseClassName); } public void generateModule( ResourceGen generator, ResourceDef.ResourceBundle resourceList, PrintWriter pw) { generateHeader(pw); String className = getClassName(); final String classNameSansPackage = Util.removePackage(className); pw.print("public class " + classNameSansPackage); final String baseClass = getBaseClassName(); if (baseClass != null) { pw.print(" extends " + baseClass); } pw.println(" {"); pw.println(" public " + classNameSansPackage + "() throws IOException {"); pw.println(" }"); pw.println(" private static final String baseName = " + Util.quoteForJava(getClassName()) + ";"); pw.println(" /**"); pw.println(" * Retrieves the singleton instance of {@link " + classNameSansPackage + "}. If"); pw.println(" * the application has called {@link #setThreadLocale}, returns the"); pw.println(" * resource for the thread's locale."); pw.println(" */"); pw.println(" public static synchronized " + classNameSansPackage + " instance() {"); pw.println(" return (" + classNameSansPackage + ") instance(baseName, getThreadOrDefaultLocale(), ResourceBundle.getBundle(baseName, getThreadOrDefaultLocale()));"); pw.println(" }"); pw.println(" /**"); pw.println(" * Retrieves the instance of {@link " + classNameSansPackage + "} for the given locale."); pw.println(" */"); pw.println(" public static synchronized " + classNameSansPackage + " instance(Locale locale) {"); pw.println(" return (" + classNameSansPackage + ") instance(baseName, locale, ResourceBundle.getBundle(baseName, locale));"); pw.println(" }"); if (resourceList.code != null) { pw.println(" // begin of included code"); pw.print(resourceList.code.cdata); pw.println(" // end of included code"); } for (int j = 0; j < resourceList.resources.length; j++) { ResourceDef.Resource resource = resourceList.resources[j]; generateResource(resource, pw); } pw.println(""); postModule(pw); pw.println("}"); } protected void postModule(PrintWriter pw) { } public void generateResource(ResourceDef.Resource resource, PrintWriter pw) { if (resource.text == null) { throw new BuildException( "Resource '" + resource.name + "' has no message"); } String text = resource.text.cdata; String comment = ResourceGen.getComment(resource); final String resourceInitcap = ResourceGen.getResourceInitcap(resource);// e.g. "Internal" String definitionClass = "org.eigenbase.resgen.ResourceDefinition"; String parameterList = getParameterList(text); String argumentList = getArgumentList(text); // e.g. "p0, p1" String argumentArray = argumentList.equals("") ? "emptyObjectArray" : "new Object[] {" + argumentList + "}"; // e.g. "new Object[] {p0, p1}" pw.println(); Util.generateCommentBlock(pw, resource.name, text, comment); pw.println(" public static final " + definitionClass + " " + resourceInitcap + " = new " + definitionClass + "(\"" + resourceInitcap + "\", " + Util.quoteForJava(text) + ");"); pw.println(" public String get" + resourceInitcap + "(" + parameterList + ") {"); pw.println(" return " + resourceInitcap + ".instantiate(" + addLists("this", argumentArray) + ").toString();"); pw.println(" }"); if (resource instanceof ResourceDef.Exception) { ResourceDef.Exception exception = (ResourceDef.Exception) resource; String errorClassName = getErrorClass(exception); final ExceptionDescription ed = new ExceptionDescription(errorClassName); if (ed.hasInstCon) { pw.println(" public " + errorClassName + " new" + resourceInitcap + "(" + parameterList + ") {"); pw.println(" return new " + errorClassName + "(" + resourceInitcap + ".instantiate(" + addLists("this", argumentArray) + "));"); pw.println(" }"); } else if (ed.hasInstThrowCon) { pw.println(" public " + errorClassName + " new" + resourceInitcap + "(" + parameterList + ") {"); pw.println(" return new " + errorClassName + "(" + resourceInitcap + ".instantiate(" + addLists("this", argumentArray) + "), null);"); pw.println(" }"); } else if (ed.hasStringCon) { pw.println(" public " + errorClassName + " new" + resourceInitcap + "(" + parameterList + ") {"); pw.println(" return new " + errorClassName + "(get" + resourceInitcap + "(" + argumentList + "));"); pw.println(" }"); } else if (ed.hasStringThrowCon) { pw.println(" public " + errorClassName + " new" + resourceInitcap + "(" + parameterList + ") {"); pw.println(" return new " + errorClassName + "(get" + resourceInitcap + "(" + argumentList + "), null);"); pw.println(" }"); } if (ed.hasInstThrowCon) { pw.println(" public " + errorClassName + " new" + resourceInitcap + "(" + addLists(parameterList, "Throwable err") + ") {"); pw.println(" return new " + errorClassName + "(" + resourceInitcap + ".instantiate(" + addLists("this", argumentArray) + "), err);"); pw.println(" }"); } else if (ed.hasStringThrowCon) { pw.println(" public " + errorClassName + " new" + resourceInitcap + "(" + addLists(parameterList, "Throwable err") + ") {"); pw.println(" return new " + errorClassName + "(get" + resourceInitcap + "(" + argumentList + "), err);"); pw.println(" }"); } } } /** * Description of the constructs that an exception class has. */ class ExceptionDescription { boolean hasInstCon; boolean hasInstThrowCon; boolean hasStringCon; boolean hasStringThrowCon; /** * Figures out what constructors the exception class has. We'd * prefer to use *init(ResourceDefinition rd)
or
* init(ResourceDefinition rd, Throwable e)
* if it has them, but we can use
* init(String s)
and
* init(String s, Throwable e)
* as a fall-back.
*
* Prints a warming message if the class cannot be loaded.
*
* @param errorClassName Name of exception class
*/
ExceptionDescription(String errorClassName)
{
hasInstCon = false;
hasInstThrowCon = false;
hasStringCon = false;
hasStringThrowCon = false;
try {
Class errorClass;
try {
errorClass = Class.forName(errorClassName);
} catch (ClassNotFoundException e) {
// Might be in the java.lang package, for which we
// allow them to omit the package name.
errorClass = Class.forName("java.lang." + errorClassName);
}
Constructor[] constructors = errorClass.getConstructors();
for (int i = 0; i < constructors.length; i++) {
Constructor constructor = constructors[i];
Class[] types = constructor.getParameterTypes();
if (types.length == 1 &&
ResourceInstance.class.isAssignableFrom(types[0])) {
hasInstCon = true;
}
if (types.length == 1 &&
String.class.isAssignableFrom(types[0])) {
hasStringCon = true;
}
if (types.length == 2 &&
ResourceInstance.class.isAssignableFrom(types[0]) &&
Throwable.class.isAssignableFrom(types[1])) {
hasInstThrowCon = true;
}
if (types.length == 2 &&
String.class.isAssignableFrom(types[0]) &&
Throwable.class.isAssignableFrom(types[1])) {
hasStringThrowCon = true;
}
}
} catch (ClassNotFoundException e) {
if (warnedClasses.add(errorClassName)) {
System.out.println("Warning: Could not find exception " +
"class '" + errorClassName + "' on classpath. " +
"Exception factory methods will not be generated.");
}
}
}
}
// helper
protected static String addLists(String x, String y) {
if (x == null || x.equals("")) {
if (y == null || y.equals("")) {
return "";
} else {
return y;
}
} else if (y == null || y.equals("")) {
return x;
} else {
return x + ", " + y;
}
}
protected static String addLists(String x, String y, String z) {
return addLists(x, addLists(y, z));
}
}
// End JavaBaseGenerator.java
resgen/src/org/eigenbase/xom/ 0000755 0001750 0001750 00000000000 11543216467 016351 5 ustar drazzib drazzib resgen/src/org/eigenbase/xom/StringEscaper.java 0000444 0001750 0001750 00000015524 11543216467 021772 0 ustar drazzib drazzib /*
// $Id: //open/util/resgen/src/org/eigenbase/xom/StringEscaper.java#6 $
// Package org.eigenbase.xom is an XML Object Mapper.
// Copyright (C) 2005-2005 The Eigenbase Project
// Copyright (C) 2005-2005 Disruptive Tech
// Copyright (C) 2005-2005 LucidEra, Inc.
// Portions Copyright (C) 2000-2005 Kana Software, Inc. and others.
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published by the
// Free Software Foundation; either version 2 of the License, or (at your
// option) any later version approved by The Eigenbase Project.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.eigenbase.xom;
import java.util.*;
/**
* StringEscaper
is a utility for replacing special characters
* with escape sequences in strings. Initially, a StringEscaper starts out as
* an identity transform in the "mutable" state. Call defineEscape as many
* times as necessary to set up mappings, and then call makeImmutable() before
* using appendEscapedString to actually apply the defined transform. Or, use one of
* the global mappings pre-defined here.
NodeDef
represents a node in a parse tree. It is a base class
* for {@link ElementDef}, {@link TextDef}, etc.
*
* @author jhyde
* @since 11 October, 2001
* @version $Id: //open/util/resgen/src/org/eigenbase/xom/NodeDef.java#4 $
**/
public interface NodeDef {
/**
* Returns the name of this node's tag.
**/
String getName();
/**
* Returns the type of this element.
* Values are as for {@link DOMWrapper#getType}.
*/
int getType();
/**
* Returns the text inside this node.
**/
String getText();
/**
* Returns the children of this node.
**/
NodeDef[] getChildren();
/**
* Outputs this element definition in XML to any XMLOutput.
* @param out the XMLOutput class to display the XML
**/
void displayXML(XMLOutput out, int indent);
/**
* Outputs this node to any PrintWriter,
* in a formatted fashion with automatic indenting.
* @param out the PrintWriter to which to write this NodeDef.
* @param indent the indentation level for the printout.
*/
void display(PrintWriter out, int indent);
/**
* Retrieves the {@link DOMWrapper} which was used to create this
* node. Only works if this nodes's {@link MetaDef.Element#keepDef} was
* true (or, if it is not set, if the default
* {@link MetaDef.Model#defaultKeepDef} is true);
* otherwise, returns null
.
*
* @return wrapper underlying this node
*/
DOMWrapper getWrapper();
/**
* Returns the location of this element in its document.
*
* @return location of this element, or null if location is not available
*/
Location getLocation();
}
// End NodeDef.java
resgen/src/org/eigenbase/xom/ParserTester.java 0000444 0001750 0001750 00000012575 11543216467 021647 0 ustar drazzib drazzib /*
// $Id: //open/util/resgen/src/org/eigenbase/xom/ParserTester.java#3 $
// Package org.eigenbase.xom is an XML Object Mapper.
// Copyright (C) 2005-2005 The Eigenbase Project
// Copyright (C) 2005-2005 Disruptive Tech
// Copyright (C) 2005-2005 LucidEra, Inc.
// Portions Copyright (C) 2000-2005 Kana Software, Inc. and others.
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published by the
// Free Software Foundation; either version 2 of the License, or (at your
// option) any later version approved by The Eigenbase Project.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// KLO, 22 July, 2001
*/
package org.eigenbase.xom;
import java.io.*;
/**
* Test the MSParser
*/
public class ParserTester {
// parser determines the type of parser to use. Currently we support
// MSXML and XERCES.
private int parserType;
private static final int MSXML = 1;
private static final int XERCES = 2;
// These members contain the actual parsers to use. Only one will
// ever be set.
private Parser parser;
// This member contain the model document type
String modelDocType;
// This member contain the URL for the DTD file
String dtdUrl;
//Read the XML file
public ParserTester(String dtdFile,
int parserType)
throws XOMException, IOException
{
this.parserType = parserType;
parser = null;
File dtdPath = new File(dtdFile);
this.modelDocType = dtdFile.substring(0, dtdFile.indexOf("."));
this.dtdUrl = "file:" + dtdPath.getAbsolutePath();
switch (parserType) {
// case MSXML:
// parser = org.eigenbase.xom.wrappers.MSXMLWrapper.createParser(
// modelDocType, dtdPath);
// break;
case XERCES:
parser = new org.eigenbase.xom.wrappers.XercesDOMParser(true);
break;
default:
throw new XOMException("Unknown parser type: " + parserType);
}
}
public void testFile(String testFile)
throws XOMException
{
// parsing directly from an input stream (rather than a reader).
String xmlString = null;
try {
StringWriter sWriter = new StringWriter();
FileReader reader = new FileReader(testFile);
if(parserType != MSXML) {
PrintWriter out = new PrintWriter(sWriter);
out.println("");
if(modelDocType != null)
out.println("");
out.flush();
}
readerToWriter(reader, sWriter);
reader.close();
xmlString = sWriter.toString();
} catch (IOException ex) {
throw new XOMException("Unable to read input test "
+ testFile + ": " + ex.getMessage());
}
parser.parse(xmlString);
System.out.println("Parsing document succeeded.");
}
/**
* Helper function to copy from a reader to a writer
*/
private static void readerToWriter(Reader reader, Writer writer)
throws IOException
{
int numChars;
final int bufferSize = 16384;
char[] buffer = new char[bufferSize];
while((numChars = reader.read(buffer)) != -1) {
if(numChars > 0)
writer.write(buffer, 0, numChars);
}
}
/**
* The ParserTester tests MSXML parser and Xerces Parser. Arguments:
* TODO jvs 15-Mar-2005: XOM is nice, but there's no longer a lot of reason for it to exist now that we have JAXB to do the same thing without any tricky bootstrapping issues. So we should phase out XOM.
Revision | $Id: //open/util/resgen/src/org/eigenbase/xom/package.html#2 $ |
---|---|
Copyright | Copyright (C) 2005-2005 The Eigenbase Project |
Copyright | Copyright (C) 2005-2005 Disruptive Tech |
Copyright | Copyright (C) 2005-2005 Red Square, Inc. |
Copyright | Portions Copyright (C) 2001-2005 Kana Software, Inc. |
Author | Dan Sommerfield, Julian Hyde |
The schema is defined in an XML file, whose format is somewhat similar to an
XML Schema. The (self-describing) meta-schema is meta.xml
(
as you can see, an XSL style-sheet formats each schema into a javadoc-like web
page).
Other schemas include resource.xml
.
The utilities in this package enable conversion of XML (stored in a DOM-style model) into a typesafe Java representation. Roughly, conversion occurs as follows:
Element
becomes a Java Class. The Java Class name matches the
Element name. All classes representing Elements descend from
{@link org.eigenbase.xom.ElementDef}. All classes from the same model are
implemented as static inner classes of a single enclosure class.
By convention, enclosure classes always end in "Def".Attribute
becomes a public member of its enclosing Element class.PCDATA
) content becomes a single public String called cdata
.ANY
content becomes a single array of {@link org.eigenbase.xom.ElementDef}
called children
.
Converting an XML Document to its XOM representation is a two-step process. First, you parse the XML into a DOM representation using your favorite W3C DOM Level 1-compliant parser (note: MSXML is supported as well for backward compatibility). Then, you instantiate the XOM {@link org.eigenbase.xom.ElementDef} subclass corresponding to the root element in the document, passing a portion of the DOM as input to the constructor. After this, the fully-constructed root element provides complete typesafe access to the whole document!
Specific instructions for parsing using a DOM-compliant parser (such as XERCES):
Specific instructions for parsing using Microsoft's XML Parser:
There is now an ANT target, XOMGen
, implemented by {@link org.eigenbase.xom.XOMGenTask}.
There is another helpful utility we use to verify that the stuff we generate works. It is class {@link MetaTester}, and you invoke it as follows:
All the arguments are the same as the ones for {@link org.eigenbase.xom.MetaGenerator}, except for tests. <tests> is a list of test .xml files that should be valid according to the generated DTD. The tester will validate the java files against the DTD and also against the java class. It also runs some basic checks on the java class to make sure that it works.jview <classpath_options> org.eigenbase.xom.MetaTester [-debug] [-msxml | -xerces] <XML model file> <output dir> [<tests>...]
meta.xml
, we generate corresponding DTD and NameDef.java
at build time. {@link MetaDef} is tricky, because it
depends upon itself; use the ant all target to rebuild it.
<Entity>
, all <Attribute>
s
must occur before all <Object>
, <Array>
or <CData>
elements.This package is dependent upon the following other packages:
Class {@link XOMGenTask} only is dependent upon {@link org.apache.tools.ant}. You therefore require ant.jar
to build, but not to run.
Class {@link org.eigenbase.xom.wrappers.XercesDOMParser} only is dependent upon {@link org.xml.sax} and {@link org.apache.xerces.parsers.DOMParser}. You therefore require xerces.jar
to build, but not to run.
resgen/src/org/eigenbase/xom/Parser.java 0000444 0001750 0001750 00000006134 11543216467 020452 0 ustar drazzib drazzib /*
// $Id: //open/util/resgen/src/org/eigenbase/xom/Parser.java#4 $
// Package org.eigenbase.xom is an XML Object Mapper.
// Copyright (C) 2005-2005 The Eigenbase Project
// Copyright (C) 2005-2005 Disruptive Tech
// Copyright (C) 2005-2005 LucidEra, Inc.
// Portions Copyright (C) 2001-2005 Kana Software, Inc. and others.
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published by the
// Free Software Foundation; either version 2 of the License, or (at your
// option) any later version approved by The Eigenbase Project.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// jhyde, 2 August, 2001
*/
package org.eigenbase.xom;
import java.io.InputStream;
import java.io.Reader;
import java.net.URL;
/**
* The Parser
interface abstracts the behavior which the
* org.eigenbase.xom
package needs from an XML parser.
*
*
If you don't care which implementation you get, call {@link * XOMUtil#createDefaultParser} to create a parser.
* * @author jhyde * @since 2 August, 2001 * @version $Id: //open/util/resgen/src/org/eigenbase/xom/Parser.java#4 $ **/ public interface Parser { /** * Sets whether to retain position information. * @param keepPositions Whether to keep position information. */ void setKeepPositions(boolean keepPositions); /** * Returns whether the parser is retaining position information. * * @return Whether to keep position information. */ boolean isKeepPositions(); /** * Parses a string and returns a wrapped element. * * @param sXml XML string * @return Wrapped element * @throws XOMException on error */ DOMWrapper parse(String sXml) throws XOMException; /** * Parses an input stream and returns a wrapped element. * * @param is Input stream * @return Wrapped element * @throws XOMException on error */ DOMWrapper parse(InputStream is) throws XOMException; /** * Parses the contents of a URL and returns a wrapped element. * * @param url URL * @return Wrapped element * @throws XOMException on error */ DOMWrapper parse(URL url) throws XOMException; /** * Parses the contents of a reader and returns a wrapped element. * * @param reader Reader * @return Wrapped element * @throws XOMException on error */ DOMWrapper parse(Reader reader) throws XOMException; /** * Creates a wrapper representing an XML element. * * @param tagName Name of element * @return Wrapper element */ DOMWrapper create(String tagName); } // End Parser.java resgen/src/org/eigenbase/xom/CommentDef.java 0000444 0001750 0001750 00000004027 11543216467 021236 0 ustar drazzib drazzib /* // $Id: //open/util/resgen/src/org/eigenbase/xom/CommentDef.java#3 $ // Package org.eigenbase.xom is an XML Object Mapper. // Copyright (C) 2005-2005 The Eigenbase Project // Copyright (C) 2005-2005 Disruptive Tech // Copyright (C) 2005-2005 LucidEra, Inc. // Portions Copyright (C) 2000-2005 Kana Software, Inc. and others. // // This library is free software; you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License as published by the // Free Software Foundation; either version 2 of the License, or (at your // option) any later version approved by The Eigenbase Project. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // jhyde, 11 October, 2001 */ package org.eigenbase.xom; import java.io.PrintWriter; /** * todo: * * @author jhyde * @since 11 October, 2001 * @version $Id: //open/util/resgen/src/org/eigenbase/xom/CommentDef.java#3 $ **/ public class CommentDef extends TextDef { public CommentDef() { super(); } public CommentDef(String s) { super(s); } public CommentDef(DOMWrapper _def) throws XOMException { super(_def); } // override ElementDef public int getType() { return DOMWrapper.COMMENT; } // implement NodeDef public void display(PrintWriter pw, int indent) { pw.print(""); } // implement NodeDef public void displayXML(XMLOutput out, int indent) { out.beginNode(); out.print(""); } } // End CommentDef.java resgen/src/org/eigenbase/xom/Any.java 0000444 0001750 0001750 00000002635 11543216467 017747 0 ustar drazzib drazzib /* // $Id: //open/util/resgen/src/org/eigenbase/xom/Any.java#3 $ // Package org.eigenbase.xom is an XML Object Mapper. // Copyright (C) 2005-2005 The Eigenbase Project // Copyright (C) 2005-2005 Disruptive Tech // Copyright (C) 2005-2005 LucidEra, Inc. // Portions Copyright (C) 2001-2005 Kana Software, Inc. and others. // // This library is free software; you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License as published by the // Free Software Foundation; either version 2 of the License, or (at your // option) any later version approved by The Eigenbase Project. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // jhyde, 31 October, 2001 */ package org.eigenbase.xom; /** * An element which has 'Any' content. * * @author jhyde * @since 31 October, 2001 * @version $Id: //open/util/resgen/src/org/eigenbase/xom/Any.java#3 $ **/ public interface Any { NodeDef[] getChildren(); void setChildren(NodeDef[] children); } // End Any.java resgen/src/org/eigenbase/xom/DOMElementParser.java 0000444 0001750 0001750 00000111741 11543216467 022325 0 ustar drazzib drazzib /* // $Id: //open/util/resgen/src/org/eigenbase/xom/DOMElementParser.java#6 $ // Package org.eigenbase.xom is an XML Object Mapper. // Copyright (C) 2005-2005 The Eigenbase Project // Copyright (C) 2005-2005 Disruptive Tech // Copyright (C) 2005-2005 LucidEra, Inc. // Portions Copyright (C) 2000-2005 Kana Software, Inc. and others. // // This library is free software; you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License as published by the // Free Software Foundation; either version 2 of the License, or (at your // option) any later version approved by The Eigenbase Project. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // dsommerfield, 6 November, 2000 */ package org.eigenbase.xom; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Vector; /** * DOMElementParser is a utility wrapper around DOMWrapper. * Implements a parseable stream of child DOMWrappers and also provides * validation on an XML document beyond the DTD. */ public class DOMElementParser { private DOMWrapper wrapper; private DOMWrapper[] children; private int currentIndex; private DOMWrapper currentChild; private int optionIndex; private String prefix; private Class enclosure; /** * Constructs a new ElementParser based on an Element of the XML parse * tree wrapped in a DOMWrapper, and a prefix (to be applied to all element * tags except the root), and the name of the enclosing class. * @param wrapper a DOMWrapper representing the section of the XML parse tree * to traverse. */ public DOMElementParser(DOMWrapper wrapper, String prefix, Class enclosure) throws XOMException { this.wrapper = wrapper; children = wrapper.getElementChildren(); currentIndex = 0; currentChild = null; getNextElement(); this.prefix = prefix; if (prefix == null) { this.prefix = ""; } this.enclosure = enclosure; } /** * Private helper function to retrieve the next child element in sequence. * @return the next element, or null if the enumerator has no more * elements to return. */ private void getNextElement() { if (currentIndex >= children.length) { currentChild = null; } else { currentChild = children[currentIndex++]; } } /** * Private helper function to verify that the next element matches a * specific name. * @param name name of the element to match. Names are not case-sensitive. * @throws XOMException if there is no current element or the names do * not match. */ private void requiredName(String name) throws XOMException { String augName = prefix + name; if (currentChild == null) { throw new XOMException( "Expected <" + augName + "> but found " + "nothing."); } else if (!augName.equalsIgnoreCase(currentChild.getTagName())) { throw new XOMException( "Expected <" + augName + "> but found <" + currentChild.getTagName() + ">"); } } /** * Private helper function to determine if the next element has the * specified name. * @return true if the next element's name matches name. Matching * is not case-sensitive. Returns false if there is no next element or * if the names don't match. */ private boolean optionalName(String name) { String augName = prefix + name; if (currentChild == null) { return false; } else if (augName.equalsIgnoreCase(currentChild.getTagName())) { return true; } else { return false; } } /** * Returns the enclosure class associated with clazz, or falls back on * the fixed enclosure if none can be found. */ private Class getEnclosureClass(Class clazz) { // Instead of using a fixed enclosure, derive it from the given Class. // If we can't figure it out, just use the given enclosure instead. Class thisEnclosure = enclosure; String className = clazz.getName(); int dollarPos = className.indexOf('$'); if (dollarPos >= 0) { String encName = className.substring(0, dollarPos); try { thisEnclosure = Class.forName(encName); } catch (ClassNotFoundException ex) { throw new AssertFailure("Enclosure class " + encName + " not found."); } } return thisEnclosure; } /** * Private helper function to determine if the next element's corresponding * definition class is a subclass of the given class. This may be used * to detect if a name matches a class. * @param clazz the class to match the next element against. * @return true if the next element's name matches the given class, false * otherwise. * @throws XOMException if the next name is invalid (either doesn't * start with DM or has no associated definition class). */ private boolean nameMatchesClass(Class clazz) throws XOMException { // Get the next name. It must start with the set prefix, and it must // match a definition in the enclosure class. Class thisEnclosure = getEnclosureClass(clazz); Class nextClass = ElementDef.getElementClass(currentChild, thisEnclosure, prefix); // Determine if nextClass is a subclass of clazz. Return true if so. return nextClass != null && clazz.isAssignableFrom(nextClass); } /** * This function retrieves a required String element from this parser, * advancing the parser after the read. * @param elementName the name of the element to retrieve. * @return the String value stored inside the element to retrieve. * @throws XOMException if there is no element with the given name. */ public String requiredString(String elementName) throws XOMException { requiredName(elementName); String retval = currentChild.getText().trim(); getNextElement(); return retval; } /** * This function retrieves an optional String element from this parser, * advancing the parser if the element is found. * If no element of the correct name is found, this function returns null. * @param elementName the name of the element to retrieve. * @return the String value stored inside the element to retrieve. */ public String optionalString(String elementName) throws XOMException { if (optionalName(elementName)) { String retval = currentChild.getText().trim(); getNextElement(); return retval; } else { return null; } } /** * This function retrieves a required Element from this parser, * advancing the parser after the read. * @param elementName the name of the element to retrieve. * @return the DOMWrapper to retrieve. * @throws XOMException if there is no element with the given name. */ public DOMWrapper requiredElement(String elementName) throws XOMException { requiredName(elementName); DOMWrapper prevWrapper = currentChild; getNextElement(); return prevWrapper; } /** * This function is used to return a CDATA section as text. It does * no parsing. * @return the contents of the CDATA element as text. */ public String getText() { return wrapper.getText().trim(); } /** * This function retrieves an optional Element from this parser, * advancing the parser if the element is found. * If no element of the correct name is found, this function returns null. * @param elementName the name of the element to retrieve. * @return the DOMWrapper to retreive, or null if none found. */ public DOMWrapper optionalElement(String elementName) throws XOMException { if (optionalName(elementName)) { DOMWrapper prevChild = currentChild; getNextElement(); return prevChild; } else { return null; } } /** * This private helper function formats a list of element names into * a readable string for error messages. */ private String formatOption(String[] elementNames) { StringBuffer sbuf = new StringBuffer(); for (int i = 0; i < elementNames.length; i++) { sbuf.append("Some text
. FREETEXT
* elements always have a tag name of NULL and have no children. It
* maps to a {@link TextDef}.
**/
public static final int FREETEXT = 1;
/**
* ELEMENT is a type of DOM Element representing a named tag, possibly
* containing attributes, child elements, and text. It maps to a {@link
* ElementDef} (or a generated class derived from it), or a {@link
* GenericDef}.
**/
public static final int ELEMENT = 2;
/**
* COMMENT is a type of DOM Element representing an XML comment. It maps
* to a {@link CommentDef}.
**/
public static final int COMMENT = 3;
/**
* CDATA is a type of DOM Element representing a piece of text embedded in
* a CDATA section, for example,
* <![CDATA[Some text]]>
.
* CDATA elements always have a tag name of NULL and have no children. It
* maps to a {@link CdataDef}.
**/
public static final int CDATA = 4;
/**
* Returns the type of this element/node. DOMWrapper supports only four
* possibilities: {@link #FREETEXT}, {@link #ELEMENT}, {@link #COMMENT},
* {@link #CDATA}.
*/
public int getType();
/**
* Returns the tag name of this element, or null for TEXT elements.
*/
public String getTagName();
/**
* Returns the value of the attribute with the given attrName. If the
* attribute is not defined, this method returns null.
*/
public String getAttribute(String attrName);
/**
* Returns a list of attribute names.
**/
public String[] getAttributeNames();
/**
* Returns a flattened representation of the text inside thie element.
* For a TEXT element, this returns the text itself. For an ELEMENT
* element, this returns all pieces of text within the element,
* with all markup removed.
*/
public String getText();
/**
* Returns this node serialized as XML.
**/
public String toXML();
/**
* Returns all children of this element, including TEXT elements, as
* an array of DOMWrappers.
*/
public DOMWrapper[] getChildren();
/**
* Returns all element children of this element as an array of
* DOMWrappers.
*/
public DOMWrapper[] getElementChildren();
/**
* Returns the location of this element.
*/
public Location getLocation();
}
// End DOMWrapper.java
resgen/src/org/eigenbase/xom/XMLAttrVector.java 0000444 0001750 0001750 00000011307 11543216467 021672 0 ustar drazzib drazzib /*
// $Id: //open/util/resgen/src/org/eigenbase/xom/XMLAttrVector.java#3 $
// Package org.eigenbase.xom is an XML Object Mapper.
// Copyright (C) 2005-2005 The Eigenbase Project
// Copyright (C) 2005-2005 Disruptive Tech
// Copyright (C) 2005-2005 LucidEra, Inc.
// Portions Copyright (C) 2000-2005 Kana Software, Inc. and others.
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published by the
// Free Software Foundation; either version 2 of the License, or (at your
// option) any later version approved by The Eigenbase Project.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// dsommerfield, 12 December, 2000
*/
package org.eigenbase.xom;
import java.io.PrintWriter;
import java.util.Vector;
/**
* XMLAttrVector is an class which assists in writing XML attributes to a
* stream.
*/
public class XMLAttrVector {
// Vector to hold all attributes and their values.
private Vector attrs;
/**
* This private helper class holds an attribute-value pair. It is used
* as the element of the vector attrs.
*/
private static class AttrVal
{
public AttrVal(String attr, String val)
{
this.attr = attr;
this.val = val;
}
public String attr;
public String val;
}
/**
* Construct an empty XMLAttrVector. Attribute/value pairs may be added
* with the add() functions below.
*/
public XMLAttrVector()
{
attrs = new Vector();
}
/**
* Returns the number of attributes.
**/
public int size()
{
return attrs.size();
}
/**
* Add a new attribute/value pair based on a String value. Note that
* attrVal may be null, in which case no attribute/value pair is added.
* @param attrName the name of the attribute.
* @param attrVal the String value of the attribute.
* @return this (to allow chaining)
*/
public XMLAttrVector add(String attrName, Object attrVal)
{
if(attrVal != null)
attrs.addElement(new AttrVal(attrName, attrVal.toString()));
return this;
}
/**
* Add a new attribute/value pair based on an int value.
* @param attrName the name of the attribute.
* @param attrVal the int value of the attribute.
* @return this (to allow chaining)
*/
public XMLAttrVector add(String attrName, int attrVal)
{
attrs.addElement(new AttrVal(attrName, ""+attrVal));
return this;
}
/**
* Add a new attribute/value pair based on a double value.
* @param attrName the name of the attribute.
* @param attrVal the double value of the attribute.
* @return this (to allow chaining)
*/
public XMLAttrVector add(String attrName, double attrVal)
{
attrs.addElement(new AttrVal(attrName, ""+attrVal));
return this;
}
/**
* Add a new attribute/value pair based on a boolean value.
* True is represented as "true", and false as "false".
* @param attrName the name of the attribute.
* @param attrVal the boolean value of the attribute.
* @return this (to allow chaining)
*/
public XMLAttrVector add(String attrName, boolean attrVal)
{
if(attrVal)
attrs.addElement(new AttrVal(attrName, "true"));
else
attrs.addElement(new AttrVal(attrName, "false"));
return this;
}
/**
* Displays the entire attribute/value pair list, given a PrintWriter
* to which to display and an indentation level.
* This function is typically called from XMLOutput.
* @param out PrintWriter to which to write output.
* @param indent indentation level.
*/
public void display(PrintWriter out, int indent)
{
// The indentation level is not used; all attribute/value pairs
// are rendered on the same line.
for(int i=0; i