rdftemplate

Library for generating XML documents from RDF data using templates
git clone https://code.djc.id.au/git/rdftemplate/
commit ff520d00acc268ad4dad8499de6767a5bee370f7
parent 6330b79c168738eb70602afcd0ddd0d400f4211e
Author: Dan Callaghan <djc@djc.id.au>
Date:   Tue,  6 Oct 2009 22:40:02 +1000

allow selector expressions to evaluate to XML event sequences which can be inserted directly

Diffstat:
Msrc/main/java/au/com/miskinhill/rdftemplate/TemplateInterpolator.java | 76+++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------
Asrc/main/java/au/com/miskinhill/rdftemplate/XMLStream.java | 26++++++++++++++++++++++++++
2 files changed, 77 insertions(+), 25 deletions(-)
diff --git a/src/main/java/au/com/miskinhill/rdftemplate/TemplateInterpolator.java b/src/main/java/au/com/miskinhill/rdftemplate/TemplateInterpolator.java
@@ -129,9 +129,28 @@ public class TemplateInterpolator {
                             throw new TemplateSyntaxException("rdf:for and rdf:content cannot both be present on an element");
                         } else if (contentAttribute != null) {
                             consumeTree(start, reader);
-                            start = interpolateAttributes(start, node);
                             Selector<?> selector = selectorFactory.get(contentAttribute.getValue());
-                            writeTreeForContent(writer, start, selector.singleResult(node));
+                            Object replacement = selector.singleResult(node);
+                            start = interpolateAttributes(start, node);
+                            Set<Attribute> attributes = cloneAttributesWithout(start, CONTENT_ACTION_QNAME);
+                            if (replacement instanceof Literal) {
+                                Literal literal = (Literal) replacement;
+                                if (!StringUtils.isEmpty(literal.getLanguage())) {
+                                    attributes.add(eventFactory.createAttribute(XML_LANG_QNAME, ((Literal) replacement).getLanguage()));
+                                    if (start.getName().getNamespaceURI().equals(XHTML_NS_URI)) {
+                                        String xhtmlPrefixInContext = start.getNamespaceContext().getPrefix(XHTML_NS_URI);
+                                        QName xhtmlLangQNameForContext; // ugh
+                                        if (xhtmlPrefixInContext.isEmpty())
+                                            xhtmlLangQNameForContext = new QName("lang");
+                                        else
+                                            xhtmlLangQNameForContext = new QName(XHTML_NS_URI, "lang", xhtmlPrefixInContext);
+                                        attributes.add(eventFactory.createAttribute(xhtmlLangQNameForContext, literal.getLanguage()));
+                                    }
+                                }
+                            }
+                            writer.add(eventFactory.createStartElement(start.getName(), attributes.iterator(), start.getNamespaces()));
+                            writeTreeForContent(writer, replacement);
+                            writer.add(eventFactory.createEndElement(start.getName(), start.getNamespaces()));
                         } else if (forAttribute != null) {
                             start = cloneStartWithAttributes(start, cloneAttributesWithout(start, FOR_ACTION_QNAME));
                             List<XMLEvent> tree = consumeTree(start, reader);
@@ -148,12 +167,12 @@ public class TemplateInterpolator {
                 }
                 case XMLStreamConstants.CHARACTERS: {
                     Characters characters = (Characters) event;
-                    writer.add(eventFactory.createCharacters(interpolateString(characters.getData(), node)));
+                    interpolateCharacters(writer, characters, node);
                     break;
                 }
                 case XMLStreamConstants.CDATA: {
                     Characters characters = (Characters) event;
-                    writer.add(eventFactory.createCData(interpolateString(characters.getData(), node)));
+                    interpolateCharacters(writer, characters, node);
                     break;
                 }
                 default:
@@ -237,43 +256,50 @@ public class TemplateInterpolator {
                 throw new UnsupportedOperationException("Not an RDFNode: " + replacement);
             }
             
-            matcher.appendReplacement(substituted, replacementValue.replace("$", "\\$"));;
+            matcher.appendReplacement(substituted, replacementValue.replace("$", "\\$"));
         }
         matcher.appendTail(substituted);
         return substituted.toString();
     }
     
