/* * * Copyright 2005 Eric van der Vlist, Dyomedea This file is part of XMLfr. TreeBind is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. TreeBind 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 General Public License for more details. You should have received a copy of the GNU General Public License along with XMLfr; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Contributors: * Eric van der Vlist (vdv@dyomedea.com) */ package com.dyomedea.ops.processors; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.dom4j.Node; import org.orbeon.oxf.pipeline.api.PipelineContext; import org.orbeon.oxf.processor.SimpleProcessor; import org.orbeon.oxf.util.PooledXPathExpression; import org.orbeon.oxf.util.XPathCache; import org.orbeon.oxf.xml.XMLUtils; import org.orbeon.saxon.dom4j.DocumentWrapper; import org.orbeon.saxon.xpath.XPathException; import org.xml.sax.Attributes; import org.xml.sax.ContentHandler; import org.xml.sax.Locator; import org.xml.sax.SAXException; public class XpathTransformer extends SimpleProcessor { static String INPUT_DATA = "data"; static String INPUT_CONFIG = "config"; static Pattern xpathPattern = Pattern.compile("\\{[^{}]*\\}"); public void generateData(PipelineContext context, ContentHandler contentHandler) { DocumentWrapper wrapper = new DocumentWrapper(readCacheInputAsDOM4J( context, INPUT_DATA), null); XpathFilter xpf = new XpathFilter(context, contentHandler, wrapper); readInputAsSAX(context, INPUT_CONFIG, xpf); } class XpathFilter implements ContentHandler { private ContentHandler out; private PipelineContext context; private DocumentWrapper wrapper; private Map namespaces = new HashMap(); private StringBuffer text = new StringBuffer(); public XpathFilter(PipelineContext context, ContentHandler out, DocumentWrapper wrapper) { this.out = out; this.context = context; this.wrapper = wrapper; } private void flushText() throws SAXException { Matcher m = xpathPattern.matcher(text.toString()); StringBuffer sb = new StringBuffer(); while (m.find()) { String toReplace = m.group(); String replacement; String striped = toReplace.substring(1, toReplace.length() -1); if (m.start() > 0 && text.charAt(m.start()-1) == '{' && m.end() < text.length() && text.charAt(m.end()) == '}' ) { replacement = striped; } else { try { replacement = xpathEvaluate (striped); } catch (XPathException e) { throw new SAXException(e); } } m.appendReplacement(sb, replacement); } m.appendTail(sb); out.characters(sb.toString().toCharArray(), 0, sb.length()); text.setLength(0); } private String xpathEvaluate(String xpathExp) throws XPathException, SAXException { PooledXPathExpression xpath = null; xpath = XPathCache.getXPathExpression(context, wrapper, xpathExp, namespaces); List results = xpath.evaluate(); for (Iterator i = results.iterator(); i.hasNext();) { Object result = i.next(); if ( result == null ) continue; if (result instanceof Node) { return ((Node) result).getStringValue(); } if (result instanceof String) { return (String) result; } if ( result instanceof Double ) { final double d = ( ( Double )result ).doubleValue(); return XMLUtils.removeScientificNotation( d ); } if ( result instanceof Boolean ) { return ((Boolean) result).toString(); } String message = "Unsupported type returned by XPath expression: " + (result == null ? "null" : result.getClass().getName()); throw new SAXException(message); /*TODO: use throw new ValidationException(message, locationData); */ } xpath.returnToPool(); return null; } public void setDocumentLocator(Locator locator) { out.setDocumentLocator(locator); } public void startDocument() throws SAXException { out.startDocument(); } public void endDocument() throws SAXException { out.endDocument(); } public void startPrefixMapping(String prefix, String uri) throws SAXException { out.startPrefixMapping(prefix, uri); namespaces.put(prefix, uri); } public void endPrefixMapping(String prefix) throws SAXException { out.endPrefixMapping(prefix); namespaces.remove(prefix); } public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException { flushText(); out.startElement(uri, localName, qName, atts); } public void endElement(String uri, String localName, String qName) throws SAXException { flushText(); out.endElement(uri, localName, qName); } public void characters(char[] ch, int start, int length) throws SAXException { text.append(ch, start, length); } public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException { out.ignorableWhitespace(ch, start, length); } public void processingInstruction(String target, String data) throws SAXException { flushText(); out.processingInstruction(target, data); } public void skippedEntity(String name) throws SAXException { out.skippedEntity(name); } } }