Author: b...@google.com
Date: Tue Mar 17 11:59:31 2009
New Revision: 5028

Added:
     
changes/bobv/clientbundle/user/src/com/google/gwt/resources/css/ast/CollapsedNode.java
    
(contents, props changed)
     
changes/bobv/clientbundle/user/src/com/google/gwt/resources/css/ast/CssNodeCloner.java
    
(contents, props changed)
     
changes/bobv/clientbundle/user/src/com/google/gwt/resources/css/ast/HasSelectors.java
    
(contents, props changed)
     
changes/bobv/clientbundle/user/test/com/google/gwt/resources/rg/CssNodeClonerTest.java
    
(contents, props changed)
Modified:
     
changes/bobv/clientbundle/user/src/com/google/gwt/resources/css/ast/CssProperty.java
     
changes/bobv/clientbundle/user/src/com/google/gwt/resources/css/ast/CssRule.java
     
changes/bobv/clientbundle/user/src/com/google/gwt/resources/css/ast/CssSprite.java
     
changes/bobv/clientbundle/user/src/com/google/gwt/resources/rg/CssResourceGenerator.java
     
changes/bobv/clientbundle/user/test/com/google/gwt/resources/ResourcesSuite.java
     
changes/bobv/clientbundle/user/test/com/google/gwt/resources/rg/CssReorderTest.java
     
changes/bobv/clientbundle/user/test/com/google/gwt/resources/rg/CssRtlTest.java
     
changes/bobv/clientbundle/user/test/com/google/gwt/resources/rg/CssTestCase.java
     
changes/bobv/clientbundle/user/test/com/google/gwt/resources/rg/selectorMerging_expected.css
     
changes/bobv/clientbundle/user/test/com/google/gwt/resources/rg/selectorMerging_test.css

Log:
Merge r1572 and 1573 from incubator that fixed an AST aliasing bug.

Original log entries:
   Fix an aliasing bug in SplitRulesVisitor by adding a general-purpose  
CssNode cloner.
   Ensure immutability of all Value types.
   Improve testing framework to detect aliasing in the future.

svn diff -c1572 | sed 's|gwt\.libideas\.|gwt.|g' | sed 's|gwt/libideas/| 
gwt/|g'
svn revert CSSResourceTest.java



Added:  
changes/bobv/clientbundle/user/src/com/google/gwt/resources/css/ast/CollapsedNode.java
==============================================================================
--- (empty file)
+++  
changes/bobv/clientbundle/user/src/com/google/gwt/resources/css/ast/CollapsedNode.java
   
Tue Mar 17 11:59:31 2009
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2009 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.resources.css.ast;
+
+import java.util.List;
+
+/**
+ * This delegate class bypasses traversal of a node, instead traversing the
+ * node's children. Any modifications made to the node list of the  
CollapsedNode
+ * will be reflected in the original node.
+ */
+public class CollapsedNode extends CssNode implements HasNodes {
+
+  private final List<CssNode> nodes;
+
+  public CollapsedNode(HasNodes parent) {
+    this(parent.getNodes());
+  }
+
+  public CollapsedNode(List<CssNode> nodes) {
+    this.nodes = nodes;
+  }
+
+  public List<CssNode> getNodes() {
+    return nodes;
+  }
+
+  public void traverse(CssVisitor visitor, Context context) {
+    visitor.acceptWithInsertRemove(getNodes());
+  }
+}
\ No newline at end of file

Added:  
changes/bobv/clientbundle/user/src/com/google/gwt/resources/css/ast/CssNodeCloner.java
==============================================================================
--- (empty file)
+++  
changes/bobv/clientbundle/user/src/com/google/gwt/resources/css/ast/CssNodeCloner.java
   
