Additional infrastructure for generating source code outside of a
GWT generator.
Review at http://gwt-code-reviews.appspot.com/1421805
Review by: unnurg@google.com
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@10114 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/codegen/rebind/GwtCodeGenContext.java b/user/src/com/google/gwt/codegen/rebind/GwtCodeGenContext.java
new file mode 100644
index 0000000..7c04f43
--- /dev/null
+++ b/user/src/com/google/gwt/codegen/rebind/GwtCodeGenContext.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.codegen.rebind;
+
+import com.google.gwt.codegen.server.AbortablePrintWriter;
+import com.google.gwt.codegen.server.CodeGenContext;
+import com.google.gwt.codegen.server.JavaSourceWriterBuilder;
+import com.google.gwt.core.ext.GeneratorContext;
+import com.google.gwt.core.ext.TreeLogger;
+
+import java.io.PrintWriter;
+
+/**
+ * {@link CodeGenContext} implementation for use within a GWT
+ * {@link com.google.gwt.core.ext.Generator}.
+ * <p>
+ * Experimental API - subject to change.
+ */
+public class GwtCodeGenContext implements CodeGenContext {
+
+ private final TreeLogger logger;
+ private final GeneratorContext ctx;
+
+ /**
+ * @param logger
+ * @param ctx
+ */
+ public GwtCodeGenContext(TreeLogger logger, GeneratorContext ctx) {
+ this.logger = logger;
+ this.ctx = ctx;
+ }
+
+ public JavaSourceWriterBuilder addClass(String pkgName, String className) {
+ return addClass(null, pkgName, className);
+ }
+
+ public JavaSourceWriterBuilder addClass(String superPath, String pkgName, String className) {
+ String superPkg = superPath == null ? pkgName : superPath + "." + pkgName;
+ final PrintWriter pw = ctx.tryCreate(logger, superPkg, className);
+ if (pw == null) {
+ return null;
+ }
+ return new JavaSourceWriterBuilder(new AbortablePrintWriter(pw) {
+ @Override
+ protected void onClose(boolean aborted) {
+ if (!aborted) {
+ ctx.commit(logger, pw);
+ }
+ }
+ }, pkgName, className);
+ }
+
+ public void error(String msg) {
+ logger.log(TreeLogger.ERROR, msg);
+ }
+
+ public void error(String msg, Throwable cause) {
+ logger.log(TreeLogger.ERROR, msg, cause);
+ }
+
+ public void error(Throwable cause) {
+ logger.log(TreeLogger.ERROR, cause.getMessage(), cause);
+ }
+
+ public void warn(String msg) {
+ logger.log(TreeLogger.WARN, msg);
+ }
+
+ public void warn(String msg, Throwable cause) {
+ logger.log(TreeLogger.WARN, msg, cause);
+ }
+
+ public void warn(Throwable cause) {
+ logger.log(TreeLogger.WARN, cause.getMessage(), cause);
+ }
+}
diff --git a/user/src/com/google/gwt/codegen/server/AbortablePrintWriter.java b/user/src/com/google/gwt/codegen/server/AbortablePrintWriter.java
new file mode 100644
index 0000000..112e9e2
--- /dev/null
+++ b/user/src/com/google/gwt/codegen/server/AbortablePrintWriter.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.codegen.server;
+
+import java.io.PrintWriter;
+
+/**
+ * Wrapper for a {@link PrintWriter} that adds the ability to abort creation
+ * and an onClose hook
+ * <p>
+ * Experimental API - subject to change.
+ */
+public class AbortablePrintWriter extends PrintWriter {
+
+ private boolean isClosed = false;
+
+ /**
+ * Wrap a {@link PrintWriter} instance.
+ *
+ * @param pw
+ * @throws RuntimeException if there are reflection errors accessing the out
+ * field in pw
+ */
+ public AbortablePrintWriter(PrintWriter pw) {
+ super(pw);
+ }
+
+ /**
+ * Abort creation of this output.
+ */
+ public void abort() {
+ if (!isClosed) {
+ flush();
+ super.close();
+ isClosed = true;
+ onClose(true);
+ }
+ }
+
+ @Override
+ public void close() {
+ if (!isClosed) {
+ flush();
+ super.close();
+ isClosed = true;
+ onClose(false);
+ }
+ }
+
+ /**
+ * Called exactly once when this {@link PrintWriter} is closed or aborted.
+ *
+ * @param aborted
+ */
+ protected void onClose(boolean aborted) {
+ // Do nothing by default.
+ }
+}
diff --git a/user/src/com/google/gwt/codegen/server/CodeGenContext.java b/user/src/com/google/gwt/codegen/server/CodeGenContext.java
new file mode 100644
index 0000000..705ff97
--- /dev/null
+++ b/user/src/com/google/gwt/codegen/server/CodeGenContext.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.codegen.server;
+
+/**
+ * Context for code generators.
+ * <p>
+ * Experimental API - subject to change.
+ */
+public interface CodeGenContext {
+
+ /**
+ * An exception which can be thrown by a code generator to abort - callers of
+ * code generators should catch this exception.
+ */
+ class AbortCodeGenException extends RuntimeException {
+ public AbortCodeGenException() {
+ super();
+ }
+
+ public AbortCodeGenException(String msg) {
+ super(msg);
+ }
+
+ public AbortCodeGenException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+
+ public AbortCodeGenException(Throwable cause) {
+ super(cause);
+ }
+ }
+
+ /**
+ * Begin generating a new class.
+ *
+ * @param pkgName
+ * @param className
+ * @return a {@link JavaSourceWriterBuilder} for the requested class or null if it
+ * could not be created, such as if it already exists
+ */
+ JavaSourceWriterBuilder addClass(String pkgName, String className);
+
+ /**
+ * Begin generating a new class, possibly using GWT super-source.
+ *
+ * @param superPath super-source prefix, or null if a regular class
+ * @param pkgName
+ * @param className
+ * @return a {@link JavaSourceWriterBuilder} for the requested class or null if it
+ * could not be created, such as if it already exists
+ */
+ JavaSourceWriterBuilder addClass(String superPath, String pkgName, String className);
+
+ /**
+ * Log a fatal error during code generation.
+ *
+ * @param msg
+ */
+ void error(String msg);
+
+ /**
+ * Log a fatal error during code generation.
+ *
+ * @param msg
+ * @param cause
+ */
+ void error(String msg, Throwable cause);
+
+ /**
+ * Log a fatal error during code generation.
+ *
+ * @param cause
+ */
+ void error(Throwable cause);
+
+ /**
+ * Log a non-fatal warning during code generation.
+ *
+ * @param msg
+ */
+ void warn(String msg);
+
+ /**
+ * Log a non-fatal warning during code generation.
+ *
+ * @param msg
+ * @param cause
+ */
+ void warn(String msg, Throwable cause);
+
+ /**
+ * Log a non-fatal warning during code generation.
+ *
+ * @param cause
+ */
+ void warn(Throwable cause);
+}
diff --git a/user/src/com/google/gwt/codegen/server/CodeGenUtils.java b/user/src/com/google/gwt/codegen/server/CodeGenUtils.java
new file mode 100644
index 0000000..bc9107e
--- /dev/null
+++ b/user/src/com/google/gwt/codegen/server/CodeGenUtils.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.codegen.server;
+
+/**
+ * Helper class for code generators.
+ */
+public class CodeGenUtils {
+
+ /**
+ * Produce a string which represents a string literal in Java source,
+ * including null values.
+ *
+ * @param literal
+ * @return Java source representation of the string literal
+ */
+ public static String asStringLiteral(String literal) {
+ return literal == null ? "null" : '"' + escape(literal) + '"';
+ }
+
+ /**
+ * Escapes string content to be a valid string literal.
+ *
+ * @param unescaped
+ * @return an escaped version of <code>unescaped</code>, suitable for being
+ * enclosed in double quotes in Java source
+ */
+ public static String escape(String unescaped) {
+ int extra = 0;
+ for (int in = 0, n = unescaped.length(); in < n; ++in) {
+ switch (unescaped.charAt(in)) {
+ case '\0':
+ case '\n':
+ case '\r':
+ case '\"':
+ case '\\':
+ ++extra;
+ break;
+ }
+ }
+
+ if (extra == 0) {
+ return unescaped;
+ }
+
+ char[] oldChars = unescaped.toCharArray();
+ char[] newChars = new char[oldChars.length + extra];
+ for (int in = 0, out = 0, n = oldChars.length; in < n; ++in, ++out) {
+ char c = oldChars[in];
+ switch (c) {
+ case '\0':
+ newChars[out++] = '\\';
+ c = '0';
+ break;
+ case '\n':
+ newChars[out++] = '\\';
+ c = 'n';
+ break;
+ case '\r':
+ newChars[out++] = '\\';
+ c = 'r';
+ break;
+ case '\"':
+ newChars[out++] = '\\';
+ c = '"';
+ break;
+ case '\\':
+ newChars[out++] = '\\';
+ c = '\\';
+ break;
+ }
+ newChars[out] = c;
+ }
+
+ return String.valueOf(newChars);
+ }
+
+ private CodeGenUtils() {
+ }
+}
diff --git a/user/src/com/google/gwt/codegen/server/JavaSourceWriter.java b/user/src/com/google/gwt/codegen/server/JavaSourceWriter.java
new file mode 100644
index 0000000..0d91400
--- /dev/null
+++ b/user/src/com/google/gwt/codegen/server/JavaSourceWriter.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.codegen.server;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * A mechanism to write Java source files.
+ *
+ * @see JavaSourceWriterBuilder
+ * <p>
+ * Experimental API - subject to change.
+ */
+public class JavaSourceWriter extends SourceWriterBase {
+
+ private static final Pattern PKG_REGEX_BOTH = Pattern.compile("(com\\.google|javax?)\\..*");
+ private static final Pattern PKG_REGEX_GOOGLE = Pattern.compile("com\\.google\\..*");
+ private static final Pattern PKG_REGEX_JAVA = Pattern.compile("javax?\\..*");
+
+ private final AbortablePrintWriter printWriter;
+
+ /**
+ * @param printWriter
+ * @param targetPackageName
+ * @param imports
+ * @param isClass
+ * @param classJavaDocComment
+ * @param annotationDeclarations
+ * @param targetClassShortName
+ * @param superClassName
+ * @param interfaceNames
+ */
+ public JavaSourceWriter(AbortablePrintWriter printWriter, String targetPackageName,
+ Iterable<String> imports, boolean isClass, String classJavaDocComment,
+ Iterable<String> annotationDeclarations, String targetClassShortName, String superClassName,
+ Iterable<String> interfaceNames) {
+ this.printWriter = printWriter;
+ if (targetPackageName == null) {
+ throw new IllegalArgumentException("Cannot supply a null package name to"
+ + targetClassShortName);
+ }
+ // TODO: support a user-specified file header
+ if (targetPackageName.length() > 0) {
+ println("package " + targetPackageName + ";");
+ }
+
+ // Write imports, splitting into com.google, other, and java/javax groups
+ writeImportGroup(imports, PKG_REGEX_GOOGLE, true);
+ writeImportGroup(imports, PKG_REGEX_BOTH, false);
+ writeImportGroup(imports, PKG_REGEX_JAVA, true);
+
+ // Write class header
+ if (classJavaDocComment != null) {
+ beginJavaDocComment();
+ print(classJavaDocComment);
+ endJavaDocComment();
+ } else {
+ // beginJavaDocComment adds its own leading newline, make up for it here.
+ println();
+ }
+ for (String annotation : annotationDeclarations) {
+ println('@' + annotation);
+ }
+ if (isClass) {
+ emitClassDecl(targetClassShortName, superClassName, interfaceNames);
+ } else {
+ emitInterfaceDecl(targetClassShortName, superClassName, interfaceNames);
+ }
+ println(" {");
+ indent();
+ }
+
+ @Override
+ public void abort() {
+ printWriter.abort();
+ }
+
+ @Override
+ public void close() {
+ super.close();
+ printWriter.close();
+ }
+
+ @Override
+ protected void writeString(String s) {
+ printWriter.print(s);
+ }
+
+ private void emitClassDecl(String targetClassShortName,
+ String superClassName, Iterable<String> interfaceNames) {
+ print("public class " + targetClassShortName);
+ if (superClassName != null) {
+ print(" extends " + superClassName);
+ }
+ boolean first = true;
+ for (String interfaceName : interfaceNames) {
+ if (first) {
+ print(" implements ");
+ first = false;
+ } else {
+ print(", ");
+ }
+ print(interfaceName);
+ }
+ }
+
+ private void emitInterfaceDecl(String targetClassShortName,
+ String superClassName, Iterable<String> interfaceNames) {
+ if (superClassName != null) {
+ throw new IllegalArgumentException("Cannot set superclass name "
+ + superClassName + " on a interface.");
+ }
+ print("public interface " + targetClassShortName);
+ boolean first = true;
+ for (String interfaceName : interfaceNames) {
+ if (first) {
+ print(" extends ");
+ first = false;
+ } else {
+ print(", ");
+ }
+ print(interfaceName);
+ }
+ }
+
+ /**
+ * Write a group of imports matching or not matching a regex.
+ *
+ * @param imports
+ * @param regex
+ * @param includeMatches true to include imports matching the regex, false to
+ * include only those that don't match
+ */
+ private void writeImportGroup(Iterable<String> imports, Pattern regex, boolean includeMatches) {
+ boolean firstOfGroup = true;
+ for (String importEntry : imports) {
+ Matcher matcher = regex.matcher(importEntry);
+ if (matcher.matches() == includeMatches) {
+ if (firstOfGroup) {
+ println();
+ firstOfGroup = false;
+ }
+ println("import " + importEntry + ";");
+ }
+ }
+ }
+}
diff --git a/user/src/com/google/gwt/codegen/server/JavaSourceWriterBuilder.java b/user/src/com/google/gwt/codegen/server/JavaSourceWriterBuilder.java
new file mode 100644
index 0000000..eb99420
--- /dev/null
+++ b/user/src/com/google/gwt/codegen/server/JavaSourceWriterBuilder.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.codegen.server;
+
+import java.util.ArrayList;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+
+/**
+ * A builder for {@link JavaSourceWriter} instances.
+ * <p>
+ * Experimental API - subject to change.
+ */
+public class JavaSourceWriterBuilder {
+
+ private final String className;
+ private final String packageName;
+ private final AbortablePrintWriter printWriter;
+
+ private final List<String> annotations = new ArrayList<String>();
+
+ private boolean isClass = true;
+
+ private String classComment;
+
+ private final Set<String> imports = new TreeSet<String>();
+
+ private final Set<String> interfaceNames = new LinkedHashSet<String>();
+
+ private String superClassName;
+
+ /**
+ * @param printWriter
+ * @param packageName
+ * @param className
+ */
+ public JavaSourceWriterBuilder(AbortablePrintWriter printWriter, String packageName,
+ String className) {
+ this.printWriter = printWriter;
+ this.packageName = packageName;
+ this.className = className;
+ }
+
+ /**
+ * Add an class/interface annotation.
+ *
+ * @param declaration
+ */
+ public void addAnnotationDeclaration(String declaration) {
+ annotations.add(declaration);
+ }
+
+ /**
+ * Add an implemented/extended interface.
+ *
+ * @param intfName
+ */
+ public void addImplementedInterface(String intfName) {
+ interfaceNames.add(intfName);
+ }
+
+ /**
+ * Add an import entry.
+ *
+ * @param typeName fully-qualified source name
+ */
+ public void addImport(String typeName) {
+ imports.add(typeName);
+ }
+
+ /**
+ * Creates an implementation of {@link JavaSourceWriter} that can be used to write
+ * the innards of a class. Note that the subsequent changes to this factory do
+ * not affect the returned instance.
+ *
+ * @return a {@link JavaSourceWriter} instance
+ * @throws RuntimeException If the settings on this factory are inconsistent
+ * or invalid
+ */
+ public SourceWriter createSourceWriter() {
+ return new JavaSourceWriter(printWriter, packageName, imports, isClass, classComment,
+ annotations, className, superClassName, interfaceNames);
+ }
+
+ /**
+ * Get the annotations.
+ *
+ * @return list of annotations
+ */
+ public Iterable<String> getAnnotationDeclarations() {
+ return annotations;
+ }
+
+ /**
+ * Get the simple name of the class being created.
+ *
+ * @return class name
+ */
+ public String getClassName() {
+ return className;
+ }
+
+ /**
+ * Get the fully-qualified source name of the class being created.
+ *
+ * @return fqcn
+ */
+ public String getFullyQualifiedClassName() {
+ return getPackageName() + "." + getClassName();
+ }
+
+ /**
+ * Get the implemented/extended interfaces for the class being created.
+ *
+ * @return list of interface names
+ */
+ public Iterable<String> getInterfaceNames() {
+ return interfaceNames;
+ }
+
+ /**
+ * Get the package of the class being created.
+ *
+ * @return package name
+ */
+ public String getPackageName() {
+ return packageName;
+ }
+
+ /**
+ * Get the superclass for the class being created.
+ *
+ * @return superclass name
+ */
+ public String getSuperclassName() {
+ return superClassName;
+ }
+
+ /**
+ * We are creating an interface instead of a class.
+ */
+ public void makeInterface() {
+ isClass = false;
+ }
+
+ /**
+ * Sets the java doc comment for <code>this</code>.
+ *
+ * @param comment java doc comment.
+ */
+ public void setJavaDocCommentForClass(String comment) {
+ classComment = comment;
+ }
+
+ /**
+ * Set the superclass of the class being created.
+ *
+ * @param superclassName
+ */
+ public void setSuperclass(String superclassName) {
+ superClassName = superclassName;
+ }
+}
diff --git a/user/src/com/google/gwt/codegen/server/LoggingCodeGenContext.java b/user/src/com/google/gwt/codegen/server/LoggingCodeGenContext.java
new file mode 100644
index 0000000..5980195
--- /dev/null
+++ b/user/src/com/google/gwt/codegen/server/LoggingCodeGenContext.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.codegen.server;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Base implementation of {@link CodeGenContext} which logs via
+ * {@link java.util.logging.Logger}.
+ * <p>
+ * Experimental API - subject to change.
+ */
+public abstract class LoggingCodeGenContext implements CodeGenContext {
+
+ private final Logger logger;
+
+ protected LoggingCodeGenContext() {
+ this(Logger.getAnonymousLogger());
+ }
+
+ protected LoggingCodeGenContext(String loggerName) {
+ this(Logger.getLogger(loggerName));
+ }
+
+ protected LoggingCodeGenContext(Logger logger) {
+ this.logger = logger;
+ }
+
+ public JavaSourceWriterBuilder addClass(String pkgName, String className) {
+ return addClass(null, pkgName, className);
+ }
+
+ public abstract JavaSourceWriterBuilder addClass(String superPkg, String pkgName,
+ String className);
+
+ public void error(String msg) {
+ logger.log(Level.SEVERE, msg);
+ }
+
+ public void error(String msg, Throwable cause) {
+ logger.log(Level.SEVERE, msg, cause);
+ }
+
+ public void error(Throwable cause) {
+ logger.log(Level.SEVERE, cause.getMessage(), cause);
+ }
+
+ public void warn(String msg) {
+ logger.log(Level.WARNING, msg);
+ }
+
+ public void warn(String msg, Throwable cause) {
+ logger.log(Level.WARNING, msg);
+ }
+
+ public void warn(Throwable cause) {
+ logger.log(Level.WARNING, cause.getMessage(), cause);
+ }
+}
diff --git a/user/src/com/google/gwt/codegen/server/SourceWriter.java b/user/src/com/google/gwt/codegen/server/SourceWriter.java
new file mode 100644
index 0000000..681bc0d
--- /dev/null
+++ b/user/src/com/google/gwt/codegen/server/SourceWriter.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.codegen.server;
+
+/**
+ * A mechanism to write source files.
+ *
+ * @see JavaSourceWriterBuilder
+ * <p>
+ * Experimental API - subject to change.
+ */
+public interface SourceWriter {
+
+ /**
+ * Abort the source file being generated.
+ */
+ void abort();
+
+ /**
+ * Begin emitting a JavaDoc comment.
+ */
+ void beginJavaDocComment();
+
+ /**
+ * Close the source file being generated.
+ */
+ void close();
+
+ /**
+ * End emitting a JavaDoc comment.
+ */
+ void endJavaDocComment();
+
+ /**
+ * Increase indent level.
+ */
+ void indent();
+
+ /**
+ * Print a line at an increased indentation level without altering the indent
+ * level for the next line.
+ *
+ * @param string
+ */
+ void indentln(String string);
+
+ /**
+ * Format and print a line at an increased indentation level without altering
+ * the indent level for the next line.
+ *
+ * @param format format string, as in {@link String#format(String, Object...)}
+ * @param args arguments for the format string
+ */
+ void indentln(String format, Object... args);
+
+ /**
+ * Decrease indent level.
+ */
+ void outdent();
+
+ /**
+ * Write a string without a line terminator.
+ *
+ * @param s
+ */
+ void print(String s);
+
+ /**
+ * Format and print a string without a line terminator.
+ *
+ * @param format format string, as in {@link String#format(String, Object...)}
+ * @param args arguments for the format string
+ */
+ void print(String format, Object... args);
+
+ /**
+ * Write a line terminator.
+ */
+ void println();
+
+ /**
+ * Write a string with a line terminator.
+ *
+ * @param string
+ */
+ void println(String string);
+
+ /**
+ * Format and print a string with a line terminator.
+ *
+ * @param format format string, as in {@link String#format(String, Object...)}
+ * @param args arguments for the format string
+ */
+ void println(String format, Object... args);
+}
diff --git a/user/src/com/google/gwt/codegen/server/SourceWriterBase.java b/user/src/com/google/gwt/codegen/server/SourceWriterBase.java
new file mode 100644
index 0000000..d5c9def
--- /dev/null
+++ b/user/src/com/google/gwt/codegen/server/SourceWriterBase.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.codegen.server;
+
+/**
+ * Base implementation of {@link SourceWriter} that implements all the indenting
+ * and keeping track of comments.
+ * <p>
+ * Experimental API - subject to change.
+ */
+public abstract class SourceWriterBase implements SourceWriter {
+
+ private boolean atStart;
+
+ /**
+ * Are we currently in a comment?
+ */
+ private boolean inComment;
+
+ private int indent;
+
+ public abstract void abort();
+
+ public void beginJavaDocComment() {
+ println("\n/**");
+ inComment = true;
+ }
+
+ public void close() {
+ outdent();
+ println("}");
+ }
+
+ public void endJavaDocComment() {
+ inComment = false;
+ println("\n */");
+ }
+
+ public void indent() {
+ indent++;
+ }
+
+ public void indentln(String string) {
+ indent();
+ println(string);
+ outdent();
+ }
+
+ public void indentln(String format, Object... args) {
+ indentln(String.format(format, args));
+ }
+
+ public void outdent() {
+ if (indent > 0) {
+ --indent;
+ }
+ }
+
+ public void print(String s) {
+ // If we just printed a newline, print an indent.
+ //
+ if (atStart) {
+ for (int j = 0; j < indent; ++j) {
+ writeString(" ");
+ }
+ if (inComment) {
+ writeString(" * ");
+ }
+ atStart = false;
+ }
+ // Now print up to the end of the string or the next newline.
+ //
+ String rest = null;
+ int i = s.indexOf("\n");
+ if (i > -1 && i < s.length() - 1) {
+ rest = s.substring(i + 1);
+ s = s.substring(0, i + 1);
+ }
+ writeString(s);
+ // If rest is non-null, then s ended with a newline and we recurse.
+ //
+ if (rest != null) {
+ atStart = true;
+ print(rest);
+ }
+ }
+
+ public void print(String format, Object... args) {
+ print(String.format(format, args));
+ }
+
+ public void println() {
+ print("\n");
+ atStart = true;
+ }
+
+ public void println(String string) {
+ print(string);
+ println();
+ }
+
+ public void println(String format, Object... args) {
+ println(String.format(format, args));
+ }
+
+ /**
+ * Write a string to the underlying output.
+ *
+ * @param s
+ */
+ protected abstract void writeString(String s);
+}
diff --git a/user/src/com/google/gwt/codegen/server/StringSourceWriter.java b/user/src/com/google/gwt/codegen/server/StringSourceWriter.java
new file mode 100644
index 0000000..67092d2
--- /dev/null
+++ b/user/src/com/google/gwt/codegen/server/StringSourceWriter.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.codegen.server;
+
+/**
+ * A SourceWriter that accumulates source and returns it in the
+ * {@link #toString()} method.
+ * <p>
+ * Experimental API - subject to change.
+ */
+public class StringSourceWriter extends SourceWriterBase {
+
+ private final StringBuilder buf = new StringBuilder();
+ private boolean aborted = false;
+
+ @Override
+ public void abort() {
+ aborted = true;
+ }
+
+ @Override
+ public String toString() {
+ return aborted ? null : buf.toString();
+ }
+
+ @Override
+ protected void writeString(String s) {
+ buf.append(s);
+ }
+}