diff -Nru jatl-0.2.2/debian/changelog jatl-0.2.3/debian/changelog --- jatl-0.2.2/debian/changelog 2015-08-31 08:21:26.000000000 +0000 +++ jatl-0.2.3/debian/changelog 2016-10-29 22:32:32.000000000 +0000 @@ -1,3 +1,16 @@ +jatl (0.2.3-1) unstable; urgency=medium + + * New upstream release + - Depend on libcommons-lang3-java instead of libcommons-lang-java + * Build with the DH sequencer instead of CDBS + * Updated the Homepage field + * Fixed the Vcs-* fields + * Standards-Version updated to 3.9.8 + * Switch to debhelper level 10 + * Removed debian/orig-tar.sh + + -- Emmanuel Bourg Sun, 30 Oct 2016 00:32:32 +0200 + jatl (0.2.2-2) unstable; urgency=medium * Ignore the dependency on maven-scm (Closes: #797457) diff -Nru jatl-0.2.2/debian/compat jatl-0.2.3/debian/compat --- jatl-0.2.2/debian/compat 2013-10-03 07:24:20.000000000 +0000 +++ jatl-0.2.3/debian/compat 2016-10-29 22:27:32.000000000 +0000 @@ -1 +1 @@ -9 +10 diff -Nru jatl-0.2.2/debian/control jatl-0.2.3/debian/control --- jatl-0.2.2/debian/control 2015-08-31 08:10:07.000000000 +0000 +++ jatl-0.2.3/debian/control 2016-10-29 22:30:33.000000000 +0000 @@ -3,14 +3,14 @@ Priority: optional Maintainer: Debian Java Maintainers Uploaders: Emmanuel Bourg -Build-Depends: cdbs, debhelper (>= 9), default-jdk, maven-debian-helper (>= 1.5) +Build-Depends: debhelper (>= 10), default-jdk, maven-debian-helper (>= 1.5) Build-Depends-Indep: junit4, - libcommons-lang-java (>= 2.5), + libcommons-lang3-java, libmaven-bundle-plugin-java, -Standards-Version: 3.9.6 -Vcs-Svn: svn://anonscm.debian.org/pkg-java/trunk/jatl -Vcs-Browser: http://anonscm.debian.org/viewvc/pkg-java/trunk/jatl -Homepage: http://code.google.com/p/jatl/ +Standards-Version: 3.9.8 +Vcs-Git: https://anonscm.debian.org/git/pkg-java/jatl.git +Vcs-Browser: https://anonscm.debian.org/cgit/pkg-java/jatl.git +Homepage: https://github.com/agentgt/jatl Package: libjatl-java Architecture: all diff -Nru jatl-0.2.2/debian/copyright jatl-0.2.3/debian/copyright --- jatl-0.2.2/debian/copyright 2013-10-03 07:24:20.000000000 +0000 +++ jatl-0.2.3/debian/copyright 2016-10-29 22:27:32.000000000 +0000 @@ -1,6 +1,6 @@ Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: JATL -Source: http://jatl.googlecode.com/archive/jatl-0.2.2.zip +Source: https://github.com/agentgt/jatl Files: * Copyright: 2010-2013, Adam Gent diff -Nru jatl-0.2.2/debian/orig-tar.sh jatl-0.2.3/debian/orig-tar.sh --- jatl-0.2.2/debian/orig-tar.sh 2013-10-03 07:24:20.000000000 +0000 +++ jatl-0.2.3/debian/orig-tar.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,18 +0,0 @@ -#!/bin/sh -e - -VERSION=$2 -TAR=../jatl_$VERSION.orig.tar.xz - -wget http://jatl.googlecode.com/archive/jatl-$VERSION.zip -unzip jatl-$VERSION.zip -rm jatl-$VERSION.zip -XZ_OPT=--best tar -c -J -f $TAR --exclude '.hg*' jatl-* -rm -rf jatl-* $3 - -# move to directory 'tarballs' -if [ -r .svn/deb-layout ]; then - . .svn/deb-layout - mv $TAR $origDir - echo "moved $TAR to $origDir" -fi - diff -Nru jatl-0.2.2/debian/rules jatl-0.2.3/debian/rules --- jatl-0.2.2/debian/rules 2013-10-03 07:24:20.000000000 +0000 +++ jatl-0.2.3/debian/rules 2016-10-29 22:27:32.000000000 +0000 @@ -1,9 +1,7 @@ #!/usr/bin/make -f -include /usr/share/cdbs/1/rules/debhelper.mk -include /usr/share/cdbs/1/class/maven.mk - -JAVA_HOME := /usr/lib/jvm/default-java +%: + dh $@ get-orig-source: - uscan --download-version $(DEB_UPSTREAM_VERSION) --force-download --rename + uscan --download-current-version --force-download --rename --repack --compression xz diff -Nru jatl-0.2.2/debian/watch jatl-0.2.3/debian/watch --- jatl-0.2.2/debian/watch 2015-08-31 08:12:19.000000000 +0000 +++ jatl-0.2.3/debian/watch 2016-10-29 22:27:32.000000000 +0000 @@ -1,2 +1,2 @@ version=3 -https://github.com/agentgt/jatl/tags .*/jatl-([\d\.]+).tar.gz debian debian/orig-tar.sh +https://github.com/agentgt/jatl/tags .*/jatl-([\d\.]+).tar.gz diff -Nru jatl-0.2.2/.hgignore jatl-0.2.3/.hgignore --- jatl-0.2.2/.hgignore 1970-01-01 00:00:00.000000000 +0000 +++ jatl-0.2.3/.hgignore 2016-09-27 16:25:58.000000000 +0000 @@ -0,0 +1,9 @@ + +syntax: regexp +^\.settings$ +syntax: regexp +^\.classpath$ +syntax: regexp +^\.project$ +syntax: regexp +^target$ \ No newline at end of file diff -Nru jatl-0.2.2/pom.xml jatl-0.2.3/pom.xml --- jatl-0.2.2/pom.xml 1979-12-30 23:00:00.000000000 +0000 +++ jatl-0.2.3/pom.xml 2016-09-27 16:25:58.000000000 +0000 @@ -3,7 +3,7 @@ com.googlecode.jatl jatl - 0.2.2 + 0.2.3 jar jatl @@ -25,11 +25,15 @@ oss-parent 5 + - scm:hg:https://jatl.googlecode.com/hg/ - scm:hg:https://jatl.googlecode.com/hg/ - https://jatl.googlecode.com/hg/ + scm:git:git@github.com:agentgt/jatl.git + scm:git:git@github.com:agentgt/jatl.git + scm:git:git@github.com:agentgt/jatl.git + HEAD + + 2010 @@ -47,8 +51,8 @@ - googlecode - http://code.google.com/p/jatl/issues/list + github + https://github.com/agentgt/jatl/issues @@ -356,11 +360,11 @@ test - commons-lang - commons-lang - 2.5 + org.apache.commons + commons-lang3 + 3.4 jar - compile + test diff -Nru jatl-0.2.2/README.md jatl-0.2.3/README.md --- jatl-0.2.2/README.md 1970-01-01 00:00:00.000000000 +0000 +++ jatl-0.2.3/README.md 2016-09-27 16:25:58.000000000 +0000 @@ -0,0 +1,158 @@ + +# Java Anti-Template Language (JATL) + +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). + +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. + +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. + +Feel free to contact me on twitter: [@agentgt](http://twitter.com/agentgt). + +## News + +Yes JATL has not been touched for awhile. Not because its not useful or dead but rather there were no changes that were needed. + +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. + + +### 0.3.0 Planned + + * ~~Full~~ Better HTML5 support (XML style HTML5). + * Appendable instead of Writer (no need to wrap StringBuilder) + * Better Indentation strategy documentation + * Better escaping strategy (see [Issue #10](http://code.google.com/p/jatl/issues/detail?id=10)) + * Remove Commons Lang dependency (JATL will have no dependencies) + * A generic concrete XML Builder and Writer (Issue #15). + +### 0.2.2 Released + + * Issue #11 fixed. [New method of composition](http://site.jatl.googlecode.com/hg/apidocs/com/googlecode/jatl/MarkupWriter.html). + * Issue #16 fixed. + +### 0.2.1 Released + +[Issue #10](http://code.google.com/p/jatl/issues/detail?id=10) fixed. + +### 0.2.0 Released + +Experimental indentation strategy is in and OSGI support. + +#### Better Indentation Support (more to come later): + +[Indenter Unit Test Example](http://code.google.com/p/jatl/source/browse/src/test/java/com/googlecode/jatl/HtmlBuilderTest.java#537) + +[Indenter Javadoc... not done yet](http://site.jatl.googlecode.com/hg/apidocs/com/googlecode/jatl/Indenter.html) + +## Download + +Use Maven or its variants/emulators: + +```xml + + com.googlecode.jatl + jatl + 0.2.2 + +``` + +The jars are in the central repository http://repo1.maven.org/maven2/com/googlecode/jatl/jatl. + +## Inspiration + +Are you tired of using !StringBuffer to generate Markup (HTML/XML) but don't want to use a templating language or some XML library? + +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. + +I know I do. + +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. + + +See my posting on stackoverflow that caused me to write this library: +http://stackoverflow.com/questions/3583846/java-html-builder-anti-template-library + +## Examples + +```java + @Test + public void testExample() throws Exception { + //From http://codemonkeyism.com/the-best-markup-builder-i-could-build-in-java/ + new Html(writer) {{ + bind("id", "foo"); + bind("coolName", "Awesomo"); + html(); + body(); + h1().text("Name Games").end(); + p().id("${id}").text("Hello ${coolName}, and hello").end(); + makeList("Kyle","Stan", "Eric", "${coolName}"); + endAll(); + done(); + } + Html makeList(String ... names) { + ul(); + for(String name : names) { + li().text(name).end(); + } + return end(); + } + }; + String result = writer.getBuffer().toString(); + String expected = "\n" + + "\n" + + " \n" + + "

Name Games\n" + + "

\n" + + "

Hello Awesomo, and hello\n" + + "

\n" + + "
    \n" + + "
  • Kyle\n" + + "
  • \n" + + "
  • Stan\n" + + "
  • \n" + + "
  • Eric\n" + + "
  • \n" + + "
  • Awesomo\n" + + "
  • \n" + + "
\n" + + " \n" + + ""; + assertEquals(expected, result); + } +``` + +_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. + +*For more examples please browse the [unit test code](https://github.com/agentgt/jatl/blob/master/src/test/java/com/googlecode/jatl/HtmlBuilderTest.java).* + +## Features + +### Efficient + + * Stream/Writer based thus very memory friendly for large documents. + * No unnecessary template preprocessing (parsing and AST building/walking). + * No reflection used. Templating languages like Velocity use reflection heavily. + * Very small footprint and only one dependency: commons-lang which most projects already have. + +### Flexible and easy to learn + + * Its Java so you already know it. + * Easy to extend: [Use regular Java inheritance](http://site.jatl.googlecode.com/hg/apidocs/com/googlecode/jatl/MarkupBuilder.html). + * 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). + * You can generate snippets or an entire document. + * You can nest different markup builders (DSLs within DSL). + * 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)). + * 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) + +### Elegant and Convenient + + * Use either fluent (`.method()`) style chaining or normal style (`method();`). + * No more typing annoying `<`, `>` + * No more grep'ing or searching for templates when they can be right in the controller. + * The XML/XHTML generated is pretty printed to the way you would expect. No more weird whitespace. + * [Fast variable expansion of `${...}`](http://site.jatl.googlecode.com/hg/apidocs/com/googlecode/jatl/MarkupBuilder.html#bind()) + * [Easy to close tags](http://site.jatl.googlecode.com/hg/apidocs/com/googlecode/jatl/MarkupBuilder.html#end()). + +## Documentation + +Please see [Javadoc](http://site.jatl.googlecode.com/hg/apidocs/index.html) diff -Nru jatl-0.2.2/src/main/java/com/googlecode/jatl/HtmlBuilder.java jatl-0.2.3/src/main/java/com/googlecode/jatl/HtmlBuilder.java --- jatl-0.2.2/src/main/java/com/googlecode/jatl/HtmlBuilder.java 1979-12-30 23:00:00.000000000 +0000 +++ jatl-0.2.3/src/main/java/com/googlecode/jatl/HtmlBuilder.java 2016-09-27 16:25:58.000000000 +0000 @@ -18,8 +18,6 @@ import java.io.Writer; -import org.apache.commons.lang.StringEscapeUtils; - /** * Most of the XHTML tags and attributes are available as methods. * A rule of thumb is that most tags are zero parameter methods and attribute @@ -71,9 +69,4 @@ return getSelf(); } - @Override - protected String escapeMarkup(String raw) { - return StringEscapeUtils.escapeHtml(raw); - } - } diff -Nru jatl-0.2.2/src/main/java/com/googlecode/jatl/HtmlWriter.java jatl-0.2.3/src/main/java/com/googlecode/jatl/HtmlWriter.java --- jatl-0.2.2/src/main/java/com/googlecode/jatl/HtmlWriter.java 1979-12-30 23:00:00.000000000 +0000 +++ jatl-0.2.3/src/main/java/com/googlecode/jatl/HtmlWriter.java 2016-09-27 16:25:58.000000000 +0000 @@ -16,13 +16,15 @@ package com.googlecode.jatl; +import java.io.StringWriter; import java.io.Writer; /** * Writes HTML using an {@link HtmlBuilder} * by calling {@link #write(Writer)}. *

- * Example:

+ * Example: + *

*
 	html = new HtmlWriter() {
 		protected void build() {
@@ -65,7 +67,19 @@
 	}	
 	
 	/**
+	 * Uses a StringWriter to write the HTML created in {@link #build()}. 
+	 * {@inheritDoc}
+	 */
+	@Override
+	public String toString() {
+		return write(new StringWriter()).getBuffer().toString();
+	}
+	
+	/**
 	 * Should build the markup and is called by {@link #write(Writer)}.
+	 * This method should describe the markup that should be built and is the entry point to the JATL DSL.
+	 * If you are making your own custom {@link HtmlWriter} do not override this method
+	 * as it is the method used by anonymous classes to describe the markup.
 	 * @see MarkupBuilder
 	 */
 	protected abstract void build();
diff -Nru jatl-0.2.2/src/main/java/com/googlecode/jatl/Indenter.java jatl-0.2.3/src/main/java/com/googlecode/jatl/Indenter.java
--- jatl-0.2.2/src/main/java/com/googlecode/jatl/Indenter.java	1979-12-30 23:00:00.000000000 +0000
+++ jatl-0.2.3/src/main/java/com/googlecode/jatl/Indenter.java	2016-09-27 16:25:58.000000000 +0000
@@ -44,7 +44,7 @@
 	 * @param p closing policy for the tag to be indented.
 	 * @param empty if the tag is empty.
 	 * @see MarkupBuilder#indent(Indenter)
-	 * @throws IOException
+	 * @throws IOException pass through from orginal writer. 
 	 */
 	public void indentTag(
 			Appendable a,
diff -Nru jatl-0.2.2/src/main/java/com/googlecode/jatl/InternalStrSubstitutor.java jatl-0.2.3/src/main/java/com/googlecode/jatl/InternalStrSubstitutor.java
--- jatl-0.2.2/src/main/java/com/googlecode/jatl/InternalStrSubstitutor.java	1970-01-01 00:00:00.000000000 +0000
+++ jatl-0.2.3/src/main/java/com/googlecode/jatl/InternalStrSubstitutor.java	2016-09-27 16:25:58.000000000 +0000
@@ -0,0 +1,92 @@
+/**
+ * Copyright (C) 2012 the original author or authors.
+ *
+ * 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.
+ */
+package com.googlecode.jatl;
+
+import java.util.Map;
+
+class InternalStrSubstitutor {
+
+	private Map map;
+
+	public InternalStrSubstitutor(Map map) {
+	        this.map = map;
+	    }
+
+	public String replace(
+			String str) {
+		if (str == null) return null;
+		StringBuilder sb = new StringBuilder();
+		char[] strArray = str.toCharArray();
+		int i = 0;
+		while (i < strArray.length - 1) {
+			if (strArray.length > 2 && strArray[i] == '$' && strArray[i + 1] == '$') {
+				sb.append(strArray[i]);
+				i+=2;
+			}
+			else if (strArray.length > 2 && strArray[i] == '$' && strArray[i + 1] == '{') {
+				final int o = i;
+				i = i + 2;
+				int begin = i;
+				while (i < strArray.length && (strArray[i] != '}')) {
+					++i;
+				}
+				if (i >= strArray.length) {
+					sb.append(str.substring(o,strArray.length));
+					break;
+				}
+				String key  = str.substring(begin, i++);
+				final Object value;
+				if (key.contains(":-")) {
+					String[] s = key.split(":-");
+					key = s[0];
+					if (map.containsKey(key)) {
+						value = map.get(key);
+					}
+					else {
+						value = s[1];
+					}
+				}
+				else {
+					value = map.get(key);	
+				}
+				final boolean hasKey = map.containsKey(key);
+				
+				if (value != null) {
+					sb.append(toString(value));	
+				}
+				else if (value == null && hasKey) {
+					sb.append("${}");
+				}
+				else {
+					sb.append("${").append(key).append("}");
+				}
+				
+			}
+			else {
+				sb.append(strArray[i]);
+				++i;
+			}
+		}
+		if (i < strArray.length)
+			sb.append(strArray[i]);
+		return sb.toString();
+	}
+
+	private static String toString(Object o) {
+		return o.toString();
+	}
+
+}
diff -Nru jatl-0.2.2/src/main/java/com/googlecode/jatl/InternalValidationUtils.java jatl-0.2.3/src/main/java/com/googlecode/jatl/InternalValidationUtils.java
--- jatl-0.2.2/src/main/java/com/googlecode/jatl/InternalValidationUtils.java	1970-01-01 00:00:00.000000000 +0000
+++ jatl-0.2.3/src/main/java/com/googlecode/jatl/InternalValidationUtils.java	2016-09-27 16:25:58.000000000 +0000
@@ -0,0 +1,45 @@
+/**
+ * Copyright (C) 2012 the original author or authors.
+ *
+ * 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.
+ */
+package com.googlecode.jatl;
+
+class InternalValidationUtils {
+	public static boolean isBlank(
+			String str) {
+		int strLen;
+		if (str == null || (strLen = str.length()) == 0) {
+			return true;
+		}
+		for (int i = 0; i < strLen; i++) {
+			if ((Character.isWhitespace(str.charAt(i)) == false)) {
+				return false;
+			}
+		}
+		return true;
+	}
+	public static void isTrue(boolean b, String message) {
+		if (!b) throw new IllegalArgumentException(message);
+	}
+	public static void notEmpty(
+			String string,
+			String message) {
+		if (string == null || string.length() == 0) {
+			throw new IllegalArgumentException(message);
+		}
+	}
+	public static void notNull(Object o, String message) {
+		if (o == null) throw new IllegalArgumentException(message);
+	}
+}
diff -Nru jatl-0.2.2/src/main/java/com/googlecode/jatl/MarkupBuilder.java jatl-0.2.3/src/main/java/com/googlecode/jatl/MarkupBuilder.java
--- jatl-0.2.2/src/main/java/com/googlecode/jatl/MarkupBuilder.java	1979-12-30 23:00:00.000000000 +0000
+++ jatl-0.2.3/src/main/java/com/googlecode/jatl/MarkupBuilder.java	2016-09-27 16:25:58.000000000 +0000
@@ -16,10 +16,10 @@
 
 package com.googlecode.jatl;
 
-import static org.apache.commons.lang.StringUtils.isBlank;
-import static org.apache.commons.lang.Validate.isTrue;
-import static org.apache.commons.lang.Validate.notEmpty;
-import static org.apache.commons.lang.Validate.notNull;
+import static com.googlecode.jatl.InternalValidationUtils.isBlank;
+import static com.googlecode.jatl.InternalValidationUtils.isTrue;
+import static com.googlecode.jatl.InternalValidationUtils.notEmpty;
+import static com.googlecode.jatl.InternalValidationUtils.notNull;
 
 import java.io.IOException;
 import java.io.PrintWriter;
@@ -32,8 +32,6 @@
 import java.util.Map.Entry;
 import java.util.Stack;
 
-import org.apache.commons.lang.text.StrSubstitutor;
-
 import com.googlecode.jatl.Indenter.TagIndentSpot;
 
 /**
@@ -267,12 +265,14 @@
 	 * the prefix is set to a non-null non-empty string, the prefix will
 	 * be added in front of the tag.
 	 * 

- * Example:

+ * Example: + *

*
 	 * ns("html").div().end();
 	 * 
- * Result: *

+ * Result: + *

*
 	 * <html:div></html:div>
 	 * 
@@ -345,7 +345,7 @@ /** * Write text with out escaping or variable expansion. - * @param text + * @param text text to be directly written with escaping or expansion (be careful). * @return never null. * @see #raw(String, boolean) */ @@ -354,7 +354,7 @@ } /** * Writes text with out escaping. - * @param text + * @param text maybe null, a null is a no operation. * @param expand true does variable expansion. * @return never null. */ @@ -372,13 +372,14 @@ * and {@link #text(String) text}. Variables are represented with by ${...}. *

* Example + *

*
 	 * bind("name", "Ferris");
 	 * text("${name}");
 	 * 
*

* Variables are expanded in order and can be referred in a later binding. - *

+ *

*
 	 * bind("firstName", "Adam");
 	 * bind("lastName", "Gent");
@@ -389,7 +390,7 @@
 	 * @return never null.
 	 */
 	public final T bind(String name, Object value) {
-		notEmpty(name);
+		notEmpty(name, "name");
 		Object v = value != null && value instanceof String ? expand(value.toString()) : value;
 	    bindings.put(name, v);
 	    return getSelf();
@@ -424,8 +425,8 @@
 	 * Starts a tag using the default closing policy {@link TagClosingPolicy#NORMAL}.
 	 * 

* Equivalent to: {@code start("tag", TagClosingPolicy.NORMAL)}. - * - * @param tag + *

+ * @param tag the name of the tag, should never be null. * @return this, never null. * @see #start(String, TagClosingPolicy) */ @@ -461,9 +462,15 @@ * @param attrs name value pairs. Its invalid for an odd number of arguments. * @return never null. * @throws IllegalArgumentException odd number of arguments. + * @throws IllegalStateException if there are no open start tags. */ public final T attr(String ... attrs ) { - isTrue(attrs.length % 2 == 0); + isTrue(attrs.length % 2 == 0, "attributes should be multiple of 2 (name=value)"); + if (tagStack.isEmpty() || (tagStack.peek().start)) { + throw new IllegalStateException("There are no open tags to add attributes too. " + + "Markup attributes should only be added " + + "immediatly after starting a tag."); + } checkWriter(); for (int n = 0, v = 1; v < attrs.length; n+=2, v+=2) { getAttributes().put(attrs[n], attrs[v]); @@ -508,7 +515,7 @@ } /** * Closes the last {@link #start(String) start tag}. - * This is equivalent to </tag> or <tag/> depending + * This is equivalent to </tag> or <tag/> depending * on the {@link TagClosingPolicy}. * @return never null. * @see #start(String, TagClosingPolicy) @@ -653,7 +660,7 @@ } private String expand(String text) { - StrSubstitutor s = new StrSubstitutor(bindings); + InternalStrSubstitutor s = new InternalStrSubstitutor(bindings); return s.replace(text); } @@ -692,7 +699,6 @@ * @see #text(String) * @see #escapeAttributeMarkup(String) */ - @SuppressWarnings("deprecation") protected String escapeElementMarkup(String raw) { return escapeMarkup(raw); } @@ -772,7 +778,7 @@ * no child tags or text. *
    *
  • <tag/>
  • - *
  • </tag>
  • + *
  • <tag>...</tag>
  • *
* Unlike {@link #SELF self closing} tags a {@link #NORMAL} tag must be explicitly closed. */ @@ -781,27 +787,32 @@ /** * The tag is always a self closing tag. *
    - *
  • <tag/>
  • + *
  • <tag/>...
  • *
+ *

* When a tag has this policy the tag can be implicitly closed - * by {@link MarkupBuilder#start(String, TagClosingPolicy) starting the next tag}:

+ * by {@link MarkupBuilder#start(String, TagClosingPolicy) starting the next tag}: + *

*
 		 * start("self",TagClosingPolicy.SELF).start("next");
 		 * 
- * Result:

+ *

+ * Result: + *

*
 		 * <self/>
 		 * <next>
 		 * ...
 		 * 
- * + * */ SELF, /** * The tag is always closed with a matching closing tag * regardless if there is no child tag or text. *
    - *
  • <tag/>
  • + *
  • <tag>...</tag>
  • + *
  • <tag></tag>
  • *
*/ PAIR; diff -Nru jatl-0.2.2/src/main/java/com/googlecode/jatl/MarkupWriter.java jatl-0.2.3/src/main/java/com/googlecode/jatl/MarkupWriter.java --- jatl-0.2.2/src/main/java/com/googlecode/jatl/MarkupWriter.java 1979-12-30 23:00:00.000000000 +0000 +++ jatl-0.2.3/src/main/java/com/googlecode/jatl/MarkupWriter.java 2016-09-27 16:25:58.000000000 +0000 @@ -24,16 +24,17 @@ *

* This is useful when you want to define the markup but do not * have the {@link Writer} yet. - *

Often this is the case when: + *

Often this is the case when: *
    - *

    *

  • MVC framework (such as Spring MVC) where the writer is not available * till rendering time.
  • *
  • Composition of builders.
  • *
- * You can achieve functional composition of markup builders by creating methods that return {@link MarkupWriter}s. + *

+ * You can achieve functional composition of markup builders by creating methods that return {@link MarkupWriter}s. + *

*
-	private HtmlWriter createInput(final String name, final String value) {
+	private static HtmlWriter createInput(final String name, final String value) {
 		return new HtmlWriter() {
 			protected void build() {
 				input().name(name).value(value).end();
@@ -41,7 +42,7 @@
 		};
 	}
 	
-	private HtmlWriter createForm(final HtmlWriter ... inputs) {
+	private static HtmlWriter createForm(final HtmlWriter ... inputs) {
 		return new HtmlWriter() {
 			protected void build() {
 				form().write(inputs).end();
@@ -49,8 +50,10 @@
 		};
 	}
  * 
- * See {@link MarkupBuilder#write(MarkupWriter...)} + * Notice that the above methods are static as they are used as functions and do not mutate their containing class. *

+ * See {@link MarkupBuilder#write(MarkupWriter...)} + *

* @author agent * @see HtmlWriter * @see MarkupBuilder#write(MarkupWriter...) diff -Nru jatl-0.2.2/src/main/java/com/googlecode/jatl/package-info.java jatl-0.2.3/src/main/java/com/googlecode/jatl/package-info.java --- jatl-0.2.2/src/main/java/com/googlecode/jatl/package-info.java 1979-12-30 23:00:00.000000000 +0000 +++ jatl-0.2.3/src/main/java/com/googlecode/jatl/package-info.java 2016-09-27 16:25:58.000000000 +0000 @@ -17,7 +17,8 @@ /** * For quickstart documentation: http://jatl.googlecode.com/ and {@link com.googlecode.jatl.MarkupBuilder}. *

- * Example

+ * Example + *

*
 	StringWriter sw = new StringWriter();
 	new Html(sw) {{
@@ -44,6 +45,7 @@
  * 
*

* See {@link com.googlecode.jatl.MarkupBuilder} for creating your own markup builders. + *

*/ package com.googlecode.jatl; diff -Nru jatl-0.2.2/src/main/java/com/googlecode/jatl/XmlBuilderWriter.java jatl-0.2.3/src/main/java/com/googlecode/jatl/XmlBuilderWriter.java --- jatl-0.2.2/src/main/java/com/googlecode/jatl/XmlBuilderWriter.java 1970-01-01 00:00:00.000000000 +0000 +++ jatl-0.2.3/src/main/java/com/googlecode/jatl/XmlBuilderWriter.java 2016-09-27 16:25:58.000000000 +0000 @@ -0,0 +1,68 @@ +/** + * Copyright (C) 2012 the original author or authors. + * + * 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. + */ +package com.googlecode.jatl; + +import java.io.StringWriter; +import java.io.Writer; + +/** + * For generic XML creation subclass this class for a custom XML writer or use {@link XmlWriter}. + * When subclassing make the child class abstract and do not implement {@link #build()}. + * The {@link #toString()} on this class will do what you expect: generate the XML as a string. + * @author agent + * + * @param generally itself for fluent style + */ +public abstract class XmlBuilderWriter extends MarkupBuilder implements MarkupBuilderWriter { + + public XmlBuilderWriter() { + super(); + } + + public W write(W writer) { + setWriter(writer); + build(); + done(); + return writer; + } + + public W write(W writer, int depth) { + setWriter(writer); + setDepth(depth); + build(); + done(); + return writer; + } + + /** + * Uses a StringWriter to write the XML created in {@link #build()}. + * {@inheritDoc} + */ + @Override + public String toString() { + return write(new StringWriter()).getBuffer().toString(); + } + + /** + * Should build the markup and is called by {@link #write(Writer)}. + * This method should describe the markup that should be built and is the entry point to the JATL DSL. + * If you are making your own custom {@link XmlWriter} do not override this method + * as it is the method used by anonymous classes to describe the markup. + * @see MarkupBuilder + */ + protected abstract void build(); + +} diff -Nru jatl-0.2.2/src/main/java/com/googlecode/jatl/XmlWriter.java jatl-0.2.3/src/main/java/com/googlecode/jatl/XmlWriter.java --- jatl-0.2.2/src/main/java/com/googlecode/jatl/XmlWriter.java 1970-01-01 00:00:00.000000000 +0000 +++ jatl-0.2.3/src/main/java/com/googlecode/jatl/XmlWriter.java 2016-09-27 16:25:58.000000000 +0000 @@ -0,0 +1,31 @@ +/** + * Copyright (C) 2012 the original author or authors. + * + * 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. + */ +package com.googlecode.jatl; + +/** + * For generic XML creation. If you want to generate HTML you might prefer {@link HtmlWriter}. + * The {@link #toString()} on this class will do what you expect: generate the XML as a string. + * @author agent + * + */ +public abstract class XmlWriter extends XmlBuilderWriter { + + @Override + protected XmlWriter getSelf() { + return this; + } + +} diff -Nru jatl-0.2.2/src/test/java/com/googlecode/jatl/HtmlBuilderTest.java jatl-0.2.3/src/test/java/com/googlecode/jatl/HtmlBuilderTest.java --- jatl-0.2.2/src/test/java/com/googlecode/jatl/HtmlBuilderTest.java 1979-12-30 23:00:00.000000000 +0000 +++ jatl-0.2.3/src/test/java/com/googlecode/jatl/HtmlBuilderTest.java 2016-09-27 16:25:58.000000000 +0000 @@ -639,4 +639,47 @@ assertEquals(expected, result); } + @Test + public void testIssue18TagClosingPairPolicyFailing() throws Exception { + new Html(sw) { + { + div(); + hr().text(""); + end(); + } + }; + String result = writer.getBuffer().toString(); + String expected = "\n" + + "
\n" + + "
\n" + + "
"; + assertEquals(expected, result); + } + + @Test(expected=IllegalStateException.class) + public void testIllegalAttributeOrder() throws Exception { + new Html(sw) { + { + div(); + hr().text(""); + attr("id", "cat"); + div().end(); + end(); + } + }; + writer.getBuffer().toString(); + } + + @Test(expected=IllegalStateException.class) + public void testIllegalAttributesBeforeStartTag() throws Exception { + new Html(sw) { + { + attr("id", "cat"); + div(); + end(); + } + }; + writer.getBuffer().toString(); + } + } diff -Nru jatl-0.2.2/src/test/java/com/googlecode/jatl/InternalStrSubstitutorTest.java jatl-0.2.3/src/test/java/com/googlecode/jatl/InternalStrSubstitutorTest.java --- jatl-0.2.2/src/test/java/com/googlecode/jatl/InternalStrSubstitutorTest.java 1970-01-01 00:00:00.000000000 +0000 +++ jatl-0.2.3/src/test/java/com/googlecode/jatl/InternalStrSubstitutorTest.java 2016-09-27 16:25:58.000000000 +0000 @@ -0,0 +1,322 @@ +/** + * Copyright (C) 2012 the original author or authors. + * + * 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. + */ +package com.googlecode.jatl; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import java.util.HashMap; +import java.util.Map; + +import org.junit.After; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; + +/** + * Test class for InternalStrSubstitutor. + * Copied from commons lang. + */ +public class InternalStrSubstitutorTest { + + private Map values; + + @Before + public void setUp() throws Exception { + values = new HashMap(); + values.put("animal", "quick brown fox"); + values.put("target", "lazy dog"); + } + + @After + public void tearDown() throws Exception { + values = null; + } + + //----------------------------------------------------------------------- + /** + * Tests simple key replace. + */ + @Test + public void testReplaceSimple() { + doTestReplace("The quick brown fox jumps over the lazy dog.", "The ${animal} jumps over the ${target}.", true); + } + + /** + * Tests simple key replace. + */ + @Test + public void testReplaceSolo() { + doTestReplace("quick brown fox", "${animal}", false); + } + + /** + * Tests replace with no variables. + */ + @Test + public void testReplaceNoVariables() { + doTestNoReplace("The balloon arrived."); + } + + /** + * Tests replace with null. + */ + @Test + public void testReplaceNull() { + doTestNoReplace(null); + } + + /** + * Tests replace with null. + */ + @Test + public void testReplaceEmpty() { + doTestNoReplace(""); + } + + /** + * Tests key replace changing map after initialization (not recommended). + */ + @Test + public void testReplaceChangedMap() { + final InternalStrSubstitutor sub = new InternalStrSubstitutor(values); + values.put("target", "moon"); + assertEquals("The quick brown fox jumps over the moon.", sub.replace("The ${animal} jumps over the ${target}.")); + } + + /** + * Tests unknown key replace. + */ + @Test + public void testReplaceUnknownKey() { + doTestReplace("The ${person} jumps over the lazy dog.", "The ${person} jumps over the ${target}.", true); + //I'm not sure I understand how the falling is supposed to happen + //doTestReplace("The ${person} jumps over the lazy dog. 1234567890.", "The ${person} jumps over the ${target}. ${undefined.number:-1234567890}.", true); + } + + /** + * Tests adjacent keys. + */ + @Test + public void testReplaceAdjacentAtStart() { + values.put("code", "GBP"); + values.put("amount", "12.50"); + final InternalStrSubstitutor sub = new InternalStrSubstitutor(values); + assertEquals("GBP12.50 charged", sub.replace("${code}${amount} charged")); + } + + /** + * Tests adjacent keys. + */ + @Test + public void testReplaceAdjacentAtEnd() { + values.put("code", "GBP"); + values.put("amount", "12.50"); + final InternalStrSubstitutor sub = new InternalStrSubstitutor(values); + assertEquals("Amount is GBP12.50", sub.replace("Amount is ${code}${amount}")); + } + + /** + * Tests simple recursive replace. + */ + @Ignore // recursive is not supported right now. + @Test + public void testReplaceRecursive() { + values.put("animal", "${critter}"); + values.put("target", "${pet}"); + values.put("pet", "${petCharacteristic} dog"); + values.put("petCharacteristic", "lazy"); + values.put("critter", "${critterSpeed} ${critterColor} ${critterType}"); + values.put("critterSpeed", "quick"); + values.put("critterColor", "brown"); + values.put("critterType", "fox"); + doTestReplace("The quick brown fox jumps over the lazy dog.", "The ${animal} jumps over the ${target}.", true); + + values.put("pet", "${petCharacteristicUnknown:-lazy} dog"); + doTestReplace("The quick brown fox jumps over the lazy dog.", "The ${animal} jumps over the ${target}.", true); + } + + /** + * Tests escaping. + */ + @Test + public void testReplaceEscaping() { + doTestReplace("The ${animal} jumps over the lazy dog.", "The $${animal} jumps over the ${target}.", true); + } + + /** + * Tests escaping. + */ + @Test + public void testReplaceSoloEscaping() { + doTestReplace("${animal}", "$${animal}", false); + } + + /** + * Tests complex escaping. + */ + @Test + public void testReplaceComplexEscaping() { + doTestReplace("The ${quick brown fox} jumps over the lazy dog.", "The $${${animal}} jumps over the ${target}.", true); + //Still not sure what the weird number stuff is + //doTestReplace("The ${quick brown fox} jumps over the lazy dog. ${1234567890}.", "The $${${animal}} jumps over the ${target}. $${${undefined.number:-1234567890}}.", true); + } + + /** + * Tests when no prefix or suffix. + */ + @Test + public void testReplaceNoPrefixNoSuffix() { + doTestReplace("The animal jumps over the lazy dog.", "The animal jumps over the ${target}.", true); + } + + /** + * Tests when no incomplete prefix. + */ + @Test + public void testReplaceIncompletePrefix() { + doTestReplace("The {animal} jumps over the lazy dog.", "The {animal} jumps over the ${target}.", true); + } + + /** + * Tests when prefix but no suffix. + */ + @Test + public void testReplacePrefixNoSuffix() { + doTestReplace("The ${animal jumps over the ${target} lazy dog.", "The ${animal jumps over the ${target} ${target}.", true); + } + + /** + * Tests when suffix but no prefix. + */ + @Test + public void testReplaceNoPrefixSuffix() { + doTestReplace("The animal} jumps over the lazy dog.", "The animal} jumps over the ${target}.", true); + } + + /** + * Tests when no variable name. + */ + @Test + public void testReplaceEmptyKeys() { + doTestReplace("The ${} jumps over the lazy dog.", "The ${} jumps over the ${target}.", true); + doTestReplace("The animal jumps over the lazy dog.", "The ${:-animal} jumps over the ${target}.", true); + } + + /** + * Tests replace creates output same as input. + */ + @Ignore("recursion not supported") + @Test + public void testReplaceToIdentical() { + values.put("animal", "$${${thing}}"); + values.put("thing", "animal"); + doTestReplace("The ${animal} jumps.", "The ${animal} jumps.", true); + } + + /** + * Tests a cyclic replace operation. + * The cycle should be detected and cause an exception to be thrown. + */ + @Ignore("recursion not supported") + @Test + public void testCyclicReplacement() { + final Map map = new HashMap(); + map.put("animal", "${critter}"); + map.put("target", "${pet}"); + map.put("pet", "${petCharacteristic} dog"); + map.put("petCharacteristic", "lazy"); + map.put("critter", "${critterSpeed} ${critterColor} ${critterType}"); + map.put("critterSpeed", "quick"); + map.put("critterColor", "brown"); + map.put("critterType", "${animal}"); + InternalStrSubstitutor sub = new InternalStrSubstitutor(map); + try { + sub.replace("The ${animal} jumps over the ${target}."); + fail("Cyclic replacement was not detected!"); + } catch (final IllegalStateException ex) { + // expected + } + + // also check even when default value is set. + map.put("critterType", "${animal:-fox}"); + sub = new InternalStrSubstitutor(map); + try { + sub.replace("The ${animal} jumps over the ${target}."); + fail("Cyclic replacement was not detected!"); + } catch (final IllegalStateException ex) { + // expected + } + } + + /** + * Tests interpolation with weird boundary patterns. + */ + @Test + public void testReplaceWeirdPattens() { + doTestNoReplace(""); + doTestNoReplace("${}"); + doTestNoReplace("${ }"); + doTestNoReplace("${\t}"); + doTestNoReplace("${\n}"); + doTestNoReplace("${\b}"); + doTestNoReplace("${"); + doTestNoReplace("$}"); + doTestNoReplace("}"); + doTestNoReplace("${}$"); + doTestNoReplace("${${"); + doTestNoReplace("${${}}"); + doTestNoReplace("${$${}}"); + doTestNoReplace("${$$${}}"); + doTestNoReplace("${$$${$}}"); + doTestNoReplace("${${}}"); + doTestNoReplace("${${ }}"); + } + + + private void doTestNoReplace(final String replaceTemplate) { + final InternalStrSubstitutor sub = new InternalStrSubstitutor(values); + + if (replaceTemplate == null) { + assertEquals(null, sub.replace((String) null)); + + } else { + assertEquals(replaceTemplate, sub.replace(replaceTemplate)); + final StringBuilder bld = new StringBuilder(replaceTemplate); + //assertFalse(sub.replaceIn(bld)); + assertEquals(replaceTemplate, bld.toString()); + } + } + + + @Test + public void testSubstituteNoPreserveEscape() { + final String org = "${not-escaped} $${escaped}"; + final Map map = new HashMap(); + map.put("not-escaped", "value"); + + InternalStrSubstitutor sub = new InternalStrSubstitutor(map); + assertEquals("value ${escaped}", sub.replace(org)); + + } + + //----------------------------------------------------------------------- + private void doTestReplace(final String expectedResult, final String replaceTemplate, final boolean substring) { + final InternalStrSubstitutor sub = new InternalStrSubstitutor(values); + assertEquals(expectedResult, sub.replace(replaceTemplate)); + + } +} \ No newline at end of file diff -Nru jatl-0.2.2/src/test/java/com/googlecode/jatl/XmlWriterTest.java jatl-0.2.3/src/test/java/com/googlecode/jatl/XmlWriterTest.java --- jatl-0.2.2/src/test/java/com/googlecode/jatl/XmlWriterTest.java 1970-01-01 00:00:00.000000000 +0000 +++ jatl-0.2.3/src/test/java/com/googlecode/jatl/XmlWriterTest.java 2016-09-27 16:25:58.000000000 +0000 @@ -0,0 +1,46 @@ +/** + * Copyright (C) 2012 the original author or authors. + * + * 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. + */ + +package com.googlecode.jatl; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +public class XmlWriterTest { + + XmlWriter xml; + + @Test + public void testWriter() throws Exception { + //Do not write yet. + xml = new XmlWriter() { + @Override + protected void build() { + start("element").attr("attribute", "first").indent(indentOff).end(); + indent(indentOn); + start("element").attr("attribute", "second").end(); + done(); + } + }; + //Now write. + String actual = xml.toString(); + String expected = "\n" + + ""; + assertEquals(expected, actual); + + } +}