/*
 * @(#)CCCreatorUtil.java
 *
 * Copyright (C) 2003 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;

import java.io.File;
import java.io.IOException;

import junit.framework.Assert;
import net.sourceforge.groboutils.autodoc.v1.AutoDoc;
import net.sourceforge.groboutils.codecoverage.v2.compiler.CompilerCreatorUtil;
import net.sourceforge.groboutils.codecoverage.v2.compiler.ModifiedClass;
import net.sourceforge.groboutils.codecoverage.v2.compiler.ModifiedMethod;
import net.sourceforge.groboutils.codecoverage.v2.compiler.ParseCoverageLogger;
import net.sourceforge.groboutils.codecoverage.v2.datastore.AnalysisModuleSet;
import net.sourceforge.groboutils.codecoverage.v2.datastore.ClassRecord;
import net.sourceforge.groboutils.codecoverage.v2.datastore.DatastoreCreatorUtil;
import net.sourceforge.groboutils.codecoverage.v2.datastore.DirMetaDataReader;
import net.sourceforge.groboutils.codecoverage.v2.datastore.DirMetaDataWriter;
import net.sourceforge.groboutils.codecoverage.v2.datastore.IClassMetaDataWriter;
import net.sourceforge.groboutils.codecoverage.v2.datastore.IMetaDataReader;
import net.sourceforge.groboutils.codecoverage.v2.datastore.IMetaDataWriter;
import net.sourceforge.groboutils.codecoverage.v2.logger.DirectoryChannelLogReader;
import net.sourceforge.groboutils.codecoverage.v2.logger.DirectoryChannelLogger;
import net.sourceforge.groboutils.codecoverage.v2.module.BranchCountMeasure;
import net.sourceforge.groboutils.codecoverage.v2.module.BytecodeCountMeasure;
import net.sourceforge.groboutils.codecoverage.v2.module.DefaultAnalysisMetaData;
import net.sourceforge.groboutils.codecoverage.v2.module.LineCountMeasure;
import net.sourceforge.groboutils.codecoverage.v2.report.AnalysisModuleData;

import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.classfile.Method;
import org.apache.bcel.generic.MethodGen;


/**
 * Helper for creating code coverage test objects.
 *
 * @author    Matt Albrecht <a href="mailto:groboclown@users.sourceforge.net">groboclown@users.sourceforge.net</a>
 * @version   $Date: 2004/04/15 05:48:27 $
 * @since     January 23, 2003
 */
public class CCCreatorUtil
{
    private static final Class THIS_CLASS = CCCreatorUtil.class;
    private static final AutoDoc DOC = new AutoDoc( THIS_CLASS );

    
    
    public static AnalysisModuleSet createAnalysisModuleSet( int count )
    {
        return createAnalysisModuleSet( createAnalysisModules( count ) );
    }
    
    
    public static AnalysisModuleSet createAnalysisModuleSet(
            IAnalysisModule[] amL )
    {
        Assert.assertNotNull( amL );
        AnalysisModuleSet ams = new AnalysisModuleSet( amL );
        return ams;
    }


