/*
 * $Id: TdfAnalyzer.java,v 1.16 2006/08/02 19:14:43 marbobo Exp $
 *
 * Copyright (c) Siemens AG 2008 
 *           (c) Universität Paderborn 2008 
 * Licensed under EUPL 1.1. 
 *
 * $Log: TdfAnalyzer.java,v $
 * Revision 1.16  2006/08/02 19:14:43  marbobo
 * + SearchAnalyzer (=WhiteSpaceAnalyzer) statt StandardAnalyzer zum indexieren aller Felder ausser "contents" verwenden
 *
 * Revision 1.15  2006/07/17 13:04:01  lawern
 * Neuer typografiegewichteter Index und k-NN-Klassifikation aus meiner Diss.
 *
 * Revision 1.14  2006/02/23 13:37:07  lawern
 * Debug-Ausgabe entfernt
 *
 * Revision 1.13  2006/02/23 13:04:42  lawern
 * replaceSpecialChars wegen Sonderzeichen aus Excel-Dokumenten eingebaut
 *
 * Revision 1.12  2005/07/05 11:58:46  lawern
 * Typographiegewichtung erstmal wieder auskommentiert.
 *
 * Revision 1.11  2005/04/25 18:05:38  marbobo
 * + Verwendung von Standardanalyser
 *
 * Revision 1.10  2005/04/14 09:02:55  lawern
 * Stemming überarbeitet und erstmal ausgeschaltet.
 *
 * Revision 1.9  2005/04/13 06:37:26  lawern
 * Analyser mit Typografiegewichtung
 *
 * Revision 1.8  2005/02/11 15:07:20  lawern
 * StopAnalyzer jetzt ohne englische Stoppwörter
 *
 * Revision 1.7  2004/06/27 17:08:23  marbobo
 * + Quellcode maintenance
 *
 * Revision 1.6  2004/03/17 11:34:38  lawern
 * Probleme mit alten Analysern gefixt.
 *
 * Revision 1.5  2003/12/24 10:12:05  bigmac
 * Projektweite Import-Optimierung
 *
 * Revision 1.4  2003/10/02 13:38:02  lawern
 * Bug der bei leeren Dokumenten auftritt gefixt.
 *
 * Revision 1.3  2003/09/16 08:12:36  lawern
 * fälschlicherweise wurde bisher nicht in Kleinschrift indiziert
 *
 * Revision 1.2  2003/04/23 14:55:34  lawern
 * Ergänzungen für Volltextverarbeitung
 *
 * Revision 1.1  2003/04/22 11:03:52  lawern
 * Initiale Version
 *
 *
 */
package vkc.analyse;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.Token;
import org.apache.lucene.analysis.TokenStream;

import vkc.converter.TdfDocConverter;
import vkc.util.StringUtils;

import java.io.IOException;
import java.io.Reader;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;


/**
 * Diese Methode hilft Lucene die VKC-internen TDF-Dateien zu indizieren.
 *
 * @author lawern
 */
public final class TdfAnalyzer extends Analyzer {
    private Analyser analyser = null;
    private int language = -1;

    /**
     * Diese Methode wird von Lucene zur Indizierung des übergebenen
     * TDF-Streams aufgerufen.
     */
    public TokenStream tokenStream(String fieldName, Reader reader) {
        if ((fieldName.equals("contents")) &&
                (reader.getClass().getName().equals("java.io.BufferedReader"))) {
            //TDF-Elements erzeugen
            TdfDocConverter converter = new TdfDocConverter();
            converter.convert(reader, null);

            Collection tdfElements = converter.getElements();
            
            //Keywords erzeugen
            analyser = Analyser.createAnalyser(tdfElements, language, converter.mostUsedWeight, converter.minWeight, Analyser.globalStemming);

            List tokens;

            if (analyser != null) {
                tokens = analyser.getTokens();
            } else {
                tokens = new ArrayList();
            }

            return new TdfTokenStream(tokens, converter.mostUsedWeight, converter.minWeight);
        } 

        return (new SearchAnalyzer().tokenStream(fieldName, reader));
    }

