/*
 * $Header: /home/cvspublic/jakarta-slide/src/stores/org/apache/slide/index/lucene/LuceneExpressionFactory.java,v 1.8 2005/03/11 09:48:20 luetzkendorf Exp $
 * $Revision: 1.8 $
 * $Date: 2005/03/11 09:48:20 $
 *
 * ====================================================================
 *
 * Copyright 1999-2004 The Apache Software Foundation
 *
 * Licensed 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.slide.index.lucene;

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

import org.apache.slide.content.NodeProperty;
import org.apache.slide.index.lucene.expressions.AbstractLuceneExpression;
import org.apache.slide.index.lucene.expressions.BetweenExpression;
import org.apache.slide.index.lucene.expressions.ContainsExpression;
import org.apache.slide.index.lucene.expressions.EqExpression;
import org.apache.slide.index.lucene.expressions.GtExpression;
import org.apache.slide.index.lucene.expressions.IsCollectionExpression;
import org.apache.slide.index.lucene.expressions.IsDefinedExpression;
import org.apache.slide.index.lucene.expressions.IsPrincipalExpression;
import org.apache.slide.index.lucene.expressions.IsVersionHistoryExpression;
import org.apache.slide.index.lucene.expressions.LikeExpression;
import org.apache.slide.index.lucene.expressions.LtExpression;
import org.apache.slide.index.lucene.expressions.MergeExpression;
import org.apache.slide.index.lucene.expressions.MergeExpressionMixed;
import org.apache.slide.index.lucene.expressions.PropcontainsExpression;
import org.apache.slide.index.lucene.expressions.PropertyContainsExpression;
import org.apache.slide.search.BadQueryException;
import org.apache.slide.search.PropertyProvider;
import org.apache.slide.search.basic.BasicExpressionFactory;
import org.apache.slide.search.basic.IBasicExpression;
import org.apache.slide.search.basic.IBasicQuery;
import org.apache.slide.search.basic.Literals;
import org.jdom.Element;

/**
 */
public class LuceneExpressionFactory extends BasicExpressionFactory {

    private Index index; 

    /**
     * Constructor
     *
     */
    public LuceneExpressionFactory(Index index) {
        this.index = index;
    }

    public IBasicExpression createMergeExpression (String mergeOperator,
                                                   String namespace,
                                                   Collection expressionsToMerge)
        throws BadQueryException
    {
        IBasicExpression result = null;
        
        if (expressionsToMerge.size() == 0) return null; // TOCHECK

        List luceneExpressions = new ArrayList(expressionsToMerge.size());
        List basicExpressions = new ArrayList(expressionsToMerge.size());
        for(Iterator i = expressionsToMerge.iterator(); i.hasNext();) {
            Object o = i.next();
            if (o instanceof AbstractLuceneExpression) {
                luceneExpressions.add(o);
            } else {
                basicExpressions.add(o);
            }
        }
        
        if (namespace.equals (NodeProperty.NamespaceCache.DEFAULT_URI)) {
            if (mergeOperator.equals(Literals.OR)) {
                if (basicExpressions.size() > 0 && luceneExpressions.size() > 0) {
                    // a mixture
                    IBasicExpression e1 = new MergeExpression(this.index, false, luceneExpressions);
                    e1.setFactory(this);
                    basicExpressions.add(0, e1);
                    result =  super.createMergeExpression(Literals.OR, namespace, basicExpressions);
                } else if (luceneExpressions.size() > 0) {
                    // lucene expressions only
                    result = new MergeExpression(this.index, false, expressionsToMerge);
                } else {
                    // basic expressions only
                    result = super.createMergeExpression(mergeOperator, 
                            namespace, expressionsToMerge);
                }
            } 
            else if (mergeOperator.equals(Literals.AND)) {
                if (basicExpressions.size() > 0 && luceneExpressions.size() > 0) {
                    // a mixture between fast lucene expr. and slow basic expr.
                    AbstractLuceneExpression e = new MergeExpression(this.index, 
                            true, luceneExpressions);
                    e.setFactory(this);
                    result = new MergeExpressionMixed(this.index, e, 
                            basicExpressions);
                } else if (luceneExpressions.size() > 0) {
                    // lucene expressions only
                    result = new MergeExpression(this.index, true, 
                            expressionsToMerge);
                } else {
                    // basic expressions only
                    result = super.createMergeExpression(mergeOperator, 
                            namespace, expressionsToMerge);
                }
            }
            else {
                super.createMergeExpression(mergeOperator, namespace, 
                        expressionsToMerge);
            }
        }
        if (result != null) {
            result.setFactory(this);
        }

        return result;
    }

    public IBasicExpression createExpression (Element element)
        throws BadQueryException
    {
        IBasicExpression result = null;

        if (element == null) {
            return super.createExpression(element);
        } else {
            String namespace = element.getNamespace().getURI();
            if (namespace.equals (NodeProperty.NamespaceCache.DEFAULT_URI)) {
                result = createDAVExpression(element);
            }
            else if (namespace.equals(NodeProperty.NamespaceCache.SLIDE_URI)) {
                result = createSlideExpression(element);
            }
            else {
                result = super.createExpression(element);                
            }
        }
        if (result != null) {
            result.setFactory(this);
        }
        return result;
    }


