From 6ded834d77ef1820c989dc4b7fb372192de8d679 Mon Sep 17 00:00:00 2001
From: Ralf Jung <ralfjung-e@gmx.de>
Date: Sun, 21 Aug 2011 14:12:42 +0200
Subject: [PATCH] fix closing tags in xforms:output (<a/> has to be printed as
 <a></a>)

---
 .../orbeon/oxf/xforms/control/XFormsControl.java   |  101 ++++++++++----------
 1 files changed, 49 insertions(+), 52 deletions(-)

diff --git a/src/java/org/orbeon/oxf/xforms/control/XFormsControl.java b/src/java/org/orbeon/oxf/xforms/control/XFormsControl.java
index 821d11b..bac1384 100644
--- a/src/java/org/orbeon/oxf/xforms/control/XFormsControl.java
+++ b/src/java/org/orbeon/oxf/xforms/control/XFormsControl.java
@@ -663,7 +663,7 @@ public abstract class XFormsControl implements XFormsEventTarget, XFormsEventObs
     }
 
     /**
-     * Rewrite an HTML value which may contain URLs, for example in @src or @href attributes.
+     * Rewrite an HTML value which may contain URLs, for example in @src or @href attributes. Also deals with closing element tags.
      *
      * @param propertyContext   current context
      * @param rawValue          value to rewrite
@@ -674,57 +674,54 @@ public abstract class XFormsControl implements XFormsEventTarget, XFormsEventObs
         if (rawValue == null)
             return null;
 
-        // Quick check for the most common attributes, src and href. Ideally we should check more.
-        final boolean needsRewrite = rawValue.indexOf("src=") != -1 || rawValue.indexOf("href=") != -1;
-        final String result;
-        if (needsRewrite) {
-            // Rewrite URLs
-            final StringBuilder sb = new StringBuilder(rawValue.length() * 2);// just an approx of the size it may take
-            // NOTE: we do our own serialization here, but it's really simple (no namespaces) and probably reasonably efficient
-            XFormsUtils.streamHTMLFragment(new XHTMLRewrite().getRewriteXMLReceiver(propertyContext, new ForwardingXMLReceiver() {
-
-                private boolean isStartElement;
-
-                public void characters(char[] chars, int start, int length) throws SAXException {
-                    sb.append(XMLUtils.escapeXMLMinimal(new String(chars, start, length)));// NOTE: not efficient to create a new String here
-                    isStartElement = false;
-                }
-
-                public void startElement(String uri, String localname, String qName, Attributes attributes) throws SAXException {
-                    sb.append('<');
-                    sb.append(localname);
-                    final int attributeCount = attributes.getLength();
-                    for (int i = 0; i < attributeCount; i++) {
-
-                        final String currentName = attributes.getLocalName(i);
-                        final String currentValue = attributes.getValue(i);
-
-                        sb.append(' ');
-                        sb.append(currentName);
-                        sb.append("=\"");
-                        sb.append(currentValue);
-                        sb.append('"');
-                    }
-                    sb.append('>');
-                    isStartElement = true;
-                }
-
-                public void endElement(String uri, String localname, String qName) throws SAXException {
-                    if (!isStartElement) {
-                        // We serialize to HTML: don't close elements that just opened (will cover <br>, <hr>, etc.)
-                        sb.append("</");
-                        sb.append(localname);
-                        sb.append('>');
-                    }
-                    isStartElement = false;
-                }
-            }, true), rawValue, locationData, "xhtml");
-            result = sb.toString();
-        } else {
-            // No rewriting needed
-            result = rawValue;
-        }
-        return result;
+		final StringBuilder sb = new StringBuilder(rawValue.length() * 2);// just an approx of the size it may take
+		// NOTE: we do our own serialization here, but it's really simple (no namespaces) and probably reasonably efficient
+		XFormsUtils.streamHTMLFragment(new XHTMLRewrite().getRewriteXMLReceiver(propertyContext, new ForwardingXMLReceiver() {
+
+			private final String[] voidElementsNames = {
+				// HTML 5: http://www.w3.org/TR/html5/syntax.html#void-elements
+				"area", "base", "br", "col", "command", "embed", "hr", "img", "input", "keygen", "link", "meta", "param", "source", "track", "wbr",
+				// Legacy
+				"basefont", "frame", "isindex"
+			};
+			private final Set<String> voidElements = new HashSet<String>(Arrays.asList(voidElementsNames));
+			private boolean isStartElement;
+
+			public void characters(char[] chars, int start, int length) throws SAXException {
+				sb.append(XMLUtils.escapeXMLMinimal(new String(chars, start, length)));// NOTE: not efficient to create a new String here
+				isStartElement = false;
+			}
+
+			public void startElement(String uri, String localname, String qName, Attributes attributes) throws SAXException {
+				sb.append('<');
+				sb.append(localname);
+				final int attributeCount = attributes.getLength();
+				for (int i = 0; i < attributeCount; i++) {
+
+					final String currentName = attributes.getLocalName(i);
+					final String currentValue = attributes.getValue(i);
+
+					sb.append(' ');
+					sb.append(currentName);
+					sb.append("=\"");
+					sb.append(currentValue);
+					sb.append('"');
+				}
+				sb.append('>');
+				isStartElement = true;
+			}
+
+			public void endElement(String uri, String localname, String qName) throws SAXException {
+				if (!isStartElement || !voidElements.contains(localname)) {
+					// We serialize to HTML: don't close elements that just opened (will cover <br>, <hr>, etc.). Be sure not to drop closing elements of other tags though!
+					sb.append("</");
+					sb.append(localname);
+					sb.append('>');
+				}
+				isStartElement = false;
+			}
+		}, true), rawValue, locationData, "xhtml");
+        return sb.toString();
     }
 
     /**
-- 
1.7.5.4