    public static IAnalysisModule[] createAnalysisModules( int count )
    {
        IAnalysisModule amL[] = new IAnalysisModule[ count ];
        for (int i = 0; i < count; ++i)
        {
            amL[i] = createIAnalysisModule( 0 );
        };
        return amL;
    }
    
    
    private static int moduleIndex = 0;
    public static IAnalysisModule createIAnalysisModule( int type )
    {
        IAnalysisModule am;
        switch (type)
        {
            case 0:
                am = createIAnalysisModule( "n"+(moduleIndex++),
                    "u", "text/plain" );
                break;
            case 1:
                am = createIAnalysisModule( "a", "b", "text/html" );
                break;
            case 2:
                am = new LineCountMeasure();
                break;
            case 3:
                am = new BytecodeCountMeasure();
                break;
            case 4:
                am = new BranchCountMeasure();
                break;
            default:
                am = null;
        }
        return am;
    }
    
    
    public static IAnalysisModule createIAnalysisModule( String n, String u,
            String m )
    {
        IAnalysisModule am = DatastoreCreatorUtil.createAnalysisModule(
            n, u, m );
        return am;
    }
    
    
    public static ClassRecord createClassRecord( Class c,
            ModifiedMethod mm, AnalysisModuleSet ams )
    {
        Assert.assertNotNull(
            "Null method.",
            mm );
        Assert.assertNotNull( "Null class.", c );
        ClassRecord cr = new ClassRecord( c.getName(),
            0, "Source.java", new String[] { mm.getMethodName() }, ams );
        return cr;
    }
    
    
    public static ModifiedClass createModifiedClass( Class c )
            throws IOException
    {
        Assert.assertNotNull( c );
        String filename = BytecodeLoaderUtil.getClassFilename( c );
        ModifiedClass mc = new ModifiedClass( filename,
            BytecodeLoaderUtil.loadBytecode( filename ) );
        return mc;
    }
    
    
    public static ModifiedClass createModifiedClass( ParseCoverageLogger pcl,
            Class c )
            throws IOException
    {
        Assert.assertNotNull( c );
        String filename = BytecodeLoaderUtil.getClassFilename( c );
        ModifiedClass mc = createModifiedClass( pcl, filename,
            BytecodeLoaderUtil.loadBytecode( filename ) );
        return mc;
    }
    
    
    public static ModifiedClass createModifiedClass( ParseCoverageLogger pcl,
            String classFilename, byte[] bytecode )
            throws IOException
    {
        Assert.assertNotNull( bytecode );
        ModifiedClass mc = new ModifiedClass( pcl, classFilename, bytecode );
        return mc;
    }
    
    
    public static ModifiedMethod getMainModifiedMethod( ModifiedClass mc )
    {
        ModifiedMethod mm[] = mc.getMethods();
        for (int i = 0; i < mm.length; ++i)
        {
            if ("main([Ljava/lang/String;)V".equals( mm[i].getMethodName() ))
            {
                return mm[i];
            }
        }
        Assert.fail( "Class "+mc.getClassName()+
            " does not have a main method." );
        // needs a return
        throw new IllegalStateException("Unreachable statement.");
    }

    
    public static ModifiedMethod createModifiedMethod( JavaClass jc,
            int methodIndex, Method m, MethodGen mg )
    {
        return CompilerCreatorUtil.createModifiedMethod( jc, methodIndex,
            m, mg );
    }

    
    public static ModifiedMethod createModifiedMethod( Class c,
            int methodIndex )
            throws IOException
    {
        JavaClass jc = BCELCreatorUtil.createJavaClass( c );
        Method m = BCELCreatorUtil.getMethod( jc, methodIndex );
        MethodGen mg = BCELCreatorUtil.createMethodGen( jc, methodIndex );
        ModifiedMethod mm = createModifiedMethod( jc, methodIndex, m, mg );
        return mm;
    }
    
    
    public static IMethodCode createIMethodCode( Class c, int methodIndex,
            AnalysisModuleSet ams, int measureIndex )
            throws IOException
    {
        ModifiedMethod mm = createModifiedMethod( c, methodIndex );
        return createIMethodCode( c, mm, ams, measureIndex );
    }
    
    
    public static IMethodCode createIMethodCode( Class c, ModifiedMethod mm,
            AnalysisModuleSet ams, int measureIndex )
    {
        IMethodCode mc = CompilerCreatorUtil.createIMethodCode(
            (short)measureIndex, mm, createClassRecord( c, mm, ams ) );
        return mc;
    }
            
    
    
