Package org.jboss.dashboard.commons.filter

Source Code of org.jboss.dashboard.commons.filter.AbstractFilter

/**
* Copyright (C) 2012 JBoss Inc
*
* 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.jboss.dashboard.commons.filter;

import org.jboss.dashboard.commons.text.StringUtil;
import org.jboss.dashboard.commons.comparator.ComparatorUtils;

import java.util.*;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.collections.CollectionUtils;
import bsh.EvalError;
import bsh.Interpreter;

/**
* An abstract that defines those basic services a entity filter.
*/
public abstract class AbstractFilter implements FilterByCriteria, Cloneable {

    /**
     * Filter list.
     * Each entry contains a filter properties array of Object[9]
     * (property id, filter max/min interval, max/min values included, set of allowed values and allow mode, property alias, extra info).
     * The position in the list determines the filter priority in descending order:
     * the first filter into the list is the most prioritary.
     */
    protected List filterProperties;

    /**
     * The filter condition logical expression.
     */
    protected String filterCondition;

    /**
     * Wildcard symbol.
     */
    protected String wildcard;

    /**
     * Greater than symbol.
     */
    protected String gt;

    /**
     * Less than symbol.
     */
    protected String lt;

    /**
     * Greater or equals than symbol.
     */
    protected String gtOrEq;

    /**
     * Less or equals than symbol.
     */
    protected String ltOrEq;

    /**
     * The criteria locale.
     */
    protected Locale locale;

    /**
     * Map used to store properties filter results when evauating the overall filter condition.
     */
    protected transient Map _filterVarValues;

    /**
     * BSH interpreter per thread cache.
     */
    protected transient ThreadLocal _bshIntepreterThread;

    /**
     * Logger
     */
    private static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(AbstractFilter.class);

    public AbstractFilter() {
        filterProperties = new ArrayList();
        filterCondition = null;
        wildcard = "\u002a";
        gt = "\u003e";
        lt = "\u003c";
        gtOrEq = "\u003e\u003d";
        ltOrEq = "\u003c\u003d";
        locale = Locale.getDefault();
        _filterVarValues = new HashMap();
        _bshIntepreterThread = new ThreadLocal();
    }

    public Locale getLocale() {
        return locale;
    }

    public void setLocale(Locale locale) {
        this.locale = locale;
    }

    public String getWildcard() {
        return wildcard;
    }

    public void setWildcard(String wildcard) {
        this.wildcard = wildcard;
    }

    public String getGt() {
        return gt;
    }

    public void setGt(String gt) {
        this.gt = gt;
    }

    public String getGtOrEq() {
        return gtOrEq;
    }

    public void setGtOrEq(String gtOrEq) {
        this.gtOrEq = gtOrEq;
    }

    public String getLt() {
        return lt;
    }

    public void setLt(String lt) {
        this.lt = lt;
    }

    public String getLtOrEq() {
        return ltOrEq;
    }

    public void setLtOrEq(String ltOrEq) {
        this.ltOrEq = ltOrEq;
    }

    public void addProperties(FilterByCriteria filter) {
        if (filter == null) return;
        String[] propIds = filter.getPropertyIds();
        for (int i = 0; i < propIds.length; i++) {
            String propId = propIds[i];
            Object minValue = filter.getPropertyMinValue(propId);
            Object maxValue = filter.getPropertyMaxValue(propId);
            boolean minInc = filter.minValueIncluded(propId);
            boolean maxInc = filter.maxValueIncluded(propId);
            Collection allowed = filter.getPropertyAllowedValues(propId);
            int allowMode = filter.getPropertyAllowMode(propId);
            addProperty(propId, minValue, minInc, maxValue, maxInc, allowed, allowMode);
            setExtraInfo(propId, filter.getExtraInfo(propId));
            setVariableName(propId, getVariableName(propId));
        }
    }

