Codebase list jatl / ad68e89
Merge tag 'upstream/0.2.3' Upstream version 0.2.3 Emmanuel Bourg 7 years ago
16 changed file(s) with 891 addition(s) and 50 deletion(s). Raw diff Collapse all Expand all
0
1 syntax: regexp
2 ^\.settings$
3 syntax: regexp
4 ^\.classpath$
5 syntax: regexp
6 ^\.project$
7 syntax: regexp
8 ^target$
0
1 # Java Anti-Template Language (JATL)
2
3 Is an extremely lightweight efficient Java library that generates XHTML or XML by using an a elegant [fluent styled](http://en.wikipedia.org/wiki/Fluent_interface) micro [DSL](http://en.wikipedia.org/wiki/Domain_specific_language).
4
5 I made this while developing my (now defunkt) [social taste recommendation engine](http://evocatus.com) and is heavily used in my [mobile recruiting](https://recruitinghop.com) company.
6
7 I find it particularly useful for creating complicated XML/HTML snippets programmatic-ly in Java that you can use as lambdas or helpers in [jMustache](https://github.com/samskivert/jmustache) or [Handlebars.java](https://github.com/jknack/handlebars.java) (respectively). Its also very good at creating extremely large XML documents while using very little memory.
8
9 Feel free to contact me on twitter: [@agentgt](http://twitter.com/agentgt).
10
11 ## News
12
13 Yes JATL has not been touched for awhile. Not because its not useful or dead but rather there were no changes that were needed.
14
15 HOWEVER Java 8 is out and actually works!! Finally the oracle overlords have bequeathed us with elegant deferred logic and sort of traits. Consequently JATL will probably probably be changed dramatically as there were many things people wanted to do that were rather difficult or not very elegant with the anonymous classes.
16
17
18 ### 0.3.0 Planned
19
20 * ~~Full~~ Better HTML5 support (XML style HTML5).
21 * Appendable instead of Writer (no need to wrap StringBuilder)
22 * Better Indentation strategy documentation
23 * Better escaping strategy (see [Issue #10](http://code.google.com/p/jatl/issues/detail?id=10))
24 * Remove Commons Lang dependency (JATL will have no dependencies)
25 * A generic concrete XML Builder and Writer (Issue #15).
26
27 ### 0.2.2 Released
28
29 * Issue #11 fixed. [New method of composition](http://site.jatl.googlecode.com/hg/apidocs/com/googlecode/jatl/MarkupWriter.html).
30 * Issue #16 fixed.
31
32 ### 0.2.1 Released
33
34 [Issue #10](http://code.google.com/p/jatl/issues/detail?id=10) fixed.
35
36 ### 0.2.0 Released
37
38 Experimental indentation strategy is in and OSGI support.
39
40 #### Better Indentation Support (more to come later):
41
42 [Indenter Unit Test Example](http://code.google.com/p/jatl/source/browse/src/test/java/com/googlecode/jatl/HtmlBuilderTest.java#537)
43
44 [Indenter Javadoc... not done yet](http://site.jatl.googlecode.com/hg/apidocs/com/googlecode/jatl/Indenter.html)
45
46 ## Download
47
48 Use Maven or its variants/emulators:
49
50 ```xml
51 <dependency>
52 <groupId>com.googlecode.jatl</groupId>
53 <artifactId>jatl</artifactId>
54 <version>0.2.2</version>
55 </dependency>
56 ```
57
58 The jars are in the central repository http://repo1.maven.org/maven2/com/googlecode/jatl/jatl.
59
60 ## Inspiration
61
62 Are you tired of using !StringBuffer to generate Markup (HTML/XML) but don't want to use a templating language or some XML library?
63
64 Do you want to be able to generate HTML like [Groovy's Markup Builders](http://groovy.codehaus.org/GroovyMarkup) or [Haml](http://haml-lang.com/ Ruby's) but with Java.
65
66 I know I do.
67
68 There are lots of advantages for using Java as templating language instead of something like Velocity, Freemarker, and Jelly. If you are using an IDE such as Eclipse with JATL you can get syntax highlighting, code refactoring and content assistance for free. Not to mention you now have one less language to remember and deal with.
69
70
71 See my posting on stackoverflow that caused me to write this library:
72 http://stackoverflow.com/questions/3583846/java-html-builder-anti-template-library
73
74 ## Examples
75
76 ```java
77 @Test
78 public void testExample() throws Exception {
79 //From http://codemonkeyism.com/the-best-markup-builder-i-could-build-in-java/
80 new Html(writer) {{
81 bind("id", "foo");
82 bind("coolName", "Awesomo");
83 html();
84 body();
85 h1().text("Name Games").end();
86 p().id("${id}").text("Hello ${coolName}, and hello").end();
87 makeList("Kyle","Stan", "Eric", "${coolName}");
88 endAll();
89 done();
90 }
91 Html makeList(String ... names) {
92 ul();
93 for(String name : names) {
94 li().text(name).end();
95 }
96 return end();
97 }
98 };
99 String result = writer.getBuffer().toString();
100 String expected = "\n" +
101 "<html>\n" +
102 " <body>\n" +
103 " <h1>Name Games\n" +
104 " </h1>\n" +
105 " <p id=\"foo\">Hello Awesomo, and hello\n" +
106 " </p>\n" +
107 " <ul>\n" +
108 " <li>Kyle\n" +
109 " </li>\n" +
110 " <li>Stan\n" +
111 " </li>\n" +
112 " <li>Eric\n" +
113 " </li>\n" +
114 " <li>Awesomo\n" +
115 " </li>\n" +
116 " </ul>\n" +
117 " </body>\n" +
118 "</html>";
119 assertEquals(expected, result);
120 }
121 ```
122
123 _Notice the double brace after the Html constructor. This is done on purpose._ Double braces on anonymous class definitions is how many libraries including JATL achieve a DSL.
124
125 *For more examples please browse the [unit test code](https://github.com/agentgt/jatl/blob/master/src/test/java/com/googlecode/jatl/HtmlBuilderTest.java).*
126
127 ## Features
128
129 ### Efficient
130
131 * Stream/Writer based thus very memory friendly for large documents.
132 * No unnecessary template preprocessing (parsing and AST building/walking).
133 * No reflection used. Templating languages like Velocity use reflection heavily.
134 * Very small footprint and only one dependency: commons-lang which most projects already have.
135
136 ### Flexible and easy to learn
137
138 * Its Java so you already know it.
139 * Easy to extend: [Use regular Java inheritance](http://site.jatl.googlecode.com/hg/apidocs/com/googlecode/jatl/MarkupBuilder.html).
140 * Composition is easy, [just keep adding methods JQuery plugin style](http://code.google.com/p/jatl/source/browse/src/test/java/com/googlecode/jatl/CustomHtmlBuilderTest.java) or use [deferred writers](http://site.jatl.googlecode.com/hg/apidocs/com/googlecode/jatl/MarkupWriter.html).
141 * You can generate snippets or an entire document.
142 * You can nest different markup builders (DSLs within DSL).
143 * No more worrying about [HTML escaping as it is done automatically](http://site.jatl.googlecode.com/hg/apidocs/com/googlecode/jatl/MarkupBuilder.html#text(java.lang.String)).
144 * Dreaded [Self Closing div tag](http://stackoverflow.com/questions/3907744/keep-jspx-from-creating-self-closing-tags-div-div-div) is no longer a problem with [flexible tag closing policy](http://site.jatl.googlecode.com/hg/apidocs/com/googlecode/jatl/MarkupBuilder.TagClosingPolicy.html)
145
146 ### Elegant and Convenient
147
148 * Use either fluent (`.method()`) style chaining or normal style (`method();`).
149 * No more typing annoying `<`, `>`
150 * No more grep'ing or searching for templates when they can be right in the controller.
151 * The XML/XHTML generated is pretty printed to the way you would expect. No more weird whitespace.
152 * [Fast variable expansion of `${...}`](http://site.jatl.googlecode.com/hg/apidocs/com/googlecode/jatl/MarkupBuilder.html#bind())
153 * [Easy to close tags](http://site.jatl.googlecode.com/hg/apidocs/com/googlecode/jatl/MarkupBuilder.html#end()).
154
155 ## Documentation
156
157 Please see [Javadoc](http://site.jatl.googlecode.com/hg/apidocs/index.html)
22
33 <groupId>com.googlecode.jatl</groupId>
44 <artifactId>jatl</artifactId>
5 <version>0.2.2</version>
5 <version>0.2.3</version>
66 <packaging>jar</packaging>
77
88 <name>jatl</name>
2424 <artifactId>oss-parent</artifactId>
2525 <version>5</version>
2626 </parent>
27
2728 <scm>
28 <connection>scm:hg:https://jatl.googlecode.com/hg/</connection>
29 <developerConnection>scm:hg:https://jatl.googlecode.com/hg/</developerConnection>
30 <url>https://jatl.googlecode.com/hg/</url>
29 <connection>scm:git:git@github.com:agentgt/jatl.git</connection>
30 <developerConnection>scm:git:git@github.com:agentgt/jatl.git</developerConnection>
31 <url>scm:git:git@github.com:agentgt/jatl.git</url>
32 <tag>HEAD</tag>
3133 </scm>
34
35
3236 <inceptionYear>2010</inceptionYear>
3337 <licenses>
3438 <license>
4650 </organization>
4751
4852 <issueManagement>
49 <system>googlecode</system>
50 <url>http://code.google.com/p/jatl/issues/list</url>
53 <system>github</system>
54 <url>https://github.com/agentgt/jatl/issues</url>
5155 </issueManagement>
5256
5357 <developers>
355359 <scope>test</scope>
356360 </dependency>
357361 <dependency>
358 <groupId>commons-lang</groupId>
359 <artifactId>commons-lang</artifactId>
360 <version>2.5</version>
362 <groupId>org.apache.commons</groupId>
363 <artifactId>commons-lang3</artifactId>
364 <version>3.4</version>
361365 <type>jar</type>
362 <scope>compile</scope>
366 <scope>test</scope>
363367 </dependency>
364368 </dependencies>
365369
1616 package com.googlecode.jatl;
1717
1818 import java.io.Writer;
19
20 import org.apache.commons.lang.StringEscapeUtils;
2119
2220 /**
2321 * Most of the XHTML tags and attributes are available as methods.
7068 return getSelf();
7169 }
7270
73 @Override
74 protected String escapeMarkup(String raw) {
75 return StringEscapeUtils.escapeHtml(raw);
76 }
77
7871 }
1515
1616 package com.googlecode.jatl;
1717
18 import java.io.StringWriter;
1819 import java.io.Writer;
1920
2021 /**
2122 * Writes HTML using an {@link HtmlBuilder}
2223 * by calling {@link #write(Writer)}.
2324 * <p>
24 * <strong>Example:</strong><p>
25 * <strong>Example:</strong>
26 * </p>
2527 * <pre>
2628 html = new HtmlWriter() {
2729 protected void build() {
6466 }
6567
6668 /**
69 * Uses a StringWriter to write the HTML created in {@link #build()}.
70 * {@inheritDoc}
71 */
72 @Override
73 public String toString() {
74 return write(new StringWriter()).getBuffer().toString();
75 }
76
77 /**
6778 * Should build the markup and is called by {@link #write(Writer)}.
79 * This method should describe the markup that should be built and is the entry point to the JATL DSL.
80 * If you are making your own custom {@link HtmlWriter} do not override this method
81 * as it is the method used by anonymous classes to describe the markup.
6882 * @see MarkupBuilder
6983 */
7084 protected abstract void build();
4343 * @param p closing policy for the tag to be indented.
4444 * @param empty if the tag is empty.
4545 * @see MarkupBuilder#indent(Indenter)
46 * @throws IOException
46 * @throws IOException pass through from orginal writer.
4747 */
4848 public void indentTag(
4949 Appendable a,
0 /**
1 * Copyright (C) 2012 the original author or authors.
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15 package com.googlecode.jatl;
16
17 import java.util.Map;
18
19 class InternalStrSubstitutor {
20
21 private Map<String, ?> map;
22
23 public InternalStrSubstitutor(Map<String, ?> map) {
24 this.map = map;
25 }
26
27 public String replace(
28 String str) {
29 if (str == null) return null;
30 StringBuilder sb = new StringBuilder();
31 char[] strArray = str.toCharArray();
32 int i = 0;
33 while (i < strArray.length - 1) {
34 if (strArray.length > 2 && strArray[i] == '$' && strArray[i + 1] == '$') {
35 sb.append(strArray[i]);
36 i+=2;
37 }
38 else if (strArray.length > 2 && strArray[i] == '$' && strArray[i + 1] == '{') {
39 final int o = i;
40 i = i + 2;
41 int begin = i;
42 while (i < strArray.length && (strArray[i] != '}')) {
43 ++i;
44 }
45 if (i >= strArray.length) {
46 sb.append(str.substring(o,strArray.length));
47 break;
48 }
49 String key = str.substring(begin, i++);
50 final Object value;
51 if (key.contains(":-")) {
52 String[] s = key.split(":-");
53 key = s[0];
54 if (map.containsKey(key)) {
55 value = map.get(key);
56 }
57 else {
58 value = s[1];
59 }
60 }
61 else {
62 value = map.get(key);
63 }
64 final boolean hasKey = map.containsKey(key);
65
66 if (value != null) {
67 sb.append(toString(value));
68 }
69 else if (value == null && hasKey) {
70 sb.append("${}");
71 }
72 else {
73 sb.append("${").append(key).append("}");
74 }
75
76 }
77 else {
78 sb.append(strArray[i]);
79 ++i;
80 }
81 }
82 if (i < strArray.length)
83 sb.append(strArray[i]);
84 return sb.toString();
85 }
86
87 private static String toString(Object o) {
88 return o.toString();
89 }
90
91 }
0 /**
1 * Copyright (C) 2012 the original author or authors.
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15 package com.googlecode.jatl;
16
17 class InternalValidationUtils {
18 public static boolean isBlank(
19 String str) {
20 int strLen;
21 if (str == null || (strLen = str.length()) == 0) {
22 return true;
23 }
24 for (int i = 0; i < strLen; i++) {
25 if ((Character.isWhitespace(str.charAt(i)) == false)) {
26 return false;
27 }
28 }
29 return true;
30 }
31 public static void isTrue(boolean b, String message) {
32 if (!b) throw new IllegalArgumentException(message);
33 }
34 public static void notEmpty(
35 String string,
36 String message) {
37 if (string == null || string.length() == 0) {
38 throw new IllegalArgumentException(message);
39 }
40 }
41 public static void notNull(Object o, String message) {
42 if (o == null) throw new IllegalArgumentException(message);
43 }
44 }
1515
1616 package com.googlecode.jatl;
1717
18 import static org.apache.commons.lang.StringUtils.isBlank;
19 import static org.apache.commons.lang.Validate.isTrue;
20 import static org.apache.commons.lang.Validate.notEmpty;
21 import static org.apache.commons.lang.Validate.notNull;
18 import static com.googlecode.jatl.InternalValidationUtils.isBlank;
19 import static com.googlecode.jatl.InternalValidationUtils.isTrue;
20 import static com.googlecode.jatl.InternalValidationUtils.notEmpty;
21 import static com.googlecode.jatl.InternalValidationUtils.notNull;
2222
2323 import java.io.IOException;
2424 import java.io.PrintWriter;
3030 import java.util.Map;
3131 import java.util.Map.Entry;
3232 import java.util.Stack;
33
34 import org.apache.commons.lang.text.StrSubstitutor;
3533
3634 import com.googlecode.jatl.Indenter.TagIndentSpot;
3735
266264 * the prefix is set to a non-null non-empty string, the prefix will
267265 * be added in front of the tag.
268266 * <p>
269 * <strong>Example:</strong><p>
267 * <strong>Example:</strong>
268 * </p>
270269 * <pre>
271270 * ns("html").div().end();
272271 * </pre>
272 * <p>
273273 * <strong>Result:</strong>
274 * <p>
274 * </p>
275275 * <pre>
276276 * &lt;html:div&gt;&lt;/html:div&gt;
277277 * </pre>
344344
345345 /**
346346 * Write text with out escaping or variable expansion.
347 * @param text
347 * @param text text to be directly written with escaping or expansion (be careful).
348348 * @return never <code>null</code>.
349349 * @see #raw(String, boolean)
350350 */
353353 }
354354 /**
355355 * Writes text with out escaping.
356 * @param text
356 * @param text maybe <code>null</code>, a <code>null</code> is a no operation.
357357 * @param expand <code>true</code> does variable expansion.
358358 * @return never <code>null</code>.
359359 */
371371 * and {@link #text(String) text}. Variables are represented with by <code>${...}</code>.
372372 * <p>
373373 * <strong>Example</strong>
374 * </p>
374375 * <pre>
375376 * bind("name", "Ferris");
376377 * text("${name}");
377378 * </pre>
378379 * <p>
379380 * <em>Variables are expanded in order and can be referred in a later binding.</em>
380 * <p>
381 * </p>
381382 * <pre>
382383 * bind("firstName", "Adam");
383384 * bind("lastName", "Gent");
388389 * @return never <code>null</code>.
389390 */
390391 public final T bind(String name, Object value) {
391 notEmpty(name);
392 notEmpty(name, "name");
392393 Object v = value != null && value instanceof String ? expand(value.toString()) : value;
393394 bindings.put(name, v);
394395 return getSelf();
423424 * Starts a tag using the default closing policy {@link TagClosingPolicy#NORMAL}.
424425 * <p>
425426 * Equivalent to: {@code start("tag", TagClosingPolicy.NORMAL)}.
426 *
427 * @param tag
427 * </p>
428 * @param tag the name of the tag, should never be <code>null</code>.
428429 * @return this, never <code>null</code>.
429430 * @see #start(String, TagClosingPolicy)
430431 */
460461 * @param attrs name value pairs. Its invalid for an odd number of arguments.
461462 * @return never <code>null</code>.
462463 * @throws IllegalArgumentException odd number of arguments.
464 * @throws IllegalStateException if there are no open start tags.
463465 */
464466 public final T attr(String ... attrs ) {
465 isTrue(attrs.length % 2 == 0);
467 isTrue(attrs.length % 2 == 0, "attributes should be multiple of 2 (name=value)");
468 if (tagStack.isEmpty() || (tagStack.peek().start)) {
469 throw new IllegalStateException("There are no open tags to add attributes too. " +
470 "Markup attributes should only be added " +
471 "immediatly after starting a tag.");
472 }
466473 checkWriter();
467474 for (int n = 0, v = 1; v < attrs.length; n+=2, v+=2) {
468475 getAttributes().put(attrs[n], attrs[v]);
507514 }
508515 /**
509516 * Closes the last {@link #start(String) start tag}.
510 * This is equivalent to <code>&lt;/tag&gt; or &lt;tag/&gt; depending
517 * This is equivalent to <code>&lt;/tag&gt;</code> or <code>&lt;tag/&gt;</code> depending
511518 * on the {@link TagClosingPolicy}.
512519 * @return never <code>null</code>.
513520 * @see #start(String, TagClosingPolicy)
652659 }
653660
654661 private String expand(String text) {
655 StrSubstitutor s = new StrSubstitutor(bindings);
662 InternalStrSubstitutor s = new InternalStrSubstitutor(bindings);
656663 return s.replace(text);
657664 }
658665
691698 * @see #text(String)
692699 * @see #escapeAttributeMarkup(String)
693700 */
694 @SuppressWarnings("deprecation")
695701 protected String escapeElementMarkup(String raw) {
696702 return escapeMarkup(raw);
697703 }
771777 * no child tags or text.
772778 * <ul>
773779 * <li><code>&lt;tag/&gt;</code></li>
774 * <li><code>&lt;/tag&gt;</code></li>
780 * <li><code>&lt;tag&gt;...&lt;/tag&gt;</code></li>
775781 * </ul>
776782 * <em>Unlike {@link #SELF self closing} tags a {@link #NORMAL} tag must be explicitly closed.</em>
777783 */
780786 /**
781787 * The tag is always a self closing tag.
782788 * <ul>
783 * <li><code>&lt;tag/&gt;</code></li>
789 * <li><code>&lt;tag/&gt;...</code></li>
784790 * </ul>
791 * <p>
785792 * <em>When a tag has this policy the tag can be implicitly closed
786 * by {@link MarkupBuilder#start(String, TagClosingPolicy) starting the next tag}:</em><p>
793 * by {@link MarkupBuilder#start(String, TagClosingPolicy) starting the next tag}:</em>
794 * </p>
787795 * <pre>
788796 * start("self",TagClosingPolicy.SELF).start("next");
789797 * </pre>
790 * Result:<p>
798 * <p>
799 * Result:
800 * </p>
791801 * <pre>
792802 * &lt;self/&gt;
793803 * &lt;next&gt;
794804 * ...
795805 * </pre>
796 *
806 *
797807 */
798808 SELF,
799809 /**
800810 * The tag is always closed with a matching closing tag
801811 * regardless if there is no child tag or text.
802812 * <ul>
803 * <li><code>&lt;tag/&gt;</code></li>
813 * <li><code>&lt;tag&gt;...&lt;/tag&gt;</code></li>
814 * <li><code>&lt;tag&gt;&lt;/tag&gt;</code></li>
804815 * </ul>
805816 */
806817 PAIR;
2323 * <p>
2424 * This is useful when you want to define the markup but do not
2525 * have the {@link Writer} yet.
26 * <p> Often this is the case when:
26 * </p> Often this is the case when:
2727 * <ul>
28 * <p>
2928 * <li>MVC framework (such as Spring MVC) where the writer is not available
3029 * till rendering time. </li>
3130 * <li>Composition of builders.</li>
3231 * </ul>
33 * You can achieve functional composition of markup builders by creating methods that return {@link MarkupWriter}s.
32 * <p>
33 * You can achieve <strong>functional composition</strong> of markup builders by creating methods that return {@link MarkupWriter}s.
34 * </p>
3435 * <pre>
35 private HtmlWriter createInput(final String name, final String value) {
36 private static HtmlWriter createInput(final String name, final String value) {
3637 return new HtmlWriter() {
3738 protected void build() {
3839 input().name(name).value(value).end();
4041 };
4142 }
4243
43 private HtmlWriter createForm(final HtmlWriter ... inputs) {
44 private static HtmlWriter createForm(final HtmlWriter ... inputs) {
4445 return new HtmlWriter() {
4546 protected void build() {
4647 form().write(inputs).end();
4849 };
4950 }
5051 * </pre>
52 * <em>Notice that the above methods are static as they are used as functions and do not mutate their containing class.</em>
53 * <p>
5154 * See {@link MarkupBuilder#write(MarkupWriter...)}
52 * <p>
55 * </p>
5356 * @author agent
5457 * @see HtmlWriter
5558 * @see MarkupBuilder#write(MarkupWriter...)
0 /**
1 * Copyright (C) 2012 the original author or authors.
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15 package com.googlecode.jatl;
16
17 import java.io.StringWriter;
18 import java.io.Writer;
19
20 /**
21 * For generic XML creation subclass this class for a custom XML writer or use {@link XmlWriter}.
22 * When subclassing make the child class <code>abstract</code> and do not implement {@link #build()}.
23 * The {@link #toString()} on this class will do what you expect: generate the XML as a string.
24 * @author agent
25 *
26 * @param <T> generally itself for fluent style
27 */
28 public abstract class XmlBuilderWriter<T> extends MarkupBuilder<T> implements MarkupBuilderWriter {
29
30 public XmlBuilderWriter() {
31 super();
32 }
33
34 public <W extends Writer> W write(W writer) {
35 setWriter(writer);
36 build();
37 done();
38 return writer;
39 }
40
41 public <W extends Writer> W write(W writer, int depth) {
42 setWriter(writer);
43 setDepth(depth);
44 build();
45 done();
46 return writer;
47 }
48
49 /**
50 * Uses a StringWriter to write the XML created in {@link #build()}.
51 * {@inheritDoc}
52 */
53 @Override
54 public String toString() {
55 return write(new StringWriter()).getBuffer().toString();
56 }
57
58 /**
59 * Should build the markup and is called by {@link #write(Writer)}.
60 * This method should describe the markup that should be built and is the entry point to the JATL DSL.
61 * If you are making your own custom {@link XmlWriter} do not override this method
62 * as it is the method used by anonymous classes to describe the markup.
63 * @see MarkupBuilder
64 */
65 protected abstract void build();
66
67 }
0 /**
1 * Copyright (C) 2012 the original author or authors.
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15 package com.googlecode.jatl;
16
17 /**
18 * For generic XML creation. If you want to generate HTML you might prefer {@link HtmlWriter}.
19 * The {@link #toString()} on this class will do what you expect: generate the XML as a string.
20 * @author agent
21 *
22 */
23 public abstract class XmlWriter extends XmlBuilderWriter<XmlWriter> {
24
25 @Override
26 protected XmlWriter getSelf() {
27 return this;
28 }
29
30 }
1616 /**
1717 * For quickstart documentation: <a href="http://jatl.googlecode.com/">http://jatl.googlecode.com/</a> and {@link com.googlecode.jatl.MarkupBuilder}.
1818 * <p>
19 * <strong>Example</strong><p>
19 * <strong>Example</strong>
20 * </p>
2021 * <pre>
2122 StringWriter sw = new StringWriter();
2223 new Html(sw) {{
4344 * </pre>
4445 * <p>
4546 * See {@link com.googlecode.jatl.MarkupBuilder} for creating your own markup builders.
47 * </p>
4648 */
4749 package com.googlecode.jatl;
4850
638638 assertEquals(expected, result);
639639 }
640640
641 @Test
642 public void testIssue18TagClosingPairPolicyFailing() throws Exception {
643 new Html(sw) {
644 {
645 div();
646 hr().text("");
647 end();
648 }
649 };
650 String result = writer.getBuffer().toString();
651 String expected = "\n" +
652 "<div>\n" +
653 " <hr/>\n" +
654 "</div>";
655 assertEquals(expected, result);
656 }
657
658 @Test(expected=IllegalStateException.class)
659 public void testIllegalAttributeOrder() throws Exception {
660 new Html(sw) {
661 {
662 div();
663 hr().text("");
664 attr("id", "cat");
665 div().end();
666 end();
667 }
668 };
669 writer.getBuffer().toString();
670 }
671
672 @Test(expected=IllegalStateException.class)
673 public void testIllegalAttributesBeforeStartTag() throws Exception {
674 new Html(sw) {
675 {
676 attr("id", "cat");
677 div();
678 end();
679 }
680 };
681 writer.getBuffer().toString();
682 }
683
641684 }
0 /**
1 * Copyright (C) 2012 the original author or authors.
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15 package com.googlecode.jatl;
16
17 import static org.junit.Assert.assertEquals;
18 import static org.junit.Assert.fail;
19
20 import java.util.HashMap;
21 import java.util.Map;
22
23 import org.junit.After;
24 import org.junit.Before;
25 import org.junit.Ignore;
26 import org.junit.Test;
27
28 /**
29 * Test class for InternalStrSubstitutor.
30 * Copied from commons lang.
31 */
32 public class InternalStrSubstitutorTest {
33
34 private Map<String, String> values;
35
36 @Before
37 public void setUp() throws Exception {
38 values = new HashMap<String, String>();
39 values.put("animal", "quick brown fox");
40 values.put("target", "lazy dog");
41 }
42
43 @After
44 public void tearDown() throws Exception {
45 values = null;
46 }
47
48 //-----------------------------------------------------------------------
49 /**
50 * Tests simple key replace.
51 */
52 @Test
53 public void testReplaceSimple() {
54 doTestReplace("The quick brown fox jumps over the lazy dog.", "The ${animal} jumps over the ${target}.", true);
55 }
56
57 /**
58 * Tests simple key replace.
59 */
60 @Test
61 public void testReplaceSolo() {
62 doTestReplace("quick brown fox", "${animal}", false);
63 }
64
65 /**
66 * Tests replace with no variables.
67 */
68 @Test
69 public void testReplaceNoVariables() {
70 doTestNoReplace("The balloon arrived.");
71 }
72
73 /**
74 * Tests replace with null.
75 */
76 @Test
77 public void testReplaceNull() {
78 doTestNoReplace(null);
79 }
80
81 /**
82 * Tests replace with null.
83 */
84 @Test
85 public void testReplaceEmpty() {
86 doTestNoReplace("");
87 }
88
89 /**
90 * Tests key replace changing map after initialization (not recommended).
91 */
92 @Test
93 public void testReplaceChangedMap() {
94 final InternalStrSubstitutor sub = new InternalStrSubstitutor(values);
95 values.put("target", "moon");
96 assertEquals("The quick brown fox jumps over the moon.", sub.replace("The ${animal} jumps over the ${target}."));
97 }
98
99 /**
100 * Tests unknown key replace.
101 */
102 @Test
103 public void testReplaceUnknownKey() {
104 doTestReplace("The ${person} jumps over the lazy dog.", "The ${person} jumps over the ${target}.", true);
105 //I'm not sure I understand how the falling is supposed to happen
106 //doTestReplace("The ${person} jumps over the lazy dog. 1234567890.", "The ${person} jumps over the ${target}. ${undefined.number:-1234567890}.", true);
107 }
108
109 /**
110 * Tests adjacent keys.
111 */
112 @Test
113 public void testReplaceAdjacentAtStart() {
114 values.put("code", "GBP");
115 values.put("amount", "12.50");
116 final InternalStrSubstitutor sub = new InternalStrSubstitutor(values);
117 assertEquals("GBP12.50 charged", sub.replace("${code}${amount} charged"));
118 }
119
120 /**
121 * Tests adjacent keys.
122 */
123 @Test
124 public void testReplaceAdjacentAtEnd() {
125 values.put("code", "GBP");
126 values.put("amount", "12.50");
127 final InternalStrSubstitutor sub = new InternalStrSubstitutor(values);
128 assertEquals("Amount is GBP12.50", sub.replace("Amount is ${code}${amount}"));
129 }
130
131 /**
132 * Tests simple recursive replace.
133 */
134 @Ignore // recursive is not supported right now.
135 @Test
136 public void testReplaceRecursive() {
137 values.put("animal", "${critter}");
138 values.put("target", "${pet}");
139 values.put("pet", "${petCharacteristic} dog");
140 values.put("petCharacteristic", "lazy");
141 values.put("critter", "${critterSpeed} ${critterColor} ${critterType}");
142 values.put("critterSpeed", "quick");
143 values.put("critterColor", "brown");
144 values.put("critterType", "fox");
145 doTestReplace("The quick brown fox jumps over the lazy dog.", "The ${animal} jumps over the ${target}.", true);
146
147 values.put("pet", "${petCharacteristicUnknown:-lazy} dog");
148 doTestReplace("The quick brown fox jumps over the lazy dog.", "The ${animal} jumps over the ${target}.", true);
149 }
150
151 /**
152 * Tests escaping.
153 */
154 @Test
155 public void testReplaceEscaping() {
156 doTestReplace("The ${animal} jumps over the lazy dog.", "The $${animal} jumps over the ${target}.", true);
157 }
158
159 /**
160 * Tests escaping.
161 */
162 @Test
163 public void testReplaceSoloEscaping() {
164 doTestReplace("${animal}", "$${animal}", false);
165 }
166
167 /**
168 * Tests complex escaping.
169 */
170 @Test
171 public void testReplaceComplexEscaping() {
172 doTestReplace("The ${quick brown fox} jumps over the lazy dog.", "The $${${animal}} jumps over the ${target}.", true);
173 //Still not sure what the weird number stuff is
174 //doTestReplace("The ${quick brown fox} jumps over the lazy dog. ${1234567890}.", "The $${${animal}} jumps over the ${target}. $${${undefined.number:-1234567890}}.", true);
175 }
176
177 /**
178 * Tests when no prefix or suffix.
179 */
180 @Test
181 public void testReplaceNoPrefixNoSuffix() {
182 doTestReplace("The animal jumps over the lazy dog.", "The animal jumps over the ${target}.", true);
183 }
184
185 /**
186 * Tests when no incomplete prefix.
187 */
188 @Test
189 public void testReplaceIncompletePrefix() {
190 doTestReplace("The {animal} jumps over the lazy dog.", "The {animal} jumps over the ${target}.", true);
191 }
192
193 /**
194 * Tests when prefix but no suffix.
195 */
196 @Test
197 public void testReplacePrefixNoSuffix() {
198 doTestReplace("The ${animal jumps over the ${target} lazy dog.", "The ${animal jumps over the ${target} ${target}.", true);
199 }
200
201 /**
202 * Tests when suffix but no prefix.
203 */
204 @Test
205 public void testReplaceNoPrefixSuffix() {
206 doTestReplace("The animal} jumps over the lazy dog.", "The animal} jumps over the ${target}.", true);
207 }
208
209 /**
210 * Tests when no variable name.
211 */
212 @Test
213 public void testReplaceEmptyKeys() {
214 doTestReplace("The ${} jumps over the lazy dog.", "The ${} jumps over the ${target}.", true);
215 doTestReplace("The animal jumps over the lazy dog.", "The ${:-animal} jumps over the ${target}.", true);
216 }
217
218 /**
219 * Tests replace creates output same as input.
220 */
221 @Ignore("recursion not supported")
222 @Test
223 public void testReplaceToIdentical() {
224 values.put("animal", "$${${thing}}");
225 values.put("thing", "animal");
226 doTestReplace("The ${animal} jumps.", "The ${animal} jumps.", true);
227 }
228
229 /**
230 * Tests a cyclic replace operation.
231 * The cycle should be detected and cause an exception to be thrown.
232 */
233 @Ignore("recursion not supported")
234 @Test
235 public void testCyclicReplacement() {
236 final Map<String, String> map = new HashMap<String, String>();
237 map.put("animal", "${critter}");
238 map.put("target", "${pet}");
239 map.put("pet", "${petCharacteristic} dog");
240 map.put("petCharacteristic", "lazy");
241 map.put("critter", "${critterSpeed} ${critterColor} ${critterType}");
242 map.put("critterSpeed", "quick");
243 map.put("critterColor", "brown");
244 map.put("critterType", "${animal}");
245 InternalStrSubstitutor sub = new InternalStrSubstitutor(map);
246 try {
247 sub.replace("The ${animal} jumps over the ${target}.");
248 fail("Cyclic replacement was not detected!");
249 } catch (final IllegalStateException ex) {
250 // expected
251 }
252
253 // also check even when default value is set.
254 map.put("critterType", "${animal:-fox}");
255 sub = new InternalStrSubstitutor(map);
256 try {
257 sub.replace("The ${animal} jumps over the ${target}.");
258 fail("Cyclic replacement was not detected!");
259 } catch (final IllegalStateException ex) {
260 // expected
261 }
262 }
263
264 /**
265 * Tests interpolation with weird boundary patterns.
266 */
267 @Test
268 public void testReplaceWeirdPattens() {
269 doTestNoReplace("");
270 doTestNoReplace("${}");
271 doTestNoReplace("${ }");
272 doTestNoReplace("${\t}");
273 doTestNoReplace("${\n}");
274 doTestNoReplace("${\b}");
275 doTestNoReplace("${");
276 doTestNoReplace("$}");
277 doTestNoReplace("}");
278 doTestNoReplace("${}$");
279 doTestNoReplace("${${");
280 doTestNoReplace("${${}}");
281 doTestNoReplace("${$${}}");
282 doTestNoReplace("${$$${}}");
283 doTestNoReplace("${$$${$}}");
284 doTestNoReplace("${${}}");
285 doTestNoReplace("${${ }}");
286 }
287
288
289 private void doTestNoReplace(final String replaceTemplate) {
290 final InternalStrSubstitutor sub = new InternalStrSubstitutor(values);
291
292 if (replaceTemplate == null) {
293 assertEquals(null, sub.replace((String) null));
294
295 } else {
296 assertEquals(replaceTemplate, sub.replace(replaceTemplate));
297 final StringBuilder bld = new StringBuilder(replaceTemplate);
298 //assertFalse(sub.replaceIn(bld));
299 assertEquals(replaceTemplate, bld.toString());
300 }
301 }
302
303
304 @Test
305 public void testSubstituteNoPreserveEscape() {
306 final String org = "${not-escaped} $${escaped}";
307 final Map<String, String> map = new HashMap<String, String>();
308 map.put("not-escaped", "value");
309
310 InternalStrSubstitutor sub = new InternalStrSubstitutor(map);
311 assertEquals("value ${escaped}", sub.replace(org));
312
313 }
314
315 //-----------------------------------------------------------------------
316 private void doTestReplace(final String expectedResult, final String replaceTemplate, final boolean substring) {
317 final InternalStrSubstitutor sub = new InternalStrSubstitutor(values);
318 assertEquals(expectedResult, sub.replace(replaceTemplate));
319
320 }
321 }
0 /**
1 * Copyright (C) 2012 the original author or authors.
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 package com.googlecode.jatl;
17
18 import static org.junit.Assert.assertEquals;
19
20 import org.junit.Test;
21
22 public class XmlWriterTest {
23
24 XmlWriter xml;
25
26 @Test
27 public void testWriter() throws Exception {
28 //Do not write yet.
29 xml = new XmlWriter() {
30 @Override
31 protected void build() {
32 start("element").attr("attribute", "first").indent(indentOff).end();
33 indent(indentOn);
34 start("element").attr("attribute", "second").end();
35 done();
36 }
37 };
38 //Now write.
39 String actual = xml.toString();
40 String expected = "<element attribute=\"first\"/>\n" +
41 "<element attribute=\"second\"/>";
42 assertEquals(expected, actual);
43
44 }
45 }