org.kuali.kra.budget.calculator.QueryList.java Source code

Java tutorial

Introduction

Here is the source code for org.kuali.kra.budget.calculator.QueryList.java

Source

/*
 * Copyright 2005-2013 The Kuali Foundation
 * 
 * Licensed under the Educational Community 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.osedu.org/licenses/ECL-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.kuali.kra.budget.calculator;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.RandomAccess;

import org.kuali.kra.budget.BudgetDecimal;
import org.kuali.kra.budget.calculator.query.Equals;
import org.kuali.kra.budget.calculator.query.GreaterThan;
import org.kuali.kra.budget.calculator.query.LesserThan;
import org.kuali.kra.budget.calculator.query.Operator;
import org.kuali.kra.budget.calculator.query.RelationalOperator;
import org.kuali.rice.core.api.util.type.KualiDecimal;

/**
 * This class...
 */
public final class QueryList<E> implements List<E>, RandomAccess, Cloneable, Serializable {

    private static final long serialVersionUID = -3215265492607686197L;
    private static final org.apache.commons.logging.Log LOG = org.apache.commons.logging.LogFactory
            .getLog(QueryList.class);
    //using delegation to make it easier to swap out underlying implementation
    //also helps with possible inheritance bugs...
    //cannot make final because of clone otherwise this should always assumed to be non-null upon construction
    private /*final*/ ArrayList<E> backingList;

    public QueryList() {
        this.backingList = new ArrayList<E>();
    }

    public QueryList(Collection<E> dataList) {
        this.backingList = new ArrayList<E>(dataList);
    }

    /** Overridden method for sorting. By default it will sort in ascending order.
     * sorts the QueryList by the fieldName in ascending or descending order.
     * Note: the field Object should be of Comparable type.
     * @param fieldName field which is used to sort the bean.
     * @return boolean indicating whether the sort is completed successfully or not.
     */
    public boolean sort(String fieldName) {
        return sort(fieldName, true);
    }

    /**
     * sorts the QueryList by the fieldName in ascending or descending order.
     * Note: the field Objects should be of Comparable type.
     * @param fieldName field which is used to sort the bean.
     * @param ascending if true sorting is done in ascending order,
     * else sorting is done in descending order.
     * @return boolean indicating whether the sort is completed successfully or not.
     */
    public boolean sort(String fieldName, boolean ascending) {
        return sort(fieldName, ascending, false);
    }

    /**
     * sorts the QueryList by the fieldName in ascending or descending order.
     * Note: the field Object should be of Comparable type.
     * @return boolean indicating whether the sort is completed successfully or not.
     * @param ignoreCase use only when comparing strings. as default implementation uses case sensitive comparison.
     * @param fieldName field which is used to sort the bean.
     * @param ascending if true sorting is done in ascending order,
     * else sorting is done in descending order.
     */
    @SuppressWarnings("rawtypes")
    public boolean sort(String fieldName, boolean ascending, boolean ignoreCase) {
        Object current, next;
        int compareValue = 0;
        Field field = null;
        Method method = null;
        if (this.size() == 0) {
            return false;
        }
        Class dataClass = get(0).getClass();
        String methodName = null;
        try {
            field = dataClass.getDeclaredField(fieldName);
            if (!field.isAccessible()) {
                throw new NoSuchFieldException();
            }
        } catch (NoSuchFieldException noSuchFieldException) {
            //field not available. Use method invokation.
            try {
                methodName = "get" + (fieldName.charAt(0) + "").toUpperCase() + fieldName.substring(1);
                method = dataClass.getMethod(methodName, null);
            } catch (NoSuchMethodException noSuchMethodException) {
                noSuchMethodException.printStackTrace();
                return false;
            }
        }

        for (int index = 0; index < size() - 1; index++) {
            for (int nextIndex = index + 1; nextIndex < size(); nextIndex++) {
                current = get(index);
                next = get(nextIndex);
                //Check if current and next implements Comparable else can't compare.
                //so return without comparing.May be we can have an exception for this purpose.
                try {
                    if (field != null && field.isAccessible()) {
                        Comparable thisObj = (Comparable) field.get(current);
                        Comparable otherObj = (Comparable) field.get(next);
                        if (thisObj == null) {
                            compareValue = -1;
                        } else if (otherObj == null) {
                            compareValue = 1;
                        } else {
                            if (thisObj instanceof String && ignoreCase) {
                                compareValue = ((String) thisObj).compareToIgnoreCase((String) otherObj);
                            } else {
                                compareValue = thisObj.compareTo(otherObj);
                            }
                        }
                    } else {
                        Comparable thisObj = null;
                        Comparable otherObj = null;
                        if (methodName != null) {
                            Method thisObjMethod = current.getClass().getMethod(methodName, null);
                            Method otherObjMethod = next.getClass().getMethod(methodName, null);
                            thisObj = (Comparable) thisObjMethod.invoke(current, null);
                            otherObj = (Comparable) otherObjMethod.invoke(next, null);
                        } else {
                            thisObj = (Comparable) method.invoke(current, null);
                            otherObj = (Comparable) method.invoke(next, null);
                        }
                        if (thisObj == null) {
                            compareValue = -1;
                        } else if (otherObj == null) {
                            compareValue = 1;
                        } else {
                            if (thisObj instanceof String && ignoreCase) {
                                compareValue = ((String) thisObj).compareToIgnoreCase((String) otherObj);
                            } else {
                                compareValue = thisObj.compareTo(otherObj);
                            }
                        }
                    }
                } catch (IllegalAccessException illegalAccessException) {
                    LOG.warn(illegalAccessException.getMessage());
                    return false;
                } catch (InvocationTargetException invocationTargetException) {
                    LOG.warn(invocationTargetException.getMessage(), invocationTargetException);
                    return false;
                } catch (SecurityException e) {
                    LOG.warn(e.getMessage(), e);
                    return false;
                } catch (NoSuchMethodException e) {
                    LOG.warn(e.getMessage(), e);
                    return false;
                }

                if (ascending && compareValue > 0) {
                    E temp = get(index);
                    set(index, get(nextIndex));
                    set(nextIndex, temp);
                } else if (!ascending && compareValue < 0) {
                    E temp = get(index);
                    set(index, get(nextIndex));
                    set(nextIndex, temp);
                }

            }
        }
        return true;
    }

