/**
 * Copyright (c) 2014, the LESK-WSD-DSM AUTHORS.
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * Neither the name of the University of Bari nor the names of its contributors
 * may be used to endorse or promote products derived from this software without
 * specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007
 *
 */
package di.uniba.it.wsd;

import di.uniba.it.wsd.data.ExecuteStatistics;
import di.uniba.it.wsd.data.POSenum;
import di.uniba.it.wsd.data.RelatedSynset;
import di.uniba.it.wsd.data.SynsetOut;
import di.uniba.it.wsd.data.Token;
import di.uniba.it.wsd.dsm.ObjectVector;
import di.uniba.it.wsd.dsm.VectorStore;
import di.uniba.it.wsd.dsm.VectorUtils;
import edu.mit.jwi.item.IPointer;
import edu.mit.jwi.item.POS;
import it.uniroma1.lcl.babelnet.BabelGloss;
import it.uniroma1.lcl.babelnet.BabelNet;
import it.uniroma1.lcl.babelnet.BabelSense;
import it.uniroma1.lcl.babelnet.BabelSenseSource;
import it.uniroma1.lcl.babelnet.BabelSynset;
import it.uniroma1.lcl.jlt.util.Language;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.math.stat.StatUtils;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.analysis.tokenattributes.TermAttribute;
import org.apache.lucene.util.Version;
import org.tartarus.snowball.SnowballStemmer;
import org.tartarus.snowball.ext.frenchStemmer;
import org.tartarus.snowball.ext.germanStemmer;
import org.tartarus.snowball.ext.italianStemmer;
import org.tartarus.snowball.ext.porterStemmer;
import org.tartarus.snowball.ext.spanishStemmer;

/**
 * This class implements the Word Sense Disambiguation algorithm
 *
 * @author Pierpaolo Basile [email protected]
 */
public class RevisedLesk {

    /**
     * Constant for WordNet output format
     */
    public static final int OUT_WORDNET = 1000;

    /**
     * Constant for BabelNet output format
     */
    public static final int OUT_BABELNET = 1100;

    /**
     * Constant for Synset Distribution function
     */
    public static final int SD_PROB = 2000;
    /**
     * Constant for Synset Distribution function
     */
    public static final int SD_PROB_CROSS = 2100;
    /**
     * Constant for Synset Distribution function
     */
    public static final int SD_OCC = 2200;
    /**
     * Constant for Wiki scoring function
     */
    public static final int WIKI_LEV = 3000;
    /**
     * Constant for Wiki scoring function
     */
    public static final int WIKI_UNI = 3100;
    private int outType = OUT_BABELNET;
    private int sdType = SD_PROB;
    private int wikiType = WIKI_LEV;
    private double weightWsd = 0.5;
    private double weightSd = 0.5;
    private BabelNet babelNet;
    private int contextSize = 5;
    private Language language;
    private boolean stemming = false;
    private VectorStore dsm;
    private int maxDepth = 1;
    private SenseFreqAPI senseFreq;
    private boolean scoreGloss = true;
    private ExecuteStatistics execStats = new ExecuteStatistics();
    private static final Logger logger = Logger.getLogger(RevisedLesk.class.getName());

    /**
     *
     * @param language
     */
    public RevisedLesk(Language language) {
        this.language = language;
    }

    /**
     *
     * @param language
     * @param dsm
     */
    public RevisedLesk(Language language, VectorStore dsm) {
        this.language = language;
        this.dsm = dsm;
    }

    /**
     *
     * @return
     */
    public int getSdType() {
        return sdType;
    }

    /**
     *
     * @param sdType
     */
    public void setSdType(int sdType) {
        this.sdType = sdType;
    }

    /**
     *
     * @return
     */
    public int getWikiType() {
        return wikiType;
    }

    /**
     *
     * @param wikiType
     */
    public void setWikiType(int wikiType) {
        this.wikiType = wikiType;
    }

    /**
     *
     * @return
     */
    public double getWeightWsd() {
        return weightWsd;
    }

