You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@cocoon.apache.org by re...@apache.org on 2009/12/21 14:14:59 UTC

svn commit: r892809 [1/2] - in /cocoon/cocoon3/trunk/cocoon-sax: ./ src/main/java/org/apache/cocoon/sax/component/ src/main/java/org/apache/cocoon/sax/xpointer/ src/main/javacc/ src/test/java/org/apache/cocoon/sax/component/ src/test/resources/

Author: reinhard
Date: Mon Dec 21 13:14:59 2009
New Revision: 892809

URL: http://svn.apache.org/viewvc?rev=892809&view=rev
Log:
COCOON3-3 add XInclude transformer by Simone Tripodi

Added:
    cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/component/XIncludeTransformer.java   (with props)
    cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/
    cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/AbstractPointerPart.java   (with props)
    cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/PointerPart.java   (with props)
    cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/ShorthandPart.java   (with props)
    cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/TransformerHandlerFactory.java   (with props)
    cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/UnsupportedPart.java   (with props)
    cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/XPointer.java   (with props)
    cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/XPointerContext.java   (with props)
    cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/XPointerPart.java   (with props)
    cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/XmlnsPart.java   (with props)
    cocoon/cocoon3/trunk/cocoon-sax/src/main/javacc/
    cocoon/cocoon3/trunk/cocoon-sax/src/main/javacc/xpointer-fw.jj
    cocoon/cocoon3/trunk/cocoon-sax/src/test/java/org/apache/cocoon/sax/component/XIncludeTransformerTest.java   (with props)
    cocoon/cocoon3/trunk/cocoon-sax/src/test/resources/just-text.txt   (with props)
    cocoon/cocoon3/trunk/cocoon-sax/src/test/resources/licenses.xml   (with props)
    cocoon/cocoon3/trunk/cocoon-sax/src/test/resources/xinclude-deprecated_xpointer.xml   (with props)
    cocoon/cocoon3/trunk/cocoon-sax/src/test/resources/xinclude-fallback.xml   (with props)
    cocoon/cocoon3/trunk/cocoon-sax/src/test/resources/xinclude-shorthand.xml   (with props)
    cocoon/cocoon3/trunk/cocoon-sax/src/test/resources/xinclude-text-only.xml   (with props)
    cocoon/cocoon3/trunk/cocoon-sax/src/test/resources/xinclude-xml.xml   (with props)
    cocoon/cocoon3/trunk/cocoon-sax/src/test/resources/xinclude-xpointer.xml   (with props)
Modified:
    cocoon/cocoon3/trunk/cocoon-sax/pom.xml

Modified: cocoon/cocoon3/trunk/cocoon-sax/pom.xml
URL: http://svn.apache.org/viewvc/cocoon/cocoon3/trunk/cocoon-sax/pom.xml?rev=892809&r1=892808&r2=892809&view=diff
==============================================================================
--- cocoon/cocoon3/trunk/cocoon-sax/pom.xml (original)
+++ cocoon/cocoon3/trunk/cocoon-sax/pom.xml Mon Dec 21 13:14:59 2009
@@ -29,7 +29,7 @@
     <version>3.0.0-alpha-2-SNAPSHOT</version>
     <relativePath>../parent</relativePath>
   </parent>
-  
+
   <groupId>org.apache.cocoon.sax</groupId>
   <artifactId>cocoon-sax</artifactId>
   <version>3.0.0-alpha-2-SNAPSHOT</version>
@@ -89,6 +89,41 @@
     <plugins>
       <plugin>
         <groupId>org.codehaus.mojo</groupId>
+        <artifactId>javacc-maven-plugin</artifactId>
+        <version>2.6</version>
+        <executions>
+          <execution>
+            <id>javacc</id>
+            <phase>generate-sources</phase>
+            <goals>
+              <goal>javacc</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>build-helper-maven-plugin</artifactId>
+        <version>1.3</version>
+        <executions>
+          <execution>
+            <id>add-source</id>
+            <phase>generate-sources</phase>
+            <goals>
+              <goal>add-source</goal>
+            </goals>
+            <configuration>
+              <sources>
+                <source>target/generated-sources/javacc</source>
+              </sources>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
         <artifactId>exec-maven-plugin</artifactId>
         <executions>
           <execution>
@@ -117,7 +152,17 @@
       </plugin>
     </plugins>
   </build>
-  
+
+  <reporting>
+    <plugins>
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>javacc-maven-plugin</artifactId>
+        <version>2.6</version>
+      </plugin>
+    </plugins>
+  </reporting>
+
   <profiles>
     <profile>
       <id>it</id>
@@ -129,8 +174,9 @@
             <configuration>
               <excludes>
                 <exclude>src/test/resources/META-INF/services/javax.xml.transform.TransformerFactory</exclude>
+                <exclude>src/test/resources/just-text.txt</exclude>
               </excludes>
-            </configuration>            
+            </configuration>
             <executions>
               <execution>
                 <phase>verify</phase>
@@ -139,9 +185,9 @@
                 </goals>
               </execution>
             </executions>
-          </plugin>          
+          </plugin>
         </plugins>
-      </build>        
-    </profile>  
-  </profiles>   
+      </build>
+    </profile>
+  </profiles>
 </project>

