Repository: wicket
Updated Branches:
  refs/heads/wicket-6.x 013a56ae3 -> 057457135


WICKET-5827 Allow to apply multiple Javascript / CSS compressors


Project: http://git-wip-us.apache.org/repos/asf/wicket/repo
Commit: http://git-wip-us.apache.org/repos/asf/wicket/commit/05745713
Tree: http://git-wip-us.apache.org/repos/asf/wicket/tree/05745713
Diff: http://git-wip-us.apache.org/repos/asf/wicket/diff/05745713

Branch: refs/heads/wicket-6.x
Commit: 0574571358553ebc8e6911658c12a629cd677f28
Parents: 013a56a
Author: klopfdreh <klopfdreh@tobiass-mbp>
Authored: Wed Feb 4 13:26:25 2015 +0100
Committer: Martin Tzvetanov Grigorov <mgrigo...@apache.org>
Committed: Fri Feb 13 22:50:15 2015 +0200

----------------------------------------------------------------------
 .../request/resource/CssPackageResource.java    |  17 +-
 .../wicket/resource/CompositeCssCompressor.java | 101 ++++++++++++
 .../resource/CompositeJavaScriptCompressor.java |  88 +++++++++++
 .../apache/wicket/resource/CssUrlReplacer.java  |  90 +++++++++++
 .../IScopeAwareTextResourceProcessor.java       |  38 +++++
 .../wicket/resource/CssUrlReplacerTest.java     | 158 +++++++++++++++++++
 6 files changed, 491 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/wicket/blob/05745713/wicket-core/src/main/java/org/apache/wicket/request/resource/CssPackageResource.java
----------------------------------------------------------------------
diff --git 
a/wicket-core/src/main/java/org/apache/wicket/request/resource/CssPackageResource.java
 
b/wicket-core/src/main/java/org/apache/wicket/request/resource/CssPackageResource.java
index f6cf010..bbc1826 100644
--- 
a/wicket-core/src/main/java/org/apache/wicket/request/resource/CssPackageResource.java
+++ 
b/wicket-core/src/main/java/org/apache/wicket/request/resource/CssPackageResource.java
@@ -20,6 +20,7 @@ import java.util.Locale;
 
 import org.apache.wicket.Application;
 import org.apache.wicket.css.ICssCompressor;
+import org.apache.wicket.resource.IScopeAwareTextResourceProcessor;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -32,6 +33,8 @@ public class CssPackageResource extends PackageResource
 
        private static final Logger log = 
LoggerFactory.getLogger(CssPackageResource.class);
 
