/*
 * $Header: /home/cvspublic/jakarta-slide/proposals/tamino/src/store/org/apache/slide/store/tamino/datastore/search/XPathPropertyExecuter.java,v 1.3 2004/07/30 06:51:58 ozeigermann Exp $
 * $Revision: 1.3 $
 * $Date: 2004/07/30 06:51:58 $
 *
 * ====================================================================
 *
 * 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 com.softwareag.tamino.db.api.accessor.TQuery;
import com.softwareag.tamino.db.api.accessor.TXMLObjectAccessor;
import com.softwareag.tamino.db.api.objectModel.TXMLObject;
import com.softwareag.tamino.db.api.objectModel.TXMLObjectIterator;
import com.softwareag.tamino.db.api.response.TResponse;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.apache.slide.search.InvalidQueryException;
import org.apache.slide.store.tamino.common.XDatastoreException;
import org.apache.slide.store.tamino.common.XGlobals;
import org.apache.slide.store.tamino.datastore.XConnection;
import org.apache.slide.store.tamino.datastore.XConnectionKey;
import org.apache.slide.store.tamino.datastore.XConnectionPool;
import org.apache.slide.store.tamino.datastore.XContentId;
import org.apache.slide.store.tamino.store.ISlideAccessor;
import org.apache.slide.util.XException;
import org.jdom.Element;

/**
 * <p>Collects all XPathProperties of one query and tries to build one tamino query
 * for all properties. The last node of a query (/test/otto ==> otto) is
 * significant to link a result element to the original query. </p> <p>
 * A query is not added if the lastNode specifies an attribute, another query
 * with that lastNode already exists, or the query is already resolved.</p>
 *
 * @author martin.wallmer@softwareag.com
 *
 * @version $Revision: 1.3 $
 */
public class XPathPropertyExecuter {

    /** contains connection information */
    private ISlideAccessor slideAccessor;

    /** all queries that shall be combined, identified by lastNode */
    private Map optimizedQueries = new HashMap();

    /** the tamino query String */
    private String optimizedQuery;

    private boolean isThereSomethingToDo;
    /**
     * Constructor
     *
     * @param    query               a  XBasicQueryImpl
     * @param    contentQueryFilter  an IContentQueryFilter
     * @param    slideAccessor       an ISlideAccessor
     *
     */
    public XPathPropertyExecuter (XBasicQueryImpl query,
                                  IContentQueryFilter contentQueryFilter,
                                  ISlideAccessor slideAccessor) {

        this.slideAccessor = slideAccessor;

        StringBuffer sb = new StringBuffer ();
        XRequestedPropertiesImpl reqProp =
            (XRequestedPropertiesImpl)query.requestedProperties();

        boolean isFirst = true;
        isThereSomethingToDo = false;

        if (reqProp.hasXPathProperties()) {

            Iterator it = reqProp.xPathIterator();
            while (it.hasNext()) {
                XPathProperty xProp = (XPathProperty)it.next();
                XPathContentQuery contentQuery = xProp.getXPathContentQuery();

                XPath xPath = contentQuery.getXPath();
                String lastNode = xPath.getLastNode();

                if (!ignoreThis (contentQuery, lastNode)) {
                    isThereSomethingToDo = true;
                    if (!isFirst)
                        sb.append (" | ");
                    else
                        isFirst = false;

                    optimizedQueries.put (lastNode, contentQuery);
                    xPath.setContentQueryFilter (contentQueryFilter);
                    sb.append (xPath.getXpathString());
                }
            }
            optimizedQuery = sb.toString();
            // System.out.println (optimizedQuery);
        }


    }

    /**
     * check, if the contentQuery may be added to the set of optimzed queries.
     *
     * @param    contentQuery        a  XPathContentQuery
     * @param    lastNode            a  String
     *
     * @return   a boolean
     *
     */
    private boolean ignoreThis (XPathContentQuery contentQuery, String lastNode) {
        boolean result = false;

        if (optimizedQueries.containsKey (lastNode)) {
            result = true;
        }
        else if (lastNode.startsWith ("@")) {
            result = true;
        }
        else if (contentQuery.isResolved()) {
            result = true;
        }

        return result;
    }




    /**
     * Executes the optimized query. The result is scanned, the result else
     * are passed to the XContentQueries to which they belong (identified by the
     * name of the result element ==> lastNode). After execution the affected
     * queries are marked as resolved
     *
     * @throws   XDatastoreException
     * @throws   InvalidQueryException
     *
     */
    void resolveXPathProperties ()
        throws XException, InvalidQueryException {

        if (isThereSomethingToDo) {
            String collection = slideAccessor.getParameter(XGlobals.TAMINO_COLLECTION);
            XConnection con = null;
            XConnectionKey conKey = new XConnectionKey (slideAccessor);

            try {
                con = XConnectionPool.getInstance().getXConnection (conKey);
                TXMLObjectAccessor objAccessor =
                    con.newXMLJObjectAccessor (collection);

                TQuery tQuery = TQuery.newInstance (optimizedQuery);
                TXMLObject txmlObject = null;

                //System.out.println(xPathWithFilter);

                TResponse response;
                response = objAccessor.query (tQuery);

                if (!response.getReturnValue().equals("0"))
                    throw new InvalidQueryException
                        ("query failed, return value is " + response.getReturnValue());

                // comes here only in context of xpathproperty, no limit
                // (limited by contentQueryFilter)

                TXMLObjectIterator it = response.getXMLObjectIterator();
                while (it.hasNext()) {
                    txmlObject = it.next();

                    Element resultElement = (Element)txmlObject.getElement();
                    String key = resultElement.getName();

                    XPathContentQuery xpq =
                        (XPathContentQuery)optimizedQueries.get (key);

                    XContentId contentId = new XContentId (txmlObject.getId(),
                                                           xpq.getXPath().getSchema(),
                                                           true);

                    xpq.addResultObject (resultElement, contentId);
                }

                setResolved();


            } catch (InvalidQueryException e) {
                e.fillInStackTrace();
                throw (e);
            } catch (Exception e) {
                throw new XDatastoreException (e);
            }
            finally {
                con.close();
            }
        }
    }

    /**
     * Method setResolved
     *
     */
    private void setResolved() {
        Iterator contentQueryIterator = optimizedQueries.values().iterator();

        while (contentQueryIterator.hasNext()) {
            ((XPathContentQuery)contentQueryIterator.next()).setIsResolved();
        }
    }
}