Tue Mar 17 11:59:31 2009
@@ -0,0 +1,310 @@
+/*
+ * Copyright 2009 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.resources.css.ast;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Stack;
+
+/**
+ * Clones CssNodes.
+ */
+public class CssNodeCloner extends CssVisitor {
+
+  /**
+   * Clone a list of nodes.
+   */
+  public static <T extends CssNode> List<T> clone(Class<T> clazz, List<T>  
nodes) {
+
+    // Push a fake context that will contain the cloned node
+    List<CssNode> topContext = new ArrayList<CssNode>();
+    final List<CssProperty> topProperties = new ArrayList<CssProperty>();
+    final List<CssSelector> topSelectors = new ArrayList<CssSelector>();
+
+    CssNodeCloner cloner = new CssNodeCloner();
+    cloner.curentNodes.push(topContext);
+    cloner.currentHasProperties = new HasProperties() {
+      public List<CssProperty> getProperties() {
+        return topProperties;
+      }
+    };
+    cloner.currentHasSelectors = new HasSelectors() {
+      public List<CssSelector> getSelectors() {
+        return topSelectors;
+      }
+    };
+
+    // Process the nodes
+    cloner.accept(nodes);
+
+    /*
+     * Return the requested data. Different AST nodes will have been  
collected
+     * in different parts of the initial context.
+     */
+    List<?> toIterate;
+    if (CssProperty.class.isAssignableFrom(clazz)) {
+      toIterate = topProperties;
+    } else if (CssSelector.class.isAssignableFrom(clazz)) {
+      toIterate = topSelectors;
+    } else {
+      toIterate = topContext;
+    }
+
+    assert toIterate.size() == nodes.size() : "Wrong number of nodes in  
top "
+        + "context. Expecting: " + nodes.size() + " Found: " +  
toIterate.size();
+
+    // Create the final return value
+    List<T> toReturn = new ArrayList<T>(toIterate.size());
+    for (Object node : toIterate) {
+      assert clazz.isInstance(node) : "Return type mismatch. Expecting: "
+          + clazz.getName() + " Found: " + node.getClass().getName();
+
+      // Cast to the correct type to avoid an unchecked generic cast
+      toReturn.add(clazz.cast(node));
+    }
+
+    return toReturn;
+  }
+
+  /**
+   * Clone a single node.
+   */
+  public static <T extends CssNode> T clone(Class<T> clazz, T node) {
+    return clone(clazz, Collections.singletonList(node)).get(0);
+  }
+
+  private HasProperties currentHasProperties;
+
+  private HasSelectors currentHasSelectors;
+
+  private final Stack<List<CssNode>> curentNodes = new  
Stack<List<CssNode>>();
+
+  private CssNodeCloner() {
+  }
+
+  @Override
+  public void endVisit(CssMediaRule x, Context ctx) {
+    popNodes(x);
+  }
+
+  @Override
+  public void endVisit(CssNoFlip x, Context ctx) {
+    popNodes(x);
+  }
+
+  @Override
+  public void endVisit(CssStylesheet x, Context ctx) {
+    popNodes(x);
+  }
+
+  @Override
+  public boolean visit(CssDef x, Context ctx) {
+    CssDef newDef = new CssDef(x.getKey());
+    newDef.getValues().addAll(x.getValues());
+
+    addToNodes(newDef);
+    return true;
+  }
+
+  @Override
+  public boolean visit(CssEval x, Context ctx) {
+    assert x.getValues().size() == 1;
+    assert x.getValues().get(0).isExpressionValue() != null;
+
+    String value =  
x.getValues().get(0).isExpressionValue().getExpression();
+    CssEval newEval = new CssEval(x.getKey(), value);
+    addToNodes(newEval);
+    return true;
+  }
+
+  /**
+   * A CssIf has two lists of nodes, so we want to handle traversal in this
+   * visitor.
+   */
+  @Override
+  public boolean visit(CssIf x, Context ctx) {
+    CssIf newIf = new CssIf();
+
+    if (x.getExpression() != null) {
+      newIf.setExpression(x.getExpression());
+    } else {
+      newIf.setProperty(x.getPropertyName());
+
+      String[] newValues = new String[x.getPropertyValues().length];
+      System.arraycopy(x.getPropertyValues(), 0, newValues, 0,  
newValues.length);
+      newIf.setPropertyValues(newValues);
+
+      newIf.setNegated(x.isNegated());
+    }
+
+    // Handle the "then" part
+    pushNodes(newIf);
+    accept(x.getNodes());
+    popNodes(x, newIf);
+
+    /*
+     * Push the "else" part as though it were its own node, but don't add  
it as
+     * its own top-level node.
+     */
+    CollapsedNode oldElseNodes = new CollapsedNode(x.getElseNodes());
+    CollapsedNode newElseNodes = new CollapsedNode(newIf.getElseNodes());
+    pushNodes(newElseNodes, false);
+    accept(oldElseNodes);
+    popNodes(oldElseNodes, newElseNodes);
+
+    return false;
+  }
+
+  @Override
+  public boolean visit(CssMediaRule x, Context ctx) {
+    CssMediaRule newRule = new CssMediaRule();
+    newRule.getMedias().addAll(newRule.getMedias());
+
+    pushNodes(newRule);
+    return true;
+  }
+
+  @Override
+  public boolean visit(CssNoFlip x, Context ctx) {
+    pushNodes(new CssNoFlip());
+    return true;
+  }
+
+  @Override
+  public boolean visit(CssPageRule x, Context ctx) {
+    CssPageRule newRule = new CssPageRule();
+    newRule.setPseudoPage(x.getPseudoPage());
+    addToNodes(newRule);
+    return true;
+  }
+
+  @Override
+  public boolean visit(CssProperty x, Context ctx) {
+    CssProperty newProperty = new CssProperty(x.getName(), x.getValues(),
+        x.isImportant());
+    currentHasProperties.getProperties().add(newProperty);
+    return true;
+  }
+
+  @Override
+  public boolean visit(CssRule x, Context ctx) {
+    CssRule newRule = new CssRule();
+    addToNodes(newRule);
+    return true;
+  }
+
+  @Override
+  public boolean visit(CssSelector x, Context ctx) {
+    CssSelector newSelector = new CssSelector(x.getSelector());
+    currentHasSelectors.getSelectors().add(newSelector);
+    return true;
+  }
+
+  @Override
+  public boolean visit(CssSprite x, Context ctx) {
+    CssSprite newSprite = new CssSprite();
+    newSprite.setResourceFunction(x.getResourceFunction());
+    addToNodes(newSprite);
+    return true;
+  }
+
+  @Override
+  public boolean visit(CssStylesheet x, Context ctx) {
+    CssStylesheet newSheet = new CssStylesheet();
+    pushNodes(newSheet);
+    return true;
+  }
+
+  @Override
+  public boolean visit(CssUrl x, Context ctx) {
+    assert x.getValues().size() == 1;
+    assert x.getValues().get(0).isIdentValue() != null;
+    String ident = x.getValues().get(0).isIdentValue().getIdent();
+    CssUrl newUrl = new CssUrl(x.getKey(), ident);
+    addToNodes(newUrl);
+    return true;
+  }
+
+  /**
+   * Add a cloned node instance to the output.
+   */
+  private void addToNodes(CssNode node) {
+    curentNodes.peek().add(node);
+
+    currentHasProperties = node instanceof HasProperties ? (HasProperties)  
node
+        : null;
+    currentHasSelectors = node instanceof HasSelectors ? (HasSelectors)  
node
+        : null;
+  }
+
+  /**
+   * Remove a frame.
+   *
+   * @param original the node that was being cloned so that validity  
checks may
+   *          be performed
+   */
+  private List<CssNode> popNodes(HasNodes original) {
+    List<CssNode> toReturn = curentNodes.pop();
+
+    if (toReturn.size() != original.getNodes().size()) {
+      throw new RuntimeException("Insufficient number of nodes for a "
+          + original.getClass().getName() + " Expected: "
+          + original.getNodes().size() + " Found: " + toReturn.size());
+    }
+
+    return toReturn;
+  }
+
+  /**
+   * Remove a frame.
+   *
+   * @param original the node that was being cloned so that validity  
checks may
+   *          be performed
+   * @param expected the HasNodes whose nodes were being populated by the  
frame
+   *          being removed.
+   */
+  private List<CssNode> popNodes(HasNodes original, HasNodes expected) {
+    List<CssNode> toReturn = popNodes(original);
+
+    if (toReturn != expected.getNodes()) {
+      throw new RuntimeException("Incorrect parent node list popped");
+    }
+
+    return toReturn;
+  }
+
+  /**
+   * Push a new frame, adding the new parent as a child of the current  
parent.
+   */
+  private <T extends CssNode & HasNodes> void pushNodes(T parent) {
+    pushNodes(parent, true);
+  }
+
+  /**
+   * Push a new frame.
+   *
+   * @param addToNodes if <code>true</code> add the new parent node as a  
child
+   *          of the current parent.
+   */
+  private <T extends CssNode & HasNodes> void pushNodes(T parent,
+      boolean addToNodes) {
+    if (addToNodes) {
+      addToNodes(parent);
+    }
+    this.curentNodes.push(parent.getNodes());
+  }
+}

