/*
 * $Header: /home/cvspublic/jakarta-slide/proposals/tamino/src/store/org/apache/slide/store/tamino/datastore/search/XBasicQueryImpl.java,v 1.4 2004/09/15 14:58:25 pnever Exp $
 * $Revision: 1.4 $
 * $Date: 2004/09/15 14:58:25 $
 *
 * ====================================================================
 *
 * 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.tamino.datastore.search;

import java.util.Iterator;
import java.util.List;
import org.apache.slide.common.PropertyParseException;
import org.apache.slide.common.RequestedProperties;
import org.apache.slide.common.ServiceAccessException;
import org.apache.slide.search.BadQueryException;
import org.apache.slide.search.InvalidQueryException;
import org.apache.slide.search.InvalidScopeException;
import org.apache.slide.search.QueryScope;
import org.apache.slide.search.SearchQueryResult;
import org.apache.slide.search.basic.BasicQueryImpl;
import org.apache.slide.search.basic.IBasicExpressionFactory;
import org.apache.slide.search.basic.OrderBy;
import org.apache.slide.store.tamino.common.IDescriptorsHandler;
import org.apache.slide.store.tamino.datastore.search.XPathPropertyMapper;
import org.apache.slide.store.tamino.store.ISlideAccessor;
import org.apache.slide.store.tamino.store.XParentStore;
import org.apache.slide.util.Configuration;
import org.apache.slide.util.XException;
import org.apache.slide.util.nodeset.NodeSet;
import org.jdom.Element;

/**
 * An XBasicQuery represents the query and is able to deliver a
 * SearchQueryResult using the execute method. This xdav specific implementation
 * extends basicsearch with tamino content queries (xpath)
 *
 * @author <a href="mailto:martin.wallmer@softwareag.com">Martin Wallmer</a>
 * @version $Revision: 1.4 $
 */
public class XBasicQueryImpl extends BasicQueryImpl implements XBasicQuery {
    
    private XPathPool xpathPool;
        
    /** Null if binding is disabled; initialized by parseQuery **/
    private XDescriptorsMappingList descriptorsMappingList;
        
    /**
     * constructs an XBasicQuery, allow creation by reflection
     *
     */
    public XBasicQueryImpl () {
        notNormalizer = new XNotNormalizer ();
        xpathPool = new XPathPool (this);
        //expressionFactory = new XBasicExpressionFactory();
    }
    
    /**
     * Method createRequestedProperties
     *
     * @param    propElement         an Element
     *
     * @return   a RequestedProperties
     *
     * @throws   BadQueryException
     *
     */
    protected RequestedProperties createRequestedProperties (Element propElement)
        throws InvalidQueryException {
        try {
            return new XRequestedPropertiesImpl (propElement, this);
        }
        catch (PropertyParseException e) {
            throw new InvalidQueryException (e.getMessage(), e);
        }
    }
    
    /**
     * Method execute
     *
     * @return   a SearchQueryResult
     *
     * @throws   ServiceAccessException
     *
     */
    public SearchQueryResult execute () throws ServiceAccessException {
        SearchQueryResult result;
        
        try {
            descriptorsMappingList = createDescriptorsMappingList();
        } catch (InvalidScopeException e) {
            // TODO: correct?
            result = new SearchQueryResult ();
            result.setStatus (SearchQueryResult.STATUS_INVALID_SCOPE);
            result.setHref (queryScope.getHref());
            return result;
        }
        result = super.execute();
        return result;
    }
    
    /**
     * Method getContentQuery
     *
     * @param    attributes          a  List
     * @param    text                a  String
     *
     * @return   a XContentQuery
     *
     * @throws   InvalidQueryException
     *
     */
    public XPathContentQuery getContentQuery (List attributes, String text) throws BadQueryException {
        return xpathPool.getContentQuery (attributes, text);
    }
    
    /**
     * Method getContentQuery
     *
     * @param    xPathElement        an Element
     *
     * @return   a XContentQuery
     *
     * @throws   InvalidQueryException
     *
     */
    public XPathContentQuery getContentQuery (Element xPathElement) throws InvalidQueryException {
        return xpathPool.getContentQuery (xPathElement);
    }
    
