package util;

import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.InputStreamReader;
import java.io.Serializable;
import java.net.URL;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Random;
import java.util.Vector;

import oracle.jdbc.driver.OracleConnection;
import oracle.jdbc.driver.OracleStatement;
import conf.ConfigConstants;
import bingo.crawler.BINGOCrawler;
import bingo.crawler.handler.parser.StemmerDriver;
import bingo.db.DBFrame;
import bingo.db.DBInterface;
import bingo.db.MySQLInterface;
import bingo.db.OracleInterface;
import edu.brandeis.cs.steele.wn.DictionaryDatabase;
import edu.brandeis.cs.steele.wn.FileBackedDictionary;
import edu.brandeis.cs.steele.wn.IndexWord;
import edu.brandeis.cs.steele.wn.POS;
import edu.brandeis.cs.steele.wn.Synset;

/**
 *  Die Klasse dient als Zwischenspeicher für diverse Objekte, auf die das
 *  System zur Laufzeit zugreift. Zudem werden systemweit gültige Konstanten
 *  definiert. Darüber hinaus ist das Objekt zuständig für den Aufbau und
 *  Abschluss der Oracle-Datenbankverbindung inklusive der Benutzer und
 *  Login-Verwaltung. Da zur Laufzeit nur genau eine Instanz dieses Objekts
 *  existieren darf, ist es als Singelton implementiert.
 */
public class SessionBuffer implements Serializable {
    
    private static ConfigConstants cc = ConfigConstants.getInstance();
    
    public boolean bShowParserDialog;
    public boolean bParserBreak;
    private HashSet stopWords;
    private Vector baseURLBuffer = new Vector();
    private HashSet registeredObjects = new HashSet();
    private BingoTreeNode rootNode = null;
    private HashMap nodeClassifiers = null;
    private Random random = new Random( new java.util.Date().getTime() );
    public static DBInterface dbinterface = null;
    public static oracle.jdbc.driver.OracleDriver oracleDriver = null;
    public static com.mysql.jdbc.Driver mySQLDriver = null;
    public static BINGOCrawler crawler;
    
    private static boolean traceFlag = true; // schaltet den StackTrace an oder aus; default true
    private static int logLevel; // maximaler Level, bis zu dem Meldungen ausgegeben werden
    
    //Systemweite Einstellungen
    public static String langFile;
    public static int numDF;
    public static int numMI;
    public static int numHITS;
    public static int linksPerDoc;
    public static boolean ignoreNoSparse;
    
    public static int maxCrawlerThreads;
    public static int downloadTimeout;
    public static int maxVisiblePages; // Anzahl der Seiten, die im Webgraph angezeigt werden vor Refresh
    
    public static boolean offlineCrawling;
    public static boolean hostCrawling;
    
    public static int previewLength = 500;
    public static int titleLength = 100;
    
    public static int batchSize;
    public static int chunkSize;
    
    public static boolean useGoogle;
    public static int numGoogleLink;
    public static int numGoogleRelated;
    
    public static HashMap allMimes;
    public static HashMap allowedMimes;
    
    public static int useSVMClassifier;
    
    public static boolean useLinkTable;
    
    public static boolean saveStopwordTerms;
    
    public static URL   sStopwordFile,
    sBookmarkFile,
    sFavoriteFile,
    sDefaultsFile,
    sFeatureFile,
    sClassifierFile,
    sAllowedMimesFile,
    sAllMimesFile;
    
    /**
     *  Der Name der Tabelle mit allen erfassten Dokumenten.
     */
    public static String sTable = "bingodocuments";
    /**
     *  Eine Fehlermeldung.
     */
    public static String errorMsg = null;
    /**
     *  Timeout-Zeit für eine TimedSocket in Millis.
     */
    public static int timeoutDelay;
    /**
     *  Maximal erlaubte Hierarchietiefe (Höhe des Baums).
     */
    public static int maxDepth = 100;
    
    public static int maxCrawlingDepth;
    
    /**
     *  Datenbankverbindung ja/nein.
     */
    public static boolean dbConnect = false;
    
    /**
     *  Sollen die Quelltexte mitgespeichert werden?
     */
    public static boolean storeStreams;
    
    /**
     *  Benutzte Stemming.
     */
    public static boolean bSTEM;
    
  /*
   * Stemmer language (english, german.. see snowball package for details)
   */
    public static String stemmerLanguage;
    public static Class stemClass = null;
    
    public static int repeatStemmer;
    
    
    public String getStemmerLanguage() {
        return stemmerLanguage;
    }
    
    public void setPreviewLength(int i) {
        previewLength = i;
    }
    
    public int getPreviewLength() {
        return previewLength;
    }
    
    public void setTitleLength(int i) {
        titleLength = i;
    }
    
