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/09/05 23:48:21 UTC

svn commit: r692560 - in /incubator/shindig/trunk/java/gadgets/src: main/java/org/apache/shindig/gadgets/ main/java/org/apache/shindig/gadgets/http/ main/java/org/apache/shindig/gadgets/rewrite/ main/java/org/apache/shindig/gadgets/rewrite/lexer/ test/...

Author: johnh
Date: Fri Sep  5 14:48:21 2008
New Revision: 692560

URL: http://svn.apache.org/viewvc?rev=692560&view=rev
Log:
Changing HttpResponse ContentRewriter API to take a MutableContent, and implementing HttpResponse rewriting by the new rewriter corps. I've used MutableContent for the moment simply because it is available and allows me to port over text/css content rewriting without augmenting the Parse Tree APIs to accommodate CSS content in symbolic/parsed form, which in turn would require plumbing changes to inject a CssParser in various places. Per discussion on shindig-dev@, this parameter may change to a generic parse tree once that occurs or some other CSS (and JS) content manipulation strategy is devised. At that time, the Gadget rewriting API will probably involve passing in Context and a new intermediary representation rather than just Gadget.

* Renamed MutableHtmlContent to MutableContent. It might not represent HTML.
* Implemented Rewriters' HttpResponse rewriting methods.
* Updated plumbing to use these new methods.

Next up, cleaning AbstractHttpCache and continuing the API discussion.


Added:
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/MutableContent.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/HtmlContentRewriter.java
    incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/MutableContentTest.java
Removed:
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/MutableHtmlContent.java
    incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/MutableHtmlContentTest.java
Modified:
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/Gadget.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/AbstractHttpCache.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/ContentRewriter.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/JsTagConcatContentRewriter.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/LinkingTagContentRewriter.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/NoOpContentRewriter.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/StyleLinksContentRewriter.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/lexer/DefaultContentRewriter.java
    incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/GadgetTestFixture.java
    incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/http/AbstractHttpCacheTest.java
    incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/AppendingRewriter.java

Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/Gadget.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/Gadget.java?rev=692560&r1=692559&r2=692560&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/Gadget.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/Gadget.java Fri Sep  5 14:48:21 2008
@@ -37,7 +37,7 @@
  * Intermediary representation of all state associated with processing
  * of a single gadget request.
  */
