First step of isolating a bunch of code that is used for generating
code, both within a GWT generator or as a separate build step for
building server-side implementations.

Also, this improves StringGenerator to produce String-valued
expressions like those before SafeHtml was added.

Public review at http://gwt-code-reviews.appspot.com/1422807


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@10060 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/tools/cldr-import/src/com/google/gwt/tools/cldr/DateTimeFormatInfoProcessor.java b/tools/cldr-import/src/com/google/gwt/tools/cldr/DateTimeFormatInfoProcessor.java
index 2ca504b..c15ac79 100644
--- a/tools/cldr-import/src/com/google/gwt/tools/cldr/DateTimeFormatInfoProcessor.java
+++ b/tools/cldr-import/src/com/google/gwt/tools/cldr/DateTimeFormatInfoProcessor.java
@@ -15,12 +15,12 @@
  */
 package com.google.gwt.tools.cldr;
 
+import com.google.gwt.codegen.server.StringGenerator;
 import com.google.gwt.core.ext.UnableToCompleteException;
 import com.google.gwt.i18n.client.DateTimeFormatInfo;
 import com.google.gwt.i18n.client.impl.cldr.DateTimeFormatInfoImpl;
 import com.google.gwt.i18n.rebind.DateTimePatternGenerator;
 import com.google.gwt.i18n.rebind.MessageFormatParser;
-import com.google.gwt.i18n.rebind.StringGenerator;
 import com.google.gwt.i18n.rebind.MessageFormatParser.ArgumentChunk;
 import com.google.gwt.i18n.rebind.MessageFormatParser.DefaultTemplateChunkVisitor;
 import com.google.gwt.i18n.rebind.MessageFormatParser.StringChunk;
@@ -201,8 +201,8 @@
         prefix = ", ";
       }
       pw.println(") {");