    public int getTitleLength() {
        return titleLength;
    }
    
    
    public void setStemmerLanguage( String language )
	   {
	      try
	      {
	         this.stemClass = this.getClass().getClassLoader().loadClass( "net/sf/snowball/ext/" + language + "Stemmer" );
	         this.stemmerLanguage = language;
	         sStopwordFile = cl.getResource( dataDir + "stopwords/" + stemmerLanguage + "/" + cc.get( "sStopwordFile" ) );
	         setStopwords();
	         
	         if (dbConnect)
	           DBInterface.getInstance().pushStopwords();
	 
	      }
	      catch ( Exception e )
	      {
	         log(e);
	      }
	   }
    
    /**
     *  Lösche Stoppwörter.
     */
    public static boolean bCUT;
    /**
     *  Benutze WordNet Dictionary.
     */
    public static boolean bWN;
    /**
     *  Beachte Dokumente pro Knoten oder global.
     */
    private static boolean obeyLevel;
    /**
     *  Feature-Selektion auf Basis der Oberkategorie oder direkt im Kindknoten
     *  (restriktiver, da weniger Terme)
     */
    private static boolean useUnionSelect;
    /**
     *  Benutze eine Logdatei 'session.log'.
     */
    public static boolean useLog;
    /**
     *  Der Standard-Schwellwert für SVM-Modelle. Der ermittelte Abstand zur
     *  Hyperebene muss für ein positives Klassifikationsergbenis eines Dokuments
     *  über diesem Wert liegen.
     */
    public static double dThreshold;
    /**
     *  Der Oracle-Host.
     */
    public static String dbHost;
    
    public static String dbType = null;// oracle oder mysql
    
    /**
     *  Der Service-Name für die Oracle-Verbindung.
     */
    public static String sServiceName;
    /**
     *  Das Datenverzeichnis.
     */
    public static String dataDir = null;
    /**
     *  Benutzername.
     */
    public static String defaultUser;
    
    public static String defaultUserPasswd;
    
    private static int modellingType;
    
    private static BufferedWriter logFile;
    /**
     *  Die Oracle-Verbindung.
     */
    //public static OracleConnection oracleConnection = null;
    public static Connection dbConnection = null;
    
    /**
     *  Maximale Grösse der Crawler-Queue in der DB. Normalerweise unbeschränkt.
     */
    public static int maxQueueSize;
    
    public static boolean hardFocus;
    public static boolean softFocus;
    
    public static int maxTunnelDistance;
    // max laenge der Bruecke
    
    private DictionaryDatabase dictionary;
    private Hashtable cachedSynonyms;
    
    // Singelton
    private static SessionBuffer sessionBuffer = null;
    
    public int featuresPerClass;
    
    public int defaultFrameHeight;
    public int defaultFrameWidth;
    
    private ClassLoader cl;
    
