From cvs-return-25121-apmail-cocoon-cvs-archive=cocoon.apache.org@cocoon.apache.org Thu Nov 03 14:48:13 2005
Return-Path:
+ * This class also provides a very convenient and safe way of handling
+ * namespace declarations in SAX pipes. It also allows to filter duplicate namespace
+ * declarations that too often clutter up XML documents that went through
+ * several transformations, and avoid useless namespace declarations that aren't followed
+ * by element events.
+ *
+ * Usage example in a SAX pipe:
+ *
+ * When using {@link #enterScope()}/{@link #leaveScope()}, this method does nothing and always
+ * returns
+ * Typically called in a SAX handler before sending a
+ * Typically called in a SAX handler before sending a
+ * Typically called in a SAX handler after sending a
+ * Typically called in a SAX handler after sending a
+ * It handles both duplicate declarations (i.e. a namespace already declared by a
+ * parent element) and empty namespaces scopes (i.e. start/stopPrefixMapping with
+ * no element inbetween) that can be produced by some components (e.g. JXTG or
+ * BrowserUpdateTransformer). Such empty scopes confuse the Xalan serializer which
+ * then produces weird namespace declarations (
+ * This is a the most simple use of {@link NamespacesTable}.
+ *
+ * @version CVS $Id: RedundantNamespacesFilter.java 231484 2005-08-11 17:18:02Z sylvain $
+ */
+public class RedundantNamespacesFilter extends AbstractXMLPipe {
+
+ /** Layered storage for all namespace declarations */
+ private NamespacesTable ns = new NamespacesTable();
+
+ /**
+ * No-arg constructor. Requires an explicit call to
+ * Compared to {@link org.apache.cocoon.components.sax.XMLByteStreamCompiler},
+ * this class is many times faster at sending out the recorded SAX events since
+ * it doesn't need to convert between byte and char representations etc.
+ * On the other hand, its data structure is more complex then a simple byte array,
+ * making XMLByteStreamCompiler better in case the recorded SAX should be stored long-term.
+ *
+ * Use this class if you need to frequently generate smaller amounts of SAX events,
+ * or replay a set of recorded start events immediately. Both {@link ContentHandler} and {@link LexicalHandler} are supported, the only
+ * exception is that the setDocumentLocator event is not recorded. Usage:
+ * External entities are not yet taken into account when determing the current base.
+ */
+public class XMLBaseSupport {
+ public static final String XMLBASE_NAMESPACE_URI = "http://www.w3.org/XML/1998/namespace";
+ public static final String XMLBASE_ATTRIBUTE = "base";
+
+ /** Increased on each startElement, decreased on each endElement. */
+ private int level = 0;
+ /**
+ * The stack contains an instance of {@link BaseInfo} for each XML element
+ * that contained an xml:base attribute (not for the other elements).
+ */
+ private Stack bases = new Stack();
+ private SourceResolver resolver;
+ private Logger logger;
+
+ public XMLBaseSupport(SourceResolver resolver, Logger logger) {
+ this.resolver = resolver;
+ this.logger = logger;
+ }
+
+ public void setDocumentLocation(String loc) throws SAXException {
+ // -2 is used as level to avoid this BaseInfo to be ever popped of the stack
+ bases.push(new BaseInfo(loc, -2));
+ }
+
+ public void startElement(String namespaceURI, String localName, String qName, Attributes attrs) throws SAXException {
+ level++;
+ String base = attrs.getValue(XMLBASE_NAMESPACE_URI, XMLBASE_ATTRIBUTE);
+ if (base != null) {
+ Source baseSource = null;
+ String baseUrl;
+ try {
+ baseSource = resolve(getCurrentBase(), base);
+ baseUrl = baseSource.getURI();
+ } finally {
+ if (baseSource != null) {
+ resolver.release(baseSource);
+ }
+ }
+ bases.push(new BaseInfo(baseUrl, level));
+ }
+ }
+
+ public void endElement(String namespaceURI, String localName, String qName) {
+ if (getCurrentBaseLevel() == level)
+ bases.pop();
+ level--;
+ }
+
+ /**
+ * Warning: do not forget to release the source returned by this method.
+ */
+ private Source resolve(String baseURI, String location) throws SAXException {
+ try {
+ Source source;
+ if (baseURI != null) {
+ source = resolver.resolveURI(location, baseURI, Collections.EMPTY_MAP);
+ } else {
+ source = resolver.resolveURI(location);
+ }
+ if (logger.isDebugEnabled()) {
+ logger.debug("XMLBaseSupport: resolved location " + location +
+ " against base URI " + baseURI + " to " + source.getURI());
+ }
+ return source;
+ } catch (IOException e) {
+ throw new SAXException("XMLBaseSupport: problem resolving uri.", e);
+ }
+ }
+
+ /**
+ * Makes the given path absolute based on the current base URL. Do not forget to release
+ * the returned source object!
+ * @param spec any URL (relative or absolute, containing a scheme or not)
+ */
+ public Source makeAbsolute(String spec) throws SAXException {
+ return resolve(getCurrentBase(), spec);
+ }
+
+ private String getCurrentBase() {
+ if (bases.size() > 0) {
+ BaseInfo baseInfo = (BaseInfo)bases.peek();
+ return baseInfo.getUrl();
+ }
+ return null;
+ }
+
+ private int getCurrentBaseLevel() {
+ if (bases.size() > 0) {
+ BaseInfo baseInfo = (BaseInfo)bases.peek();
+ return baseInfo.getLevel();
+ }
+ return -1;
+ }
+
+ private static final class BaseInfo {
+ private String url;
+ private int level;
+
+ public BaseInfo(String url, int level) {
+ this.url = url;
+ this.level = level;
+ }
+
+ public String getUrl() {
+ return url;
+ }
+
+ public int getLevel() {
+ return level;
+ }
+ }
+}
Propchange: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/xml/XMLBaseSupport.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/xml/XMLConsumer.java
URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/xml/XMLConsumer.java?rev=330548&view=auto
==============================================================================
--- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/xml/XMLConsumer.java (added)
+++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/xml/XMLConsumer.java Thu Nov 3 05:41:06 2005
@@ -0,0 +1,46 @@
+/*
+ * Copyright 1999-2004 The Apache Software Foundation.
+ *
+ * Licensed 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.xml;
+
+/**
+ * This interfaces identifies classes that consume XML data, receiving
+ * notification of SAX events.
+ *
+ * An XMLConsumer is also a SAX ContentHandler and a SAX LexicalHandler. That
+ * means the XMLConsumer has to respect all the contracts with the SAX
+ * interfaces. SAX stands for Serialized API for XML. A document start, and
+ * each element start must be matched by the corresponding element end or
+ * document end. So why does Cocoon use SAX instead of manipulating a DOM?
+ * For two main reasons: performance and scalability. A DOM tree is much more
+ * heavy on system memory than successive calls to an API. SAX events can be
+ * sent as soon as they are read from the originating XML, the parsing and
+ * processing can happen essentially at the same time.
+ *
+ * Most people's needs will be handled just fine with the ContentHandler
+ * interface, as that declares your namespaces. However if you need lexical
+ * support to resolve entity names and such, you need the LexicalHandler
+ * interface. The AbstractXMLConsumer base class can make implementing this
+ * interface easier so that you only need to override the events you intend to
+ * do anything with.
+ * This interface exists in both Cocoon 1 and Cocoon 2 and to ensure
+ * a minimal compatibility between the two versions. Cocoon 2 only objects can implement the SAX-only
+ * NamespacesTable namespaces = new NamespacesTable();
+ * ContentHandler nextHandler;
+ *
+ * public void startPrefixMapping(String prefix, String uri) throws SAXException {
+ * namespaces.addDeclaration(prefix, uri);
+ * }
+ *
+ * public void startElement(...) throws SAXException {
+ * // automatically start mappings for this scope
+ * namespaces.enterScope(nextHandler);
+ * nextHandler.startElement(...);
+ * }
+ *
+ * public void endElement(...) throws SAXException {
+ * nextHandler.endElement(...);
+ * // automatically end mappings for this scope
+ * namespaces.leaveScope(nextHandler);
+ * }
+ *
+ * public void endPrefixMapping(String prefix) throws SAXException {
+ * // Ignore, it is handled by leaveScope()
+ * }
+ *
+ *
+ * @version $Id: NamespacesTable.java 233343 2005-08-18 18:06:44Z sylvain $
+ */
+public class NamespacesTable {
+ /** The last namespace declaration. */
+ private Entry lastEntry;
+
+ /** The entry that start the prefix mappings for the scope that's about to be entered
+ * or was just left.
+ */
+ private Entry lastDeclaredEntry;
+
+ private boolean usesScopes = false;
+
+ /**
+ * Construct a new NamespacesTable
instance.
+ */
+ public NamespacesTable() {
+ clear();
+ }
+
+ /**
+ * Clear and reinitialize this namespace table before reuse.
+ *
+ * @since 2.1.8
+ */
+ public void clear() {
+ this.lastEntry = Entry.create("","");
+ this.addDeclaration("xml", "http://www.w3.org/XML/1998/namespace");
+ // Lock this scope
+ this.lastEntry.closedScopes = 1;
+ }
+
+ /**
+ * Declare a new namespace prefix-uri mapping.
+ *
+ * @return The newly added Declaration
.
+ */
+ public Declaration addDeclaration(String prefix, String uri) {
+ // Find a previous declaration of the same prefix
+ Entry dup = this.lastEntry;
+ while (dup != null && !dup.prefix.equals(prefix)) {
+ dup = dup.previous;
+ }
+
+ if (dup != null) {
+ if (usesScopes && dup.uri.equals(uri)) {
+ return dup;
+ }
+ dup.overriden = true;
+ }
+
+ Entry e = Entry.create(prefix, uri);
+ e.previous = this.lastEntry;
+ e.overrides = dup;
+ this.lastEntry = e;
+ // this always starts the declared prefix chain
+ this.lastDeclaredEntry = e;
+ return e;
+ }
+
+ /**
+ * Undeclare a namespace prefix-uri mapping. If the prefix was previously declared
+ * mapping another URI, its value is restored.
+ * null
, as declaration removal is handled in {@link #leaveScope()}.
+ *
+ * @return the removed Declaration
or null.
+ */
+ public Declaration removeDeclaration(String prefix) {
+ if (usesScopes) {
+ // Automatically handled in leaveScope
+ return null; // or throw and IllegalStateException if enterScope(handler) was used?
+ }
+
+ Entry current = this.lastEntry;
+ Entry afterCurrent = null;
+ while(current != null) {
+ if (current.closedScopes > 0) {
+ // Don't undeclare mappings not declared in this scope
+ return null;
+ }
+
+ if (current.prefix.equals(prefix)) {
+ // Got it
+ // Remove it from the chain
+ if (afterCurrent != null) {
+ afterCurrent.previous = current.previous;
+ }
+ // And report closed scopes on the previous entry
+ current.previous.closedScopes += current.closedScopes;
+ Entry overrides = current.overrides;
+ if (overrides != null) {
+ // No more overriden
+ overrides.overriden = false;
+ }
+
+ if (this.lastDeclaredEntry == current) {
+ if (current.previous.closedScopes == 0) {
+ this.lastDeclaredEntry = current.previous;
+ } else {
+ this.lastDeclaredEntry = null;
+ }
+ }
+
+ if (this.lastEntry == current) {
+ this.lastEntry = current.previous;
+ }
+
+ return current;
+ }
+
+ afterCurrent = current;
+ current = current.previous;
+ }
+
+ // Not found
+ return null;
+ }
+
+ /**
+ * Enter a new scope. This starts a new, empty list of declarations for the new scope.
+ * startElement()
+ * event.
+ *
+ * @since 2.1.8
+ */
+ public void enterScope() {
+ this.usesScopes = true;
+ this.lastEntry.closedScopes++;
+ this.lastDeclaredEntry = null;
+ }
+
+ /**
+ * Start all declared mappings of the current scope and enter a new scope. This starts a new,
+ * empty list of declarations for the new scope.
+ * startElement()
+ * event.
+ *
+ * @param handler the handler that will receive startPrefixMapping events.
+ * @throws SAXException
+ * @since 2.1.8
+ */
+ public void enterScope(ContentHandler handler) throws SAXException {
+ this.usesScopes = true;
+ Entry current = this.lastEntry;
+ while (current != null && current.closedScopes == 0) {
+ handler.startPrefixMapping(current.prefix, current.uri);
+ current = current.previous;
+ }
+ this.lastEntry.closedScopes++;
+ this.lastDeclaredEntry = null;
+ }
+
+ /**
+ * Leave a scope. The namespace declarations that occured before the corresponding
+ * enterScope()
are no more visible using the resolution methods, but
+ * still available using {@link #getCurrentScopeDeclarations()} until the next call
+ * to {@link #addDeclaration(String, String)} or {@link #enterScope()}.
+ * endElement()
+ * event.
+ *
+ * @since 2.1.8
+ */
+ public void leaveScope() {
+ Entry current = this.lastEntry;
+
+ // Purge declarations that were added but not included in a scope
+ while (current.closedScopes == 0) {
+ current = current.previous;
+ }
+
+ current.closedScopes--;
+
+ if (current.closedScopes == 0) {
+ this.lastDeclaredEntry = current;
+ } else {
+ // More than one scope closed here: no local declarations
+ this.lastDeclaredEntry = null;
+ }
+
+ while (current != null && current.closedScopes == 0) {
+ Entry overrides = current.overrides;
+ if (overrides != null) {
+ // No more overriden
+ overrides.overriden = false;
+ }
+ current = current.previous;
+ }
+ this.lastEntry = current;
+ }
+
+ /**
+ * Leave a scope. The namespace declarations that occured before the corresponding
+ * enterScope()
are no more visible using the resolution methods, but
+ * still available using {@link #getCurrentScopeDeclarations()} until the next call
+ * to {@link #addDeclaration(String, String)} or {@link #enterScope()}.
+ * endElement()
+ * event.
+ *
+ * @param handler the handler that will receive endPrefixMapping events.
+ * @throws SAXException
+ * @since 2.1.8
+ */
+ public void leaveScope(ContentHandler handler) throws SAXException {
+ Entry current = this.lastEntry;
+
+ // Purge declarations that were added but not included in a scope
+ while (current.closedScopes == 0) {
+ current = current.previous;
+ }
+
+ current.closedScopes--;
+
+ if (current.closedScopes == 0) {
+ this.lastDeclaredEntry = current;
+ } else {
+ // More than one scope closed here: no local declarations
+ this.lastDeclaredEntry = null;
+ }
+
+ while (current != null && current.closedScopes == 0) {
+ handler.endPrefixMapping(current.prefix);
+ Entry overrides = current.overrides;
+ if (overrides != null) {
+ // No more overriden
+ overrides.overriden = false;
+ }
+ current = current.previous;
+ }
+
+ this.lastEntry = current;
+ }
+
+ private static final Declaration[] NO_DECLS = new Declaration[0];
+
+ /**
+ * Get the declarations that were declared within the current scope.
+ *
+ * @return the declarations (possibly empty, but never null)
+ * @since 2.1.8
+ */
+ public Declaration[] getCurrentScopeDeclarations() {
+ int count = 0;
+ Entry current = this.lastDeclaredEntry;
+ while (current != null && current.closedScopes == 0) {
+ count++;
+ current = current.previous;
+ }
+
+ if (count == 0) return NO_DECLS;
+
+ Declaration[] decls = new Declaration[count];
+ count = 0;
+ current = this.lastDeclaredEntry;
+ while (current != null && current.closedScopes == 0) {
+ decls[count++] = current;
+ current = current.previous;
+ }
+ return decls;
+ }
+
+ /**
+ * Return the URI associated with the given prefix or null if the
+ * prefix was not mapped.
+ */
+ public String getUri(String prefix) {
+ Entry current = this.lastEntry;
+ while (current != null) {
+ if (current.prefix.equals(prefix)) {
+ return current.uri;
+ }
+ current = current.previous;
+ }
+
+ // Not found
+ return null;
+ }
+
+ /**
+ * Return an array with all prefixes currently mapped to the specified URI.
+ *
+ * The array length might be zero if no prefixes are associated with
+ * the specified uri.
+ *
+ * @return A non-null String
array.
+ */
+ public String[] getPrefixes(String uri) {
+
+ Entry current=this.lastEntry;
+ int count=0;
+ while (current!=null) {
+ if(!current.overriden && current.uri.equals(uri))
+ count++;
+ current=current.previous;
+ }
+ if (count==0) return(new String[0]);
+
+ String prefixes[]=new String[count];
+ count=0;
+ current = this.lastEntry;
+ while (current!=null) {
+ if(!current.overriden && current.uri.equals(uri))
+ prefixes[count++] = current.prefix;
+ current = current.previous;
+ }
+ return prefixes;
+ }
+
+
+ /**
+ * Return one of the prefixes currently mapped to the specified URI or
+ * null.
+ */
+ public String getPrefix(String uri) {
+ Entry current = this.lastEntry;
+ while (current != null) {
+ if(!current.overriden && current.uri.equals(uri))
+ return current.prefix;
+ current = current.previous;
+ }
+ return null;
+ }
+
+ /**
+ * Resolve a namespace-aware name against the current namespaces
+ * declarations.
+ *
+ * @param uri The namespace URI or null if not known.
+ * @param raw The raw (complete) name or null if not known.
+ * @param prefix The namespace prefix or null if not known.
+ * @param local The local name or null if not known.
+ * @return A non-null Name
.
+ * @exception SAXException If the name cannot be resolved.
+ */
+ public Name resolve(String uri, String raw, String prefix, String local)
+ throws SAXException {
+ if (uri==null) uri="";
+ if (raw==null) raw="";
+ if (prefix==null) prefix="";
+ if (local==null) local="";
+ // Start examining the URI
+ if (raw.length()>0) {
+ // The raw name was specified
+ int pos=raw.indexOf(':');
+ if (pos>0) {
+ // We have a namespace prefix:local separator
+ String pre=raw.substring(0,pos);
+ String loc=raw.substring(pos+1);
+ if (prefix.length()==0) prefix=pre;
+ else if (!prefix.equals(pre))
+ throw new SAXException("Raw/Prefix mismatch");
+ if (local.length()==0) local=loc;
+ else if (!local.equals(loc))
+ throw new SAXException("Raw/Local Name mismatch");
+ } else {
+ // We don't have a prefix:local separator
+ if (prefix.length()>0)
+ throw new SAXException("Raw Name/Prefix mismatch");
+ if (local.length()==0) local=raw;
+ else if (!local.equals(raw))
+ throw new SAXException("Raw Name/Local Name mismatch");
+ }
+ } else {
+ // The raw name was not specified
+ if (local.length()==0) throw new SAXException("No Raw/Local Name");
+ if (prefix.length()==0) raw=local;
+ else raw=prefix+':'+local;
+ }
+ // We have resolved and checked data between the raw, local, and
+ // prefix... We have to doublecheck the namespaces.
+ if (uri.length()>0) {
+ // We have a URI and a prefix, check them
+ if ((prefix.length()>0) && (!uri.equals(this.getUri(prefix)))) {
+ throw new SAXException("URI/Prefix mismatch [" + prefix + "," + uri + "]");
+ } else {
+ String temp=this.getPrefix(uri);
+ if (temp==null) throw new SAXException("URI not declared");
+ else if (temp.length()>0) {
+ prefix=temp;
+ raw=prefix+':'+local;
+ }
+ }
+ } else {
+ // We don't have a URI, check if we can find one from the prefix.
+ String temp=this.getUri(prefix);
+ if (temp==null) throw new SAXException("Prefix not declared");
+ else uri=temp;
+ }
+ NameImpl name=new NameImpl();
+ if (uri.length() > 0) name.uri=uri;
+ else name.uri=null;
+ name.raw=raw;
+ name.prefix=prefix;
+ name.local=local;
+ return(name);
+ }
+
+ /** The internal entry structure for this table. */
+ private static class Entry implements Declaration {
+ /** The URI string. */
+ protected String uri="";
+ /** The prefix string. */
+ protected String prefix="";
+ /** The previous declaration. */
+ protected Entry previous;
+ protected Entry overrides;
+ protected int closedScopes = 0;
+ protected boolean overriden = false;
+
+ /** Create a new namespace declaration. */
+ protected static Entry create(String prefix, String uri) {
+ // Create a new entry
+ Entry e = new Entry();
+ // Set the prefix string.
+ if (prefix != null) e.prefix=prefix;
+ // Set the uri string.
+ if (uri != null) e.uri=uri;
+ // Return the entry
+ return e;
+ }
+
+ /** Return the namespace URI. */
+ public String getUri() { return this.uri; }
+ /** Return the namespace prefix. */
+ public String getPrefix() { return this.prefix; }
+ }
+
+ /** The default namespace-aware name declaration implementation */
+ private static class NameImpl implements Name {
+ /** The namespace URI. */
+ protected String uri;
+ /** The namespace prefix. */
+ protected String prefix;
+ /** The namespace local name. */
+ protected String local;
+ /** The namespace raw name. */
+ protected String raw;
+
+ /** Return the namespace URI. */
+ public String getUri() { return this.uri; }
+ /** Return the namespace prefix. */
+ public String getPrefix() { return this.prefix; }
+ /** Return the namespace local name. */
+ public String getLocalName() { return this.local; }
+ /** Return the namespace raw name. */
+ public String getQName() { return this.raw; }
+ }
+
+ /**
+ * A namespace-aware name. (This interface is used in conjunction
+ * with NamespacesTable
).
+ */
+ public interface Name {
+ /** Return the namespace URI. */
+ String getUri();
+ /** Return the namespace prefix. */
+ String getPrefix();
+ /** Return the namespace local name. */
+ String getLocalName();
+ /** Return the namespace raw name. */
+ String getQName();
+ }
+
+ /**
+ * A namespace declaration. (This interface is used in conjunction
+ * with NamespacesTable
).
+ */
+ public interface Declaration {
+ /** Return the namespace URI. */
+ String getUri();
+ /** Return the namespace prefix. */
+ String getPrefix();
+ }
+}
Propchange: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/xml/NamespacesTable.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/xml/ParamSaxBuffer.java
URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/xml/ParamSaxBuffer.java?rev=330548&view=auto
==============================================================================
--- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/xml/ParamSaxBuffer.java (added)
+++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/xml/ParamSaxBuffer.java Thu Nov 3 05:41:06 2005
@@ -0,0 +1,206 @@
+/*
+ * Copyright 1999-2004 The Apache Software Foundation.
+ *
+ * Licensed 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.xml;
+
+import java.util.Iterator;
+import java.util.Map;
+import java.io.Writer;
+import java.io.IOException;
+
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.Attributes;
+
+/**
+ * Modification of the SAX buffer with parameterization capabilities.
+ *
+ * Any {name}
expression inside of the character events can be
+ * replaced by the content of another SaxBuffer if it is present in the map
+ * passed to the {@link #toSAX(ContentHandler, Map)} method.
+ *
+ * @author Vadim Gritsenko
+ * @version CVS $Id: ParamSaxBuffer.java 106182 2004-11-22 14:16:31Z bruno $
+ */
+public class ParamSaxBuffer extends SaxBuffer {
+
+ /**
+ * If ch (in characters()) contains an unmatched '{' then
+ * we save the chars from '{' onward in previous_ch.
+ * Next call to characters() prepends the saved chars to ch before processing
+ * (and sets previous_ch to null).
+ */
+ private char[] previous_ch = null;
+
+ /**
+ * Creates empty SaxBuffer
+ */
+ public ParamSaxBuffer() {
+ }
+
+ /**
+ * Creates copy of another SaxBuffer
+ */
+ public ParamSaxBuffer(SaxBuffer saxBuffer) {
+ super(saxBuffer);
+ }
+
+ /**
+ * Parses text and extracts {name}
parameters for later
+ * substitution.
+ */
+ public void characters(char ch[], int start, int length) throws SAXException {
+
+ if (previous_ch != null) {
+ // prepend char's from previous_ch to ch
+ char[] buf = new char[length + previous_ch.length];
+ System.arraycopy(previous_ch, 0, buf, 0, previous_ch.length);
+ System.arraycopy(ch, start, buf, previous_ch.length, length);
+ ch = buf;
+ start = 0;
+ length += previous_ch.length;
+ previous_ch = null;
+ }
+
+ final int end = start + length;
+ for (int i = start; i < end; i++) {
+ if (ch[i] == '{') {
+ // Send any collected characters so far
+ if (i > start) {
+ addBit(new Characters(ch, start, i - start));
+ }
+
+ // Find closing brace, and construct parameter name
+ StringBuffer name = new StringBuffer();
+ int j = i + 1;
+ for (; j < end; j++) {
+ if (ch[j] == '}') {
+ break;
+ }
+ name.append(ch[j]);
+ }
+ if (j == end) {
+ // '{' without a closing '}'
+ // save char's from '{' in previous_ch in case the following call to characters()
+ // provides the '}'
+ previous_ch = new char[end - i];
+ System.arraycopy(ch, i, previous_ch, 0, end - i);
+ break;
+ }
+ addBit(new Parameter(name.toString()));
+
+ // Continue processing
+ i = j;
+ start = j + 1;
+ continue;
+ }
+ }
+
+ // Send any tailing characters
+ if (start < end) {
+ addBit(new Characters(ch, start, end - start));
+ }
+ }
+
+ public void endElement(String namespaceURI, String localName, String qName) throws SAXException {
+ flushChars();
+ super.endElement(namespaceURI, localName, qName);
+ }
+
+ public void ignorableWhitespace(char ch[], int start, int length) throws SAXException {
+ flushChars();
+ super.ignorableWhitespace(ch, start, length);
+ }
+
+ public void processingInstruction(String target, String data) throws SAXException {
+ flushChars();
+ super.processingInstruction(target, data);
+ }
+
+ public void startDocument() throws SAXException {
+ flushChars();
+ super.startDocument();
+ }
+
+ public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException {
+ flushChars();
+ super.startElement(namespaceURI, localName, qName, atts);
+ }
+
+ public void endDocument() throws SAXException {
+ flushChars();
+ super.endDocument();
+ }
+
+ public void comment(char ch[], int start, int length) throws SAXException {
+ flushChars();
+ super.comment(ch, start, length);
+ }
+
+ public void endDTD() throws SAXException {
+ flushChars();
+ super.endDTD();
+ }
+
+ public void startDTD(String name, String publicId, String systemId) throws SAXException {
+ flushChars();
+ super.startDTD(name, publicId, systemId);
+ }
+
+ private void flushChars() {
+ // Handle saved chars (in case we had a '{' with no matching '}').
+ if (previous_ch != null) {
+ addBit(new Characters(previous_ch, 0, previous_ch.length));
+ previous_ch = null;
+ }
+ }
+
+ /**
+ * @param parameters map containing SaxBuffers
+ */
+ public void toSAX(ContentHandler contentHandler, Map parameters) throws SAXException {
+ for (Iterator i = bits(); i.hasNext();) {
+ SaxBit saxbit = (SaxBit)i.next();
+ if (saxbit instanceof Parameter) {
+ ((Parameter)saxbit).send(contentHandler, parameters);
+ } else {
+ saxbit.send(contentHandler);
+ }
+ }
+ }
+
+
+ final static class Parameter implements SaxBit {
+ private final String name;
+
+ public Parameter(String name) {
+ this.name = name;
+ }
+
+ public void send(ContentHandler contentHandler) {
+ }
+
+ public void send(ContentHandler contentHandler, Map parameters) throws SAXException {
+ SaxBuffer value = (SaxBuffer)parameters.get(name);
+ if (value != null) {
+ value.toSAX(contentHandler);
+ }
+ }
+
+ public void dump(Writer writer) throws IOException {
+ writer.write("[Parameter] name=" + name);
+ }
+ }
+}
Propchange: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/xml/ParamSaxBuffer.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/xml/RedundantNamespacesFilter.java
URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/xml/RedundantNamespacesFilter.java?rev=330548&view=auto
==============================================================================
--- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/xml/RedundantNamespacesFilter.java (added)
+++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/xml/RedundantNamespacesFilter.java Thu Nov 3 05:41:06 2005
@@ -0,0 +1,78 @@
+/*
+ * Copyright 1999-2005 The Apache Software Foundation.
+ *
+ * Licensed 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.xml;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+
+/**
+ * A SAX filter that strips out redundant namespace declarations.
+ *
+ * xmlns:%@$#^@#="%@$#^@#"
).
+ *
+ * setConsumer()
.
+ */
+ public RedundantNamespacesFilter() {
+ // Nothing
+ }
+
+ /**
+ * Creates a filter directly linked to its consumer
+ *
+ * @param consumer
+ * the SAX stream consumer
+ */
+ public RedundantNamespacesFilter(XMLConsumer consumer) {
+ setConsumer(consumer);
+ }
+
+ public void startPrefixMapping(String prefix, String uri) throws SAXException {
+ // Just declare it: duplicate declarations are ignorede by NamespacesTable
+ ns.addDeclaration(prefix, uri);
+ }
+
+ public void startElement(String uri, String loc, String raw, Attributes a) throws SAXException {
+ // Declare namespaces for this scope, if any
+ ns.enterScope(this.contentHandler);
+ super.startElement(uri, loc, raw, a);
+ }
+
+ public void endElement(String uri, String loc, String raw) throws SAXException {
+ super.endElement(uri, loc, raw);
+ ns.leaveScope(this.contentHandler);
+ }
+
+ public void endPrefixMapping(String prefix) throws SAXException {
+ ns.removeDeclaration(prefix);
+ }
+}
Propchange: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/xml/RedundantNamespacesFilter.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/xml/SaxBuffer.java
URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/xml/SaxBuffer.java?rev=330548&view=auto
==============================================================================
--- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/xml/SaxBuffer.java (added)
+++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/xml/SaxBuffer.java Thu Nov 3 05:41:06 2005
@@ -0,0 +1,569 @@
+/*
+ * Copyright 1999-2004 The Apache Software Foundation.
+ *
+ * Licensed 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.xml;
+
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.Locator;
+import org.xml.sax.Attributes;
+import org.xml.sax.ext.LexicalHandler;
+import org.apache.excalibur.xml.sax.XMLizable;
+import org.apache.avalon.excalibur.pool.Recyclable;
+
+import java.io.Serializable;
+import java.io.Writer;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Collections;
+
+/**
+ * A class that can record SAX events and replay them later.
+ *
+ *
+ *
+ *
+ * XMLizable
+ * interface.
+ * XMLProducer -> (XMLConsumer)XMLPipe(XMLProducer) -> XMLConsumer + *+ *
+ * A typical example would be using the FileGenerator (an XMLProducer), sending + * events to an XSLTTransformer (an XMLPipe), which then sends events to an + * HTMLSerializer (an XMLConsumer). The XSLTTransformer acts as an XMLConsumer + * to the FileGenerator, and also acts as an XMLProducer to the HTMLSerializer. + * It is still the responsibility of the XMLPipe component to ensure that the + * XML passed on to the next component is valid--provided the XML received from + * the previous component is valid. In layman's terms it means if you don't + * intend to alter the input, just pass it on. In most cases we just want to + * transform a small snippet of XML. For example, inserting a snippet of XML + * based on an embedded element in a certain namespace. Anything that doesn't + * belong to the namespace you are worried about should be passed on as is. + *
+ * + * @author Stefano Mazzocchi + * @version CVS $Id: XMLPipe.java 279586 2005-09-08 17:12:20Z bloritsch $ + */ +public interface XMLPipe extends XMLConsumer, XMLProducer {} Propchange: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/xml/XMLPipe.java ------------------------------------------------------------------------------ svn:eol-style = native Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/xml/XMLProducer.java URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/xml/XMLProducer.java?rev=330548&view=auto ============================================================================== --- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/xml/XMLProducer.java (added) +++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/xml/XMLProducer.java Thu Nov 3 05:41:06 2005 @@ -0,0 +1,53 @@ +/* + * Copyright 1999-2004 The Apache Software Foundation. + * + * Licensed 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.xml; + +/** + * This interfaces identifies classes that produce XML data, sending SAX + * events to the configuredXMLConsumer
.
+ *
+ * The XMLProducer is comprised of only one method to give the component the
+ * next element of the pipeline. Cocoon calls the setConsumer()
+ * method with the reference to the next XMLConsumer in the pipeline. The
+ * approach allows the XMLProducer to call the different SAX related methods on
+ * the XMLConsumer without knowing ahead of time what that consumer will be.
+ * The design is very simple and very powerful in that it allows Cocoon to
+ * daisy chain several components in any order and then execute the pipeline.
+ *
+ * Any producer can be paired with any consumer and we have a pipeline. The
+ * core design is very powerful and allows the end user to mix and match
+ * sitemap components as they see fit. Cocoon will always call
+ * setConsumer()
on every XMLProducer in a pipeline or it will
+ * throw an exception saying that the pipeline is invalid (i.e. there is no
+ * serializer for the pipeline). The only contract that the XMLProducer has to
+ * worry about is that it must always make calls to the XMLConsumer passed in
+ * through the setConsumer()
method.
+ *
XMLConsumer
that will receive XML data.
+ *
+ * @param consumer The XMLConsumer target for SAX events.
+ */
+ void setConsumer(XMLConsumer consumer);
+}
Propchange: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/xml/XMLProducer.java
------------------------------------------------------------------------------
svn:eol-style = native