sylvain 2003/01/06 07:24:12
Modified: . Tag: cocoon_2_0_3_branch changes.xml
src/documentation/xdocs/userdocs/serializers Tag:
cocoon_2_0_3_branch serializers.xml
ziparchive-serializer.xml
src/java/org/apache/cocoon/serialization Tag:
cocoon_2_0_3_branch ZipArchiveSerializer.java
Log:
ZipArchiveSerializer now accepts inline content for archive entries
Revision Changes Path
No revision
No revision
1.138.2.82 +5 -1 xml-cocoon2/changes.xml
Index: changes.xml
===================================================================
RCS file: /home/cvs/xml-cocoon2/changes.xml,v
retrieving revision 1.138.2.81
retrieving revision 1.138.2.82
diff -u -r1.138.2.81 -r1.138.2.82
--- changes.xml 28 Dec 2002 07:06:51 -0000 1.138.2.81
+++ changes.xml 6 Jan 2003 15:24:11 -0000 1.138.2.82
@@ -40,6 +40,10 @@
</devs>
<release version="@version@" date="@date@">
+ <action dev="SW" type="update">
+ ZipArchiveSerializer now accepts inline content for entries of the zip archive
+ and not only source URLs.
+ </action>
<action dev="BH" type="update">
Update XML Serializer, HTML Serializer , and Text Serializer documentation,
added XHTML Serializer documentation in the serializer user documentation
section.
No revision
No revision
1.1.2.3 +1 -0
xml-cocoon2/src/documentation/xdocs/userdocs/serializers/serializers.xml
Index: serializers.xml
===================================================================
RCS file:
/home/cvs/xml-cocoon2/src/documentation/xdocs/userdocs/serializers/serializers.xml,v
retrieving revision 1.1.2.2
retrieving revision 1.1.2.3
diff -u -r1.1.2.2 -r1.1.2.3
--- serializers.xml 28 Dec 2002 07:06:52 -0000 1.1.2.2
+++ serializers.xml 6 Jan 2003 15:24:12 -0000 1.1.2.3
@@ -57,6 +57,7 @@
<li><link href="svgtiff-serializer.html">SVG/TIFF Serializer</link></li>
<li><link href="vrml-serializer.html">VRML Serializer</link></li>
<li><link href="link-serializer.html">Link Serializer</link></li>
+ <li><link href="ziparchive-serializer.html">Zip archive
Serializer</link></li>
</ul>
</s1>
</body>
1.1.2.2 +29 -15
xml-cocoon2/src/documentation/xdocs/userdocs/serializers/ziparchive-serializer.xml
Index: ziparchive-serializer.xml
===================================================================
RCS file:
/home/cvs/xml-cocoon2/src/documentation/xdocs/userdocs/serializers/ziparchive-serializer.xml,v
retrieving revision 1.1.2.1
retrieving revision 1.1.2.2
diff -u -r1.1.2.1 -r1.1.2.2
--- ziparchive-serializer.xml 31 Oct 2002 16:22:49 -0000 1.1.2.1
+++ ziparchive-serializer.xml 6 Jan 2003 15:24:12 -0000 1.1.2.2
@@ -15,22 +15,36 @@
<s1 title="Zip archive Serializer">
<p>The Zip archive serializer generates a zip archive by aggregating
several sources.</p>
- <p>The input document should describe entries of the archive by
means of
- their name (which can be a path) in the archive and the source of the
entry
- contents. These are Cocoon sources, and as such can use any of the
protocols
- handled by Cocoon, including "cocoon:" to include dynamically generated
- content in the archive.
- </p>
+ <p>The input document should describe entries of the archive by means of
+ their name (which can be a path) and their content either as URLs or
+ inline data :</p>
+ <ul>
+ <li>URLs, given by the "src" attribute, are Cocoon sources and as such
+ can use any of the protocols handled by Cocoon, including "cocoon:" to
+ include dynamically generated content in the archive.</li>
+ <li>inline data is represented by an XML document that is serialized to the
+ zip entry using the serializer identified by the "serializer" attribute.</li>
+ </ul>
+ <p>
+ Example :
+ </p>
+<source>
+<zip:archive xmlns:zip="http://apache.org/cocoon/zip-archive/1.0">
+ <zip:entry name="foo.html" src="cocoon://dynFoo.html"/>
+ <zip:entry name="images/bar.jpeg" src="bar.jpeg"/>
+ <zip:entry name="index.html" serializer="html">
+ <html>
+ <head>
+ <title>Index page</title>
+ </head>
+ <body>
+ Please go <a href="foo.html">there</a>
+ </body>
+ </html>
+ </zip:entry>
+</zip:archive:zip>
+</source>
- <p>
- Example :
- </p>
- <source><![CDATA[
-<zip-archive:zip xmlns:zip-archive="http://apache.org/cocoon/zip-archive/1.0">
- <zip-archive:entry name="foo.html" src="cocoon://dynFoo.html"/>
- <zip-archive:entry name="images/bar.jpeg" src="bar.jpeg"/>
-</zip-archive:zip>
-]]></source>
<ul>
<li>Name: zip</li>
<li>Class:
org.apache.cocoon.serialization.ZipArchiveSerializer</li>
No revision
No revision
1.1.2.2 +248 -66
xml-cocoon2/src/java/org/apache/cocoon/serialization/ZipArchiveSerializer.java
Index: ZipArchiveSerializer.java
===================================================================
RCS file:
/home/cvs/xml-cocoon2/src/java/org/apache/cocoon/serialization/ZipArchiveSerializer.java,v
retrieving revision 1.1.2.1
retrieving revision 1.1.2.2
diff -u -r1.1.2.1 -r1.1.2.2
--- ZipArchiveSerializer.java 31 Oct 2002 16:22:49 -0000 1.1.2.1
+++ ZipArchiveSerializer.java 6 Jan 2003 15:24:12 -0000 1.1.2.2
@@ -51,6 +51,12 @@
package org.apache.cocoon.serialization;
+import org.apache.avalon.excalibur.pool.Recyclable;
+import org.apache.avalon.framework.component.ComponentException;
+import org.apache.avalon.framework.component.ComponentManager;
+import org.apache.avalon.framework.component.ComponentSelector;
+import org.apache.avalon.framework.component.Composable;
+
import org.apache.cocoon.caching.CacheValidity;
import org.apache.cocoon.caching.Cacheable;
import org.apache.cocoon.components.CocoonComponentManager;
@@ -59,9 +65,12 @@
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
+import org.xml.sax.helpers.NamespaceSupport;
+import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
@@ -69,148 +78,321 @@
* A serializer that builds Zip archives by aggregating several sources.
* <p>
* The input document should describe entries of the archive by means of
- * their name (which can be a path) in the archive and the source of the entry
- * contents. These are Cocoon sources, and as such can use any of the protocols
- * handled by Cocoon, including "cocoon:" to include dynamically generated
- * content in the archive.
+ * their name (which can be a path) and their content either as URLs or
+ * inline data :
+ * <ul>
+ * <li>URLs, given by the "src" attribute, are Cocoon sources and as such
+ * can use any of the protocols handled by Cocoon, including "cocoon:" to
+ * include dynamically generated content in the archive.</li>
+ * <li>inline data is represented by an XML document that is serialized to the
+ * zip entry using the serializer identified by the "serializer" attribute.</li>
+ * </ul>
* <p>
* Example :
* <pre>
* <zip:archive xmlns:zip="http://apache.org/cocoon/zip-archive/1.0">
* <zip:entry name="foo.html" src="cocoon://dynFoo.html"/>
* <zip:entry name="images/bar.jpeg" src="bar.jpeg"/>
+ * <zip:entry name="index.html" serializer="html">
+ * <html>
+ * <head>
+ * <title>Index page</title>
+ * </head>
+ * <body>
+ * Please go <a href="foo.html">there</a>
+ * </body<
+ * </html>
+ * </zip:entry>
* </zip:archive:zip>
* </pre>
*
* @author <a href="http://www.apache.org/~sylvain">Sylvain Wallez</a>
+ * @version CVS $Id$
*/
-// TODO (1) : handle more attributes on <zip> for properties of ZipOutputStream
+// TODO (1) : handle more attributes on <archive> for properties of ZipOutputStream
// such as comment or default compression method and level
// TODO (2) : handle more attributes on <entry> for properties of ZipEntry
// (compression method and level, time, comment, etc.)
-// TODO (3) : allow the entry to be inlined by using "serializer" instead of "src",
e.g. :
-// <entry name="foo.png" serializer="svg2png">
-// <svg>...</svg>
-// </entry>
-
-public class ZipArchiveSerializer extends AbstractSerializer
-{
+public class ZipArchiveSerializer extends AbstractSerializer implements Composable {
/**
* The namespace for elements handled by this serializer,
* "http://apache.org/cocoon/zip-archive/1.0".
*/
public static final String ZIP_NAMESPACE =
"http://apache.org/cocoon/zip-archive/1.0";
-
+
+ private static final int START_STATE = 0;
+ private static final int IN_ZIP_STATE = 1;
+ private static final int IN_CONTENT_STATE = 2;
+
+ /** The component manager */
+ protected ComponentManager manager;
+
+ /** The serializer component selector */
+ protected ComponentSelector selector;
+
/** The Zip stream where entries will be written */
protected ZipOutputStream zipOutput;
-
- /** Have we encountered the toplevel "zip" element ? */
- protected boolean inZip = false;
-
+
+ /** The current state */
+ protected int state = START_STATE;
+
/** The resolver to get sources */
protected SourceResolver resolver;
-
+
/** Temporary byte buffer to read source data */
protected byte[] buffer = new byte[1024];
-
+
+ /** Serializer used when in IN_CONTENT state */
+ protected Serializer serializer;
+
+ /** Current depth of the serialized content */
+ protected int contentDepth;
+
+ /** Used to collect namespaces */
+ private NamespaceSupport nsSupport = new NamespaceSupport();
+
+ /**
+ * @see
org.apache.avalon.framework.component.Composable#compose(ComponentManager)
+ */
+ public void compose(ComponentManager manager) throws ComponentException {
+ this.manager = manager;
+ }
+
/**
* Always return "application/x-zip" which is the default for Zip archives.
*/
- public String getMimeType()
- {
+ public String getMimeType() {
return "application/x-zip";
}
/**
* @see org.xml.sax.ContentHandler#startDocument()
*/
- public void startDocument() throws SAXException
- {
+ public void startDocument() throws SAXException {
+ this.state = START_STATE;
this.zipOutput = new ZipOutputStream(this.output);
- this.inZip = false;
this.resolver = CocoonComponentManager.getCurrentEnvironment();
}
/**
- * @see org.xml.sax.ContentHandler#startElement(String, String, String,
Attributes)
+ * Begin the scope of a prefix-URI Namespace mapping.
+ *
+ * @param prefix The Namespace prefix being declared.
+ * @param uri The Namespace URI the prefix is mapped to.
*/
- public void startElement(String namespaceURI, String localName, String qName,
Attributes atts) throws SAXException
- {
- if (!inZip) {
- // expecting "zip" as the first element
- if (namespaceURI.equals(ZIP_NAMESPACE) && localName.equals("archive")) {
- this.inZip = true;
- } else {
- throw new SAXException("Expecting 'archive' root element (got '" +
localName + "')");
- }
+ public void startPrefixMapping(String prefix, String uri) throws SAXException {
+ if (state == IN_CONTENT_STATE) {
+ // Pass to the serializer
+ super.startPrefixMapping(prefix, uri);
+
} else {
- // expecting "entry" element
- if (namespaceURI.equals(ZIP_NAMESPACE) && localName.equals("entry")) {
- // Get the source
- addEntry(atts);
- } else {
- throw new SAXException("Expecting 'entry' element (got '" +
localName + "')");
+ // Register it if it's not our own namespace (useless to content)
+ if (!uri.equals(ZIP_NAMESPACE)) {
+ this.nsSupport.declarePrefix(prefix, uri);
}
}
}
+ // Note : no need to implement endPrefixMapping() as we just need to pass it
through if there
+ // is a serializer, which is what the superclass does.
+
+ /**
+ * @see org.xml.sax.ContentHandler#startElement(String, String, String,
Attributes)
+ */
+ public void startElement(String namespaceURI, String localName, String qName,
Attributes atts)
+ throws SAXException {
+ switch (state) {
+ case START_STATE :
+ // expecting "zip" as the first element
+ if (namespaceURI.equals(ZIP_NAMESPACE) &&
localName.equals("archive")) {
+ this.nsSupport.pushContext();
+ this.state = IN_ZIP_STATE;
+ } else {
+ throw new SAXException(
+ "Expecting 'archive' root element (got '" + localName +
"')");
+ }
+ break;
+
+ case IN_ZIP_STATE :
+ // expecting "entry" element
+ if (namespaceURI.equals(ZIP_NAMESPACE) &&
localName.equals("entry")) {
+ this.nsSupport.pushContext();
+ // Get the source
+ addEntry(atts);
+ } else {
+ throw new SAXException("Expecting 'entry' element (got '" +
localName + "')");
+ }
+ break;
+
+ case IN_CONTENT_STATE :
+ this.contentDepth++;
+ super.startElement(namespaceURI, localName, qName, atts);
+ break;
+ }
+ }
+
+ /**
+ * @see org.xml.sax.ContentHandler#characters(char[], int, int)
+ */
+ public void characters(char[] buffer, int offset, int length) throws
SAXException {
+ // Propagate text to the serializer only if we have encountered the
content's top-level
+ // element. Otherwhise, the serializer may be confused by some characters
occuring between
+ // startDocument() and the first startElement() (e.g. Batik fails hard in
that case)
+ if (this.state == IN_CONTENT_STATE && this.contentDepth > 0) {
+ super.characters(buffer, offset, length);
+ }
+ }
+
/**
* Add an entry in the archive.
* @param atts the attributes that describe the entry
*/
- protected void addEntry(Attributes atts) throws SAXException
- {
+ protected void addEntry(Attributes atts) throws SAXException {
String name = atts.getValue("name");
if (name == null) {
throw new SAXException("No name given to the Zip entry");
}
-
+
String src = atts.getValue("src");
- if (src == null) {
- throw new SAXException("No source given for the Zip entry");
+ String serializerType = atts.getValue("serializer");
+
+ if (src == null && serializerType == null) {
+ throw new SAXException(
+ "No source nor serializer given for the Zip entry '" + name + "'");
+ }
+
+ if (src != null && serializerType != null) {
+ throw new SAXException(
+ "Cannot specify both 'src' and 'serializer' on a Zip entry '" +
name + "'");
}
-
+
try {
// Create a new Zip entry
ZipEntry entry = new ZipEntry(name);
this.zipOutput.putNextEntry(entry);
-
- // Get the source and its data
- Source source = resolver.resolve(src);
- InputStream sourceInput = source.getInputStream();
-
- // Copy the source to the zip
- int len;
- while ((len = sourceInput.read(this.buffer)) > 0) {
- this.zipOutput.write(this.buffer, 0, len);
+
+ if (src != null) {
+ // Get the source and its data
+ Source source = resolver.resolve(src);
+ InputStream sourceInput = source.getInputStream();
+
+ // Copy the source to the zip
+ int len;
+ while ((len = sourceInput.read(this.buffer)) > 0) {
+ this.zipOutput.write(this.buffer, 0, len);
+ }
+
+ // and close the entry
+ this.zipOutput.closeEntry();
+
+ } else {
+ // Serialize content
+ if (this.selector == null) {
+ this.selector =
+ (ComponentSelector) this.manager.lookup(Serializer.ROLE +
"Selector");
+ }
+
+ // Get the serializer
+ this.serializer = (Serializer) this.selector.select(serializerType);
+
+ // Direct its output to the zip file, filtering calls to close()
+ // (we don't want the archive to be closed by the serializer)
+ this.serializer.setOutputStream(new
FilterOutputStream(this.zipOutput) {
+ public void close() { /*nothing*/
+ }
+ });
+
+ // Set it as the current XMLConsumer
+ this.setConsumer(serializer);
+
+ // start its document
+ this.serializer.startDocument();
+
+ // and give it any namespaces already declared
+ Enumeration prefixes = this.nsSupport.getPrefixes();
+ while (prefixes.hasMoreElements()) {
+
+ String prefix = (String) prefixes.nextElement();
+ super.startPrefixMapping(prefix, this.nsSupport.getURI(prefix));
+ }
+
+ this.state = IN_CONTENT_STATE;
+ this.contentDepth = 0;
}
-
- // and close the entry
- this.zipOutput.closeEntry();
-
- } catch(RuntimeException re) {
+
+ } catch (RuntimeException re) {
throw re;
- } catch(SAXException se) {
+ } catch (SAXException se) {
throw se;
- } catch(Exception e) {
+ } catch (Exception e) {
throw new SAXException(e);
}
}
/**
+ * @see org.xml.sax.ContentHandler#endElement(String, String, String)
+ */
+ public void endElement(String namespaceURI, String localName, String qName)
+ throws SAXException {
+ if (state == IN_CONTENT_STATE) {
+ super.endElement(namespaceURI, localName, qName);
+ this.contentDepth--;
+
+ if (this.contentDepth == 0) {
+ // End of this entry
+
+ // close all declared namespaces.
+ Enumeration prefixes = this.nsSupport.getPrefixes();
+ while (prefixes.hasMoreElements()) {
+ String prefix = (String) prefixes.nextElement();
+ super.endPrefixMapping(prefix);
+ }
+
+ super.endDocument();
+
+ try {
+ this.zipOutput.closeEntry();
+ } catch (IOException ioe) {
+ throw new SAXException(ioe);
+ }
+
+ super.setConsumer(null);
+ this.selector.release(this.serializer);
+ this.serializer = null;
+
+ // Go back to listening for entries
+ this.state = IN_ZIP_STATE;
+ }
+ } else {
+ this.nsSupport.popContext();
+ }
+ }
+
+ /**
* @see org.xml.sax.ContentHandler#endDocument()
*/
- public void endDocument() throws SAXException
- {
+ public void endDocument() throws SAXException {
try {
// Close the zip archive
this.zipOutput.finish();
-
- } catch(IOException ioe) {
+
+ } catch (IOException ioe) {
throw new SAXException(ioe);
}
+ }
+ /**
+ * @see org.apache.avalon.excalibur.pool.Recyclable#recycle()
+ */
+ public void recycle() {
+ if (this.serializer != null) {
+ this.selector.release(this.serializer);
+ }
+ if (this.selector != null) {
+ this.manager.release(this.selector);
+ }
+ super.recycle();
}
}
----------------------------------------------------------------------
In case of troubles, e-mail: [EMAIL PROTECTED]
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]