Small refactor in JsObjectLiteral.

Change-Id: Ia937d5bd5b1b7286ecc481cbafcca7c3e0e0de03
diff --git a/dev/core/src/com/google/gwt/dev/js/CoverageInstrumentor.java b/dev/core/src/com/google/gwt/dev/js/CoverageInstrumentor.java
index 2ca63b4..f937102 100644
--- a/dev/core/src/com/google/gwt/dev/js/CoverageInstrumentor.java
+++ b/dev/core/src/com/google/gwt/dev/js/CoverageInstrumentor.java
@@ -27,13 +27,10 @@
 import com.google.gwt.dev.js.ast.JsNumberLiteral;
 import com.google.gwt.dev.js.ast.JsObjectLiteral;
 import com.google.gwt.dev.js.ast.JsProgram;
-import com.google.gwt.dev.js.ast.JsPropertyInitializer;
 import com.google.gwt.dev.js.ast.JsStringLiteral;
 import com.google.gwt.thirdparty.guava.common.annotations.VisibleForTesting;
 import com.google.gwt.thirdparty.guava.common.collect.Multimap;
 
-import java.util.List;
-
 /**
  * Instruments the generated JavaScript to record code coverage information
  * about the original Java source.
@@ -77,21 +74,15 @@
   @VisibleForTesting
   static JsObjectLiteral baselineCoverage(SourceInfo info,
       Multimap<String, Integer> instrumentableLines) {
-    JsObjectLiteral baseline = new JsObjectLiteral(info);
-    List<JsPropertyInitializer> properties = baseline.getPropertyInitializers();
+    JsObjectLiteral.Builder baselineBuilder = JsObjectLiteral.builder().setSourceInfo(info);
     for (String filename : instrumentableLines.keySet()) {
-      JsPropertyInitializer pair = new JsPropertyInitializer(info);
-      pair.setLabelExpr(new JsStringLiteral(info, filename));
-      JsObjectLiteral lines = new JsObjectLiteral(info);
-      List<JsPropertyInitializer> coverage = lines.getPropertyInitializers();
+      JsObjectLiteral.Builder linesBuilder = JsObjectLiteral.builder().setSourceInfo(info);
       for (int line : instrumentableLines.get(filename)) {
-        coverage.add(new JsPropertyInitializer(info,
-            new JsNumberLiteral(info, line), new JsNumberLiteral(info, 0)));
+        linesBuilder.add(new JsNumberLiteral(info, line), new JsNumberLiteral(info, 0));
       }
-      pair.setValueExpr(lines);
-      properties.add(pair);
+      baselineBuilder.add(new JsStringLiteral(info, filename), linesBuilder.build());
     }
-    return baseline;
+    return baselineBuilder.build();
   }
 
   private Multimap<String, Integer> instrumentableLines;
diff --git a/dev/core/src/com/google/gwt/dev/js/JsParser.java b/dev/core/src/com/google/gwt/dev/js/JsParser.java
index 7b58712..370aff7 100644
--- a/dev/core/src/com/google/gwt/dev/js/JsParser.java
+++ b/dev/core/src/com/google/gwt/dev/js/JsParser.java
@@ -51,7 +51,6 @@
 import com.google.gwt.dev.js.ast.JsParameter;
 import com.google.gwt.dev.js.ast.JsPostfixOperation;
 import com.google.gwt.dev.js.ast.JsPrefixOperation;
-import com.google.gwt.dev.js.ast.JsPropertyInitializer;
 import com.google.gwt.dev.js.ast.JsRegExp;
 import com.google.gwt.dev.js.ast.JsReturn;
 import com.google.gwt.dev.js.ast.JsRootScope;
@@ -845,7 +844,8 @@
   }
 
   private JsExpression mapObjectLit(Node objLitNode) throws JsParserException {
-    JsObjectLiteral toLit = new JsObjectLiteral(makeSourceInfo(objLitNode));
+    JsObjectLiteral.Builder objectLiteralBuilder = JsObjectLiteral.builder()
+        .setSourceInfo(makeSourceInfo(objLitNode));
     Node fromPropInit = objLitNode.getFirstChild();
     while (fromPropInit != null) {
 
@@ -861,17 +861,14 @@
             + toLabelExpr, objLitNode);
       }
       JsExpression toValueExpr = mapExpression(fromValueExpr);
-
-      JsPropertyInitializer toPropInit = new JsPropertyInitializer(
-          makeSourceInfo(fromLabelExpr), toLabelExpr, toValueExpr);
-      toLit.getPropertyInitializers().add(toPropInit);
+      objectLiteralBuilder.add(makeSourceInfo(fromLabelExpr), toLabelExpr, toValueExpr);
 
       // Begin the next property initializer, if there is one.
       //
       fromPropInit = fromPropInit.getNext();
     }
 
-    return toLit;
+    return objectLiteralBuilder.build();
   }
 
   private JsExpression mapOptionalExpression(Node exprNode)
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsObjectLiteral.java b/dev/core/src/com/google/gwt/dev/js/ast/JsObjectLiteral.java
index f60c448..3d9a71b 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsObjectLiteral.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsObjectLiteral.java
@@ -14,8 +14,9 @@
 package com.google.gwt.dev.js.ast;
 
 import com.google.gwt.dev.jjs.SourceInfo;
+import com.google.gwt.dev.jjs.SourceOrigin;
+import com.google.gwt.thirdparty.guava.common.collect.Lists;
 
-import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -23,7 +24,57 @@
  */
 public final class JsObjectLiteral extends JsLiteral {
 
-  private final List<JsPropertyInitializer> properties = new ArrayList<JsPropertyInitializer>();
+  public static Builder builder() {
+    return new Builder();
+  }
+
+  /**
+   * Builder class for JsObjectLiterals.
+   */
+  public static class Builder {
+
+    private Builder() { }
+
+    private List<JsPropertyInitializer> propertyInitializers = Lists.newArrayList();
+    private SourceInfo sourceInfo = SourceOrigin.UNKNOWN;
+    private boolean internable = false;
+
+    public Builder add(String property, JsExpression value) {
+      add(new JsNameRef(sourceInfo, property), value);
+      return this;
+    }
+
+    public Builder add(JsExpression property, JsExpression value) {
+      add(sourceInfo, property, value);
+      return this;
+    }
+
+    public Builder add(SourceInfo sourceInfo, JsExpression property, JsExpression value) {
+      propertyInitializers.add(new JsPropertyInitializer(sourceInfo, property, value));
+      return this;
+    }
+
+    public Builder setSourceInfo(SourceInfo info) {
+      sourceInfo = info;
+      return this;
+    }
+
+    public Builder setInternable() {
+      internable = true;
+      return this;
+    }
+
+    public JsObjectLiteral build() {
+      JsObjectLiteral objectLiteral = new JsObjectLiteral(sourceInfo);
+      objectLiteral.getPropertyInitializers().addAll(propertyInitializers);
+      if (internable) {
+        objectLiteral.setInternable();
+      }
+      return objectLiteral;
+    }
+  }
+
+  private final List<JsPropertyInitializer> properties = Lists.newArrayList();
 
   private boolean internable = false;
 
@@ -40,6 +91,15 @@
     properties.add(new JsPropertyInitializer(sourceInfo, label, value));
   }
 
+  /**
+   * Adds a property and its initial value to the object literal.
+   * <p>
+   * NOTE: Does not check for duplicate names.
+   */
+  public void addProperty(SourceInfo sourceInfo, String label, JsExpression value) {
+    addProperty(sourceInfo, new JsStringLiteral(sourceInfo, label), value);
+  }
+
   @Override
   public boolean equals(Object that) {
     if (that == null || this.getClass() != that.getClass()) {