You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@shindig.apache.org by jo...@apache.org on 2008/08/15 03:27:23 UTC

svn commit: r686101 - in /incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse: ./ caja/

Author: johnh
Date: Thu Aug 14 18:27:23 2008
New Revision: 686101

URL: http://svn.apache.org/viewvc?rev=686101&view=rev
Log:
Framework for generating a parse tree of HTML and CSS content.

These interfaces are defined in order to cleanly separate parsing logic
from classes that manipulate a given parse tree (HTML or CSS). The parse
tree objects are intended to be only as complex as is needed for the vast
majority of content rewriting manipulation. They provide structure but
no more semantics (validation, CSS resolution, etc.) than that.


Added:
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/GadgetCssDeclaration.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/GadgetCssParser.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/GadgetCssRule.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/GadgetHtmlAttribute.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/GadgetHtmlNode.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/GadgetHtmlParser.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/ParsedCssDeclaration.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/ParsedCssRule.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/ParsedHtmlAttribute.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/ParsedHtmlNode.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/caja/
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/caja/CajaCssParser.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/caja/CajaHtmlParser.java

Added: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/GadgetCssDeclaration.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/GadgetCssDeclaration.java?rev=686101&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/GadgetCssDeclaration.java (added)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/GadgetCssDeclaration.java Thu Aug 14 18:27:23 2008
@@ -0,0 +1,72 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.shindig.gadgets.parse;
+
+/**
+ * Mutable wrapper around a {@code ParsedCssDeclaration}.
+ * Used by rewriting to manipulate parsed gadget CSS, and
+ * to separate parsing from manipulation code.
+ */
+public class GadgetCssDeclaration {
+  private String name;
+  private String value;
+  
+  /**
+   * Construct a mutable attribute out of an immutable parsed one.
+   * @param source Parsed CSS declaration
+   */
+  public GadgetCssDeclaration(ParsedCssDeclaration source) {
+    this.name = source.getName();
+    this.value = source.getValue();
+  }
+  
+  /**
+   * Construct a mutable CSS declaration from a name/value pair.
+   * @param name Name of attribute
+   * @param value Value of attribute
+   */
+  public GadgetCssDeclaration(String name, String value) {
+    this.name = name;
+    this.value = value;
+  }
+  
+  /**
+   * @return Name of the HTML attribute
+   */
+  public String getName() {
+    return name;
+  }
+  
+  /**
+   * @return Value of the HTML attribute
+   */
+  public String getValue() {
+    return value;
+  }
+  
+  /**
+   * Only provide an API for setting value. To set
+   * a new attribute a developer can simply create
+   * a new one. To replace, the developer can delete
+   * the existing one before doing so.
+   * @param value New HTML attribute value.
+   */
+  public void setValue(String value) {
+    this.value = value;
+  }
+}

Added: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/GadgetCssParser.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/GadgetCssParser.java?rev=686101&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/GadgetCssParser.java (added)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/GadgetCssParser.java Thu Aug 14 18:27:23 2008
@@ -0,0 +1,34 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.shindig.gadgets.parse;
+
+import org.apache.shindig.gadgets.GadgetException;
+
+import java.util.List;
+
+/**
+ * Parser for CSS content. Parsing may be done on a fully-formed
+ * CSS block, such as the contents of a CSS file or <style> block.
+ * 
+ * {@see ParsedCssRule} and {@see ParsedCssDeclaration} for additional
+ * parsing requirements and semantics.
+ */
+public interface GadgetCssParser {
+  public List<ParsedCssRule> parse(String css) throws GadgetException;
+  public List<ParsedCssDeclaration> parseInline(String style) throws GadgetException;
+}

Added: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/GadgetCssRule.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/GadgetCssRule.java?rev=686101&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/GadgetCssRule.java (added)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/GadgetCssRule.java Thu Aug 14 18:27:23 2008
@@ -0,0 +1,144 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.shindig.gadgets.parse;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Mutable wrapper around a {@code ParsedCssRule}.
+ * Used by rewriting to manipulate parsed gadget CSS, and
+ * to separate parsing from manipulation code.
+ */
+public class GadgetCssRule {
+  private final List<String> selectors;
+  private final Map<String, String> declarations;
+  
+  /**
+   * Create a new {@code GadgetCssRule} from a {@code ParsedCssRule}
+   * @param source Parsed CSS rule
+   */
+  public GadgetCssRule(ParsedCssRule source) {
+    this();
+    for (String selector : source.getSelectors()) {
+      addSelector(selector, null);
+    }
+    
+    // Last decl with a given key "wins" - duplicates are therefore ignored.
+    for (ParsedCssDeclaration decl : source.getDeclarations()) {
+      setDeclaration(decl.getName(), decl.getValue());
+    }
+  }
+  
+  /**
+   * Create a new, blank rule. At least one selector must be added
+   * for the rule to be valid (and serializable).
+   */
+  public GadgetCssRule() {
+    selectors = new LinkedList<String>();
+    declarations = new HashMap<String, String>();
+  }
+  
+  /**
+   * Adds a new selector after the provided entry. Selector order in a given
+   * rule is significant in CSS.
+   * @param selector Selector to add (will be automatically trimmed)
+   * @param before Selector key after which to add new, null for list end
+   * @return Whether or not the selector was freshly added
+   */
+  public boolean addSelector(String selector, String before) {
+    selector = selector.trim();
+    int selIx = selectors.indexOf(selector);
+    if (selIx >= 0) {
+      return false;
+    }
+    if (before == null) {
+      return selectors.add(selector);
+    }
+    int befIx = selectors.indexOf(before);
+    if (befIx >= 0) {
+      selectors.add(befIx, selector);
+    } else {
+      selectors.add(selector);
+    }
+    return true;
+  }
+  
+  /**
+   * @param selector Selector to remove
+   * @return Whether or not the selector was present and removed
+   */
+  public boolean removeSelector(String selector) {
+    return selectors.remove(selector);
+  }
+
+  /**
+   * @param selector Selector whose presence in the rule to test
+   * @return Whether or not the selector exists in the rule
+   */
+  public boolean hasSelector(String selector) {
+    return selectors.contains(selector);
+  }
+  
+  /**
+   * @return Unmodifiable list of selectors
+   */
+  public List<String> getSelectors() {
+    return Collections.unmodifiableList(selectors);
+  }
+  
+  /**
+   * Add a declaration by key/value. Key is trimmed.
+   * @param key Declaration key, either new or replaced
+   * @param value Declaration value, either new or replaced
+   */
+  public void setDeclaration(String key, String value) {
+    key = key.trim();
+    declarations.put(key, value);
+  }
+
+  /**
+   * @param key Key for the declaration to remove.
+   * @return Whether or not the declaration existed and was removed
+   */
+  public boolean removeDeclaration(String key) {
+    key = key.trim();
+    return declarations.remove(key) != null;
+  }
+  
+  /**
+   * Get a given declaration's value by key.
+   * @param key Key for the declaration
+   * @return Declaration's value, or null if not present
+   */
+  public String getDeclarationValue(String key) {
+    key = key.trim();
+    return declarations.get(key);
+  }
+  
+  /**
+   * @return Unmodifiable set of existing declaration keys.
+   */
+  public Set<String> getDeclarationKeys() {
+    return Collections.unmodifiableSet(declarations.keySet());
+  }
+}

Added: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/GadgetHtmlAttribute.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/GadgetHtmlAttribute.java?rev=686101&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/GadgetHtmlAttribute.java (added)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/GadgetHtmlAttribute.java Thu Aug 14 18:27:23 2008
@@ -0,0 +1,72 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.shindig.gadgets.parse;
+
+/**
+ * Mutable wrapper around a {@code ParsedHtmlAttribute}.
+ * Used by rewriting to manipulate a parsed gadget DOM, and
+ * to separate parsing from manipulation code.
+ */
+public class GadgetHtmlAttribute {
+  private String name;
+  private String value;
+  
+  /**
+   * Construct a mutable attribute out of an immutable parsed one.
+   * @param source Parsed HTML attribute
+   */
+  public GadgetHtmlAttribute(ParsedHtmlAttribute source) {
+    this.name = source.getName();
+    this.value = source.getValue();
+  }
+  
+  /**
+   * Construct a mutable attribute from a name/value pair.
+   * @param name Name of attribute
+   * @param value Value of attribute
+   */
+  public GadgetHtmlAttribute(String name, String value) {
+    this.name = name;
+    this.value = value;
+  }
+  
+  /**
+   * @return Name of the HTML attribute
+   */
+  public String getName() {
+    return name;
+  }
+  
+  /**
+   * @return Value of the HTML attribute
+   */
+  public String getValue() {
+    return value;
+  }
+  
+  /**
+   * Only provide an API for setting value. To set
+   * a new attribute a developer can simply create
+   * a new one. To replace, the developer can delete
+   * the existing one before doing so.
+   * @param value New HTML attribute value.
+   */
+  public void setValue(String value) {
+    this.value = value;
+  }
+}

