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

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

import net.sourceforge.groboutils.codecoverage.v2.IAnalysisModule;
import net.sourceforge.groboutils.util.io.v1.ExtensionFilenameFilter;

/**
 * Accesses the meta-data at the I/O level in a directory structure.
 *
 * @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
 * @see       ZipMetaDataReader
 */
class DirMetaDataIO
{
    private static final org.apache.log4j.Logger LOG =
        org.apache.log4j.Logger.getLogger( DirMetaDataIO.class );
    
    private static final String MODULE_SET_FILENAME = "moduleset.txt";
    private static final String CLASS_FILE_EXT = ".classdata.txt";
    private static final String MARK_FILE_EXT = ".classmarks.txt";
    
    
    private File basedir;
    private static final ExtensionFilenameFilter CLASS_FILE_FILTER =
        new ExtensionFilenameFilter( CLASS_FILE_EXT, true );
    static {
        CLASS_FILE_FILTER.allowsDirectories( false );
    }
    
    public DirMetaDataIO( File dir )
            throws IOException
    {
        if (dir == null)
        {
            throw new IllegalArgumentException( "No null args." );
        }
        if (dir.exists() && !dir.isDirectory())
        {
            throw new IllegalArgumentException(
                "File "+dir+" is not a directory." );
        }
        if (!dir.exists())
        {
            dir.mkdir();
        }
        this.basedir = dir;
    }
    
    
    /**
     * 
     */
    public AnalysisModuleSet getAnalysisModuleSet()
            throws IOException
    {
        checkClosed();
        AnalysisModuleSetIO amsr = new AnalysisModuleSetIO();
        AnalysisModuleSet ams;
        File amsf = getAnalysisModuleSetFile();
        if (!amsf.exists())
        {
            ams = new AnalysisModuleSet();
        }
        else
        {
            BufferedReader fr = new BufferedReader( new FileReader( amsf ) );
            try
            {
                ams = amsr.readAnalysisModuleSet( fr );
            }
            finally
            {
                fr.close();
            }
        }
        return ams;
    }
    
    
    /**
     * 
     */
    public void putAnalysisModuleSet( AnalysisModuleSet ams )
            throws IOException
    {
        if (ams == null)
        {
            throw new IllegalArgumentException("no null args");
        }
        checkClosed();
        AnalysisModuleSetIO amsw = new AnalysisModuleSetIO();
        File amsf = getAnalysisModuleSetFile();
        BufferedWriter bw = new BufferedWriter( new FileWriter( amsf ) );
        try
        {
            amsw.writeAnalysisModuleSet( ams, bw );
        }
        finally
        {
            bw.close();
        }
    }
    
    
    /**
     * 
     */
    public String[] getClassSigsForAnalysisModule( IAnalysisModule am )
            throws IOException
    {
        if (am == null)
        {
            throw new IllegalArgumentException("no null args");
        }
        checkClosed();
        File amDir = getAnalysisModuleDir( am );
        String names[] = amDir.list( CLASS_FILE_FILTER );
        for (int i = 0; i < names.length; ++i)
        {
            names[i] = names[i].substring( 0, names[i].length() -
                CLASS_FILE_EXT.length() );
        }
        return names;
    }
    
    
    /**
     * 
     */
    public ClassRecord getClassRecord( IAnalysisModule am, String classSig )
            throws IOException
    {
        return getClassRecord( getAnalysisModuleSet(), am, classSig );
    }
    
    
    /**
     * If the class record does not exist, then <tt>null</tt> is returned.
     */
    public ClassRecord getClassRecord( AnalysisModuleSet ams,
            IAnalysisModule am, String classSig )
            throws IOException
    {
        if (am == null || classSig == null || ams == null)
        {
            throw new IllegalArgumentException("no null args");
        }
        checkClosed();
        File cf = getClassDataFileForModule( am, classSig );
        ClassRecordIO crr = new ClassRecordIO();
        BufferedReader fr;
        try
        {
            fr = new BufferedReader( new FileReader( cf ) );
        }
        catch (java.io.FileNotFoundException fnfe)
        {
            LOG.warn( "Couldn't find the file '"+cf+
                "' containing the class details." );
            return null;
        }
        
        ClassRecord cr;
        try
        {
            cr = crr.readClass( ams, fr );
        }
        finally
        {
            fr.close();
        }
        
        // read marks
        File mcf = getClassMarkFileForModule( am, cr.getClassSignature() );
        try
        {
            fr = new BufferedReader( new FileReader( mcf ) );
        }
        catch (java.io.FileNotFoundException fnfe)
        {
            LOG.warn( "Could not find file '"+mcf+"' containing marks." );
            return cr;
        }
        
        try
        {
            crr.readMarks( cr, fr );
        }
        finally
        {
            fr.close();
        }
        
        return cr;
    }
    
    
    /**
     * 
     */
    public void putClassRecord( IAnalysisModule am, ClassRecord cr )
            throws IOException
    {
        if (am == null || cr == null)
        {
            throw new IllegalArgumentException("no null args");
        }
        checkClosed();
        File cf = getClassDataFileForModule( am, cr.getClassSignature() );
        ClassRecordIO crw = new ClassRecordIO();
        BufferedWriter fw = new BufferedWriter( new FileWriter( cf ) );
        try
        {
            crw.writeClass( cr, fw );
        }
        finally
        {
            fw.close();
        }
        
        // write marks
        File mcf = getClassMarkFileForModule( am, cr.getClassSignature() );
        fw = new BufferedWriter( new FileWriter( mcf ) );
        try
        {
            crw.writeMarksForAnalysisModule( cr, am, fw );
        }
        finally
        {
            fw.close();
        }
    }
    
    
    /**
     * 
     */
    public void deleteClassRecord( IAnalysisModule am, ClassRecord cr )
            throws IOException
    {
        if (am == null || cr == null)
        {
            throw new IllegalArgumentException("no null args");
        }
        checkClosed();
        File cf = getClassDataFileForModule( am, cr.getClassSignature() );
        if (cf.exists())
        {
            cf.delete();
        }
    }
    
    
    /**
     * 
     */
    private File getAnalysisModuleSetFile()
    {
        File moduleSetFile = new File( this.basedir, MODULE_SET_FILENAME );
        return moduleSetFile;
    }
    
    
    /**
     * 
     */
    private File getAnalysisModuleDir( IAnalysisModule am )
            throws IOException
    {
        File newDir = new File( this.basedir, am.getMeasureName() );
        if (newDir.exists() && !newDir.isDirectory())
        {
            throw new IOException( "Expected measure directory '"+newDir+
                "', but it isn't a directory." );
        }
        if (!newDir.exists())
        {
            newDir.mkdir();
        }
        return newDir;
    }
    
    
    /**
     * 
     */
    private File getClassDataFileForModule( IAnalysisModule am,
            String classSig )
            throws IOException
    {
        File mf = getAnalysisModuleDir( am );
        File cf = new File( mf, classSig + CLASS_FILE_EXT );
        return cf;
    }
    
    
    /**
     * 
     */
    private File getClassMarkFileForModule( IAnalysisModule am,
            String classSig )
            throws IOException
    {
        File mf = getAnalysisModuleDir( am );
        File cf = new File( mf, classSig + MARK_FILE_EXT );
        return cf;
    }
    
    
    
    /**
     * This can be called multiple times without error.
     */
    public void close()
            throws IOException
    {
        LOG.debug( "Closing DirMetaDataIO." );
        this.basedir = null;
    }
    
    
    /**
     * 
     */
    private void checkClosed()
            throws IOException
    {
        if (isClosed())
        {
            throw new IOException( "Directory storage has been closed." );
        }
    }
    
    
    /**
     * 
     */
    boolean isClosed()
    {
        return (this.basedir == null);
    }
    
    
    protected void finalize() throws Throwable
    {
        Exception ex = null;
        if (!isClosed())
        {
            ex = new IllegalStateException("Did not close writer.");
            close();
        }
        
        super.finalize();
        
        // class-based post condition
        if (ex != null)
        {
            throw ex;
        }
    }
}

