/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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 org.apache.cocoon.components.xpointer; import org.xml.sax.SAXException; import org.xml.sax.Attributes; import org.xml.sax.Locator; import org.apache.cocoon.xml.AbstractXMLPipe; import org.apache.cocoon.components.source.SourceUtil; import org.apache.cocoon.ProcessingException; import java.util.StringTokenizer; import java.util.ArrayList; import java.io.IOException; /** * A custom XPointer scheme that allows to include the content of a specific element without * building a DOM. The element must be specified using an absolute path reference such as * <tt>/html/body</tt>. Namespace prefixes within these element names are supported. * * <p>This xpointer scheme will always be succesful (thus any further xpointer parts will * never be executed). * * <p>The scheme name for this XPointer scheme is 'elementpath' and its namespace is * http://apache.org/cocoon/xpointer. * * <p>See the samples for a usage example. */ public class ElementPathPart implements PointerPart { private String expression; public ElementPathPart(String expression) { this.expression = expression; } public boolean process(XPointerContext xpointerContext) throws SAXException { PathInclusionPipe pipe = new PathInclusionPipe(expression, xpointerContext); pipe.setConsumer(xpointerContext.getXmlConsumer()); try { SourceUtil.toSAX(xpointerContext.getSource(), pipe); } catch (IOException e) { throw new SAXException("Exception while trying to XInclude data: " + e.getMessage(), e); } catch (ProcessingException e) { throw new SAXException("Exception while trying to XInclude data: " + e.getMessage(), e); } return true; } public static class PathInclusionPipe extends AbstractXMLPipe { /** The QNames that must be matched before inclusion can start. */ private QName[] elementPath; /** The current element nesting level. */ private int level; /** Should we currently be including? */ private boolean include; /** The element nesting level since we started inclusion, used to know when to stop inclusion. */ private int includeLevel; /** The element nesting level that should currently be matched. */ private int levelToMatch; private boolean done; public PathInclusionPipe(String expression, XPointerContext xpointerContext) throws SAXException { // parse the expression to an array of QName objects ArrayList path = new ArrayList(); StringTokenizer tokenizer = new StringTokenizer(expression, "/"); while (tokenizer.hasMoreTokens()) { String token = tokenizer.nextToken(); try { path.add(QName.parse(token, xpointerContext)); } catch (SAXException e) { throw new SAXException("Error in element path xpointer expression \"" + expression + "\": " + e.getMessage()); } } if (path.size() < 1) throw new SAXException("Invalid element path xpointer expression \"" + expression + "\"."); this.elementPath = (QName[])path.toArray(new QName[path.size()]); this.level = -1; this.include = false; this.levelToMatch = 0; this.done = false; } public void startElement(String namespaceURI, String localName, String raw, Attributes a) throws SAXException { level++; if (include) { super.startElement(namespaceURI, localName, raw, a); return; } if (!done && level == levelToMatch && elementPath[level].matches(namespaceURI, localName)) { levelToMatch++; if (levelToMatch == elementPath.length) { include = true; done = true; includeLevel = level; } } } public void endElement(String uri, String loc, String raw) throws SAXException { if (include && level == includeLevel) include = false; if (include) super.endElement(uri, loc, raw); level--; } public void setDocumentLocator(Locator locator) { if (include) super.setDocumentLocator(locator); } public void startDocument() throws SAXException { if (include) super.startDocument(); } public void endDocument() throws SAXException { if (include) super.endDocument(); } public void characters(char c[], int start, int len) throws SAXException { if (include) super.characters(c, start, len); } public void ignorableWhitespace(char c[], int start, int len) throws SAXException { if (include) super.ignorableWhitespace(c, start, len); } public void processingInstruction(String target, String data) throws SAXException { if (include) super.processingInstruction(target, data); } public void skippedEntity(String name) throws SAXException { if (include) super.skippedEntity(name); } public void startDTD(String name, String publicId, String systemId) throws SAXException { if (include) super.startDTD(name, publicId, systemId); } public void endDTD() throws SAXException { if (include) super.endDTD(); } public void startEntity(String name) throws SAXException { if (include) super.startEntity(name); } public void endEntity(String name) throws SAXException { if (include) super.endEntity(name); } public void startCDATA() throws SAXException { if (include) super.startCDATA(); } public void endCDATA() throws SAXException { if (include) super.endCDATA(); } public void comment(char ch[], int start, int len) throws SAXException { if (include) super.comment(ch, start, len); } public static class QName { private String namespaceURI; private String localName; public QName(String namespaceURI, String localName) { this.namespaceURI = namespaceURI; this.localName = localName; } public static QName parse(String qName, XPointerContext xpointerContext) throws SAXException { int pos = qName.indexOf(':'); if (pos > 0) { String prefix = qName.substring(0, pos); String localName = qName.substring(pos + 1); String namespaceURI = xpointerContext.prefixToNamespace(prefix); if (namespaceURI == null) throw new SAXException("Namespace prefix \"" + prefix + "\" not declared."); return new QName(prefix, localName); } return new QName("", qName); } public String getNamespaceURI() { return namespaceURI; } public String getLocalName() { return localName; } public boolean matches(String namespaceURI, String localName) { return this.localName.equals(localName) && this.namespaceURI.equals(namespaceURI); } } } }