    /**
     * Liefert den Analyser der für die Indizierung verwendet wird.
     *
     * @return Analyser der für die Indizierung verwendet wird
     */
    public Analyser getAnalyser() {
        return analyser;
    }

    /**
     * Setzt den Analyser der für die Indizierung verwendet wird.
     *
     */
    public void setAnalyser(Analyser analyser) {
        this.analyser = analyser;
    }
    
    public void setLanguage(int language){
    	this.language = language;
    }
}


final class TdfTokenStream extends TokenStream {
    List tokens;
    List heavierTokens = new ArrayList();
    TdfToken currentToken = null;
    Token token;
    int tokenPos = 1;
    int tokenListPointer = 0;
    int heavierTokenListPointer = 0;
    int listSize = 0;
    int mostUsedWeightSquare; //Gewicht des Fliesstextes
    int minWeight;

    /**
     * Initialisierung
     *
     * @param tdfElements Liste von TdfElement-Objekten aus dem DocConverter
     * @param keywords HashSet mit Keywords aus der Analyse
     */
    public TdfTokenStream(List tokens, int mostUsedWeight, int minWeight) {
        this.tokens = tokens;
        listSize = tokens.size();
        this.mostUsedWeightSquare = mostUsedWeight * mostUsedWeight;
        this.minWeight = minWeight;
    }

    /**
     * Liefert das naechste Token für den Lucene-Indexer.
     * 
     * Wegen Phrasensuche werden in jedem Durchlauf alle Tokens nur einmal geschrieben. 
     * In den Folgedurchläufen werden die Tokens mit geringerem Gewicht weggelassen.
     */
    public Token next() throws IOException {
        if (listSize != 0) {

            if (tokenListPointer < listSize) {
                //Zuerst einmal alle Tokens schreiben und eine zweite Liste mit den schwereren Tokens aufbauen.
                currentToken = (TdfToken) tokens.get(tokenListPointer);
                tokenListPointer++;

                int tokenWeight = (int) Math.ceil(currentToken.tdfWeight / mostUsedWeightSquare);

                if (tokenWeight > 1) {
                    //Zweite Liste mit schweren Tokens aufbauen
                    currentToken.tokenWeight = tokenWeight-1;
//                    System.out.print(currentToken.termText+"("+currentToken.tokenWeight+") ");
                    heavierTokens.add(currentToken);
                }

                currentToken.termText = StringUtils.replaceSpecialChars(currentToken.termText.toLowerCase());
                
            } else {
                //Schwerere Tokens mehrfach ausgeben, wenn erste Tokensliste einmal durchlaufen.
                if (heavierTokenListPointer == heavierTokens.size()) {
                    if (heavierTokens.size() == 0) {
                        //Dokument fertig indexiert!
//                        System.out.println("Document completely indexed!");
                        return null;
                    }

                    //Wenn bei Abarbeitung der Liste hinten angekommen, dann vorne wieder anfangen.
                    heavierTokenListPointer = 0; 
                }
                
                currentToken = (TdfToken) heavierTokens.get(heavierTokenListPointer);
                if (currentToken.tokenWeight > 1) {
                    currentToken.tokenWeight--;
                    heavierTokenListPointer++;
                } else {
                    //Token aus Liste entfernen, wenn das Gewicht zu klein ist.
                    heavierTokens.remove(heavierTokenListPointer);
                    //Hier muss wegen des remove der heavierTokenListPointer nicht erhöht werden!
                }
            }

            int nextTokenPos = tokenPos + currentToken.termText.length();
            token = new Token(currentToken.termText, tokenPos, nextTokenPos);
            tokenPos = nextTokenPos;

//            System.out.print(currentToken.termText+" ");
            return token;
        } 

        return null;
    }

    public void close() throws IOException {
        tokens = null;
        heavierTokens = null;
        currentToken = null;
    }
}