/*
 * $Header: /home/cvspublic/jakarta-slide/src/share/org/apache/slide/search/basic/OrderBy.java,v 1.11 2004/11/29 18:48:46 luetzkendorf Exp $
 * $Revision: 1.11 $
 * $Date: 2004/11/29 18:48:46 $
 *
 * ====================================================================
 *
 * Copyright 1999-2002 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.search.basic;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import org.apache.slide.common.PropertyName;
import org.apache.slide.search.CompareHint;
import org.apache.slide.search.InvalidQueryException;
import org.jdom.Attribute;
import org.jdom.Element;
import org.jdom.Namespace;


/**
 * Encapsulate an OrderBy expression. Also supplies a Comparator according
 * to the specified parameters in the OrderBy expression,
 *
 * @version $Revision: 1.11 $
 */
public class OrderBy {

    /** the comparator according to this expression */
    private Comparator theComparator = new _Comparator ();

    /** all orderby elements of this expression */
    protected List orderByElements = new ArrayList ();

    /**
     * initializes an OrderBy.
     *
     * @param orderByElement  the JDOM element containing the
     *        orderBy expression
     */
    public void init (Element orderByElement) throws InvalidQueryException {
        Namespace nameSpace = orderByElement.getNamespace ();
        Iterator it =
            orderByElement.getChildren (Literals.ORDER, nameSpace).iterator();

        while (it.hasNext()) {
            Element order = (Element) it.next();
            PropertyName p = getProperty (order);
            boolean isAscending = isAscending (order);
            boolean isCaseSensitive = isCaseSensitive (order);

            orderByElements.add
                (createCompareHint (p, isAscending, isCaseSensitive));
        }
    }

    protected CompareHint createCompareHint (PropertyName prop,
                                             boolean isAscending,
                                             boolean isCaseSensitive) {
        return new CompareHint (prop, isAscending, isCaseSensitive) ;
    }

    /**
     * Method getComparator
     *
     * @return   the comparator according to the OrderBy expression
     *
     */
    public Comparator getComparator () {
        return theComparator;
    }


    /**
     * Method isCaseSensitive
     *
     * @param    order               an order Element
     *
     * @return   true if this order element shall regard case
     *
     */
    private boolean isCaseSensitive (Element order) {
        boolean result = true;
        Attribute caseSens =
            order.getAttribute (Literals.CASESENSITIVE, order.getNamespace());

        if (caseSens != null) {
            try {
                result = caseSens.getBooleanValue();
            }
            catch (org.jdom.DataConversionException e) {
                e.printStackTrace();
            }
        }
        return result;
    }



    /**
     * Method isAscending
     *
     * @param    order               an Element
     *
     * @return   a boolean
     *
     * @throws   InvalidQueryException if ascending and descending is supplied
     *
     */
    private boolean isAscending (Element order) throws InvalidQueryException {
        Element asc = order.getChild (Literals.ASCENDING, order.getNamespace());
        Element desc = order.getChild (Literals.DESCENDING, order.getNamespace());
        boolean result = true;

        if (asc != null && desc != null)
            throw new InvalidQueryException ("either ascending or descending may be supplied");

        if (desc != null)
            result = false;

        return result;
    }

    /**
     * Method getPropName
     *
     * @param    order               an Element
     *
     * @return   a String
     *
     * @throws   InvalidQueryException
     *
     */
    private PropertyName getProperty (Element orderElem) throws InvalidQueryException {

        List propList =
            orderElem.getChild (Literals.PROP, orderElem.getNamespace()).getChildren();

        if (propList.size() != 1)
            throw new InvalidQueryException
                ("Expected exactly 1 prop element, found " + propList.size());

        Element propElem = (Element)propList.get(0);

        return createProperty (propElem);
    }

    /**
     * Method createProperty
     *
     * @param    propElem            an Element
     *
     * @return   a _Property
     *
     */
    protected PropertyName createProperty (Element propElem) throws InvalidQueryException {
        String name = propElem.getName();
        String nameSpace = propElem.getNamespace().getURI();
        return PropertyName.getPropertyName(name, nameSpace);
    }


    /**
     * a comparator that sorts according to the OrderBy element
     */
    private class _Comparator implements Comparator  {

        /**
         * Compares two ComparableResources. Two resources are equal, if they
         * have the same URI. Each <order> element within <orderby> generates
         * a CompareHint object containing the property to compare and ascending
         * / descending. If the two resources are not equal, they are compared
         * according the first CompareHint. If this one returns "equal" (0),
         * they are compared according the next CompareHint and so on. If the
         * two resources are equal according all CompareHints, the first one is
         * considered to be greater than the second.
         *
         * @param    o1                  an Object
         * @param    o2                  an Object
         *
         * @return   an int
         *
         */
        public int compare(Object o1, Object o2) {
            ComparableResource r1 = (ComparableResource)o1;
            ComparableResource r2 = (ComparableResource)o2;

            int result = 0;
            if (r1.getInternalHref().equals (r2.getInternalHref())) {
                result = 0;
            }
            else {

                Iterator it = orderByElements.iterator();
                while (it.hasNext() && result == 0) {
                    CompareHint obe = (CompareHint)it.next();
                    result = r1.compareTo (r2, obe);
                }

                if (result == 0)
                    result = 1;
            }

            return result;
        }
    }

}

