/*
 * @(#)ClassRecord.java
 *
 * Copyright (C) 2002-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.datastore;

import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import net.sourceforge.groboutils.codecoverage.v2.IAnalysisModule;
import net.sourceforge.groboutils.codecoverage.v2.util.ClassSignatureUtil;

/**
 * Contains data associated with a parsed class.  Each class is associated
 * with a collection of analysis modules' marks.
 *
 * @author    Matt Albrecht <a href="mailto:groboclown@users.sourceforge.net">groboclown@users.sourceforge.net</a>
 * @version   $Date: 2004/04/15 05:48:26 $
 * @since     December 15, 2002
 */
public class ClassRecord
{
    private String className;
    private long classCRC;
    private String sourceFileName;
    private Map methodToIndex;
    private String[] methodSignatures;
    private AnalysisModuleSet amSet;
    private List marks[]; // each index is for the analysis module
    
    public ClassRecord( String className, long classCRC, String sourceFileName,
            String[] methSigs, AnalysisModuleSet ams )
    {
        // we can have an empty set of methods.
        if (className == null || className.length() <= 0 ||
            methSigs == null || ams == null || sourceFileName == null)
        {
            throw new IllegalArgumentException( "No null args." );
        }
        
        this.className = className;
        this.classCRC = classCRC;
        this.sourceFileName = sourceFileName;
        int len = methSigs.length;
        if (len > Short.MAX_VALUE)
        {
            throw new IllegalStateException(
                "Too many methods.  There is a maximum internal count of "+
                Short.MAX_VALUE+"." );
        }
        this.methodSignatures = new String[ len ];
        this.methodToIndex = new HashMap();
        for (int i = 0; i < len; ++i)
        {
            if (methSigs[i] == null)
            {
                throw new IllegalArgumentException( "No null args." );
            }
            
            this.methodSignatures[i] = methSigs[i];
            this.methodToIndex.put( methSigs[i], new Short( (short)i ) );
        }
        
        // make a copy of the set, so it isn't changed underneath us.
        this.amSet = new AnalysisModuleSet( ams );
        len = this.amSet.getAnalysisModuleCount();
        this.marks = new List[ len ];
        for (int i = 0; i < len; ++i)
        {
            this.marks[i] = new LinkedList();
        }
    }
    
    
    /**
     * Gets the name of the corresponding class for this record.
     *
     * @return the class name.
     */
    public String getClassName()
    {
        return this.className;
    }
    
    
    /**
     * Returns the cyclic redundancy check (CRC) for the class in this record.
     *
     * @return the class CRC.
     */
    public long getClassCRC()
    {
        return this.classCRC;
    }
    
    
    /**
     * Returns the implementation-specific class signature for the class this
     * record refers to.  It should be a String which is unique for this class,
     * even if multiple class files have the same class name (note that if the
     * class files are identical, it makes perfect sense to return identical
     * signatures).
     *
     * @return the unique signature for the class in this record.
     */
    public String getClassSignature()
    {
        return ClassSignatureUtil.getInstance().
            createClassSignature( getClassName(), getClassCRC() );
    }
    
    
    /**
     * Returns the name of the Java source file.
     *
     * @return the source file name
     */
    public String getSourceFileName()
    {
        return this.sourceFileName;
    }
    
    
    /**
     * Returns a copy of the internal analysis module set.
     *
     * @return a copy of the set of analysis modules.
     */
    public AnalysisModuleSet getAnalysisModuleSet()
    {
        return new AnalysisModuleSet( this.amSet );
    }
    
    
    /**
     * Returns a list of known method signatures for this class.
     *
     * @return all method signatures known for this class, in the correct
     *    sorted order.
     */
    public String[] getMethods()
    {
        int len = this.methodSignatures.length;
        String s[] = new String[ len ];
        System.arraycopy( this.methodSignatures, 0, s, 0, len );
        return s;
    }
    
    
    /**
     * Returns the index (as a short) for the given method signature.  If the
     * signature is not registered, then <tt>-1</tt> will be returned.
     *
     * @param methodSignature the signature to find the corresponding index
     *      for in this class.
     * @return the index for <tt>methodSignature</tt> if it is in this method,
     *      or <tt>-1</tt> if it is not in the list.
     * @exception IllegalArgumentException if <tt>methodSignature</tt> is
     *      <tt>null</tt>.
     */
    public short getMethodIndex( String methodSignature )
    {
        if (methodSignature == null)
        {
            throw new IllegalArgumentException( "No null args." );
        }
        Short i = (Short)this.methodToIndex.get( methodSignature );
        if (i == null)
        {
            return -1;
        }
        return i.shortValue();
    }
    
    
    /**
     * Returns the total number of method signatures for this class.
     *
     * @return the method signature count.
     */
    public int getMethodCount()
    {
        return this.methodSignatures.length;
    }
    
    
    /**
     * Returns the method signature at the given index.
     *
     * @param index the index of the method signature to find.
     * @return the method signature at index <tt>index</tt>.
     * @exception IllegalArgumentException if <tt>index</tt> is not within the
     *      bounds of [0 .. <tt>getMethodCount()</tt>-1 ].
     */
    public String getMethodAt( short index )
    {
        int iindex = (int)index;
        if (iindex < 0 || iindex >= this.methodSignatures.length)
        {
            throw new IllegalArgumentException( "Index out of bounds [0.."+
                this.methodSignatures.length+")" );
        }
        return this.methodSignatures[ iindex ];
    }
    
    
    /**
     * Adds a mark record, and ensures that it is unique upon insertion.
     *
     * @param mr the new mark to add.  Adding this mark will complete its
     *      internal data structure.
     */
    public void addMark( MarkRecord mr )
    {
        if (mr == null)
        {
            throw new IllegalArgumentException( "No null args." );
        }
        mr.processMark( this, getAnalysisModuleSet() );
        int moduleIndex = (int)mr.getAnalysisModuleIndex();
        
        Iterator iter = this.marks[ moduleIndex ].iterator();
        boolean add = true;
        while (iter.hasNext())
        {
            MarkRecord listRecord = (MarkRecord)iter.next();
            if (listRecord.equals( mr ))
            {
                add = false;
                break;
            }
        }
        if (add)
        {
            this.marks[ moduleIndex ].add( mr );
        }
    }
    
    
    public MarkRecord[] getMarksForAnalysisModule( String measureName )
    {
        int moduleIndex = (int)this.amSet.getMeasureIndex( measureName );
        if (moduleIndex < 0 || moduleIndex >= this.marks.length)
        {
            throw new IllegalArgumentException(
                "Unknown analysis module '"+measureName+
                "' (index = "+moduleIndex+")" );
        }
        
        List list = this.marks[ moduleIndex ];
        MarkRecord mr[] = (MarkRecord[])list.toArray(
            new MarkRecord[ list.size() ] );
        return mr;
    }
    
    
    public MarkRecord[] getMarksForAnalysisModule( IAnalysisModule am )
    {
        return getMarksForAnalysisModule( am.getMeasureName() );
    }
}