-      final StringBuffer buf = new StringBuffer();
-      final StringGenerator gen = new StringGenerator(buf, false);
+      final StringBuilder buf = new StringBuilder();
+      final StringGenerator gen = StringGenerator.create(buf, false);
       try {
         List<TemplateChunk> chunks = MessageFormatParser.parse(value);
         for (TemplateChunk chunk : chunks) {
diff --git a/user/src/com/google/gwt/codegen/rebind/package-info.java b/user/src/com/google/gwt/codegen/rebind/package-info.java
new file mode 100644
index 0000000..ec93b96
--- /dev/null
+++ b/user/src/com/google/gwt/codegen/rebind/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+/**
+ * Classes used by code generation, specific to GWT generators.
+ */
+@com.google.gwt.util.PreventSpuriousRebuilds
+package com.google.gwt.codegen.rebind;
diff --git a/user/src/com/google/gwt/codegen/server/PlainStringGenerator.java b/user/src/com/google/gwt/codegen/server/PlainStringGenerator.java
new file mode 100644
index 0000000..80b2675
--- /dev/null
+++ b/user/src/com/google/gwt/codegen/server/PlainStringGenerator.java
@@ -0,0 +1,68 @@
+/*
+ * 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 to produce string expressions consisting of literals and
+ * computed values.
+ */
+class PlainStringGenerator extends StringGenerator {
+
+  private boolean firstExpression = true;
+
+  /**
+   * Initialize the StringGenerator with an output buffer.
+   *
+   * @param buf output buffer
+   */
+  PlainStringGenerator(StringBuilder buf) {
+    super(buf);
+  }
+
+  @Override
+  protected void afterExpression(Type type) {
+    firstExpression = false;
+  }
+
+  @Override
+  protected void beforeExpression(Type type) {
+    if (firstExpression) {
+      if (type == Type.PRIMITIVE) {
+        buf.append("\"\" + ");
+      }
+    } else {
+      buf.append(" + ");
+    }
+  }
+
+  @Override
+  protected void finishOutput() {
+    if (firstExpression) {
+      buf.append("\"\"");
+    }
+  }
+
+  @Override
+  protected void forceStringPrefix() {
+    if (firstExpression) {
+      buf.append("\"\" + ");
+    }
+  }
+
+  @Override
+  protected void forceStringSuffix() {
+  }
+}
diff --git a/user/src/com/google/gwt/codegen/server/SafeHtmlStringGenerator.java b/user/src/com/google/gwt/codegen/server/SafeHtmlStringGenerator.java
new file mode 100644
index 0000000..3c96c62
--- /dev/null
+++ b/user/src/com/google/gwt/codegen/server/SafeHtmlStringGenerator.java
@@ -0,0 +1,76 @@
+/*
+ * 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 com.google.gwt.safehtml.shared.SafeHtmlBuilder;
+
+/**
+ * Helper class to produce string expressions consisting of literals and
+ * computed values.
+ */
+class SafeHtmlStringGenerator extends StringGenerator {
+
+  /**
+   * Fully-qualified class name of the SafeHtmlBuilder class.
+   */
+  private static final String SAFE_HTML_BUILDER_FQCN = SafeHtmlBuilder.class.getCanonicalName();
+
+  /**
+   * Initialize the StringGenerator with an output buffer.
+   *
+   * @param buf output buffer
+   */
+  SafeHtmlStringGenerator(StringBuilder buf) {
+    super(buf);
+    buf.append("new " + SAFE_HTML_BUILDER_FQCN + "()");
+  }
+
+  @Override
+  protected void afterExpression(Type type) {
+    buf.append(')');
+  }
+
+  @Override
+  protected void beforeExpression(Type type) {
+    switch (type) {
+      case LITERAL:
+        buf.append(".appendHtmlConstant(");
+        break;
+      case PRIMITIVE:
+      case SAFE:
+        buf.append(".append(");
+        break;
+      case OTHER:
+        buf.append(".appendEscaped(");
+        break;
+    }
+  }
+
+  @Override
+  protected void finishOutput() {
+    buf.append(".toSafeHtml()");
+  }
+
+  @Override
+  protected void forceStringPrefix() {
+    buf.append("String.valueOf(");
+  }
+
+  @Override
+  protected void forceStringSuffix() {
+    buf.append(")");
+  }
+}
diff --git a/user/src/com/google/gwt/codegen/server/StringGenerator.java b/user/src/com/google/gwt/codegen/server/StringGenerator.java
new file mode 100644
index 0000000..2ccceb1
--- /dev/null
+++ b/user/src/com/google/gwt/codegen/server/StringGenerator.java
@@ -0,0 +1,153 @@
+/*
+ * 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 to produce string expressions consisting of literals and
+ * computed values.
+ */
+public abstract class StringGenerator {
+
+  /**
+   * Type of expression being processed.
+   */
+  protected enum Type {
+    LITERAL,
+    PRIMITIVE,
+    SAFE,
+    OTHER,
+  }
+
+  /**
+   * Create a {@link StringGenerator} instance.
+   * 
+   * @param buf
+   * @param returnsSafeHtml
+   * @return {@link StringGenerator} instance
+   */
+  public static StringGenerator create(StringBuilder buf, boolean returnsSafeHtml) {
+    if (returnsSafeHtml) {
+      return new SafeHtmlStringGenerator(buf);
+    } else {
+      return new PlainStringGenerator(buf);
+    }
+  }
+
+  /**
+   * Output string buffer.
+   */
+  protected final StringBuilder buf;
+
+  /**
+   * True if we are in the middle of a string literal.
+   */
+  protected boolean inString;
+
+  /**
+   * Initialize the StringGenerator with an output buffer.
+   *
+   * @param buf output buffer
+   */
+  protected StringGenerator(StringBuilder buf) {
+    this.buf = buf;
+    inString = false;
+  }
+  /**
+   * Append an expression to this string expression.
+   *
+   * @param expression to add
+   * @param isSafeHtmlTyped true if the expression is known to be of type
+   *     {@link com.google.gwt.safehtml.shared.SafeHtml SafeHtml}; only relevant
+   *     if this generator has been initialized to generate a
+   *     {@link com.google.gwt.safehtml.shared.SafeHtml SafeHtml}-valued
+   *     expression
+   * @param isPrimitiveTyped true if the expression is of a primitive type;
+   *     only relevant if this generator has been initialized to generate a
+   *     {@link com.google.gwt.safehtml.shared.SafeHtml SafeHtml}-valued
+   *     expression
+   * @param needsConversionToString true if the expression is not known to be
+   *     of type String and needs to be converted
+   */
+  public void appendExpression(String expression, boolean isSafeHtmlTyped,
+      boolean isPrimitiveTyped, boolean needsConversionToString) {
+    if (inString) {
+      buf.append('"');
+      afterExpression(Type.LITERAL);
+      inString = false;
+    }
+    Type type;
+    if (isPrimitiveTyped) {
+      type = Type.PRIMITIVE;
+    } else if (isSafeHtmlTyped) {
+      type = Type.SAFE;
+    } else {
+      type = Type.OTHER;
+    }
+    beforeExpression(type);
+    if (type == Type.OTHER && needsConversionToString) {
+      forceStringPrefix();
+    }
+    buf.append(expression);
+    if (type == Type.OTHER && needsConversionToString) {
+      forceStringSuffix();
+    }
+    afterExpression(type);
+  }
+
+  /**
+   * Append part of a string literal.
+   *
+   * @param str part of string literal
+   */
+  public void appendStringLiteral(String str) {
+    if (!inString) {
+      beforeExpression(Type.LITERAL);
+      buf.append('"');
+      inString = true;
+    }
+    buf.append(str);
+  }
+
+  /**
+   * Append an expression to this string expression.
+   *
+   * @param expression to add, which the caller asserts is String-valued
+   */
+  public void appendStringValuedExpression(String expression) {
+    appendExpression(expression, false, false, false);
+  }
+
+  /**
+   * Complete the string, closing an open quote and handling empty strings.
+   */
+  public void completeString() {
+    if (inString) {
+      buf.append('"');
+      afterExpression(Type.LITERAL);
+    }
+    finishOutput();
+  }
+
+  protected abstract void afterExpression(Type type);
+
+  protected abstract void beforeExpression(Type type);
+
+  protected abstract void finishOutput();
+
+  protected abstract void forceStringPrefix();
+
+  protected abstract void forceStringSuffix();
+}
diff --git a/user/src/com/google/gwt/codegen/server/package-info.java b/user/src/com/google/gwt/codegen/server/package-info.java
new file mode 100644
index 0000000..e300f28
--- /dev/null
+++ b/user/src/com/google/gwt/codegen/server/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+
+/**
+ * Classes used by code generation, either on the server or from a GWT
+ * generator.
+ */
+@com.google.gwt.util.PreventSpuriousRebuilds
+package com.google.gwt.codegen.server;
diff --git a/user/src/com/google/gwt/i18n/rebind/MessagesMethodCreator.java b/user/src/com/google/gwt/i18n/rebind/MessagesMethodCreator.java
index d475483..8e05738 100644
--- a/user/src/com/google/gwt/i18n/rebind/MessagesMethodCreator.java
+++ b/user/src/com/google/gwt/i18n/rebind/MessagesMethodCreator.java
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.i18n.rebind;
 
+import com.google.gwt.codegen.server.StringGenerator;
 import com.google.gwt.core.ext.TreeLogger;
 import com.google.gwt.core.ext.UnableToCompleteException;
 import com.google.gwt.core.ext.typeinfo.JArrayType;
@@ -922,7 +923,7 @@
      *
      * @param logger
      * @param locale current locale
-     * @param out StringBuffer to append to
+     * @param out StringBuilder to append to
      * @param formatArgs format-specific arguments
      * @param subformat the remainder of the format string
      * @param argName the name of the argument to use in the generated code
@@ -1247,8 +1248,8 @@
       final String val1, final JType elemType, final boolean isSafeHtml,
       String listPattern, final boolean formatSecond, final Parameters params)
       throws UnableToCompleteException {
-    final StringBuffer buf = new StringBuffer();
-    final StringGenerator gen = new StringGenerator(buf, isSafeHtml);
+    final StringBuilder buf = new StringBuilder();
+    final StringGenerator gen = StringGenerator.create(buf, isSafeHtml);
     try {
       List<TemplateChunk> chunks = MessageFormatParser.parse(listPattern);
       for (TemplateChunk chunk : chunks) {
@@ -1331,8 +1332,8 @@
     writer.println("int i = arg" + listArgNum + "_size;");
     writer.println("if (i > 0) {");
     writer.indent();
-    StringBuffer outbuf = new StringBuffer();
-    StringGenerator buf = new StringGenerator(outbuf, isSafeHtml);
+    StringBuilder outbuf = new StringBuilder();
+    StringGenerator buf = StringGenerator.create(outbuf, isSafeHtml);
     formatArg(logger, locale, buf, listArg, listAccessor.getElement("--i"),
         elemType, params);
     buf.completeString();
@@ -1464,8 +1465,8 @@
   private void generateString(final TreeLogger logger, final GwtLocale locale,
       final String template, final Parameters paramsAccessor, SourceWriter writer,
       final boolean isSafeHtml, final int lastPluralArgNumber) throws UnableToCompleteException {
-    StringBuffer outputBuf = new StringBuffer();
-    final StringGenerator buf = new StringGenerator(outputBuf, isSafeHtml);
+    StringBuilder outputBuf = new StringBuilder();
+    final StringGenerator buf = StringGenerator.create(outputBuf, isSafeHtml);
     final int n = paramsAccessor.getCount();
     try {
       for (TemplateChunk chunk : MessageFormatParser.parse(template)) {
diff --git a/user/src/com/google/gwt/i18n/rebind/StringGenerator.java b/user/src/com/google/gwt/i18n/rebind/StringGenerator.java
deleted file mode 100644
index 1200220..0000000
--- a/user/src/com/google/gwt/i18n/rebind/StringGenerator.java
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Copyright 2010 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.i18n.rebind;
-
-/**
- * Helper class to produce string expressions consisting of literals and
- * computed values.
- */
-public class StringGenerator {
-
-  /**
-   * Output string buffer.
-   */
-  private StringBuffer buf;
-
-  /**
-   * True if we are in the middle of a string literal.
-   */
-  private boolean inString;
-
-  /**
-   * True if the method's return type is SafeHtml (and SafeHtmlBuilder is to
-   * be used to generate the expression); otherwise a String expression is
-   * generated.
-   */
-  private final boolean returnsSafeHtml;
-
-  /**
-   * Initialize the StringGenerator with an output buffer.
-   *
-   * @param buf output buffer
-   * @param returnsSafeHtml if true, an expression of type {@link SafeHtml} is
-   *          being generated, otherwise a {@link String}-valued expression is
-   *          generated
-   */
-  public StringGenerator(StringBuffer buf, boolean returnsSafeHtml) {
-    this.buf = buf;
-    inString = false;
-    this.returnsSafeHtml = returnsSafeHtml;
-    if (returnsSafeHtml) {
-      buf.append("new " + MessagesMethodCreator.SAFE_HTML_BUILDER_FQCN + "()");
-    } else {
-      buf.append("new java.lang.StringBuffer()");
-    }
-  }
-
-  /**
-   * Append an expression to this string expression.
-   *
-   * @param expression to add
-   * @param isSafeHtmlTyped true if the expression is known to be of type
-   *          {@link SafeHtml}; only relevant if this generator has been
-   *          initialized to generate a {@link SafeHtml}-valued expression
-   * @param isPrimititiveTyped true if the expression is of a primitive type;
-   *          only relevant if this generator has been initialized to generate
-   *          a {@link SafeHtml}-valued expression
-   * @param needsConversionToString true if the expression is not known to be
-   *          of type String and needs to be converted
-   */
-  public void appendExpression(String expression, boolean isSafeHtmlTyped,
-      boolean isPrimititiveTyped, boolean needsConversionToString) {
-    if (inString) {
-      buf.append("\")");
-      inString = false;
-    }
-    /*
-     * SafeHtmlBuilder has append() methods for primitive types as well as for
-     * SafeHtml-valued expressions. For all other expression types, use
-     * appendEscaped(). In addition, if the expression is not known to be of
-     * type String, covert to String.
-     */
-    if (returnsSafeHtml && !isSafeHtmlTyped && !isPrimititiveTyped) {
-      buf.append(".appendEscaped(");
-      if (needsConversionToString) {
-        buf.append("String.valueOf(");
-      }
-    } else {
-      buf.append(".append(");
-    }
-    buf.append(expression);
-    buf.append(")");
-    if (returnsSafeHtml && !isSafeHtmlTyped && !isPrimititiveTyped
-        && needsConversionToString) {
-      buf.append(")");
-    }
-  }
-
-  /**
-   * Append part of a string literal.
-   *
-   * @param str part of string literal
-   */
-  public void appendStringLiteral(String str) {
-    if (!inString) {
-      if (returnsSafeHtml) {
-        buf.append(".appendHtmlConstant(\"");
-      } else {
-        buf.append(".append(\"");
-      }
-      inString = true;
-    }
-    buf.append(str);
-  }
-
-  /**
-   * Append an expression to this string expression.
-   *
-   * @param expression to add, which the caller asserts is String-valued
-   */
-  public void appendStringValuedExpression(String expression) {
-    appendExpression(expression, false, false, false);
-  }
-
-  /**
-   * Complete the string, closing an open quote and handling empty strings.
-   */
-  public void completeString() {
-    if (inString) {
-      buf.append("\")");
-    }
-    if (returnsSafeHtml) {
-      buf.append(".toSafeHtml()");
-    } else {
-      buf.append(".toString()");
-    }
-  }
-}
diff --git a/user/test/com/google/gwt/codegen/CodeGenSuite.java b/user/test/com/google/gwt/codegen/CodeGenSuite.java
new file mode 100644
index 0000000..cecc92f
--- /dev/null
+++ b/user/test/com/google/gwt/codegen/CodeGenSuite.java
@@ -0,0 +1,42 @@
+/*
+ * 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;
+
+import com.google.gwt.codegen.server.StringGeneratorTest;
+import com.google.gwt.junit.tools.GWTTestSuite;
+
+import junit.framework.Test;
+
+/**
+ * All codegen tests.
+ */
+public class CodeGenSuite {
+
+  /**
+   * Create a suite for all codegen tests.
+   * 
+   * @return {@link GWTTestSuite} instance
+   */
+  public static Test suite() {
+    GWTTestSuite suite = new GWTTestSuite("All codegen tests");
+
+    // $JUnit-BEGIN$
+    suite.addTestSuite(StringGeneratorTest.class);
+    // $JUnit-END$
+
+    return suite;
+  }
+}
diff --git a/user/test/com/google/gwt/codegen/server/StringGeneratorTest.java b/user/test/com/google/gwt/codegen/server/StringGeneratorTest.java
new file mode 100644
index 0000000..c4f178a
--- /dev/null
+++ b/user/test/com/google/gwt/codegen/server/StringGeneratorTest.java
@@ -0,0 +1,198 @@
+/*
+ * 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 junit.framework.TestCase;
+
+/**
+ * Test for {@link StringGenerator}.
+ */
+public class StringGeneratorTest extends TestCase {
+
+  public void testSafeHtmlArgOnlyPrimitive() {
+    StringBuilder buf = new StringBuilder();
+    StringGenerator gen = StringGenerator.create(buf, true);
+    gen.appendExpression("name", false, true, true);
+    gen.completeString();
+    assertEquals("new com.google.gwt.safehtml.shared.SafeHtmlBuilder().append(name).toSafeHtml()",
+        buf.toString());
+  }
+
+  public void testSafeHtmlArgPrimitive() {
+    StringBuilder buf = new StringBuilder();
+    StringGenerator gen = StringGenerator.create(buf, true);
+    gen.appendStringLiteral("Hello ");
+    gen.appendExpression("name", false, true, true);
+    gen.completeString();
+    assertEquals("new com.google.gwt.safehtml.shared.SafeHtmlBuilder().appendHtmlConstant("
+       + "\"Hello \").append(name).toSafeHtml()", buf.toString());
+  }
+
+  public void testSafeHtmlArgSafeHtml() {
+    StringBuilder buf = new StringBuilder();
+    StringGenerator gen = StringGenerator.create(buf, true);
+    gen.appendStringLiteral("Hello ");
+    gen.appendExpression("name", true, false, false);
+    gen.completeString();
+    assertEquals("new com.google.gwt.safehtml.shared.SafeHtmlBuilder().appendHtmlConstant("
+       + "\"Hello \").append(name).toSafeHtml()", buf.toString());
+  }
+
+  public void testSafeHtmlArgString() {
+    StringBuilder buf = new StringBuilder();
+    StringGenerator gen = StringGenerator.create(buf, true);
+    gen.appendStringLiteral("Hello ");
+    gen.appendExpression("name", false, false, false);
+    gen.completeString();
+    assertEquals("new com.google.gwt.safehtml.shared.SafeHtmlBuilder().appendHtmlConstant("
+       + "\"Hello \").appendEscaped(name).toSafeHtml()", buf.toString());
+  }
+
+  public void testSafeHtmlArgStringFirst() {
+    StringBuilder buf = new StringBuilder();
+    StringGenerator gen = StringGenerator.create(buf, true);
+    gen.appendExpression("name", false, false, false);
+    gen.appendStringLiteral(" says hello");
+    gen.completeString();
+    assertEquals("new com.google.gwt.safehtml.shared.SafeHtmlBuilder().appendEscaped(name)"
+        + ".appendHtmlConstant(\" says hello\").toSafeHtml()", buf.toString());
+  }
+
+  public void testSafeHtmlArgUnknown() {
+    StringBuilder buf = new StringBuilder();
+    StringGenerator gen = StringGenerator.create(buf, true);
+    gen.appendStringLiteral("Hello ");
+    gen.appendExpression("name", false, false, true);
+    gen.completeString();
+    assertEquals("new com.google.gwt.safehtml.shared.SafeHtmlBuilder().appendHtmlConstant("
+        + "\"Hello \").appendEscaped(String.valueOf(name)).toSafeHtml()", buf.toString());
+  }
+
+  public void testSafeHtmlArgUnknownFirst() {
+    StringBuilder buf = new StringBuilder();
+    StringGenerator gen = StringGenerator.create(buf, true);
+    gen.appendExpression("name", false, false, true);
+    gen.appendStringLiteral(" says hello");
+    gen.completeString();
+    assertEquals("new com.google.gwt.safehtml.shared.SafeHtmlBuilder().appendEscaped("
+        + "String.valueOf(name)).appendHtmlConstant(\" says hello\").toSafeHtml()",
+        buf.toString());
+  }
+
+  public void testSafeHtmlEmpty() {
+    StringBuilder buf = new StringBuilder();
+    StringGenerator gen = StringGenerator.create(buf, true);
+    gen.completeString();
+    assertEquals("new com.google.gwt.safehtml.shared.SafeHtmlBuilder().toSafeHtml()",
+        buf.toString());
+  }
+
+  public void testSafeHtmlLiteral() {
+    StringBuilder buf = new StringBuilder();
+    StringGenerator gen = StringGenerator.create(buf, true);
+    gen.appendStringLiteral("Hello");
+    gen.completeString();
+    assertEquals("new com.google.gwt.safehtml.shared.SafeHtmlBuilder().appendHtmlConstant("
+        + "\"Hello\").toSafeHtml()", buf.toString());
+  }
+
+  public void testSafeHtmlLiteralLiteral() {
+    StringBuilder buf = new StringBuilder();
+    StringGenerator gen = StringGenerator.create(buf, true);
+    gen.appendStringLiteral("Hello ");
+    gen.appendStringLiteral("there");
+    gen.completeString();
+    assertEquals("new com.google.gwt.safehtml.shared.SafeHtmlBuilder().appendHtmlConstant("
+        + "\"Hello there\").toSafeHtml()", buf.toString());
+  }
+
+  public void testStringArgOnlyPrimitive() {
+    StringBuilder buf = new StringBuilder();
+    StringGenerator gen = StringGenerator.create(buf, false);
+    gen.appendExpression("name", false, true, true);
+    gen.completeString();
+    assertEquals("\"\" + name", buf.toString());
+  }
+
+  public void testStringArgPrimitive() {
+    StringBuilder buf = new StringBuilder();
+    StringGenerator gen = StringGenerator.create(buf, false);
+    gen.appendStringLiteral("Hello ");
+    gen.appendExpression("name", false, true, true);
+    gen.completeString();
+    assertEquals("\"Hello \" + name", buf.toString());
+  }
+
+  public void testStringArgString() {
+    StringBuilder buf = new StringBuilder();
+    StringGenerator gen = StringGenerator.create(buf, false);
+    gen.appendStringLiteral("Hello ");
+    gen.appendExpression("name", false, false, false);
+    gen.completeString();
+    assertEquals("\"Hello \" + name", buf.toString());
+  }
+
+  public void testStringArgStringFirst() {
+    StringBuilder buf = new StringBuilder();
+    StringGenerator gen = StringGenerator.create(buf, false);
+    gen.appendExpression("name", false, false, false);
+    gen.appendStringLiteral(" says hello");
+    gen.completeString();
+    assertEquals("name + \" says hello\"", buf.toString());
+  }
+
+  public void testStringArgUnknown() {
+    StringBuilder buf = new StringBuilder();
+    StringGenerator gen = StringGenerator.create(buf, false);
+    gen.appendStringLiteral("Hello ");
+    gen.appendExpression("name", false, false, true);
+    gen.completeString();
+    assertEquals("\"Hello \" + name", buf.toString());
+  }
+
+  public void testStringArgUnknownFirst() {
+    StringBuilder buf = new StringBuilder();
+    StringGenerator gen = StringGenerator.create(buf, false);
+    gen.appendExpression("name", false, false, true);
+    gen.appendStringLiteral(" says hello");
+    gen.completeString();
+    assertEquals("\"\" + name + \" says hello\"", buf.toString());
+  }
+
+  public void testStringEmpty() {
+    StringBuilder buf = new StringBuilder();
+    StringGenerator gen = StringGenerator.create(buf, false);
+    gen.completeString();
+    assertEquals("\"\"", buf.toString());
+  }
+
+  public void testStringLiteral() {
+    StringBuilder buf = new StringBuilder();
+    StringGenerator gen = StringGenerator.create(buf, false);
+    gen.appendStringLiteral("Hello");
+    gen.completeString();
+    assertEquals("\"Hello\"", buf.toString());
+  }
+
+  public void testStringLiteralLiteral() {
+    StringBuilder buf = new StringBuilder();
+    StringGenerator gen = StringGenerator.create(buf, false);
+    gen.appendStringLiteral("Hello ");
+    gen.appendStringLiteral("there");
+    gen.completeString();
+    assertEquals("\"Hello there\"", buf.toString());
+  }
+}