+       private final String name;
+
        /**
         * Construct.
         * 
@@ -46,6 +49,8 @@ public class CssPackageResource extends PackageResource
        {
                super(scope, name, locale, style, variation);
 
+               this.name = name;
+
                // CSS resources can be compressed if there is configured 
ICssCompressor
                setCompress(true);
        }
@@ -62,7 +67,17 @@ public class CssPackageResource extends PackageResource
                        try
                        {
                                String nonCompressed = new 
String(processedResponse, "UTF-8");
-                               return 
compressor.compress(nonCompressed).getBytes();
+                               String output;
+                               if (compressor instanceof 
IScopeAwareTextResourceProcessor)
+                               {
+                                       IScopeAwareTextResourceProcessor 
scopeAwareProcessor = (IScopeAwareTextResourceProcessor)compressor;
+                                       output = 
scopeAwareProcessor.process(nonCompressed, getScope(), name);
+                               }
+                               else
+                               {
+                                       output = 
compressor.compress(nonCompressed);
+                               }
+                               return output.getBytes();
                        }
                        catch (Exception e)
                        {

http://git-wip-us.apache.org/repos/asf/wicket/blob/05745713/wicket-core/src/main/java/org/apache/wicket/resource/CompositeCssCompressor.java
----------------------------------------------------------------------
diff --git 
a/wicket-core/src/main/java/org/apache/wicket/resource/CompositeCssCompressor.java
 
b/wicket-core/src/main/java/org/apache/wicket/resource/CompositeCssCompressor.java
new file mode 100644
index 0000000..d8fdcc0
--- /dev/null
+++ 
b/wicket-core/src/main/java/org/apache/wicket/resource/CompositeCssCompressor.java
@@ -0,0 +1,101 @@
+/*
+ * 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.wicket.resource;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.wicket.css.ICssCompressor;
+
+/**
+ * Used to apply several {@link ICssCompressor} to the CSS compression.<br>
+ * <br>
+ * Usage:
+ * 
+ * <pre>
+ * CompositeCssCompressor compositeCssCompressor = new 
CompositeCssCompressor();
+ * 
+ * compositeCssCompressor.add(new MyCssCompressor());
+ * compositeCssCompressor.add(new AnotherCssCompressor());
+ * 
+ * this.getResourceSettings().setCssCompressor(compositeCssCompressor);
+ * </pre>
+ * 
+ * The compressors can also be given as constructor arguments.
+ * 
+ * @since 6.20.0
+ * @author Tobias Soloschenko
+ * 
+ */
+public class CompositeCssCompressor implements IScopeAwareTextResourceProcessor
+{
+       /* Compressors to compress the CSS content */
+       private final List<ICssCompressor> compressors = new 
ArrayList<ICssCompressor>();
+
+       /**
+        * Initializes the composite CSS compressor with the given {@link 
ICssCompressor}(s)
+        * 
+        * @param compressors
+        *            The {@link ICssCompressor}(s) this composite CSS 
compressor is initialized with
+        */
+       public CompositeCssCompressor(ICssCompressor... compressors)
+       {
+               this.compressors.addAll(Arrays.asList(compressors));
+       }
+
+       /**
+        * Compresses the given original content in the order of compressors. 
If no compressor has been
+        * given the original content is going to be returned.
+        */
+       @Override
+       public String process(String input, Class<?> scope, String name)
+       {
+               String compressed = input;
+               for (ICssCompressor compressor : compressors)
+               {
+                       if (compressor instanceof 
IScopeAwareTextResourceProcessor)
+                       {
+                               IScopeAwareTextResourceProcessor processor = 
(IScopeAwareTextResourceProcessor)compressor;
+                               processor.process(compressed, scope, name);
+                       }
+                       else
+                       {
+                               compressed = compressor.compress(compressed);
+                       }
+               }
+               return compressed;
+       }
+
+       @Override
+       public String compress(String original)
+       {
+               throw new 
UnsupportedOperationException(CompositeCssCompressor.class.getSimpleName() +
+                       ".process() should be used instead!");
+       }
+
+       /**
+        * Adds a ICssCompressor to the list of delegates.
+        *
+        * @return {@code this} instance, for chaining
+        */
+       public CompositeCssCompressor add(ICssCompressor compressor)
+       {
+               compressors.add(compressor);
+               return this;
+       }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/wicket/blob/05745713/wicket-core/src/main/java/org/apache/wicket/resource/CompositeJavaScriptCompressor.java
----------------------------------------------------------------------
diff --git 
a/wicket-core/src/main/java/org/apache/wicket/resource/CompositeJavaScriptCompressor.java
 
b/wicket-core/src/main/java/org/apache/wicket/resource/CompositeJavaScriptCompressor.java
new file mode 100644
index 0000000..c1f0c15
--- /dev/null
+++ 
b/wicket-core/src/main/java/org/apache/wicket/resource/CompositeJavaScriptCompressor.java
@@ -0,0 +1,88 @@
+/*
+ * 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.wicket.resource;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.wicket.javascript.IJavaScriptCompressor;
+
+/**
+ * Used to apply several {@link IJavaScriptCompressor} to the javascript 
compression.<br>
+ * <br>
+ * Usage:
+ * 
+ * <pre>
+ * CompositeJavaScriptCompressor compositeJavaScriptCompressor = new 
CompositeJavaScriptCompressor();
+ * 
+ * compositeJavaScriptCompressor.add(new MyJavaScriptCompressor());
+ * compositeJavaScriptCompressor.add(new AnotherJavaScriptCompressor());
+ * 
+ * 
this.getResourceSettings().setJavaScriptCompressor(compositeJavaScriptCompressor);
+ * </pre>
+ * 
+ * The compressors can also be given as constructor arguments.
+ * 
+ * @since 6.20.0
+ * @author Tobias Soloschenko
+ *
+ */
+public class CompositeJavaScriptCompressor implements IJavaScriptCompressor
+{
+       /* Compressors to compress javascript content */
+       private final List<IJavaScriptCompressor> compressors = new 
ArrayList<IJavaScriptCompressor>();
+
+       /**
+        * Initializes the composite javascript compressor with the given 
{@link IJavaScriptCompressor}
+        * (s)
+        * 
+        * @param compressors
+        *            The {@link IJavaScriptCompressor}(s) this composite 
javascript compressor is
+        *            initialized with
+        */
+       public CompositeJavaScriptCompressor(IJavaScriptCompressor... 
compressors)
+       {
+               this.compressors.addAll(Arrays.asList(compressors));
+       }
+
+       /**
+        * Compresses the given original content in the order of compressors. 
If no compressor has been
+        * given the original content is going to be returned.
+        */
+       @Override
+       public String compress(String original)
+       {
+               String compressed = original;
+               for (IJavaScriptCompressor compressor : compressors)
+               {
+                       compressed = compressor.compress(compressed);
+               }
+               return compressed;
+       }
+
+       /**
+        * Adds a IJavaScriptCompressor to the list of delegates.
+        * 
+        * @return {@code this} instance, for chaining
+        */
+       public CompositeJavaScriptCompressor add(IJavaScriptCompressor 
compressor)
+       {
+               compressors.add(compressor);
+               return this;
+       }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/wicket/blob/05745713/wicket-core/src/main/java/org/apache/wicket/resource/CssUrlReplacer.java
----------------------------------------------------------------------
diff --git 
a/wicket-core/src/main/java/org/apache/wicket/resource/CssUrlReplacer.java 
b/wicket-core/src/main/java/org/apache/wicket/resource/CssUrlReplacer.java
new file mode 100644
index 0000000..6ad891c
--- /dev/null
+++ b/wicket-core/src/main/java/org/apache/wicket/resource/CssUrlReplacer.java
@@ -0,0 +1,90 @@
+/*
+ * 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.wicket.resource;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.wicket.request.Url;
+import org.apache.wicket.request.cycle.RequestCycle;
+import org.apache.wicket.request.resource.PackageResourceReference;
+
+/**
+ * This compressor is used to replace url within css files with resources that 
belongs to their
+ * corresponding component classes. The compress method is not compressing any 
content, but
+ * replacing the URLs with Wicket representatives.<br>
+ * <br>
+ * Usage:
+ * 
+ * <pre>
+ * this.getResourceSettings().setCssCompressor(new CssUrlReplacer(this));
+ * </pre>
+ * 
+ * @since 6.20.0
+ * @author Tobias Soloschenko
+ */
+public class CssUrlReplacer implements IScopeAwareTextResourceProcessor
+{
+       // The pattern to find URLs in CSS resources
+       private static final Pattern URL_PATTERN = 
Pattern.compile("url\\(['|\"]*(.*?)['|\"]*\\)");
+
+       /**
+        * Replaces the URLs of CSS resources with Wicket representatives.
+        */
+       @Override
+       public String process(String input, Class<?> scope, String name)
+       {
+               RequestCycle cycle = RequestCycle.get();
+               Url cssUrl = Url.parse(name);
+               Matcher matcher = URL_PATTERN.matcher(input);
+               StringBuffer output = new StringBuffer();
+
+               while (matcher.find())
+               {
+                       Url imageCandidateUrl = Url.parse(matcher.group(1));
+                       CharSequence processedUrl;
+                       if (imageCandidateUrl.isFull())
+                       {
+                               processedUrl = 
imageCandidateUrl.toString(Url.StringMode.FULL);
+                       }
+                       else if (imageCandidateUrl.isContextAbsolute())
+                       {
+                               processedUrl = imageCandidateUrl.toString();
+                       }
+                       else
+                       {
+                               // relativize against the url for the 
containing CSS file
+                               Url cssUrlCopy = new Url(cssUrl);
+                               cssUrlCopy.resolveRelative(imageCandidateUrl);
+                               PackageResourceReference imageReference = new 
PackageResourceReference(scope,
+                                       cssUrlCopy.toString());
+                               processedUrl = cycle.urlFor(imageReference, 
null);
+
+                       }
+                       matcher.appendReplacement(output, "url('" + 
processedUrl + "')");
+               }
+               matcher.appendTail(output);
+               return output.toString();
+       }
+
+       @Override
+       public String compress(String original)
+       {
+               throw new 
UnsupportedOperationException(CssUrlReplacer.class.getSimpleName() +
+                       ".process() should be used instead!");
+       }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/wicket/blob/05745713/wicket-core/src/main/java/org/apache/wicket/resource/IScopeAwareTextResourceProcessor.java
----------------------------------------------------------------------
diff --git 
a/wicket-core/src/main/java/org/apache/wicket/resource/IScopeAwareTextResourceProcessor.java
 
b/wicket-core/src/main/java/org/apache/wicket/resource/IScopeAwareTextResourceProcessor.java
new file mode 100644
index 0000000..7d9e424
--- /dev/null
+++ 
b/wicket-core/src/main/java/org/apache/wicket/resource/IScopeAwareTextResourceProcessor.java
@@ -0,0 +1,38 @@
+/*
+ * 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.wicket.resource;
+
+/**
+ * A {@link org.apache.wicket.resource.ITextResourceCompressor} that receives 
the scope class and
+ * the resource name as a context information that it can use for the 
processing of the resource
+ */
+public interface IScopeAwareTextResourceProcessor extends 
ITextResourceCompressor
+{
+       /**
+        * Processes/manipulates a text resource.
+        *
+        *
+        * @param input
+        *            The original input to process
+        * @param scope
+        *            The scope class of the package resource
+        * @param name
+        *            The name of the package resource
+        * @return The processed input
+        */
+       public String process(String input, Class<?> scope, String name);
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/wicket/blob/05745713/wicket-core/src/test/java/org/apache/wicket/resource/CssUrlReplacerTest.java
----------------------------------------------------------------------
diff --git 
a/wicket-core/src/test/java/org/apache/wicket/resource/CssUrlReplacerTest.java 
b/wicket-core/src/test/java/org/apache/wicket/resource/CssUrlReplacerTest.java
new file mode 100644
index 0000000..f83338a
--- /dev/null
+++ 
b/wicket-core/src/test/java/org/apache/wicket/resource/CssUrlReplacerTest.java
@@ -0,0 +1,158 @@
+/*
+ * 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.wicket.resource;
+
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.is;
+
+import org.apache.wicket.WicketTestCase;
+import org.apache.wicket.mock.MockApplication;
+import org.apache.wicket.protocol.http.WebApplication;
+import 
org.apache.wicket.request.resource.caching.FilenameWithVersionResourceCachingStrategy;
+import org.apache.wicket.request.resource.caching.IStaticCacheableResource;
+import org.apache.wicket.request.resource.caching.ResourceUrl;
+import 
org.apache.wicket.request.resource.caching.version.MessageDigestResourceVersion;
+import org.junit.Test;
+
+public class CssUrlReplacerTest extends WicketTestCase
+{
+
+       private static final String DECORATION_SUFFIX = "--decorated";
+
+       @Override
+       protected WebApplication newApplication()
+       {
+               return new MockApplication()
+               {
+                       @Override
+                       protected void init()
+                       {
+                               super.init();
+
+                               getResourceSettings().setCachingStrategy(
+                                       new 
FilenameWithVersionResourceCachingStrategy("=VER=",
+                                               new 
MessageDigestResourceVersion())
+                                       {
+                                               @Override
+                                               public void 
decorateUrl(ResourceUrl url, IStaticCacheableResource resource)
+                                               {
+                                                       
url.setFileName(url.getFileName() + DECORATION_SUFFIX);
+                                               }
+                                       });
+                       }
+               };
+       }
+
+       @Test
+       public void doNotProcessFullUrls()
+       {
+               String input = ".class {background-image: 
url('http://example.com/some.img');}";
+               Class<?> scope = CssUrlReplacerTest.class;
+               String cssRelativePath = "res/css/some.css";
+               CssUrlReplacer replacer = new CssUrlReplacer();
+
+               String processed = replacer.process(input, scope, 
cssRelativePath);
+               assertThat(processed, is(input));
+       }
+
+       @Test
+       public void doNotProcessContextAbsoluteUrls()
+       {
+               String input = ".class {background-image: url('/some.img');}";
+               Class<?> scope = CssUrlReplacerTest.class;
+               String cssRelativePath = "res/css/some.css";
+               CssUrlReplacer replacer = new CssUrlReplacer();
+
+               String processed = replacer.process(input, scope, 
cssRelativePath);
+               assertThat(processed, is(input));
+       }
+
+       @Test
+       public void sameFolderSingleQuotes()
+       {
+               String input = ".class {background-image: url('some.img');}";
+               Class<?> scope = CssUrlReplacerTest.class;
+               String cssRelativePath = "res/css/some.css";
+               CssUrlReplacer replacer = new CssUrlReplacer();
+
+               String processed = replacer.process(input, scope, 
cssRelativePath);
+               assertThat(
+                       processed,
+                       is(".class {background-image: 
url('./wicket/resource/org.apache.wicket.resource.CssUrlReplacerTest/res/css/some.img"
 +
+                               DECORATION_SUFFIX + "');}"));
+       }
+
+       @Test
+       public void sameFolderDoubleQuotes()
+       {
+               String input = ".class {background-image: url(\"some.img\");}";
+               Class<?> scope = CssUrlReplacerTest.class;
+               String cssRelativePath = "res/css/some.css";
+               CssUrlReplacer replacer = new CssUrlReplacer();
+
+               String processed = replacer.process(input, scope, 
cssRelativePath);
+               assertThat(
+                       processed,
+                       is(".class {background-image: 
url('./wicket/resource/org.apache.wicket.resource.CssUrlReplacerTest/res/css/some.img"
 +
+                               DECORATION_SUFFIX + "');}"));
+       }
+
+       @Test
+       public void parentFolderAppendFolder()
+       {
+               String input = ".class {background-image: 
url('../images/some.img');}";
+               Class<?> scope = CssUrlReplacerTest.class;
+               String cssRelativePath = "res/css/some.css";
+               CssUrlReplacer replacer = new CssUrlReplacer();
+
+               String processed = replacer.process(input, scope, 
cssRelativePath);
+               assertThat(
+                       processed,
+                       is(".class {background-image: 
url('./wicket/resource/org.apache.wicket.resource.CssUrlReplacerTest/res/images/some.img"
 +
+                               DECORATION_SUFFIX + "');}"));
+       }
+
+       @Test
+       public void sameFolderAppendFolder()
+       {
+               String input = ".class {background-image: 
url('./images/some.img');}";
+               Class<?> scope = CssUrlReplacerTest.class;
+               String cssRelativePath = "res/css/some.css";
+               CssUrlReplacer replacer = new CssUrlReplacer();
+
+               String processed = replacer.process(input, scope, 
cssRelativePath);
+               assertThat(
+                       processed,
+                       is(".class {background-image: 
url('./wicket/resource/org.apache.wicket.resource.CssUrlReplacerTest/res/css/images/some.img"
 +
+                               DECORATION_SUFFIX + "');}"));
+       }
+
+       @Test
+       public void severalUrls()
+       {
+               String input = ".class {\n" + "a: url('../images/a.img');\n" + 
"b: url('./b.img');\n" + "}";
+               Class<?> scope = CssUrlReplacerTest.class;
+               String cssRelativePath = "res/css/some.css";
+               CssUrlReplacer replacer = new CssUrlReplacer();
+
+               String processed = replacer.process(input, scope, 
cssRelativePath);
+               assertThat(processed, 
containsString("CssUrlReplacerTest/res/images/a.img" +
+                       DECORATION_SUFFIX + "');"));
+               assertThat(processed, 
containsString("CssUrlReplacerTest/res/css/b.img" +
+                       DECORATION_SUFFIX + "');"));
+       }
+}
\ No newline at end of file

Reply via email to