Commits

Fabian Kraemer committed baff3e4

Expose the JSoup document via a callback and default to a concise output format

  • Participants
  • Parent commits 47e3140

Comments (0)

Files changed (7)

 *.ipr
 *.iml
 *.iws
+\.classpath
+\.project
+\.settings

File src/main/java/com/atlassian/botocss/Botocss.java

 package com.atlassian.botocss;
 
-import cz.vutbr.web.css.*;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Pattern;
+
 import org.jsoup.Jsoup;
 import org.jsoup.nodes.Document;
 import org.jsoup.nodes.Element;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.util.ArrayList;
-import java.util.List;
-import java.util.regex.Pattern;
+import cz.vutbr.web.css.CombinedSelector;
+import cz.vutbr.web.css.Declaration;
+import cz.vutbr.web.css.RuleBlock;
+import cz.vutbr.web.css.RuleSet;
+import cz.vutbr.web.css.StyleSheet;
+import cz.vutbr.web.css.Term;
+import cz.vutbr.web.css.TermAngle;
+import cz.vutbr.web.css.TermColor;
+import cz.vutbr.web.css.TermIdent;
+import cz.vutbr.web.css.TermLength;
+import cz.vutbr.web.css.TermNumber;
+import cz.vutbr.web.css.TermNumeric;
+import cz.vutbr.web.css.TermPercent;
+import cz.vutbr.web.css.TermString;
+import cz.vutbr.web.css.TermTime;
+import cz.vutbr.web.css.TermURI;
 
 import static java.util.Collections.emptyList;
 
 
     /**
      * Injects the CSS from the stylesheets into the provided HTML as inline styles.
-     * Also applies any inline <style%gt; elements found in the HTML content.
+     * Also applies any inline <style> elements found in the HTML content.
      * <p/>
      * See the Botocss home page for documentation and caveats around the
      * injection process.
         return inject(html, styles);
     }
 
+    
     /**
      * Injects parsed stylesheets into the provided HTML as inline styles.
-     * Also applies any inline &lt;style%gt; elements found in the HTML content.
+     * Also applies any inline &lt;style&gt; elements found in the HTML content.
      * Use {@link #parse} to create a stylesheet object.
      * <p/>
      * See the Botocss home page for documentation and caveats around the
      * @see #parse
      */
     public static String inject(String html, BotocssStyles styles) {
-        long start = System.currentTimeMillis();
+        return inject(html, styles, DocumentProcessors.NOOP);
+    }
+    
+    /**
+     * Injects parsed stylesheets into the provided HTML as inline styles.
+     * Also applies any inline &lt;style&gt; elements found in the HTML content.
+     * Use {@link #parse} to create a stylesheet object.
+     * <p/>
+     * See the Botocss home page for documentation and caveats around the
+     * injection process.
+     * 
+     * @param html the HTML document
+     * @param styles the pre-parsed stylesheets containing the CSS to inject
+     * @param documentProcessor modify the parsed HTML document after injection before serializing
+     * @return the injected HTML string
+     * @throws NullPointerException if the provided HTML or styles are {@code null}
+     * @see #parse
+     */
+    public static String inject(String html, BotocssStyles styles, DocumentProcessor documentProcessor) {
+	long start = System.currentTimeMillis();
 
-        Document document = Jsoup.parse(html);
+        Document documentWithCSSInlined = Jsoup.parse(html);
         log.debug("Parsed HTML document in {} ms", System.currentTimeMillis() - start);
 
         int selectorCount = 0;
         
         log.debug("Applying external stylesheets");
-        selectorCount += applyStyles(document, styles);
+        selectorCount += applyStyles(documentWithCSSInlined, styles);
 
         log.debug("Finding inline stylesheets");
         List<String> inlineCss = new ArrayList<String>();
-        for (Element inlineStyle : document.select("style")) {
+        for (Element inlineStyle : documentWithCSSInlined.select("style")) {
             inlineCss.add(inlineStyle.html());
         }
         if (!inlineCss.isEmpty()) {
             BotocssStyles inlineStyles = BotocssStyles.parse(inlineCss.toArray(new String[inlineCss.size()]));
 
             log.debug("Applying inline stylesheets");
-            selectorCount += applyStyles(document, inlineStyles);
+            selectorCount += applyStyles(documentWithCSSInlined, inlineStyles);
         }
 
-        document.outputSettings().indentAmount(4);
-        String result = document.toString().replaceAll(" *\\n", "\n");
+        documentWithCSSInlined.outputSettings().prettyPrint(false);
+        documentWithCSSInlined.outputSettings().indentAmount(0);
+
+        Document postProcessedDocumentWithCSSInlined = documentProcessor.process(documentWithCSSInlined);
+        if(postProcessedDocumentWithCSSInlined == null) {
+            postProcessedDocumentWithCSSInlined = documentWithCSSInlined; // default to the given document
+        }
+        
+        String result = postProcessedDocumentWithCSSInlined.outerHtml();
 
         log.info("Applying {} CSS selectors to HTML (length {}) took {} ms",
                 new Object[] { selectorCount, html.length(), System.currentTimeMillis() - start });
         return result;
     }
 
-    private static int applyStyles(Document document, BotocssStyles styles) {
-        int selectorCount = 0;
-        for (StyleSheet styleSheet : styles.getStyleSheets()) {
-            selectorCount += applyStylesheet(document, styleSheet);
-        }
-        return selectorCount;
-    }
-
     /**
      * Parses the provided CSS and returns a list of pre-parsed Stylesheet objects for
      * use with {@link #inject(String, BotocssStyles)}.
     public static BotocssStyles parse(String... stylesheets) {
         return BotocssStyles.parse(stylesheets);
     }
+    
+    private static int applyStyles(Document document, BotocssStyles styles) {
+        int selectorCount = 0;
+        for (StyleSheet styleSheet : styles.getStyleSheets()) {
+            selectorCount += applyStylesheet(document, styleSheet);
+        }
+        return selectorCount;
+    }
 
     private static int applyStylesheet(Document document, StyleSheet stylesheet) {
         int selectorCount = 0;

File src/main/java/com/atlassian/botocss/BotocssStyles.java

 
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 
 /**
  * with the same stylesheet.
  */
 public final class BotocssStyles {
+    
+    /**
+     * Can be used to communicate that there's no external CSS information available. 
+     */
+    @SuppressWarnings("unchecked")
+    public static final BotocssStyles EMPTY = new BotocssStyles(Collections.EMPTY_LIST);
+    
     private static final Logger log = LoggerFactory.getLogger(BotocssStyles.class);
 
     private final Iterable<StyleSheet> styleSheets;

File src/main/java/com/atlassian/botocss/DocumentProcessor.java

+package com.atlassian.botocss;
+
+import org.jsoup.nodes.Document;
+
+/**
+ * Modify the JSoup document after injection before serialization.
+ * 
+ * @see Botocss#inject(String, BotocssStyles, BotocssDocumentProcessor)
+ */
+public interface DocumentProcessor {
+
+    /**
+     * @param document the document after injection
+     * @return a new document or null to indicate that the given document was modified
+     */
+    Document process(Document document);
+}

File src/main/java/com/atlassian/botocss/DocumentProcessors.java

+package com.atlassian.botocss;
+
+import org.jsoup.nodes.Document;
+
+/**
+ * Stateless processors which can be used to modify the JSoup document before
+ * serialization.
+ * 
+ * @see DocumentProcessor
+ */
+public enum DocumentProcessors implements DocumentProcessor {
+
+    NOOP(new DocumentProcessor() {
+	@Override
+	public Document process(Document document) {
+	    return document;
+	}
+    }),
+
+    /**
+     * Sets the output settings to pretty print and indent by 4 spaces.
+     */
+    PRETTY_PRINT(new DocumentProcessor() {
+	@Override
+	public Document process(Document document) {
+	    document.outputSettings().prettyPrint(true);
+	    document.outputSettings().indentAmount(4);
+	    return document;
+	}
+    });
+
+    private DocumentProcessor delegate;
+
+    private DocumentProcessors(DocumentProcessor delegate) {
+	this.delegate = delegate;
+    }
+
+    @Override
+    public Document process(Document document) {
+	return delegate.process(document);
+    }
+
+}

File src/test/java/com/atlassian/botocss/BotocssTest.java

 import static org.hamcrest.text.IsEqualIgnoringWhiteSpace.equalToIgnoringWhiteSpace;
 import static org.junit.Assert.assertThat;
 
-public class BotocssTest
-{
+public class BotocssTest {
     @Test
     public void testInjectString() throws Exception {
-        String html = "<html><head><title>Hello</title></head><body><p>Hello, world!</p></body></html>";
-        String css1 = "body { color: #f00; background: white; }";
-        String css2 = "p { margin-top: 10px; }";
-        String output = Botocss.inject(html, css1, css2);
-        assertThat(output, equalToIgnoringWhiteSpace("<html>\n<head>\n<title>Hello</title>\n</head>\n" +
-                        "<body style=\"color: #f00; background: #fff\">\n<p style=\"margin-top: 10px\">Hello, world!</p>\n</body>\n</html>"));
+	String html = "<html><head><title>Hello</title></head><body><p>Hello, world!</p></body></html>";
+	String css1 = "body { color: #f00; background: white; }";
+	String css2 = "p { margin-top: 10px; }";
+	String output = Botocss.inject(html, css1, css2);
+	assertThat(
+		output,
+		equalToIgnoringWhiteSpace("<html><head><title>Hello</title></head>"
+			+ "<body style=\"color: #f00; background: #fff\"><p style=\"margin-top: 10px\">Hello, world!</p></body></html>"));
     }
-    
-    @Test 
+
+    @Test
     public void testParseAndInject() throws Exception {
-        String html = "<html><head><title>Hello</title></head><body><p>Hello, world!</p></body></html>";
-        String css1 = "body { color: #f00; background: white; }";
-        String css2 = "p { margin-top: 10px; }";
-        BotocssStyles styles = Botocss.parse(css1, css2);
-        String output = Botocss.inject(html, styles);
-        assertThat(output, equalToIgnoringWhiteSpace("<html>\n<head>\n<title>Hello</title>\n</head>\n" +
-                "<body style=\"color: #f00; background: #fff\">\n<p style=\"margin-top: 10px\">Hello, world!</p>\n</body>\n</html>"));
+	String html = "<html><head><title>Hello</title></head><body><p>Hello, world!</p></body></html>";
+	String css1 = "body { color: #f00; background: white; }";
+	String css2 = "p { margin-top: 10px; }";
+	BotocssStyles styles = Botocss.parse(css1, css2);
+	String output = Botocss.inject(html, styles, DocumentProcessors.PRETTY_PRINT);
+	assertThat(
+		output,
+		equalToIgnoringWhiteSpace("<html>\n<head>\n<title>Hello</title>\n</head>\n"
+			+ "<body style=\"color: #f00; background: #fff\">\n<p style=\"margin-top: 10px\">Hello, world!</p>\n</body>\n</html>"));
     }
 }

File src/test/java/com/atlassian/botocss/InjectionTest.java

     public void testStyles() throws Exception {
         String html = getResource(getClass(), fileName + ".html");
         String css = getResource(getClass(), fileName + ".css");
-        String output = css == null ? Botocss.inject(html) : Botocss.inject(html, css);
+        String output = null;
+        if(css == null) {
+            output = Botocss.inject(html, BotocssStyles.EMPTY, DocumentProcessors.PRETTY_PRINT);
+        } else {
+            BotocssStyles parsedCss = Botocss.parse(css);
+            output = Botocss.inject(html, parsedCss, DocumentProcessors.PRETTY_PRINT);
+        }
         assertThat(fileName, output, equalToIgnoringWhiteSpace(getResource(getClass(), fileName + "-output.html")));
     }
 }