/** * Copyright (C) 2010 Orbeon, Inc. * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU Lesser General Public License as published by the Free Software Foundation; either version * 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. * * The full text of the license is available at http://www.gnu.org/copyleft/lesser.html */ package org.orbeon.oxf.transformer.xupdate; import org.orbeon.dom4j.Document; import org.orbeon.dom4j.Element; import org.orbeon.dom4j.Node; import org.orbeon.dom4j.QName; import org.orbeon.jaxen.NamespaceContext; import org.orbeon.jaxen.SimpleNamespaceContext; import org.orbeon.oxf.common.OXFException; import org.orbeon.oxf.common.ValidationException; import org.orbeon.oxf.transformer.xupdate.dom4j.Dom4jUtils; import org.orbeon.oxf.transformer.xupdate.dom4j.LocationSAXContentHandler; import org.orbeon.oxf.transformer.xupdate.dom4j.NonLazyUserDataElement; import org.orbeon.oxf.transformer.xupdate.statement.*; import org.orbeon.oxf.transformer.xupdate.statement.Error; import org.orbeon.oxf.util.StringUtils; import org.orbeon.oxf.xml.dom4j.LocationData; import javax.xml.transform.sax.TemplatesHandler; import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class TemplatesHandlerImpl extends LocationSAXContentHandler implements TemplatesHandler { public javax.xml.transform.Templates getTemplates() { Document xupdateDocument = getDocument(); return new TemplatesImpl(parseStatements(xupdateDocument.getRootElement().elements())); } public void setSystemId(String systemID) { } public String getSystemId() { return null; } private Statement[] parseStatements(List nodes) { List statements = new ArrayList(); for (Iterator i = nodes.iterator(); i.hasNext();) { Node node = (Node) i.next(); if (node.getNodeType() == Node.TEXT_NODE) { if (! "".equals(StringUtils.trimAllToEmpty(node.getText()))) statements.add(new Text(StringUtils.trimAllToEmpty(node.getText()))); } else if (node.getNodeType() == Node.ELEMENT_NODE) { Element element = (Element) node; NamespaceContext namespaceContext = new SimpleNamespaceContext(Dom4jUtils.getNamespaceContext(element)); if (XUpdateConstants.XUPDATE_NAMESPACE_URI.equals(element.getNamespaceURI())) { if (element.getName().equals("remove")) { statements.add(new Remove((LocationData) element.getData(), element.attributeValue("select"), namespaceContext)); } else if (element.getName().equals("update")) { statements.add(new Update((LocationData) element.getData(), element.attributeValue("select"), namespaceContext, parseStatements(element.content()))); } else if (element.getName().equals("append")) { statements.add(new Append((LocationData) element.getData(), element.attributeValue("select"), namespaceContext, element.attributeValue("child"), parseStatements(element.content()))); } else if (element.getName().equals("insert-before")) { statements.add(new InsertBefore((LocationData) element.getData(), element.attributeValue("select"), namespaceContext, parseStatements(element.content()))); } else if (element.getName().equals("insert-after")) { statements.add(new InsertAfter((LocationData) element.getData(), element.attributeValue("select"), namespaceContext, parseStatements(element.content()))); } else if (element.getName().equals("for-each")) { statements.add(new ForEach((LocationData) element.getData(), element.attributeValue("select"), namespaceContext, parseStatements(element.content()))); } else if (element.getName().equals("while")) { statements.add(new While((LocationData) element.getData(), element.attributeValue("select"), namespaceContext, parseStatements(element.content()))); } else if (element.getName().equals("value-of")) { statements.add(new ValueOf((LocationData) element.getData(), element.attributeValue("select"), namespaceContext)); } else if (element.getName().equals("copy-of")) { statements.add(new CopyOf((LocationData) element.getData(), element.attributeValue("select"), namespaceContext)); } else if (element.getName().equals("node-set")) { statements.add(new NodeSet((LocationData) element.getData(), element.attributeValue("select"), namespaceContext)); } else if (element.getName().equals("attribute")) { statements.add(new Attribute((LocationData) element.getData(), parseQName(element), parseStatements(element.content()))); } else if (element.getName().equals("namespace")) { statements.add(new Namespace((LocationData) element.getData(), element.attributeValue("name"), element.attributeValue("select"), namespaceContext, parseStatements(element.content()))); } else if (element.getName().equals("element")) { statements.add(new DynamicElement((LocationData) element.getData(), parseQName(element), parseStatements(element.content()))); } else if (element.getName().equals("if")) { statements.add(new If((LocationData) element.getData(), element.attributeValue("test"), namespaceContext, parseStatements(element.content()))); } else if (element.getName().equals("choose")) { List whenTests = new ArrayList(); List whenNamespaceContext = new ArrayList(); List whenStatements = new ArrayList(); for (Iterator j = element.elements("when").iterator(); j.hasNext();) { Element whenElement = (Element) j.next(); whenTests.add(whenElement.attributeValue("test")); whenNamespaceContext.add(new SimpleNamespaceContext(Dom4jUtils.getNamespaceContext(whenElement))); whenStatements.add(parseStatements(whenElement.content())); } Element otherwiseElement = element.element("otherwise"); statements.add(new Choose((LocationData) element.getData(), (String[]) whenTests.toArray(new String[whenTests.size()]), (NamespaceContext[]) whenNamespaceContext.toArray(new NamespaceContext[whenNamespaceContext.size()]), (Statement[][]) whenStatements.toArray(new Statement[whenStatements.size()][]), otherwiseElement == null ? null : parseStatements(otherwiseElement.content()))); } else if (element.getName().equals("variable")) { statements.add(new Variable((LocationData) element.getData(), parseQName(element), element.attributeValue("select"), namespaceContext, parseStatements(element.content()))); } else if (element.getName().equals("assign")) { statements.add(new Assign((LocationData) element.getData(), parseQName(element), element.attributeValue("select"), namespaceContext, parseStatements(element.content()))); } else if (element.getName().equals("function")) { statements.add(new Function((LocationData) element.getData(), parseQName(element), parseStatements(element.content()))); } else if (element.getName().equals("param")) { statements.add(new Param((LocationData) element.getData(), parseQName(element), element.attributeValue("select"), namespaceContext, parseStatements(element.content()))); } else if (element.getName().equals("message")) { statements.add(new Message((LocationData) element.getData(), parseStatements(element.content()))); } else if (element.getName().equals("error")) { statements.add(new Error((LocationData) element.getData(), parseStatements(element.content()))); } else { throw new ValidationException("Unsupported XUpdate element '" + element.getQualifiedName() + "'", (LocationData) element.getData()); } } else { Element staticElement = new NonLazyUserDataElement(element.getQName()); List childNodes = new ArrayList(); for (Iterator j = element.attributes().iterator(); j.hasNext();) staticElement.add((org.orbeon.dom4j.Attribute) ((org.orbeon.dom4j.Attribute) j.next()).clone()); for (Iterator j = element.content().iterator(); j.hasNext();) { Node child = (Node) j.next(); if (child instanceof org.orbeon.dom4j.Namespace) { staticElement.add((Node) child.clone()); } else { childNodes.add(child); } } statements.add(new StaticElement((LocationData) element.getData(), staticElement, parseStatements(childNodes))); } } else if (node.getNodeType() == Node.NAMESPACE_NODE) { // Ignore namespace declarations } else { throw new OXFException("Unsupported node: " + node.getNodeTypeName()); } } return (Statement[]) statements.toArray(new Statement[statements.size()]); } /** * Parse a name / namespace attributes of <xu:element> and * <xu:attribute> */ private QName parseQName(Element element) { String name = element.attributeValue("name"); String namespace = element.attributeValue("namespace"); int columnPosition = name.indexOf(':'); // Check syntax of qname if (columnPosition == 0 || columnPosition == name.length() - 1) throw new ValidationException("Invalid qname '" + name + "'", (LocationData) element.getData()); if (columnPosition == -1 && namespace == null) { // Simple name return new QName(name); } else if (columnPosition != -1 && namespace == null) { // Qualified name using namespace declaration in context String prefix = name.substring(0, columnPosition); String namespaceFromContext = (String) Dom4jUtils.getNamespaceContext(element).get(prefix); if (namespaceFromContext == null) throw new ValidationException("No namespace declared for prefix '" + prefix + "'", (LocationData) element.getData()); return new QName(name.substring(columnPosition + 1), new org.orbeon.dom4j.Namespace(prefix, namespaceFromContext)); } else if (columnPosition == -1 && namespace != null) { // Non-qualified name with namespace declaration return new QName(name, new org.orbeon.dom4j.Namespace("", namespace)); } else if (columnPosition != -1 && namespace != null) { // Qualified name using namespace specified return new QName(name.substring(columnPosition + 1), new org.orbeon.dom4j.Namespace(name.substring(0, columnPosition), namespace)); } else { // This can't happen throw new OXFException("Invalid state"); } } }