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&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=\"" + 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&url=10.jpg\""
+ + " style=\"height:50px;\" width=\"120px\">"
+ + "<img height=\"60px\"
src=\"shindig.com/proxy?container=test&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&debug=0&nocache=0&refresh=0&"
+ + "resize_h=" + height + "&resize_w=" + width +
"&no_expand=1&url="
+ + resource;
+ }
+}
Propchange:
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/ImageResizeRewriterTest.java
------------------------------------------------------------------------------
svn:eol-style = native