/*

   Derby - Class org.apache.derby.optional.api.LuceneUtils

   Licensed to the Apache Software Foundation (ASF) under one or more
   contributor license agreements.  See the NOTICE file distributed with
   this work for additional information regarding copyright ownership.
   The ASF licenses this file to You 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.derby.optional.api;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Locale;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.queryparser.classic.MultiFieldQueryParser;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.util.Version;

import org.apache.derby.shared.common.error.PublicAPI;
import org.apache.derby.shared.common.error.StandardException;
import org.apache.derby.iapi.sql.conn.ConnectionUtil;
import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;
import org.apache.derby.optional.utils.ToolUtilities;

/**
 * <p>
 * Utility methods for the Lucene optional tool.
 * </p>
 */
public abstract class LuceneUtils
{
    /////////////////////////////////////////////////////////////////
    //
    //  CONSTANTS
    //
    /////////////////////////////////////////////////////////////////

    public  static  final   String  TEXT_FIELD_NAME = "luceneTextField";

    /////////////////////////////////////////////////////////////////
    //
    //  STATE
    //
    /////////////////////////////////////////////////////////////////

    /** Map of Analyzers keyed by language code */
    private static  HashMap<String,Class<? extends Analyzer>>   _analyzerClasses;
    static
    {
        _analyzerClasses = new HashMap<String,Class<? extends Analyzer>>();

        storeAnalyzerClass( org.apache.lucene.analysis.ar.ArabicAnalyzer.class );
        storeAnalyzerClass( org.apache.lucene.analysis.hy.ArmenianAnalyzer.class );
        storeAnalyzerClass( org.apache.lucene.analysis.eu.BasqueAnalyzer.class );
        storeAnalyzerClass( org.apache.lucene.analysis.br.BrazilianAnalyzer.class );
        storeAnalyzerClass( org.apache.lucene.analysis.bg.BulgarianAnalyzer.class );
        storeAnalyzerClass( org.apache.lucene.analysis.ca.CatalanAnalyzer.class );
        // deprecated, use StandardAnalyzer instead: storeAnalyzerClass( org.apache.lucene.analysis.cn.ChineseAnalyzer.class );
        storeAnalyzerClass( org.apache.lucene.analysis.cz.CzechAnalyzer.class );
        storeAnalyzerClass( org.apache.lucene.analysis.da.DanishAnalyzer.class );
        storeAnalyzerClass( org.apache.lucene.analysis.nl.DutchAnalyzer.class );
        storeAnalyzerClass( org.apache.lucene.analysis.en.EnglishAnalyzer.class );
        storeAnalyzerClass( org.apache.lucene.analysis.fi.FinnishAnalyzer.class );
        storeAnalyzerClass( org.apache.lucene.analysis.fr.FrenchAnalyzer.class );
        storeAnalyzerClass( org.apache.lucene.analysis.gl.GalicianAnalyzer.class );
        storeAnalyzerClass( org.apache.lucene.analysis.de.GermanAnalyzer.class );
        storeAnalyzerClass( org.apache.lucene.analysis.el.GreekAnalyzer.class );
        storeAnalyzerClass( org.apache.lucene.analysis.hi.HindiAnalyzer.class );
        storeAnalyzerClass( org.apache.lucene.analysis.hu.HungarianAnalyzer.class );
        storeAnalyzerClass( org.apache.lucene.analysis.id.IndonesianAnalyzer.class );
        storeAnalyzerClass( org.apache.lucene.analysis.ga.IrishAnalyzer.class );
        storeAnalyzerClass( org.apache.lucene.analysis.it.ItalianAnalyzer.class );
        storeAnalyzerClass( org.apache.lucene.analysis.lv.LatvianAnalyzer.class );
        storeAnalyzerClass( org.apache.lucene.analysis.no.NorwegianAnalyzer.class );
        storeAnalyzerClass( org.apache.lucene.analysis.fa.PersianAnalyzer.class );
        storeAnalyzerClass( org.apache.lucene.analysis.pt.PortugueseAnalyzer.class );
        storeAnalyzerClass( org.apache.lucene.analysis.ro.RomanianAnalyzer.class );
        storeAnalyzerClass( org.apache.lucene.analysis.ru.RussianAnalyzer.class );
        storeAnalyzerClass( org.apache.lucene.analysis.es.SpanishAnalyzer.class );
        storeAnalyzerClass( org.apache.lucene.analysis.sv.SwedishAnalyzer.class );
        storeAnalyzerClass( org.apache.lucene.analysis.th.ThaiAnalyzer.class );
        storeAnalyzerClass( org.apache.lucene.analysis.tr.TurkishAnalyzer.class );
    }

    /////////////////////////////////////////////////////////////////
    //
    //  PUBLIC BEHAVIOR
    //
    /////////////////////////////////////////////////////////////////

    /**
     * Get the version of the Lucene library on the classpath.
     *
     * @return the version of the Lucene library
     */
    @SuppressWarnings("deprecation")
    public  static  Version currentVersion()
    {
        Version retval = null;

        // the current version is the highest one
        for ( Version current : Version.values() )
        {
            if ( current == Version.LUCENE_CURRENT ) { continue; }
            
            if ( retval == null ) { retval = current; }
            else
            {
                if ( current.onOrAfter( retval ) ) { retval = current; }
            }
        }
        
        return retval;
    }