    /** sorts the QueryList by the fieldNames in ascending or descending order.
     * Note: the field Objects should be of Comparable type.
     * @param fieldNames fields which is used to sort the bean.
     * @param ascending if true sorting is done in ascending order,
     * else sorting is done in descending order.
     * @return boolean indicating whether the sort is completed successfully or not.
     * Modified code for the better performance and proper sorting.
     *Added by Sharath and Nadh
     */
    public boolean sort(String fieldNames[], boolean ascending) {
        boolean[] asce = new boolean[1];
        asce[0] = ascending;
        return sort(fieldNames, asce);
    }

    /** sorts the QueryList by the fieldNames in ascending or descending order.
     * Note: the field Objects should be of Comparable type.
     * @param fieldNames fields which is used to sort the bean.
     * @param ascending if true sorting is done in ascending order,
     * else sorting is done in descending order.
     * @return boolean indicating whether the sort is completed successfully or not.
     * Modified code for the better performance and proper sorting.
     *Added by Sharath and Nadh
     */
    public boolean sort(String fieldNames[], boolean ascending[]) {
        int index, nextIndex;
        E current, other;
        boolean swap;
        Operator operator;

        for (index = 1; index < size(); index++) {
            current = get(index);
            nextIndex = index;

            Operator relOperator[] = getRelationalOperators(fieldNames, current, ascending);
            Operator eqOperator[] = getEqualsOperators(fieldNames, current);

            while (nextIndex > 0) {
                other = get(nextIndex - 1);
                swap = false;
                for (int operatorIndex = 0; operatorIndex < relOperator.length; operatorIndex++) {
                    operator = relOperator[operatorIndex];
                    if (operator.getResult(other)) {
                        swap = true;
                        break;
                    } else {
                        //check with equals
                        operator = eqOperator[operatorIndex];
                        if (operator.getResult(other)) {
                            continue;
                        } else {
                            break;
                        }
                    }
                } //End For

                if (swap) {
                    set(nextIndex, get(nextIndex - 1));
                    nextIndex = nextIndex - 1;
                } else {
                    break;
                } //End if-else
            } //End while
            set(nextIndex, current);
        }
        return true;
    }

    /** For a given fields, it will compare the values in the value objects
     * and returns an array of relational operator for the given fields
     */
    private Operator[] getRelationalOperators(String field[], Object baseBean, boolean ascending[]) {
        //Make all Relational Operators.
        RelationalOperator relationalOperator[] = new RelationalOperator[field.length];
        boolean isascending = !ascending[0];
        for (int index = 0; index < field.length; index++) {
            if (ascending.length > 1)
                isascending = !ascending[index];

            if (!isascending) {
                GreaterThan gt = new GreaterThan(field[index], (Comparable) getFieldValue(field[index], baseBean));
                relationalOperator[index] = gt;
            } else if (isascending) {
                LesserThan lt = new LesserThan(field[index], (Comparable) getFieldValue(field[index], baseBean));
                relationalOperator[index] = lt;
            }
        }
        return relationalOperator;
    }

