/*
 * @(#)ChecksumUtil.java
 *
 * Copyright (C) 2002-2004 Matt Albrecht
 * groboclown@users.sourceforge.net
 * http://groboutils.sourceforge.net
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a
 *  copy of this software and associated documentation files (the "Software"),
 *  to deal in the Software without restriction, including without limitation
 *  the rights to use, copy, modify, merge, publish, distribute, sublicense,
 *  and/or sell copies of the Software, and to permit persons to whom the
 *  Software is furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in
 *  all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 *  THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 *  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 *  DEALINGS IN THE SOFTWARE.
 */

package net.sourceforge.groboutils.codecoverage.v2.util;

import java.util.Comparator;
import java.util.zip.CRC32;
import java.util.zip.Checksum;


/**
 * A "universal" method to extract a checksum from the various kinds of
 * data the codecoverage package needs.
 * <P>
 * This is made such that the equivalent types between the BCEL library and
 * JDI library return the same CRC.
 *
 * @author    Matt Albrecht <a href="mailto:groboclown@users.sourceforge.net">groboclown@users.sourceforge.net</a>
 * @version   $Date: 2004/04/15 05:48:26 $
 * @since     Feb 3, 2003
 */
public class ChecksumUtil
{
    protected static ChecksumUtil s_instance = new ChecksumUtil();
    
    // ensure that we ALWAYS get the same collator, no matter the
    // executing environment.
    protected final Comparator StringCollator =
        java.text.Collator.getInstance(new java.util.Locale("en", "US", ""));
    
    /**
     * Creates a new finder using the default (system) classpath.
     */
    protected ChecksumUtil()
    {
        // do nothing
    }
    
    
    public static ChecksumUtil getInstance()
    {
        return s_instance;
    }
    
    
    
    public long checksum( byte[] buff )
    {
        Checksum crc = createChecksum();
        update( crc, buff );
        return crc.getValue();
    }
    
    
    /*
    public long checksum( JavaClass jc )
    {
        Checksum crc = createChecksum();
        update( crc, jc );
        return crc.getValue();
    }
    */
    
    
    /*
    public long checksum( ReferenceType rt )
    {
        Checksum crc = createChecksum();
        update( crc, rt );
        return crc.getValue();
    }
    */
    
    
    /*
    public long checksum( org.apache.bcel.classfile.Method m )
    {
        Checksum crc = createChecksum();
        update( crc, m );
        return crc.getValue();
    }
    */
    
    
    /*
    public long checksum( Code c )
    {
        Checksum crc = createChecksum();
        update( crc, c );
        return crc.getValue();
    }
    */
    
    
    /*
    public long checksum( com.sun.jdi.Method m )
    {
        Checksum crc = createChecksum();
        update( crc, m );
        return crc.getValue();
    }
    */
    
    
    
    //------------------------------------------------------------------------
    // Factory methods
    
    protected Checksum createChecksum()
    {
        return new CRC32();
    }
    
    
    protected void update( Checksum crc, byte[] buff )
    {
        if (buff != null)
        {
            crc.update( buff, 0, buff.length );
        }
    }
    
    
    /*
    protected void update( Checksum crc, JavaClass jc )
    {
        // If JDI allowed for "bytecodes()" on the class level, as
        // opposed to the Method level, then we could just do this:
        
        // update( crc, jc.getBytes() );
        
        // However, JDI only allows bytecodes() on the method level,
        // so we must parse the checksum on a per-method basis.
        
        // Note that to ensure that the correct CRC is generated, we
        // need to ensure that the ORDER of methods is identical across
        // all runs and all types!
        
        org.apache.bcel.classfile.Method ms[] = jc.getMethods();
        ArrayList list = new ArrayList();
        for (int i = 0; i < ms.length; ++i)
        {
            list.add( ms[i] );
        }
        Iterator iter = sortList( list.iterator(), new Comparator()
            {
                public int compare( Object o1, Object o2 )
                {
                    org.apache.bcel.classfile.Method m1 =
                        (org.apache.bcel.classfile.Method)o1;
                    org.apache.bcel.classfile.Method m2 =
                        (org.apache.bcel.classfile.Method)o2;
                    
                    return StringCollator.compare( m1.getSignature(),
                        m2.getSignature() );
                }
                public boolean equals( Object o1 )
                {
                    if (o1 == null) return false;
                    if (o1 == this) return true;
                    return (o1.getClass().equals( this.getClass() ));
                }
            } );
        while (iter.hasNext())
        {
            update( crc, (org.apache.bcel.classfile.Method)iter.next() );
        }
    }
    */
    
    /*
    protected void update( Checksum crc, ReferenceType rt )
    {
        // JDI does not have a "bytecodes()" method on ReferenceType,
        // but does on methods.
        
        // Note that to ensure that the correct CRC is generated, we
        // need to ensure that the ORDER of methods is identical across
        // all runs and all types!

        Iterator iter = sortList( rt.methods().iterator(), new Comparator()
            {
                public int compare( Object o1, Object o2 )
                {
                    com.sun.jdi.Method m1 =
                        (com.sun.jdi.Method)o1;
                    com.sun.jdi.Method m2 =
                        (com.sun.jdi.Method)o2;
                    
                    return StringCollator.compare( m1.signature(),
                        m2.signature() );
                }
                public boolean equals( Object o1 )
                {
                    if (o1 == null) return false;
                    if (o1 == this) return true;
                    return (o1.getClass().equals( this.getClass() ));
                }
            } );
        while (iter.hasNext())
        {
            update( crc, (com.sun.jdi.Method)iter.next() );
        }
    }
    */
    
    /*
    protected void update( Checksum crc, org.apache.bcel.classfile.Method m )
    {
        update( crc, m.getCode() );
    }
    */
    
    /*
    protected void update( Checksum crc, Code c )
    {
        // abstract methods don't have code.
        if (c != null)
        {
            update( crc, c.getCode() );
        }
    }
    */
    
    
    /*
    protected void update( Checksum crc, com.sun.jdi.Method m )
    {
        // abstract methods may not have any bytecodes.
        // thus, this called method needs to be null-proof.
        update( crc, m.bytecodes() );
    }
    */
    
    
    /*
     * Sorts the given iteration with the given comparator.
     * Made generic enough to allow an iterator.  Should allow a
     * Collection to simplify this method, but it's simple enough.
    protected Iterator sortList( Iterator list, Comparator c )
    {
        TreeSet ts = new TreeSet( c );
        while (list.hasNext())
        {
            ts.add( list.next() );
        }
        return ts.iterator();
    }
     */
}
