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, <[EMAIL PROTECTED]> 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 [EMAIL PROTECTED] 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. > + * > + * [EMAIL PROTECTED] ParsedCssRule} and [EMAIL PROTECTED] > 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 [EMAIL PROTECTED] 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 [EMAIL PROTECTED] GadgetCssRule} from a [EMAIL PROTECTED] > 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 [EMAIL PROTECTED] 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 [EMAIL PROTECTED] 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 [EMAIL PROTECTED] 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 [EMAIL PROTECTED] ParsedHtmlNode} > objects > + * rather than a single top-level item. > + * > + * [EMAIL PROTECTED] 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 [EMAIL PROTECTED] ParsedHtmlNode} is either text-type or > + * tag-type. The following snippet of HTML provides an example of both > types: > + * > + * <div id="foo">content<div> > + * > + * This corresponds to a single top-level [EMAIL PROTECTED] ParsedHtmlNode} > + * where [EMAIL PROTECTED] getTagName()} returns "div" and has one > + * [EMAIL PROTECTED] ParsedHtmlAttribute} with N/V "id"/"foo", [EMAIL > PROTECTED] getText()} > + * is [EMAIL PROTECTED] null}, and has one [EMAIL PROTECTED] ParsedHtmlNode} > child. That > + * child in turn has [EMAIL PROTECTED] getText()} equal to "content", with > + * all other methods returning [EMAIL PROTECTED] 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 [EMAIL PROTECTED] GadgetHtmlParser}. > + */ > +public class CajaHtmlParser implements GadgetHtmlParser { > + > + /** [EMAIL PROTECTED] */ > + 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); > + } > + > + /** > + * [EMAIL PROTECTED] 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; > + } > + } > + > + /** > + * [EMAIL PROTECTED] 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; > + } > + } > +} > > >