Modified:  
changes/bobv/clientbundle/user/src/com/google/gwt/resources/css/ast/CssProperty.java
==============================================================================
---  
changes/bobv/clientbundle/user/src/com/google/gwt/resources/css/ast/CssProperty.java
     
(original)
+++  
changes/bobv/clientbundle/user/src/com/google/gwt/resources/css/ast/CssProperty.java
     
Tue Mar 17 11:59:31 2009
@@ -19,6 +19,7 @@

  import java.util.ArrayList;
  import java.util.Arrays;
+import java.util.Collections;
  import java.util.Iterator;
  import java.util.List;

@@ -130,7 +131,7 @@
      private final List<Value> values;

      public ListValue(List<Value> values) {
-      this.values = new ArrayList<Value>(values);
+      this.values = Collections.unmodifiableList(new  
ArrayList<Value>(values));
      }

      public ListValue(Value... values) {

Modified:  
changes/bobv/clientbundle/user/src/com/google/gwt/resources/css/ast/CssRule.java
==============================================================================
---  
changes/bobv/clientbundle/user/src/com/google/gwt/resources/css/ast/CssRule.java
         
(original)
+++  
changes/bobv/clientbundle/user/src/com/google/gwt/resources/css/ast/CssRule.java
         
Tue Mar 17 11:59:31 2009
@@ -21,9 +21,9 @@
  /**
   *
   */
-public class CssRule extends CssNode implements HasProperties {
-  private final List<CssProperty> properties = new  
ArrayList<CssProperty>();
-  private final List<CssSelector> selectors = new ArrayList<CssSelector>();
+public class CssRule extends CssNode implements HasProperties,  
HasSelectors {
+  protected final List<CssProperty> properties = new  
ArrayList<CssProperty>();
+  protected final List<CssSelector> selectors = new  
ArrayList<CssSelector>();

    public List<CssProperty> getProperties() {
      return properties;

Modified:  
changes/bobv/clientbundle/user/src/com/google/gwt/resources/css/ast/CssSprite.java
==============================================================================
---  
changes/bobv/clientbundle/user/src/com/google/gwt/resources/css/ast/CssSprite.java
       
(original)
+++  
changes/bobv/clientbundle/user/src/com/google/gwt/resources/css/ast/CssSprite.java
       
Tue Mar 17 11:59:31 2009
@@ -167,8 +167,15 @@
      return resourceFunction;
    }

+  public void setResourceFunction(String resourceFunction) {
+    this.resourceFunction = resourceFunction;
+  }
+
    public void traverse(CssVisitor visitor, Context context) {
-    visitor.visit(this, context);
+    if (visitor.visit(this, context)) {
+      visitor.acceptWithInsertRemove(selectors);
+      visitor.acceptWithInsertRemove(getProperties());
+    }
      visitor.endVisit(this, context);
    }


Added:  
changes/bobv/clientbundle/user/src/com/google/gwt/resources/css/ast/HasSelectors.java
==============================================================================
--- (empty file)
+++  
changes/bobv/clientbundle/user/src/com/google/gwt/resources/css/ast/HasSelectors.java
    
Tue Mar 17 11:59:31 2009
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2009 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.resources.css.ast;
+
+import java.util.List;
+
+/**
+ * Something that has CSS selectors.
+ */
+public interface HasSelectors {
+  List<CssSelector> getSelectors();
+}

Modified:  
changes/bobv/clientbundle/user/src/com/google/gwt/resources/rg/CssResourceGenerator.java
==============================================================================
---  
changes/bobv/clientbundle/user/src/com/google/gwt/resources/rg/CssResourceGenerator.java
         
(original)
+++  
changes/bobv/clientbundle/user/src/com/google/gwt/resources/rg/CssResourceGenerator.java
         
Tue Mar 17 11:59:31 2009
@@ -41,6 +41,7 @@
  import com.google.gwt.resources.client.ImageResource.RepeatStyle;
  import com.google.gwt.resources.css.CssGenerationVisitor;
  import com.google.gwt.resources.css.GenerateCssAst;
+import com.google.gwt.resources.css.ast.CollapsedNode;
  import com.google.gwt.resources.css.ast.Context;
  import com.google.gwt.resources.css.ast.CssCompilerException;
  import com.google.gwt.resources.css.ast.CssDef;
@@ -50,6 +51,7 @@
  import com.google.gwt.resources.css.ast.CssModVisitor;
  import com.google.gwt.resources.css.ast.CssNoFlip;
  import com.google.gwt.resources.css.ast.CssNode;
+import com.google.gwt.resources.css.ast.CssNodeCloner;
  import com.google.gwt.resources.css.ast.CssProperty;
  import com.google.gwt.resources.css.ast.CssRule;
  import com.google.gwt.resources.css.ast.CssSelector;
@@ -198,32 +200,6 @@
    }

    /**
-   * This delegate class bypasses traversal of a node, instead traversing  
the
-   * node's children. Any modifications made to the node list of the
-   * CollapsedNode will be reflected in the original node.
-   */
-  static class CollapsedNode extends CssNode implements HasNodes {
-
-    private final List<CssNode> nodes;
-
-    public CollapsedNode(HasNodes parent) {
-      this(parent.getNodes());
-    }
-
-    public CollapsedNode(List<CssNode> nodes) {
-      this.nodes = nodes;
-    }
-
-    public List<CssNode> getNodes() {
-      return nodes;
-    }
-
-    public void traverse(CssVisitor visitor, Context context) {
-      visitor.acceptWithInsertRemove(getNodes());
-    }
-  }
-
-  /**
     * Statically evaluates {...@literal @if} rules.
     */
    static class IfEvaluator extends CssModVisitor {
@@ -436,7 +412,6 @@
      @Override
      public void endVisit(CssProperty x, Context ctx) {
        String name = x.getName();
-      List<Value> values = x.getValues().getValues();

        if (name.equalsIgnoreCase("left")) {
          x.setName("right");
@@ -453,7 +428,9 @@
        } else if (name.contains("-left-")) {
          x.setName(name.replace("-left-", "-right-"));
        } else {
+        List<Value> values = new  
ArrayList<Value>(x.getValues().getValues());
          invokePropertyHandler(x.getName(), values);
+        x.setValue(new CssProperty.ListValue(values));
        }
      }

@@ -692,7 +669,8 @@
        for (CssSelector sel : x.getSelectors()) {
          CssRule newRule = new CssRule();
          newRule.getSelectors().add(sel);
-        newRule.getProperties().addAll(x.getProperties());
+        newRule.getProperties().addAll(
+            CssNodeCloner.clone(CssProperty.class, x.getProperties()));
          ctx.insertBefore(newRule);
        }
        ctx.removeMe();

Modified:  
changes/bobv/clientbundle/user/test/com/google/gwt/resources/ResourcesSuite.java
==============================================================================
---  
changes/bobv/clientbundle/user/test/com/google/gwt/resources/ResourcesSuite.java
         
(original)
+++  
changes/bobv/clientbundle/user/test/com/google/gwt/resources/ResourcesSuite.java
         
Tue Mar 17 11:59:31 2009
@@ -20,6 +20,7 @@
  import com.google.gwt.resources.client.ImageResourceTest;
  import com.google.gwt.resources.client.NestedBundleTest;
  import com.google.gwt.resources.client.TextResourceTest;
+import com.google.gwt.resources.rg.CssNodeClonerTest;
  import com.google.gwt.resources.rg.CssReorderTest;
  import com.google.gwt.resources.rg.CssRtlTest;

@@ -35,6 +36,7 @@
      suite.addTestSuite(CSSResourceTest.class);
      suite.addTestSuite(CssReorderTest.class);
      suite.addTestSuite(CssRtlTest.class);
+    suite.addTestSuite(CssNodeClonerTest.class);
      suite.addTestSuite(ImageResourceTest.class);
      suite.addTestSuite(NestedBundleTest.class);
      suite.addTestSuite(TextResourceTest.class);

Added:  
changes/bobv/clientbundle/user/test/com/google/gwt/resources/rg/CssNodeClonerTest.java
==============================================================================
--- (empty file)
+++  
changes/bobv/clientbundle/user/test/com/google/gwt/resources/rg/CssNodeClonerTest.java
   
Tue Mar 17 11:59:31 2009
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2009 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.resources.rg;
+
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.resources.css.GenerateCssAst;
+import com.google.gwt.resources.css.ast.CssNode;
+import com.google.gwt.resources.css.ast.CssNodeCloner;
+import com.google.gwt.resources.css.ast.CssProperty;
+import com.google.gwt.resources.css.ast.CssSelector;
+import com.google.gwt.resources.css.ast.CssStylesheet;
+
+import java.net.URL;
+import java.util.List;
+
+/**
+ * Tests the CssNodeCloner utility class.
+ */
+public class CssNodeClonerTest extends CssTestCase {
+
+  public void testClone() throws UnableToCompleteException {
+    CssStylesheet sheet = GenerateCssAst.exec(TreeLogger.NULL,
+        new URL[] {getClass().getClassLoader().getResource(
+            "com/google/gwt/resources/client/test.css")});
+
+    CssStylesheet cloned = CssNodeCloner.clone(CssStylesheet.class, sheet);
+
+    assertNotSame(sheet, cloned);
+    assertNoAliasing(cloned);
+  }
+
+  public void testCloneList() throws UnableToCompleteException {
+    CssStylesheet sheet = GenerateCssAst.exec(TreeLogger.NULL,
+        new URL[] {getClass().getClassLoader().getResource(
+            "com/google/gwt/resources/client/test.css")});
+
+    List<CssNode> cloned = CssNodeCloner.clone(CssNode.class,  
sheet.getNodes());
+
+    assertEquals(sheet.getNodes().size(), cloned.size());
+
+    for (CssNode node : cloned) {
+      assertNoAliasing(node);
+    }
+  }
+
+  public void testCloneProperty() {
+    CssProperty.IdentValue value = new CssProperty.IdentValue("value");
+    CssProperty p = new CssProperty("name", value, true);
+
+    CssProperty clone = CssNodeCloner.clone(CssProperty.class, p);
+
+    assertNotSame(p, clone);
+    assertEquals(p.getName(), clone.getName());
+    assertEquals(value.getIdent(),
+        clone.getValues().getValues().get(0).isIdentValue().getIdent());
+  }
+
+  public void testCloneSelector() {
+    CssSelector sel = new CssSelector("a , b");
+
+    CssSelector clone = CssNodeCloner.clone(CssSelector.class, sel);
+
+    assertNotSame(sel, clone);
+    assertEquals(sel.getSelector(), clone.getSelector());
+  }
+}

Modified:  
changes/bobv/clientbundle/user/test/com/google/gwt/resources/rg/CssReorderTest.java
==============================================================================
---  
changes/bobv/clientbundle/user/test/com/google/gwt/resources/rg/CssReorderTest.java
      
(original)
+++  
changes/bobv/clientbundle/user/test/com/google/gwt/resources/rg/CssReorderTest.java
      
Tue Mar 17 11:59:31 2009
@@ -36,7 +36,8 @@

    private CssVisitor[] makeVisitors() {
      return new CssVisitor[] {
-        new SplitRulesVisitor(), new MergeIdenticalSelectorsVisitor(),
-        new MergeRulesByContentVisitor()};
+        new SplitRulesVisitor(), new AliasDetector(),
+        new MergeIdenticalSelectorsVisitor(), new AliasDetector(),
+        new MergeRulesByContentVisitor(), new AliasDetector()};
    }
  }

Modified:  
changes/bobv/clientbundle/user/test/com/google/gwt/resources/rg/CssRtlTest.java
==============================================================================
---  
changes/bobv/clientbundle/user/test/com/google/gwt/resources/rg/CssRtlTest.java 
 
(original)
+++  
changes/bobv/clientbundle/user/test/com/google/gwt/resources/rg/CssRtlTest.java 
 
Tue Mar 17 11:59:31 2009
@@ -17,6 +17,7 @@

  import com.google.gwt.core.ext.TreeLogger;
  import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.resources.css.ast.CssVisitor;
  import com.google.gwt.resources.rg.CssResourceGenerator.RtlVisitor;

  /**
@@ -24,26 +25,30 @@
   */
  public class CssRtlTest extends CssTestCase {
    public void testBackgroundProperties() throws UnableToCompleteException {
-    test(TreeLogger.NULL, "backgroundProperties", true, new RtlVisitor());
+    test(TreeLogger.NULL, "backgroundProperties", true, makeVisitors());
    }

    public void testCursorProperties() throws UnableToCompleteException {
-    test(TreeLogger.NULL, "cursorProperties", true, new RtlVisitor());
+    test(TreeLogger.NULL, "cursorProperties", true, makeVisitors());
    }

    public void testDirectionUpdatedInBodyOnly() throws  
UnableToCompleteException {
-    test(TreeLogger.NULL, "directionProperty", true, new RtlVisitor());
+    test(TreeLogger.NULL, "directionProperty", true, makeVisitors());
    }

    public void testFourValuedProperties() throws UnableToCompleteException {
-    test(TreeLogger.NULL, "fourValuedProperties", true, new RtlVisitor());
+    test(TreeLogger.NULL, "fourValuedProperties", true, makeVisitors());
    }

    public void testLeftRightProperties() throws UnableToCompleteException {
-    test(TreeLogger.NULL, "leftRightProperties", true, new RtlVisitor());
+    test(TreeLogger.NULL, "leftRightProperties", true, makeVisitors());
    }

    public void testNoFlip() throws UnableToCompleteException {
-    test(TreeLogger.NULL, "noflip", true, new RtlVisitor());
+    test(TreeLogger.NULL, "noflip", true, makeVisitors());
+  }
+
+  private CssVisitor[] makeVisitors() {
+    return new CssVisitor[] {new RtlVisitor(), new AliasDetector()};
    }
  }

Modified:  
changes/bobv/clientbundle/user/test/com/google/gwt/resources/rg/CssTestCase.java
==============================================================================
---  
changes/bobv/clientbundle/user/test/com/google/gwt/resources/rg/CssTestCase.java
         
(original)
+++  
changes/bobv/clientbundle/user/test/com/google/gwt/resources/rg/CssTestCase.java
         
Tue Mar 17 11:59:31 2009
@@ -20,13 +20,18 @@
  import com.google.gwt.core.ext.UnableToCompleteException;
  import com.google.gwt.core.ext.typeinfo.JClassType;
  import com.google.gwt.resources.css.GenerateCssAst;
+import com.google.gwt.resources.css.ast.CssNode;
  import com.google.gwt.resources.css.ast.CssStylesheet;
  import com.google.gwt.resources.css.ast.CssVisitor;
+import com.google.gwt.resources.css.ast.HasNodes;
  import com.google.gwt.resources.ext.ResourceContext;

  import junit.framework.TestCase;

  import java.net.URL;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;

  /**
   * Contains functions for golden-output tests that are concerned with  
structural
@@ -35,6 +40,37 @@
  class CssTestCase extends TestCase {

    /**
+   * Triggers an assertion if a CssNode is traversed more than once.
+   *
+   * @see CssTestCase#assertNoAliasing(CssNode)
+   */
+  protected static class AliasDetector extends CssVisitor {
+    private final Map<CssNode, Void> seen = new IdentityHashMap<CssNode,  
Void>();
+
+    @Override
+    protected void doAccept(List<? extends CssNode> list) {
+      for (CssNode node : list) {
+        doAccept(node);
+      }
+    }
+
+    @Override
+    protected void doAcceptWithInsertRemove(List<? extends CssNode> list) {
+      for (CssNode node : list) {
+        doAccept(node);
+      }
+    }
+
+    @Override
+    protected <T extends CssNode> T doAccept(T node) {
+      assertFalse("Found repeated node " + node.toString(),
+          seen.containsKey(node));
+      seen.put(node, null);
+      return super.doAccept(node);
+    }
+  }
+
+  /**
     * Total fake, no implementations.
     */
    private static class FakeContext implements ResourceContext {
@@ -67,35 +103,29 @@
    }

    /**
-   * Runs a test.
-   *
-   * @param testName is used to compute the test and expected resource  
paths.
-   * @param reversible if <code>true</code>, the test will attempt to  
transform
-   *          the expected css into the test css
+   * Asserts that two CssNodes are identical.
     */
-  void test(TreeLogger logger, String testName, boolean reversible,
-      CssVisitor... visitors) throws UnableToCompleteException {
-    String packagePath =  
getClass().getPackage().getName().replace('.', '/')
-        + "/";
-    URL testUrl = getClass().getClassLoader().getResource(
-        packagePath + testName + "_test.css");
-    assertNotNull("Cauld not find testUrl", testUrl);
-    URL expectedUrl = getClass().getClassLoader().getResource(
-        packagePath + testName + "_expected.css");
-    assertNotNull("Cauld not find testUrl", expectedUrl);
-
-    test(logger, testUrl, expectedUrl, visitors);
+  protected static <T extends CssNode & HasNodes> void assertEquals(
+      TreeLogger logger, T expected, T test) throws  
UnableToCompleteException {
+    String expectedCss = CssResourceGenerator.makeExpression(logger,
+        new FakeContext(), null, expected, false);
+    String testCss = CssResourceGenerator.makeExpression(logger,
+        new FakeContext(), null, test, false);
+    assertEquals(expectedCss, testCss);
+  }

-    if (reversible) {
-      test(logger, expectedUrl, testUrl, visitors);
-    }
+  /**
+   * Ensure that no CssNode is traversed more than once due to AST errors.
+   */
+  protected static void assertNoAliasing(CssNode node) {
+    (new AliasDetector()).accept(node);
    }

    /**
     * Compares the generated Java expressions for an input file,  
transformed in
     * order by the specified visitors, and a golden-output file.
     */
-  private void test(TreeLogger logger, URL test, URL expected,
+  private static void test(TreeLogger logger, URL test, URL expected,
        CssVisitor... visitors) throws UnableToCompleteException {

      CssStylesheet expectedSheet = null;
@@ -112,10 +142,31 @@
        v.accept(testSheet);
      }

-    String testCss = CssResourceGenerator.makeExpression(logger,
-        new FakeContext(), null, testSheet, false);
-    String expectedCss = CssResourceGenerator.makeExpression(logger,
-        new FakeContext(), null, expectedSheet, false);
-    assertEquals(expectedCss, testCss);
+    assertEquals(logger, expectedSheet, testSheet);
+  }
+
+  /**
+   * Runs a test.
+   *
+   * @param testName is used to compute the test and expected resource  
paths.
+   * @param reversible if <code>true</code>, the test will attempt to  
transform
+   *          the expected css into the test css
+   */
+  protected void test(TreeLogger logger, String testName, boolean  
reversible,
+      CssVisitor... visitors) throws UnableToCompleteException {
+    String packagePath =  
getClass().getPackage().getName().replace('.', '/')
+        + "/";
+    URL testUrl = getClass().getClassLoader().getResource(
+        packagePath + testName + "_test.css");
+    assertNotNull("Cauld not find testUrl", testUrl);
+    URL expectedUrl = getClass().getClassLoader().getResource(
+        packagePath + testName + "_expected.css");
+    assertNotNull("Cauld not find testUrl", expectedUrl);
+
+    test(logger, testUrl, expectedUrl, visitors);
+
+    if (reversible) {
+      test(logger, expectedUrl, testUrl, visitors);
+    }
    }
  }

Modified:  
changes/bobv/clientbundle/user/test/com/google/gwt/resources/rg/selectorMerging_expected.css
==============================================================================
---  
changes/bobv/clientbundle/user/test/com/google/gwt/resources/rg/selectorMerging_expected.css
     
(original)
+++  
changes/bobv/clientbundle/user/test/com/google/gwt/resources/rg/selectorMerging_expected.css
     
Tue Mar 17 11:59:31 2009
@@ -19,6 +19,10 @@
    prop: value; foo: bar;
  }

+c {
+  prop: value;
+}
+
  aNonBlocker {
    random: other;
  }

Modified:  
changes/bobv/clientbundle/user/test/com/google/gwt/resources/rg/selectorMerging_test.css
==============================================================================
---  
changes/bobv/clientbundle/user/test/com/google/gwt/resources/rg/selectorMerging_test.css
         
(original)
+++  
changes/bobv/clientbundle/user/test/com/google/gwt/resources/rg/selectorMerging_test.css
         
Tue Mar 17 11:59:31 2009
@@ -15,7 +15,7 @@
   */

   /* Tests merging adjacent rules with identical selectors */
-a {
+a , c {
    prop: value;
  }


--~--~---------~--~----~------------~-------~--~----~
http://groups.google.com/group/Google-Web-Toolkit-Contributors
-~----------~----~----~----~------~----~------~--~---

Reply via email to