/*
 * $Header: /home/cvspublic/jakarta-slide/proposals/tamino/src/store/org/apache/slide/store/tamino/tools/config/Log4jConfig.java,v 1.3 2004/07/30 06:52:04 ozeigermann Exp $
 * $Revision: 1.3 $
 * $Date: 2004/07/30 06:52:04 $
 *
 * ====================================================================
 *
 * Copyright 1999-2004 The Apache Software Foundation.
 *
 * Licensed 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.slide.store.tamino.tools.config;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;
import org.apache.slide.store.tamino.common.XGlobals;
import org.apache.slide.util.Files;
import org.apache.slide.util.cli.Abort;


/**
 ** Class.
 **
 ** @author    peter.nevermann@softwareag.com
 ** @version   0.1
 **/
public class Log4jConfig implements XGlobals {
    
    /** Constants */
    private static final String SEP       = File.separator;
    private static final String CLASSES   = "classes";
    private static final String LOG4JPROP = "log4j.properties";
    private static final String LOG4J_CAT = "log4j.category.";
    private static final String LOG4J_ADD = "log4j.additivity.";
    private static final String ALL_CAT   = "org.apache.slide.store.tamino";
    
    /** LOG4J  */
    private static final String DEBUG                            = "DEBUG";
    private static final String INFO                             = "INFO";
    private static final String WARN                             = "WARN";
    private static final String ERROR                            = "ERROR";
    private static final String FATAL                            = "FATAL";
    private static final String CONSOLE                          = "CONSOLE";
    private static final String LOGFILE                          = "LOGFILE";
    private static final String TRACEFILE                        = "TRACEFILE";
    
    /** The categories */
    private List categoriesToEnable = new ArrayList();
    private List categoriesToDisable = new ArrayList();
    private boolean enableAllCategories = false;
    private boolean disableAllCategories = false;
    
    /** The output medium */
    private String output = CONSOLE; //CONSOLE, LOGFILE, TRACEFILE
    
    /** TWS home - required to locate log4j.properties */
    private String taminowebdavserverHome = null;
    
    /** Collect categories for which additivity has been specified */
    private List additivityList = null;
    
    private final PrintStream verbose;
    
    public Log4jConfig(PrintStream verbose) {
        this.verbose = verbose;
    }
    
    /**
     * Set category.
     * @param category the category
     */
    public void setCategory( String category ) {
        StringTokenizer tr = new StringTokenizer( category, "+-", true );
        String tok;
        String cat = "";
        while( tr.hasMoreTokens() ) {
            tok = tr.nextToken();
            if( "+".equals(tok) || "-".equals(tok) )
                cat = tr.nextToken();
            else
                cat = tok;
            
            if( cat.startsWith(".") )
                cat = ALL_CAT+cat;
            
            if( "+".equals(tok) ) {
                if( "*".equals(cat) || ALL_CAT.equals(cat) ) {
                    enableAllCategories = true;
                    categoriesToEnable.clear();
                    categoriesToEnable.add( ALL_CAT );
                    break;
                }
                else {
                    categoriesToEnable.add( cat );
                }
            }
            else if( "-".equals(tok) ) {
                if( "*".equals(cat) ) {
                    disableAllCategories = true;
                    categoriesToDisable.clear();
                    break;
                }
                else {
                    categoriesToDisable.add( cat );
                }
            }
            else {
                if( "*".equals(cat) || ALL_CAT.equals(cat) ) {
                    enableAllCategories = true;
                    categoriesToEnable.clear();
                    categoriesToEnable.add( ALL_CAT );
                    break;
                }
                else {
                    categoriesToEnable.add( cat );
                }
            }
        }
    }
    
    /**
     * Set output.
     * @param output the output
     */
    public void setOutput( String output ) {
        this.output = output;
    }
    
    /**
     * Set taminowebdavserverHome.
     * @param taminowebdavserverHome the taminowebdavserverHome
     */
    public void setTaminowebdavserverHome( String taminowebdavserverHome ) {
        this.taminowebdavserverHome = taminowebdavserverHome;
    }
    