    /**
     * <p>
     * Get the default Analyzer associated with the database Locale.
     * </p>
     *
     * @return the default Analyzer
     * @throws SQLException on error
     */
    public  static  Analyzer    defaultAnalyzer()
        throws SQLException
    {
        return getAnalyzerForLocale( ConnectionUtil.getCurrentLCC().getDatabase().getLocale() );
    }
    
    /**
     * <p>
     * Get the Analyzer associated with the given Locale.
     * </p>
     *
     * @param locale The locale whose Analyzer is needed
     * @return the Analyzer associated with the given Locale.
     * @throws SQLException on error
     */
    public  static  Analyzer    getAnalyzerForLocale( Locale locale )
        throws SQLException
    {
        String          language = locale.getLanguage();

        try {
            Class<? extends Analyzer>   analyzerClass = _analyzerClasses.get( language );
        
            if ( analyzerClass == null )    { return standardAnalyzer(); }
            else
            {
                Constructor<? extends Analyzer> constructor = analyzerClass.getConstructor( Version.class );

                return constructor.newInstance( currentVersion() );
            }
        }
        catch (IllegalAccessException iae) { throw ToolUtilities.wrap( iae ); }
        catch (InstantiationException ie)   { throw ToolUtilities.wrap( ie ); }
        catch (InvocationTargetException ite)   { throw ToolUtilities.wrap( ite ); }
        catch (NoSuchMethodException nsme)  { throw ToolUtilities.wrap( nsme ); }
    }

    /**
     * <p>
     * Get the StandardAnalyzer for parsing text.
     * </p>
     *
     * @return the standard Analyzer
     */
    public  static  Analyzer    standardAnalyzer()
    {
        return new StandardAnalyzer( currentVersion() );
    }
    
    /**
     * <p>
     * Get the default, classic QueryParser.
     * </p>
     *
     * @param version The Lucene version
     * @param fieldNames The field names of interest
     * @param analyzer The Analyzer to use
     *
     * @return the corresponding QueryParser
     */
    public  static  QueryParser defaultQueryParser
        (
         Version version,
         String[] fieldNames,
         Analyzer analyzer
         )
    {
        return new MultiFieldQueryParser( version, fieldNames, analyzer );
    }
    
    /**
     * <p>
     * Get the default index descriptor. This has a single field named TEXT,
     * a defaultAnalyzer() and a defaultQueryParser().
     * </p>
     *
     * @return the default index descriptor
     */
    public  static  LuceneIndexDescriptor   defaultIndexDescriptor()
    {
        return new DefaultIndexDescriptor();
    }
    
    /////////////////////////////////////////////////////////////////
    //
    //  MINIONS
    //
    /////////////////////////////////////////////////////////////////

    /**
     * Store an Analyzer class in the HashMap of Analyzers, keyed by language code
     *
     * @param analyzerClass An Analyzer class
     */
    private static  void    storeAnalyzerClass( Class<? extends Analyzer> analyzerClass )
    {
        _analyzerClasses.put( getLanguageCode( analyzerClass ), analyzerClass );
    }

    /**
     * <p>
     * Get the language code for a Lucene Analyzer. Each of the Analyzers
     * lives in a package whose last leg is the language code.
     * </p>
     *
     * @param analyzerClass An Analyzer class
     *
     * @return the corresponding language code
     */
    private static  String  getLanguageCode( Class<? extends Analyzer> analyzerClass )
    {
        String  className = analyzerClass.getName();
        String  packageName = className.substring( 0, className.lastIndexOf( "." ) );
        String  languageCode = packageName.substring( packageName.lastIndexOf( "." ) + 1, packageName.length() );

        return languageCode;
    }

    /////////////////////////////////////////////////////////////////
    //
    //  NESTED CLASSES
    //
    /////////////////////////////////////////////////////////////////

    /** The default LuceneIndexDescriptor */
    public  static  class   DefaultIndexDescriptor  implements LuceneIndexDescriptor
    {
        public  DefaultIndexDescriptor()    {}

        /**
         * Return the default array of field names { TEXT_FIELD_NAME }.
         *
         * @return the default array of field names
         */
        public  String[]    getFieldNames() { return new String[] { TEXT_FIELD_NAME }; }

        /**
         * Return LuceneUtils.defaultAnalyzer()
         *
         * @return the default Analyzer
         *
         * @throws SQLException on error
         */
        public Analyzer getAnalyzer()   throws SQLException
        { return LuceneUtils.defaultAnalyzer(); }

        /**
         * Return LuceneUtils.defaultQueryParser(  LuceneUtils.currentVersion(), getFieldNames(), getAnalyzer() ).
         * @return the query parser
         * @throws SQLException on error
         */
        public  QueryParser getQueryParser()
            throws SQLException
        {
            return LuceneUtils.defaultQueryParser
                (
                 LuceneUtils.currentVersion(),
                 getFieldNames(),
                 getAnalyzer()
                 );
        }
    }
    
}