Added: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/GadgetHtmlNode.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/GadgetHtmlNode.java?rev=686101&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/GadgetHtmlNode.java (added)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/GadgetHtmlNode.java Thu Aug 14 18:27:23 2008
@@ -0,0 +1,287 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.shindig.gadgets.parse;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Mutable wrapper around a {@code ParsedHtmlNode}.
+ * Used by rewriting to manipulate a parsed gadget DOM, and
+ * to separate parsing from manipulation code. Essentially
+ * a lightweight DOM1-style object.
+ */
+public class GadgetHtmlNode {
+  private final NodeType type;
+  private GadgetHtmlNode parentNode;
+  private String tagName;
+  private Map<String, String> attributes;
+  private List<GadgetHtmlNode> children;
+  private String text;
+  
+  private enum NodeType {
+    TAG, TEXT
+  }
+  
+  /**
+   * Construct a mutable HTML node from a parsed one.
+   * @param parsed HTML node object from parser.
+   */
+  public GadgetHtmlNode(ParsedHtmlNode parsed) {
+    if (parsed.getText() == null) {
+      // Tag type
+      type = NodeType.TAG;
+      parentNode = null;
+      tagName = parsed.getTagName();
+      attributes = new HashMap<String, String>();
+      for (ParsedHtmlAttribute attrib : parsed.getAttributes()) {
+        setAttribute(attrib.getName(), attrib.getValue());
+      }
+      children = new LinkedList<GadgetHtmlNode>();
+      for (ParsedHtmlNode node: parsed.getChildren()) {
+        appendChild(new GadgetHtmlNode(node));
+      }
+    } else {
+      type = NodeType.TEXT;
+      setText(parsed.getText());
+    }
+  }
+  
+  /**
+   * Construct a tag-type HTML node.
+   * @param tagName Tag name for new node, must not be null.
+   * @param attributes Name/value pairs for new attributes, or null if none.
+   */
+  public GadgetHtmlNode(String tag, String[][] attribs) {
+    type = NodeType.TAG;
+    tagName = tag;
+    attributes = new HashMap<String, String>();
+    if (attribs != null) {
+      for (String[] attrib : attribs) {
+        if (attrib == null || attrib.length != 2) {
+          throw new UnsupportedOperationException(
+              "Coding error: Invalid GadgetHtmlNode creation");
+        }
+        setAttribute(attrib[0], attrib[1]);
+      }
+    }
+    children = new LinkedList<GadgetHtmlNode>();
+  }
+  
+  /**
+   * Construct a text-type HTML node.
+   * @param text Textual contents of new node.
+   */
+  public GadgetHtmlNode(String text) {
+    type = NodeType.TEXT;
+    setText(text);
+  }
+  
+  /**
+   * @return True if the node is text type
+   */
+  public boolean isText() {
+    return type == NodeType.TEXT;
+  }
+  
+  /**
+   * @return Tag name for the HTML node.
+   */
+  public String getTagName() {
+    validateNodeType(NodeType.TAG);
+    return tagName;
+  }
+  
+  /**
+   * @param newTag New tag name to set for the node
+   * @return True if the tag name was set, false if invalid
+   */
+  public boolean setTagName(String newTag) {
+    validateNodeType(NodeType.TAG);
+    if (tagName != null) {
+      newTag = newTag.trim();
+      if (newTag.matches("[\\w\\-_:]+")) {
+        this.tagName = newTag;
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Retrieve an attribute by key.
+   * @param key Attribute key to look up.
+   * @return Value associated with key, or null if none.
+   */
+  public String getAttributeValue(String key) {
+    validateNodeType(NodeType.TAG);
+    return attributes.get(key);
+  }
+  
+  /**
+   * Remove an attribute by key.
+   * @param key Key for attribute to remove.
+   * @return Whether or not an attribute with that key was removed.
+   */
+  public boolean removeAttribute(String key) {
+    validateNodeType(NodeType.TAG);
+    boolean hasBefore = hasAttribute(key);
+    attributes.remove(key);
+    return hasBefore && !hasAttribute(key);
+  }
+  
+  /**
+   * Set an attribute's key/value.
+   * @param key Attribute key.
+   * @param value Attribute value.
+   * @return Whether or not the set operation succeeded.
+   */
+  public boolean setAttribute(String key, String value) {
+    validateNodeType(NodeType.TAG);
+    String putKey = validateAttributeKey(key);
+    if (putKey == null) {
+      return false;
+    }
+    attributes.put(putKey, value);
+    return true;
+  }
+  
+  /**
+   * @param key Key whose existence to test in the attribute set
+   * @return Whether or not the node has an attribute for the given key
+   */
+  public boolean hasAttribute(String key) {
+    validateNodeType(NodeType.TAG);
+    return attributes.containsKey(key);
+  }
+  
+  /**
+   * @return Immutable set of attribute keys.
+   */
+  public Set<String> getAttributeKeys() {
+    validateNodeType(NodeType.TAG);
+    return Collections.unmodifiableSet(attributes.keySet());
+  }
+  
+  // DOM-like node management helpers
+  /**
+   * Append a new node to this node's children.
+   * @param node New node to append.
+   */
+  public void appendChild(GadgetHtmlNode node) {
+    insertBefore(node, null);
+  }
+  
+  /**
+   * Insert a new node before another given node. If the relative
+   * node is not found or null, insert the new node at the end of
+   * this node's children.
+   * @param node New node to insert.
+   * @param before Node before which to insert {@code node}.
+   */
+  public void insertBefore(GadgetHtmlNode node, GadgetHtmlNode before) {
+    validateNodeType(NodeType.TAG);
+    node.setParentNode(this);
+    if (before == null) {
+      children.add(node);
+      return;
+    }
+    int befIx = children.indexOf(before);
+    if (befIx >= 0) {
+      children.add(befIx, node);
+    } else {
+      children.add(node);
+    }
+  }
+  
+  /**
+   * Remove the given node from the tree.
+   * @param node Node to remove.
+   * @return Whether or not the node was removed.
+   */
+  public boolean removeChild(GadgetHtmlNode node) {
+    validateNodeType(NodeType.TAG);
+    
+    // For good measure, dissociate from parent
+    node.setParentNode(null);
+    return children.remove(node);
+  }
+  
+  /**
+   * Returns this nodes parent, or null if none exists.
+   * @return
+   */
+  public GadgetHtmlNode getParentNode() {
+    return parentNode;
+  }
+  
+  // Internal helper: sets parent for tree-node management
+  private void setParentNode(GadgetHtmlNode parent) {
+    parentNode = parent;
+  }
+  
+  /**
+   * Returns an unmodifiable list of current child nodes.
+   * @return
+   */
+  public List<GadgetHtmlNode> getChildren() {
+    validateNodeType(NodeType.TAG);
+    return Collections.unmodifiableList(children);
+  }
+  
+  /**
+   * @return Text for this node if text-type.
+   */
+  public String getText() {
+    validateNodeType(NodeType.TEXT);
+    return text;
+  }
+  
+  /**
+   * Set new text value for the node.
+   * @param text New text value for the node.
+   */
+  public void setText(String text) {
+    validateNodeType(NodeType.TEXT);
+    this.text = text;
+  }
+  
+  // Helper that cleans up and validates an attribute key
+  private String validateAttributeKey(String key) {
+    if (key == null) {
+      return null;
+    }
+    key = key.trim();
+    if (!key.matches("[\\w\\d_\\-:]+")) {
+      return null;
+    }
+    return key;
+  }
+  
+  // Helper that enforces correct API usage by type
+  private void validateNodeType(NodeType expected) {
+    if (type != expected) {
+      throw new UnsupportedOperationException("Code error: " +
+          "Attempted " + expected + " operation on node of type " + type);
+    }
+  }
+}

Added: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/GadgetHtmlParser.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/GadgetHtmlParser.java?rev=686101&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/GadgetHtmlParser.java (added)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/GadgetHtmlParser.java Thu Aug 14 18:27:23 2008
@@ -0,0 +1,34 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.shindig.gadgets.parse;
+
+import org.apache.shindig.gadgets.GadgetException;
+
+import java.util.List;
+
+/**
+ * Parser for arbitrary HTML content. The content may simply be a
+ * fragment or snippet of HTML rather than a fully-structured Document,
+ * so the interface returns a list of {@code ParsedHtmlNode} objects
+ * rather than a single top-level item.
+ * 
+ * {@see ParsedHtmlNode} for parsing details
+ */
+public interface GadgetHtmlParser {
+  public List<ParsedHtmlNode> parse(String source) throws GadgetException;
+}

Added: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/ParsedCssDeclaration.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/ParsedCssDeclaration.java?rev=686101&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/ParsedCssDeclaration.java (added)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/ParsedCssDeclaration.java Thu Aug 14 18:27:23 2008
@@ -0,0 +1,36 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.shindig.gadgets.parse;
+
+/**
+ * Interface for a single CSS declaration, eg. color: blue; in:
+ * #id {
+ *   color: blue;
+ * }
+ */
+public interface ParsedCssDeclaration {
+  /**
+   * @return Name of the declaration
+   */
+  public String getName();
+  
+  /**
+   * @return Value of the declaration
+   */
+  public String getValue();
+}

Added: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/ParsedCssRule.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/ParsedCssRule.java?rev=686101&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/ParsedCssRule.java (added)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/ParsedCssRule.java Thu Aug 14 18:27:23 2008
@@ -0,0 +1,37 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.shindig.gadgets.parse;
+
+import java.util.List;
+
+/**
+ * Simplified interface for a parsed CSS rule.
+ * 
+ * For rule:
+ * #id1, .class1 {
+ *   color: blue;
+ *   font-size: 10 em;
+ * }
+ * 
+ * Selectors are "#id1" and ".class1", and ParsedCssDeclarations
+ * are name/value "color"/"blue" and "font-size"/"10 em".
+ */
+public interface ParsedCssRule {
+  public List<String> getSelectors();
+  public List<ParsedCssDeclaration> getDeclarations();
+}

Added: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/ParsedHtmlAttribute.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/ParsedHtmlAttribute.java?rev=686101&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/ParsedHtmlAttribute.java (added)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/ParsedHtmlAttribute.java Thu Aug 14 18:27:23 2008
@@ -0,0 +1,33 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.shindig.gadgets.parse;
+
+/**
+ * Simple name/value representation of a parsed HTML attribute.
+ */
+public interface ParsedHtmlAttribute {
+  /**
+   * @return HTML attribute name.
+   */
+  public String getName();
+  
+  /**
+   * @return HTML attribute value.
+   */
+  public String getValue();
+}

Added: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/ParsedHtmlNode.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/ParsedHtmlNode.java?rev=686101&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/ParsedHtmlNode.java (added)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/ParsedHtmlNode.java Thu Aug 14 18:27:23 2008
@@ -0,0 +1,56 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.shindig.gadgets.parse;
+
+import java.util.List;
+
+/**
+ * Simplified interface wrapping a unit of parsed HTML.
+ * Each {@code ParsedHtmlNode} is either text-type or
+ * tag-type. The following snippet of HTML provides an example of both types:
+ * 
+ * &lt;div id="foo"&gt;content&lt;div&gt;
+ * 
+ * This corresponds to a single top-level {@code ParsedHtmlNode}
+ * where {@code getTagName()} returns "div" and has one
+ * {@code ParsedHtmlAttribute} with N/V "id"/"foo", {@code getText()}
+ * is {@code null}, and has one {@code ParsedHtmlNode} child. That
+ * child in turn has {@code getText()} equal to "content", with
+ * all other methods returning {@code null}.
+ */
+public interface ParsedHtmlNode {
+  /**
+   * @return Tag name for an HTML element, or null if text-type.
+   */
+  public String getTagName();
+  
+  /**
+   * @return List of HTML attributes on an element, or null if text-type
+   */
+  public List<ParsedHtmlAttribute> getAttributes();
+  
+  /**
+   * @return List of child nodes of the HTML element, or null if text-type
+   */
+  public List<ParsedHtmlNode> getChildren();
+  
+  /**
+   * @return Unescaped text as contained in an HTML string; null if tag-type
+   */
+  public String getText();
+}

Added: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/caja/CajaCssParser.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/caja/CajaCssParser.java?rev=686101&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/caja/CajaCssParser.java (added)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/caja/CajaCssParser.java Thu Aug 14 18:27:23 2008
@@ -0,0 +1,176 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.shindig.gadgets.parse.caja;
+
+import com.google.caja.lexer.CharProducer;
+import com.google.caja.lexer.CssLexer;
+import com.google.caja.lexer.CssTokenType;
+import com.google.caja.lexer.InputSource;
+import com.google.caja.lexer.ParseException;
+import com.google.caja.lexer.Token;
+import com.google.caja.lexer.TokenConsumer;
+import com.google.caja.lexer.TokenQueue;
+import com.google.caja.parser.css.CssParser;
+import com.google.caja.parser.css.CssTree;
+import com.google.caja.reporting.MessageContext;
+import com.google.caja.reporting.RenderContext;
+import com.google.caja.util.Criterion;
+
+import org.apache.shindig.gadgets.GadgetException;
+import org.apache.shindig.gadgets.parse.GadgetCssParser;
+import org.apache.shindig.gadgets.parse.ParsedCssDeclaration;
+import org.apache.shindig.gadgets.parse.ParsedCssRule;
+
+import java.io.StringReader;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.List;
+
+public class CajaCssParser implements GadgetCssParser {
+
+  public List<ParsedCssRule> parse(String css) throws GadgetException {
+    if (css.matches("\\s*")) {
+      return new ArrayList<ParsedCssRule>(0);
+    }
+    
+    CssParser parser = getParser(css);
+    CssTree.StyleSheet stylesheet = null;
+    
+    try {
+      stylesheet = parser.parseStyleSheet();
+    } catch (ParseException e) {
+      throw new GadgetException(GadgetException.Code.CSS_PARSE_ERROR, e);
+    }
+    
+    ArrayList<ParsedCssRule> rules =
+        new ArrayList<ParsedCssRule>(stylesheet.children().size());
+    for (CssTree node : stylesheet.children()) {
+      if (node instanceof CssTree.RuleSet) {
+        rules.add(new CajaParsedCssRule((CssTree.RuleSet)node));
+      }
+    }
+    
+    return rules;
+  }
+
+  public List<ParsedCssDeclaration> parseInline(String style)
+      throws GadgetException {
+    if (style.matches("\\s*")) {
+      return new ArrayList<ParsedCssDeclaration>();
+    }
+    
+    CssParser parser = getParser(style);
+    CssTree.DeclarationGroup declGroup = null;
+    
+    try {
+      declGroup = parser.parseDeclarationGroup();
+    } catch (ParseException e) {
+      throw new GadgetException(GadgetException.Code.CSS_PARSE_ERROR, e);
+    }
+    
+    List<ParsedCssDeclaration> attributes =
+        new ArrayList<ParsedCssDeclaration>(declGroup.children().size());
+    for (CssTree node : declGroup.children()) {
+      if (node instanceof CssTree.Declaration) {
+        CssTree.Declaration decl = (CssTree.Declaration)node;
+        if (decl.getProperty() != null) {
+          attributes.add(new CajaParsedCssDeclaration(decl));
+        }
+      }
+    }
+    
+    return attributes;
+  }
+  
+  private CssParser getParser(String content) {
+    InputSource source = null;
+    try {
+      source = new InputSource(new URI("http://dummy.com/"));
+    } catch (URISyntaxException e) {
+      // Never happens. Dummy URI needed to satisfy API.
+      // We may want to pass in the gadget URI for auditing
+      // purposes at some point.
+    }
+    CharProducer producer = CharProducer.Factory.create(new StringReader(content), source);
+    CssLexer lexer = new CssLexer(producer);
+    return new CssParser(new TokenQueue<CssTokenType>(
+        lexer,
+        source,
+        new Criterion<Token<CssTokenType>>() {  
+          public boolean accept(Token<CssTokenType> tok) {
+            return tok.type != CssTokenType.COMMENT
+                && tok.type != CssTokenType.SPACE;
+          }
+        }));
+  }
+  
+  private static final String renderCssTreeElement(CssTree elem) {
+    StringBuffer selBuffer = new StringBuffer();
+    TokenConsumer tc = elem.makeRenderer(selBuffer, null);
+    elem.render(new RenderContext(new MessageContext(), tc));
+    return selBuffer.toString();
+  }
+  
+  private static class CajaParsedCssRule implements ParsedCssRule {
+    private final List<ParsedCssDeclaration> attributes;
+    private final List<String> selectors;
+    
+    private CajaParsedCssRule(CssTree.RuleSet ruleSet) {
+      attributes = new ArrayList<ParsedCssDeclaration>();
+      selectors = new ArrayList<String>();
+      
+      for (CssTree child : ruleSet.children()) {
+        if (child instanceof CssTree.Selector) {
+          selectors.add(renderCssTreeElement(child));
+        } else if (child instanceof CssTree.Declaration) {
+          CssTree.Declaration decl = (CssTree.Declaration)child;
+          if (decl.getProperty() != null) {
+            attributes.add(new CajaParsedCssDeclaration(decl));
+          }
+        }
+      }
+    }
+
+    public List<ParsedCssDeclaration> getDeclarations() {
+      return attributes;
+    }
+
+    public List<String> getSelectors() {
+      return selectors;
+    }
+  }
+  
+  private static class CajaParsedCssDeclaration implements ParsedCssDeclaration {
+    private final String key;
+    private final String value;
+    
+    private CajaParsedCssDeclaration(CssTree.Declaration declaration) {
+      key = declaration.getProperty().getPropertyName();
+      value = renderCssTreeElement(declaration.getExpr());
+    }
+    
+    public String getName() {
+      return key;
+    }
+
+    public String getValue() {
+      return value;
+    }
+  }
+}

Added: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/caja/CajaHtmlParser.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/caja/CajaHtmlParser.java?rev=686101&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/caja/CajaHtmlParser.java (added)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/caja/CajaHtmlParser.java Thu Aug 14 18:27:23 2008
@@ -0,0 +1,163 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.shindig.gadgets.parse.caja;
+
+import com.google.caja.lexer.CharProducer;
+import com.google.caja.lexer.HtmlLexer;
+import com.google.caja.lexer.HtmlTokenType;
+import com.google.caja.lexer.InputSource;
+import com.google.caja.lexer.ParseException;
+import com.google.caja.lexer.TokenQueue;
+import com.google.caja.parser.html.DomParser;
+import com.google.caja.parser.html.DomTree;
+import com.google.caja.reporting.MessageQueue;
+import com.google.caja.reporting.SimpleMessageQueue;
+
+import org.apache.shindig.gadgets.GadgetException;
+import org.apache.shindig.gadgets.parse.GadgetHtmlParser;
+import org.apache.shindig.gadgets.parse.ParsedHtmlAttribute;
+import org.apache.shindig.gadgets.parse.ParsedHtmlNode;
+
+import java.io.StringReader;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Caja-based implementation of a {@code GadgetHtmlParser}.
+ */
+public class CajaHtmlParser implements GadgetHtmlParser {
+
+  /** {@inheritDoc */
+  public List<ParsedHtmlNode> parse(String source) throws GadgetException {
+    // Wrap the whole thing in a top-level node to get full contents.
+    DomParser parser = getParser("<html>" + source + "</html>");
+    
+    DomTree domTree = null;
+    try {
+      domTree = parser.parseFragment();
+    } catch (ParseException e) {
+      throw new GadgetException(GadgetException.Code.CSS_PARSE_ERROR, e);
+    }
+    
+    List<ParsedHtmlNode> nodes =
+        new ArrayList<ParsedHtmlNode>(domTree.children().size());
+    for (DomTree child : domTree.children()) {
+      nodes.add(new CajaParsedHtmlNode(child));
+    }
+    return nodes;
+  }
+  
+  public DomParser getParser(String content) {
+    InputSource source = null;
+    try {
+      source = new InputSource(new URI("http://dummy.com/"));
+    } catch (URISyntaxException e) {
+      // Never happens. Dummy URI needed to satisfy API.
+      // We may want to pass in the gadget URI for auditing
+      // purposes at some point.
+    }
+    CharProducer producer = CharProducer.Factory.create(
+        new StringReader(content), source);
+    HtmlLexer lexer = new HtmlLexer(producer);
+    MessageQueue mQueue = new SimpleMessageQueue();
+    return new DomParser(new TokenQueue<HtmlTokenType>(lexer, source), false, mQueue);
+  }
+
+  /**
+   * {@code ParsedHtmlNode} implementation built using Caja parsing primitives.
+   */
+  private static class CajaParsedHtmlNode implements ParsedHtmlNode {
+    private final List<ParsedHtmlAttribute> attributes;
+    private final List<ParsedHtmlNode> children;
+    private final String name;
+    private final String text;
+    
+    private CajaParsedHtmlNode(DomTree elem) {
+      if (elem instanceof DomTree.Tag) {
+        DomTree.Tag tag = (DomTree.Tag)elem;
+        attributes = new ArrayList<ParsedHtmlAttribute>();
+        children = new ArrayList<ParsedHtmlNode>();
+        name = tag.getTagName();
+        text = null;
+        for (DomTree child : elem.children()) {
+          if (child instanceof DomTree.Attrib) {
+            attributes.add(new CajaParsedHtmlAttribute((DomTree.Attrib)child));
+          } else {
+            children.add(new CajaParsedHtmlNode(child));
+          }
+        }
+      } else if (elem instanceof DomTree.Text ||
+                 elem instanceof DomTree.CData) {
+        // DomTree.CData can theoretically occur since it's supported
+        // in HTML5, but the implementation doesn't supply this yet.
+        attributes = null;
+        children = null;
+        name = null;
+        text = ((DomTree.Text)elem).getValue();
+      } else {
+        // This should never happen. The only remaining types are
+        // DomTree.Fragment, which is simply a top-level container
+        // that results from the DomTree.parseFragment() method,
+        // and DomTree.Value, which is always a child of DomTree.Attrib.
+        attributes = null;
+        children = null;
+        name = null;
+        text = null;
+      }
+    }
+    
+    public List<ParsedHtmlAttribute> getAttributes() {
+      return attributes;
+    }
+
+    public List<ParsedHtmlNode> getChildren() {
+      return children;
+    }
+
+    public String getTagName() {
+      return name;
+    }
+
+    public String getText() {
+      return text;
+    }
+  }
+  
+  /**
+   * {@code ParsedHtmlAttribute} built from a Caja DomTree primitive.
+   */
+  private static class CajaParsedHtmlAttribute implements ParsedHtmlAttribute {
+    private final String name;
+    private final String value;
+    
+    private CajaParsedHtmlAttribute(DomTree.Attrib attrib) {
+      name = attrib.getAttribName();
+      value = attrib.getAttribValue();
+    }
+    
+    public String getName() {
+      return name;
+    }
+
+    public String getValue() {
+      return value;
+    }
+  }
+}



Re: svn commit: r686101 - in /incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse: ./ caja/

Posted by John Hjelmstad <fa...@google.com>.
Accidentally committed three CLs in one. I'll revert and submit one-by-one.
In any case, none of this infrastructure links with existing functionality
at this point: it's all new.

On Thu, Aug 14, 2008 at 6:27 PM, <jo...@apache.org> wrote:

> Author: johnh
> Date: Thu Aug 14 18:27:23 2008
> New Revision: 686101
>
> URL: http://svn.apache.org/viewvc?rev=686101&view=rev
> Log:
> Framework for generating a parse tree of HTML and CSS content.
>
> These interfaces are defined in order to cleanly separate parsing logic
> from classes that manipulate a given parse tree (HTML or CSS). The parse
> tree objects are intended to be only as complex as is needed for the vast
> majority of content rewriting manipulation. They provide structure but
> no more semantics (validation, CSS resolution, etc.) than that.
>
>
> Added:
>
>  incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/
>
>  incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/GadgetCssDeclaration.java
>
>  incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/GadgetCssParser.java
>
>  incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/GadgetCssRule.java
>
>  incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/GadgetHtmlAttribute.java
>
>  incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/GadgetHtmlNode.java
>
>  incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/GadgetHtmlParser.java
>
>  incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/ParsedCssDeclaration.java
>
>  incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/ParsedCssRule.java
>
>  incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/ParsedHtmlAttribute.java
>
>  incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/ParsedHtmlNode.java
>
>  incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/caja/
>
>  incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/caja/CajaCssParser.java
>
>  incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/caja/CajaHtmlParser.java
>
> Added:
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/GadgetCssDeclaration.java
> URL:
> http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/GadgetCssDeclaration.java?rev=686101&view=auto
>
> ==============================================================================
> ---
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/GadgetCssDeclaration.java
> (added)
> +++
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/GadgetCssDeclaration.java
> Thu Aug 14 18:27:23 2008
> @@ -0,0 +1,72 @@
> +/**
> + * Licensed to the Apache Software Foundation (ASF) under one
> + * or more contributor license agreements. See the NOTICE file
> + * distributed with this work for additional information
> + * regarding copyright ownership. The ASF licenses this file
> + * to you 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 org.apache.shindig.gadgets.parse;
> +
> +/**
> + * Mutable wrapper around a {@code ParsedCssDeclaration}.
> + * Used by rewriting to manipulate parsed gadget CSS, and
> + * to separate parsing from manipulation code.
> + */
> +public class GadgetCssDeclaration {
> +  private String name;
> +  private String value;
> +
> +  /**
> +   * Construct a mutable attribute out of an immutable parsed one.
> +   * @param source Parsed CSS declaration
> +   */
> +  public GadgetCssDeclaration(ParsedCssDeclaration source) {
> +    this.name = source.getName();
> +    this.value = source.getValue();
> +  }
> +
> +  /**
> +   * Construct a mutable CSS declaration from a name/value pair.
> +   * @param name Name of attribute
> +   * @param value Value of attribute
> +   */
> +  public GadgetCssDeclaration(String name, String value) {
> +    this.name = name;
> +    this.value = value;
> +  }
> +
> +  /**
> +   * @return Name of the HTML attribute
> +   */
> +  public String getName() {
> +    return name;
> +  }
> +
> +  /**
> +   * @return Value of the HTML attribute
> +   */
> +  public String getValue() {
> +    return value;
> +  }
> +
> +  /**
> +   * Only provide an API for setting value. To set
> +   * a new attribute a developer can simply create
> +   * a new one. To replace, the developer can delete
> +   * the existing one before doing so.
> +   * @param value New HTML attribute value.
> +   */
> +  public void setValue(String value) {
> +    this.value = value;
> +  }
> +}
>
> Added:
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/GadgetCssParser.java
> URL:
> http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/GadgetCssParser.java?rev=686101&view=auto
>
> ==============================================================================
> ---
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/GadgetCssParser.java
> (added)
> +++
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/GadgetCssParser.java
> Thu Aug 14 18:27:23 2008
> @@ -0,0 +1,34 @@
> +/**
> + * Licensed to the Apache Software Foundation (ASF) under one
> + * or more contributor license agreements. See the NOTICE file
> + * distributed with this work for additional information
> + * regarding copyright ownership. The ASF licenses this file
> + * to you 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 org.apache.shindig.gadgets.parse;
> +
> +import org.apache.shindig.gadgets.GadgetException;
> +
> +import java.util.List;
> +
> +/**
> + * Parser for CSS content. Parsing may be done on a fully-formed
> + * CSS block, such as the contents of a CSS file or &lt;style&gt; block.
> + *
> + * {@see ParsedCssRule} and {@see ParsedCssDeclaration} for additional
> + * parsing requirements and semantics.
> + */
> +public interface GadgetCssParser {
> +  public List<ParsedCssRule> parse(String css) throws GadgetException;
> +  public List<ParsedCssDeclaration> parseInline(String style) throws
> GadgetException;
> +}
>
> Added:
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/GadgetCssRule.java
> URL:
> http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/GadgetCssRule.java?rev=686101&view=auto
>
> ==============================================================================
> ---
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/GadgetCssRule.java
> (added)
> +++
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/GadgetCssRule.java
> Thu Aug 14 18:27:23 2008
> @@ -0,0 +1,144 @@
> +/**
> + * Licensed to the Apache Software Foundation (ASF) under one
> + * or more contributor license agreements. See the NOTICE file
> + * distributed with this work for additional information
> + * regarding copyright ownership. The ASF licenses this file
> + * to you 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 org.apache.shindig.gadgets.parse;
> +
> +import java.util.Collections;
> +import java.util.HashMap;
> +import java.util.LinkedList;
> +import java.util.List;
> +import java.util.Map;
> +import java.util.Set;
> +
> +/**
> + * Mutable wrapper around a {@code ParsedCssRule}.
> + * Used by rewriting to manipulate parsed gadget CSS, and
> + * to separate parsing from manipulation code.
> + */
> +public class GadgetCssRule {
> +  private final List<String> selectors;
> +  private final Map<String, String> declarations;
> +
> +  /**
> +   * Create a new {@code GadgetCssRule} from a {@code ParsedCssRule}
> +   * @param source Parsed CSS rule
> +   */
> +  public GadgetCssRule(ParsedCssRule source) {
> +    this();
> +    for (String selector : source.getSelectors()) {
> +      addSelector(selector, null);
> +    }
> +
> +    // Last decl with a given key "wins" - duplicates are therefore
> ignored.
> +    for (ParsedCssDeclaration decl : source.getDeclarations()) {
> +      setDeclaration(decl.getName(), decl.getValue());
> +    }
> +  }
> +
> +  /**
> +   * Create a new, blank rule. At least one selector must be added
> +   * for the rule to be valid (and serializable).
> +   */
> +  public GadgetCssRule() {
> +    selectors = new LinkedList<String>();
> +    declarations = new HashMap<String, String>();
> +  }
> +
> +  /**
> +   * Adds a new selector after the provided entry. Selector order in a
> given
> +   * rule is significant in CSS.
> +   * @param selector Selector to add (will be automatically trimmed)
> +   * @param before Selector key after which to add new, null for list end
> +   * @return Whether or not the selector was freshly added
> +   */
> +  public boolean addSelector(String selector, String before) {
> +    selector = selector.trim();
> +    int selIx = selectors.indexOf(selector);
> +    if (selIx >= 0) {
> +      return false;
> +    }
> +    if (before == null) {
> +      return selectors.add(selector);
> +    }
> +    int befIx = selectors.indexOf(before);
> +    if (befIx >= 0) {
> +      selectors.add(befIx, selector);
> +    } else {
> +      selectors.add(selector);
> +    }
> +    return true;
> +  }
> +
> +  /**
> +   * @param selector Selector to remove
> +   * @return Whether or not the selector was present and removed
> +   */
> +  public boolean removeSelector(String selector) {
> +    return selectors.remove(selector);
> +  }
> +
> +  /**
> +   * @param selector Selector whose presence in the rule to test
> +   * @return Whether or not the selector exists in the rule
> +   */
> +  public boolean hasSelector(String selector) {
> +    return selectors.contains(selector);
> +  }
> +
> +  /**
> +   * @return Unmodifiable list of selectors
> +   */
> +  public List<String> getSelectors() {
> +    return Collections.unmodifiableList(selectors);
> +  }
> +
> +  /**
> +   * Add a declaration by key/value. Key is trimmed.
> +   * @param key Declaration key, either new or replaced
> +   * @param value Declaration value, either new or replaced
> +   */
> +  public void setDeclaration(String key, String value) {
> +    key = key.trim();
> +    declarations.put(key, value);
> +  }
> +
> +  /**
> +   * @param key Key for the declaration to remove.
> +   * @return Whether or not the declaration existed and was removed
> +   */
> +  public boolean removeDeclaration(String key) {
> +    key = key.trim();
> +    return declarations.remove(key) != null;
> +  }
> +
> +  /**
> +   * Get a given declaration's value by key.
> +   * @param key Key for the declaration
> +   * @return Declaration's value, or null if not present
> +   */
> +  public String getDeclarationValue(String key) {
> +    key = key.trim();
> +    return declarations.get(key);
> +  }
> +
> +  /**
> +   * @return Unmodifiable set of existing declaration keys.
> +   */
> +  public Set<String> getDeclarationKeys() {
> +    return Collections.unmodifiableSet(declarations.keySet());
> +  }
> +}
>
> Added:
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/GadgetHtmlAttribute.java
> URL:
> http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/GadgetHtmlAttribute.java?rev=686101&view=auto
>
> ==============================================================================
> ---
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/GadgetHtmlAttribute.java
> (added)
> +++
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/GadgetHtmlAttribute.java
> Thu Aug 14 18:27:23 2008
> @@ -0,0 +1,72 @@
> +/**
> + * Licensed to the Apache Software Foundation (ASF) under one
> + * or more contributor license agreements. See the NOTICE file
> + * distributed with this work for additional information
> + * regarding copyright ownership. The ASF licenses this file
> + * to you 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 org.apache.shindig.gadgets.parse;
> +
> +/**
> + * Mutable wrapper around a {@code ParsedHtmlAttribute}.
> + * Used by rewriting to manipulate a parsed gadget DOM, and
> + * to separate parsing from manipulation code.
> + */
> +public class GadgetHtmlAttribute {
> +  private String name;
> +  private String value;
> +
> +  /**
> +   * Construct a mutable attribute out of an immutable parsed one.
> +   * @param source Parsed HTML attribute
> +   */
> +  public GadgetHtmlAttribute(ParsedHtmlAttribute source) {
> +    this.name = source.getName();
> +    this.value = source.getValue();
> +  }
> +
> +  /**
> +   * Construct a mutable attribute from a name/value pair.
> +   * @param name Name of attribute
> +   * @param value Value of attribute
> +   */
> +  public GadgetHtmlAttribute(String name, String value) {
> +    this.name = name;
> +    this.value = value;
> +  }
> +
> +  /**
> +   * @return Name of the HTML attribute
> +   */
> +  public String getName() {
> +    return name;
> +  }
> +
> +  /**
> +   * @return Value of the HTML attribute
> +   */
> +  public String getValue() {
> +    return value;
> +  }
> +
> +  /**
> +   * Only provide an API for setting value. To set
> +   * a new attribute a developer can simply create
> +   * a new one. To replace, the developer can delete
> +   * the existing one before doing so.
> +   * @param value New HTML attribute value.
> +   */
> +  public void setValue(String value) {
> +    this.value = value;
> +  }
> +}
>
> Added:
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/GadgetHtmlNode.java
> URL:
> http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/GadgetHtmlNode.java?rev=686101&view=auto
>
> ==============================================================================
> ---
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/GadgetHtmlNode.java
> (added)
> +++
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/GadgetHtmlNode.java
> Thu Aug 14 18:27:23 2008
> @@ -0,0 +1,287 @@
> +/**
> + * Licensed to the Apache Software Foundation (ASF) under one
> + * or more contributor license agreements. See the NOTICE file
> + * distributed with this work for additional information
> + * regarding copyright ownership. The ASF licenses this file
> + * to you 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 org.apache.shindig.gadgets.parse;
> +
> +import java.util.Collections;
> +import java.util.HashMap;
> +import java.util.LinkedList;
> +import java.util.List;
> +import java.util.Map;
> +import java.util.Set;
> +
> +/**
> + * Mutable wrapper around a {@code ParsedHtmlNode}.
> + * Used by rewriting to manipulate a parsed gadget DOM, and
> + * to separate parsing from manipulation code. Essentially
> + * a lightweight DOM1-style object.
> + */
> +public class GadgetHtmlNode {
> +  private final NodeType type;
> +  private GadgetHtmlNode parentNode;
> +  private String tagName;
> +  private Map<String, String> attributes;
> +  private List<GadgetHtmlNode> children;
> +  private String text;
> +
> +  private enum NodeType {
> +    TAG, TEXT
> +  }
> +
> +  /**
> +   * Construct a mutable HTML node from a parsed one.
> +   * @param parsed HTML node object from parser.
> +   */
> +  public GadgetHtmlNode(ParsedHtmlNode parsed) {
> +    if (parsed.getText() == null) {
> +      // Tag type
> +      type = NodeType.TAG;
> +      parentNode = null;
> +      tagName = parsed.getTagName();
> +      attributes = new HashMap<String, String>();
> +      for (ParsedHtmlAttribute attrib : parsed.getAttributes()) {
> +        setAttribute(attrib.getName(), attrib.getValue());
> +      }
> +      children = new LinkedList<GadgetHtmlNode>();
> +      for (ParsedHtmlNode node: parsed.getChildren()) {
> +        appendChild(new GadgetHtmlNode(node));
> +      }
> +    } else {
> +      type = NodeType.TEXT;
> +      setText(parsed.getText());
> +    }
> +  }
> +
> +  /**
> +   * Construct a tag-type HTML node.
> +   * @param tagName Tag name for new node, must not be null.
> +   * @param attributes Name/value pairs for new attributes, or null if
> none.
> +   */
> +  public GadgetHtmlNode(String tag, String[][] attribs) {
> +    type = NodeType.TAG;
> +    tagName = tag;
> +    attributes = new HashMap<String, String>();
> +    if (attribs != null) {
> +      for (String[] attrib : attribs) {
> +        if (attrib == null || attrib.length != 2) {
> +          throw new UnsupportedOperationException(
> +              "Coding error: Invalid GadgetHtmlNode creation");
> +        }
> +        setAttribute(attrib[0], attrib[1]);
> +      }
> +    }
> +    children = new LinkedList<GadgetHtmlNode>();
> +  }
> +
> +  /**
> +   * Construct a text-type HTML node.
> +   * @param text Textual contents of new node.
> +   */
> +  public GadgetHtmlNode(String text) {
> +    type = NodeType.TEXT;
> +    setText(text);
> +  }
> +
> +  /**
> +   * @return True if the node is text type
> +   */
> +  public boolean isText() {
> +    return type == NodeType.TEXT;
> +  }
> +
> +  /**
> +   * @return Tag name for the HTML node.
> +   */
> +  public String getTagName() {
> +    validateNodeType(NodeType.TAG);
> +    return tagName;
> +  }
> +
> +  /**
> +   * @param newTag New tag name to set for the node
> +   * @return True if the tag name was set, false if invalid
> +   */
> +  public boolean setTagName(String newTag) {
> +    validateNodeType(NodeType.TAG);
> +    if (tagName != null) {
> +      newTag = newTag.trim();
> +      if (newTag.matches("[\\w\\-_:]+")) {
> +        this.tagName = newTag;
> +        return true;
> +      }
> +    }
> +    return false;
> +  }
> +
> +  /**
> +   * Retrieve an attribute by key.
> +   * @param key Attribute key to look up.
> +   * @return Value associated with key, or null if none.
> +   */
> +  public String getAttributeValue(String key) {
> +    validateNodeType(NodeType.TAG);
> +    return attributes.get(key);
> +  }
> +
> +  /**
> +   * Remove an attribute by key.
> +   * @param key Key for attribute to remove.
> +   * @return Whether or not an attribute with that key was removed.
> +   */
> +  public boolean removeAttribute(String key) {
> +    validateNodeType(NodeType.TAG);
> +    boolean hasBefore = hasAttribute(key);
> +    attributes.remove(key);
> +    return hasBefore && !hasAttribute(key);
> +  }
> +
> +  /**
> +   * Set an attribute's key/value.
> +   * @param key Attribute key.
> +   * @param value Attribute value.
> +   * @return Whether or not the set operation succeeded.
> +   */
> +  public boolean setAttribute(String key, String value) {
> +    validateNodeType(NodeType.TAG);
> +    String putKey = validateAttributeKey(key);
> +    if (putKey == null) {
> +      return false;
> +    }
> +    attributes.put(putKey, value);
> +    return true;
> +  }
> +
> +  /**
> +   * @param key Key whose existence to test in the attribute set
> +   * @return Whether or not the node has an attribute for the given key
> +   */
> +  public boolean hasAttribute(String key) {
> +    validateNodeType(NodeType.TAG);
> +    return attributes.containsKey(key);
> +  }
> +
> +  /**
> +   * @return Immutable set of attribute keys.
> +   */
> +  public Set<String> getAttributeKeys() {
> +    validateNodeType(NodeType.TAG);
> +    return Collections.unmodifiableSet(attributes.keySet());
> +  }
> +
> +  // DOM-like node management helpers
> +  /**
> +   * Append a new node to this node's children.
> +   * @param node New node to append.
> +   */
> +  public void appendChild(GadgetHtmlNode node) {
> +    insertBefore(node, null);
> +  }
> +
> +  /**
> +   * Insert a new node before another given node. If the relative
> +   * node is not found or null, insert the new node at the end of
> +   * this node's children.
> +   * @param node New node to insert.
> +   * @param before Node before which to insert {@code node}.
> +   */
> +  public void insertBefore(GadgetHtmlNode node, GadgetHtmlNode before) {
> +    validateNodeType(NodeType.TAG);
> +    node.setParentNode(this);
> +    if (before == null) {
> +      children.add(node);
> +      return;
> +    }
> +    int befIx = children.indexOf(before);
> +    if (befIx >= 0) {
> +      children.add(befIx, node);
> +    } else {
> +      children.add(node);
> +    }
> +  }
> +
> +  /**
> +   * Remove the given node from the tree.
> +   * @param node Node to remove.
> +   * @return Whether or not the node was removed.
> +   */
> +  public boolean removeChild(GadgetHtmlNode node) {
> +    validateNodeType(NodeType.TAG);
> +
> +    // For good measure, dissociate from parent
> +    node.setParentNode(null);
> +    return children.remove(node);
> +  }
> +
> +  /**
> +   * Returns this nodes parent, or null if none exists.
> +   * @return
> +   */
> +  public GadgetHtmlNode getParentNode() {
> +    return parentNode;
> +  }
> +
> +  // Internal helper: sets parent for tree-node management
> +  private void setParentNode(GadgetHtmlNode parent) {
> +    parentNode = parent;
> +  }
> +
> +  /**
> +   * Returns an unmodifiable list of current child nodes.
> +   * @return
> +   */
> +  public List<GadgetHtmlNode> getChildren() {
> +    validateNodeType(NodeType.TAG);
> +    return Collections.unmodifiableList(children);
> +  }
> +
> +  /**
> +   * @return Text for this node if text-type.
> +   */
> +  public String getText() {
> +    validateNodeType(NodeType.TEXT);
> +    return text;
> +  }
> +
> +  /**
> +   * Set new text value for the node.
> +   * @param text New text value for the node.
> +   */
> +  public void setText(String text) {
> +    validateNodeType(NodeType.TEXT);
> +    this.text = text;
> +  }
> +
> +  // Helper that cleans up and validates an attribute key
> +  private String validateAttributeKey(String key) {
> +    if (key == null) {
> +      return null;
> +    }
> +    key = key.trim();
> +    if (!key.matches("[\\w\\d_\\-:]+")) {
> +      return null;
> +    }
> +    return key;
> +  }
> +
> +  // Helper that enforces correct API usage by type
> +  private void validateNodeType(NodeType expected) {
> +    if (type != expected) {
> +      throw new UnsupportedOperationException("Code error: " +
> +          "Attempted " + expected + " operation on node of type " + type);
> +    }
> +  }
> +}
>
> Added:
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/GadgetHtmlParser.java
> URL:
> http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/GadgetHtmlParser.java?rev=686101&view=auto
>
> ==============================================================================
> ---
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/GadgetHtmlParser.java
> (added)
> +++
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/GadgetHtmlParser.java
> Thu Aug 14 18:27:23 2008
> @@ -0,0 +1,34 @@
> +/**
> + * Licensed to the Apache Software Foundation (ASF) under one
> + * or more contributor license agreements. See the NOTICE file
> + * distributed with this work for additional information
> + * regarding copyright ownership. The ASF licenses this file
> + * to you 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 org.apache.shindig.gadgets.parse;
> +
> +import org.apache.shindig.gadgets.GadgetException;
> +
> +import java.util.List;
> +
> +/**
> + * Parser for arbitrary HTML content. The content may simply be a
> + * fragment or snippet of HTML rather than a fully-structured Document,
> + * so the interface returns a list of {@code ParsedHtmlNode} objects
> + * rather than a single top-level item.
> + *
> + * {@see ParsedHtmlNode} for parsing details
> + */
> +public interface GadgetHtmlParser {
> +  public List<ParsedHtmlNode> parse(String source) throws GadgetException;
> +}
>
> Added:
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/ParsedCssDeclaration.java
> URL:
> http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/ParsedCssDeclaration.java?rev=686101&view=auto
>
> ==============================================================================
> ---
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/ParsedCssDeclaration.java
> (added)
> +++
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/ParsedCssDeclaration.java
> Thu Aug 14 18:27:23 2008
> @@ -0,0 +1,36 @@
> +/**
> + * Licensed to the Apache Software Foundation (ASF) under one
> + * or more contributor license agreements. See the NOTICE file
> + * distributed with this work for additional information
> + * regarding copyright ownership. The ASF licenses this file
> + * to you 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 org.apache.shindig.gadgets.parse;
> +
> +/**
> + * Interface for a single CSS declaration, eg. color: blue; in:
> + * #id {
> + *   color: blue;
> + * }
> + */
> +public interface ParsedCssDeclaration {
> +  /**
> +   * @return Name of the declaration
> +   */
> +  public String getName();
> +
> +  /**
> +   * @return Value of the declaration
> +   */
> +  public String getValue();
> +}
>
> Added:
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/ParsedCssRule.java
> URL:
> http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/ParsedCssRule.java?rev=686101&view=auto
>
> ==============================================================================
> ---
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/ParsedCssRule.java
> (added)
> +++
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/ParsedCssRule.java
> Thu Aug 14 18:27:23 2008
> @@ -0,0 +1,37 @@
> +/**
> + * Licensed to the Apache Software Foundation (ASF) under one
> + * or more contributor license agreements. See the NOTICE file
> + * distributed with this work for additional information
> + * regarding copyright ownership. The ASF licenses this file
> + * to you 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 org.apache.shindig.gadgets.parse;
> +
> +import java.util.List;
> +
> +/**
> + * Simplified interface for a parsed CSS rule.
> + *
> + * For rule:
> + * #id1, .class1 {
> + *   color: blue;
> + *   font-size: 10 em;
> + * }
> + *
> + * Selectors are "#id1" and ".class1", and ParsedCssDeclarations
> + * are name/value "color"/"blue" and "font-size"/"10 em".
> + */
> +public interface ParsedCssRule {
> +  public List<String> getSelectors();
> +  public List<ParsedCssDeclaration> getDeclarations();
> +}
>
> Added:
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/ParsedHtmlAttribute.java
> URL:
> http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/ParsedHtmlAttribute.java?rev=686101&view=auto
>
> ==============================================================================
> ---
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/ParsedHtmlAttribute.java
> (added)
> +++
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/ParsedHtmlAttribute.java
> Thu Aug 14 18:27:23 2008
> @@ -0,0 +1,33 @@
> +/**
> + * Licensed to the Apache Software Foundation (ASF) under one
> + * or more contributor license agreements. See the NOTICE file
> + * distributed with this work for additional information
> + * regarding copyright ownership. The ASF licenses this file
> + * to you 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 org.apache.shindig.gadgets.parse;
> +
> +/**
> + * Simple name/value representation of a parsed HTML attribute.
> + */
> +public interface ParsedHtmlAttribute {
> +  /**
> +   * @return HTML attribute name.
> +   */
> +  public String getName();
> +
> +  /**
> +   * @return HTML attribute value.
> +   */
> +  public String getValue();
> +}
>
> Added:
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/ParsedHtmlNode.java
> URL:
> http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/ParsedHtmlNode.java?rev=686101&view=auto
>
> ==============================================================================
> ---
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/ParsedHtmlNode.java
> (added)
> +++
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/ParsedHtmlNode.java
> Thu Aug 14 18:27:23 2008
> @@ -0,0 +1,56 @@
> +/**
> + * Licensed to the Apache Software Foundation (ASF) under one
> + * or more contributor license agreements. See the NOTICE file
> + * distributed with this work for additional information
> + * regarding copyright ownership. The ASF licenses this file
> + * to you 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 org.apache.shindig.gadgets.parse;
> +
> +import java.util.List;
> +
> +/**
> + * Simplified interface wrapping a unit of parsed HTML.
> + * Each {@code ParsedHtmlNode} is either text-type or
> + * tag-type. The following snippet of HTML provides an example of both
> types:
> + *
> + * &lt;div id="foo"&gt;content&lt;div&gt;
> + *
> + * This corresponds to a single top-level {@code ParsedHtmlNode}
> + * where {@code getTagName()} returns "div" and has one
> + * {@code ParsedHtmlAttribute} with N/V "id"/"foo", {@code getText()}
> + * is {@code null}, and has one {@code ParsedHtmlNode} child. That
> + * child in turn has {@code getText()} equal to "content", with
> + * all other methods returning {@code null}.
> + */
> +public interface ParsedHtmlNode {
> +  /**
> +   * @return Tag name for an HTML element, or null if text-type.
> +   */
> +  public String getTagName();
> +
> +  /**
> +   * @return List of HTML attributes on an element, or null if text-type
> +   */
> +  public List<ParsedHtmlAttribute> getAttributes();
> +
> +  /**
> +   * @return List of child nodes of the HTML element, or null if text-type
> +   */
> +  public List<ParsedHtmlNode> getChildren();
> +
> +  /**
> +   * @return Unescaped text as contained in an HTML string; null if
> tag-type
> +   */
> +  public String getText();
> +}
>
> Added:
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/caja/CajaCssParser.java
> URL:
> http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/caja/CajaCssParser.java?rev=686101&view=auto
>
> ==============================================================================
> ---
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/caja/CajaCssParser.java
> (added)
> +++
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/caja/CajaCssParser.java
> Thu Aug 14 18:27:23 2008
> @@ -0,0 +1,176 @@
> +/**
> + * Licensed to the Apache Software Foundation (ASF) under one
> + * or more contributor license agreements. See the NOTICE file
> + * distributed with this work for additional information
> + * regarding copyright ownership. The ASF licenses this file
> + * to you 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 org.apache.shindig.gadgets.parse.caja;
> +
> +import com.google.caja.lexer.CharProducer;
> +import com.google.caja.lexer.CssLexer;
> +import com.google.caja.lexer.CssTokenType;
> +import com.google.caja.lexer.InputSource;
> +import com.google.caja.lexer.ParseException;
> +import com.google.caja.lexer.Token;
> +import com.google.caja.lexer.TokenConsumer;
> +import com.google.caja.lexer.TokenQueue;
> +import com.google.caja.parser.css.CssParser;
> +import com.google.caja.parser.css.CssTree;
> +import com.google.caja.reporting.MessageContext;
> +import com.google.caja.reporting.RenderContext;
> +import com.google.caja.util.Criterion;
> +
> +import org.apache.shindig.gadgets.GadgetException;
> +import org.apache.shindig.gadgets.parse.GadgetCssParser;
> +import org.apache.shindig.gadgets.parse.ParsedCssDeclaration;
> +import org.apache.shindig.gadgets.parse.ParsedCssRule;
> +
> +import java.io.StringReader;
> +import java.net.URI;
> +import java.net.URISyntaxException;
> +import java.util.ArrayList;
> +import java.util.List;
> +
> +public class CajaCssParser implements GadgetCssParser {
> +
> +  public List<ParsedCssRule> parse(String css) throws GadgetException {
> +    if (css.matches("\\s*")) {
> +      return new ArrayList<ParsedCssRule>(0);
> +    }
> +
> +    CssParser parser = getParser(css);
> +    CssTree.StyleSheet stylesheet = null;
> +
> +    try {
> +      stylesheet = parser.parseStyleSheet();
> +    } catch (ParseException e) {
> +      throw new GadgetException(GadgetException.Code.CSS_PARSE_ERROR, e);
> +    }
> +
> +    ArrayList<ParsedCssRule> rules =
> +        new ArrayList<ParsedCssRule>(stylesheet.children().size());
> +    for (CssTree node : stylesheet.children()) {
> +      if (node instanceof CssTree.RuleSet) {
> +        rules.add(new CajaParsedCssRule((CssTree.RuleSet)node));
> +      }
> +    }
> +
> +    return rules;
> +  }
> +
> +  public List<ParsedCssDeclaration> parseInline(String style)
> +      throws GadgetException {
> +    if (style.matches("\\s*")) {
> +      return new ArrayList<ParsedCssDeclaration>();
> +    }
> +
> +    CssParser parser = getParser(style);
> +    CssTree.DeclarationGroup declGroup = null;
> +
> +    try {
> +      declGroup = parser.parseDeclarationGroup();
> +    } catch (ParseException e) {
> +      throw new GadgetException(GadgetException.Code.CSS_PARSE_ERROR, e);
> +    }
> +
> +    List<ParsedCssDeclaration> attributes =
> +        new ArrayList<ParsedCssDeclaration>(declGroup.children().size());
> +    for (CssTree node : declGroup.children()) {
> +      if (node instanceof CssTree.Declaration) {
> +        CssTree.Declaration decl = (CssTree.Declaration)node;
> +        if (decl.getProperty() != null) {
> +          attributes.add(new CajaParsedCssDeclaration(decl));
> +        }
> +      }
> +    }
> +
> +    return attributes;
> +  }
> +
> +  private CssParser getParser(String content) {
> +    InputSource source = null;
> +    try {
> +      source = new InputSource(new URI("http://dummy.com/"));
> +    } catch (URISyntaxException e) {
> +      // Never happens. Dummy URI needed to satisfy API.
> +      // We may want to pass in the gadget URI for auditing
> +      // purposes at some point.
> +    }
> +    CharProducer producer = CharProducer.Factory.create(new
> StringReader(content), source);
> +    CssLexer lexer = new CssLexer(producer);
> +    return new CssParser(new TokenQueue<CssTokenType>(
> +        lexer,
> +        source,
> +        new Criterion<Token<CssTokenType>>() {
> +          public boolean accept(Token<CssTokenType> tok) {
> +            return tok.type != CssTokenType.COMMENT
> +                && tok.type != CssTokenType.SPACE;
> +          }
> +        }));
> +  }
> +
> +  private static final String renderCssTreeElement(CssTree elem) {
> +    StringBuffer selBuffer = new StringBuffer();
> +    TokenConsumer tc = elem.makeRenderer(selBuffer, null);
> +    elem.render(new RenderContext(new MessageContext(), tc));
> +    return selBuffer.toString();
> +  }
> +
> +  private static class CajaParsedCssRule implements ParsedCssRule {
> +    private final List<ParsedCssDeclaration> attributes;
> +    private final List<String> selectors;
> +
> +    private CajaParsedCssRule(CssTree.RuleSet ruleSet) {
> +      attributes = new ArrayList<ParsedCssDeclaration>();
> +      selectors = new ArrayList<String>();
> +
> +      for (CssTree child : ruleSet.children()) {
> +        if (child instanceof CssTree.Selector) {
> +          selectors.add(renderCssTreeElement(child));
> +        } else if (child instanceof CssTree.Declaration) {
> +          CssTree.Declaration decl = (CssTree.Declaration)child;
> +          if (decl.getProperty() != null) {
> +            attributes.add(new CajaParsedCssDeclaration(decl));
> +          }
> +        }
> +      }
> +    }
> +
> +    public List<ParsedCssDeclaration> getDeclarations() {
> +      return attributes;
> +    }
> +
> +    public List<String> getSelectors() {
> +      return selectors;
> +    }
> +  }
> +
> +  private static class CajaParsedCssDeclaration implements
> ParsedCssDeclaration {
> +    private final String key;
> +    private final String value;
> +
> +    private CajaParsedCssDeclaration(CssTree.Declaration declaration) {
> +      key = declaration.getProperty().getPropertyName();
> +      value = renderCssTreeElement(declaration.getExpr());
> +    }
> +
> +    public String getName() {
> +      return key;
> +    }
> +
> +    public String getValue() {
> +      return value;
> +    }
> +  }
> +}
>
> Added:
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/caja/CajaHtmlParser.java
> URL:
> http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/caja/CajaHtmlParser.java?rev=686101&view=auto
>
> ==============================================================================
> ---
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/caja/CajaHtmlParser.java
> (added)
> +++
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/caja/CajaHtmlParser.java
> Thu Aug 14 18:27:23 2008
> @@ -0,0 +1,163 @@
> +/**
> + * Licensed to the Apache Software Foundation (ASF) under one
> + * or more contributor license agreements. See the NOTICE file
> + * distributed with this work for additional information
> + * regarding copyright ownership. The ASF licenses this file
> + * to you 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 org.apache.shindig.gadgets.parse.caja;
> +
> +import com.google.caja.lexer.CharProducer;
> +import com.google.caja.lexer.HtmlLexer;
> +import com.google.caja.lexer.HtmlTokenType;
> +import com.google.caja.lexer.InputSource;
> +import com.google.caja.lexer.ParseException;
> +import com.google.caja.lexer.TokenQueue;
> +import com.google.caja.parser.html.DomParser;
> +import com.google.caja.parser.html.DomTree;
> +import com.google.caja.reporting.MessageQueue;
> +import com.google.caja.reporting.SimpleMessageQueue;
> +
> +import org.apache.shindig.gadgets.GadgetException;
> +import org.apache.shindig.gadgets.parse.GadgetHtmlParser;
> +import org.apache.shindig.gadgets.parse.ParsedHtmlAttribute;
> +import org.apache.shindig.gadgets.parse.ParsedHtmlNode;
> +
> +import java.io.StringReader;
> +import java.net.URI;
> +import java.net.URISyntaxException;
> +import java.util.ArrayList;
> +import java.util.List;
> +
> +/**
> + * Caja-based implementation of a {@code GadgetHtmlParser}.
> + */
> +public class CajaHtmlParser implements GadgetHtmlParser {
> +
> +  /** {@inheritDoc */
> +  public List<ParsedHtmlNode> parse(String source) throws GadgetException
> {
> +    // Wrap the whole thing in a top-level node to get full contents.
> +    DomParser parser = getParser("<html>" + source + "</html>");
> +
> +    DomTree domTree = null;
> +    try {
> +      domTree = parser.parseFragment();
> +    } catch (ParseException e) {
> +      throw new GadgetException(GadgetException.Code.CSS_PARSE_ERROR, e);
> +    }
> +
> +    List<ParsedHtmlNode> nodes =
> +        new ArrayList<ParsedHtmlNode>(domTree.children().size());
> +    for (DomTree child : domTree.children()) {
> +      nodes.add(new CajaParsedHtmlNode(child));
> +    }
> +    return nodes;
> +  }
> +
> +  public DomParser getParser(String content) {
> +    InputSource source = null;
> +    try {
> +      source = new InputSource(new URI("http://dummy.com/"));
> +    } catch (URISyntaxException e) {
> +      // Never happens. Dummy URI needed to satisfy API.
> +      // We may want to pass in the gadget URI for auditing
> +      // purposes at some point.
> +    }
> +    CharProducer producer = CharProducer.Factory.create(
> +        new StringReader(content), source);
> +    HtmlLexer lexer = new HtmlLexer(producer);
> +    MessageQueue mQueue = new SimpleMessageQueue();
> +    return new DomParser(new TokenQueue<HtmlTokenType>(lexer, source),
> false, mQueue);
> +  }
> +
> +  /**
> +   * {@code ParsedHtmlNode} implementation built using Caja parsing
> primitives.
> +   */
> +  private static class CajaParsedHtmlNode implements ParsedHtmlNode {
> +    private final List<ParsedHtmlAttribute> attributes;
> +    private final List<ParsedHtmlNode> children;
> +    private final String name;
> +    private final String text;
> +
> +    private CajaParsedHtmlNode(DomTree elem) {
> +      if (elem instanceof DomTree.Tag) {
> +        DomTree.Tag tag = (DomTree.Tag)elem;
> +        attributes = new ArrayList<ParsedHtmlAttribute>();
> +        children = new ArrayList<ParsedHtmlNode>();
> +        name = tag.getTagName();
> +        text = null;
> +        for (DomTree child : elem.children()) {
> +          if (child instanceof DomTree.Attrib) {
> +            attributes.add(new
> CajaParsedHtmlAttribute((DomTree.Attrib)child));
> +          } else {
> +            children.add(new CajaParsedHtmlNode(child));
> +          }
> +        }
> +      } else if (elem instanceof DomTree.Text ||
> +                 elem instanceof DomTree.CData) {
> +        // DomTree.CData can theoretically occur since it's supported
> +        // in HTML5, but the implementation doesn't supply this yet.
> +        attributes = null;
> +        children = null;
> +        name = null;
> +        text = ((DomTree.Text)elem).getValue();
> +      } else {
> +        // This should never happen. The only remaining types are
> +        // DomTree.Fragment, which is simply a top-level container
> +        // that results from the DomTree.parseFragment() method,
> +        // and DomTree.Value, which is always a child of DomTree.Attrib.
> +        attributes = null;
> +        children = null;
> +        name = null;
> +        text = null;
> +      }
> +    }
> +
> +    public List<ParsedHtmlAttribute> getAttributes() {
> +      return attributes;
> +    }
> +
> +    public List<ParsedHtmlNode> getChildren() {
> +      return children;
> +    }
> +
> +    public String getTagName() {
> +      return name;
> +    }
> +
> +    public String getText() {
> +      return text;
> +    }
> +  }
> +
> +  /**
> +   * {@code ParsedHtmlAttribute} built from a Caja DomTree primitive.
> +   */
> +  private static class CajaParsedHtmlAttribute implements
> ParsedHtmlAttribute {
> +    private final String name;
> +    private final String value;
> +
> +    private CajaParsedHtmlAttribute(DomTree.Attrib attrib) {
> +      name = attrib.getAttribName();
> +      value = attrib.getAttribValue();
> +    }
> +
> +    public String getName() {
> +      return name;
> +    }
> +
> +    public String getValue() {
> +      return value;
> +    }
> +  }
> +}
>
>
>