/*


    ========== licence begin GPL
    Copyright (C) 2002-2003 SAP AG

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    as published by the Free Software Foundation; either version 2
    of the License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
    ========== licence end


*/

package com.sap.dbtech.util;

import java.io.*;
import java.util.*;
/**
 *
 */
public abstract class Tracer {
    public static final boolean traceAny_C = true;
    public static final int noTrace_C = 0;
    public static final int mediumTrace_C = 0;
    public static final int fullTrace_C = 10;
    private static long maxLinesInTrace = Long.MAX_VALUE;
    private static long curLinesInTrace = 0;
    private static String filename = null;
    private static int traceVerbosity = noTrace_C;
    private static PrintStream nullPrinter = new NullPrinter ();
    private static PrintStream log = nullPrinter;
    public static int maxTraceBuf = 400;
    private static Thread lastThread = null;
    /**
     * Tracer constructor comment.
     */
    private Tracer() {
        super();
    }
    /**
     *
     */
    private static void checkThreadChange () {
        Thread t = Thread.currentThread ();
        if (t != lastThread) {
            lastThread = t;
            log.println ("");
            log.println ("---- Thread " + Integer.toHexString (t.hashCode ())
                    + " " + t.getName ()+" Timestamp: "+(new java.sql.Timestamp(System.currentTimeMillis())));
        }
    }

    private static void checkTracefile () {
      if (curLinesInTrace >= maxLinesInTrace){
        try {
          Tracer.close();
          File oldfile = new File(filename+".old");
          File curfile = new File(filename);
          try {
            oldfile.delete();
          } catch(Exception exc){exc.printStackTrace();}
          curfile.renameTo(oldfile);
          Tracer.log = new PrintStream (new FileOutputStream (Tracer.filename, false));
        }
        catch (IOException ex) {
          //ignore
          ex.printStackTrace();
        }
        curLinesInTrace = 0;
      }
      curLinesInTrace++;
    }
    /**
     *
     */
    static public void close () {
        if (log != null) {
            log.close ();
            log = null;
        }
    }
    /**
     *
     * @param verbosity int
     * @param self java.lang.Object
     * @param name java.lang.String
     */
    public static void entry (int verbosity, Object self, String name) {
        if ((verbosity <= traceVerbosity) && (log != null)) {
            entry (verbosity, self, name, null);
        }
    }
    /**
     *
     * @param verbosity int
     * @param self java.lang.Object
     * @param name java.lang.String
     */
    public static void entry (
        int verbosity,
        Object self,
        String name,
        Object [] args)
    {
        if ((verbosity <= traceVerbosity) && (log != null)) {
            StringBuffer line = new StringBuffer ();
            String selfString;
            String methodString;

            if (self instanceof String) {
                line.append (self);
            }
            else {
                String className = self.getClass ().toString ();
                line.append (className.substring (className.lastIndexOf ('.')));
                line.append ("@");
                line.append (Integer.toHexString((self.hashCode())));
                line.append ("::");
            }
            if (args == null) {
                methodString = name;
            }
            else {
                methodString = StringUtil.sprintfs (name, args);
            }
            line.append (methodString);
            println (line.toString ());
        }
    }
    /**
     *
     * @param verbosity int
     * @param self java.lang.Object
     * @param name java.lang.String
     */
    public static void entry (
        int verbosity,
        Object self,
        String name,
        int arg1)
    {
        if ((verbosity <= traceVerbosity) && (log != null)) {
            entry (verbosity, self, name, new Object [] {new Integer (arg1)});
        }
    }
    /**
     *
     * @param verbosity int
     * @param self java.lang.Object
     * @param name java.lang.String
     */
    public static void entry (
        int verbosity,
        Object self,
        String name,
        int arg1,
        int arg2)
    {
        if ((verbosity <= traceVerbosity) && (log != null)) {
            entry (verbosity, self, name,
                new Object [] {new Integer (arg1), new Integer (arg2)});
        }
    }
    /**
     *
     * @param verbosity int
     * @param self java.lang.Object
     * @param name java.lang.String
     */
    public static void entry (
        int verbosity,
        Object self,
        String name,
        Object arg1)
    {
        if ((verbosity <= traceVerbosity) && (log != null)) {
            entry (verbosity, self, name, new Object [] {arg1});
        }
    }
    /**
     *
     * @param verbosity int
     * @param self java.lang.Object
     * @param name java.lang.String
     */
    public static void entry (
        int verbosity,
        Object self,
        String name,
        Object arg1,
        Object arg2)
    {
        if ((verbosity <= traceVerbosity) && (log != null)) {
            entry (verbosity, self, name, new Object [] {arg1, arg2});
        }
    }
    /**
     *
     * @param verbosity int
     * @param self java.lang.Object
     * @param name java.lang.String
     */
    public static void entry (
        int verbosity,
        Object self,
        String name,
        Object arg1,
        Object arg2,
        Object arg3)
    {
        if ((verbosity <= traceVerbosity) && (log != null)) {
            entry (verbosity, self, name, new Object [] {arg1, arg2, arg3});
        }
    }
    /**
     *
     * @param verbosity int
     * @param self java.lang.Object
     * @param name java.lang.String
     */
    public static void entry (
        int verbosity,
        Object self,
        String name,
        Object arg1,
        Object arg2,
        Object arg3,
        Object arg4)
    {
        if ((verbosity <= traceVerbosity) && (log != null)) {
            entry (verbosity, self, name, new Object [] {arg1, arg2, arg3, arg4});
        }
    }
    /**
     *
     */
    public static void flush () {
        log.flush ();
    }
    /**
     *
     * @param line java.lang.String
     */
    public static void forceLine (String line) {
        try {
            PrintWriter writer = new PrintWriter (new FileWriter ("c:/temp/va.prt", true));
            writer.println (line);
            writer.close ();
        }
        catch (IOException ioExc) {
            // ignore
        }
    }
    /**
     *
     * @param log java.io.PrintStream
     */
    public static void forceNewLog (String filename)
        throws IOException
    {
        forceNewLog(filename, true);
    }
    /**
     *
     * @param log java.io.PrintStream
     */
    public static void forceNewLog (String afilename, boolean append)
        throws IOException
    {
        forceNewLog(afilename, append, Long.MAX_VALUE);
    }

