cocoon-cvs mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From sylv...@apache.org
Subject cvs commit: xml-cocoon2/src/java/org/apache/cocoon/serialization ZipArchiveSerializer.java
Date Mon, 06 Jan 2003 15:24:12 GMT
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>
  +&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>
  
  
  
  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>
    *   &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
  -{
  +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:     webmaster@xml.apache.org
To unsubscribe, e-mail:          cocoon-cvs-unsubscribe@xml.apache.org
For additional commands, e-mail: cocoon-cvs-help@xml.apache.org


Mime
View raw message