13 Commits

Author SHA1 Message Date
David
8052cdaff1 [maven-release-plugin] prepare release j2html-0.99 2017-04-20 23:13:16 +02:00
David
34b2410830 Add withClasses(String... classes) method 2017-04-20 23:08:12 +02:00
David
75d361fa20 Merge pull request #51 from tipsy/iff-else
Add generic iff-method to use in method calls
2017-04-20 21:34:07 +02:00
David
3e91da0640 Add generic iff-method to use in method calls 2017-04-17 16:20:12 +02:00
David
d1613b0570 Change Error to RuntimeException 2017-04-17 15:35:06 +02:00
David
a841271ba4 Merge pull request #50 from tipsy/inline-static-fix
Fix #14 (Unable to read files from directories other that /src/main/r…
2017-04-16 10:37:09 +02:00
David
9272438940 Simplify resource handling and throw exception 2017-04-13 21:21:01 +02:00
David
3d43eb159f Fix #14 (Unable to read files from directories other that /src/main/resources) 2017-04-13 01:25:16 +02:00
David
30bfe4ea7f Small formatting fix 2017-04-12 19:40:26 +02:00
David
7409772c20 Update README.md 2017-04-12 19:30:26 +02:00
David
29e16df057 Improve escaper performance 2017-03-28 22:23:33 +02:00
David
6903bcbe4d Update README.md 2017-01-30 00:28:58 +01:00
David
56b830782f [maven-release-plugin] prepare for next development iteration 2017-01-29 21:32:54 +01:00
15 changed files with 230 additions and 50 deletions

View File