    public static void forceNewLog (String afilename, boolean append, long maxSize)
        throws IOException
    {
        Tracer.log = new PrintStream (new FileOutputStream (afilename, append));
        Tracer.maxLinesInTrace = maxSize;
        Tracer.filename = afilename;
    }
    /**
     *
     * @return java.io.PrintStream
     */
    public static PrintStream getLog () {
        return log;
    }
    /**
     *
     * @return boolean
     * @param verbosity int
     */
    public static boolean isOn (int verbosity) {
        return verbosity <= traceVerbosity;
    }
    /**
     *
     * @param log java.io.PrintStream
     */
    public static void openLog (String filename)
        throws IOException
    {
        if ((log == nullPrinter) || (log == null) || (log == System.out)) {
            Tracer.forceNewLog(filename);
        }
    }
    /**
     *
     * @param text java.lang.String
     */
    static public synchronized void println (String text) {
        checkTracefile();
        checkThreadChange();
        log.println (text);
    }
    /**
     *
     * @param text java.lang.String
     */
    static public synchronized void print (String text) {
        checkTracefile();
        checkThreadChange();
        log.print (text);
    }
    /**
     *
     * @param text java.lang.String
     */
    static public synchronized void print (String text, java.util.Properties info) {
       String val;
       checkTracefile();
       checkThreadChange();
       log.println (text);
       if (info == null) return;
       for (Enumeration e = info.keys() ; e.hasMoreElements() ;) {
            String key = (String)e.nextElement();
            if (key.equals("password")){
              val = "***";
            } else {
              val = info.getProperty(key);
            }
            if (val != null && val.length() > 40) {
                val = val.substring(0, 37) + "...";
            }
           log.println(key + "=" + val);
       }
    }
    /**
     *
     * @param log java.io.PrintStream
     */
    public static void setLog (PrintStream log) {
        Tracer.log = log;
    }
    /**
     *
     * @return int
     * @param verbosity int
     */
    public static int setVerbosity (int verbosity) {
        int result = traceVerbosity;
        traceVerbosity = verbosity;
        return result;
    }
    /**
     *
     * @param line java.lang.String
     */
    public static synchronized void trace (String line) {
        if (log != null) {
            checkThreadChange();
            log.println (line);
        }
    }
    /**
     *
     * @param exc java.lang.Throwable
     */
    public static synchronized void traceException (Throwable exc) {
        if (log != null) {
            checkThreadChange ();
            log.println ("Timestamp: "+(new java.sql.Timestamp(System.currentTimeMillis())));
            exc.printStackTrace (log);
        }
    }
    /**
     *
     * @param format java.lang.String
     * @param args java.lang.Object[]
     */
    public static void tracef (String format, Object [] args) {
        if (log != null) {
            StringUtil.fprintfs (log, format, args);
        }
    }
    /**
     *
     * @param comment java.lang.String
     * @param obj Traceable
     * @param verbosity int
     */
    public static void traceObject (String comment, Traceable obj) {
        traceObject (comment, obj, traceVerbosity);
    }
    /**
     *
     * @param comment java.lang.String
     * @param obj Traceable
     * @param verbosity int
     */
    public static synchronized void
    traceObject (
        String comment,
        Traceable obj,
        int verbosity)
    {
        int maxBuf;

        if (log != null) {
            checkThreadChange();
            if (comment != null) {
                trace (comment);
            }
            if (verbosity <= traceVerbosity) {
                obj.traceOn (log);
            }
            else {
                obj.traceOn (log, maxTraceBuf);
            }
        }
    }
    /**
     *
     */
    public static void
    whereAmI ()
    {
        if (log != null) {
            checkThreadChange ();
            Tracer.println ("whereAmI");//#print
            Throwable thr = new Throwable();
            thr.printStackTrace(log);
        }
    }

