diff --git a/.classpath b/.classpath
deleted file mode 100644
index 8e189f0..0000000
--- a/.classpath
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/.project b/.project
deleted file mode 100644
index 9f35eb7..0000000
--- a/.project
+++ /dev/null
@@ -1,36 +0,0 @@
-
-
- CSSInliner
-
-
-
-
-
- org.eclipse.wst.common.project.facet.core.builder
-
-
-
-
- org.eclipse.jdt.core.javabuilder
-
-
-
-
- org.eclipse.wst.validation.validationbuilder
-
-
-
-
- org.eclipse.m2e.core.maven2Builder
-
-
-
-
-
- org.eclipse.jem.workbench.JavaEMFNature
- org.eclipse.wst.common.modulecore.ModuleCoreNature
- org.eclipse.m2e.core.maven2Nature
- org.eclipse.jdt.core.javanature
- org.eclipse.wst.common.project.facet.core.nature
-
-
diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs
deleted file mode 100644
index 107056a..0000000
--- a/.settings/org.eclipse.jdt.core.prefs
+++ /dev/null
@@ -1,12 +0,0 @@
-eclipse.preferences.version=1
-org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
-org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
-org.eclipse.jdt.core.compiler.compliance=1.6
-org.eclipse.jdt.core.compiler.debug.lineNumber=generate
-org.eclipse.jdt.core.compiler.debug.localVariable=generate
-org.eclipse.jdt.core.compiler.debug.sourceFile=generate
-org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
-org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
-org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
-org.eclipse.jdt.core.compiler.source=1.6
diff --git a/.settings/org.eclipse.m2e.core.prefs b/.settings/org.eclipse.m2e.core.prefs
deleted file mode 100644
index 84d23c8..0000000
--- a/.settings/org.eclipse.m2e.core.prefs
+++ /dev/null
@@ -1,4 +0,0 @@
-activeProfiles=nexus
-eclipse.preferences.version=1
-resolveWorkspaceProjects=true
-version=1
diff --git a/.settings/org.eclipse.wst.common.component b/.settings/org.eclipse.wst.common.component
deleted file mode 100644
index 5439c8a..0000000
--- a/.settings/org.eclipse.wst.common.component
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
diff --git a/.settings/org.eclipse.wst.common.project.facet.core.xml b/.settings/org.eclipse.wst.common.project.facet.core.xml
deleted file mode 100644
index 5c9bd75..0000000
--- a/.settings/org.eclipse.wst.common.project.facet.core.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/.settings/org.jboss.ide.eclipse.as.core.prefs b/.settings/org.jboss.ide.eclipse.as.core.prefs
deleted file mode 100644
index cf3aa3a..0000000
--- a/.settings/org.jboss.ide.eclipse.as.core.prefs
+++ /dev/null
@@ -1,2 +0,0 @@
-eclipse.preferences.version=1
-org.jboss.ide.eclipse.as.core.singledeployable.deployableList=
diff --git a/LICENSE b/LICENSE
deleted file mode 100644
index 6b0b127..0000000
--- a/LICENSE
+++ /dev/null
@@ -1,203 +0,0 @@
-
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "[]"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright [yyyy] [name of copyright owner]
-
- Licensed 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.
-
diff --git a/NOTICE b/NOTICE
deleted file mode 100644
index 5fe1301..0000000
--- a/NOTICE
+++ /dev/null
@@ -1,24 +0,0 @@
-CSSInliner
-Copyright 2012 Leonty Belskiy, leonti@eleonti.com.
-
-
-
-Licensed 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.
-
-
-
-This project includes PMD, Copyright (c) 2002-2009, InfoEther, Inc.
-All rights reserved. http://pmd.sourceforge.net/
-
-This product includes software developed in part by support from the Defense
-Advanced Research Project Agency (DARPA)
diff --git a/README.md b/README.md
index 579440c..642018d 100644
--- a/README.md
+++ b/README.md
@@ -12,11 +12,17 @@ If used for email, 'class' attributes can be removed:
##Build
-The dependency com.osbcp.cssparser:cssparser:1.4 is not available
-in maven central. Deploy the jar from lib/osbcp-css-parser-1.4.jar
-to your own maven repository so that it fits the dependency
-declaration in the pom.
-
+The dependency com.osbcp:cssparser:1.5 is now available
+in maven central.
+
+##Other similar libraries
+- CSS parsers
+ - [https://github.com/phax/ph-css](https://github.com/phax/ph-css)
+ - [http://stackoverflow.com/questions/1513587/looking-for-a-css-parser-in-java](http://stackoverflow.com/questions/1513587/looking-for-a-css-parser-in-java)
+ - [http://sourceforge.net/p/cssparser/](http://sourceforge.net/p/cssparser/)
+- Other resources
+ - [inline vs internal vs external css](https://vineetgupta22.wordpress.com/2011/07/09/inline-vs-internal-vs-external-css/)
+
##Changes
v1.0 2012-02-05 Leonti Bielski
@@ -30,4 +36,3 @@ Copyright 2012 Leonti Bielski, leonti.me
Licensed under the Apache License, Version 2.0
-
diff --git a/lib/osbcp-css-parser-1.4.jar b/lib/osbcp-css-parser-1.4.jar
deleted file mode 100644
index 72ca813..0000000
Binary files a/lib/osbcp-css-parser-1.4.jar and /dev/null differ
diff --git a/pom.xml b/pom.xml
index 2987b2f..1327883 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,22 +1,57 @@
-
+4.0.0com.leontyCSSInliner
- 1.0
-
-
-
- org.apache.maven.plugins
- maven-compiler-plugin
- 3.0
-
- 1.7
- 1.7
-
-
-
-
+ 1.5-SNAPSHOT
+ Java CSS Inliner
+ Utility to inline css styles in html document to be used in emails.
+ ${github.url}
+
+
+ The Apache License, Version 2.0
+ http://www.apache.org/licenses/LICENSE-2.0.txt
+
+
+
+
+ Leonti
+ https://github.com/Leonti
+
+
+ Tocco AG
+ https://www.tocco.ch
+
+
+ Costin Grigore
+ raisercostin@gmail.com
+ brainlight
+ http://raisercostin.org
+
+
+
+ 1.7
+ 1.7
+ UTF-8
+ raisercostin
+ CSSInliner
+ https://github.com/${github.user}/${github.repo}
+ raisercostin
+ maven
+ CSSInliner
+
+
+
+ scm:git:${github.url}.git
+ scm:git:${github.url}.git
+ ${github.url}
+ HEAD
+
+
+
+ bintray
+ https://api.bintray.com/maven/${bintray.user}/${bintray.repo}/${bintray.package}
+
+ org.jsoup
@@ -24,14 +59,95 @@
1.8.1
- com.osbcp.cssparser
+ com.osbcpcssparser
- 1.4
+ 1.7
+
+
+ ch.qos.logback
+ logback-classic
+ 1.1.3org.testngtestng6.8.8
+ test
+
+
+ junit
+ junit
+ 4.8.2
+ test
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.3
+
+ ${source.java.version}
+ ${target.java.version}
+ ${project.build.sourceEncoding}
+
+
+
+ maven-release-plugin
+ 2.5.2
+
+ false
+ release
+ true
+
+
+
+
+
+
+ release
+
+
+
+ maven-source-plugin
+
+
+ attach-sources
+
+ jar
+
+
+
+
+
+ maven-javadoc-plugin
+
+
+ attach-javadocs
+
+ jar
+
+
+
+
+
+
+
+
+
+
+ jcenter-bintray
+ Bintray JCenter Maven Repository
+ default
+ https://jcenter.bintray.com/
+
+ true
+
+
+ false
+
+
+
\ No newline at end of file
diff --git a/src/main/java/com/leonty/CSSInliner.java b/src/main/java/com/leonty/CSSInliner.java
index 3cbcb45..6fec2c5 100644
--- a/src/main/java/com/leonty/CSSInliner.java
+++ b/src/main/java/com/leonty/CSSInliner.java
@@ -1,5 +1,10 @@
package com.leonty;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -7,37 +12,70 @@
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
+import org.jsoup.select.Selector.SelectorParseException;
+import org.slf4j.LoggerFactory;
import com.osbcp.cssparser.CSSParser;
import com.osbcp.cssparser.PropertyValue;
import com.osbcp.cssparser.Rule;
public class CSSInliner {
+ private static final org.slf4j.Logger LOG = LoggerFactory
+ .getLogger(CSSInliner.class);
public static String inlineCss(String html, String css) throws Exception {
return inlineCss(html, css, false);
}
-
- public static String inlineCss(String html, String css, Boolean removeAttributes) throws Exception {
+
+ /** Returns the cleaned html that will be used for inlining css. */
+ public static String cleanup(String html) {
+ Document doc = Jsoup.parse(html);
+ return doc.html();
+ }
+
+ public static String inlineCss(String html, String css,
+ Boolean removeAttributes) throws Exception {
+ Document doc = Jsoup.parse(html);
+ HashMap> selected = new HashMap>();
+ css += getInternalStyles(doc);
+ extractSelectors(css, doc, selected);
+ mergeCss(removeAttributes, selected);
+ return doc.html();
+ }
+
+ public static String inlineCss(String html, Boolean removeAttributes)
+ throws Exception {
Document doc = Jsoup.parse(html);
HashMap> selected = new HashMap>();
-
- css += getDocumentStyles(doc);
- extractSelectors(css, doc, selected);
-
- mergeCss(removeAttributes, selected);
-
+ String css = readExternalStyles(doc);
+ remvoeExternalStyles(doc);
+ css += getInternalStyles(doc);
+ extractSelectors(css, doc, selected);
+ mergeCss(removeAttributes, selected);
+ return doc.html();
+ }
+
+ public static String internalCss(String html) throws IOException {
+ Document doc = Jsoup.parse(html);
+ String css = readExternalStyles(doc);
+ css += getInternalStyles(doc);
+ remvoeExternalStyles(doc);
+ Element head = doc.head();
+ head.append("");
return doc.html();
}
private static void mergeCss(Boolean removeAttributes,
HashMap> selected) {
- Iterator>> it = selected.entrySet().iterator();
+ Iterator>> it = selected.entrySet()
+ .iterator();
while (it.hasNext()) {
Map.Entry> pair = it.next();
@@ -52,66 +90,179 @@ private static void mergeCss(Boolean removeAttributes,
if (initialStyle != null && !initialStyle.isEmpty()) {
style += initialStyle;
}
-
+
pair.getKey().attr("style", style);
-
+
// if this is used for email we don't need id and style attributes
// as all of our styles are already inline
if (removeAttributes) {
- pair.getKey().removeAttr("class");
+ pair.getKey().removeAttr("class");
}
}
}
private static void extractSelectors(String css, Document doc,
HashMap> selected) throws Exception {
- List rules = CSSParser.parse(css);
+ extractSelectors(css, doc, selected, false);
+ }
+ private static void extractSelectors(String css, Document doc,
+ HashMap> selected, boolean debug)
+ throws Exception {
+ LOG.trace("extract selectors from [{}]", css);
+ List rules = CSSParser.parse(css);
+ ArrayList ignored = new ArrayList();
for (Rule rule : rules) {
-
for (com.osbcp.cssparser.Selector selector : rule.getSelectors()) {
-
- Elements elements = doc.select(selector.toString());
-
- for (Element element : elements) {
-
- // list of selectors to apply to this element
- ArrayList selectors = new ArrayList();
-
- // this element already has selectors attached to it - get reference to the list of existing selectors
- if (selected.containsKey(element)) {
- selectors = selected.get(element);
+ Try validSelector = select(doc, selector.toString());
+ if (validSelector.isDefined()) {
+ Elements elements = validSelector.get();
+ for (Element element : elements) {
+
+ // list of selectors to apply to this element
+ ArrayList selectors = new ArrayList();
+
+ // this element already has selectors attached to it
+ // -
+ // get
+ // reference to the list of existing selectors
+ if (selected.containsKey(element)) {
+ selectors = selected.get(element);
+ }
+
+ // add new selector containing styles to this dom
+ // element
+ selectors.add(new Selector(selector.toString(),
+ getStyleString(rule)));
+
+ selected.put(element, selectors);
}
-
- // add new selector containing styles to this dom element
- selectors.add(new Selector(selector.toString(), getStyleString(rule)));
-
- selected.put(element, selectors);
+ } else {
+ ignored.add(selector);
+ if (debug)
+ LOG.warn("Couldn't select with [" + selector.toString()
+ + "]", validSelector.failure());
}
}
}
+ LOG.warn(
+ "{} selectors where ignored while {} where returned. Switch to trace to get a list.",
+ ignored.size(), selected.size());
+ LOG.trace("The following selectors where ignored {}", ignored);
+ }
+
+ private static Try select(Document doc, String selector) {
+ try {
+ return Try.success(doc.select(selector));
+ } catch (SelectorParseException e) {
+ return Try.failure(e);
+ }
+ }
+
+ private static String readExternalStyles(Document doc) throws IOException {
+ Elements elements = selectExternalStyle(doc);
+ StringBuilder styles = new StringBuilder();
+ for (Element style : elements) {
+ String url = style.attr("href");
+ LOG.info("Download external css from [{}]", url);
+ String cssContent = Jsoup.connect(url).execute().body();
+ cssContent = replaceRelativeUrls(cssContent, url);
+ styles.append("/*external css from [").append(url).append("]*/\n")
+ .append(cssContent).append("\n");
+ }
+ return styles.toString();
+ }
+ private static final Pattern url1 = Pattern.compile("url\\([ ]*'([^']+)'[ ]*\\)");
+ private static final Pattern url2 = Pattern.compile("url\\([ ]*\"([^\"]+)\"[ ]*\\)");
+
+ private static String replaceRelativeUrls(String cssContent, String url) {
+ String result = cssContent;
+ result = replaceUrlWithSeparator(result, url, "'", url1);
+ result = replaceUrlWithSeparator(result, url, "\"", url2);
+ return result;
+ }
+
+ private static String replaceUrlWithSeparator(String cssContent,
+ String url, String separator, Pattern urlMatcher) {
+ try {
+ String result = cssContent;
+ Matcher m = urlMatcher.matcher(result);
+ StringBuffer sb = new StringBuffer();
+ URI fullURL = new URI(url);
+ URI parent = fullURL.getPath().endsWith("/") ? fullURL.resolve("..") : fullURL.resolve(".");
+ while (m.find()) {
+ String foundUrl = m.group(1);
+ //System.out.println(foundUrl + "->" + isAbsoluteURL(foundUrl));
+ String replacement = "";
+ if (isAbsoluteURL(foundUrl))
+ replacement = foundUrl;
+ else
+ replacement = parent.toString() + foundUrl;
+ m.appendReplacement(sb, "url("+separator+replacement+separator+")");
+ }
+ m.appendTail(sb);
+ result = sb.toString();
+ result = url2.matcher(result).replaceAll("url(\"$1\")");
+ return result;
+ } catch (URISyntaxException e) {
+ throw new RuntimeException(e);
+ }
}
-
- public static String getDocumentStyles(Document doc) {
+
+ /*see http://stackoverflow.com/questions/10159186/how-to-get-parent-url-in-java */
+ public static boolean isAbsoluteURL(String url) {
+ try {
+ final URL baseHTTP = new URL("http://example.com");
+ final URL baseFILE = new URL("file:///");
+ URL frelative = new URL(baseFILE, url);
+ URL hrelative = new URL(baseHTTP, url);
+ // System.err.println("DEBUG: file URL: " + frelative.toString());
+ // System.err.println("DEBUG: http URL: " + hrelative.toString());
+ return frelative.equals(hrelative);
+ } catch (MalformedURLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static boolean isAbsoluteURL2(String url) {
+ try {
+ URI u = new URI(url);
+ return u.isAbsolute();
+ } catch (URISyntaxException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static void remvoeExternalStyles(Document doc) {
+ Elements elements = selectExternalStyle(doc);
+ elements.remove();
+ }
+
+ private static Elements selectExternalStyle(Document doc) {
+ return doc.select("link[rel=stylesheet],link[type=text/css]");
+ }
+
+ public static String getInternalStyles(Document doc) throws IOException {
Elements elements = doc.select("style");
-
+
String styles = "";
for (Element style : elements) {
styles += style.html();
}
-
+
// we don't need style elements anymore
elements.remove();
return styles;
}
-
+
public static String getStyleString(Rule rule) {
String style = "";
-
+
for (PropertyValue propertyValue : rule.getPropertyValues()) {
- style += propertyValue.getProperty() + ": " + propertyValue.getValue() + "; ";
+ style += propertyValue.getProperty() + ": "
+ + propertyValue.getValue() + "; ";
}
-
+
return style;
}
}
diff --git a/src/main/java/com/leonty/Try.java b/src/main/java/com/leonty/Try.java
new file mode 100644
index 0000000..3a0b9a3
--- /dev/null
+++ b/src/main/java/com/leonty/Try.java
@@ -0,0 +1,75 @@
+package com.leonty;
+
+abstract class Try {
+
+ public abstract T get();
+
+ public abstract Throwable failure();
+
+ public abstract boolean isDefined();
+
+ public static Try success(T value) {
+ return new Success(value);
+ }
+
+ public static Try failure(Throwable e) {
+ return new Failure(e);
+ }
+
+ private static class Success extends Try {
+ private final T value;
+
+ public Success(T value) {
+ this.value = value;
+ }
+
+ @Override
+ public T get() {
+ return value;
+ }
+
+ @Override
+ public boolean isDefined() {
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "Success(" + value + ")";
+ }
+
+ @Override
+ public Throwable failure() {
+ throw new RuntimeException(
+ "The result is success. Cannot get a failure.");
+ }
+ }
+
+ private static class Failure extends Try {
+ public final Throwable error;
+
+ public Failure(Throwable error) {
+ this.error = error;
+ }
+
+ @Override
+ public T get() {
+ throw new RuntimeException("Try thrown an exception.", error);
+ }
+
+ @Override
+ public boolean isDefined() {
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return "Failure(" + error + ")";
+ }
+
+ @Override
+ public Throwable failure() {
+ return error;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/com/leonty/test/CSSInlinerTest.java b/src/test/java/com/leonty/test/CSSInlinerTest.java
index 76e2f12..7c8cb57 100644
--- a/src/test/java/com/leonty/test/CSSInlinerTest.java
+++ b/src/test/java/com/leonty/test/CSSInlinerTest.java
@@ -12,10 +12,11 @@
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.testng.annotations.DataProvider;
-import org.testng.annotations.Test;
-
-import static org.testng.Assert.assertEquals;
+import static org.junit.Assert.*;
+import org.junit.Test;
+//import org.testng.annotations.Test;
+//import static org.testng.AssertJUnit.assertEquals;
public class CSSInlinerTest {
@Test
@@ -25,10 +26,37 @@ public void test() throws Exception {
String inlinedHtml = readFile("inlined.html");
- assertEquals(CSSInliner.inlineCss(html, css, true), inlinedHtml);
+ //assertEquals(CSSInliner.inlineCss(html, css, true), inlinedHtml);
+ assertEquals(normalized(inlinedHtml), normalized(CSSInliner.inlineCss(html, css, true)));
+ }
+
+ @Test
+ public void test2() throws Exception {
+ String html = readFile("test2.html");
+ //String css = readFile("test.css");
+
+ String inlinedHtml = readFile("test2-inlined.html");
+
+ //assertEquals(CSSInliner.inlineCss(html, css, true), inlinedHtml);
+ assertEquals(normalized(CSSInliner.cleanup(inlinedHtml)), normalized(CSSInliner.inlineCss(html, true)));
+ }
+
+ @Test
+ public void testInternalStyle() throws Exception {
+ String html = readFile("test2.html");
+ //String css = readFile("test.css");
+
+ String internalHtml = readFile("test2-internal.html");
+
+ //assertEquals(CSSInliner.inlineCss(html, css, true), inlinedHtml);
+ assertEquals(normalized(CSSInliner.cleanup(internalHtml)), normalized(CSSInliner.internalCss(html)));
+ }
+
+ private String normalized(String text) {
+ return text.replaceAll("\\r\\n|\\n|\\r", "\n").replaceAll("\\t"," ").replaceAll("\\s+\\n","\n");
}
- @Test(dataProvider = "selectorPrecedenceData")
+ @org.testng.annotations.Test(dataProvider = "selectorPrecedenceData")
public void testSelectorPrecedence(String css, String initialStyleAttribute, String expectedStyleAttribute) throws Exception {
String html =
"\n" +
diff --git a/src/test/java/com/leonty/test/SelectorTest.java b/src/test/java/com/leonty/test/SelectorTest.java
index d51b5ff..3407df5 100644
--- a/src/test/java/com/leonty/test/SelectorTest.java
+++ b/src/test/java/com/leonty/test/SelectorTest.java
@@ -1,9 +1,12 @@
package com.leonty.test;
import com.leonty.Selector;
-import org.testng.annotations.Test;
-import static org.testng.AssertJUnit.assertEquals;
+import static org.junit.Assert.*;
+import org.junit.Test;
+
+//import org.testng.annotations.Test;
+//import static org.testng.AssertJUnit.assertEquals;
public class SelectorTest {
diff --git a/src/test/resources/com/leonty/test/test2-inlined.html b/src/test/resources/com/leonty/test/test2-inlined.html
new file mode 100644
index 0000000..c3f8da8
--- /dev/null
+++ b/src/test/resources/com/leonty/test/test2-inlined.html
@@ -0,0 +1,88 @@
+
+
+
+ Cauta -
+
+
+
+
+
+