@@ -7,17 +7,17 @@ Java to HTML generator. Enjoy typesafe HTML generation.
The project webpage is [j2html.com](http://j2html.com).
##Getting started
###Add the maven dependency
## Getting started
### Add the maven dependency
```xml
<dependency>
<groupId>com.j2html</groupId>
<artifactId>j2html</artifactId>
<version>0.7</version>
<version>0.88</version>
</dependency>
```
###Import TagCreator and start building HTML
### Import TagCreator and start building HTML
```java
import static j2html.TagCreator.*;

View File

@@ -10,7 +10,7 @@
<groupId>com.j2html</groupId>
<artifactId>j2html</artifactId>
<version>0.88</version>
<version>0.99</version>
<name>j2html</name>
<description>Java to HTML builder with a fluent API</description>
@@ -52,6 +52,12 @@
<version>1.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.0</version>
<scope>test</scope>
</dependency>
</dependencies>
<packaging>jar</packaging>

View File

@@ -17,6 +17,25 @@ public class TagCreator {
private TagCreator() {}
/**
* Generic if-expression to do if'ing inside method calls
*
* @param <T> The derived generic parameter type
* @param condition the condition to if-on
* @param ifValue the value to return if condition is true
* @return value if condition is true, null otherwise
*/
public static <T> T iff(boolean condition, T ifValue) {
return condition ? ifValue : null;
}
/**
* Like {@link j2html.TagCreator#iff}, but returns else-value instead of null
*/
public static <T> T iffElse(boolean condition, T ifValue, T elseValue) {
return condition ? ifValue : elseValue;
}
/**
* Creates a DomContent object containing HTML using a mapping function on a collection
* Intended usage: {@literal each(numbers, n -> li(n.toString()))}

View File

@@ -22,7 +22,10 @@ public class ContainerTag extends Tag<ContainerTag> {
*/
public ContainerTag with(DomContent child) {
if (this == child) {
throw new Error("Cannot append a tag to itself.");
throw new RuntimeException("Cannot append a tag to itself.");
}
if (child == null) {
return this; // in some cases, like when using iff(), we ignore null children
}
children.add(child);
return this;

View File

@@ -1,48 +1,46 @@
package j2html.tags;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import j2html.utils.CSSMin;
import j2html.utils.JSMin;
import static j2html.TagCreator.rawHtml;
import static j2html.TagCreator.script;
import static j2html.TagCreator.style;
import static j2html.TagCreator.rawHtml;
public class InlineStaticResource {
public enum TargetFormat {CSS_MIN, CSS, JS_MIN, JS}
public static ContainerTag get(String path, TargetFormat format) {
ContainerTag errorAlert = script().with(rawHtml("alert('Unable to read file. File: \"" + path + "\", Type: \"" + format + "\"')"));
String fileString = getFileAsString(path);
if(fileString != null) {
switch(format) {
case CSS_MIN : return style().with(rawHtml(compressCss(fileString)));
case JS_MIN : return script().with(rawHtml(compressJs(fileString)));
switch (format) {
case CSS_MIN : return style().with(rawHtml(CSSMin.compress(fileString)));
case JS_MIN : return script().with(rawHtml(JSMin.compressJs(fileString)));
case CSS : return style().with(rawHtml(fileString));
case JS : return script().with(rawHtml(fileString));
default : return errorAlert;
default : throw new RuntimeException("Invalid target format");
}
}
return errorAlert;
}
public static String getFileAsString(String path) {
try {
return new String(Files.readAllBytes(Paths.get(InlineStaticResource.class.getResource(path).toURI())), "UTF-8");
} catch (Exception e) {
return null;
return readFileAsString(Paths.get(InlineStaticResource.class.getResource(path).toURI()));
} catch (Exception e1) {
try {
return readFileAsString(Paths.get(path));
} catch (Exception e2) {
throw new RuntimeException("Couldn't find file with path='" + path + "'");
}
}
}
private static String compressCss(String code) {
return CSSMin.compress(code);
}
private static String compressJs(String code) {
return JSMin.compressJs(code);
private static String readFileAsString(Path path) throws IOException {
return new String(Files.readAllBytes(path), "UTF-8");
}
}

View File

@@ -1,6 +1,8 @@
package j2html.tags;
import java.util.ArrayList;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import j2html.attributes.Attr;
import j2html.attributes.Attribute;
@@ -91,6 +93,16 @@ public abstract class Tag<T extends Tag<T>> extends DomContent {
* Convenience methods that call attr with predefined attributes
* @return itself for easy chaining
*/
public T withClasses(String... classes) {
return attr(Attr.CLASS,
Stream.of(classes)
.map(c -> c != null ? c : "")
.collect(Collectors.joining(" "))
.trim()
);
}
public T isAutoComplete() { return attr(Attr.AUTOCOMPLETE, null); }
public T isAutoFocus() { return attr(Attr.AUTOFOCUS, null); }
public T isHidden() { return attr(Attr.HIDDEN, null); }

View File

@@ -1,28 +1,36 @@
package j2html.utils;
import java.util.HashMap;
import java.util.Map;
public class SimpleEscaper {
private static Map<Character, String> map = new HashMap<Character, String>() {{
put('&', "&amp;");
put('<', "&lt;");
put('>', "&gt;");
put('"', "&quot;");
put('\'', "&#x27;");
}};
public static String escape(String s) {
if(s == null) {
if (s == null) {
return null;
}
String escapedString = "";
for(char c : s.toCharArray()) {
String escaped = map.get(c);
escapedString += escaped != null ? escaped : c;
StringBuilder escapedText = new StringBuilder();
char currentChar;
for (int i = 0; i < s.length(); i++) {
currentChar = s.charAt(i);
switch (currentChar) {
case '<':
escapedText.append("&lt;");
break;
case '>':
escapedText.append("&gt;");
break;
case '&':
escapedText.append("&amp;");
break;
case '"':
escapedText.append("&quot;");
break;
case '\'':
escapedText.append("&#x27;");
break;
default:
escapedText.append(currentChar);
}
return escapedString;
}
return escapedText.toString();
}
}

View File

@@ -0,0 +1 @@
public class AnyContent{}

7
src/test/files/test.css Normal file
View File

@@ -0,0 +1,7 @@
body {
background: goldenrod;
margin-top: 10px;
margin-right: 10px;
margin-bottom: 10px;
margin-left: 10px;
}

3
src/test/files/test.html Normal file
View File

@@ -0,0 +1,3 @@
<body>
Any content
</body>

6
src/test/files/test.js Normal file
View File

@@ -0,0 +1,6 @@
(function () {
var test = 5;
var tast = 10;
var testTast = test + tast;
console.log(testTast);
})();

View File

@@ -0,0 +1,49 @@
package j2html;
import java.util.concurrent.Callable;
import org.apache.commons.lang3.StringEscapeUtils;
import org.junit.Test;
import j2html.utils.SimpleEscaper;
public class PerformanceTest {
private String shortTestString = "<body>\n"
+ " <h1 class=\"example\">Heading!</h1>\n"
+ " <img src=\"img/hello.png\">\n"
+ "</body>";
// syntax-highlighted getting started example from j2html.com:
private String longTestString =
"<pre class=\" language-java\"><code class=\" language-java\"><span class=\"token keyword\">import</span> <span class=\"token keyword\">static</span> j2html<span class=\"token punctuation\">.</span>TagCreator<span class=\"token punctuation\">.</span>*<span class=\"token punctuation\">;</span>\n"
+ "\n"
+ "<span class=\"token keyword\">public</span> <span class=\"token keyword\">class</span> <span class=\"token class-name\">Main</span> <span class=\"token punctuation\">{</span>\n"
+ " <span class=\"token keyword\">public</span> <span class=\"token keyword\">static</span> <span class=\"token keyword\">void</span> <span class=\"token function\">main<span class=\"token punctuation\">(</span></span>String<span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span> args<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n"
+ " <span class=\"token function\">body<span class=\"token punctuation\">(</span></span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">with<span class=\"token punctuation\">(</span></span>\n"
+ " <span class=\"token function\">h1<span class=\"token punctuation\">(</span></span><span class=\"token string\">\"Heading!\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">withClass<span class=\"token punctuation\">(</span></span><span class=\"token string\">\"example\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n"
+ " <span class=\"token function\">img<span class=\"token punctuation\">(</span></span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">withSrc<span class=\"token punctuation\">(</span></span><span class=\"token string\">\"img/hello.png\"</span><span class=\"token punctuation\">)</span>\n"
+ " <span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">render<span class=\"token punctuation\">(</span></span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n"
+ " <span class=\"token punctuation\">}</span>\n"
+ "<span class=\"token punctuation\">}</span>\n"
+ "</code></pre>";
@Test
public void test_escaper_performnce() throws Exception {
timeEscaper("SimpleEscaper#short", () -> SimpleEscaper.escape(shortTestString));
timeEscaper("SimpleEscaper#long", () -> SimpleEscaper.escape(longTestString));
timeEscaper("ApacheEscaper#short", () -> StringEscapeUtils.escapeHtml4(shortTestString));
timeEscaper("ApacheEscaper#long", () -> StringEscapeUtils.escapeHtml4(longTestString));
}
private void timeEscaper(String name, Callable escaper) throws Exception {
long startTime = System.nanoTime();
for (int i = 0; i < 1000; i++) {
escaper.call();
}
long endTime = System.nanoTime();
long durationMs = (endTime - startTime) / 1000000;
System.out.println(String.format("%-21s%s", name + ":", +durationMs + "ms"));
}
}

View File

@@ -0,0 +1,45 @@
package j2html.tags;
import org.junit.Test;
import static j2html.TagCreator.fileAsEscapedString;
import static j2html.TagCreator.fileAsString;
import static j2html.TagCreator.scriptWithInlineFile_min;
import static j2html.TagCreator.styleWithInlineFile_min;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
public class InlineStaticResourceTest {
private static final String EOL = System.getProperty("line.separator"); // System independent End Of Line
@Test
public void testAllTags() throws Exception {
String expectedCss = "<style>body{background:#daa520;margin-bottom:10px;margin-left:10px;margin-right:10px;margin-top:10px}</style>";
String expectedJs = "<script>(function(){var test=5;var tast=10;var testTast=test+tast;console.log(testTast);})();</script>";
String expectedHtml = "<body>" + EOL + " Any content" + EOL + "</body>" + EOL;
String expectedEscapedHtml = "&lt;body&gt;" + EOL + " Any content" + EOL + "&lt;/body&gt;" + EOL;
String expectedAnyContent = "public class AnyContent{}" + EOL;
// classpath files
assertThat(styleWithInlineFile_min("/test.css").render(), is(expectedCss));
assertThat(scriptWithInlineFile_min("/test.js").render(), is(expectedJs));
assertThat(fileAsString("/test.html").render(), is(expectedHtml));
assertThat(fileAsEscapedString("/test.html").render(), is(expectedEscapedHtml));
assertThat(fileAsString("/AnyContent.java").render(), is(expectedAnyContent));
// files outside classpath
assertThat(styleWithInlineFile_min("src/test/files/test.css").render(), is(expectedCss));
assertThat(scriptWithInlineFile_min("src/test/files/test.js").render(), is(expectedJs));
assertThat(fileAsString("src/test/files/test.html").render(), is(expectedHtml));
assertThat(fileAsEscapedString("src/test/files/test.html").render(), is(expectedEscapedHtml));
assertThat(fileAsString("src/test/files/AnyContent.java").render(), is(expectedAnyContent));
}
@Test(expected=RuntimeException.class)
public void testExceptionForBadPath() throws Exception {
styleWithInlineFile_min("NOT A FILE");
}
}

View File

@@ -16,6 +16,24 @@ public class TagCreatorTest {
List<Employee> employees = Arrays.asList(new Employee(1, "Name 1", "Title 1"), new Employee(2, "Name 2", "Title 2"), new Employee(3, "Name 3", "Title 3"));
@Test
public void testIff() throws Exception {
String expected = "<div><p>Test</p><a href=\"#\">Test</a></div>";
String actual = div().with(
p("Test"),
iff(1 == 1, a("Test").withHref("#")),
iff(1 == 2, a("Tast").withHref("#"))
).render();
assertThat(actual, is(expected));
}
@Test
public void testIffElse() throws Exception {
String expected = "<div><p>Tast</p></div>";
String actual = div().with(iffElse(1 == 2, p("Test"), p("Tast"))).render();
assertThat(actual, is(expected));
}
@Test
public void testEach() throws Exception {
String j2htmlMap = ul().with(
@@ -65,11 +83,6 @@ public class TagCreatorTest {
assertThat(text("text").render(), is("text"));
assertThat(text("<script> and \"</script>\"").render(), is("&lt;script&gt; and &quot;&lt;/script&gt;&quot;"));
assertThat(rawHtml("<script>").render(), is("<script>"));
assertThat(styleWithInlineFile_min("/test.css").render(), is("<style>body{background:#daa520;margin-bottom:10px;margin-left:10px;margin-right:10px;margin-top:10px}</style>"));
assertThat(scriptWithInlineFile_min("/test.js").render(), is("<script>(function(){var test=5;var tast=10;var testTast=test+tast;console.log(testTast);})();</script>"));
assertThat(fileAsString("/test.html").render(), is("<body>" + EOL + " Any content" + EOL + "</body>" + EOL));
assertThat(fileAsEscapedString("/test.html").render(), is("&lt;body&gt;" + EOL + " Any content" + EOL + "&lt;/body&gt;" + EOL));
assertThat(fileAsString("/AnyContent.java").render(), is("public class AnyContent{}" + EOL));
//EmptyTags
assertThat(document().render(), is("<!DOCTYPE html>"));

View File

@@ -3,9 +3,11 @@ package j2html.tags;
import org.junit.Test;
import static j2html.TagCreator.body;
import static j2html.TagCreator.div;
import static j2html.TagCreator.footer;
import static j2html.TagCreator.header;
import static j2html.TagCreator.html;
import static j2html.TagCreator.iff;
import static j2html.TagCreator.main;
import static j2html.TagCreator.p;
import static j2html.TagCreator.tag;
@@ -49,4 +51,12 @@ public class TagTest {
assertThat(tagOne.equals(tagTwo), is(true));
}
@Test
public void testWithClasses() throws Exception {
String expected = "<div class=\"c1 c2\"></div>";
String actual = div().withClasses("c1", iff(1 == 1, "c2"), iff(1 == 2, "c3")).render();
assertThat(actual, is(expected));
}
}