/*

Derby - Class org.apache.derbyDemo.vtis.example.SubversionLogVTI

Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements.  See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License.  You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

*/

package org.apache.derbyDemo.vtis.example;

import java.io.*;
import java.sql.*;
import java.text.SimpleDateFormat;

import org.apache.derbyDemo.vtis.core.*;

/**
 * <p>
 * This VTI makes a table out of the output of the subversion log ("svn log") command.
 * </p>
 */
public    class   SubversionLogVTI  extends FlatFileVTI
{
    ///////////////////////////////////////////////////////////////////////////////////
    //
    // CONSTANTS
    //
    ///////////////////////////////////////////////////////////////////////////////////

    private static  final   String[]    COLUMN_NAMES =
    {
        "XID", "committer", "commit_time", "line_count", "description"
    };

    private static  final   int XID = 0;
    private static  final   int COMMITTER = XID + 1;
    private static  final   int COMMIT_TIME = COMMITTER + 1;
    private static  final   int LINE_COUNT = COMMIT_TIME + 1;
    private static  final   int DESCRIPTION = LINE_COUNT + 1;

    private static  final   String  RECORD_HEADER = "------------------------------------------------------------------------";
    

    ///////////////////////////////////////////////////////////////////////////////////
    //
    // STATE
    //
    ///////////////////////////////////////////////////////////////////////////////////

    private SimpleDateFormat    _dateFormatter;
    
    ///////////////////////////////////////////////////////////////////////////////////
    //
    // CONSTRUCTORS
    //
    ///////////////////////////////////////////////////////////////////////////////////

    /**
     * <p>
     * Build a SubversionLogVTI given the name of the output file created by the
     * "svn log" command.
     * </p>
     */
    public  SubversionLogVTI( String logFileName )
    {
        super( COLUMN_NAMES, logFileName );
    }

    ///////////////////////////////////////////////////////////////////////////////////
    //
    // TABLE FUNCTION METHOD
    //
    ///////////////////////////////////////////////////////////////////////////////////


    /**
     * <p>
     * This is the method which is registered as a table function.
     * </p>
     */
    public  static  ResultSet   subversionLogVTI( String logFileName )
        throws SQLException
    {
        return new SubversionLogVTI( logFileName );
    }

    ///////////////////////////////////////////////////////////////////////////////////
    //
    // FlatFileVTI BEHAVIOR TO BE IMPLEMENTED BY SUBCLASSES
    //
    ///////////////////////////////////////////////////////////////////////////////////

    /**
     * <p>
     * Parse the next chunk of text, using readLine(), and return the next row.
     * Returns null if the file is exhausted.
     * </p>
     */
    protected  String[]  parseRow( ) throws SQLException
    {
        String[]    newRow = new String[ COLUMN_NAMES.length ];

        String      headerLine = readNextLine();

        if ( headerLine == null ) { return null; }
        if ( !isRecordHeader( headerLine ) )
        {
            throw new SQLException( "Badly formatted record header: " + headerLine );
        }

        // the next line has all of the columns except for DESCRIPTION

        String      mainline = readNextLine();

        if ( mainline == null ) { return null; }
        
        int         oldIdx[] = new int[ 1 ];

        oldIdx[ 0 ] = 0;

        for ( int i = 0; i < DESCRIPTION; i++ ) { newRow[ i ] = readField( mainline, oldIdx ); }

        // get the number of lines in the DESCRIPTION
        int     descriptionLineCount = 0;

        try {
            String  lineCountField = newRow[ LINE_COUNT ];

            if ( lineCountField != null )
            {
                lineCountField = lineCountField.trim();
                String  numberString = lineCountField.substring( 0, lineCountField.indexOf( ' ' ) );

                descriptionLineCount = Integer.parseInt( numberString );
            }
        }
        catch ( Throwable t )
        {
            throw wrap( "Error parsing description line count at line " + getLineNumber() + ": " + mainline, t );
        }

        // account for extra blank line
        descriptionLineCount++;

        // the rest of the record is the DESCRIPTION

        StringBuffer buffer = new StringBuffer();

        for ( int i = 0; i < descriptionLineCount; i++ )
        {
            String  nextLine = readNextLine();
            
            buffer.append( nextLine );
        }

        newRow[ DESCRIPTION ] = buffer.toString();

        return newRow;
    }

    ///////////////////////////////////////////////////////////////////////////////////
    //
    // ResultSet METHOD
    //
    ///////////////////////////////////////////////////////////////////////////////////

    /**
     * <p>
     * Subversion formats timestamps thusly: "2007-09-16 11:17:37 -0700 (Sun, 16 Sep 2007)"
     * </p>
     */
    public java.sql.Timestamp getTimestamp(int columnIndex) throws SQLException
    {
        String          columnValue = getString( columnIndex ).trim();

        try {
            SimpleDateFormat    dateFormatter = getDateFormatter();
            java.util.Date      rawDate = dateFormatter.parse( columnValue );
            long                time = rawDate.getTime();

            return new java.sql.Timestamp( time );            

        } catch (Throwable t) { throw new SQLException( t.getMessage() ); }
    }

    ///////////////////////////////////////////////////////////////////////////////////
    //
    // MINIONS
    //
    ///////////////////////////////////////////////////////////////////////////////////

    /**
     * <p>
     * Read the next field in the main line of the record. Fields are delimited
     * by | or line-end.
     * </p>
     */
    private String  readField( String mainline, int[] oldIdx )
        throws SQLException
    {
        String      result = null;
        int           fieldStart = oldIdx[ 0 ];

        int           fieldEnd = mainline.indexOf( "|", fieldStart );

        if ( fieldEnd < 0 ) { fieldEnd = mainline.length(); }

        // this can happen, if for instance, we encounter a badly formatted record
        if ( fieldStart > fieldEnd ) { return null; }

        try {
            result = mainline.substring( fieldStart, fieldEnd );

            if ( result != null ) { result = result.trim(); }
            
        } catch (Throwable t)
        {
            throw wrap( "Bad record at line " + getLineNumber() + ". Field start = " + fieldStart + ", fieldEnd = " + fieldEnd + ": " + mainline, t );
        }

        oldIdx[ 0 ] = fieldEnd + 1;

        return result;
    }
    
    /**
     * <p>
     * Returns true if a line is a record header.
     * </p>
     */
    private boolean isRecordHeader( String line )
    {
        if ( line.startsWith( RECORD_HEADER ) ) { return true; }
        else { return false; }
    }
    
    /**
     * <p>
     * Read a line, possibly just using the last line that was pushed back.
     * </p>
     */
    private String  readNextLine()  throws SQLException
    {
        String      retval;
        
        retval = readLine();

        return retval;
    }
    
    /**
     * <p>
     * Subversion formats timestamps thusly: "2007-09-16 11:17:37 -0700 (Sun, 16 Sep 2007)"
     * </p>
     */
    private SimpleDateFormat    getDateFormatter()
    {
        if ( _dateFormatter == null )
        {
            _dateFormatter = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss Z (EEE, dd MMM yyyy)" );
        }

        return _dateFormatter;
    }


}