-    private void writeTreeForContent(XMLEventWriter writer, StartElement start, Object replacement)
+    private void interpolateCharacters(XMLEventWriter writer, Characters characters, RDFNode node) throws XMLStreamException {
+        String template = characters.getData();
+        if (!SUBSTITUTION_PATTERN.matcher(template).find()) {
+            writer.add(characters); // fast path
+            return;
+        }
+        Matcher matcher = SUBSTITUTION_PATTERN.matcher(template);
+        int lastAppendedPos = 0;
+        while (matcher.find()) {
+            writer.add(eventFactory.createCharacters(template.substring(lastAppendedPos, matcher.start())));
+            lastAppendedPos = matcher.end();
+            String expression = matcher.group(1);
+            Object replacement = selectorFactory.get(expression).singleResult(node);
+            writeTreeForContent(writer, replacement);
+        }
+        writer.add(eventFactory.createCharacters(template.substring(lastAppendedPos)));
+    }
+    
+    private void writeTreeForContent(XMLEventWriter writer, Object replacement)
             throws XMLStreamException {
         if (replacement instanceof RDFNode) {
             RDFNode replacementNode = (RDFNode) replacement;
             if (replacementNode.isLiteral()) {
                 Literal literal = (Literal) replacementNode;
-                Set<Attribute> attributes = cloneAttributesWithout(start, CONTENT_ACTION_QNAME);
-                
-                if (!StringUtils.isEmpty(literal.getLanguage())) {
-                    attributes.add(eventFactory.createAttribute(XML_LANG_QNAME, literal.getLanguage()));
-                    if (start.getName().getNamespaceURI().equals(XHTML_NS_URI)) {
-                        String xhtmlPrefixInContext = start.getNamespaceContext().getPrefix(XHTML_NS_URI);
-                        QName xhtmlLangQNameForContext; // ugh
-                        if (xhtmlPrefixInContext.isEmpty())
-                            xhtmlLangQNameForContext = new QName("lang");
-                        else
-                            xhtmlLangQNameForContext = new QName(XHTML_NS_URI, "lang", xhtmlPrefixInContext);
-                        attributes.add(eventFactory.createAttribute(xhtmlLangQNameForContext, literal.getLanguage()));
-                    }
-                }
-                
-                writer.add(eventFactory.createStartElement(start.getName(), attributes.iterator(), start.getNamespaces()));
                 if (literal.isWellFormedXML()) {
-                    writeXMLLiteral(start.getNamespaceContext(), literal.getLexicalForm(), writer);
+                    writeXMLLiteral(literal.getLexicalForm(), writer);
                 } else {
                     writer.add(eventFactory.createCharacters(literal.getValue().toString()));
                 }
-                writer.add(eventFactory.createEndElement(start.getName(), start.getNamespaces()));
             } else {
                 throw new UnsupportedOperationException("Not a literal: " + replacementNode);
             }
+        } else if (replacement instanceof String) {
+            writer.add(eventFactory.createCharacters((String) replacement));
+        } else if (replacement instanceof XMLStream) {
+            for (XMLEvent event: (XMLStream) replacement) {
+                writer.add(event);
+            }
         } else {
             throw new UnsupportedOperationException("Not an RDFNode: " + replacement);
         }
@@ -291,7 +317,7 @@ public class TemplateInterpolator {
         return attributes;
     }
     
-    private void writeXMLLiteral(NamespaceContext nsContext, String literal, XMLEventWriter writer)
+    private void writeXMLLiteral(String literal, XMLEventWriter writer)
             throws XMLStreamException {
         XMLEventReader reader = inputFactory.createXMLEventReader(new StringReader(literal));
         while (reader.hasNext()) {
diff --git a/src/main/java/au/com/miskinhill/rdftemplate/XMLStream.java b/src/main/java/au/com/miskinhill/rdftemplate/XMLStream.java
@@ -0,0 +1,26 @@
+package au.com.miskinhill.rdftemplate;
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.xml.stream.events.XMLEvent;
+
+public class XMLStream implements Iterable<XMLEvent> {
+    
+    private final List<XMLEvent> events;
+    
+    public XMLStream(XMLEvent... events) {
+        this.events = Arrays.asList(events);
+    }
+    
+    public XMLStream(List<XMLEvent> events) {
+        this.events = events;
+    }
+    
+    @Override
+    public Iterator<XMLEvent> iterator() {
+        return events.iterator();
+    }
+
+}