    public void addProperty(String propertyId,
                            Object minValue, boolean minValueIncluded,
                            Object maxValue, boolean maxValueIncluded,
                            Collection allowedValues, int allowMode) {

        // Remove old values.
        Iterator it = filterProperties.iterator();
        while (it.hasNext()) {
            Object[] filterProps = (Object[]) it.next();
            if (filterProps[0].equals(propertyId.trim())) it.remove();
        }
        // Register new ones.
        Object[] filterProps = new Object[] {propertyId.trim(), // Identifier
                                    minValue, minValueIncluded, // Min. Value
                                    maxValue, maxValueIncluded, // Max. value
                                    allowedValues == null ? allowedValues : new ArrayList(allowedValues), // Allowed values
                                    new Integer(allowMode), // Allowed values mode
                                    StringUtil.toJavaIdentifier(propertyId.trim())// Variable name
                                    propertyId.trim()}; // Extra info.
        filterProperties.add(filterProps);

        // If property set is modified then the current filter condition must be discarded.
        setFilterCondition(null);
    }

    public boolean addProperty(String propertyId, String filterCriteria) {
        if (filterCriteria != null) filterCriteria = filterCriteria.trim();
        String[] filterValues = StringUtils.split(filterCriteria, ",");
        if (filterValues.length == 0) return false;

        // Construct property filter.
        Object minValue = null;
        boolean minIncl = false;
        Object maxValue = null;
        boolean maxIncl = false;
        Set allowedValues = new HashSet();
        for (int i = 0; i < filterValues.length; i++) {
            // Catch min. value.
            String filterValue = filterValues[i];
            if (filterValue.startsWith(getGt())) {
                if (filterValue.startsWith(getGtOrEq())) minIncl = true;
                if (minIncl) filterValue = filterValue.substring(getGtOrEq().length()).trim();
                else filterValue = filterValue.substring(getGt().length()).trim();
                minValue = filterValue;
            }
            // Catch max. value.
            else if (filterValue.startsWith(getLt())) {
                if (filterValue.startsWith(getLtOrEq())) maxIncl = true;
                if (maxIncl) filterValue = filterValue.substring(getLtOrEq().length()).trim();
                else filterValue = filterValue.substring(getLt().length()).trim();
                maxValue = filterValue;
            }
            // Catch allowed value.
            else {
                allowedValues.add(filterValue);
            }
        }
        // Register property.
        addProperty(propertyId, minValue, minIncl, maxValue, maxIncl, allowedValues, ALLOW_ANY);
        return true;
    }

    public void removeProperty(String propertyId) {
        Object[] filterProps = getProperty(propertyId);
        if (filterProps != null) {
            filterProperties.remove(filterProps);

            // If property set is modified then the current filter condition must be discarded.
            setFilterCondition(null);
        }
    }

    public void removeProperties(FilterByCriteria filter) {
        if (filter == null) return;
        String[] propIds = filter.getPropertyIds();
        for (int i = 0; i < propIds.length; i++) {
            String propId = propIds[i];
            removeProperty(propId);
        }
    }

    public void removeAllProperty() {
        filterProperties.clear();

        // If property set is modified then the current filter condition must be discarded.
        setFilterCondition(null);

    }

    public int getPropertyPriority(String propertyId) {
        Object[] filterProps = getProperty(propertyId);
        if (filterProps != null) return filterProperties.indexOf(filterProps) + 1;
        return 0;
    }

    public Comparable getPropertyMinValue(String propertyId) {
        Object[] prop = getProperty(propertyId);
        if (prop == null) return null;
        return (Comparable)prop[1];
    }

    public boolean minValueIncluded(String propertyId) {
        Object[] prop = getProperty(propertyId);
        if (prop == null) return false;
        return ((Boolean)prop[2]).booleanValue();
    }

    public Comparable getPropertyMaxValue(String propertyId) {
        Object[] prop = getProperty(propertyId);
        if (prop == null) return null;
        return (Comparable)prop[3];
    }