    protected SessionBuffer() {
        this.dataDir = "bingo/data/";
        
        // Variablen initialisieren
        this.initSettings();
        
        cl = this.getClass().getClassLoader();
        
        sStopwordFile = cl.getResource( dataDir + "stopwords/" + stemmerLanguage + "/" + cc.get( "sStopwordFile" ) );
        sBookmarkFile = cl.getResource( dataDir + cc.get( "sBookmarkFile" ) );
        sFavoriteFile = cl.getResource( dataDir + cc.get( "sFavoriteFile" ) );
        sDefaultsFile = cl.getResource( dataDir + cc.get( "sDefaultsFile" ) );
        sFeatureFile  = cl.getResource( dataDir + cc.get( "sFeatureFile" ) );
        sClassifierFile  = cl.getResource( dataDir + cc.get( "sClassifierFile" ) );
        sAllowedMimesFile = cl.getResource( dataDir + cc.get( "sAllowedMimesFile" ) );
        sAllMimesFile = cl.getResource( dataDir + cc.get( "sAllMimesFile" ) );
        
        if ( useLog ) {
            initLog();
        }
        
        if ( bWN ) {
            dictionary = new FileBackedDictionary();
            cachedSynonyms = new Hashtable( 10000 );
        }
        
        if ( allMimes == null ) {
            allMimes = readAllMimes();
        }
        
        if ( allowedMimes == null ) {
            allowedMimes = readAllowedMimes();
        }
        
        try {
            GraphicsDevice device = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
            
            defaultFrameHeight = device.getDisplayMode().getHeight() * 4 / 7;
            defaultFrameWidth = device.getDisplayMode().getWidth() * 5 / 7;
        }
        catch ( Error any ) {
            defaultFrameHeight = 768;
            defaultFrameWidth = 1024;
        }
        setStemmerLanguage( stemmerLanguage );
    }
    
    
  /*
   *  Returns the max allowed size for the given datatype (HTML, PDF...)
   */
    public static int getMaxsizeFor( String mime ) {
        try {
            if ( allowedMimes == null ) {
                return 0;
            }
            return ( (MimeType) allowedMimes.get( mime ) ).getMaxSize();
        }
        catch ( NullPointerException e ) {
            System.err.println( "ContentType <" + mime + "> not found in allowed mimes!" );
            return 0;
        }
    }
    
    
    /**
     *  Initialisiert die Log-Datei 'session.log'.
     */
    public static void initLog() {
        try {
            logFile = new BufferedWriter( new FileWriter( System.getProperty( "user.dir" ) + System.getProperty( "file.separator" ) + "session.log", false ) );
            logFile.newLine();
            logFile.write( "LOG-FILE-STARTUP: " + new java.util.Date().toString() );
            logFile.newLine();
            logFile.newLine();
            logFile.flush();
            useLog = true;
        }
        catch ( Exception any ) {
            log(any);
        }
    }
    
    
    /**
     *  Setzt das bSTEM Attribut, das angibt, ob der Parser zusätzlich Stemming
     *  von Wörtern verwenden soll.
     *
     * @param  b  Der Wert.
     */
    public void setSTEM( boolean b ) {
        this.bSTEM = b;
    }
    
    
    /**
     *  Setzt das bCUT Attribut, das angibt, ob der Parser zusätzlich Stoppwörter
     *  löschen soll.
     *
     * @param  b  Der Wert.
     */
    public void setCUT( boolean b ) {
        this.bCUT = b;
    }
    
    
    /**
     *  Legt fest, ob beim Modellieren nur URLs des jeweiligen Vaterknotens als
     *  Diskrimatoren benutzt werden sollen, oder ob für jeden Knoten immer alle
     *  URLs der Hierarchie gegenübergestellt werden.
     *
     * @param  b  Der Wert.
     */
    public void setObeyLevel( boolean b ) {
        this.obeyLevel = b;
    }
    
    
    /**
     *  Legt fest, ob beim Modellieren Klassifizieren eines Dokuments
     *  Feature-Selektion auf der Vereiningung der Feature-Räume der
     *  Unterkategorien oder direkt im Kindknoten (restriktiver, da weniger Terme)
     *  gewählt werden soll.
     *
     * @param  b  Der Wert.
     */
    public void setUseUnionSelect( boolean b ) {
        this.useUnionSelect = b;
    }
    
    
    /**
     *  Liefert, ob beim Modellieren Klassifizieren eines Dokuments
     *  Feature-Selektion auf der Vereiningung der Feature-Räume der
     *  Unterkategorien oder direkt im Kindknoten (restriktiver, da weniger Terme)
     *  gewählt werden soll.
     *
     * @return    useUnionSelect Der Wert.
     */
    public boolean getUseUnionSelect() {
        return useUnionSelect;
    }
    
    
    /**
     *  Setzt das bShowParserDialog Attribut.
     *
     * @param  b  The new ShowParserDialog value
     */
    public void setShowParserDialog( boolean b ) {
        this.bShowParserDialog = b;
    }
    
    
    /**
     *  Setzt das bParserBreak Attribut.
     *
     * @param  b  Der Wert.
     */
    public void setParserBreak( boolean b ) {
        this.bParserBreak = b;
    }
    
    
    /**
     *  Parst und lädt Stoppwörter mit Hilfe des Objekts StopwordParser.
     */
    private void setStopwords()
	   {
	      try
	      {
	         log( "Stopwords: " + sStopwordFile, 3 );
	         File file = new File (dataDir + "stopwords/" + stemmerLanguage + "/" + cc.get( "sStopwordFile" ));
	         char[] buffer = new char[(int)file.length()];
	         FileReader fr = new FileReader (file);
	         fr.read(buffer, 0, buffer.length);
	         fr = null;file = null;
	        
	         stopWords = new HashSet();
	         ArrayList tokens = StemmerDriver.parse(buffer);
	         buffer = null;
	         
	         
	         Iterator it = tokens.iterator();
	         while (it.hasNext())
	         {   
	               String token = (String)it.next();
	               // token = stem( token );
	               
	               if ( token != null && token.length() > 0 )
	               {
	                  stopWords.add( token );
	               }

	         }
	         it = null;
	         tokens = null;
	         
	         log( " --> " + stopWords.size() + " STOPWORDS.", 3 );
	         
	         //            if ( dbinterface != null )
	         //                dbinterface.pushStopwords();
	      }
	      catch ( Exception e )
	      {
	         log(e);
	      }
	   }
    
    
    /**
     *  Definiert die Termgewichtung von Features beim Modellieren und
     *  Klassifizieren. Entweder TF, TF* IDF oder TF/maxTF * log( IDF ).
     *
     * @param  i  Der Wert.
     */
    public void setModellingType( int i ) {
        this.modellingType = i;
    }
    
    
    /**
     *  Definiert den Wurzelknoten des Hierachiebaums und damit referenziert den
     *  gesamten Baum.
     *
     * @param  tn  Der Wurzelknoten.
     */
    public void setRootNode( BingoTreeNode tn ) {
        this.rootNode = tn;
        treeUpdated();
    }
    
    
    /**
     *  Definiert den Vektor mit SVM-Modellen zu den einzelnen Knoten des
     *  Hierarchiebaums.
     *
     * @param  nm  Der Vektor mit SVM-Modellen.
     */
    public void setNodeClassifiers( HashMap nm ) {
        this.nodeClassifiers = nm;
    }
    