    /**
     *
     * @param weightWsd
     */
    public void setWeightWsd(double weightWsd) {
        this.weightWsd = weightWsd;
    }

    /**
     *
     * @return
     */
    public double getWeightSd() {
        return weightSd;
    }

    /**
     *
     * @param weightSd
     */
    public void setWeightSd(double weightSd) {
        this.weightSd = weightSd;
    }

    /**
     *
     * @return
     */
    public SenseFreqAPI getSenseFreq() {
        return senseFreq;
    }

    /**
     *
     * @param senseFreq
     */
    public void setSenseFreq(SenseFreqAPI senseFreq) {
        this.senseFreq = senseFreq;
    }

    /**
     *
     * @return
     */
    public Language getLanguage() {
        return language;
    }

    /**
     *
     * @param language
     */
    public void setLanguage(Language language) {
        this.language = language;
    }

    /**
     *
     * @return
     */
    public int getContextSize() {
        return contextSize;
    }

    /**
     *
     * @param contextSize
     */
    public void setContextSize(int contextSize) {
        this.contextSize = contextSize;
    }

    /**
     *
     * @return
     */
    public VectorStore getDsm() {
        return dsm;
    }

    /**
     *
     */
    public void init() {
        babelNet = BabelNet.getInstance();
        logger.log(Level.INFO, "Language: {0}", this.language);
        logger.log(Level.INFO, "Context size: {0}", this.contextSize);
        logger.log(Level.INFO, "Depth: {0}", this.maxDepth);
        logger.log(Level.INFO, "Stemmig: {0}", this.stemming);
        logger.log(Level.INFO, "Score gloss: {0}", this.scoreGloss);
        logger.log(Level.INFO, "Weigths, wsd: {0}, synset distr. {1}", new Object[]{weightWsd, weightSd});
        if (this.senseFreq != null) {
            logger.info("Sense distribution ENABLED");
            if (sdType == SD_OCC) {
                logger.info("Sense distribution type=occurrences");
            } else if (sdType == SD_PROB) {
                logger.info("Sense distribution type=probability");
            } else if (sdType == SD_PROB_CROSS) {
                logger.info("Sense distribution type=cross probability");
            }
            if (wikiType == WIKI_LEV) {
                logger.info("Wiki synset scoring: LEV");
            } else if (wikiType == WIKI_UNI) {
                logger.info("Wiki synset scoring: Uniform");
            }
        } else {
            logger.info("Sense distribution DISABLED");
        }
        if (this.dsm != null) {
            logger.info("Distributional Semantic Model ENABLED");
        } else {
            logger.info("Distributional Semantic Model DISABLED");
        }
    }

    /**
     *
     */
    public void close() {
    }

    private SnowballStemmer getStemmer(Language language) {
        if (language.equals(Language.EN)) {
            return new porterStemmer();
        } else if (language.equals(Language.ES)) {
            return new spanishStemmer();
        } else if (language.equals(Language.FR)) {
            return new frenchStemmer();
        } else if (language.equals(Language.DE)) {
            return new germanStemmer();
        } else if (language.equals(Language.IT)) {
            return new italianStemmer();
        } else {
            return null;
        }
    }

