/*
 * @(#)DirectoryChannelLogger.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.logger;

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

import net.sourceforge.groboutils.codecoverage.v2.IChannelLogger;


/**
 * Logs coverage reports to a directory of logs.  The directories are split
 * by the channel index, and the directory contains one log per class file
 * analyzed.
 * <p>
 * As of 2004-Jun-3, the output format has changed to:
 * <PRE>
 *    <i>method index</i> <i>mark index</i> EOL
 * </PRE>
 * where both indices are output in hexidecimal format.  This is for performance
 * reasons.
 *
 * @author    Matt Albrecht <a href="mailto:groboclown@users.sourceforge.net">groboclown@users.sourceforge.net</a>
 * @version   $Date: 2004/07/07 09:39:10 $
 * @since     December 17, 2002
 */
public class DirectoryChannelLogger implements IChannelLogger
{
    public static final String CLASS_LOG_EXTENTION = ".class.log";
    
    protected File baseDir;
    
    
    public DirectoryChannelLogger( File baseDir )
    {
        // basedir can be null!
        this.baseDir = baseDir;
        if (baseDir == null)
        {
            System.err.println("DirectoryLogger base directory is null.");
        }
        else
        {
            if (!baseDir.exists())
            {
                baseDir.mkdir();
            }
        }
    }
    
    
    /**
     * Records a coverage of a marked bytecode instruction.  This method should
     * never throw an exception.
     *
     * @param classSignature a signature of the class file being covered.
     *        this signature includes the fully-qualified name of the class,
     *        along with a checksum to uniquely identify it.
     * @param methodIndex index for a method within the class.  The meta-data
     *        store will know how to translate the index to a method signature.
     * @param markIndex the index of the bytecode instruction mark for this
     *        particular channel.
     */
    public void cover( String classSignature, short methodIndex,
            short markIndex )
    {
        if (this.baseDir != null)
        {
//System.err.println("DirectoryChannelLogger: "+classSignature+":"+methodIndex+"."+markIndex);
            File f = getClassFile( this.baseDir, classSignature );
            FileWriter fw = null;
            try
            {
                char[] out = createCoverString( methodIndex, markIndex );
                synchronized (this)
                {
                    fw = new FileWriter( f.toString(), true );
                    fw.write( out );
                    fw.flush();
//System.out.println("** wrote ["+out+"] to "+f.getAbsolutePath());
                }
            }
            catch (IOException ioe)
            {
                // gasp!!!!
                ioe.printStackTrace();
                
                // prevent these errors from occuring again
                this.baseDir = null;
            }
            finally
            {
                if (fw != null)
                {
                    try
                    {
                        fw.close();
                    }
                    catch (IOException ioe)
                    {
                        // gasp again!
                        ioe.printStackTrace();
                        
                        // this this happened on a close, I'll ignore it
                    }
                }
            }
        }
    }
    
    
    private static final char[] HEX  = {
        '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'
    };
    /**
     * Make static final so that the invocation time is minimized.
     * <p>
     * This now returns a character array, for performance reasons.  The
     * array's format is in hexidecimal.
     */
    protected static final char[] createCoverString(
            short methodIndex, short markIndex )
    {
        char c[] = new char[10];
        c[9] = '\n';
        
        // avoid the integer promotion later on,
        // and do the masking with the HEX index lookup
        int imeth = (int)methodIndex;
        int imark = (int)markIndex;
        
        // unroll the loop
        
        // make sure we do a bitwise shift, not an arithmetic shift.
        c[8] = HEX[ imark & 0xf ]; imark >>>= 4;
        c[7] = HEX[ imark & 0xf ]; imark >>>= 4;
        c[6] = HEX[ imark & 0xf ]; imark >>>= 4;
        c[5] = HEX[ imark & 0xf ];
        
        c[4] = ' ';
        
        c[3] = HEX[ imeth & 0xf ]; imeth >>>= 4;
        c[2] = HEX[ imeth & 0xf ]; imeth >>>= 4;
        c[1] = HEX[ imeth & 0xf ]; imeth >>>= 4;
        c[0] = HEX[ imeth & 0xf ];
        
        return c;
    }
    
    
    /**
     * Make static final so that the invocation time is minimized.
     */
    protected static final File getClassFile( File basedir,
            String classSignature )
    {
        return new File( basedir, classSignature + CLASS_LOG_EXTENTION );
    }
}