    /** For a given fields, it will compare the values in the value objects
     * and returns an array of equal operator for the given fields
     */
    private Operator[] getEqualsOperators(String field[], Object baseBean) {
        //Make all Equals Operators.
        Equals equals[] = new Equals[field.length];
        for (int index = 0; index < equals.length; index++) {
            Equals eq = new Equals(field[index], (Comparable) getFieldValue(field[index], baseBean));
            equals[index] = eq;
        }
        return equals;
    }

    /** filters querylist which contains only those beans which satisfies the operator condition.
     * @param operator Operator.
     * @return querylist which contains only those beans which satisfies the operator condition.
     */
    public QueryList<E> filter(Operator operator) {
        QueryList<E> filterResult = new QueryList<E>();
        if (this.size() > 0) {
            E baseBean = null;
            for (int index = 0; index < this.size(); index++) {
                baseBean = this.get(index);
                if (operator.getResult(baseBean) == true) {
                    filterResult.add(baseBean);
                }
            }
        }
        return filterResult;
    }

    /** calculates the sum of the field in this QueryList.
     * @param fieldName field of bean whose sum has to be calculated.
     * @return sum.
     */
    public double sum(String fieldName) {
        return sum(fieldName, null, null);
    }//End Sum

    /** calculates the sum of the Objects of the specified field in this QueryList.
     * @param fieldName field of bean whose sum has to be calculated.
     * @return sum as BigDecimal Object.
     */
    public BudgetDecimal sumObjects(String fieldName) {
        return new BudgetDecimal(sum(fieldName, null, null));
    }//End Sum

    /** calculates the sum of the field in this QueryList.
     * @param fieldName field of bean whose sum has to be calculated.
     * @param arg argument for the getter method of field if it takes any argumnt,
     * else can be null.
     * @param value value for the argument, else can be null.
     * @return returns sum.
     */
    public double sum(String fieldName, Class arg, Object value) {
        if (size() == 0) {
            return 0;
        }

        Object current;
        Field field = null;
        Method method = null;
        Class dataClass = get(0).getClass();
        double sum = 0;

        try {
            field = dataClass.getDeclaredField(fieldName);

            Class fieldClass = field.getType();
            if (!(fieldClass.equals(Integer.class) || fieldClass.equals(Long.class)
                    || fieldClass.equals(Double.class) || fieldClass.equals(Float.class)
                    || fieldClass.equals(BigDecimal.class) || fieldClass.equals(BigInteger.class)
                    || fieldClass.equals(BudgetDecimal.class) || fieldClass.equals(KualiDecimal.class)
                    || fieldClass.equals(int.class) || fieldClass.equals(long.class)
                    || fieldClass.equals(float.class) || fieldClass.equals(double.class))) {
                throw new UnsupportedOperationException("Data Type not numeric");
            }

            if (!field.isAccessible()) {
                throw new NoSuchFieldException();
            }
        } catch (NoSuchFieldException noSuchFieldException) {
            try {
                String methodName = "get" + (fieldName.charAt(0) + "").toUpperCase() + fieldName.substring(1);
                if (arg != null) {
                    Class args[] = { arg };
                    method = dataClass.getMethod(methodName, args);
                } else {
                    method = dataClass.getMethod(methodName, null);
                }
            } catch (NoSuchMethodException noSuchMethodException) {
                noSuchMethodException.printStackTrace();
            }
        }

        for (int index = 0; index < size(); index++) {
            current = get(index);

            try {
                if (field != null && field.isAccessible()) {
                    sum = sum + Double.parseDouble(((Comparable) field.get(current)).toString());
                } else {
                    Comparable dataValue;
                    if (value != null) {
                        Object values[] = { value };
                        dataValue = (Comparable) method.invoke(current, values);
                    } else {
                        dataValue = (Comparable) method.invoke(current, null);
                    }
                    if (dataValue != null) {
                        sum += Double.parseDouble(dataValue.toString());
                    }

                }
            } catch (IllegalAccessException illegalAccessException) {
                illegalAccessException.printStackTrace();
            } catch (InvocationTargetException invocationTargetException) {
                invocationTargetException.printStackTrace();
            }
        }
        return sum;
    }

    /** calculates the sum of the field in this QueryList.
     * @param fieldName field of bean whose sum has to be calculated.
     * @param operator to get filrtered vector on which sum will be called.
     * @return returns sum.
     */
    public double sum(String fieldName, Operator operator) {
        return filter(operator).sum(fieldName);
    }

    /** calculates the sum of the field in this QueryList.
     * @param fieldName field of bean whose sum has to be calculated.
     * @param operator to get filrtered vector on which sum will be called.
     * @return returns sum.
     */
    public BudgetDecimal sumObjects(String fieldName, Operator operator) {
        return filter(operator).sumObjects(fieldName);
    }