    /**
     * Method getXPathPropertyMapper
     *
     * @return   a XPathPropertyMapper
     *
     */
    XPathPropertyMapper getXPathPropertyMapper () {
        return XPathPropertyMapper.getInstance ((ISlideAccessor)getStore());
    }
    
    /**
     * Method createNewOrderBy
     *
     * @param    orderByElement      the &lt;orderby&gt; element
     *
     * @return   an XOrderBy
     *
     * @throws   InvalidQueryException
     *
     */
    protected OrderBy createNewOrderBy (Element orderByElement) throws InvalidQueryException {
        XOrderBy orderBy = new XOrderBy ();
        orderBy.init (orderByElement, this);
        
        return orderBy;
    }
    
    
    private XDescriptorsMappingList createDescriptorsMappingList() throws InvalidScopeException {
        int depth;
        
        if (Configuration.useBinding(getStore())) {
            depth = getProtectedDepth();
            QueryScope scope = getScope();
            
            String scopeHref = scope.getHref();
            if (depth == QueryScope.DEPTH_INFINITY) {
                depth = Integer.MAX_VALUE;
            }
            IDescriptorsHandler handler =
                ((XParentStore) getStore()).getDescriptorsStore().getDescriptorsHandler();
            
            NodeSet nodeSet = new NodeSet (new DescriptorsAdapter (handler));
            
            nodeSet.maxDepth(depth); // TODO: of-by-one error?
            
            Iterator it = scope.getIncludeSet().iterator();
            
            // if nothing is defined, include all
            if (!it.hasNext()) {
                nodeSet.include (createPattern("%"));
            }
            
            while (it.hasNext()) {
                String pattern = (String)it.next();
                pattern = createPattern (pattern);
                nodeSet.include (pattern);
            }
            
            it = scope.getExcludeSet().iterator();
            while (it.hasNext()) {
                String pattern = (String)it.next();
                pattern = createPattern (pattern);
                nodeSet.exclude (pattern);
             }
            
            try {
                return XDescriptorsMappingList.create (handler, scopeHref, nodeSet);
                
            } catch (XException e) {
                // TODO: correct?
                throw new InvalidScopeException(e.getMessage());
            }
            
        } else {
            return null;
        }
    }
    
    private String createPattern(String pattern)
    {
        pattern = pattern.replace ('%', '*');
        pattern = "**/" + pattern;
        return pattern;
    }
    
    
    /**
     * Method getProtectedDepth
     *
     * @return   an int
     *
     */
    public int getProtectedDepth() {
        int depth;
        
        depth = getScope().getDepth();
        if (depth == QueryScope.DEPTH_INFINITY) {
            // TODO: impose upper depth limit to provent denial of service attacks
        }
        return depth;
    }
    
    /**
     * Method getDescriptorsMappingList
     *
     * @return   a XDescriptorsMappingList
     *
     */
    public XDescriptorsMappingList getDescriptorsMappingList() {
        return descriptorsMappingList;
    }
    
    
    /**
     * Checks, if a XPathContentQuery may pose a limit on the tamino query.
     * Returns true if WHERE clause contains only xpath expression AND ORDEBY
     * is either not specified or orders by XPath query
     *
     * @return   a boolean
     *
     */
    public boolean isContentLimitAllowed () {
        boolean result;
        
        boolean xpathOnly = (getExpression() instanceof XPathExpression);
        XOrderBy ob = (XOrderBy)this.getOrderBy();
        
        if ((ob == null || ob.isXPathPropertyOrder()) && xpathOnly)
            result = true;
        else
            result = false;
        
        return result;
    }
    
    /**
     * Checks, if XQueryExecuter may pose a limit on the tamino query.
     *
     * @return   a boolean
     *
     */
    public boolean isMetaLimitAllowed () {
        boolean result;
        
        // metaOnly if neither XPathExpression nor XMergeExpression
        boolean metaOnly = (!(getExpression() instanceof XPathExpression) &&
                                !(getExpression() instanceof XMergeExpression));
        
        XOrderBy ob = (XOrderBy)this.getOrderBy();
        
        if ((ob == null || ob.isMetaPropertyOrder()) && metaOnly)
            result = true;
        else
            result = false;
        
        return result;
    }
}