   public static synchronized void dumpLocks(java.sql.Connection conn) throws java.sql.SQLException{
    log.println (
        dumpLockAsString(conn)
    );
   }

   public static synchronized String dumpLockAsString(java.sql.Connection conn) throws java.sql.SQLException{
     checkTracefile();
     checkThreadChange();
     StringBuffer sb = new StringBuffer(200);
     java.sql.Statement lockstmt = conn.createStatement();
     java.sql.ResultSet rs = lockstmt.executeQuery("SELECT SESSION, TABLENAME, ROWID, ROWIDHEX, REQMODE, REQSTATE, LOCKMODE, LOCKSTATE FROM lockstatistics");
       sb.append("DUMP LOCKSTATISTICS\n");
       if (rs.next()){
         sb.append("SESSION;TABLENAME;ROWID;ROWIDHEX;REQMODE;REQSTATE;LOCKMODE;LOCKSTATE\n");
         do {
           sb.append (rs.getString("SESSION")+";"
                      +rs.getString("TABLENAME")+";"
                      +rs.getString("ROWID")+";"
                      +Hex2String(rs.getBytes("ROWIDHEX"))+";"
                      +rs.getString("REQMODE")+";"
                      +rs.getString("REQSTATE")+";"
                      +rs.getString("LOCKMODE")+";"
                      +rs.getString("LOCKSTATE")+"\n");
         }while (rs.next());
       }
       else
         sb.append("=>no locks found\n");

       rs.close();
       return sb.toString();
  }
   
  public static String Hex2String(byte[] bytearr){
      if (bytearr==null)
        return "null";
      StringBuffer sb = new StringBuffer();

      for(int k=0; k<bytearr.length; k++){
           if ((bytearr[k]& 0xFF) <16)
            sb.append("0");
           sb.append( Integer.toString(bytearr[k]& 0xFF,16) );
      }
      return sb.toString();

   }


  public static byte[] String2Hex(String bytearr){
  
      if (bytearr==null || bytearr.length()%2 != 0)
        return new byte[]{};
      
      byte[] dest = new byte[bytearr.length()/2];
      
      for (int i = 0; i < dest.length; i++) {
        String val =   bytearr.substring(2*i,2*i+2);
        dest[i] = (byte) Integer.parseInt(val,16);  
      }
      return dest;      
   }
}