    public boolean maxValueIncluded(String propertyId) {
        Object[] prop = getProperty(propertyId);
        if (prop == null) return false;
        return ((Boolean)prop[4]).booleanValue();
    }

    public List getPropertyAllowedValues(String propertyId) {
        Object[] prop = getProperty(propertyId);
        if (prop == null) return new ArrayList();
        return (List)prop[5];
    }

    public int getPropertyAllowMode(String propertyId) {
        Object[] prop = getProperty(propertyId);
        if (prop == null) return ALLOW_ANY;
        return ((Integer)prop[6]).intValue();
    }

    public void setVariableName(String propertyId, String varName) {
        Object[] propArray = getProperty(propertyId);
        if (propArray != null) propArray[7] = varName;
    }

    public String getVariableName(String propertyId) {
        Object[] prop = getProperty(propertyId);
        if (prop == null) return null;
        return (String)prop[7];
    }

    public String getExtraInfo(String propertyId) {
        Object[] prop = getProperty(propertyId);
        if (prop == null) return null;
        return (String)prop[8];
    }

    public void setExtraInfo(String propertyId, String extraInfo) {
        Object[] propArray = getProperty(propertyId);
        if (propArray != null) propArray[8] = extraInfo;
    }

    public void setFilterCondition(String logicalExpression) {
        filterCondition = logicalExpression;
    }

    public String getFilterCondition() {
        if (filterCondition != null) return filterCondition;

        // By default an AND condition for all properties is calculated.
        StringBuffer buf = new StringBuffer();
        Iterator it = filterProperties.iterator();
        while (it.hasNext()) {
            Object[] filterProp = (Object[]) it.next();
            if (buf.length() > 0) buf.append(" AND ");
            String varName = (String) filterProp[7];
            buf.append(StringUtil.toJavaIdentifier(varName));
        }
        return buf.toString();
    }

    public String[] getPropertyIds() {
        String[] ids = new String[filterProperties.size()];
        for (int i = 0; i < ids.length; i++) {
            Object[] filterProps = (Object[]) filterProperties.get(i);
            ids[i] = (String) filterProps[0];
        }
        return ids;
    }

    public boolean containsProperty(String propertyId) {
        return getProperty(propertyId) != null;
    }

    public boolean containsProperty(Collection propIds) {
        if (propIds == null || propIds.isEmpty()) return false;
        Iterator it = propIds.iterator();
        while (it.hasNext()) {
            if (containsProperty(it.next().toString())) return true;
        }
        return false;
    }


    protected Object[] getProperty(String propertyId) {
        Iterator it = filterProperties.iterator();
        while (it.hasNext()) {
            Object[] filterProps = (Object[]) it.next();
            if (filterProps[0].equals(propertyId.trim())) return filterProps;
        }
        return null;
    }

    public String toString() {
        StringBuffer buf = new StringBuffer();
        Iterator it = filterProperties.iterator();
        while (it.hasNext()) {
            Object[] filterProp = (Object[]) it.next();
            buf.append("Property ").append(filterProp[0]).append(": ");
            for (int i = 1; i < filterProp.length; i++) buf.append(filterProp[i]).append(" ");
            buf.append("\r\n");
        }
        buf.append("Filter condition: ").append(filterCondition).append("");
        buf.append("\r\n");
        return buf.toString();
    }