    /**
     *  Liefert den Vektor mit allen zwischengespeicherten BaseURL-Objekten.
     *
     * @return    Der Vektor.
     */
    public Vector getURLBuffer() {
        return baseURLBuffer;
    }
    
    /**
     *  Prüft einen String auf Stoppwort.
     *
     * @param  s  Description of the Parameter
     * @return    The stopword value
     */
    public boolean isStopword( String s ) {
        return ( stopWords.contains( s ) );
    }
    
    
    public Iterator getStopwords() {
        return stopWords.iterator();
    }
    
    
    public String lookupDictionary( String s ) {
        String cSyn = (String) cachedSynonyms.get( s );
        
        try {
            if ( cSyn != null ) {
                return cSyn;
            } else {
                for ( int i = 0; i < POS.CATS.length; i++ ) {
                    IndexWord word = dictionary.lookupIndexWord( POS.CATS[i], s );
                    
                    if ( word != null ) {
                        Synset[] senses = word.getSenses();
                        Synset sense = senses[0];
                        String syn = sense.getWord( 0 ).getDescription().toLowerCase();
                        
                        log( "WN:     " + s + " --> " + syn, 4 );
                        cachedSynonyms.put( s, syn );
                        return syn;
                    }
                }
            }
        }
        catch ( Exception any )
        {}
        return s;
    }
    
    
    public void setUseWordNet( boolean b ) {
        if ( b ) {
            dictionary = new FileBackedDictionary();
            if ( cachedSynonyms == null ) {
                cachedSynonyms = new Hashtable( 10000 );
            }
        } else {
            dictionary = null;
        }
        bWN = b;
    }
    
    
    /**
     *  Liefert die Modellierungsmethode.
     *
     * @return    Der Wert.
     */
    public int getModellingType() {
        return modellingType;
    }
    
    
    /**
     *  Liefert den Wurzelknoten des Hierachiebaums und damit referenziert den
     *  gesamten Baum.
     *
     * @return    Der Wurzelknoten.
     */
    public BingoTreeNode getRootNode() {
        if (rootNode == null)
            rootNode = new BingoTreeNode("ROOT");
        
        return rootNode;
    }
    
    
    /**
     *  Liefert den Vektor mit SVM-Modellen zu den einzelnen Knoten des
     *  Hierarchiebaums.
     *
     * @return    Der Vektor.
     */
    public HashMap getNodeClassifiers() {
        return nodeClassifiers;
    }
    
    
    /**
     *  Löscht die Zwischenablage.
     */
    public void clearURLBuffer() {
        this.baseURLBuffer = new Vector();
    }
    
    
    /**
     *  Setzt ein Objekt in die Zwischenablage.
     *
     * @param  baseURL  Die BaseURL.
     */
    public void addURLtoBuffer( Object baseURL ) {
        baseURLBuffer.addElement( baseURL );
    }
    
    
    /**
     *  Setzt einen Vektor von Objekten als Zwischenablage.
     *
     * @param  vURLs  The new uRLBuffer value
     */
    public void setURLBuffer( Vector vURLs ) {
        baseURLBuffer = vURLs;
    }
    
    
    /**
     *  Registriert ein Objekt vom Typ TreeUpdateListener zur Benachrichtigung bei
     *  Änderung der Trainingsdaten.
     *
     * @param  l  Der Listener.
     */
    public void registerForTreeUpdating( TreeUpdateListener l ) {
        registeredObjects.add( l );
    }
    
    public void treeUpdated() {
        if (registeredObjects == null || registeredObjects.size() <= 0)
            return;
        Iterator it = registeredObjects.iterator();
        while (it.hasNext()) {
            TreeUpdateListener tmp = (TreeUpdateListener)it.next();
            tmp.updateTree(rootNode);
            tmp=null;
        }
        it=null;
    }
    
    
    