    /**
     * Execute the task
     * @exception BuildException if "execute" goes wrong
     */
    public void execute() throws Abort, IOException {
        File dir = new File( taminowebdavserverHome+SEP+WEB_INF+SEP+CLASSES );
        File file = new File( dir, LOG4JPROP );
        
        log( "Starting trace configuration:" );
        log( "  config file: "+file );
        if( enableAllCategories )
            log( "  enabling all categories" );
        if( disableAllCategories )
            log( "  disabling all categories" );
        if( !enableAllCategories && !categoriesToEnable.isEmpty() )
            log( "  categories to enable: "+categoriesToEnable );
        if( !disableAllCategories && !categoriesToDisable.isEmpty() )
            log( "  categories to disable: "+categoriesToDisable );
        
        log( "  output: "+output );
        
        List lines;
        BufferedWriter writer = null;
        String line = null;
        Iterator iter;
        
        lines = Files.readLines(file);
        // collect first categories with additivity
        additivityList = new ArrayList();
        
        iter = lines.iterator();
        while( iter.hasNext() ) {
            line = (String) iter.next();
            if( line.startsWith(LOG4J_ADD) ) {
                String cat = getCategory( line );
                if( cat != null )
                    additivityList.add( cat );
            }
        }
        
        writer = new BufferedWriter( new FileWriter(file) );
        
        int ln = 0;
        
        iter = lines.iterator();
        while( iter.hasNext() ) {
            line = (String) iter.next();
            ln++;
            
            if( line.startsWith(LOG4J_CAT) ) {
                String cat = getCategory(line);
                String prio = getPriority(line);
                if( cat != null ) {
                    if( disableAllCategories ) {
                        if( DEBUG.equals(prio) )
                            if( ALL_CAT.equals(cat) )
                                line = modifyLine( line, cat, INFO, CONSOLE );
                            else
                                line = modifyLine( line, cat, INFO, (String)null );
                    }
                    else if( categoriesToEnable.contains(cat) ) {
                        line = modifyLine( line, cat, DEBUG, output );
                        categoriesToEnable.remove(cat);
                    }
                    else if( categoriesToDisable.contains(cat) ) {
                        if( DEBUG.equals(prio) )
                            if( ALL_CAT.equals(cat) )
                                line = modifyLine( line, cat, INFO, CONSOLE );
                            else
                                line = modifyLine( line, cat, INFO, (String)null );
                    }
                }
            }
            
            writer.write( line );
            writer.newLine();
        }
        
        if( !categoriesToEnable.isEmpty() ) {
            Iterator i = categoriesToEnable.iterator();
            while( i.hasNext() ) {
                String cat = (String)i.next();
                line = createLine( cat, DEBUG, output );
                writer.write( line );
                writer.newLine();
            }
        }
        
        writer.close();
    }
    
    /**
     * Return category of category- or additivity line, null else.
     */
    private String getCategory( String line ) {
        String result = null;
        int eq = line.indexOf( '=' );
        if( eq > 0 ) {
            // get the category
            if( line.startsWith(LOG4J_CAT) )
                result = line.substring( LOG4J_CAT.length(), eq );
            else if( line.startsWith(LOG4J_ADD) )
                result = line.substring( LOG4J_ADD.length(), eq );
        }
        return result;
    }
    
    /**
     * Return priority of category line, null else.
     */
    private String getPriority( String line ) {
        String result = null;
        if( line.startsWith(LOG4J_CAT) ) {
            int eq = line.indexOf( '=' );
            if( eq > 0 ) {
                int cm = line.indexOf( ',' );
                // get the priority
                try {
                    if( cm > 0 )
                        result = line.substring( eq+1, cm );
                    else
                        result = line.substring( eq+1 );
                }
                catch( IndexOutOfBoundsException x ) {}
            }
        }
        return result;
    }
    
    /**
     * Return true if line starts with ; or #
     */
    private boolean isCommentLine( String line ) {
        return ( line.startsWith(";") || line.startsWith("#") );
    }
    
    /**
     * Configure the given line
     */
    private String modifyLine( String line, String category, String priority, String output ) {
        StringBuffer b = new StringBuffer();
        String[] token = new String[3];
        StringTokenizer t = new StringTokenizer( line, "=," );
        token[0] = t.nextToken();
        token[1] = t.nextToken();
        if( t.hasMoreTokens() )
            token[2] = t.nextToken();
        
        b.append( token[0] );
        
        if( priority != null && priority.length() > 0 )
            b.append( "=" ).append( priority );
        else
            b.append( "=" ).append( token[1] );
        
        if( output != null && output.length() > 0 ) {
            // CONSOLE is default by rootCategory
            if( !CONSOLE.equals(output) ) {
                b.append( "," ).append( output );
                if( !additivityList.contains(category) ) {
                    // disable additivity
                    b.append( "\n" );
                    b.append( LOG4J_ADD+category );
                    b.append( "=" ).append( "false" );
                }
            }
        }
        else if( token[2] != null && token[2].length() > 0 ) {
            //          b.append( "," ).append( token[2] );
            output = token[2];
            if( !CONSOLE.equals(output) ) {
                b.append( "," ).append( output );
                if( !additivityList.contains(category) ) {
                    // disable additivity
                    b.append( "\n" );
                    b.append( LOG4J_ADD+category );
                    b.append( "=" ).append( "false" );
                }
            }
        }
        
        return b.toString();
    }
    
    /**
     * Create a new line
     */
    private String createLine( String category, String priority, String output ) {
        StringBuffer b = new StringBuffer();
        
        b.append( LOG4J_CAT+category );
        b.append( "=" ).append( priority );
        
        if( output != null && output.length() > 0 ) {
            // CONSOLE is default by rootCategory
            if( !CONSOLE.equals(output) ) {
                b.append( "," ).append( output );
                if( !additivityList.contains(category) ) {
                    // disable additivity
                    b.append( "\n" );
                    b.append( LOG4J_ADD+category );
                    b.append( "=" ).append( "false" );
                }
            }
        }
        
        return b.toString();
    }
    
    private void log(String str) {
        verbose.println(str);
    }
}

