Author: gagan
Date: Tue Dec 28 10:15:31 2010
New Revision: 1053297

URL: http://svn.apache.org/viewvc?rev=1053297&view=rev
Log:
Patch by satya3656 | Issue 3480041: Adding ImageResizeRewriter that makes 
server side scaling of images possible. | http://codereview.appspot.com/3480041/

Added:
    
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/ImageResizeRewriter.java
   (with props)
    
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/ImageResizeRewriterTest.java
   (with props)

Added: 
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/ImageResizeRewriter.java
URL: 
http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/ImageResizeRewriter.java?rev=1053297&view=auto
==============================================================================
--- 
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/ImageResizeRewriter.java
 (added)
+++ 
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/ImageResizeRewriter.java
 Tue Dec 28 10:15:31 2010
@@ -0,0 +1,187 @@
+/*
+ * 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 com.google.inject.Inject;
+import com.google.common.collect.Lists;
+
+import org.apache.shindig.gadgets.Gadget;
+import org.apache.shindig.gadgets.GadgetException;
+import org.apache.shindig.gadgets.uri.ProxyUriManager;
+import org.apache.shindig.gadgets.uri.UriStatus;
+import org.apache.shindig.common.uri.Uri;
+import org.apache.commons.lang.math.NumberUtils;
+import org.w3c.dom.Node;
+import org.w3c.dom.Element;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * This rewriter helps in appending the image size parameters (extracted from 
inline styles, height
+ * and width) to the proxied resource urls so that server side resizing can be 
done when ever
+ * possible. Non-proxied resource URLs are ignored by this rewriter.
+ */
+public class ImageResizeRewriter extends DomWalker.Rewriter {
+  private final ContentRewriterFeature.Factory featureConfigFactory;
+  private final ProxyUriManager proxyUriManager;
+  
+  @Inject
+  public ImageResizeRewriter(ProxyUriManager proxyUriManager,
+                             ContentRewriterFeature.Factory 
featureConfigFactory) {
+    this.featureConfigFactory = featureConfigFactory;
+    this.proxyUriManager = proxyUriManager;
+  }
+
+  @Override
+  protected List<DomWalker.Visitor> makeVisitors(Gadget context, Uri 
gadgetUri) {
+    ContentRewriterFeature.Config config = featureConfigFactory.get(gadgetUri);
+    return Arrays.<DomWalker.Visitor>asList(new 
ImageResizeVisitor(proxyUriManager, config));
+  }
+  
+  public static class ImageResizeVisitor implements DomWalker.Visitor {
+    protected final ProxyUriManager proxyUriManager;
+    protected final ContentRewriterFeature.Config featureConfig;
+
+    public ImageResizeVisitor(ProxyUriManager proxyUriManager,
+                              ContentRewriterFeature.Config featureConfig) {
+      this.proxyUriManager = proxyUriManager;
+      this.featureConfig = featureConfig;
+    }
+    
+    public VisitStatus visit(Gadget gadget, Node node) throws 
RewritingException {
+      if (node.getNodeType() == Node.ELEMENT_NODE &&
+          node.getNodeName().equalsIgnoreCase("img")) {
+        Element imageElement = (Element) node;
+
+        // We process the <img> tag in following cases
+        // a) it has 'height' and 'width' but no 'id' and 'class' attributes.
+        // b) it has inline style attribute.
+        // TODO(satya): please beware of max-height, etc fields.
+        if ((!isEmpty(imageElement, "height") && !isEmpty(imageElement, 
"width") &&
+             isEmpty(imageElement, "id") && isEmpty(imageElement, "class")) ||
+            (!isEmpty(imageElement, "style"))) {
+          return addHeightWidthParams(imageElement);
+        }
+      }
+      return VisitStatus.BYPASS;
+    }
+
+    private boolean isEmpty(Element element, String attribute) {
+      return element.getAttribute(attribute).isEmpty();
+    }
+
+    public boolean revisit(Gadget gadget, List<Node> nodes) throws 
RewritingException {
+      return true;
+    }
+
+    private VisitStatus addHeightWidthParams(Element imgElement) {
+      // We want to append image resize params only to urls that are proxied 
through us.
+      String uriStr = imgElement.getAttribute("src").trim();
+      Uri uri = Uri.parse(uriStr);
+      ProxyUriManager.ProxyUri proxied = null;
+
+      // Try parsing this uri as a ProxyUri.
+      try {
+        proxied = proxyUriManager.process(uri);
+      } catch (GadgetException e) {
+        return VisitStatus.BYPASS;
+      }
+
+      if (null == proxied || proxied.getStatus() == UriStatus.BAD_URI) {
+        return VisitStatus.BYPASS;  
+      }
+
+      VisitStatus status = VisitStatus.BYPASS;
+
+      // We consider only cases where both image dimensions are in 'px' 
format. As '%', 'em'
+      // units are relative to the parent, it is more difficult to infer those 
values.
+      // Specifically, we consider only the following cases:
+      //   i) style specifies both height and width
+      //   ii) height and width are both specified and style does not specify 
these attributes.
+      //   iii) height and width are both specified and style overrides one of 
these.
+      // All other cases are ignored.
+      Integer height = 
getIntegerPrefix(imgElement.getAttribute("height").trim());
+      Integer width = 
getIntegerPrefix(imgElement.getAttribute("width").trim());
+      if (null == height || null == width) {
+        height = null;
+        width = null;
+      }
+
+      // Inline style tags trump everything, including inline height/width 
attribute,
+      // height/width inherited from css.
+      if (!"".equals(imgElement.getAttribute("style"))) {
+        String styleStr = imgElement.getAttribute("style");
+
+        for (String attr : styleStr.split(";")) {
+          String[] splits = attr.split(":");
+          if (splits.length != 2) {
+            continue;
+          }
+
+          if ("height".equalsIgnoreCase(splits[0].trim())) {
+            Integer styleHeight = getIntegerPrefix(splits[1].trim());
+            if (null != styleHeight) {
+              height = styleHeight;
+            }
+          }
+
+          if ("width".equalsIgnoreCase(splits[0].trim())) {
+            Integer styleWidth = getIntegerPrefix(splits[1].trim());
+            if (null != styleWidth) {
+              width = styleWidth;
+            }
+          }
+        }
+      }
+
+      if (null != height && null != width) {
+        proxied.setResize(width, height, null, true);
+        List<Uri> updatedUri = 
proxyUriManager.make(Lists.newArrayList(proxied),
+                                                    
featureConfig.getExpires());
+        if (updatedUri.size() == 1) {
+          imgElement.setAttribute("src", updatedUri.get(0).toString());
+          status = VisitStatus.MODIFY;
+        }
+      }
+
+      return status;
+    }
+
+    private Integer getIntegerPrefix(String input) {
+      String integerPrefix = "";
+      if (NumberUtils.isDigits(input)) {
+        integerPrefix = input;
+      } else if (input.endsWith("px") &&
+                 NumberUtils.isDigits(input.substring(0, input.length() - 2))) 
{
+        integerPrefix = input.substring(0, input.length() - 2);
+      }
+
+      Integer value = null;
+      if (!integerPrefix.isEmpty()) {
+        try {
+          value = NumberUtils.createInteger(integerPrefix);
+        } catch (NumberFormatException e) {
+          // ignore
+        }
+      }
+      return value;
+    }
+  }
+}

