Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/xml/LoggingEntityResolver.java URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/xml/LoggingEntityResolver.java?rev=330548&view=auto ============================================================================== --- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/xml/LoggingEntityResolver.java (added) +++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/xml/LoggingEntityResolver.java Thu Nov 3 05:41:06 2005 @@ -0,0 +1,55 @@ +/* + * 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.apache.avalon.framework.logger.AbstractLogEnabled; +import org.xml.sax.EntityResolver; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +import java.io.IOException; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +/** + * Logging entity resolver to assist in caching. + * + * @author Donald Ball + * @version CVS $Id: LoggingEntityResolver.java 30941 2004-07-29 19:56:58Z vgritsenko $ + */ +public class LoggingEntityResolver extends AbstractLogEnabled implements EntityResolver { + + protected EntityResolver resolver; + protected Set dependencies; + + public LoggingEntityResolver(EntityResolver resolver) { + this.resolver = resolver; + dependencies = new HashSet(); + } + + public InputSource resolveEntity(String public_id, String system_id) throws SAXException,IOException { + InputSource input_source = resolver.resolveEntity(public_id,system_id); + dependencies.add(input_source); + getLogger().debug("Dependency: "+input_source.getSystemId()); + return input_source; + } + + public Set getDependencies() { + return Collections.unmodifiableSet(dependencies); + } + +} Propchange: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/xml/LoggingEntityResolver.java ------------------------------------------------------------------------------ svn:eol-style = native Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/xml/NamespacesTable.java URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/xml/NamespacesTable.java?rev=330548&view=auto ============================================================================== --- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/xml/NamespacesTable.java (added) +++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/xml/NamespacesTable.java Thu Nov 3 05:41:06 2005 @@ -0,0 +1,534 @@ +/* + * 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; + +/** + * Keeps track of namespaces declarations and resolve namespaces names. + *

+ * 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: + *

+ *   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. + *

+ * When using {@link #enterScope()}/{@link #leaveScope()}, this method does nothing and always + * returns 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. + *

+ * Typically called in a SAX handler before sending a 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. + *

+ * Typically called in a SAX handler before sending a 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()}. + *

+ * Typically called in a SAX handler after sending a 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()}. + *

+ * Typically called in a SAX handler after sending a 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. + * + *

+ * 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 (xmlns:%@$#^@#="%@$#^@#"). + * + *

+ * 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 + * 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. + * + *

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.

+ * + * @version CVS $Id: SaxBuffer.java 54075 2004-10-08 13:02:12Z vgritsenko $ + */ +public class SaxBuffer extends AbstractSAXFragment + implements XMLConsumer, Recyclable, Serializable { + + /** + * Stores list of {@link SaxBit} objects. + */ + protected List saxbits; + + /** + * Creates empty SaxBuffer + */ + public SaxBuffer() { + this.saxbits = new ArrayList(); + } + + /** + * Creates SaxBuffer based on the provided bits list. + */ + public SaxBuffer(List bits) { + this.saxbits = bits; + } + + /** + * Creates copy of another SaxBuffer + */ + public SaxBuffer(SaxBuffer saxBuffer) { + this.saxbits = new ArrayList(saxBuffer.saxbits); + } + + // + // ContentHandler Interface + // + + public void skippedEntity(String name) throws SAXException { + saxbits.add(new SkippedEntity(name)); + } + + public void setDocumentLocator(Locator locator) { + // Don't record this event + } + + public void ignorableWhitespace(char ch[], int start, int length) throws SAXException { + saxbits.add(new IgnorableWhitespace(ch, start, length)); + } + + public void processingInstruction(String target, String data) throws SAXException { + saxbits.add(new PI(target, data)); + } + + public void startDocument() throws SAXException { + saxbits.add(StartDocument.SINGLETON); + } + + public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException { + saxbits.add(new StartElement(namespaceURI, localName, qName, atts)); + } + + public void endPrefixMapping(String prefix) throws SAXException { + saxbits.add(new EndPrefixMapping(prefix)); + } + + public void characters(char ch[], int start, int length) throws SAXException { + saxbits.add(new Characters(ch, start, length)); + } + + public void endElement(String namespaceURI, String localName, String qName) throws SAXException { + saxbits.add(new EndElement(namespaceURI, localName, qName)); + } + + public void endDocument() throws SAXException { + saxbits.add(EndDocument.SINGLETON); + } + + public void startPrefixMapping(String prefix, String uri) throws SAXException { + saxbits.add(new StartPrefixMapping(prefix, uri)); + } + + // + // LexicalHandler Interface + // + + public void endCDATA() throws SAXException { + saxbits.add(EndCDATA.SINGLETON); + } + + public void comment(char ch[], int start, int length) throws SAXException { + saxbits.add(new Comment(ch, start, length)); + } + + public void startEntity(String name) throws SAXException { + saxbits.add(new StartEntity(name)); + } + + public void endDTD() throws SAXException { + saxbits.add(EndDTD.SINGLETON); + } + + public void startDTD(String name, String publicId, String systemId) throws SAXException { + saxbits.add(new StartDTD(name, publicId, systemId)); + } + + public void startCDATA() throws SAXException { + saxbits.add(StartCDATA.SINGLETON); + } + + public void endEntity(String name) throws SAXException { + saxbits.add(new EndEntity(name)); + } + + // + // Public Methods + // + + /** + * Add a bit containing XMLizable object + */ + public void xmlizable(XMLizable xml) { + saxbits.add(new XMLizableBit(xml)); + } + + /** + * @return true if buffer is empty + */ + public boolean isEmpty() { + return saxbits.isEmpty(); + } + + /** + * @return unmodifiable list of SAX bits + */ + public List getBits() { + return Collections.unmodifiableList(saxbits); + } + + /** + * Stream this buffer into the provided content handler. + * If contentHandler object implements LexicalHandler, it will get lexical + * events as well. + */ + public void toSAX(ContentHandler contentHandler) throws SAXException { + for (Iterator i = saxbits.iterator(); i.hasNext();) { + SaxBit saxbit = (SaxBit)i.next(); + saxbit.send(contentHandler); + } + } + + /** + * @return String value of the buffer + */ + public String toString() { + // NOTE: This method is used in i18n XML bundle implementation + final StringBuffer value = new StringBuffer(); + for (Iterator i = saxbits.iterator(); i.hasNext();) { + final SaxBit saxbit = (SaxBit) i.next(); + if (saxbit instanceof Characters) { + ((Characters) saxbit).toString(value); + } + } + + return value.toString(); + } + + /** + * Clear this buffer + */ + public void recycle() { + saxbits.clear(); + } + + /** + * Dump buffer contents into the provided writer. + */ + public void dump(Writer writer) throws IOException { + Iterator i = saxbits.iterator(); + while (i.hasNext()) { + final SaxBit saxbit = (SaxBit) i.next(); + saxbit.dump(writer); + } + writer.flush(); + } + + // + // Implementation Methods + // + + /** + * Adds a SaxBit to the bits list + */ + protected final void addBit(SaxBit bit) { + saxbits.add(bit); + } + + /** + * Iterates through the bits list + */ + protected final Iterator bits() { + return saxbits.iterator(); + } + + /** + * SaxBit is a representation of the SAX event. Every SaxBit is immutable object. + */ + interface SaxBit { + public void send(ContentHandler contentHandler) throws SAXException; + public void dump(Writer writer) throws IOException; + } + + public final static class StartDocument implements SaxBit, Serializable { + public static final StartDocument SINGLETON = new StartDocument(); + + public void send(ContentHandler contentHandler) throws SAXException { + contentHandler.startDocument(); + } + + public void dump(Writer writer) throws IOException { + writer.write("[StartDocument]\n"); + } + } + + public final static class EndDocument implements SaxBit, Serializable { + public static final EndDocument SINGLETON = new EndDocument(); + + public void send(ContentHandler contentHandler) throws SAXException { + contentHandler.endDocument(); + } + + public void dump(Writer writer) throws IOException { + writer.write("[EndDocument]\n"); + } + } + + public final static class PI implements SaxBit, Serializable { + public final String target; + public final String data; + + public PI(String target, String data) { + this.target = target; + this.data = data; + } + + public void send(ContentHandler contentHandler) throws SAXException { + contentHandler.processingInstruction(target, data); + } + + public void dump(Writer writer) throws IOException { + writer.write("[ProcessingInstruction] target=" + target + ",data=" + data + "\n"); + } + } + + public final static class StartDTD implements SaxBit, Serializable { + public final String name; + public final String publicId; + public final String systemId; + + public StartDTD(String name, String publicId, String systemId) { + this.name = name; + this.publicId = publicId; + this.systemId = systemId; + } + + public void send(ContentHandler contentHandler) throws SAXException { + if (contentHandler instanceof LexicalHandler) + ((LexicalHandler)contentHandler).startDTD(name, publicId, systemId); + } + + public void dump(Writer writer) throws IOException { + writer.write("[StartDTD] name=" + name + ",publicId=" + publicId + ",systemId=" + systemId + "\n"); + } + } + + public final static class EndDTD implements SaxBit, Serializable { + public static final EndDTD SINGLETON = new EndDTD(); + + public void send(ContentHandler contentHandler) throws SAXException { + if (contentHandler instanceof LexicalHandler) + ((LexicalHandler)contentHandler).endDTD(); + } + + public void dump(Writer writer) throws IOException { + writer.write("[EndDTD]\n"); + } + } + + public final static class StartEntity implements SaxBit, Serializable { + public final String name; + + public StartEntity(String name) { + this.name = name; + } + + public void send(ContentHandler contentHandler) throws SAXException { + if (contentHandler instanceof LexicalHandler) + ((LexicalHandler)contentHandler).startEntity(name); + } + + public void dump(Writer writer) throws IOException { + writer.write("[StartEntity] name=" + name + "\n"); + } + } + + public final static class EndEntity implements SaxBit, Serializable { + public final String name; + + public EndEntity(String name) { + this.name = name; + } + + public void send(ContentHandler contentHandler) throws SAXException { + if (contentHandler instanceof LexicalHandler) + ((LexicalHandler)contentHandler).endEntity(name); + } + + public void dump(Writer writer) throws IOException { + writer.write("[EndEntity] name=" + name + "\n"); + } + } + + public final static class SkippedEntity implements SaxBit, Serializable { + public final String name; + + public SkippedEntity(String name) { + this.name = name; + } + + public void send(ContentHandler contentHandler) throws SAXException { + contentHandler.skippedEntity(name); + } + + public void dump(Writer writer) throws IOException { + writer.write("[SkippedEntity] name=" + name + "\n"); + } + } + + public final static class StartPrefixMapping implements SaxBit, Serializable { + public final String prefix; + public final String uri; + + public StartPrefixMapping(String prefix, String uri) { + this.prefix = prefix; + this.uri = uri; + } + + public void send(ContentHandler contentHandler) throws SAXException { + contentHandler.startPrefixMapping(prefix, uri); + } + + public void dump(Writer writer) throws IOException { + writer.write("[StartPrefixMapping] prefix=" + prefix + ",uri=" + uri + "\n"); + } + } + + public final static class EndPrefixMapping implements SaxBit, Serializable { + public final String prefix; + + public EndPrefixMapping(String prefix) { + this.prefix = prefix; + } + + public void send(ContentHandler contentHandler) throws SAXException { + contentHandler.endPrefixMapping(prefix); + } + + public void dump(Writer writer) throws IOException { + writer.write("[EndPrefixMapping] prefix=" + prefix + "\n"); + } + } + + public final static class StartElement implements SaxBit, Serializable { + public final String namespaceURI; + public final String localName; + public final String qName; + public final Attributes attrs; + + public StartElement(String namespaceURI, String localName, String qName, Attributes attrs) { + this.namespaceURI = namespaceURI; + this.localName = localName; + this.qName = qName; + this.attrs = new org.xml.sax.helpers.AttributesImpl(attrs); + } + + public void send(ContentHandler contentHandler) throws SAXException { + contentHandler.startElement(namespaceURI, localName, qName, attrs); + } + + public void dump(Writer writer) throws IOException { + writer.write("[StartElement] namespaceURI=" + namespaceURI + ",localName=" + localName + ",qName=" + qName + "\n"); + for (int i = 0; i < attrs.getLength(); i++) { + writer.write(" [Attribute] namespaceURI=" + attrs.getURI(i) + ",localName=" + attrs.getLocalName(i) + ",qName=" + attrs.getQName(i) + ",type=" + attrs.getType(i) + ",value=" + attrs.getValue(i) + "\n"); + } + } + } + + public final static class EndElement implements SaxBit, Serializable { + public final String namespaceURI; + public final String localName; + public final String qName; + + public EndElement(String namespaceURI, String localName, String qName) { + this.namespaceURI = namespaceURI; + this.localName = localName; + this.qName = qName; + } + + public void send(ContentHandler contentHandler) throws SAXException { + contentHandler.endElement(namespaceURI, localName, qName); + } + + public void dump(Writer writer) throws IOException { + writer.write("[EndElement] namespaceURI=" + namespaceURI + ",localName=" + localName + ",qName=" + qName + "\n"); + } + } + + public final static class Characters implements SaxBit, Serializable { + public final char[] ch; + + public Characters(char[] ch, int start, int length) { + // make a copy so that we don't hold references to a potentially large array we don't control + this.ch = new char[length]; + System.arraycopy(ch, start, this.ch, 0, length); + } + + public void send(ContentHandler contentHandler) throws SAXException { + contentHandler.characters(ch, 0, ch.length); + } + + public void toString(StringBuffer value) { + value.append(ch); + } + + public void dump(Writer writer) throws IOException { + writer.write("[Characters] ch=" + new String(ch) + "\n"); + } + } + + public final static class Comment implements SaxBit, Serializable { + public final char[] ch; + + public Comment(char[] ch, int start, int length) { + // make a copy so that we don't hold references to a potentially large array we don't control + this.ch = new char[length]; + System.arraycopy(ch, start, this.ch, 0, length); + } + + public void send(ContentHandler contentHandler) throws SAXException { + if (contentHandler instanceof LexicalHandler) + ((LexicalHandler)contentHandler).comment(ch, 0, ch.length); + } + + public void dump(Writer writer) throws IOException { + writer.write("[Comment] ch=" + new String(ch) + "\n"); + } + } + + public final static class StartCDATA implements SaxBit, Serializable { + public static final StartCDATA SINGLETON = new StartCDATA(); + + public void send(ContentHandler contentHandler) throws SAXException { + if (contentHandler instanceof LexicalHandler) + ((LexicalHandler)contentHandler).startCDATA(); + } + + public void dump(Writer writer) throws IOException { + writer.write("[StartCDATA]\n"); + } + } + + public final static class EndCDATA implements SaxBit, Serializable { + public static final EndCDATA SINGLETON = new EndCDATA(); + + public void send(ContentHandler contentHandler) throws SAXException { + if (contentHandler instanceof LexicalHandler) + ((LexicalHandler)contentHandler).endCDATA(); + } + + public void dump(Writer writer) throws IOException { + writer.write("[EndCDATA]\n"); + } + } + + public final static class IgnorableWhitespace implements SaxBit, Serializable { + public final char[] ch; + + public IgnorableWhitespace(char[] ch, int start, int length) { + // make a copy so that we don't hold references to a potentially large array we don't control + this.ch = new char[length]; + System.arraycopy(ch, start, this.ch, 0, length); + } + + public void send(ContentHandler contentHandler) throws SAXException { + contentHandler.ignorableWhitespace(ch, 0, ch.length); + } + + public void dump(Writer writer) throws IOException { + writer.write("[IgnorableWhitespace] ch=" + new String(ch) + "\n"); + } + } + + public final static class XMLizableBit implements SaxBit, Serializable { + public final XMLizable xml; + + public XMLizableBit(XMLizable xml) { + this.xml = xml; + } + + public void send(ContentHandler contentHandler) throws SAXException { + this.xml.toSAX(new EmbeddedXMLPipe(contentHandler)); + } + + public void dump(Writer writer) throws IOException { + if (xml instanceof SaxBuffer) { + writer.write("[XMLizable] Begin nested SaxBuffer\n"); + ((SaxBuffer)xml).dump(writer); + writer.write("[XMLizable] End nested SaxBuffer\n"); + } else { + writer.write("[XMLizable] xml=" + xml + "\n"); + } + } + } +} Propchange: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/xml/SaxBuffer.java ------------------------------------------------------------------------------ svn:eol-style = native Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/xml/StringXMLizable.java URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/xml/StringXMLizable.java?rev=330548&view=auto ============================================================================== --- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/xml/StringXMLizable.java (added) +++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/xml/StringXMLizable.java Thu Nov 3 05:41:06 2005 @@ -0,0 +1,59 @@ +/* + * Copyright 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.apache.excalibur.xml.sax.XMLizable; +import org.xml.sax.ContentHandler; +import org.xml.sax.SAXException; +import org.xml.sax.InputSource; + +import javax.xml.parsers.SAXParserFactory; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.ParserConfigurationException; +import java.io.IOException; +import java.io.StringReader; + +/** + * XMLizable a String + * + * @since 2.1.7 + * @author Bruno Dumon + */ +public class StringXMLizable implements XMLizable { + private String data; + + public StringXMLizable(String data) { + this.data = data; + } + + public void toSAX(ContentHandler contentHandler) throws SAXException { + SAXParserFactory parserFactory = SAXParserFactory.newInstance(); + parserFactory.setNamespaceAware(true); + SAXParser parser = null; + try { + parser = parserFactory.newSAXParser(); + } catch (ParserConfigurationException e) { + throw new SAXException("Error creating SAX parser.", e); + } + parser.getXMLReader().setContentHandler(contentHandler); + InputSource is = new InputSource(new StringReader(data)); + try { + parser.getXMLReader().parse(is); + } catch (IOException e) { + throw new SAXException(e); + } + } +} Propchange: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/xml/StringXMLizable.java ------------------------------------------------------------------------------ svn:eol-style = native Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/xml/XMLBaseSupport.java URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/xml/XMLBaseSupport.java?rev=330548&view=auto ============================================================================== --- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/xml/XMLBaseSupport.java (added) +++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/xml/XMLBaseSupport.java Thu Nov 3 05:41:06 2005 @@ -0,0 +1,152 @@ +/* + * 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.Attributes; +import org.xml.sax.SAXException; +import org.apache.excalibur.source.SourceResolver; +import org.apache.excalibur.source.Source; +import org.apache.avalon.framework.logger.Logger; + +import java.util.Stack; +import java.util.Collections; +import java.io.IOException; + +/** + * Helper class for handling xml:base attributes. + * + *

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. + *

+ * + * @author Pierpaolo Fumagalli + * (Apache Software Foundation) + * @version CVS $Id: XMLConsumer.java 279586 2005-09-08 17:12:20Z bloritsch $ + */ +public interface XMLConsumer extends org.apache.excalibur.xml.sax.XMLConsumer { +} Propchange: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/xml/XMLConsumer.java ------------------------------------------------------------------------------ svn:eol-style = native Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/xml/XMLFragment.java URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/xml/XMLFragment.java?rev=330548&view=auto ============================================================================== --- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/xml/XMLFragment.java (added) +++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/xml/XMLFragment.java Thu Nov 3 05:41:06 2005 @@ -0,0 +1,40 @@ +/* + * 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.w3c.dom.Node; + +/** + * This interface must be implemented by classes willing + * to provide an XML representation of their current state. + * + *

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 XMLizable + * interface.

+ * + * @author Sylvain Wallez + * @author Ricardo Rocha for the original XObject class + * @version CVS $Id: XMLFragment.java 53979 2004-10-07 14:26:29Z vgritsenko $ + */ +public interface XMLFragment extends org.apache.excalibur.xml.sax.XMLizable { + + /** + * Appends children representing the object's state to the given node. + */ + void toDOM(Node node) throws Exception; +} Propchange: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/xml/XMLFragment.java ------------------------------------------------------------------------------ svn:eol-style = native Added: cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/xml/XMLMulticaster.java URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/xml/XMLMulticaster.java?rev=330548&view=auto ============================================================================== --- cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/xml/XMLMulticaster.java (added) +++ cocoon/whiteboard/maven2/cocoon-flat-layout/cocoon-core/src/main/java/org/apache/cocoon/xml/XMLMulticaster.java Thu Nov 3 05:41:06 2005 @@ -0,0 +1,163 @@ +/* + * 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.Attributes; +import org.xml.sax.ContentHandler; +import org.xml.sax.Locator; +import org.xml.sax.SAXException; +import org.xml.sax.ext.LexicalHandler; + +/** + * @author Carsten Ziegeler + * @version CVS $Id: XMLMulticaster.java 30941 2004-07-29 19:56:58Z vgritsenko $ + */ + +public final class XMLMulticaster implements XMLConsumer { + + /** + * The XMLMulticaster forwards incomming sax events to a list of + * receiving objects. + */ + private ContentHandler[] contentHandlerList; + private LexicalHandler[] lexicalHandlerList; + + /** + * Create a new XMLMulticaster with two consumers + */ + public XMLMulticaster(XMLConsumer firstConsumer, XMLConsumer secondConsumer) { + this.contentHandlerList = new ContentHandler[] {firstConsumer, secondConsumer}; + this.lexicalHandlerList = new LexicalHandler[] {firstConsumer, secondConsumer}; + } + + /** + * Create a new XMLMulticaster from two contentHandler/lexicalHandler pairs + */ + public XMLMulticaster(ContentHandler firstContentHandler, + LexicalHandler firstLexicalHandler, + ContentHandler secondContentHandler, + LexicalHandler secondLexicalHandler) { + this.contentHandlerList = new ContentHandler[] {firstContentHandler, secondContentHandler}; + this.lexicalHandlerList = new LexicalHandler[] {firstLexicalHandler, secondLexicalHandler}; + } + + public XMLMulticaster(ContentHandler[] chList, + LexicalHandler[] lhList) { + this.contentHandlerList = chList; + this.lexicalHandlerList = lhList; + } + + public void startDocument() throws SAXException { + for(int i=0; i + * Because an XMLPipe is both a source and a sink for SAX events, the basic + * contract that you need to worry about is that you must forward any SAX + * events on that you are not intercepting and transforming. As you receive + * your startDocument event, pass it on to the XMLConsumer you received as part + * of the XMLProducer side of the contract. An example ASCII art will help + * make it a bit more clear: + *

+ *
+ * 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 configured XMLConsumer. + *

+ * 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. + *

+ * + * @author Pierpaolo Fumagalli + * (Apache Software Foundation) + * @version CVS $Id: XMLProducer.java 279586 2005-09-08 17:12:20Z bloritsch $ + */ +public interface XMLProducer { + + /** + * Set the 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