    /**
     *  Schliesst die OracleConnection.
     */
    public void closeDB() {
        if ( dbConnect ) {
            try {
                dbinterface.close();
                DBFrame.close();
                //oracleConnection.close();
                dbConnection.close();
                log( "DB-Verbindung: " + sServiceName + ":" + dbHost + " geschlossen.", 2 );
            }
            catch ( Exception e ) {
                log(e);
            }
        }
    }
    
    
    /**
     *  Speichert eine Fehlermeldung.
     *
     * @param  s  Die neue Fehlermeldung.
     */
    public static void setErrorMsg( String s ) {
        errorMsg = s;
    }
    
    
    /**
     *  Erstellt eine OracleConnection über die JDBC-Schnittstelle
     *  (jdbc.driver.OracleDriver).
     *
     * @return    Die OracleConnection.
     */
    public static synchronized OracleConnection getOracleConnection() {
        OracleConnection oc = null;
        
        if ( dbType == null ) {
            dbType = cc.get( "dbType" );
        }
        if ( dbType.equals( "oracle" ) ) {
            try {
                if ( oracleDriver == null ) {
                    oracleDriver = new oracle.jdbc.driver.OracleDriver();
                    DriverManager.registerDriver( oracleDriver );
                }
                oc = (OracleConnection) DriverManager.getConnection(
                "jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=" + dbHost + ")(PORT=1521)))(CONNECT_DATA=(SERVICE_NAME=" + sServiceName + ")))",
                defaultUser,
                defaultUserPasswd
                );
                oc.setAutoCommit( false );
                log( "New Connection " + sServiceName + ":" + dbHost + " established.", 2 );
                dbConnect = true;
                
            }
            catch ( SQLException q ) {
                log(q);
                dbConnect = false;
                
                q.printStackTrace();
                System.exit(1);
            }
        }
        
        return oc;
    }
    
    
    public static synchronized Connection getMySQLConnection() {
        Connection con = null;
        
        if ( dbType == null ) {
            dbType = cc.get( "dbType" );
        }
        if ( dbType.equals( "mysql" ) ) {
            try {
                if ( mySQLDriver == null ) {
                    mySQLDriver = new com.mysql.jdbc.Driver();
                    DriverManager.registerDriver( mySQLDriver );
                }
                String url = "jdbc:mysql://" + dbHost + "/" + sServiceName;
                
                con = DriverManager.getConnection( url, defaultUser, defaultUserPasswd );
                con.setAutoCommit( false );
                log( "New Connection " + sServiceName + ":" + dbHost + " established.", 2 );
                dbConnect = true;
                
            }
            catch ( Exception e ) {
                log(e);
            }
        }
        return con;
    }
    
    
    public static synchronized Connection getDBConnection() {
        if ( dbType == null ) {
            dbType = cc.get( "dbType" );
        }
        if ( dbType.equals( "oracle" ) ) {
            return getOracleConnection();
        } else {
            return getMySQLConnection();
        }
    }
    
    
    /**
     *  Entscheidet ob beim Modellieren nur URLs des jeweiligen Vaterknotens als
     *  Diskrimatoren benutzt werden sollen, oder ob für jeden Knoten immer alle
     *  URLs der Hierarchie gegenübergestellt werden.
     *
     * @return    Der Wert.
     */
    public static boolean getObeyLevel() {
        return obeyLevel;
    }
    
    
    /**
     *  Liefert eine zwischengespeicherte Fehlermeldung.
     *
     * @return    Die Fehlermeldung.
     */
    public static String getErrorMsg() {
        return errorMsg;
    }
    
    
    /**
     *  Erzeugt eine Zufallszahl (Integer), die in einem gebenenen Intervall liegt
     *  und die nicht bereits in einem Vector von Zahlen vorhanden ist.
     *
     * @param  excluded  Der Vector mit auszuschliessenden Zahlen
     * @param  lower     Die untere Schranke des Intervalls.
     * @param  upper     Die obere Schranke des Intervalls.
     * @return           Die Zufallszahl.
     */
    public int getRandom( Vector excluded, int lower, int upper ) {
        Integer i = new Integer( 0 );
        boolean bFound = false;
        
        do {
            bFound = false;
            i = new Integer( Math.abs( ( random.nextInt() + lower ) % upper ) );
            for ( Enumeration enum = excluded.elements(); enum.hasMoreElements();  ) {
                if ( i.intValue() == ( (Integer) enum.nextElement() ).intValue() ) {
                    bFound = true;
                    break;
                }
            }
        } while ( bFound );
        
        return i.intValue();
    }
    
    
    /**
     *  Liefert ein neues Statement für eine DB-Verbindung
     *
     * @return    Das Statement.
     */
    public static OracleStatement getStatement() {
        try {
            //return (OracleStatement)oracleConnection.createStatement();
            return (OracleStatement) dbConnection.createStatement();
        }
        catch ( Exception any ) {
            return null;
        }
    }
    
    
    /**
     *  Methode zum Aufruf der (einzigen) Instanz der Klasse. Es wird keine
     *  Datenbankverbindung aufgebaut.
     *
     * @return    Die Instanz.
     */
    public static synchronized SessionBuffer getInstance() {
        if ( sessionBuffer == null ) {
            sessionBuffer = new SessionBuffer();
        }
        return sessionBuffer;
    }
    
    
    /**
     *  Methode zum Aufruf der (einzigen) Instanz der Klasse. Es wird eine
     *  Datenbankverbindung zu dem spezififizierten Oracle-Host aufgebaut. Dieser
     *  Verbindungsaufbau ist nur einmal zur Laufzeit erlaubt.
     *
     * @param  host         Der Host.
     * @param  serviceName  Der ServiceName.
     * @param  user         Description of the Parameter
     * @param  pass         Description of the Parameter
     * @return              Die Instanz.
     */
    public static SessionBuffer getInstance( String user, String pass, String host, String serviceName ) {
        defaultUser = user;
        defaultUserPasswd = pass;
        dbHost = host;
        sServiceName = serviceName;
        
        if ( sessionBuffer == null ) {
            sessionBuffer = new SessionBuffer();
        }
        if ( !dbConnect ) {
            //oracleConnection = getOracleConnection();
            dbConnection = getDBConnection();
            if ( dbConnection == null ) {
                System.exit( 0 );
            }
            //dbinterface = new myDBInterface();
            if ( dbType.equals( "oracle" ) ) {
                dbinterface = new OracleInterface();
            } else {
                dbinterface = new MySQLInterface();
            }
        }
        return sessionBuffer;
    }
    
    
    /**
     *  Methode zum Aufruf der (einzigen) Instanz der Klasse. Es wird angegeben,
     *  ob eine Datenbankverbindung zu einem vorher spezififizierten Oracle-Host
     *  aufgebaut werden soll. Dieser Verbindungsaufbau ist nur einmal zur
     *  Laufzeit erlaubt.
     *
     * @param  connect  Benutze DB-Anbindung.
     * @return          Die Instanz.
     */
    public static SessionBuffer getInstance( boolean connect ) {
        if ( sessionBuffer == null ) {
            sessionBuffer = new SessionBuffer();
        }
        
        if ( !dbConnect && connect ) {
            
            LoginDialog lDialog = new LoginDialog();
            
            if ( lDialog.isOK() ) {
                defaultUser = lDialog.getUser();
                defaultUserPasswd = lDialog.getPassword();
                dbHost = lDialog.getHost();
                sServiceName = lDialog.getSID();
                //oracleConnection = getOracleConnection();
                dbConnection = getDBConnection();
                //dbinterface = new myDBInterface();
                if ( dbType.equals( "oracle" ) ) {
                    dbinterface = new OracleInterface();
                }
                else {
                    dbinterface = new MySQLInterface();
                }
            } else {
                defaultUser = null;
                defaultUserPasswd = null;
                dbHost = null;
                sServiceName = null;
            }
            if ( dbHost == null || !connect ) {
                log( "DB-Connection: <none>", 2 );
                dbConnect = false;
                
            }
        }
        
        if ( !dbConnect && !BINGODesktop.isNull() ) {
            BINGODesktop.getInstance().disableDBMenues();
        }
        
        return sessionBuffer;
    }
    