    /**
     *
     * @param text
     * @return
     * @throws IOException
     */
    public Map<String, Float> buildBag(String text) throws IOException {
        Map<String, Float> bag = new HashMap<>();
        Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_CURRENT);
        SnowballStemmer stemmer = null;
        if (stemming) {
            stemmer = getStemmer(language);
            if (stemmer == null) {
                Logger.getLogger(RevisedLesk.class.getName()).log(Level.WARNING, "No stemmer for language {0}", language);
            }
        }
        TokenStream tokenStream = analyzer.tokenStream("gloss", new StringReader(text));
        while (tokenStream.incrementToken()) {
            TermAttribute token = (TermAttribute) tokenStream.getAttribute(TermAttribute.class);
            String term = token.term();
            if (stemmer != null) {
                stemmer.setCurrent(term);
                if (stemmer.stem()) {
                    term = stemmer.getCurrent();
                }
            }
            Float c = bag.get(term);
            if (c == null) {
                bag.put(term, 1f);
            } else {
                bag.put(term, c + 1f);
            }
        }
        return bag;
    }

    private Map<String, Float> buildContext(List<Token> sentence, int pivot) throws Exception {
        int i = pivot - 1;
        int c = 0;
        StringBuilder sb = new StringBuilder();
        while (i >= 0 && c < contextSize) {
            if (sentence.get(i).getPos() != POSenum.OTHER) {
                sb.append(sentence.get(i).getToken());
                sb.append(" ");
                c++;
            }
            i--;
        }
        i = pivot + 1;
        c = 0;
        while (i < sentence.size() && c < contextSize) {
            if (sentence.get(i).getPos() != POSenum.OTHER) {
                sb.append(sentence.get(i).getToken());
                sb.append(" ");
                c++;
            }
            i++;
        }
        return buildBag(sb.toString());
    }

    private void getRelatedSynsets(Map<BabelSynset, RelatedSynset> map, int distance) throws IOException {
        List<BabelSynset> listKey = new ArrayList<>(map.keySet());
        for (BabelSynset synset : listKey) {
            RelatedSynset get = map.get(synset);
            if (!get.isVisited()) {
                get.setVisited(true);
                Map<IPointer, List<BabelSynset>> relatedMap = synset.getRelatedMap();
                Iterator<IPointer> itRel = relatedMap.keySet().iterator();
                while (itRel.hasNext()) {
                    IPointer pointer = itRel.next();
                    if (!pointer.getName().equalsIgnoreCase("antonym")) {
                        List<BabelSynset> list = relatedMap.get(pointer);
                        for (BabelSynset relSynset : list) {
                            RelatedSynset rs = map.get(relSynset);
                            if (rs == null) {
                                map.put(relSynset, new RelatedSynset(relSynset, distance));
                            }
                        }
                    }
                }
            }
        }
    }

    private Map<String, Float> buildGlossBag(BabelSynset synset) throws IOException {
        Map<BabelSynset, RelatedSynset> relatedMap = new HashMap<>();
        relatedMap.put(synset, new RelatedSynset(synset, 0));
        for (int i = 0; i < maxDepth; i++) {
            getRelatedSynsets(relatedMap, i + 1);
        }
        Iterator<BabelSynset> itRel = relatedMap.keySet().iterator();
        Map<String, Float> bag = new HashMap<>();
        while (itRel.hasNext()) {
            BabelSynset relSynset = itRel.next();
            RelatedSynset rs = relatedMap.get(relSynset);
            List<BabelGloss> glosses = relSynset.getGlosses(language);
            List<String> glossesToProcess = new ArrayList<>();
            execStats.incrementTotalGloss();
            if (glosses.isEmpty()) {
                logger.log(Level.FINEST, "No gloss for synset: {0}", relSynset);
                execStats.incrementNoGloss();
                List<BabelSense> senses = relSynset.getSenses(this.language);
                StringBuilder sb = new StringBuilder();
                for (BabelSense bs : senses) {
                    sb.append(bs.getLemma().replace("_", " ")).append(" ");
                }
                glossesToProcess.add(sb.toString());
            } else {
                for (BabelGloss gloss : glosses) {
                    glossesToProcess.add(gloss.getGloss());
                }
            }
            float df = maxDepth + 1 - rs.getDistance();
            for (String gloss : glossesToProcess) {
                Map<String, Float> gbag = buildBag(gloss);
                Iterator<String> iterator = gbag.keySet().iterator();
                while (iterator.hasNext()) {
                    String term = iterator.next();
                    Float c = bag.get(term);
                    if (c == null) {
                        bag.put(term, df * gbag.get(term));
                    } else {
                        bag.put(term, c + df * gbag.get(term));
                    }
                }
            }
        }
        return bag;
    }

    private float gf(List<Map<String, Float>> mapList, String key) {
        float gf = 0;
        for (Map<String, Float> map : mapList) {
            if (map.containsKey(key)) {
                gf++;
            }
        }
        return gf;
    }

    private List<Map<String, Float>> buildGlossBag(List<BabelSense> senses) throws IOException {
        List<Map<String, Float>> mapList = new ArrayList<>();
        for (BabelSense sense : senses) {
            mapList.add(buildGlossBag(sense.getSynset()));
        }
        if (scoreGloss) {
            for (Map<String, Float> map : mapList) {
                Iterator<String> iterator = map.keySet().iterator();
                while (iterator.hasNext()) {
                    String key = iterator.next();
                    float igf = 1 + (float) (Math.log(senses.size() / gf(mapList, key)) / Math.log(2));
                    float gf = map.get(key);
                    map.put(key, gf * igf);
                }
            }
        }
        return mapList;
    }

    private double simBag(Map<String, Float> bag1, Map<String, Float> bag2) {
        double n1 = 0;
        double n2 = 0;
        double ip = 0;
        Iterator<String> it1 = bag1.keySet().iterator();
        while (it1.hasNext()) {
            String t1 = it1.next();
            Float v1 = bag1.get(t1);
            if (bag2.containsKey(t1)) {
                ip += v1.doubleValue() * bag2.get(t1).doubleValue();
            }
            n1 += Math.pow(v1.doubleValue(), 2);
        }
        Iterator<Float> it2 = bag2.values().iterator();
        while (it2.hasNext()) {
            n2 += Math.pow(it2.next().doubleValue(), 2);
        }
        if (n1 == 0 || n2 == 0) {
            return 0;
        } else {
            return ip / (n1 * n2);
        }
    }

    private float[] buildVector(Map<String, Float> bag, boolean normalize) {
        Iterator<String> it = bag.keySet().iterator();
        float[] bagv = new float[ObjectVector.vecLength];
        while (it.hasNext()) {
            String t1 = it.next();
            float w = bag.get(t1);
            float[] v = dsm.getVector(t1);
            execStats.incrementTotalSVhit();
            if (v != null) {
                for (int k = 0; k < v.length; k++) {
                    bagv[k] += w * v[k];
                }
            } else {
                execStats.incrementMissSV();
            }
        }
        if (normalize && !VectorUtils.isZeroVector(bagv)) {
            bagv = VectorUtils.getNormalizedVector(bagv);
        }
        return bagv;
    }

    private double simVector(Map<String, Float> bag1, Map<String, Float> bag2) {
        float[] gv1 = buildVector(bag1, true);
        float[] gv2 = buildVector(bag2, true);
        return VectorUtils.scalarProduct(gv1, gv2);
    }

    private double sim(Map<String, Float> bag1, Map<String, Float> bag2) {
        if (dsm != null) {
            return simVector(bag1, bag2);
        } else {
            return simBag(bag1, bag2);
        }
    }

    private List<BabelSense> lookupSense(Language language, String lemma, POS postag) throws IOException {
        List<BabelSense> senses = babelNet.getSenses(language, lemma, postag, BabelSenseSource.WN);
        if (senses == null || senses.isEmpty()) {
            senses = babelNet.getSenses(language, lemma.replace(" ", "_"), postag, BabelSenseSource.WN);
        }
        if (senses == null || senses.isEmpty()) {
            senses = babelNet.getSenses(language, lemma, postag, BabelSenseSource.WNTR);
        }
        if (senses == null || senses.isEmpty()) {
            senses = babelNet.getSenses(language, lemma.replace(" ", "_"), postag, BabelSenseSource.WNTR);
        }
        if (senses == null || senses.isEmpty()) {
            senses = babelNet.getSenses(language, lemma, postag);
        }
        if (senses == null || senses.isEmpty()) {
            senses = babelNet.getSenses(language, lemma.replace(" ", "_"), postag);
        }
        /*
         if (senses == null || senses.isEmpty()) {
         senses = babelNet.getSenses(language, lemma);
         }
         if (senses == null || senses.isEmpty()) {
         senses = babelNet.getSenses(language, lemma.replace(" ", "_"));
         }
         */
        if (senses == null || senses.isEmpty()) {
            Logger.getLogger(RevisedLesk.class.getName()).log(Level.WARNING, "No senses for {0}, pos-tag {1}", new Object[]{lemma, postag});
        }
        //remove duplicate senses
        if (senses != null && !senses.isEmpty()) {
            Set<String> ids = new HashSet<>();
            for (int i = senses.size() - 1; i >= 0; i--) {
                if (!ids.add(senses.get(i).getSynset().getId())) {
                    senses.remove(i);
                }
            }
        }
        return senses;
    }

    private String convertPosEnum(POSenum pos) {
        if (pos == POSenum.NOUN) {
            return "n";
        } else if (pos == POSenum.VERB) {
            return "v";
        } else if (pos == POSenum.ADJ) {
            return "a";
        } else if (pos == POSenum.ADV) {
            return "r";
        } else {
            return "o";
        }
    }

    /**
     *
     * @param sentence
     * @throws Exception
     */
    public void disambiguate(List<Token> sentence) throws Exception {
        execStats = new ExecuteStatistics();
        System.out.println();
        for (int i = 0; i < sentence.size(); i++) {
            System.out.print(".");
            Token token = sentence.get(i);
            if (token.isToDisambiguate() && token.getSynsetList().isEmpty()) {
                Map<String, Float> contextBag = buildContext(sentence, i);
                if (token.getPos() != POSenum.OTHER) {
                    List<BabelSense> senses = null;
                    if (token.getPos() == POSenum.NOUN) {
                        senses = lookupSense(language, token.getLemma(), POS.NOUN);
                    } else if (token.getPos() == POSenum.VERB) {
                        senses = lookupSense(language, token.getLemma(), POS.VERB);
                    } else if (token.getPos() == POSenum.ADJ) {
                        senses = lookupSense(language, token.getLemma(), POS.ADJECTIVE);
                    } else if (token.getPos() == POSenum.ADV) {
                        senses = lookupSense(language, token.getLemma(), POS.ADVERB);
                    }
                    if (senses != null) {
                        float[] as = null;
                        if (senseFreq != null && sdType == SD_OCC) {
                            as = senseFreq.getOccurrencesArray(senses);
                        }
                        List<Map<String, Float>> buildGlossBag = buildGlossBag(senses);
                        for (int j = 0; j < senses.size(); j++) {
                            double sim = 0;
                            Map<String, Float> bag = buildGlossBag.get(j);
                            //assign WSD algorithm score
                            sim = sim(contextBag, bag);
                            if (senseFreq != null) {
                                if (sdType == SD_PROB) { //sense distribution based on conditional probability p(si|w)
                                    if (language.equals(Language.EN)) {  //English is the WordNet native language
                                        if (senses.get(j).getWordNetOffset() != null && senses.get(j).getWordNetOffset().length() > 0) {
                                            String lemmakey = token.getLemma() + "#" + convertPosEnum(token.getPos());
                                            float freq = senseFreq.getSynsetProbability(lemmakey, senses.get(j).getWordNetOffset(), senses.size());
                                            sim = weightWsd * sim + weightSd * freq;
                                        } else {
                                            if (wikiType == WIKI_LEV) {
                                                sim = weightWsd * sim + weightSd * computeLDscore(token.getToken(), senses.get(j).getLemma());
                                            } else {
                                                sim = weightWsd * sim + weightSd * (1 / (double) senses.size());
                                            }
                                        }
                                    } else { //Translate WordNet synset from other language
                                        String mainSense = senses.get(j).getSynset().getMainSense();
                                        if (mainSense != null && !mainSense.startsWith("WIKI:") && mainSense.length() > 0) {
                                            String lemmakey = token.getLemma() + "#" + convertPosEnum(token.getPos());
                                            float maxFreq = senseFreq.getMaxSenseProbability(lemmakey, senses.get(j), senses.size());
                                            sim = weightWsd * sim + weightSd * maxFreq;
                                        } else {
                                            if (wikiType == WIKI_LEV) {
                                                sim = weightWsd * sim + weightSd * computeLDscore(token.getToken(), senses.get(j).getLemma());
                                            } else {
                                                sim = weightWsd * sim + weightSd * (1 / (double) senses.size());
                                            }
                                        }
                                    }
                                } else if (sdType == SD_PROB_CROSS) { //Use english sense distribution for other langauge
                                    String mainSense = senses.get(j).getSynset().getMainSense();
                                    if (mainSense != null && !mainSense.startsWith("WIKI:") && mainSense.length() > 0) {
                                        int si = mainSense.lastIndexOf("#");
                                        String lemmakey = mainSense.substring(0, si);
                                        float maxFreq = senseFreq.getMaxSenseProbability(lemmakey, senses.get(j), senses.size());
                                        sim = weightWsd * sim + weightSd * maxFreq;
                                    } else {
                                        if (wikiType == WIKI_LEV) {
                                            sim = weightWsd * sim + weightSd * computeLDscore(token.getToken(), senses.get(j).getLemma());
                                        } else {
                                            sim = weightWsd * sim + weightSd * (1 / (double) senses.size());
                                        }
                                    }
                                } else if (sdType == SD_OCC) { //language independent based on synset offset
                                    sim = weightWsd * sim + weightSd * as[j];
                                }
                            }
                            if (outType == OUT_BABELNET) {
                                token.getSynsetList().add(new SynsetOut(senses.get(j).getSynset().getId(), sim));
                            } else if (outType == OUT_WORDNET) {
                                token.getSynsetList().add(new SynsetOut(senses.get(j).getSensekey(), sim));
                            } else {
                                throw new Exception("Output type not valid: " + outType);
                            }
                        }
                        Collections.sort(token.getSynsetList());
                        //logger.log(Level.FINEST, "{0}\tmean: {1}\tvariance: {2}", new Object[]{token.toString(), getMean(token.getSynsetList()), getVariance(token.getSynsetList())});
                    }
                }
            }
        }
    }

    private float computeLDscore(String s1, String s2) {
        float maxLength = (float) Math.max(s1.length(), s2.length());
        float ld = (float) StringUtils.getLevenshteinDistance(s1, s2);
        return 1 - ld / maxLength;
    }

    /**
     *
     * @param list
     * @return
     */
    public double getMean(List<SynsetOut> list) {
        double[] scores = new double[list.size()];
        int l = 0;
        for (SynsetOut out : list) {
            scores[l] = out.getScore();
            l++;
        }
        return StatUtils.mean(scores);
    }

    /**
     *
     * @param list
     * @return
     */
    public double getVariance(List<SynsetOut> list) {
        double[] scores = new double[list.size()];
        int l = 0;
        for (SynsetOut out : list) {
            scores[l] = out.getScore();
            l++;
        }
        return StatUtils.variance(scores);
    }

    /**
     *
     * @return
     */
    public BabelNet getBabelNet() {
        return babelNet;
    }

    /**
     * @return the stemming
     */
    public boolean isStemming() {
        return stemming;
    }

    /**
     * @param stemming the stemming to set
     */
    public void setStemming(boolean stemming) {
        this.stemming = stemming;
    }

    /**
     * @return the outType
     */
    public int getOutType() {
        return outType;
    }

    /**
     * @param outType the outType to set
     */
    public void setOutType(int outType) {
        this.outType = outType;
    }

    /**
     *
     * @return
     */
    public int getMaxDepth() {
        return maxDepth;
    }

    /**
     *
     * @param maxDepth
     */
    public void setMaxDepth(int maxDepth) {
        this.maxDepth = maxDepth;
    }

    /**
     *
     * @return
     */
    public boolean isScoreGloss() {
        return scoreGloss;
    }

    /**
     *
     * @param scoreGloss
     */
    public void setScoreGloss(boolean scoreGloss) {
        this.scoreGloss = scoreGloss;
    }

    /**
     *
     * @return
     */
    public ExecuteStatistics getExecStats() {
        return execStats;
    }

}