    /**
     * Check if a value satisfies a property filter.
     * @param propertyId The property to pass.
     * @param value The value to check.
     */
    public boolean pass(String propertyId, Object value) throws IllegalArgumentException {
        // Check min. value.
        Object[] propertyFilter = getProperty(propertyId);
        Object minValue = propertyFilter[1];
        if (minValue != null) {
            boolean minValueIncluded = ((Boolean) propertyFilter[2]).booleanValue();
            if (value == null) return false;
            switch (compareBySimilarity(propertyId, minValue, value)) {
                case 1: return false;
                case 0: if (!minValueIncluded) return false;
            }
        }
        //log.debug("Min. value for property " + propertyId + " satisfied: " + minValue);

        // Check max. value.
        Object maxValue = propertyFilter[3];
        if (maxValue != null) {
            if (value == null) return false;
            boolean maxValueIncluded = ((Boolean) propertyFilter[4]).booleanValue();
            switch (compareBySimilarity(propertyId, maxValue, value)) {
                case -1: return false;
                case 0: if (!maxValueIncluded) return false;
            }
        }
        //log.debug("Max. value for property " + propertyId + " satisfied: " + maxValue);

        // Check allowed values.
        List allowedValues = (List) propertyFilter[5];
        int allowMode = ((Integer) propertyFilter[6]).intValue();
        if (!pass(propertyId, value, allowedValues, allowMode)) return false;
        //log.debug("Allowed values for property " + propertyId + " satisfied: " + allowedValues);

        // All checks satisfied.
        return true;
    }

    /**
     * Check if a given value (either a single object or a collection) satisfies a collection of allowed values.
     * @param value
     * @param allowedValues
     * @param allowMode See <code>ALLOW_</code> constants defined.
     */
    public boolean pass(String propertyId, Object value, List allowedValues, int allowMode) {
        // If the value is a collection , iterate over it to check all values.
        if (value instanceof Collection) {
            Collection values = (Collection) value;
            Iterator it = values.iterator();
            while (it.hasNext()) {
                Object aValue = it.next();
                boolean pass = passValue(propertyId, aValue, allowedValues, allowMode);
                if (pass) return true;
            }
            return false;
        }
        // The value is a single object.
        return passValue(propertyId, value, allowedValues, allowMode);
    }

    /**
     * Check if a given value satisfies a collection of allowed values.
     * @param value
     * @param allowedValues
     * @param allowMode See <code>ALLOW_</code> constants defined.
     */
    public boolean passValue(String propertyId, Object value, List allowedValues, int allowMode) {
        if (allowedValues == null || allowedValues.isEmpty()) return true;
        boolean anySatisfied = false;
        for (int i = 0; i < allowedValues.size(); i++) {
            Object allowedObj = allowedValues.get(i);
            boolean valueSatisfied = (compareBySimilarity(propertyId, allowedObj, value) == 0);
            switch(allowMode) {
                case ALLOW_ALL: if (!valueSatisfied) return false;
                case ALLOW_ANY: if (valueSatisfied) anySatisfied = true; break;
                case ALLOW_NONE: if (valueSatisfied) return false;
            }
        }
        // Checks satisfied.
        if (allowMode == ALLOW_ANY && !anySatisfied) return false;
        return true;
    }

    /**
     * Check if the given object instance matchs the specified pattern.
     * @param propertyId The property to compare.
     * @param pattern The pattern. In Strings wildcard is accepted.
     * @param value The object to check.
     * @return Equality comparison if pattern has no wildcards.
     */
    protected int compareBySimilarity(String propertyId, Object pattern, Object value) {
        // If pattern is wildcard-based then compare by similarity.
        // The values are formatted as the user views them.
        if (pattern instanceof String && ((String) pattern).indexOf(getWildcard()) != -1) {
            String s1 = formatForDisplay(propertyId, pattern).trim();
            String s2 = formatForDisplay(propertyId, value).trim();

            String target = StringUtil.replaceAll(s1, wildcard, "");
            if (s1.startsWith(wildcard) && s1.endsWith(wildcard) && s2.indexOf(target) != -1) return 0;
            if (s1.endsWith(wildcard) && s2.startsWith(target)) return 0;
            if (s1.startsWith(wildcard) && s2.endsWith(target)) return 0;
            return compare(target, s2, 1);
        }
        // If pattern is value-based then compare by equality.
        else {
            if (value instanceof String) {
                // Performance improvement: strings do not require formatting.
                String s1 = (String) value;
                String s2 = pattern != null ? pattern.toString() : null;
                return compare(s1, s2, 1);
            } else {
                // Non-string values need to be formatted for comparison first.
                String s1 = formatForComparison(propertyId, pattern).trim();
                String s2 = formatForComparison(propertyId, value).trim();
                return compare(s1, s2, 1);
            }
        }
    }