-public class Gadget extends MutableHtmlContent {
+public class Gadget extends MutableContent {
   private final GadgetContext context;
   
   /**

Added: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/MutableContent.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/MutableContent.java?rev=692560&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/MutableContent.java (added)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/MutableContent.java Fri Sep  5 14:48:21 2008
@@ -0,0 +1,170 @@
+/*
+ * 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;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.util.List;
+
+import org.apache.shindig.gadgets.parse.GadgetHtmlNode;
+import org.apache.shindig.gadgets.parse.GadgetHtmlParser;
+import org.apache.shindig.gadgets.parse.ParsedHtmlNode;
+
+/**
+ * Object that maintains a String representation of arbitrary contents
+ * and a consistent view of those contents as an HTML parse tree.
+ */
+public class MutableContent {
+  private String content;
+  private GadgetHtmlNode parseTree;
+  private ContentEditListener editListener;
+  private int parseEditId;
+  private int contentParseId;
+  private final GadgetHtmlParser contentParser;
+
+  public MutableContent(GadgetHtmlParser contentParser) {
+    this.contentParser = contentParser;
+    this.contentParseId = parseEditId = 0;
+  }
+
+  public static final String ROOT_NODE_TAG_NAME = "gadget-root";
+  
+  /**
+   * Retrieves the current content for this object in String form.
+   * If content has been retrieved in parse tree form and has
+   * been edited, the String form is computed from the parse tree by
+   * rendering it. It is <b>strongly</b> encouraged to avoid switching
+   * between retrieval of parse tree (through {@code getParseTree}),
+   * with subsequent edits and retrieval of String contents to avoid
+   * repeated serialization and deserialization.
+   * @return Renderable/active content.
+   */
+  public String getContent() {
+    if (parseEditId > contentParseId) {
+      // Regenerate content from parse tree node, since the parse tree
+      // was modified relative to the last time content was generated from it.
+      // This is an expensive operation that should happen only once
+      // per rendering cycle: all rewriters (or other manipulators)
+      // operating on the parse tree should happen together.
+      contentParseId = parseEditId;
+      StringWriter sw = new StringWriter();
+      for (GadgetHtmlNode node : parseTree.getChildren()) {
+        try {
+          node.render(sw);
+        } catch (IOException e) {
+          // Never happens.
+        }
+      }
+      content = sw.toString();
+    }
+    return content;
+  }
+  
+  /**
+   * Sets the object's content as a raw String. Note, this operation
+   * may be done at any time, even after a parse tree node has been retrieved
+   * and modified (though a warning will be emitted in this case). Once
+   * new content has been set, all subsequent edits to parse trees generated
+   * from the <i>previous</i> content will be invalid, throwing an
+   * {@code IllegalStateException}.
+   * @param newContent New content.
+   */
+  public void setContent(String newContent) {
+    if (content == null ||
+        !content.equals(newContent)) {
+      content = newContent;
+      if (editListener != null) {
+        editListener.stringEdited();
+      }
+    }
+  }
+  
+  /**
+   * Retrieves the object contents in parse tree form, if a
+   * {@code GadgetHtmlParser} is configured and is able to parse the string
+   * contents appropriately. The resultant parse tree has a special,
+   * single top-level node that wraps all subsequent content, with
+   * tag name {@code ROOT_NODE_TAG_NAME}. While it may be edited just
+   * as any other node may, doing so is pointless since the root node
+   * is stripped out during rendering. Any edits to the returned parse
+   * tree performed after the source {@code MutableHtmlContent} has new content
+   * set via {@code setContent} will throw an {@code IllegalStateException}
+   * to maintain content consistency in the object. To modify the object's
+   * contents by parse tree after setting new String contents,
+   * this method must be called again. However, this practice is highly
+   * discouraged, as parsing a tree from String is a costly operation.
+   * @return Top-level node whose children represent the gadget's contents, or
+   *         null if no parser is configured, String contents are null, or contents unparseable.
+   */
+  public GadgetHtmlNode getParseTree() {
+    if (parseTree != null && !editListener.stringWasEdited()) {
+      return parseTree;
+    }
+  
+    if (content == null || contentParser == null) {
+      return null;
+    }
+  
+    // One ContentEditListener per parse tree.
+    editListener = new ContentEditListener();
+    parseTree = new GadgetHtmlNode(ROOT_NODE_TAG_NAME, null);
+    List<ParsedHtmlNode> parsed = null;
+    try {
+      parsed = contentParser.parse(content);
+    } catch (GadgetException e) {
+      // TODO: emit info message
+      return null;
+    }
+  
+    if (parsed == null) {
+      return null;
+    }
+    
+    for (ParsedHtmlNode parsedNode : parsed) {
+      parseTree.appendChild(new GadgetHtmlNode(parsedNode, editListener));
+    }
+  
+    // Parse tree created from content: edit IDs are the same
+    contentParseId = parseEditId;
+    return parseTree;
+  }
+  
+  // Intermediary object tracking edit behavior for the MutableHtmlContent to help maintain
+  // state consistency. GadgetHtmlNode calls nodeEdited whenever a modification
+  // is made to its original source.
+  private class ContentEditListener implements GadgetHtmlNode.EditListener {
+    private boolean stringEdited = false;
+  
+    public void nodeEdited() {
+      ++parseEditId;
+      if (stringEdited) {
+        // Parse tree is invalid: a new String representation was set
+        // as tree source in the meantime.
+        throw new IllegalStateException("Edited parse node after setting String content");
+      }
+    }
+  
+    private void stringEdited() {
+      stringEdited = true;
+    }
+  
+    private boolean stringWasEdited() {
+      return stringEdited;
+    }
+  }
+}

Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/AbstractHttpCache.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/AbstractHttpCache.java?rev=692560&r1=692559&r2=692560&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/AbstractHttpCache.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/AbstractHttpCache.java Fri Sep  5 14:48:21 2008
@@ -17,6 +17,7 @@
  */
 package org.apache.shindig.gadgets.http;
 
+import org.apache.shindig.gadgets.MutableContent;
 import org.apache.shindig.gadgets.rewrite.ContentRewriter;
 import org.apache.shindig.gadgets.rewrite.ContentRewriterRegistry;
 
@@ -143,12 +144,13 @@
    */
   protected HttpResponse rewrite(HttpRequest request, HttpResponse response) {
     if (rewriterRegistry != null) {
-      HttpResponse rewritten = response;
+      MutableContent mc = new MutableContent(null);
+      mc.setContent(response.getResponseAsString());
       for (ContentRewriter rewriter : rewriterRegistry.getRewriters()) {
-        rewritten = rewriter.rewrite(request, rewritten);
+        rewriter.rewrite(request, response, mc);
       }
-      if (response != rewritten) {
-        return rewritten;
+      if (!mc.getContent().equals(response.getResponseAsString())) {
+        return new HttpResponseBuilder(response).setResponseString(mc.getContent()).create();
       }
     }
     return null;

Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/ContentRewriter.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/ContentRewriter.java?rev=692560&r1=692559&r2=692560&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/ContentRewriter.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/ContentRewriter.java Fri Sep  5 14:48:21 2008
@@ -18,6 +18,7 @@
 package org.apache.shindig.gadgets.rewrite;
 
 import org.apache.shindig.gadgets.Gadget;
+import org.apache.shindig.gadgets.MutableContent;
 import org.apache.shindig.gadgets.http.HttpRequest;
 import org.apache.shindig.gadgets.http.HttpResponse;
 import org.apache.shindig.gadgets.rewrite.lexer.DefaultContentRewriter;
@@ -34,11 +35,12 @@
 
   /**
    * Rewrite the original content located at source.
-   * @param request  Originating request
-   * @param original Original content
-   * @return A rewritten copy of the original or null if no rewriting occurred
+   * 
+   * @param request Originating request, as context.
+   * @param response Original HTTP response, for context.
+   * @param content Original content.
    */
-  public HttpResponse rewrite(HttpRequest request, HttpResponse original);
+  public void rewrite(HttpRequest request, HttpResponse original, MutableContent content);
 
   /**
    * Rewrite the gadget. The Gadget object's manipulation methods are used

Added: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/HtmlContentRewriter.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/HtmlContentRewriter.java?rev=692560&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/HtmlContentRewriter.java (added)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/HtmlContentRewriter.java Fri Sep  5 14:48:21 2008
@@ -0,0 +1,65 @@
+/*
+ * 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.rewrite;
+
+import org.apache.shindig.gadgets.Gadget;
+import org.apache.shindig.gadgets.MutableContent;
+import org.apache.shindig.gadgets.http.HttpRequest;
+import org.apache.shindig.gadgets.http.HttpResponse;
+import org.apache.shindig.gadgets.parse.GadgetHtmlNode;
+
+import java.net.URI;
+
+/**
+ * Simple helper base class for ContentRewriters that manipulate an
+ * HTML parse tree, whether in rewriting a {@code Gadget} or an
+ * {@code HttpResponse}. Passes in the URI from which the content
+ * was derived in doing so.
+ */
+public abstract class HtmlContentRewriter implements ContentRewriter {
+  
+  protected abstract void rewrite(GadgetHtmlNode root, URI baseUri);
+
+  public static String getMimeType(HttpRequest request, HttpResponse original) {
+    String mimeType = request.getRewriteMimeType();
+    if (mimeType == null) {
+      mimeType = original.getHeader("Content-Type");
+    }
+    return mimeType != null ? mimeType.toLowerCase() : null;
+  }
+  
+  public void rewrite(HttpRequest request, HttpResponse original,
+      MutableContent content) {
+    String mimeType = getMimeType(request, original);
+    if (mimeType.toLowerCase().contains("html")) {
+      rewriteHtml(content.getParseTree(), request.getUri().toJavaUri());
+    }
+  }
+
+  public void rewrite(Gadget gadget) {
+    rewriteHtml(gadget.getParseTree(), gadget.getSpec().getUrl());
+  }
+  
+  private void rewriteHtml(GadgetHtmlNode root, URI baseUri) {
+    if (root != null) {
+      rewrite(root, baseUri);
+    }
+  }
+
+}

Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/JsTagConcatContentRewriter.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/JsTagConcatContentRewriter.java?rev=692560&r1=692559&r2=692560&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/JsTagConcatContentRewriter.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/JsTagConcatContentRewriter.java Fri Sep  5 14:48:21 2008
@@ -29,6 +29,7 @@
 
 import org.apache.shindig.common.util.Utf8UrlCoder;
 import org.apache.shindig.gadgets.Gadget;
+import org.apache.shindig.gadgets.MutableContent;
 import org.apache.shindig.gadgets.http.HttpRequest;
 import org.apache.shindig.gadgets.http.HttpResponse;
 import org.apache.shindig.gadgets.parse.GadgetHtmlNode;
@@ -53,9 +54,8 @@
     }
   }
 
-  public HttpResponse rewrite(HttpRequest request, HttpResponse original) {
-    // TODO Auto-generated method stub
-    return null;
+  public void rewrite(HttpRequest request, HttpResponse original, MutableContent content) {
+    // JS Concatenation not supported for HTTP responses at present.
   }
 
   public void rewrite(Gadget gadget) {
@@ -70,7 +70,6 @@
     Queue<GadgetHtmlNode> nodesToProcess =
         new LinkedList<GadgetHtmlNode>();
     nodesToProcess.add(gadget.getParseTree());
-
     
     String concatBase = getJsConcatBase(gadget.getSpec(), rewriterFeature);
     

Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/LinkingTagContentRewriter.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/LinkingTagContentRewriter.java?rev=692560&r1=692559&r2=692560&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/LinkingTagContentRewriter.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/LinkingTagContentRewriter.java Fri Sep  5 14:48:21 2008
@@ -26,12 +26,11 @@
 import java.util.Queue;
 import java.util.Set;
 
-import org.apache.shindig.gadgets.Gadget;
-import org.apache.shindig.gadgets.http.HttpRequest;
-import org.apache.shindig.gadgets.http.HttpResponse;
 import org.apache.shindig.gadgets.parse.GadgetHtmlNode;
 
-public class LinkingTagContentRewriter implements ContentRewriter {
+import java.net.URI;
+
+public class LinkingTagContentRewriter extends HtmlContentRewriter {
   private final LinkRewriter linkRewriter;
   private final Map<String, Set<String>> tagAttributeTargets;
   
@@ -45,12 +44,7 @@
     }
   }
 
-  public HttpResponse rewrite(HttpRequest request, HttpResponse original) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	public void rewrite(Gadget gadget) {
+	protected void rewrite(GadgetHtmlNode root, URI baseUri) {
 	  if (linkRewriter == null) {
 	    // Sanity test.
 	    return;
@@ -58,11 +52,6 @@
 	  
     Queue<GadgetHtmlNode> nodesToProcess =
       new LinkedList<GadgetHtmlNode>();
-    GadgetHtmlNode root = gadget.getParseTree();
-    if (root == null) {
-      return;
-    }
-    
     nodesToProcess.addAll(root.getChildren());
   
     while (!nodesToProcess.isEmpty()) {
@@ -80,7 +69,7 @@
             
               // Attribute marked for rewriting: do it!
               curNode.setAttribute(attrKey,
-                  linkRewriter.rewrite(attrValue, gadget.getSpec().getUrl()));
+                  linkRewriter.rewrite(attrValue, baseUri));
             }
           }
         }

Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/NoOpContentRewriter.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/NoOpContentRewriter.java?rev=692560&r1=692559&r2=692560&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/NoOpContentRewriter.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/NoOpContentRewriter.java Fri Sep  5 14:48:21 2008
@@ -19,6 +19,7 @@
 package org.apache.shindig.gadgets.rewrite;
 
 import org.apache.shindig.gadgets.Gadget;
+import org.apache.shindig.gadgets.MutableContent;
 import org.apache.shindig.gadgets.http.HttpRequest;
 import org.apache.shindig.gadgets.http.HttpResponse;
 
@@ -30,11 +31,11 @@
   public NoOpContentRewriter() {
   }
 
-  public HttpResponse rewrite(HttpRequest request, HttpResponse original) {
-    return null;
+  public void rewrite(HttpRequest request, HttpResponse original, MutableContent content) {
+    // Do nothing.
   }
 
   public void rewrite(Gadget gadget) {
-	// Do nothing.
+	  // Do nothing.
   }
 }

Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/StyleLinksContentRewriter.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/StyleLinksContentRewriter.java?rev=692560&r1=692559&r2=692560&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/StyleLinksContentRewriter.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/StyleLinksContentRewriter.java Fri Sep  5 14:48:21 2008
@@ -22,11 +22,14 @@
 import java.util.Queue;
 
 import org.apache.shindig.gadgets.Gadget;
+import org.apache.shindig.gadgets.MutableContent;
 import org.apache.shindig.gadgets.http.HttpRequest;
 import org.apache.shindig.gadgets.http.HttpResponse;
 import org.apache.shindig.gadgets.parse.GadgetHtmlNode;
 import org.apache.shindig.gadgets.rewrite.ContentRewriterFeature;
 
+import java.net.URI;
+
 public class StyleLinksContentRewriter implements ContentRewriter {
   // TODO: consider providing helper base class for node-visitor content rewriters
   private final ContentRewriterFeature.Factory rewriterFeatureFactory;
@@ -38,9 +41,13 @@
     this.linkRewriter = linkRewriter;
   }
 
-  public HttpResponse rewrite(HttpRequest request, HttpResponse original) {
-    // TODO Auto-generated method stub
-    return null;
+  public void rewrite(HttpRequest request, HttpResponse original, MutableContent content) {
+    String mimeType = HtmlContentRewriter.getMimeType(request, original);
+    if (mimeType.contains("html")) {
+      rewriteHtml(content.getParseTree(), request.getUri().toJavaUri());
+    } else if (mimeType.contains("css")) {
+      content.setContent(rewriteCss(content.getContent(), request.getUri().toJavaUri()));
+    }
   }
 
   public void rewrite(Gadget gadget) {
@@ -51,13 +58,17 @@
       return;
     }
     
-    Queue<GadgetHtmlNode> nodesToProcess =
-      new LinkedList<GadgetHtmlNode>();
-    GadgetHtmlNode root = gadget.getParseTree();
+    rewriteHtml(gadget.getParseTree(), gadget.getSpec().getUrl());
+  }
+  
+  private void rewriteHtml(GadgetHtmlNode root, URI baseUri) {
     if (root == null) {
       return;
     }
     
+    Queue<GadgetHtmlNode> nodesToProcess =
+      new LinkedList<GadgetHtmlNode>();
+    
     nodesToProcess.addAll(root.getChildren());
   
     while (!nodesToProcess.isEmpty()) {
@@ -69,13 +80,16 @@
         if (curNode.getTagName().equalsIgnoreCase("style")) {
           String styleText = getNodeChildText(curNode);
           curNode.clearChildren();
-          curNode.appendChild(new GadgetHtmlNode(
-              CssRewriter.rewrite(styleText, gadget.getSpec().getUrl(), linkRewriter)));
+          curNode.appendChild(new GadgetHtmlNode(rewriteCss(styleText, baseUri)));
         }
       }
     }
   }
   
+  private String rewriteCss(String styleText, URI baseUri) {
+    return CssRewriter.rewrite(styleText, baseUri, linkRewriter);
+  }
+  
   private static String getNodeChildText(GadgetHtmlNode node) {
     // TODO: move this to GadgetHtmlNode as a helper
     StringBuilder builder = new StringBuilder();

Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/lexer/DefaultContentRewriter.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/lexer/DefaultContentRewriter.java?rev=692560&r1=692559&r2=692560&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/lexer/DefaultContentRewriter.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/lexer/DefaultContentRewriter.java Fri Sep  5 14:48:21 2008
@@ -24,9 +24,9 @@
 import org.apache.shindig.gadgets.Gadget;
 import org.apache.shindig.gadgets.GadgetException;
 import org.apache.shindig.gadgets.GadgetSpecFactory;
+import org.apache.shindig.gadgets.MutableContent;
 import org.apache.shindig.gadgets.http.HttpRequest;
 import org.apache.shindig.gadgets.http.HttpResponse;
-import org.apache.shindig.gadgets.http.HttpResponseBuilder;
 import org.apache.shindig.gadgets.rewrite.ContentRewriter;
 import org.apache.shindig.gadgets.rewrite.ContentRewriterFeature;
 import org.apache.shindig.gadgets.rewrite.CssRewriter;
@@ -35,7 +35,6 @@
 import org.apache.shindig.gadgets.spec.GadgetSpec;
 
 import java.io.ByteArrayOutputStream;
-import java.io.InputStreamReader;
 import java.io.OutputStreamWriter;
 import java.io.Reader;
 import java.io.StringReader;
@@ -83,10 +82,10 @@
     }
   }
 
-  public HttpResponse rewrite(HttpRequest request, HttpResponse original) {
+  public void rewrite(HttpRequest request, HttpResponse original, MutableContent content) {
     try {
       ByteArrayOutputStream baos = new ByteArrayOutputStream(
-          (original.getContentLength() * 110) / 100);
+          (content.getContent().length() * 110) / 100);
       OutputStreamWriter output = new OutputStreamWriter(baos, original.getEncoding());
       String mimeType = original.getHeader("Content-Type");
       if (request.getRewriteMimeType() != null) {
@@ -96,17 +95,14 @@
       if (request.getGadget() != null) {
         spec = specFactory.getGadgetSpec(request.getGadget().toJavaUri(), false);
       }
-      if (rewrite(spec, request.getUri().toJavaUri(),
-          new InputStreamReader(original.getResponse(), original.getEncoding()),
+      rewrite(spec, request.getUri().toJavaUri(),
+          new StringReader(content.getContent()),
           mimeType,
-          output)) {
-        return new HttpResponseBuilder(original).setResponse(baos.toByteArray()).create();
-      }
-      return null;
+          output);
     } catch (UnsupportedEncodingException uee) {
       throw new RuntimeException(uee);
     } catch (GadgetException ge) {
-      return null;
+      // Couldn't retrieve gadgetSpec
     }
   }
 

Modified: incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/GadgetTestFixture.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/GadgetTestFixture.java?rev=692560&r1=692559&r2=692560&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/GadgetTestFixture.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/GadgetTestFixture.java Fri Sep  5 14:48:21 2008
@@ -24,6 +24,7 @@
 import org.apache.shindig.common.cache.DefaultCacheProvider;
 import org.apache.shindig.common.testing.TestExecutorService;
 import org.apache.shindig.common.util.FakeTimeSource;
+import org.apache.shindig.gadgets.MutableContent;
 import org.apache.shindig.gadgets.http.ContentFetcherFactory;
 import org.apache.shindig.gadgets.http.HttpFetcher;
 import org.apache.shindig.gadgets.http.HttpRequest;
@@ -66,9 +67,14 @@
   
   protected static class CaptureRewriter implements ContentRewriter {
     private boolean rewroteView = false;
+    private boolean rewroteResponse = false;
 
-    public HttpResponse rewrite(HttpRequest request, HttpResponse original) {
-      return original;
+    public void rewrite(HttpRequest request, HttpResponse original, MutableContent content) {
+      rewroteResponse = true;
+    }
+    
+    public boolean responseWasRewritten() {
+      return rewroteResponse;
     }
 
     public void rewrite(Gadget gadget) {

Added: incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/MutableContentTest.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/MutableContentTest.java?rev=692560&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/MutableContentTest.java (added)
+++ incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/MutableContentTest.java Fri Sep  5 14:48:21 2008
@@ -0,0 +1,110 @@
+/*
+ * 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;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import org.apache.shindig.gadgets.parse.GadgetHtmlNode;
+import org.apache.shindig.gadgets.parse.caja.CajaHtmlParser;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public class MutableContentTest {
+  private MutableContent mhc;
+  
+  @Before
+  public void setUp() throws Exception {
+    // Note dependency on CajaHtmlParser - this isn't particularly ideal but is
+    // sufficient given that this test doesn't exercise the parser extensively at all,
+    // instead focusing on the additional utility provided by MutableHtmlContent
+    mhc = new MutableContent(new CajaHtmlParser());
+    mhc.setContent("DEFAULT VIEW");
+  }
+  
+  @Test
+  public void getContentAndParseTreeNoSets() throws Exception {
+    String content = mhc.getContent();
+    assertEquals("DEFAULT VIEW", content);
+  
+    GadgetHtmlNode root = mhc.getParseTree();
+    assertEquals(1, root.getChildren().size());
+    assertTrue(root.getChildren().get(0).isText());
+    assertEquals(content, root.getChildren().get(0).getText());
+  
+    assertSame(content, mhc.getContent());
+    assertSame(root, mhc.getParseTree());
+  }
+  
+  @Test
+  public void modifyContentReflectedInTree() throws Exception {
+    mhc.setContent("NEW CONTENT");
+    GadgetHtmlNode root = mhc.getParseTree();
+    assertEquals(1, root.getChildren().size());
+    assertEquals("NEW CONTENT", root.getChildren().get(0).getText());
+  }
+  
+  @Test
+  public void modifyTreeReflectedInContent() throws Exception {
+    GadgetHtmlNode root = mhc.getParseTree();
+  
+    // First child should be text node per other tests. Modify it.
+    root.getChildren().get(0).setText("FOO CONTENT");
+    assertEquals("FOO CONTENT", mhc.getContent());
+  
+    // Do it again
+    root.getChildren().get(0).setText("BAR CONTENT");
+    assertEquals("BAR CONTENT", mhc.getContent());
+  
+    // GadgetHtmlNode hasn't changed because string hasn't changed
+    assertSame(root, mhc.getParseTree());
+  }
+  
+  @Test
+  public void staleTreeEditsInvalidatedAfterContentSet() throws Exception {
+    GadgetHtmlNode firstRoot = mhc.getParseTree();
+  
+    // Re-set content
+    mhc.setContent("INVALIDATING CONTENT");
+  
+    // Should still be able to obtain this.
+    GadgetHtmlNode secondRoot = mhc.getParseTree();
+    assertNotSame(firstRoot, secondRoot);
+  
+    // Should be able to *obtain* first child node...
+    GadgetHtmlNode firstTextNode = firstRoot.getChildren().get(0);
+    try {
+      // ...but not edit it.
+      firstTextNode.setText("STALE-SET CONTENT");
+      fail("Should not be able to modify stale parse tree");
+    } catch (IllegalStateException e) {
+      // Expected condition.
+    }
+  
+    assertEquals("INVALIDATING CONTENT", secondRoot.getChildren().get(0).getText());
+  
+    // For good measure, modify secondRoot and get content
+    secondRoot.getChildren().get(0).setText("NEW CONTENT");
+    assertEquals("NEW CONTENT", mhc.getContent());
+  }
+}

Modified: incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/http/AbstractHttpCacheTest.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/http/AbstractHttpCacheTest.java?rev=692560&r1=692559&r2=692560&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/http/AbstractHttpCacheTest.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/http/AbstractHttpCacheTest.java Fri Sep  5 14:48:21 2008
@@ -18,6 +18,7 @@
 package org.apache.shindig.gadgets.http;
 
 import org.apache.shindig.gadgets.Gadget;
+import org.apache.shindig.gadgets.MutableContent;
 import org.apache.shindig.gadgets.rewrite.ContentRewriter;
 
 import static org.easymock.EasyMock.expect;
@@ -92,9 +93,8 @@
       gadget.setContent(PFX_STR + gadget.getContent());
     }
     
-    public HttpResponse rewrite(HttpRequest req, HttpResponse resp) {
-      return new HttpResponseBuilder(resp)
-          .setResponse((PFX_STR + resp.getResponseAsString()).getBytes()).create();
+    public void rewrite(HttpRequest req, HttpResponse resp, MutableContent c) {
+      c.setContent(PFX_STR + c.getContent());;
     }
   }
   

Modified: incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/AppendingRewriter.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/AppendingRewriter.java?rev=692560&r1=692559&r2=692560&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/AppendingRewriter.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/AppendingRewriter.java Fri Sep  5 14:48:21 2008
@@ -18,6 +18,7 @@
 package org.apache.shindig.gadgets.rewrite;
 
 import org.apache.shindig.gadgets.Gadget;
+import org.apache.shindig.gadgets.MutableContent;
 import org.apache.shindig.gadgets.http.HttpRequest;
 import org.apache.shindig.gadgets.http.HttpResponse;
 
@@ -33,13 +34,13 @@
     this.appender = appender;
   }
 
-  public HttpResponse rewrite(HttpRequest request, HttpResponse original) {
-    // Does nothing.
-    return null;
+  public void rewrite(HttpRequest request, HttpResponse original, MutableContent c) {
+    // Appends appender to the end of the content string.
+    c.setContent(c.getContent() + appender);
   }
 
   public void rewrite(Gadget gadget) {
     // Appends appender to the end of the input string.
-	gadget.setContent(gadget.getContent() + appender);
+	  gadget.setContent(gadget.getContent() + appender);
   }
 }
\ No newline at end of file