/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 * 
 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
 * 
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License. You can obtain
 * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
 * or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 * 
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
 * Sun designates this particular file as subject to the "Classpath" exception
 * as provided by Sun in the GPL Version 2 section of the License file that
 * accompanied this code.  If applicable, add the following below the License
 * Header, with the fields enclosed by brackets [] replaced by your own
 * identifying information: "Portions Copyrighted [year]
 * [name of copyright owner]"
 * 
 * Contributor(s):
 * 
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

//ByteCodeEnhancerHelper - Java Source


//***************** package ***********************************************

package com.sun.jdo.api.persistence.enhancer;


//***************** import ************************************************

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;

import java.util.zip.CRC32;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;


//#########################################################################
/**
 *  This is a helper-class to perform some useful operations outside a
 *  byte code enhancer and delegate the real work to the enhancer.
 */
//#########################################################################

public class ByteCodeEnhancerHelper
{


    /**********************************************************************
     *  Enhances a classfile.
     *
     *  @param  enhancer  The enhancer to delegate the work to.
     *  @param  in        The input stream with the Java class.
     *  @param  out       The output stream to write the enhanced class to.
     *
     *  @return  Has the input stream been enhanced?
     *
     *  @exception  EnhancerUserException  If something went wrong.
     *  @exception  EnhancerFatalError     If something went wrong.
     *
     *  @see  ByteCodeEnhancer#enhanceClassFile
     *********************************************************************/

    public static final boolean enhanceClassFile (ByteCodeEnhancer enhancer,
                                                  InputStream      in,
                                                  OutputStream     out)
                                throws EnhancerUserException,
                                       EnhancerFatalError
    {

        return enhancer.enhanceClassFile (in, new OutputStreamWrapper (out));

    }  //ByteCodeEnhancerHelper.enhanceClassFile()


    /**********************************************************************
     *  Enhances a zip file. The zip file is given as a uip input stream.
     *  It's entries are read and - if necessary - individually enhanced.
     *  The output stream has the same compression (if any) as the input
     *  stream.
     *
     *  @param  enhancer  The enhancer.
     *  @param  zip_in    The zip input stream.
     *  @param  zip_out   The zip output stream.
     *
     *  @param  <code>true</code> if at least one entry of the zip file has
     *          been enhanced, <code>false</code> otherwise.
     *
     *  @exception  EnhancerUserException  If something went wrong.
     *  @exception  EnhancerFatalError     If something went wrong.
     *
     *  @see  ByteCodeEnhancer#enhanceClassFile
     *********************************************************************/

    public static final boolean enhanceZipFile (ByteCodeEnhancer enhancer,
                                                ZipInputStream   zip_in,
                                                ZipOutputStream  zip_out)
                                throws EnhancerUserException,
                                       EnhancerFatalError
    {

        boolean enhanced = false;
        try
        {
            CRC32 crc32 = new CRC32 ();
            ZipEntry entry;
            while ((entry = zip_in.getNextEntry ()) != null)
            {
                InputStream in = zip_in;
                ZipEntry    out_entry = new ZipEntry (entry);

                //try to enhance
                if  (isClassFileEntry (entry))  //enhance the classfile
                {
                    //we have to copy the classfile, because if it won't be enhanced,
                    //the OutputStream is empty and we have to re-read the InputStream,
                    //which is impossiblewith a ZipInputStream (no mark/reset)
                    in = openZipEntry (zip_in);
                    in.mark (Integer.MAX_VALUE);
                    ByteArrayOutputStream tmp = new ByteArrayOutputStream ();
                    if  (enhancer.enhanceClassFile (in, tmp))
                    {
                        enhanced = true;
                        byte [] bytes = tmp.toByteArray ();
                        tmp.close ();
                        in.close ();
                        modifyZipEntry (out_entry, bytes, crc32);
                        in = new ByteArrayInputStream (bytes);
                    }
                    else
                    {
                        //the classfile has not been enhanced
                        in.reset ();
                    }
                }

                //copy the entry
                zip_out.putNextEntry (out_entry);
                copyZipEntry (in, zip_out);
                zip_out.closeEntry ();

                if  (in != zip_in)
                {
                    in.close ();
                }
            }
        }
        catch (IOException ex)
        {
            throw new EnhancerFatalError (ex);
        }

        return enhanced;

    }  //ByteCodeEnhancerHelper.enhanceZipFile()


    /**********************************************************************
     *  Copies a zip entry from one stream to another.
     *
     *  @param  in   The inout stream.
     *  @param  out  The output stream.
     *
     *  @exception  IOException  If the stream access failed.
     *********************************************************************/

    private static final void copyZipEntry (InputStream  in,
                                            OutputStream out)
                              throws IOException
    {

        int b;
        while ((in.available () > 0)  &&  (b = in.read ()) > -1)
        {
            out.write (b);
        }

    }  //ByteCodeEnhancerHelper.copyZipEntry()


    /**********************************************************************
     *  Opens the next zip entry of a zip input stream and copies it to
     *  a <code>java.io.ByteArrayOutputStream</code>. It's byte array is made
     *  available via an <code>java.io.ByteArrayInputStream</code> which is
     *  returned.
     *
     *  @param  in  The zip input stream.
     *
     *  @return  The newly created input stream with the next zip entry.
     *
     *  @exception  IOException  If an I/O operation failed.
     *********************************************************************/

    private static final InputStream openZipEntry (ZipInputStream in)
                                     throws IOException
    {

        ByteArrayOutputStream out = new ByteArrayOutputStream ();
        copyZipEntry (in, out);

        return new ByteArrayInputStream (out.toByteArray ());

    }  //ByteCodeEnhancerHelper.openZipEntry()


    /**********************************************************************
     *  Modifies the given zip entry so that it can be added to zip file.
     *  The given zip entry represents an enhanced class, so the zip entry
     *  has to get the correct size and checksum (but only it the entry won't
     *  be compressed).
     *
     *  @param  entry  The zip entry to modify.
     *  @param  bytes  The uncompressed byte representation of the classfile.
     *  @param  crc32  The checksum evaluator.
     *********************************************************************/

    private static final void modifyZipEntry (ZipEntry entry,
                                              byte []  bytes,
                                              CRC32    crc32)
    {

        entry.setSize (bytes.length);
        if  (entry.getMethod () == 0) //no compression (ZipInputStream.STORED - not accessible)
        {
            crc32.reset ();
            crc32.update (bytes);
            entry.setCrc (crc32.getValue ());
            entry.setCompressedSize (bytes.length);
        }

    }  //ByteCodeEnhancerHelper.modifyZipEntry()


    /**********************************************************************
     *  Determines if a given entry represents a classfile.
     *
     *  @return  Does the given entry represent a classfile?
     *********************************************************************/

    private static final boolean isClassFileEntry (ZipEntry entry)
    {

        return entry.getName ().endsWith (".class");

    }  //ByteCodeEnhancerHelper.isClassFileEntry()


}  //ByteCodeEnhancerHelper


//ByteCodeEnhancer - Java Source End