    /** Exception-Ausgabe */
    public static void log(Exception exception) {
        if (traceFlag) exception.printStackTrace();
        else System.err.println(exception.getClass() + ": " + exception.getMessage());
    }
    
    /**
     *  Erstellt eine Log-Ausgabe
     *  Bedeutung der LogLevel:
     *  0: Starten und Stoppen der Anwendung
     *  1: Crawlermeldungen (z.B. "200-->....")
     *  2: Datenbankmeldungen (z.B. "New Connection oracle92:localhost established.")
     *  3: Systemnachrichten (z.B. "--> 352 STOPWORDS.")
     *  >=4: zur freien Verfügung (Debug...)
     *
     * @param String msg: Meldung
     * @param int level: Priorität
     */
    
    public static void log( String msg, int level ) {
        if (level <= logLevel) {
            System.out.println( msg );
            if ( useLog ) {
                try {
                    logFile.write( msg );
                    logFile.newLine();
                    logFile.flush();
                }
                catch ( Exception any ) {
                    log(any);
                    useLog = false;
                }
            }
        }
    }
    
    
    public static int getBatchSize() {
        return batchSize;
    }
    
    
    public static int getChunkSize() {
        return chunkSize;
    }
    
    
    protected void finalize() {
        closeDB();
    }
    
    
    public static BINGOCrawler getCrawler() {
        if ( crawler == null )
            crawler = new BINGOCrawler();
        return crawler;
    }
    
    
    public int getNumGoogleLink() {
        return numGoogleLink;
    }
    
    
    public int getNumGoogleRelated() {
        return numGoogleRelated;
    }
    
    
    public void setNumGoogleLink( int num ) {
        numGoogleLink = num;
    }
    
    
    public void getNumGoogleRelated( int num ) {
        numGoogleRelated = num;
    }
    
    
    public int getFeaturesPerClass() {
        return featuresPerClass;
    }
    
    
    public void setFeaturesPerClass( int num ) {
        featuresPerClass = num;
    }
    
    
    public HashMap readAllMimes() {
        HashMap result = new HashMap();
        
        try {
            BufferedReader bf = new BufferedReader( new InputStreamReader( sAllMimesFile.openStream() ) );
            String line = bf.readLine();
            
            while ( line != null ) {
                int pos = line.indexOf( " " );
                String key = ( line.substring( 0, pos ) ).trim().toLowerCase();
                String value = ( line.substring( pos + 1, line.length() ) ).trim().toLowerCase();
                
                if ( !result.containsKey( key ) ){ 
                    result.put( key, new MimeType( key, value, false, 400000 ) );
            }
                line = bf.readLine();
            }
            bf.close();
        }
        catch ( Exception e ) {
            log(e);
        }
        
        return result;
    }
    
    
    public HashMap readAllowedMimes() {
        HashMap result = new HashMap();
        
        try {
            BufferedReader bf = new BufferedReader( new InputStreamReader( sAllowedMimesFile.openStream() ) );
            String line = bf.readLine();
            
            while ( line != null ) {
                int pos = line.indexOf( " " );
                int size = Integer.parseInt( line.substring( pos + 1, line.length() ) );
                String extension = "";
                String description = line.substring( 0, pos );
                
                result.put( description, new MimeType( extension, description, true, size ) );
                line = bf.readLine();
            }
            bf.close();
        }
        catch ( Exception e ) {
            log(e);
        }
        
        return result;
    }
    