    private IBasicExpression createDAVExpression (Element e) 
        throws BadQueryException
    {
        String operator = e.getName();
        
        if (operator.equals(Literals.ISCOLLECTION)) {
            return new IsCollectionExpression(this.index, false);
        } 
        if (operator.equals(Literals.NOT_ISCOLLECTION)) {
            return new IsCollectionExpression(this.index, true);
        }
        else if (operator.equals(Literals.CONTAINS)) {
            return new ContainsExpression(this.index, e, false);
        }
        else if (operator.equals(Literals.NOT_CONTAINS)) {
            return new ContainsExpression(this.index, e, true);
        }

        // the following expressions all are property related and
        // must have a <DAV:prop> element
        Element property = AbstractLuceneExpression.getPropertyElement(e);
        String namespace = property.getNamespaceURI();
        String name = property.getName();
        IndexConfiguration configuration = index.getConfiguration();
        
        if (operator.equals(Literals.EQ)) {
            if (configuration.isComparableProperty(namespace, name)) {
                return new EqExpression(this.index, e, false);
            }
        } 
        else if (operator.equals(Literals.NOT_EQ)) {
            if (configuration.isComparableProperty(namespace, name)) {
                return new EqExpression(this.index, e, true);
            }
        } 
        else if (operator.equals(Literals.LT) || operator.equals(Literals.NOT_GTE)) {
            if (configuration.isComparableProperty(namespace, name)) {
                return new LtExpression(this.index, e, false);
            }
        }
        else if (operator.equals(Literals.LTE) || operator.equals(Literals.NOT_GT)) {
            if (configuration.isComparableProperty(namespace, name)) {
                return new LtExpression(this.index, e, true);
            }
        }
        else if (operator.equals(Literals.GT) || operator.equals(Literals.NOT_LTE)) {
            if (configuration.isComparableProperty(namespace, name)) {
                return new GtExpression(this.index, e, false);
            }
        }
        else if (operator.equals(Literals.GTE) || operator.equals(Literals.NOT_LT)) {
            if (configuration.isComparableProperty(namespace, name)) {
                return new GtExpression(this.index, e, true);
            }
        }
        else if (operator.equals(Literals.LIKE)) {
            if (configuration.isStringProperty(namespace, name)) {
                return new LikeExpression(this.index, e, false);
            }
        }
        else if (operator.equals(Literals.NOT_LIKE)) {
            if (configuration.isStringProperty(namespace, name)) {
                return new LikeExpression(this.index, e, true);
            }
        }
        else if (operator.equals(Literals.ISDEFINED)) {
            if (configuration.supportsIsDefined(namespace, name)) {
                return new IsDefinedExpression(this.index, e, false);
            }
        }
        else if (operator.equals(Literals.NOT_ISDEFINED)) {
            if (configuration.supportsIsDefined(namespace, name)) {
                return new IsDefinedExpression(this.index, e, true);
            }
        }
        
        // fallback: if we don't know the operator or the property is not 
        // supported with the given operator
        return super.createExpression(e);
    }
    
    private IBasicExpression createSlideExpression(Element e) 
        throws BadQueryException
    {
        String operator = e.getName();
        
        if (operator.equals(Literals.ISPRINCIPAL)) {
            return new IsPrincipalExpression(this.index, false);
        }
        if (operator.equals(Literals.NOT_ISPRINCIPAL)) {
            return new IsPrincipalExpression(this.index, true);
        }
        if (operator.equals("is-version-history")) {
            return new IsVersionHistoryExpression(index, false);
        }
        if (operator.equals("not-is-version-history")) {
            return new IsVersionHistoryExpression(index, true);
        }
        
        Element property = AbstractLuceneExpression.getPropertyElement(e);
        String namespace = property.getNamespaceURI();
        String name = property.getName();
        IndexConfiguration configuration = index.getConfiguration();

        // TODO not-between
        if (operator.equals("between")) {
            if (configuration.isComparableProperty(namespace, name)) {
                return new BetweenExpression(this.index, e, false, false);
            }
        }
        if (operator.equals("between-inclusive")) {
            if (configuration.isComparableProperty(namespace, name)) {
                return new BetweenExpression(this.index, e, true, false);
            }
        }
        if (operator.equals("property-contains")) {
            if (configuration.isTextProperty(namespace, name)) {
                return new PropertyContainsExpression(this.index, e, false);
            }
        }
        if (operator.equals("not-property-contains")) {
            if (configuration.isTextProperty(namespace, name)) {
                return new PropertyContainsExpression(this.index, e, true);
            }
        }
        if (operator.equals(Literals.PROPCONTAINS)) {
            if (configuration.isStringProperty(namespace, name)) {
                return new PropcontainsExpression(this.index, e, false);
            }
        }
        if (operator.equals(Literals.NOT_PROPCONTAINS)) {
            if (configuration.isStringProperty(namespace, name)) {
                return new PropcontainsExpression(this.index, e, true);
            }
        }
        return super.createExpression(e);
    }

    /**
     * called by BasicExpressionCompiler after construction.
     *
     * @param    query               the associated BasicQuery
     * @param    propertyProvider    the PropertyProvider for this expression.
     *
     * @throws   BadQueryException
     *
     */
    public void init(IBasicQuery query, PropertyProvider propertyProvider)
        throws BadQueryException
    {
        this.query = query;
        this.propertyProvider = propertyProvider;
    }
}
