sylvain     2003/01/06 07:21:24

  Modified:    .        changes.xml
               src/documentation/xdocs/userdocs/serializers serializers.xml
                        ziparchive-serializer.xml
               src/java/org/apache/cocoon/serialization
                        ZipArchiveSerializer.java
  Log:
  ZipArchiveSerializer now accepts inline content for archive entries
  
  Revision  Changes    Path
  1.329     +5 -1      xml-cocoon2/changes.xml
  
  Index: changes.xml
  ===================================================================
  RCS file: /home/cvs/xml-cocoon2/changes.xml,v
  retrieving revision 1.328
  retrieving revision 1.329
  diff -u -r1.328 -r1.329
  --- changes.xml       6 Jan 2003 06:10:24 -0000       1.328
  +++ changes.xml       6 Jan 2003 15:21:24 -0000       1.329
  @@ -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="TC" type="update">    
       ImageReader extends now ResourceReader and therefor the
       "expire-time" parameter is now "expires". Also removed the
  
  
  
  1.6       +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.5
  retrieving revision 1.6
  diff -u -r1.5 -r1.6
  --- serializers.xml   25 Dec 2002 07:05:55 -0000      1.5
  +++ serializers.xml   6 Jan 2003 15:21:24 -0000       1.6
  @@ -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.3       +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.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- ziparchive-serializer.xml 31 Oct 2002 16:48:55 -0000      1.2
  +++ ziparchive-serializer.xml 6 Jan 2003 15:21:24 -0000       1.3
  @@ -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>
  +&lt;zip:archive xmlns:zip="http://apache.org/cocoon/zip-archive/1.0"&gt;
  +  &lt;zip:entry name="foo.html" src="cocoon://dynFoo.html"/&gt;
  +  &lt;zip:entry name="images/bar.jpeg" src="bar.jpeg"/&gt;
  +  &lt;zip:entry name="index.html" serializer="html"&gt;
  +    &lt;html&gt;
  +      &lt;head&gt;
  +        &lt;title&gt;Index page&lt;/title&gt;
  +      &lt;/head&gt;
  +      &lt;body&gt;
  +        Please go &lt;a href="foo.html"&gt;there&lt;/a&gt;
  +      &lt;/body&gt;
  +    &lt;/html&gt;
  +  &lt;/zip:entry&gt;
  +&lt;/zip:archive:zip&gt;
  +</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>
  
  
  
  1.4       +235 -73   
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.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- ZipArchiveSerializer.java 3 Dec 2002 10:20:01 -0000       1.3
  +++ ZipArchiveSerializer.java 6 Jan 2003 15:21:24 -0000       1.4
  @@ -51,16 +51,26 @@
   
   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.excalibur.source.Source;
  -import org.apache.excalibur.source.SourceResolver;
  +
  +import org.apache.cocoon.caching.CacheValidity;
  +import org.apache.cocoon.caching.Cacheable;
  +import org.apache.cocoon.components.CocoonComponentManager;
  +import org.apache.cocoon.environment.Source;
  +import org.apache.cocoon.environment.SourceResolver;
  +
   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;
   
  @@ -68,53 +78,88 @@
    * 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>
    *   &lt;zip:archive xmlns:zip="http://apache.org/cocoon/zip-archive/1.0"&gt;
    *     &lt;zip:entry name="foo.html" src="cocoon://dynFoo.html"/&gt;
    *     &lt;zip:entry name="images/bar.jpeg" src="bar.jpeg"/&gt;
  + *     &lt;zip:entry name="index.html" serializer="html"&gt;
  + *       &lt;html&gt;
  + *         &lt;head&gt;
  + *           &lt;title&gt;Index page&lt;/title&gt;
  + *         &lt;/head&gt;
  + *         &lt;body&gt;
  + *           Please go &lt;a href="foo.html"&gt;there&lt;/a&gt;
  + *         &lt;/body&lt;
  + *       &lt;/html&gt;
  + *     &lt;/zip:entry&gt;
    *   &lt;/zip:archive:zip&gt;
    * </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
  -    implements Composable {
  +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";;
  +    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];
   
  -    protected ComponentManager manager;
  +    /** 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.
  @@ -127,42 +172,80 @@
        * @see org.xml.sax.ContentHandler#startDocument()
        */
       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
  @@ -174,41 +257,117 @@
           }
   
           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 + "'");
           }
   
  -        SourceResolver resolver = null;
  -        Source         source   = null;
           try {
  -            resolver = (SourceResolver)this.manager.lookup(SourceResolver.ROLE);
  -            
               // Create a new Zip entry
               ZipEntry entry = new ZipEntry(name);
               this.zipOutput.putNextEntry(entry);
   
  -            // Get the source and its data
  -            source = resolver.resolveURI(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));
  +                }
   
  -            // and close the entry
  -            this.zipOutput.closeEntry();
  +                this.state = IN_CONTENT_STATE;
  +                this.contentDepth = 0;
  +            }
   
           } catch (RuntimeException re) {
               throw re;
  +        } catch (SAXException se) {
  +            throw se;
           } catch (Exception e) {
               throw new SAXException(e);
  -        } finally {
  -            if (resolver != null) {
  -                resolver.release(source);
  +        }
  +    }
  +
  +    /**
  +     * @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;
               }
  -            this.manager.release(resolver);
  +        } else {
  +            this.nsSupport.popContext();
           }
       }
   
  @@ -224,13 +383,16 @@
               throw new SAXException(ioe);
           }
       }
  -
       /**
  -     * @see 
org.apache.avalon.framework.component.Composable#compose(ComponentManager)
  +     * @see org.apache.avalon.excalibur.pool.Recyclable#recycle()
        */
  -    public void compose(ComponentManager componentManager)
  -        throws ComponentException {
  -        this.manager = componentManager;
  +    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]

Reply via email to