    /** returns the field value in the base bean for the specified field.
     * @param fieldName fieldname whose value has to be got.
     * @param baseBean Bean containing the field.
     * @return value of the field.
     */
    private Object getFieldValue(String fieldName, Object baseBean) {
        Field field = null;
        Method method = null;
        Class dataClass = baseBean.getClass();
        Object value = null;

        try {
            field = dataClass.getDeclaredField(fieldName);
            if (!field.isAccessible()) {
                throw new NoSuchFieldException();
            }
        } catch (NoSuchFieldException noSuchFieldException) {
            try {
                String methodName = "get" + (fieldName.charAt(0) + "").toUpperCase() + fieldName.substring(1);
                method = dataClass.getMethod(methodName, null);
            } catch (NoSuchMethodException noSuchMethodException) {
                noSuchMethodException.printStackTrace();
            }
        }

        try {
            if (field != null && field.isAccessible()) {
                value = field.get(baseBean);
            } else {
                value = method.invoke(baseBean, null);
            }
        } catch (IllegalAccessException illegalAccessException) {
            illegalAccessException.printStackTrace();
        } catch (InvocationTargetException invocationTargetException) {
            invocationTargetException.printStackTrace();
        }
        return value;
    }

    /** {@inheritDoc} */
    @SuppressWarnings("unchecked")
    @Override
    public QueryList<E> clone() {

        final QueryList<E> ql;

        try {
            ql = (QueryList<E>) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new AssertionError("not Cloneable");
        }

        ArrayList<E> bl = (ArrayList<E>) this.backingList.clone();
        ql.backingList = new ArrayList(bl);

        return ql;
    }

    //delegate methods.

    /** {@inheritDoc} */
    public boolean add(E o) {
        return this.backingList.add(o);
    }

    /** {@inheritDoc} */
    public void add(int index, E element) {
        this.backingList.add(index, element);
    }

    /** {@inheritDoc} */
    public boolean addAll(Collection<? extends E> c) {
        return this.backingList.addAll(c);
    }

    /** {@inheritDoc} */
    public boolean addAll(int index, Collection<? extends E> c) {
        return this.backingList.addAll(index, c);
    }

    /** {@inheritDoc} */
    public void clear() {
        this.backingList.clear();
    }

    /** {@inheritDoc} */
    public boolean contains(Object o) {
        return this.backingList.contains(o);
    }

    /** {@inheritDoc} */
    public boolean containsAll(Collection<?> c) {
        return this.backingList.containsAll(c);
    }

    /** {@inheritDoc} */
    @Override
    public boolean equals(Object o) {
        return this.backingList.equals(o);
    }

    /** {@inheritDoc} */
    public E get(int index) {
        return this.backingList.get(index);
    }

    /** {@inheritDoc} */
    @Override
    public int hashCode() {
        return this.backingList.hashCode();
    }

    /** {@inheritDoc} */
    public int indexOf(Object o) {
        return this.backingList.indexOf(o);
    }

    /** {@inheritDoc} */
    public boolean isEmpty() {
        return this.backingList.isEmpty();
    }

    /** {@inheritDoc} */
    public Iterator<E> iterator() {
        return this.backingList.iterator();
    }

    /** {@inheritDoc} */
    public int lastIndexOf(Object o) {
        return this.backingList.lastIndexOf(o);
    }

    /** {@inheritDoc} */
    public ListIterator<E> listIterator() {
        return this.backingList.listIterator();
    }

    /** {@inheritDoc} */
    public ListIterator<E> listIterator(int index) {
        return this.backingList.listIterator(index);
    }

    /** {@inheritDoc} */
    public E remove(int index) {
        return this.backingList.remove(index);
    }

    /** {@inheritDoc} */
    public boolean remove(Object o) {
        return this.backingList.remove(o);
    }

    /** {@inheritDoc} */
    public boolean removeAll(Collection<?> c) {
        return this.backingList.removeAll(c);
    }

    /** {@inheritDoc} */
    public boolean retainAll(Collection<?> c) {
        return this.backingList.retainAll(c);
    }

    /** {@inheritDoc} */
    public E set(int index, E element) {
        return this.backingList.set(index, element);
    }

    /** {@inheritDoc} */
    public int size() {
        return this.backingList.size();
    }

    /** {@inheritDoc} */
    public List<E> subList(int fromIndex, int toIndex) {
        return this.backingList.subList(fromIndex, toIndex);
    }

    /** {@inheritDoc} */
    public Object[] toArray() {
        return this.backingList.toArray();
    }

    /** {@inheritDoc} */
    public <T> T[] toArray(T[] a) {
        return this.backingList.toArray(a);
    }

    /** {@inheritDoc} */
    @Override
    public String toString() {
        return this.backingList.toString();
    }

    public List<E> toArrayList() {
        return this.backingList;
    }
}