    public void saveConfig() {
        cc.set( "softFocus", String.valueOf( softFocus ) );
        cc.set( "hardFocus", String.valueOf( hardFocus ) );
        cc.set( "storeStreams", String.valueOf( storeStreams ) );
        cc.set( "titleLength", String.valueOf( titleLength ) );
        cc.set( "previewLength", String.valueOf( previewLength ) );
        cc.set( "useSVMClassifier", String.valueOf( useSVMClassifier ) );
        cc.set( "maxTunnelDistance", String.valueOf( maxTunnelDistance ) );
        cc.set( "maxCrawlingDepth", String.valueOf( maxCrawlingDepth ) );
        cc.set( "useGoogle", String.valueOf( useGoogle ) );
        cc.set( "numGoogleLink", String.valueOf( numGoogleLink ) );
        cc.set( "numGoogleRelated", String.valueOf( numGoogleRelated ) );
        cc.set( "downloadTimeout", String.valueOf( downloadTimeout ) );
        cc.set( "maxCrawlerThreads", String.valueOf( maxCrawlerThreads ) );
        cc.set( "linksPerDoc", String.valueOf( linksPerDoc ) );
        cc.set( "batchSize", String.valueOf( batchSize ) );
        cc.set( "chunkSize", String.valueOf( chunkSize ) );
        cc.set( "maxQueueSize", String.valueOf( maxQueueSize ) );
        cc.set( "offlineCrawling", String.valueOf( offlineCrawling ) );
        cc.set( "hostCrawling", String.valueOf( hostCrawling ) );
        cc.set( "langFile", langFile );
        cc.set( "logLevel", String.valueOf( logLevel ) );
        cc.set( "showStackTrace", String.valueOf( traceFlag ) );
        if (dbType != null) cc.set( "dbType", dbType );
        cc.set( "ignoreNoSparse", String.valueOf( ignoreNoSparse ));
        cc.set( "stemmerLanguage", stemmerLanguage);
        cc.set( "repeatStemmer", String.valueOf(repeatStemmer));
        cc.set( "useLinkTable", String.valueOf(useLinkTable));
        cc.set( "maxVisiblePages", String.valueOf(maxVisiblePages));
        cc.saveConfig();
    }
    
    
    public void restoreDefault() {
        cc.restoreDefault();// restore Default from backup file
        this.initSettings();
    }
    
    
    private void initSettings() {
        /*
            Hashtable myHash = cc.getValues();
            Enumeration enum = myHash.keys();
            log("Konfiguration: ", 5);
            log("-------------- ", 5);
            while (enum.hasMoreElements()) {
                String key = (String) enum.nextElement();
                log(key + " ---> " + myHash.get(key), 5);
            }
         */
        try {
            softFocus = Boolean.valueOf( cc.get( "softFocus" ) ).booleanValue();
            hardFocus = Boolean.valueOf( cc.get( "hardFocus" ) ).booleanValue();
            storeStreams = Boolean.valueOf( cc.get( "storeStreams" ) ).booleanValue();
            titleLength = Integer.valueOf( cc.get( "titleLength" ) ).intValue();
            previewLength = Integer.valueOf( cc.get( "previewLength" ) ).intValue();
            maxTunnelDistance = Integer.valueOf( cc.get( "maxTunnelDistance" ) ).intValue();
            useSVMClassifier = Integer.valueOf( cc.get( "useSVMClassifier" ) ).intValue();
            maxCrawlingDepth = Integer.valueOf( cc.get( "maxCrawlingDepth" ) ).intValue();
            useGoogle = Boolean.valueOf( cc.get( "useGoogle" ) ).booleanValue();
            numGoogleLink = Integer.valueOf( cc.get( "numGoogleLink" ) ).intValue();
            numGoogleRelated = Integer.valueOf( cc.get( "numGoogleRelated" ) ).intValue();
            downloadTimeout = Integer.valueOf( cc.get( "downloadTimeout" ) ).intValue();
            maxCrawlerThreads = Integer.valueOf( cc.get( "maxCrawlerThreads" ) ).intValue();
            batchSize = Integer.valueOf( cc.get( "batchSize" ) ).intValue();
            chunkSize = Integer.valueOf( cc.get( "chunkSize" ) ).intValue();
            maxQueueSize = Integer.valueOf( cc.get( "maxQueueSize" ) ).intValue();
            offlineCrawling = Boolean.valueOf( cc.get( "offlineCrawling" ) ).booleanValue();
            hostCrawling = Boolean.valueOf( cc.get( "hostCrawling" ) ).booleanValue();
            linksPerDoc = Integer.valueOf( cc.get( "linksPerDoc" ) ).intValue();
            numDF = Integer.valueOf( cc.get( "numDF" ) ).intValue();
            numMI = Integer.valueOf( cc.get( "numMI" ) ).intValue();
            numHITS = Integer.valueOf( cc.get( "numHITS" ) ).intValue();
            saveStopwordTerms = Boolean.valueOf( cc.get( "saveStopwordTerms" ) ).booleanValue();
            timeoutDelay = Integer.valueOf( cc.get( "timeoutDelay" ) ).intValue();
            storeStreams = Boolean.valueOf( cc.get( "storeStreams" ) ).booleanValue();
            bCUT = Boolean.valueOf( cc.get( "bCUT" ) ).booleanValue();
            bWN = Boolean.valueOf( cc.get( "bWN" ) ).booleanValue();
            bSTEM = Boolean.valueOf( cc.get( "bSTEM" ) ).booleanValue();
            repeatStemmer = Integer.valueOf( cc.get( "repeatStemmer" ) ).intValue();
            stemmerLanguage = cc.get( "stemmerLanguage" );
            obeyLevel = Boolean.valueOf( cc.get( "obeyLevel" ) ).booleanValue();
            modellingType = Integer.valueOf( cc.get( "modellingType" ) ).intValue();
            useUnionSelect = Boolean.valueOf( cc.get( "useUnionSelect" ) ).booleanValue();
            useLog = Boolean.valueOf( cc.get( "useLog" ) ).booleanValue();
            dThreshold = Double.valueOf( cc.get( "dThreshold" ) ).doubleValue();
            featuresPerClass = Integer.valueOf( cc.get( "featuresPerClass" ) ).intValue();
            langFile = cc.get( "langFile" );
            logLevel = Integer.valueOf( cc.get( "logLevel" ) ).intValue();
            ignoreNoSparse = Boolean.valueOf(cc.get("ignoreNoSparse")).booleanValue();
            useLinkTable = Boolean.valueOf(cc.get("useLinkTable")).booleanValue();
            maxVisiblePages = Integer.valueOf(cc.get("maxVisiblePages")).intValue();
            traceFlag = Boolean.valueOf(cc.get("showStackTrace")).booleanValue();
        } catch (Exception e) {
            log(e);
        }
    }
    
    public void setLogLevel(int logLevel) {
        this.logLevel = logLevel;
    }
    
    public int getLogLevel() {
        return logLevel;
    }
    
    public void setTraceFlag(boolean traceFlag) {
        this.traceFlag = traceFlag;
    }
    
    public boolean getTraceFlag() {
        return traceFlag;
    }
}