Added: cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/component/XIncludeTransformer.java
URL: http://svn.apache.org/viewvc/cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/component/XIncludeTransformer.java?rev=892809&view=auto
==============================================================================
--- cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/component/XIncludeTransformer.java (added)
+++ cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/component/XIncludeTransformer.java Mon Dec 21 13:14:59 2009
@@ -0,0 +1,640 @@
+/*
+ * 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.sax.component;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.apache.cocoon.pipeline.ProcessingException;
+import org.apache.cocoon.sax.AbstractSAXTransformer;
+import org.apache.cocoon.sax.SAXConsumer;
+import org.apache.cocoon.sax.util.XMLUtils;
+import org.apache.cocoon.sax.xpointer.ParseException;
+import org.apache.cocoon.sax.xpointer.XPointer;
+import org.apache.cocoon.sax.xpointer.XPointerContext;
+import org.apache.cocoon.sax.xpointer.XPointerFrameworkParser;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.xml.sax.Attributes;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+
+/**
+ * $Id$
+ */
+public final class XIncludeTransformer extends AbstractSAXTransformer implements SAXConsumer {
+
+    private static final String XINCLUDE_NAMESPACE_URI = "http://www.w3.org/2001/XInclude";
+
+    private static final String XINCLUDE_INCLUDE = "include";
+
+    private static final String XINCLUDE_FALLBACK = "fallback";
+
+    private static final String XINCLUDE_HREF = "href";
+
+    private static final String XINCLUDE_XPOINTER = "xpointer";
+
+    private static final String XINCLUDE_PARSE = "parse";
+
+    private static final String XINCLUDE_ENCODING = "encoding";
+
+    private static final String DEFAULT_CHARSET = "UTF-8";
+
+    private static final String XINCLUDE_ACCEPT = "accept";
+
+    private static final String XINCLUDE_ACCEPT_LANGUAGE = "accept-language";
+
+    private static final String XINCLUDE_PARSE_XML = "xml";
+
+    private static final String XINCLUDE_PARSE_TEXT = "text";
+
+    private static final String UNKNOWN_LOCATION = "unknow location";
+
+    private static final String HTTP_ACCEPT = "Accept";
+
+    private static final String HTTP_ACCEPT_LANGUAGE = "Accept-Language";
+
+    private static final String CHARSET = "charset=";
+
+    private static final String BASE_URL = "baseUrl";
+
+    private final Log logger = LogFactory.getLog(this.getClass());
+
+    /**
+     * The nesting level of xi:include elements that have been encountered.
+     */
+    private int xIncludeElementLevel = 0;
+
+    /**
+     * The nesting level of fallback that should be used
+     */
+    private int useFallbackLevel = 0;
+
+    /**
+     * The nesting level of xi:fallback elements that have been encountered.
+     */
+    private int fallbackElementLevel;
+
+    /**
+     * Locator of the current stream, stored here so that it can be restored after
+     * another document send its content to the consumer.
+     */
+    private Locator locator;
+
+    private URL baseUrl;
+
+    /**
+     * Keep a map of namespaces prefix in the source document to pass it
+     * to the XPointerContext for correct namespace identification.
+     */
+    private final Map<String, String> namespaces = new HashMap<String, String>();
+
+    public XIncludeTransformer() {
+        // default empty constructor - used in the sitemap
+    }
+
+    /**
+     *
+     * @param baseUrl
+     */
+    public XIncludeTransformer(URL baseUrl) {
+        this.setBaseUrl(baseUrl);
+    }
+
+    @Override
+    public void setConfiguration(Map<String, ? extends Object> configuration) {
+        this.setBaseUrl((URL) configuration.get(BASE_URL));
+    }
+
+    /**
+     *
+     * @param baseUrl
+     */
+    public void setBaseUrl(URL baseUrl) {
+        this.baseUrl = baseUrl;
+    }
+
+    /**
+     * Eventually previous errors don't reset local variables status, so
+     * every time a new consumer is set, local variables should be re-initialized
+     */
+    @Override
+    protected void setSAXConsumer(final SAXConsumer xmlConsumer) {
+        super.setSAXConsumer(xmlConsumer);
+        this.xIncludeElementLevel = 0;
+        this.fallbackElementLevel = 0;
+        this.useFallbackLevel = 0;
+    }
+
+    /**
+     * Determine whether the pipe is currently in a state where contents
+     * should be evaluated, i.e. xi:include elements should be resolved
+     * and elements in other namespaces should be copied through. Will
+     * return false for fallback contents within a successful xi:include,
+     * and true for contents outside any xi:include or within an xi:fallback
+     * for an unsuccessful xi:include.
+     */
+    private boolean isEvaluatingContent() {
+        return this.xIncludeElementLevel == 0 ||
+            this.fallbackElementLevel > 0
+                    && this.fallbackElementLevel == this.useFallbackLevel;
+    }
+
+    /**
+     *
+     * @return
+     */
+    private String getLocation() {
+        if (this.locator == null) {
+            return UNKNOWN_LOCATION;
+        } else {
+            return this.locator.getSystemId()
+                + ":"
+                + this.locator.getColumnNumber()
+                + ":"
+                + this.locator.getLineNumber();
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void startDocument() throws SAXException {
+        if (this.xIncludeElementLevel == 0) {
+            this.getSAXConsumer().startDocument();
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void endDocument() throws SAXException {
+        if (this.xIncludeElementLevel == 0) {
+            this.getSAXConsumer().endDocument();
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void startElement(final String uri, final String localName, final String name, final Attributes atts)
+            throws SAXException {
+        if (XINCLUDE_NAMESPACE_URI.equals(uri)) {
+            // Handle xi:include:
+            if (XINCLUDE_INCLUDE.equals(localName)) {
+                // Process the include, unless in an ignored fallback:
+                if (this.isEvaluatingContent()) {
+                    String href = atts.getValue("", XINCLUDE_HREF);
+
+                    String parse = atts.getValue("", XINCLUDE_PARSE);
+                    // Default for @parse is "xml"
+                    if (parse == null) {
+                        parse = XINCLUDE_PARSE_XML;
+                    }
+
+                    String xpointer = atts.getValue("", XINCLUDE_XPOINTER);
+                    String encoding = atts.getValue("", XINCLUDE_ENCODING);
+
+                    String accept = atts.getValue("", XINCLUDE_ACCEPT);
+                    String acceptLanguage = atts.getValue("", XINCLUDE_ACCEPT_LANGUAGE);
+
+                    this.processXIncludeElement(href, parse, xpointer, encoding, accept, acceptLanguage);
+                }
+                this.xIncludeElementLevel++;
+            } else if (XINCLUDE_FALLBACK.equals(localName)) {
+                // Handle xi:fallback
+                this.fallbackElementLevel++;
+            } else {
+                // Unknown element:
+                throw new SAXException("Unknown XInclude element " + localName + " at " + this.getLocation());
+            }
+        } else if (this.isEvaluatingContent()) {
+            // Copy other elements through when appropriate:
+            this.getSAXConsumer().startElement(uri, localName, name, atts);
+        }
+    }
+
+    /**
+     *
+     * @param href
+     * @param parse
+     * @param xpointer
+     * @param encoding
+     * @param accept
+     * @param acceptLanguage
+     * @throws SAXException
+     */
+    private void processXIncludeElement(String href, final String parse, String xpointer, String encoding,
+            final String accept, final String acceptLanguage) throws SAXException {
+        if (this.logger.isDebugEnabled()) {
+            this.logger.debug("Processing XInclude element: href="
+                 + href
+                 + ", parse="
+                 + parse
+                 + ", xpointer="
+                 + xpointer
+                 + ", encoding="
+                 + encoding
+                 + ", accept="
+                 + accept
+                 + ", acceptLanguage="
+                 + acceptLanguage);
+        }
+
+        int fragmentIdentifierPos = href.indexOf('#');
+        if (fragmentIdentifierPos != -1) {
+            if (this.logger.isWarnEnabled()) {
+                this.logger.warn("Fragment identifer found in 'href' attribute: "
+                    + href
+                    + "\nFragment identifiers are forbidden by the XInclude specification. "
+                    + "They are still handled by XIncludeTransformer for backward "
+                    + "compatibility, but their use is deprecated and will be prohibited "
+                    + "in a future release. Use the 'xpointer' attribute instead.");
+            }
+            if (xpointer == null) {
+                xpointer = href.substring(fragmentIdentifierPos + 1);
+            }
+            href = href.substring(0, fragmentIdentifierPos);
+        }
+
+        // An empty or absent href is a reference to the current document -- this can be different than the current base
+        if (!isNotEmpty(href)) {
+            throw new SAXException("XIncludeTransformer: encountered empty href (= href pointing to the current document).");
+        }
+        URL source = this.createSource(href);
+        URLConnection urlConnection = null;
+
+        try {
+            urlConnection = source.openConnection();
+        } catch (IOException ioe) {
+            this.useFallbackLevel++;
+            this.logger.error("Error including document: " + source, ioe);
+        }
+
+        if (urlConnection != null) {
+            if (this.logger.isDebugEnabled()) {
+                this.logger.debug("Parse type=" + parse);
+            }
+
+            if (XINCLUDE_PARSE_XML.equals(parse)) {
+                /* sends Accept and Accept-Language */
+                if (urlConnection instanceof HttpURLConnection) {
+                    HttpURLConnection httpURLConnection = (HttpURLConnection) urlConnection;
+
+                    if (isNotEmpty(accept)) {
+                        httpURLConnection.setRequestProperty(HTTP_ACCEPT, accept);
+                    }
+
+                    if (isNotEmpty(acceptLanguage)) {
+                        httpURLConnection.setRequestProperty(HTTP_ACCEPT_LANGUAGE, acceptLanguage);
+                    }
+                }
+
+                if (xpointer != null && xpointer.length() > 0) {
+                    try {
+                        // create the context
+                        XPointerContext xPointerContext = new XPointerContext(xpointer, this);
+                        for (Entry<String, String> namespace : this.namespaces.entrySet()) {
+                            xPointerContext.addPrefix(namespace.getKey(), namespace.getValue());
+                        }
+
+                        // initialize the XPointer handler by parsing the xpointer
+                        XPointer xPointer = XPointerFrameworkParser.parse(xpointer);
+                        xPointer.setLog(this.logger);
+
+                        // setup components
+                        xPointer.setUp(xPointerContext);
+                        xPointer.setDocumentLocator(this.locator);
+
+                        // go!
+                        XMLUtils.toSax(urlConnection, xPointer);
+                    } catch (ParseException e) {
+                        // this exception is thrown in case of an invalid xpointer expression
+                        this.useFallbackLevel++;
+                        if (this.logger.isErrorEnabled()) {
+                            this.logger.error("Error parsing XPointer expression, will try to use fallback.", e);
+                        }
+                    } catch (IOException e) {
+                        this.useFallbackLevel++;
+                        if (this.logger.isErrorEnabled()) {
+                            this.logger.error("Error processing an xInclude, will try to use fallback.", e);
+                        }
+                    }
+                } else {
+                    // just parse the whole document and stream it
+                    XMLUtils.toSax(urlConnection, this);
+                }
+            } else if (XINCLUDE_PARSE_TEXT.equals(parse)) {
+                if (xpointer != null) {
+                    throw new SAXException("xpointer attribute must not be present when parse='text': "
+                         + this.getLocation());
+                }
+
+                // content type will be string like "text/xml; charset=UTF-8" or "text/xml"
+                String rawContentType = urlConnection.getContentType();
+
+                if (encoding == null) {
+                    // text/xml and application/xml offer only one optional parameter
+                    int index = rawContentType != null ? rawContentType.indexOf(';') : -1;
+
+                    String charset = null;
+                    if (index != -1) {
+                        // this should be something like "charset=UTF-8", but we want to
+                        // strip it down to just "UTF-8"
+                        charset = rawContentType.substring(index + 1).trim();
+                        if (charset.startsWith(CHARSET)) {
+                            charset = charset.substring(CHARSET.length()).trim();
+                            // strip quotes, if present
+                            if (charset.charAt(0) == '"'
+                                && charset.charAt(charset.length() - 1) == '"'
+                                || charset.charAt(0) == '\''
+                                    && charset.charAt(charset.length() - 1)
+                                        == '\'') {
+                                encoding =
+                                    charset.substring(1, charset.length() - 1);
+                            }
+                        } else {
+                            encoding = DEFAULT_CHARSET;
+                        }
+                    } else {
+                        encoding = DEFAULT_CHARSET;
+                    }
+                }
+
+                InputStream is = null;
+                InputStreamReader isr = null;
+                Reader reader = null;
+
+                try {
+                    is = urlConnection.getInputStream();
+                    isr = new InputStreamReader(is, encoding);
+                    reader = new BufferedReader(isr);
+
+                    int read;
+                    char ary[] = new char[1024 * 4];
+                    while ((read = reader.read(ary)) != -1) {
+                        this.getSAXConsumer().characters(ary, 0, read);
+                    }
+                } catch (IOException e) {
+                    this.useFallbackLevel++;
+                    if (this.logger.isErrorEnabled()) {
+                        this.logger.error("Error including text: ", e);
+                    }
+                } finally {
+                    closeQuietly(reader);
+                    closeQuietly(isr);
+                    closeQuietly(is);
+                }
+            } else {
+                throw new SAXException("Found 'parse' attribute with unknown value "
+                     + parse
+                     + " at "
+                     + this.getLocation());
+            }
+        }
+    }
+
+    /**
+     *
+     * @param sourceAtt
+     * @return
+     */
+    private URL createSource(final String sourceAtt) {
+        try {
+            URL source = null;
+            if (sourceAtt.contains(":")) {
+                source = new URL(sourceAtt);
+            } else {
+                source = new URL(this.baseUrl, sourceAtt);
+            }
+            if (this.logger.isDebugEnabled()) {
+                this.logger.debug("Including source: " + source);
+            }
+
+            return source;
+        } catch (MalformedURLException e) {
+            String message = "Can't parse URL " + sourceAtt;
+            if (this.logger.isErrorEnabled()) {
+                this.logger.error(message, e);
+            }
+            throw new ProcessingException(message, e);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void endElement(final String uri, final String localName, final String name) throws SAXException {
+        // Handle elements in xinclude namespace:
+        if (XINCLUDE_NAMESPACE_URI.equals(uri)) {
+            // Handle xi:include:
+            if (XINCLUDE_INCLUDE.equals(localName)) {
+                this.xIncludeElementLevel--;
+                if (this.useFallbackLevel > this.xIncludeElementLevel) {
+                    this.useFallbackLevel = this.xIncludeElementLevel;
+                }
+            } else if (XINCLUDE_FALLBACK.equals(localName)) {
+                // Handle xi:fallback:
+                this.fallbackElementLevel--;
+            }
+        } else if (this.isEvaluatingContent()) {
+            // Copy other elements through when appropriate:
+            this.getSAXConsumer().endElement(uri, localName, name);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void startPrefixMapping(final String prefix, final String uri) throws SAXException {
+        if (this.isEvaluatingContent()) {
+            // removed xinclude namespace from result document
+            if (!uri.equals(XINCLUDE_NAMESPACE_URI)) {
+                this.getSAXConsumer().startPrefixMapping(prefix, uri);
+            }
+            this.namespaces.put(prefix, uri);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void endPrefixMapping(final String prefix) throws SAXException {
+        if (this.isEvaluatingContent()) {
+            this.getSAXConsumer().endPrefixMapping(prefix);
+            this.namespaces.remove(prefix);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void startCDATA() throws SAXException {
+        if (this.isEvaluatingContent()) {
+            this.getSAXConsumer().startCDATA();
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void endCDATA() throws SAXException {
+        if (this.isEvaluatingContent()) {
+            this.getSAXConsumer().startCDATA();
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void startDTD(final String name, final String publicId, final String systemId) throws SAXException {
+        // ignoring DTD
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void endDTD() throws SAXException {
+        // ignoring DTD
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void startEntity(final String name) throws SAXException {
+        if (this.isEvaluatingContent()) {
+            this.getSAXConsumer().startEntity(name);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void endEntity(final String name) throws SAXException {
+        if (this.isEvaluatingContent()) {
+            this.getSAXConsumer().endEntity(name);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void characters(final char[] ch, final int start, final int length) throws SAXException {
+        if (this.isEvaluatingContent()) {
+            this.getSAXConsumer().characters(ch, start, length);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void ignorableWhitespace(final char[] ch, final int start, final int length) throws SAXException {
+        if (this.isEvaluatingContent()) {
+            this.getSAXConsumer().ignorableWhitespace(ch, start, length);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void comment(final char[] ch, final int start, final int length) throws SAXException {
+        // skip comments
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void processingInstruction(final String target, final String data) throws SAXException {
+        if (this.isEvaluatingContent()) {
+            this.getSAXConsumer().processingInstruction(target, data);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void setDocumentLocator(final Locator locator) {
+        if (this.logger.isDebugEnabled()) {
+            this.logger.debug("setDocumentLocator called "
+                    + locator.getSystemId());
+        }
+
+        this.locator = locator;
+        this.getSAXConsumer().setDocumentLocator(locator);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void skippedEntity(final String name) throws SAXException {
+        if (this.isEvaluatingContent()) {
+            this.getSAXConsumer().skippedEntity(name);
+        }
+    }
+
+    private static boolean isNotEmpty(final String string) {
+        return string != null && string.length() > 0;
+    }
+
+    private static void closeQuietly(final Reader reader) {
+        if (reader != null) {
+            try {
+                reader.close();
+            } catch (IOException e) {
+                // do nothing
+            }
+        }
+    }
+
+    private static void closeQuietly(final InputStream input) {
+        if (input != null) {
+            try {
+                input.close();
+            } catch (IOException e) {
+                // do nothing
+            }
+        }
+    }
+}

Propchange: cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/component/XIncludeTransformer.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/component/XIncludeTransformer.java
------------------------------------------------------------------------------
    svn:keywords = Id

Propchange: cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/component/XIncludeTransformer.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/AbstractPointerPart.java
URL: http://svn.apache.org/viewvc/cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/AbstractPointerPart.java?rev=892809&view=auto
==============================================================================
--- cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/AbstractPointerPart.java (added)
+++ cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/AbstractPointerPart.java Mon Dec 21 13:14:59 2009
@@ -0,0 +1,71 @@
+/*
+ * 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.sax.xpointer;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+
+/**
+ * $Id$
+ */
+abstract class AbstractPointerPart implements PointerPart {
+
+    public void characters(char[] ch, int start, int length) throws SAXException {
+        // do nothing as default behavior
+    }
+
+    public void endDocument() throws SAXException {
+        // do nothing as default behavior
+    }
+
+    public void endElement(String uri, String localName, String qName) throws SAXException {
+        // do nothing as default behavior
+    }
+
+    public void endPrefixMapping(String prefix) throws SAXException {
+        // do nothing as default behavior
+    }
+
+    public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException {
+        // do nothing as default behavior
+    }
+
+    public void processingInstruction(String target, String data) throws SAXException {
+        // do nothing as default behavior
+    }
+
+    public void setDocumentLocator(Locator locator) {
+        // do nothing as default behavior
+    }
+
+    public void skippedEntity(String name) throws SAXException {
+        // do nothing as default behavior
+    }
+
+    public void startDocument() throws SAXException {
+        // do nothing as default behavior
+    }
+
+    public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException {
+        // do nothing as default behavior
+    }
+
+    public void startPrefixMapping(String prefix, String uri) throws SAXException {
+        // do nothing as default behavior
+    }
+}

Propchange: cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/AbstractPointerPart.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/AbstractPointerPart.java
------------------------------------------------------------------------------
    svn:keywords = Id

Propchange: cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/AbstractPointerPart.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/PointerPart.java
URL: http://svn.apache.org/viewvc/cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/PointerPart.java?rev=892809&view=auto
==============================================================================
--- cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/PointerPart.java (added)
+++ cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/PointerPart.java Mon Dec 21 13:14:59 2009
@@ -0,0 +1,35 @@
+/*
+ * 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.sax.xpointer;
+
+import java.io.IOException;
+
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+
+/**
+ * Interface to be implemented by pointer parts (xpointer schemes).
+ */
+public interface PointerPart extends ContentHandler {
+
+    /**
+    * If this pointer part successfully identifies any subresources, it should
+    * stream them to the XMLConsumer available from the XPointerContext and return true.
+    * Otherwise this method should return false.
+    */
+    void setUp(XPointerContext xpointerContext) throws SAXException, IOException;
+}

Propchange: cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/PointerPart.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/PointerPart.java
------------------------------------------------------------------------------
    svn:keywords = Id

Propchange: cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/PointerPart.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/ShorthandPart.java
URL: http://svn.apache.org/viewvc/cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/ShorthandPart.java?rev=892809&view=auto
==============================================================================
--- cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/ShorthandPart.java (added)
+++ cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/ShorthandPart.java Mon Dec 21 13:14:59 2009
@@ -0,0 +1,155 @@
+/*
+ * 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.sax.xpointer;
+
+import java.io.IOException;
+
+import org.apache.cocoon.sax.SAXConsumer;
+import org.xml.sax.Attributes;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+
+/**
+ * Implements support for shorthand XPointers (= id-based lookup). We treat them here as if they
+ * were a pointerpart too.
+ *
+ * <p>Note that although this is implemented here, this feature depends on the presence of a DTD,
+ * and a validating parser. Currently, this means its unuseable within Cocoon.
+ * 
+ * $Id$
+ */
+public final class ShorthandPart extends AbstractPointerPart {
+
+    private final static String XMLNS_NAMESPACE_98 = "http://www.w3.org/XML/1998/namespace";
+
+    private final static String XMLNS_NAMESPACE_00 = "http://www.w3.org/2000/xmlns/";
+
+    private final static String ID = "id";
+
+    private final String shorthand;
+
+    private SAXConsumer saxConsumer;
+
+    private boolean matching = false;
+
+    private int matchingLevel = 0;
+
+    public ShorthandPart(final String shorthand) {
+        this.shorthand = shorthand;
+    }
+
+    public void setUp(final XPointerContext xpointerContext) throws SAXException, IOException {
+        this.saxConsumer = xpointerContext.getSaxConsumer();
+    }
+
+    @Override
+    public void characters(char[] ch, int start, int length) throws SAXException {
+        if (this.matching) {
+            this.saxConsumer.characters(ch, start, length);
+        }
+    }
+
+    @Override
+    public void endDocument() throws SAXException {
+        this.saxConsumer.endDocument();
+    }
+
+    @Override
+    public void endElement(String uri, String localName, String qName) throws SAXException {
+        if (this.matching) {
+            this.saxConsumer.endElement(uri, localName, qName);
+            this.matchingLevel--;
+
+            if (this.matchingLevel == 0) {
+                this.matching = false;
+            }
+        }
+    }
+
+    @Override
+    public void endPrefixMapping(String prefix) throws SAXException {
+        if (this.matching) {
+            this.saxConsumer.endPrefixMapping(prefix);
+        }
+    }
+
+    @Override
+    public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException {
+        if (this.matching) {
+            this.saxConsumer.ignorableWhitespace(ch, start, length);
+        }
+    }
+
+    @Override
+    public void processingInstruction(String target, String data) throws SAXException {
+        if (this.matching) {
+            this.saxConsumer.processingInstruction(target, data);
+        }
+    }
+
+    @Override
+    public void setDocumentLocator(Locator locator) {
+        // ignored, already set on the sax consumer
+    }
+
+    @Override
+    public void skippedEntity(String name) throws SAXException {
+        if (this.matching) {
+            this.saxConsumer.skippedEntity(name);
+        }
+    }
+
+    @Override
+    public void startDocument() throws SAXException {
+        this.saxConsumer.startDocument();
+    }
+
+    @Override
+    public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException {
+        if (!this.matching) {
+            dance: for (int i = 0; i < atts.getLength(); i++) {
+                String attributeURI = atts.getURI(i);
+                String attributeName = atts.getLocalName(i);
+                String attributeValue = atts.getValue(i);
+
+                if ((attributeURI == null
+                        || attributeURI.length() == 0
+                        || XMLNS_NAMESPACE_98.equals(attributeURI)
+                        || XMLNS_NAMESPACE_00.equals(attributeURI))
+                        && ID.equalsIgnoreCase(attributeName)
+                        && this.shorthand.equals(attributeValue)) {
+                    this.matching = true;
+                    this.matchingLevel = 0;
+
+                    break dance;
+                }
+            }
+        }
+
+        if (this.matching) {
+            this.saxConsumer.startElement(uri, localName, qName, atts);
+            this.matchingLevel++;
+        }
+    }
+
+    @Override
+    public void startPrefixMapping(String prefix, String uri) throws SAXException {
+        if (this.matching) {
+            this.saxConsumer.startPrefixMapping(prefix, uri);
+        }
+    }
+}

Propchange: cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/ShorthandPart.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/ShorthandPart.java
------------------------------------------------------------------------------
    svn:keywords = Id

Propchange: cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/ShorthandPart.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/TransformerHandlerFactory.java
URL: http://svn.apache.org/viewvc/cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/TransformerHandlerFactory.java?rev=892809&view=auto
==============================================================================
--- cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/TransformerHandlerFactory.java (added)
+++ cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/TransformerHandlerFactory.java Mon Dec 21 13:14:59 2009
@@ -0,0 +1,89 @@
+/*
+ * 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.sax.xpointer;
+
+import java.io.StringReader;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import javax.xml.transform.Source;
+import javax.xml.transform.Templates;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.sax.SAXResult;
+import javax.xml.transform.sax.SAXTransformerFactory;
+import javax.xml.transform.sax.TransformerHandler;
+import javax.xml.transform.stream.StreamSource;
+
+import org.xml.sax.ContentHandler;
+
+/**
+ * $Id$
+ */
+final class TransformerHandlerFactory {
+
+    /**
+     * A generic transformer factory to parse XSLTs.
+     */
+    private static final SAXTransformerFactory TRAX_FACTORY = (SAXTransformerFactory) TransformerFactory.newInstance();
+
+    private static final String XMLNS_PATTERN = "xmlns:%s=\"%s\"";
+
+    private static final String XSLT_PATTERN =
+        "<xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" %s>"
+        + "<xsl:template match=\"%s\">"
+        + "<xsl:copy-of select=\".\"/>"
+        + "</xsl:template></xsl:stylesheet>";
+
+    private static final Map<String, Templates> TEMPLATES = new HashMap<String, Templates>();
+
+    /**
+     * This class can't be instantiated
+     */
+    private TransformerHandlerFactory() {
+        // do nothing
+    }
+
+    public static TransformerHandler borrowHandler(final Map<String, String> namespaces,
+            final String expression,
+            final ContentHandler delegate) throws TransformerConfigurationException {
+        Templates templates = null;
+        if (TEMPLATES.containsKey(expression)) {
+            templates = TEMPLATES.get(expression);
+        } else {
+            StringBuilder builder = new StringBuilder();
+            for (Entry<String, String> namespace : namespaces.entrySet()) {
+                builder.append('\n');
+                builder.append(String.format(XMLNS_PATTERN, namespace.getKey(), namespace.getValue()));
+            }
+            String xmlNamespaces = builder.toString();
+
+            String xslt = String.format(XSLT_PATTERN, xmlNamespaces, expression);
+            Source source = new StreamSource(new StringReader(xslt));
+            templates = TRAX_FACTORY.newTemplates(source);
+            TEMPLATES.put(expression, templates);
+        }
+
+        final SAXResult result = new SAXResult();
+        result.setHandler(delegate);
+
+        TransformerHandler transformerHandler = TRAX_FACTORY.newTransformerHandler(templates);
+        transformerHandler.setResult(result);
+        return transformerHandler;
+    }
+}

Propchange: cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/TransformerHandlerFactory.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/TransformerHandlerFactory.java
------------------------------------------------------------------------------
    svn:keywords = Id

Propchange: cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/TransformerHandlerFactory.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/UnsupportedPart.java
URL: http://svn.apache.org/viewvc/cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/UnsupportedPart.java?rev=892809&view=auto
==============================================================================
--- cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/UnsupportedPart.java (added)
+++ cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/UnsupportedPart.java Mon Dec 21 13:14:59 2009
@@ -0,0 +1,41 @@
+/*
+ * 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.sax.xpointer;
+
+import java.io.IOException;
+
+import org.xml.sax.SAXException;
+
+/**
+ * $Id$
+ */
+public final class UnsupportedPart extends AbstractPointerPart {
+
+    private String schemeName;
+
+    public UnsupportedPart(final String schemeName) {
+        this.schemeName = schemeName;
+    }
+
+    public void setUp(final XPointerContext xpointerContext) throws SAXException, IOException {
+        throw new SAXException("Scheme '"
+                + this.schemeName
+                + "' not supported by this XPointer implementation, as used in the fragment identifier '"
+                + xpointerContext.getXPointer()
+                + "'");
+    }
+}

Propchange: cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/UnsupportedPart.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/UnsupportedPart.java
------------------------------------------------------------------------------
    svn:keywords = Id

Propchange: cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/UnsupportedPart.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/XPointer.java
URL: http://svn.apache.org/viewvc/cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/XPointer.java?rev=892809&view=auto
==============================================================================
--- cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/XPointer.java (added)
+++ cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/XPointer.java Mon Dec 21 13:14:59 2009
@@ -0,0 +1,177 @@
+/*
+ * 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.sax.xpointer;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.logging.Log;
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+
+/**
+ * $Id$
+ */
+public final class XPointer implements ContentHandler {
+
+    private final List<PointerPart> pointerParts = new ArrayList<PointerPart>();
+
+    private Log log;
+
+    public void setLog(Log log) {
+        this.log = log;
+    }
+
+    public void addPart(final PointerPart part) {
+        this.pointerParts.add(part);
+    }
+
+    public void setUp(final XPointerContext xpointerContext) throws SAXException, IOException {
+        for (PointerPart pointerPart : this.pointerParts) {
+            pointerPart.setUp(xpointerContext);
+        }
+    }
+
+    public void characters(char[] ch, int start, int length) throws SAXException {
+        for (ContentHandler contentHandler : this.pointerParts) {
+            try {
+                contentHandler.characters(ch, start, length);
+            } catch (SAXException e) {
+                if (this.log.isWarnEnabled()) {
+                    this.log.warn("An error occurred while spreading characters", e);
+                }
+            }
+        }
+    }
+
+    public void endDocument() throws SAXException {
+        for (ContentHandler contentHandler : this.pointerParts) {
+            try {
+                contentHandler.endDocument();
+            } catch (SAXException e) {
+                if (this.log.isWarnEnabled()) {
+                    this.log.warn("An error occurred while spreading endDocument", e);
+                }
+            }
+        }
+    }
+
+    public void endElement(String uri, String localName, String qName) throws SAXException {
+        for (ContentHandler contentHandler : this.pointerParts) {
+            try {
+                contentHandler.endElement(uri, localName, qName);
+            } catch (SAXException e) {
+                if (this.log.isWarnEnabled()) {
+                    this.log.warn("An error occurred while spreading endElement", e);
+                }
+            }
+        }
+    }
+
+    public void endPrefixMapping(String prefix) throws SAXException {
+        for (ContentHandler contentHandler : this.pointerParts) {
+            try {
+                contentHandler.endPrefixMapping(prefix);
+            } catch (SAXException e) {
+                if (this.log.isWarnEnabled()) {
+                    this.log.warn("An error occurred while spreading endPrefixMapping", e);
+                }
+            }
+        }
+    }
+
+    public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException {
+        for (ContentHandler contentHandler : this.pointerParts) {
+            try {
+                contentHandler.ignorableWhitespace(ch, start, length);
+            } catch (SAXException e) {
+                if (this.log.isWarnEnabled()) {
+                    this.log.warn("An error occurred while spreading ignorableWhitespace", e);
+                }
+            }
+        }
+    }
+
+    public void processingInstruction(String target, String data) throws SAXException {
+        for (ContentHandler contentHandler : this.pointerParts) {
+            try {
+                contentHandler.processingInstruction(target, data);
+            } catch (SAXException e) {
+                if (this.log.isWarnEnabled()) {
+                    this.log.warn("An error occurred while spreading processingInstruction", e);
+                }
+            }
+        }
+    }
+
+    public void setDocumentLocator(Locator locator) {
+        for (ContentHandler contentHandler : this.pointerParts) {
+            contentHandler.setDocumentLocator(locator);
+        }
+    }
+
+    public void skippedEntity(String name) throws SAXException {
+        for (ContentHandler contentHandler : this.pointerParts) {
+            try {
+                contentHandler.skippedEntity(name);
+            } catch (SAXException e) {
+                if (this.log.isWarnEnabled()) {
+                    this.log.warn("An error occurred while spreading skippedEntity", e);
+                }
+            }
+        }
+    }
+
+    public void startDocument() throws SAXException {
+        for (ContentHandler contentHandler : this.pointerParts) {
+            try {
+                contentHandler.startDocument();
+            } catch (SAXException e) {
+                if (this.log.isWarnEnabled()) {
+                    this.log.warn("An error occurred while spreading skippedEntity", e);
+                }
+            }
+        }
+    }
+
+    public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException {
+        for (ContentHandler contentHandler : this.pointerParts) {
+            try {
+                contentHandler.startElement(uri, localName, qName, atts);
+            } catch (SAXException e) {
+                if (this.log.isWarnEnabled()) {
+                    this.log.warn("An error occurred while spreading skippedEntity", e);
+                }
+            }
+        }
+    }
+
+    public void startPrefixMapping(String prefix, String uri) throws SAXException {
+        for (ContentHandler contentHandler : this.pointerParts) {
+            try {
+                contentHandler.startPrefixMapping(prefix, uri);
+            } catch (SAXException e) {
+                if (this.log.isWarnEnabled()) {
+                    this.log.warn("An error occurred while spreading skippedEntity", e);
+                }
+            }
+        }
+    }
+}

Propchange: cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/XPointer.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/XPointer.java
------------------------------------------------------------------------------
    svn:keywords = Id

Propchange: cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/XPointer.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/XPointerContext.java
URL: http://svn.apache.org/viewvc/cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/XPointerContext.java?rev=892809&view=auto
==============================================================================
--- cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/XPointerContext.java (added)
+++ cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/XPointerContext.java Mon Dec 21 13:14:59 2009
@@ -0,0 +1,86 @@
+/*
+ * 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.sax.xpointer;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import javax.xml.namespace.NamespaceContext;
+
+import org.apache.cocoon.sax.SAXConsumer;
+
+/**
+ * $Id$
+ */
+public final class XPointerContext implements NamespaceContext {
+
+    private final static String XML_NAMESPACE = "http://www.w3.org/XML/1998/namespace";
+
+    private final static String XMLNS_NAMESPACE = "http://www.w3.org/2000/xmlns/";
+
+    private final static String XSL_NAMESPACE = "http://www.w3.org/1999/XSL/Transform";
+
+    private final Map<String, String> namespaces = new HashMap<String, String>();
+
+    private final String xPointer;
+
+    private final SAXConsumer saxConsumer;
+
+    public XPointerContext(final String xPointer, final SAXConsumer saxConsumer) {
+        this.xPointer = xPointer;
+        this.saxConsumer = saxConsumer;
+    }
+
+    public String getXPointer() {
+        return this.xPointer;
+    }
+
+    public SAXConsumer getSaxConsumer() {
+        return this.saxConsumer;
+    }
+
+    public Map<String, String> getNamespaces() {
+        return this.namespaces;
+    }
+
+    public void addPrefix(final String prefix, final String namespaceURI) {
+        if (XML_NAMESPACE.equals(namespaceURI)
+                || XMLNS_NAMESPACE.equals(namespaceURI)
+                || XSL_NAMESPACE.equals(namespaceURI)) {
+            return;
+        }
+
+        this.namespaces.put(prefix, namespaceURI);
+    }
+
+    // This method isn't necessary for XPath processing either.
+    public String getPrefix(String namespaceURI) {
+        throw new UnsupportedOperationException();
+    }
+
+    @SuppressWarnings("unchecked")
+    // This method isn't necessary for XPath processing either.
+    public Iterator getPrefixes(final String namespaceURI) {
+        throw new UnsupportedOperationException();
+    }
+
+    // This method isn't necessary for XPath processing either.
+    public String getNamespaceURI(final String prefix) {
+        return this.namespaces.get(prefix);
+    }
+}

Propchange: cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/XPointerContext.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/XPointerContext.java
------------------------------------------------------------------------------
    svn:keywords = Id

Propchange: cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/XPointerContext.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/XPointerPart.java
URL: http://svn.apache.org/viewvc/cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/XPointerPart.java?rev=892809&view=auto
==============================================================================
--- cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/XPointerPart.java (added)
+++ cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/XPointerPart.java Mon Dec 21 13:14:59 2009
@@ -0,0 +1,161 @@
+/*
+ * 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.sax.xpointer;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import javax.xml.transform.Source;
+import javax.xml.transform.Templates;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.sax.SAXResult;
+import javax.xml.transform.sax.SAXTransformerFactory;
+import javax.xml.transform.sax.TransformerHandler;
+import javax.xml.transform.stream.StreamSource;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+
+/**
+ * $Id$
+ */
+public final class XPointerPart extends AbstractPointerPart {
+
+    /**
+     * A generic transformer factory to parse XSLTs.
+     */
+    private static final SAXTransformerFactory TRAX_FACTORY = (SAXTransformerFactory) TransformerFactory.newInstance();
+
+    private static final String XMLNS_PATTERN = "xmlns:%s=\"%s\"";
+
+    private static final String XSLT_PATTERN =
+        "<xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" %s>"
+        + "<xsl:template match=\"%s\">"
+        + "<xsl:copy-of select=\".\"/>"
+        + "</xsl:template></xsl:stylesheet>";
+
+    private static final Map<String, Templates> TEMPLATES = new HashMap<String, Templates>();
+
+    private final String expression;
+
+    private TransformerHandler traxHandler;
+
+    public XPointerPart(final String expression) {
+        this.expression = expression;
+    }
+
+    public void setUp(final XPointerContext xpointerContext) throws SAXException, IOException {
+        Templates templates;
+        if (TEMPLATES.containsKey(this.expression)) {
+            templates = TEMPLATES.get(this.expression);
+        } else {
+            StringBuilder builder = new StringBuilder();
+            for (Entry<String, String> namespace : xpointerContext.getNamespaces().entrySet()) {
+                builder.append('\n');
+                builder.append(String.format(XMLNS_PATTERN, namespace.getKey(), namespace.getValue()));
+            }
+            String xmlNamespaces = builder.toString();
+
+            String xslt = String.format(XSLT_PATTERN, xmlNamespaces, this.expression);
+            Source source = new StreamSource(new StringReader(xslt));
+            try {
+                templates = TRAX_FACTORY.newTemplates(source);
+            } catch (TransformerConfigurationException tce) {
+                throw new SAXException("XPointer expression '"
+                        + this.expression
+                        + "' not valid as used in the fragment identifier '"
+                        + xpointerContext.getXPointer()
+                        + "'", tce);
+            }
+            TEMPLATES.put(this.expression, templates);
+        }
+
+        final SAXResult result = new SAXResult();
+        result.setHandler(xpointerContext.getSaxConsumer());
+
+        try {
+            this.traxHandler = TRAX_FACTORY.newTransformerHandler(templates);
+            this.traxHandler.setResult(result);
+        } catch (TransformerConfigurationException tce) {
+            throw new SAXException("Impossible to initialize transformer handler for XPointer expression '"
+                    + this.expression
+                    + "' as used in the fragment identifier '"
+                    + xpointerContext.getXPointer()
+                    + "'", tce);
+        }
+    }
+
+    @Override
+    public void characters(char[] ch, int start, int length) throws SAXException {
+        this.traxHandler.characters(ch, start, length);
+    }
+
+    @Override
+    public void endDocument() throws SAXException {
+        this.traxHandler.endDocument();
+    }
+
+    @Override
+    public void endElement(String uri, String localName, String qName) throws SAXException {
+        this.traxHandler.endElement(uri, localName, qName);
+    }
+
+    @Override
+    public void endPrefixMapping(String prefix) throws SAXException {
+        this.traxHandler.endPrefixMapping(prefix);
+    }
+
+    @Override
+    public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException {
+        this.traxHandler.ignorableWhitespace(ch, start, length);
+    }
+
+    @Override
+    public void processingInstruction(String target, String data) throws SAXException {
+        this.traxHandler.processingInstruction(target, data);
+    }
+
+    @Override
+    public void setDocumentLocator(Locator locator) {
+        this.traxHandler.setDocumentLocator(locator);
+    }
+
+    @Override
+    public void skippedEntity(String name) throws SAXException {
+        this.traxHandler.skippedEntity(name);
+    }
+
+    @Override
+    public void startDocument() throws SAXException {
+        this.traxHandler.startDocument();
+    }
+
+    @Override
+    public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException {
+        this.traxHandler.startElement(uri, localName, qName, atts);
+    }
+
+    @Override
+    public void startPrefixMapping(String prefix, String uri) throws SAXException {
+        this.traxHandler.startPrefixMapping(prefix, uri);
+    }
+}

Propchange: cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/XPointerPart.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/XPointerPart.java
------------------------------------------------------------------------------
    svn:keywords = Id

Propchange: cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/XPointerPart.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/XmlnsPart.java
URL: http://svn.apache.org/viewvc/cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/XmlnsPart.java?rev=892809&view=auto
==============================================================================
--- cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/XmlnsPart.java (added)
+++ cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/XmlnsPart.java Mon Dec 21 13:14:59 2009
@@ -0,0 +1,46 @@
+/*
+ * 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.sax.xpointer;
+
+import java.io.IOException;
+
+import org.xml.sax.SAXException;
+
+/**
+ * $Id$
+ */
+public final class XmlnsPart extends AbstractPointerPart {
+
+    private String prefix;
+
+    private String namespace;
+
+    /**
+     * Creates an XmlnsPart.
+     */
+    public XmlnsPart(final String prefix, final String namespace) {
+        this.prefix = prefix;
+        this.namespace = namespace;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void setUp(final XPointerContext xpointerContext) throws SAXException, IOException {
+        xpointerContext.addPrefix(this.prefix, this.namespace);
+    }
+}

Propchange: cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/XmlnsPart.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/XmlnsPart.java
------------------------------------------------------------------------------
    svn:keywords = Id

Propchange: cocoon/cocoon3/trunk/cocoon-sax/src/main/java/org/apache/cocoon/sax/xpointer/XmlnsPart.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: cocoon/cocoon3/trunk/cocoon-sax/src/main/javacc/xpointer-fw.jj
URL: http://svn.apache.org/viewvc/cocoon/cocoon3/trunk/cocoon-sax/src/main/javacc/xpointer-fw.jj?rev=892809&view=auto
==============================================================================
--- cocoon/cocoon3/trunk/cocoon-sax/src/main/javacc/xpointer-fw.jj (added)
+++ cocoon/cocoon3/trunk/cocoon-sax/src/main/javacc/xpointer-fw.jj Mon Dec 21 13:14:59 2009
@@ -0,0 +1,309 @@
+options {
+    STATIC = false;
+}
+
+PARSER_BEGIN(XPointerFrameworkParser)
+/*
+ * 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.sax.xpointer;
+
+import java.io.StringReader;
+import java.util.Map;
+import java.util.HashMap;
+
+/**
+ * Parser for the XPointer Framework Syntax, see the specification at:
+ * http://www.w3.org/TR/2003/REC-xptr-framework-20030325/
+ *
+ * Some of the token definitions (for recognizing NCName's) were copied from
+ * the JXPath parser from Apache Commons (to save some typing), written by
+ * Ingo Macherius, Gerald Huck <{macherius, huck}@gmd.de> and Dmitri Plotnikov.
+ *
+ * $Id$
+ */
+public class XPointerFrameworkParser {
+
+    private static final String XPOINTER = "xpointer";
+
+    private static final String XMLNS = "xmlns";
+
+    private XPointer xpointer = new XPointer();
+
+    private Map<String, String> namespaces = new HashMap<String, String>();
+
+    public static XPointer parse(final String xpointer) throws ParseException {
+        XPointerFrameworkParser xfp = new XPointerFrameworkParser(new StringReader(xpointer));
+        xfp.pointer();
+        return xfp.getXPointer();
+    }
+
+    public XPointer getXPointer() {
+        return this.xpointer;
+    }
+
+    private String unescape(final String data) throws ParseException {
+        StringBuilder result = new StringBuilder(data.length());
+        boolean inCircumflex = false;
+        for (int i = 0; i < data.length(); i++) {
+            char c = data.charAt(i);
+            if (inCircumflex) {
+                switch (c) {
+                    case '^':
+                    case '(':
+                    case ')':
+                        result.append(c);
+                        inCircumflex = false;
+                        break;
+
+                    default:
+                        throw new ParseException("Incorrect use of circumflex character at position "
+                        + i
+                        + " in the string "
+                        + data);
+                }
+            } else if (c == '^') {
+                inCircumflex = true;
+            } else {
+                result.append(c);
+            }
+        }
+        return result.toString();
+    }
+
+}
+
+PARSER_END(XPointerFrameworkParser)
+
+TOKEN :
+{
+  <#Letter : <BaseChar> | <Ideographic> >
+| <#BaseChar :
+        (
+        ["\u0041"-"\u005A"] | ["\u0061"-"\u007A"] | ["\u00C0"-"\u00D6"] | ["\u00D8"-"\u00F6"] |
+        ["\u00F8"-"\u00FF"] | ["\u0100"-"\u0131"] | ["\u0134"-"\u013E"] | ["\u0141"-"\u0148"] |
+        ["\u014A"-"\u017E"] | ["\u0180"-"\u01C3"] | ["\u01CD"-"\u01F0"] | ["\u01F4"-"\u01F5"] |
+        ["\u01FA"-"\u0217"] | ["\u0250"-"\u02A8"] | ["\u02BB"-"\u02C1"] | "\u0386" | ["\u0388"-"\u038A"] |
+        "\u038C" | ["\u038E"-"\u03A1"] | ["\u03A3"-"\u03CE"] | ["\u03D0"-"\u03D6"] | "\u03DA" |
+        "\u03DC" |  "\u03DE" | "\u03E0" | ["\u03E2"-"\u03F3"] | ["\u0401"-"\u040C"] | ["\u040E"-"\u044F"] |
+        ["\u0451"-"\u045C"] | ["\u045E"-"\u0481"] | ["\u0490"-"\u04C4"] | ["\u04C7"-"\u04C8"] |
+        ["\u04CB"-"\u04CC"] | ["\u04D0"-"\u04EB"] | ["\u04EE"-"\u04F5"] | ["\u04F8"-"\u04F9"] |
+        ["\u0531"-"\u0556"] | "\u0559" | ["\u0561"-"\u0586"] | ["\u05D0"-"\u05EA"] | ["\u05F0"-"\u05F2"] |
+        ["\u0621"-"\u063A"] | ["\u0641"-"\u064A"] | ["\u0671"-"\u06B7"] | ["\u06BA"-"\u06BE"] |
+        ["\u06C0"-"\u06CE"] | ["\u06D0"-"\u06D3"] | "\u06D5" | ["\u06E5"-"\u06E6"] | ["\u0905"-"\u0939"] |
+        "\u093D" | ["\u0958"-"\u0961"] | ["\u0985"-"\u098C"] | ["\u098F"-"\u0990"] | ["\u0993"-"\u09A8"] |
+        ["\u09AA"-"\u09B0"] | "\u09B2" | ["\u09B6"-"\u09B9"] | ["\u09DC"-"\u09DD"] | ["\u09DF"-"\u09E1"] |
+        ["\u09F0"-"\u09F1"] | ["\u0A05"-"\u0A0A"] | ["\u0A0F"-"\u0A10"] | ["\u0A13"-"\u0A28"] |
+        ["\u0A2A"-"\u0A30"] | ["\u0A32"-"\u0A33"] | ["\u0A35"-"\u0A36"] | ["\u0A38"-"\u0A39"] |
+        ["\u0A59"-"\u0A5C"] | "\u0A5E" | ["\u0A72"-"\u0A74"] | ["\u0A85"-"\u0A8B"] | "\u0A8D" |
+        ["\u0A8F"-"\u0A91"] | ["\u0A93"-"\u0AA8"] | ["\u0AAA"-"\u0AB0"] | ["\u0AB2"-"\u0AB3"] |
+        ["\u0AB5"-"\u0AB9"] | "\u0ABD" | "\u0AE0" |  ["\u0B05"-"\u0B0C"] | ["\u0B0F"-"\u0B10"] |
+        ["\u0B13"-"\u0B28"] | ["\u0B2A"-"\u0B30"] | ["\u0B32"-"\u0B33"] | ["\u0B36"-"\u0B39"] |
+        "\u0B3D" | ["\u0B5C"-"\u0B5D"] | ["\u0B5F"-"\u0B61"] | ["\u0B85"-"\u0B8A"] |  ["\u0B8E"-"\u0B90"] |
+        ["\u0B92"-"\u0B95"] |  ["\u0B99"-"\u0B9A"] | "\u0B9C" | ["\u0B9E"-"\u0B9F"] | ["\u0BA3"-"\u0BA4"] |
+        ["\u0BA8"-"\u0BAA"] | ["\u0BAE"-"\u0BB5"] | ["\u0BB7"-"\u0BB9"] | ["\u0C05"-"\u0C0C"] |
+        ["\u0C0E"-"\u0C10"] | ["\u0C12"-"\u0C28"] | ["\u0C2A"-"\u0C33"] | ["\u0C35"-"\u0C39"] |
+        ["\u0C60"-"\u0C61"] | ["\u0C85"-"\u0C8C"] | ["\u0C8E"-"\u0C90"] | ["\u0C92"-"\u0CA8"] |
+        ["\u0CAA"-"\u0CB3"] | ["\u0CB5"-"\u0CB9"] | "\u0CDE" | ["\u0CE0"-"\u0CE1"] | ["\u0D05"-"\u0D0C"] |
+        ["\u0D0E"-"\u0D10"] | ["\u0D12"-"\u0D28"] | ["\u0D2A"-"\u0D39"] | ["\u0D60"-"\u0D61"] |
+        ["\u0E01"-"\u0E2E"] | "\u0E30" | ["\u0E32"-"\u0E33"] | ["\u0E40"-"\u0E45"] | ["\u0E81"-"\u0E82"] |
+        "\u0E84" | ["\u0E87"-"\u0E88"] | "\u0E8A" | "\u0E8D" | ["\u0E94"-"\u0E97"] | ["\u0E99"-"\u0E9F"] |
+        ["\u0EA1"-"\u0EA3"] | "\u0EA5" | "\u0EA7" | ["\u0EAA"-"\u0EAB"] | ["\u0EAD"-"\u0EAE"] | "\u0EB0" |
+        ["\u0EB2"-"\u0EB3"] | "\u0EBD" | ["\u0EC0"-"\u0EC4"] | ["\u0F40"-"\u0F47"] | ["\u0F49"-"\u0F69"] |
+        ["\u10A0"-"\u10C5"] | ["\u10D0"-"\u10F6"] | "\u1100" | ["\u1102"-"\u1103"] | ["\u1105"-"\u1107"] |
+        "\u1109" | ["\u110B"-"\u110C"] | ["\u110E"-"\u1112"] | "\u113C" | "\u113E" | "\u1140" | "\u114C" |
+        "\u114E" | "\u1150" | ["\u1154"-"\u1155"] | "\u1159" | ["\u115F"-"\u1161"] | "\u1163" | "\u1165" |
+        "\u1167" | "\u1169" | ["\u116D"-"\u116E"] | ["\u1172"-"\u1173"] | "\u1175" | "\u119E" | "\u11A8" |
+        "\u11AB" | ["\u11AE"-"\u11AF"] | ["\u11B7"-"\u11B8"] | "\u11BA" |  ["\u11BC"-"\u11C2"] | "\u11EB" |
+        "\u11F0" | "\u11F9" | ["\u1E00"-"\u1E9B"] | ["\u1EA0"-"\u1EF9"] | ["\u1F00"-"\u1F15"] |
+        ["\u1F18"-"\u1F1D"] |
+        ["\u1F20"-"\u1F45"] | ["\u1F48"-"\u1F4D"] | ["\u1F50"-"\u1F57"] | "\u1F59" | "\u1F5B" | "\u1F5D" |
+        ["\u1F5F"-"\u1F7D"] | ["\u1F80"-"\u1FB4"] | ["\u1FB6"-"\u1FBC"] | "\u1FBE" |  ["\u1FC2"-"\u1FC4"] |
+        ["\u1FC6"-"\u1FCC"] | ["\u1FD0"-"\u1FD3"] | ["\u1FD6"-"\u1FDB"] | ["\u1FE0"-"\u1FEC"] |
+        ["\u1FF2"-"\u1FF4"] | ["\u1FF6"-"\u1FFC"] | "\u2126" | ["\u212A"-"\u212B"] | "\u212E" |
+        ["\u2180"-"\u2182"] | ["\u3041"-"\u3094"] | ["\u30A1"-"\u30FA"] | ["\u3105"-"\u312C"] |
+        ["\uAC00"-"\uD7A3"]
+        ) >
+| <#Ideographic : (["\u4E00"-"\u9FA5"] | "\u3007" | ["\u3021"-"\u3029"]) >
+| <#CombiningChar :
+        (
+        ["\u0300"-"\u0345"]    |    ["\u0360"-"\u0361"]     |    ["\u0483"-"\u0486"]    |    ["\u0591"-"\u05A1"] |
+        ["\u05A3"-"\u05B9"]    |    ["\u05BB"-"\u05BD"]        |    "\u05BF"             |    ["\u05C1"-"\u05C2"] |
+        "\u05C4"             | ["\u064B"-"\u0652"] | "\u0670"             | ["\u06D6"-"\u06DC"] |
+        ["\u06DD"-"\u06DF"] | ["\u06E0"-"\u06E4"] | ["\u06E7"-"\u06E8"] | ["\u06EA"-"\u06ED"] |
+        ["\u0901"-"\u0903"] | "\u093C"    |["\u093E"-"\u094C"] | "\u094D" | ["\u0951"-"\u0954"] |
+        ["\u0962"-"\u0963"] | ["\u0981"-"\u0983"] | "\u09BC" | "\u09BE" | "\u09BF" | ["\u09C0"-"\u09C4"] |
+        ["\u09C7"-"\u09C8"] | ["\u09CB"-"\u09CD"] | "\u09D7" | ["\u09E2"-"\u09E3"] | "\u0A02" | "\u0A3C" |
+        "\u0A3E" | "\u0A3F" | ["\u0A40"-"\u0A42"] |
+        ["\u0A47"-"\u0A48"] | ["\u0A4B"-"\u0A4D"] | ["\u0A70"-"\u0A71"] | ["\u0A81"-"\u0A83"] | "\u0ABC" |
+        ["\u0ABE"-"\u0AC5"] | ["\u0AC7"-"\u0AC9"] | ["\u0ACB"-"\u0ACD"] | ["\u0B01"-"\u0B03"] | "\u0B3C" |
+        ["\u0B3E"-"\u0B43"] | ["\u0B47"-"\u0B48"] | ["\u0B4B"-"\u0B4D"] | ["\u0B56"-"\u0B57"] |
+        ["\u0B82"-"\u0B83"] | ["\u0BBE"-"\u0BC2"] | ["\u0BC6"-"\u0BC8"] | ["\u0BCA"-"\u0BCD"] | "\u0BD7" |
+        ["\u0C01"-"\u0C03"] | ["\u0C3E"-"\u0C44"] | ["\u0C46"-"\u0C48"] | ["\u0C4A"-"\u0C4D"] |
+        ["\u0C55"-"\u0C56"] | ["\u0C82"-"\u0C83"] | ["\u0CBE"-"\u0CC4"] | ["\u0CC6"-"\u0CC8"] |
+        ["\u0CCA"-"\u0CCD"] | ["\u0CD5"-"\u0CD6"] | ["\u0D02"-"\u0D03"] | ["\u0D3E"-"\u0D43"] |
+        ["\u0D46"-"\u0D48"] | ["\u0D4A"-"\u0D4D"] | "\u0D57" | "\u0E31" | ["\u0E34"-"\u0E3A"] |
+        ["\u0E47"-"\u0E4E"] | "\u0EB1" | ["\u0EB4"-"\u0EB9"] | ["\u0EBB"-"\u0EBC"] | ["\u0EC8"-"\u0ECD"] |
+        ["\u0F18"-"\u0F19"] | "\u0F35" | "\u0F37" | "\u0F39" | "\u0F3E" | "\u0F3F" | ["\u0F71"-"\u0F84"] |
+        ["\u0F86"-"\u0F8B"] | ["\u0F90"-"\u0F95"] | "\u0F97" | ["\u0F99"-"\u0FAD"] | ["\u0FB1"-"\u0FB7"] |
+        "\u0FB9" | ["\u20D0"-"\u20DC"] | "\u20E1" | ["\u302A"-"\u302F"] | "\u3099" | "\u309A"
+        )
+    >
+| <#UnicodeDigit :
+        ["\u0030"-"\u0039"] | ["\u0660"-"\u0669"] | ["\u06F0"-"\u06F9"] | ["\u0966"-"\u096F"] |
+        ["\u09E6"-"\u09EF"] | ["\u0A66"-"\u0A6F"] | ["\u0AE6"-"\u0AEF"] | ["\u0B66"-"\u0B6F"] |
+        ["\u0BE7"-"\u0BEF"] | ["\u0C66"-"\u0C6F"] | ["\u0CE6"-"\u0CEF"] | ["\u0D66"-"\u0D6F"] |
+        ["\u0E50"-"\u0E59"] | ["\u0ED0"-"\u0ED9"] | ["\u0F20"-"\u0F29"]
+    >
+| <#Extender :
+        "\u00B7" | "\u02D0" | "\u02D1" | "\u0387" | "\u0640" | "\u0E46" | "\u0EC6" |
+        "\u3005" | ["\u3031"-"\u3035"] | ["\u309D"-"\u309E"] | ["\u30FC"-"\u30FE"]
+    >
+| <NCName : (<Letter> | ["_"]) (<Letter> | <UnicodeDigit> | [".","-","_"] | <CombiningChar> | <Extender>)* >
+| <WS: ["\t", "\r", "\n", " "] >
+| <QName : (<NCName> ":")? <NCName> >
+}
+
+<DEFAULT, IN_SCHEME>
+TOKEN :
+{
+  <LBRACE: "(" >
+| <RBRACE: ")" >
+}
+
+<IN_SCHEME>
+TOKEN :
+{
+  <CIRC_LBRACE: "^(" >
+| <CIRC_RBRACE: "^)" >
+| <DOUBLE_CIRC: "^^" >
+| <NormalChar: ~["(", ")", "^"] >
+}
+
+void pointer():
+{
+}
+{
+  LOOKAHEAD(2) schemeBased() | shortHand()
+}
+
+void shortHand():
+{
+  Token x;
+}
+{
+  x = <NCName>
+  {
+    xpointer.addPart(new ShorthandPart(x.image));
+  }
+}
+
+void schemeBased():
+{
+}
+{
+  pointerPart() ( (<WS>)* pointerPart() )*
+}
+
+void pointerPart():
+{
+  Token x;
+  String schemeName;
+  String schemeData;
+}
+{
+  (x = <NCName> | x = <QName>)
+    <LBRACE>
+    {
+        // when going inside the scheme data, swith to a different lexical state
+        token_source.SwitchTo(IN_SCHEME);
+
+        // store the scheme name
+        schemeName = x.image;
+    }
+  schemeData = schemeData()
+    <RBRACE>
+    {
+        // when going outside the scheme data, swith back to the default lexical state
+        token_source.SwitchTo(DEFAULT);
+
+        // parse schemeName in prefix and localName
+        String schemeNamespace = null, schemeLocalName = null;
+        int colonPos = schemeName.indexOf(':');
+        if (colonPos != -1) {
+            String schemePrefix = schemeName.substring(0, colonPos);
+            schemeNamespace = (String) namespaces.get(schemePrefix);
+            schemeLocalName = schemeName.substring(colonPos + 1);
+        } else {
+            schemeLocalName = schemeName;
+        }
+
+        // add the pointer part
+        if (schemeNamespace == null && XMLNS.equals(schemeLocalName)) {
+            int eqPos = schemeData.indexOf("=");
+            if (eqPos == -1) {
+                throw new ParseException("xmlns scheme data should contain an equals sign");
+            }
+
+            // Note: the trimming below is not entirely correct, since space is only allowed left
+            // and right of the equal sign, but not at the beginning and end of the schemeData
+            String prefix = schemeData.substring(0, eqPos).trim();
+            String namespace = schemeData.substring(eqPos + 1, schemeData.length()).trim();
+            xpointer.addPart(new XmlnsPart(prefix, namespace));
+            namespaces.put(prefix, namespace);
+        } else if (schemeNamespace == null && XPOINTER.equals(schemeLocalName)) {
+            xpointer.addPart(new XPointerPart(schemeData));
+        } else {
+            xpointer.addPart(new UnsupportedPart(schemeName));
+        }
+    }
+}
+
+String schemeData():
+{
+    String temp;
+    StringBuilder schemeData = new StringBuilder();
+}
+{
+  (
+   ( temp = escapedData() { schemeData.append(temp); } )*
+  )
+  {
+    return unescape(schemeData.toString());
+  }
+}
+
+String escapedData():
+{
+    Token x;
+    String temp;
+    StringBuilder data = new StringBuilder();
+}
+{
+  // The reason for not making a token out of this all is that tokens cannot contain recursive definitions
+  (
+   x = <NormalChar> { data.append(x.image); }
+   | x = <CIRC_LBRACE> { data.append(x.image); }
+   | x = <CIRC_RBRACE> { data.append(x.image); }
+   | x = <DOUBLE_CIRC> { data.append(x.image); }
+   | x = <LBRACE> { data.append(x.image); }
+     temp = schemeData() { data.append(temp); }
+     x = <RBRACE> { data.append(x.image); }
+  )
+  {
+        return data.toString();
+  }
+}