/*
 * $Header: /home/cvspublic/jakarta-slide/src/stores/org/apache/slide/store/impl/rdbms/expression/RDBMSExpressionFactory.java,v 1.15 2004/11/24 09:41:41 gcasper Exp $
 * $Revision: 1.15 $
 * $Date: 2004/11/24 09:41:41 $
 *
 * ====================================================================
 *
 * 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.store.impl.rdbms.expression;

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

import org.apache.slide.common.SlideException;
import org.apache.slide.content.NodeProperty.NamespaceCache;
import org.apache.slide.search.BadQueryException;
import org.apache.slide.search.InvalidQueryException;
import org.apache.slide.search.PropertyProvider;
import org.apache.slide.search.basic.BasicExpressionFactory;
import org.apache.slide.search.basic.ComparableResourcesPool;
import org.apache.slide.search.basic.IBasicExpression;
import org.apache.slide.search.basic.IBasicQuery;
import org.apache.slide.search.basic.Literals;
import org.apache.slide.store.impl.rdbms.AbstractRDBMSStore;
import org.apache.slide.store.impl.rdbms.RDBMSComparableResourcesPool;
import org.jdom.Element;

/**
 * DASL Basic search expression factory that compiles query expressions
 * to native SQL queries.
 */
public class RDBMSExpressionFactory extends BasicExpressionFactory {
    
    private final AbstractRDBMSStore _store;
    private final RDBMSQueryContext _context;

    public RDBMSExpressionFactory(AbstractRDBMSStore store) {
        _store = store;
        _context = new RDBMSQueryContext(new RDBMSResultSet(this));
    }
    
    public void init(IBasicQuery query, PropertyProvider propertyProvider) 
    throws BadQueryException {
        super.init(query, propertyProvider);
    }
    
    public IBasicExpression createMergeExpression(String name, String namespace, Collection members)
    throws BadQueryException {
        RDBMSExpression result = null;
        if (name == null) {
            result = new RDBMSNOPExpression(_context);
        }
        else {
            ArrayList otherExpressions = null;
            ArrayList rdbmsExpressions = null;
            final Iterator iter = members.iterator();
            while (iter.hasNext()) {
                final IBasicExpression expression = (IBasicExpression) iter.next();
                if (expression instanceof RDBMSExpression) {
                    if (rdbmsExpressions == null) {
                        rdbmsExpressions = new ArrayList(members.size());
                    }
                    rdbmsExpressions.add(expression);
                }
                else {
                    if (otherExpressions == null) {
                        otherExpressions = new ArrayList(members.size());
                    }
                    otherExpressions.add(expression);
                }
            }
            result = new RDBMSMergeExpression(name, namespace, _context, rdbmsExpressions, otherExpressions);
        }
        result.setFactory(this);
        return result;
    }
    
    IBasicExpression createStandardMergeExpression(String name, String namespace, Collection members) 
    throws BadQueryException {
        return super.createMergeExpression(name, namespace, members);
    }

    public IBasicExpression createExpression(Element element) throws BadQueryException {
        IBasicExpression result = null;
        if (element == null) {
            result = new RDBMSNOPExpression(_context);
        }
        else {
            final String namespace = element.getNamespace().getURI();
            final String name = element.getName();
            if (namespace.equals(NamespaceCache.DEFAULT_URI)) {
                result = createDAVExpression(element);
            }
            else if (namespace.equals(NamespaceCache.SLIDE_URI)) {
                result = createSlideExpression(element);
            }
            else {
                throw new InvalidQueryException
                    ("operator <" + namespace + ":" + name + "> is an unprocessable entity");
                
            }
        }
        result.setFactory(this);
        return result;
    }

    protected IBasicExpression createDAVExpression(Element element) throws BadQueryException {
        if (isSQLCompilableProperty(element)) {
            if (RDBMSCompareExpression.COMPARE_OPERATORS.containsKey(element.getName())) {
                return new RDBMSCompareExpression(element, _context);
            }
            else if (element.getName().equals(Literals.ISDEFINED)) {
                return new RDBMSIsDefinedExpression(element, _context);
            }
            else if (element.getName().equals(Literals.NOT_ISDEFINED)) {
                return new RDBMSNotIsDefinedExpression(element, _context);
            }
        }
        else if (element.getName().equals(Literals.ISCOLLECTION)) {
            return new RDBMSIsCollectionExpression(element, _context);
        }
        else if (element.getName().equals(Literals.NOT_ISCOLLECTION)) {
            return new RDBMSNotIsCollectionExpression(element, _context);
        }
        return super.createExpression(element);
    }

    protected IBasicExpression createSlideExpression(Element element) throws BadQueryException {
        if (isSQLCompilableProperty(element)) {
            if (element.getName().equals(Literals.PROPCONTAINS)) {
                return new RDBMSPropContainsExpression(element, _context);
            }
            else if (element.getName().equals(Literals.NOT_PROPCONTAINS)) {
                return new RDBMSNotPropContainsExpression(element, _context);
            }
        }
        if (element.getName().equals(Literals.ISPRINCIPAL)) {
            return new RDBMSIsPrincipalExpression(element, _context);
        }
        else if (element.getName().equals(Literals.NOT_ISPRINCIPAL)) {
            return new RDBMSNotIsPrincipalExpression(element, _context);
        }
        return super.createExpression(element);
    }

    protected ComparableResourcesPool getRequestedResourcePool() {
        if (requestedResourcePool == null) {
            requestedResourcePool = new RDBMSComparableResourcesPool(_store, _context, getQuery());
        }
        return requestedResourcePool;
    }

    public boolean isSQLCompilableProperty(Element element) {
        Element davProp = element.getChild(Literals.PROP, NamespaceCache.DEFAULT_NAMESPACE);
        if (davProp != null) {
            Element property = (Element) davProp.getChildren().get(0);
            return isSQLCompilableProperty(property.getNamespaceURI(), property.getName());
        }
        return false;
    }

    public boolean isSQLCompilableProperty(String namespace, String name) {
        if (getPropertyProvider() == null) return false;
        try {
            final String uri = getQuery().getSearchToken().getSlideContext()
            	.getSlidePath(getQuery().getScope().getHref());
            return !getPropertyProvider().isSupportedProperty(uri, name, namespace);
        }
        catch (SlideException e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * converts a property name to a legal SQL alias.
     */
    public static String propertyToAlias(String propertyName) {
        // replaceAll occurs in JDK1.4
        //return "prop_" + propertyName.replaceAll("-", "_");
        
        StringBuffer buffer = new StringBuffer(propertyName.length() + 5);
        buffer.append("prop_");
        for (int i = 0, l = propertyName.length(); i < l; i++) {
            char c = propertyName.charAt(i);
            if (c == '-') c = '_';
            buffer.append(c);
        }
        return buffer.toString();
    }

}