    // Filter interface

    public synchronized boolean pass(Map obj) {
        // If the object is null then return false;
        if (obj == null || obj.isEmpty()) return false;

        // If the filter is not based on a custom logical expression then simply ensure the filter is satisfied for all its properties.
        String[] propIdArray = getPropertyIds();
        if (filterCondition == null) {
            for (int i = 0; i < propIdArray.length; i++) {
                String propertyId = propIdArray[i];
                Object propertyValue = obj.get(propertyId);
                if (!pass(propertyId, propertyValue)) return false;
            }
            return true;
        }

        // If the filter is based on a logical expression then get the results of combine that properties as specified by that expression.
        String filterCondition = getFilterCondition();
        filterCondition = StringUtil.replaceAll(filterCondition, "AND", "\u0026\u0026");
        filterCondition = StringUtil.replaceAll(filterCondition, "OR", "\u007C\u007C");
        filterCondition = StringUtil.replaceAll(filterCondition, "NOT", "\u0021");
        _filterVarValues.clear();
        try {
            for (int i = 0; i < propIdArray.length; i++) {
                String propertyId = propIdArray[i];
                String propertyVar = getVariableName(propertyId);
                Object propertyValue = obj.get(propertyId);
                Boolean propertyPassed = Boolean.valueOf(pass(propertyId, propertyValue));
                _filterVarValues.put(propertyVar, propertyPassed);
            }
            Object result = executeBeanShellScript(filterCondition, _filterVarValues);
            if (result == null) return false;
            return ((Boolean)result).booleanValue();
        }
        catch (EvalError evalError) {
            log.error("Logical beanshell expression cannot be evaluated: " + filterCondition, evalError);
            return false;
        }
    }

    public synchronized boolean pass(Object obj) {
        // If the object is null then return false;
        if (obj == null) return false;

        // If the filter is not based on a custom logical expression then simply ensure the filter is satisfied for all its properties.
        String[] propIdArray = getPropertyIds();
        if (filterCondition == null) {
            for (int i = 0; i < propIdArray.length; i++) {
                String propertyId = propIdArray[i];
                Object propertyValue = getPropertyValue(propertyId, obj);
                if (!pass(propertyId, propertyValue)) return false;
            }
            return true;
        }

        // If the filter is based on a logical expression then get the results of combine that properties as specified by that expression.
        String filterCondition = getFilterCondition();
        filterCondition = StringUtil.replaceAll(filterCondition, "AND", "\u0026\u0026");
        filterCondition = StringUtil.replaceAll(filterCondition, "OR", "\u007C\u007C");
        filterCondition = StringUtil.replaceAll(filterCondition, "NOT", "\u0021");
        _filterVarValues.clear();
        try {
            for (int i = 0; i < propIdArray.length; i++) {
                String propertyId = propIdArray[i];
                String propertyVar = getVariableName(propertyId);
                Object propertyValue = getPropertyValue(propertyId, obj);
                Boolean propertyPassed = Boolean.valueOf(pass(propertyId, propertyValue));
                _filterVarValues.put(propertyId, propertyPassed);
                _filterVarValues.put(propertyVar, propertyPassed);
            }
            Object result = executeBeanShellScript(filterCondition, _filterVarValues);
            if (result == null) return false;
            return ((Boolean)result).booleanValue();
        }
        catch (EvalError evalError) {
            log.error("Logical beanshell expression cannot be evaluated: " + filterCondition, evalError);
            return false;
        }
    }