    public static IAnalysisMetaData createIAnalysisMetaData( String c,
            String nc, byte w )
    {
        DefaultAnalysisMetaData damd = new DefaultAnalysisMetaData( c, nc, w );
        return damd;
    }
    
    
    public static AnalysisModuleData createAnalysisModuleData(
            IAnalysisModule module, IMetaDataReader mdr,
            IChannelLogReader clr )
            throws IOException
    {
        AnalysisModuleData amd = new AnalysisModuleData( module, mdr, clr );
        return amd;
    }
    
    
    public synchronized static File createNewDirectory()
    {
        File f = null;
        while (f == null || f.exists())
        {
            f = new File( ".", Long.toString( System.currentTimeMillis() ) );
        }
        f.mkdirs();
        Assert.assertTrue( "Did not generate a directory.",
            f.isDirectory() );
        return f;
    }
    
    
    public static class SimpleClassLogData {
        public String classSig;
        public int methods[];
        public int marks[];
        
        public SimpleClassLogData( String cs, int me[], int ma[] )
        {
            this.classSig = cs;
            this.methods = me;
            this.marks = ma;
            validate();
        }
        
        
        public void validate()
        {
            Assert.assertNotNull( this.classSig );
            Assert.assertNotNull( this.methods );
            Assert.assertNotNull( this.marks );
            Assert.assertEquals( this.methods.length, this.marks.length );
        }
    }
    
    
    public static void populateLogger( IChannelLogger cl,
            SimpleClassLogData inputData[] )
    {
        Assert.assertNotNull( cl );
        Assert.assertNotNull( inputData );
        for (int i = 0; i < inputData.length; ++i)
        {
            inputData[i].validate();
            DOC.getLog().info( "Logging class "+inputData[i].classSig+"." );
            for (int j = 0; j < inputData[i].methods.length; ++j)
            {
                DOC.getLog().debug( "-- mark "+inputData[i].methods[j]+"-"+
                    inputData[i].marks[j]+"." );
                cl.cover( inputData[i].classSig,
                    (short)inputData[i].methods[j],
                    (short)inputData[i].marks[j] );
            }
        }
    }
    
    
    /**
     * Creates a DirChannelLogReader suitable for testing.  You need to
     * pass in an array of channel datas (classes above) to create the
     * base data.
     */
    public static DirectoryChannelLogReader createDirectoryChannelLogReader(
            File basedir, SimpleClassLogData inputData[], int channel )
    {
        short sc = (short)channel;
        File channelDir = new File( basedir, Short.toString( sc ) );
        DirectoryChannelLogger dcl = new DirectoryChannelLogger( channelDir );
        DOC.getLog().info( "Putting log data into dir '"+channelDir+"'." );
        populateLogger( dcl, inputData );
        DirectoryChannelLogReader dclr = new DirectoryChannelLogReader(
            basedir, sc );
        return dclr;
    }
    
    
    public static void populateMetaData( IMetaDataWriter mdw, Class set[],
            IAnalysisModule modules[] )
            throws IOException
    {
        Assert.assertNotNull( mdw );
        Assert.assertNotNull( set );
        Assert.assertNotNull( modules );
        AnalysisModuleSet ams = createAnalysisModuleSet( modules );
        for (int i = 0; i < modules.length; ++i)
        {
            IClassMetaDataWriter cmdw = mdw.getClassWriter( modules[i] );
            try
            {
                for (int j = 0; j < set.length; ++j)
                {
                    ModifiedClass mc = createModifiedClass( set[j] );
                    cmdw.writeClassRecord( mc.createClassRecord( ams ) );
                }
            }
            finally
            {
                cmdw.close();
            }
        }
    }
    
    
    public static DirMetaDataReader createDirMetaDataReader( File basedir,
            Class populateSet[], IAnalysisModule modules[] )
            throws IOException
    {
        IMetaDataWriter mdw = new DirMetaDataWriter( basedir );
        try
        {
            populateMetaData( mdw, populateSet, modules );
        }
        finally
        {
            mdw.close();
        }
        
        DirMetaDataReader dmdr = new DirMetaDataReader( basedir );
        return dmdr;
    }
}