Propchange: 
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/ImageResizeRewriter.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: 
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/ImageResizeRewriterTest.java
URL: 
http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/ImageResizeRewriterTest.java?rev=1053297&view=auto
==============================================================================
--- 
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/ImageResizeRewriterTest.java
 (added)
+++ 
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/ImageResizeRewriterTest.java
 Tue Dec 28 10:15:31 2010
@@ -0,0 +1,136 @@
+/*
+ * 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.junit.Test;
+import org.junit.Before;
+import static org.junit.Assert.assertEquals;
+import org.apache.shindig.gadgets.parse.caja.CajaHtmlParser;
+import org.apache.shindig.gadgets.parse.ParseModule;
+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.uri.ProxyUriManager;
+import org.apache.shindig.gadgets.uri.DefaultProxyUriManager;
+import org.apache.shindig.common.uri.Uri;
+import org.apache.shindig.common.uri.UriBuilder;
+import org.apache.shindig.config.ContainerConfig;
+import org.apache.commons.lang.StringUtils;
+import org.easymock.EasyMock;
+
+/**
+ * Tests for {...@code ImageResizeRewriter}
+ */
+public class ImageResizeRewriterTest {
+  static final String CONTAINER = "test";
+
+  private ImageResizeRewriter rewriter;
+  private CajaHtmlParser parser;
+  private ParseModule.DOMImplementationProvider domImpl;
+  private ContainerConfig config;
+  private ContentRewriterFeature.Config featureConfig;
+  private ContentRewriterFeature.Factory factory;
+
+  @Before
+  public void setUp() {
+    config = EasyMock.createMock(ContainerConfig.class);
+    factory = EasyMock.createMock(ContentRewriterFeature.Factory.class);
+    featureConfig = EasyMock.createMock(ContentRewriterFeature.Config.class);
+    
+    ProxyUriManager proxyUriManager = new DefaultProxyUriManager(config, null);
+    rewriter = new ImageResizeRewriter(proxyUriManager, factory);
+    domImpl = new ParseModule.DOMImplementationProvider();
+    parser = new CajaHtmlParser(domImpl.get());
+    
EasyMock.expect(factory.get(EasyMock.isA(Uri.class))).andReturn(featureConfig).anyTimes();
+    EasyMock.expect(config.getString(CONTAINER, 
DefaultProxyUriManager.PROXY_HOST_PARAM))
+        .andReturn("shindig.com").anyTimes();
+    EasyMock.expect(config.getString(CONTAINER, 
DefaultProxyUriManager.PROXY_PATH_PARAM))
+        .andReturn("/proxy").anyTimes();
+    EasyMock.expect(featureConfig.getExpires()).andReturn(new 
Integer(0)).anyTimes();
+  }
+
+  @Test
+  public void testImageResizeRewriter() throws Exception {
+
+    String content = "<html><head></head><body>"
+        + "<p> p tag </p>"
+        + "<img src=\"shindig.com/proxy?container=test&url=1.jpg\">"
+        + "<img height=\"50px\" id=\"img\" 
src=\"shindig.com/proxy?container=test&url=2.jpg\">"
+        + "<img src=\"shindig.com/proxy?container=test&url=3.jpg\" 
width=\"50px\">"
+        + "<img height=\"50px\" id=\"id\" 
src=\"shindig.com/proxy?container=test&url=4.jpg\"" 
+        + " width=\"110px\">"
+        + "<img height=\"5\" 
src=\"shindig.com/proxy?container=test&url=5.jpg\" width=\"10em\">"
+        + "<img height=\"50\" 
src=\"shindig.com/proxy?container=test&url=6.jpg\" width=\"110px\">"
+        + "<img src=\"shindig.com/proxy?container=test&url=7.jpg\""
+        + " style=\"height:50px; width:110px\">"
+        + "<img src=\"example.com/8.jpg\" style=\"height:50px; width:110px\">"
+        + "<img height=\"60px\" width=\"120px\" 
src=\"shindig.com/proxy?container=test&url=9.jpg\""
+        + " style=\"height:50px; width:110px\">"
+        + "<img width=\"120px\" 
src=\"shindig.com/proxy?container=test&url=10.jpg\""
+        + " style=\"height:50px;\">"
+        + "<img height=\"60px\" 
src=\"shindig.com/proxy?container=test&url=11.jpg\""
+        + " style=\"width:110px\">"
+        + "<img height=\"60px\" 
src=\"shindig.com/proxy?container=test&url=12.jpg\""
+        + " style=\"width:110px\" width=\"50px\">"
+        + "</body></html>";
+
+    String expected = "<html><head></head><body>"
+        + "<p> p tag </p>"
+        + "<img src=\"shindig.com/proxy?container=test&amp;url=1.jpg\">"
+        + "<img height=\"50px\" id=\"img\" 
src=\"shindig.com/proxy?container=test&amp;url=2.jpg\">"
+        + "<img src=\"shindig.com/proxy?container=test&amp;url=3.jpg\" 
width=\"50px\">"
+        + "<img height=\"50px\" id=\"id\" 
src=\"shindig.com/proxy?container=test&amp;url=4.jpg\""
+        + " width=\"110px\">"
+        + "<img height=\"5\" 
src=\"shindig.com/proxy?container=test&amp;url=5.jpg\" width=\"10em\">"
+        + "<img height=\"50\" src=\"" + getProxiedUrl("6.jpg", "50", "110") + 
"\" width=\"110px\">"
+        + "<img src=\"" + getProxiedUrl("7.jpg", "50", "110") + "\""
+        + " style=\"height:50px; width:110px\">"
+        + "<img src=\"example.com/8.jpg\" style=\"height:50px; width:110px\">"
+        + "<img  height=\"60px\" src=\"" + getProxiedUrl("9.jpg", "50", "110") 
+ "\""
+        + " style=\"height:50px; width:110px\" width=\"120px\">"
+        + "<img src=\"shindig.com/proxy?container=test&amp;url=10.jpg\""
+        + " style=\"height:50px;\" width=\"120px\">"
+        + "<img height=\"60px\" 
src=\"shindig.com/proxy?container=test&amp;url=11.jpg\""
+        + " style=\"width:110px\">"
+        + "<img height=\"60px\" src=\"" + getProxiedUrl("12.jpg", "60", "110") 
+ "\""
+        + " style=\"width:110px\" width=\"50px\">"
+        + "</body></html>";
+
+    HttpRequest req = new HttpRequest(Uri.parse("http://www.shindig.com/";));
+    req.setGadget(UriBuilder.parse("http://www.shindig.com/";).toUri());
+    HttpResponse resp = new HttpResponseBuilder()
+        .setHttpStatusCode(200)
+        .setHeader("Content-Type", "text/html")
+        .setResponse(content.getBytes())
+        .create();
+    HttpResponseBuilder builder = new HttpResponseBuilder(parser, resp);
+
+    EasyMock.replay(config, featureConfig, factory);
+    rewriter.rewrite(req, builder);
+    assertEquals(StringUtils.deleteWhitespace(expected),
+                 StringUtils.deleteWhitespace(builder.getContent()));
+    EasyMock.verify(config, featureConfig, factory);
+  }
+
+  private String getProxiedUrl(String resource, String height, String width) {
+    return 
"//shindig.com/proxy?container=test&amp;debug=0&amp;nocache=0&amp;refresh=0&amp;"
+           + "resize_h=" + height + "&amp;resize_w=" + width + 
"&amp;no_expand=1&amp;url="
+           + resource;
+  }
+}

Propchange: 
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/ImageResizeRewriterTest.java
------------------------------------------------------------------------------
    svn:eol-style = native


Reply via email to