    /**
     * Executes a BeanShell script.
     * @param beanShellScript The script to execute in BeanShell format.
     * @param context A map of arguments to be passed to the script.
     * @return The script may return any object as result.
     */
    protected Object executeBeanShellScript(String beanShellScript, Map context) throws EvalError {
        // Ensure there is something to execute.
        if (beanShellScript == null || beanShellScript.trim().equals("")) return null;

        // Initialize the BeanShell interpreter.
        Interpreter bshInterpreter = (Interpreter) _bshIntepreterThread.get();
        if (bshInterpreter == null) {
            bshInterpreter = new Interpreter();
            ClassLoader loader = Thread.currentThread().getContextClassLoader();
            if (loader != null) bshInterpreter.setClassLoader(loader);
            _bshIntepreterThread.set(bshInterpreter);
        }

        // Set context.
        Iterator contextIt = context.keySet().iterator();
        while (contextIt.hasNext()) {
            String contextVar = (String) contextIt.next();
            bshInterpreter.set(contextVar, context.get(contextVar));
        }

        // Launch the BeanShell script.
        return bshInterpreter.eval(beanShellScript);
    }

    /**
     * Compares two comparable objects.
     * @param ordering: 1=ascending, -1=descending
     */
    public int compare(Comparable o1, Comparable o2, int ordering) {
        return ComparatorUtils.compare(o1, o2, ordering);
    }

    public FilterByCriteria cloneFilter() {
        try {
            AbstractFilter clone = (AbstractFilter) super.clone();
            clone._filterVarValues = new HashMap();
            clone._bshIntepreterThread = new ThreadLocal();
            clone.filterProperties = new ArrayList();
            Iterator it = filterProperties.iterator();
            while (it.hasNext()) {
                Object[] prop = (Object[]) it.next();
                Collection allowedValues = (Collection)prop[5];
                clone.filterProperties.add(new Object[] {prop[0], prop[1] , prop[2], prop[3], prop[4],
                        allowedValues == null ? null : new ArrayList(allowedValues), prop[6], prop[7], prop[8]});
            }
            return clone;
        } catch (CloneNotSupportedException e) {
            log.error("Clone exception.", e);
            return null;
        }
    }

    public boolean equals(Object obj) {
        try {
            AbstractFilter other = (AbstractFilter) obj;
            if (filterProperties.size() != other.filterProperties.size()) return false;

            for (int i=0; i < filterProperties.size(); i++) {
                Object[] tprop = (Object[]) filterProperties.get(i);
                String propId = (String) tprop[0];
                Object[] oprop = other.getProperty(propId);
                for (int j=0; j < tprop.length; j++) {
                    if (ComparatorUtils.compare(tprop[j], oprop[j], 1) != 0) {
                        return false;
                    }
                }
                // Allowed values must be strictly equals.
                Collection thisAllowed = getPropertyAllowedValues(propId);
                Collection otherAllowed = other.getPropertyAllowedValues(propId);
                if (CollectionUtils.intersection(thisAllowed, otherAllowed).size() != thisAllowed.size()) {
                    return false;
                }
            }
            return true;
        } catch (ClassCastException e) {
            return false;
        }
    }

    /**
     * Format a given property value as displayed for the the user.
     * @param propertyId The property identifier.
     * @param value The value to format.
     */
    protected abstract String formatForDisplay(String propertyId, Object value);

    /**
     * Format a given property value valid for string comparfison against other values.
     * @param propertyId The property identifier.
     * @param value The value to format.
     */
    protected abstract String formatForComparison(String propertyId, Object value);

    /**
     * Retrieve the value for a given property.
     * @param propertyId The property to search value for.
     * @param obj The object instance to ask for.
     * @return The value.
     */
    protected abstract Object getPropertyValue(String propertyId, Object obj);
}
TOP

Related Classes of org.jboss.dashboard.commons.filter.AbstractFilter

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.