Package com.ponysdk.impl.query.memory

Source Code of com.ponysdk.impl.query.memory.FilteringTools

/*
* Copyright (c) 2011 PonySDK
*  Owners:
*  Luciano Broussal  <luciano.broussal AT gmail.com>
*  Mathieu Barbier   <mathieu.barbier AT gmail.com>
*  Nicolas Ciaravola <nicolas.ciaravola.pro AT gmail.com>
*  WebSite:
*  http://code.google.com/p/pony-sdk/
*
* 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 com.ponysdk.impl.query.memory;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

import org.apache.commons.beanutils.BeanComparator;
import org.apache.commons.beanutils.PropertyUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.ponysdk.core.query.Criterion;
import com.ponysdk.core.query.Query;
import com.ponysdk.core.query.Query.QueryMode;
import com.ponysdk.core.query.Result;
import com.ponysdk.core.query.SortingType;

public final class FilteringTools {

    private static final String DOT_REGEX = "\\.";

    private static final Logger log = LoggerFactory.getLogger(FilteringTools.class);

    private static final String REGEX_END = "$";

    private static final String REGEX_MEMORY_SYNTAXE = ".*";

    private static final String REGEX_DATABASE_SYNTAXE = "%";

    private static final String REGEX_BEGIN = "^";

    private static final String EMPTY = "";

    private static final String KEYWORD_KEYS = "keys";

    private static final String KEYWORD_VALUES = "values";

    @SuppressWarnings("unchecked")
    public static <T> List<T> sortByPropertyName(List<T> data, String propertyName) {
        if (data == null) return null;
        try {
            Collections.sort(data, getPropertyComparator(propertyName));
        } catch (final ClassCastException e) {
            log.error("Failed to sort data collection [Property :" + propertyName + "]", e);
        }
        return data;
    }

    @SuppressWarnings("unchecked")
    public static <T> List<T> sortByPropertyName(List<T> data, String propertyName, Comparator<T> comparator) {
        if (data == null) return null;
        try {
            Collections.sort(data, getPropertyComparator(propertyName, comparator));
        } catch (final ClassCastException e) {
            log.error("Failed to sort data collection [Property :" + propertyName + "]", e);
        }
        return data;
    }

    public static final BeanComparator getPropertyComparator(String propertyName) {
        return new BeanComparator((null != propertyName) ? propertyName : "name");
    }

    public static <U> BeanComparator getPropertyComparator(String propertyName, Comparator<U> comparator) {
        return new BeanComparator((null != propertyName) ? propertyName : "name", comparator) {

            private static final long serialVersionUID = 5957817419869265091L;

            @SuppressWarnings("unchecked")
            @Override
            public int compare(Object o1, Object o2) {
                final String property = getProperty();
                if (property == null) { return getComparator().compare(o1, o2); }

                try {
                    final Object value1 = getValue(o1, property.split(DOT_REGEX));
                    final Object value2 = getValue(o2, property.split(DOT_REGEX));
                    return getComparator().compare(value1, value2);
                } catch (final Exception e) {
                    return getComparator().compare(o1, o2);
                }
            }
        };
    }

    public static String emptyIfNull(String str) {
        return (str != null) ? str : EMPTY;
    }

    public static List<String> filter(List<String> datas, String patternMatching) {
        if (patternMatching == null || datas == null) { return datas; }
        final List<String> validData = new ArrayList<String>();
        try {
            for (final String data : datas) {
                if (data == null) continue;
                if (data.equalsIgnoreCase(patternMatching)) {
                    validData.add(data);
                    continue;
                }
                // Now we can filter our data against the pattern
                final String text = normalisePattern(patternMatching.trim());
                final Pattern pattern = Pattern.compile(REGEX_BEGIN + text + REGEX_END, Pattern.CASE_INSENSITIVE);
                Matcher matcher = null;
                matcher = pattern.matcher(data);
                if ((matcher != null) && matcher.find()) {
                    validData.add(data);
                } else {
                    matcher = pattern.matcher("");
                    if ((matcher != null) && matcher.find()) {
                        validData.add(data);
                    }
                }
            }
        } catch (final PatternSyntaxException e) {
            if (log.isDebugEnabled()) {
                log.debug("bad pattern : " + patternMatching);
            }
        } catch (final Exception e) {
            log.error("Filter Error => pattern : " + patternMatching, e);
        }
        return validData;
    }

    public static <T> List<T> filter(List<T> datas, String fieldKey, Object value) {
        if (value == null || datas == null || fieldKey.equals(EMPTY)) { return datas; }
        final List<T> validData = new ArrayList<T>();
        try {
            final String[] pathDetails = fieldKey.split(DOT_REGEX);
            for (final T data : datas) {
                final Object val = getValue(data, pathDetails);
                if (val == null) continue;
                if (value.equals(val)) {
                    validData.add(data);
                    continue;
                }
                // Now we can filter our data against the pattern
                final String text = normalisePattern(value.toString().trim());
                final Pattern pattern = Pattern.compile(REGEX_BEGIN + text + REGEX_END, Pattern.CASE_INSENSITIVE);
                Matcher matcher = null;
                if (val instanceof Collection<?>) {
                    final Collection<?> collection = (Collection<?>) val;
                    for (final Object item : collection) {
                        matcher = pattern.matcher(item.toString());
                        if ((matcher != null) && matcher.find()) {
                            validData.add(data);
                            break;
                        }
                    }
                    if (collection.isEmpty()) {
                        matcher = pattern.matcher("");
                        if ((matcher != null) && matcher.find()) {
                            validData.add(data);
                        }
                    }
                } else if (val.toString() != null) {
                    matcher = pattern.matcher(val.toString());
                    if ((matcher != null) && matcher.find()) {
                        validData.add(data);
                    } else {
                        matcher = pattern.matcher("");
                        if ((matcher != null) && matcher.find()) {
                            validData.add(data);
                        }
                    }
                }
            }
        } catch (final PatternSyntaxException e) {
            if (log.isDebugEnabled()) {
                log.debug("bad pattern : " + value);
            }
        } catch (final Exception e) {
            log.error("Filter Error => pattern : " + value + " , property : " + fieldKey, e);
        }
        return validData;
    }

    /**
     * Used to Filter a list of data, scoped by a <code>propertyPath</code>, according to a
     * <code>patternName</code>. The property path is used to go as deep as one wants into each object of the
     * <code>datas</code>. It takes form as a string representing attributes separated by dots, e.g.
     * <code>attribute1.attribute2.attribute3</code><br/>
     * <br/>
     * If an attribute is a map, the tokens <code>keys</code> or <code>values</code> can be used to retrieve
     * the corresponding data as a Collection. E.g. <code>attribute1.mapAttribute.keys.attribute2</code>
     *
     * @param <T>
     *            the type of the data obtained with the propertyPath that is tested against the patternName
     * @param datas
     *            the list of data to be filtered
     * @param propertyPath
     *            the chain of attribute representing a path deep into the data objects
     * @param patternName
     *            a string used to filter data
     * @return the list of filtered data
     */
    public static <T> List<T> filter(List<T> datas, String propertyPath, String patternName) {
        if (datas == null || patternName.equals(EMPTY) || propertyPath.equals(EMPTY)) { return datas; }
        final List<T> validData = new ArrayList<T>();
        try {
            final String[] pathDetails = propertyPath.split(DOT_REGEX);
            for (final T data : datas) {
                final Object val = getValue(data, pathDetails);
                if (val == null) continue;
                // Now we can filter our data against the pattern
                final String value = normalisePattern(patternName.trim());
                final Pattern pattern = Pattern.compile(REGEX_BEGIN + value + REGEX_END, Pattern.CASE_INSENSITIVE);
                Matcher matcher = null;
                if (val instanceof Collection<?>) {
                    final Collection<?> collection = (Collection<?>) val;
                    for (final Object item : collection) {
                        matcher = pattern.matcher(item.toString());
                        if ((matcher != null) && matcher.find()) {
                            validData.add(data);
                            break;
                        }
                    }
                    if (collection.isEmpty()) {
                        matcher = pattern.matcher("");
                        if ((matcher != null) && matcher.find()) {
                            validData.add(data);
                        }
                    }
                } else if (val.toString() != null) {
                    matcher = pattern.matcher(val.toString());
                    if ((matcher != null) && matcher.find()) {
                        validData.add(data);
                    } else {
                        matcher = pattern.matcher("");
                        if ((matcher != null) && matcher.find()) {
                            validData.add(data);
                        }
                    }
                }
            }
        } catch (final PatternSyntaxException e) {
            if (log.isDebugEnabled()) {
                log.debug("bad pattern : " + patternName);
            }
        } catch (final Exception e) {
            log.error("Filter Error => pattern : " + patternName + " , property : " + propertyPath, e);
        }
        return validData;
    }

    @SuppressWarnings("unchecked")
    public static <T> Object getValue(T currentData, String[] pathDetails) throws Exception {
        final int lastIndex = pathDetails.length - 1;
        int curIndex = 0;
        Object val = null;
        Object tmpVal = PropertyUtils.getProperty(currentData, pathDetails[curIndex]);
        if (tmpVal == null) return null;
        curIndex++;
        // We parse the pathDetails array to extract the target object
        while (curIndex <= lastIndex) {
            if (pathDetails[curIndex].equals(KEYWORD_KEYS) && tmpVal instanceof Map) {
                tmpVal = ((Map<?, ?>) tmpVal).keySet();
            } else if (pathDetails[curIndex].equals(KEYWORD_VALUES) && tmpVal instanceof Map) {
                tmpVal = ((Map<?, ?>) tmpVal).values();
            } else if (tmpVal instanceof Collection) {
                Object value = null;
                for (final Object o : (Collection<?>) tmpVal) {
                    if (o instanceof Entry) {
                        final Entry<?, ?> entry = (Entry<?, ?>) o;
                        if (entry.getKey().equals(pathDetails[curIndex])) {
                            value = entry.getValue();
                        }
                    } else {
                        if (value == null) value = new ArrayList<Object>();
                        ((ArrayList<Object>) value).add(PropertyUtils.getProperty(o, pathDetails[curIndex]));
                    }
                }
                tmpVal = value;
            } else {
                tmpVal = PropertyUtils.getProperty(tmpVal, pathDetails[curIndex]);
            }
            curIndex++;
        }
        // Once we have our final object we should be able to cast it
        val = tmpVal;
        return val;
    }

    public static <T> List<T> filter(List<T> datas, Map<String, String> fields) {
        for (final Map.Entry<String, String> entry : fields.entrySet()) {
            datas = filter(datas, entry.getKey(), entry.getValue());
        }
        return datas;
    }

    public static <T> List<T> filter(List<T> datas, List<Criterion> criteria) {
        if (criteria == null) return datas;

        for (final Criterion criterion : criteria) {
            datas = filter(datas, criterion.getPojoProperty(), criterion.getValue());
        }
        return datas;
    }

    public static List<String> filterStringCollection(List<String> datas, List<Criterion> criteria) {
        if (criteria == null) return datas;

        for (final Criterion criterion : criteria) {
            datas = filter(datas, (String) criterion.getValue());
        }
        return datas;
    }

    // TODO nciaravola must be a criterion into SmartCC Criteria
    public static <T> List<T> filterByDisjunction(List<T> datas, List<Criterion> criteria) {
        if (criteria == null || criteria.isEmpty()) return datas;

        final List<T> result = new ArrayList<T>();
        for (final Criterion criterion : criteria) {
            result.addAll(filter(datas, criterion.getPojoProperty(), criterion.getValue()));
        }
        return result;
    }

    public static <T> List<T> sort(List<T> datas, List<Criterion> criteria) {
        if (criteria == null) return datas;

        for (final Criterion criterion : criteria) {
            final SortingType sortingType = criterion.getSortingType();
            if (sortingType != null && sortingType != SortingType.NONE) {
                final Comparator<T> comparator = new Comparator<T>() {

                    @Override
                    public int compare(T o1, T o2) {
                        if (o1 == null && o2 == null) return 0;
                        if (o1 == null) {
                            if (sortingType == SortingType.ASCENDING) return -1;
                            return 1;
                        }
                        if (o2 == null) {
                            if (sortingType == SortingType.ASCENDING) return 1;
                            return -1;
                        }
                        if (o1.equals(o2)) return 0;
                        if (sortingType == SortingType.ASCENDING) { return o1.toString().toLowerCase().compareTo(o2.toString().toLowerCase()); }
                        return o2.toString().toLowerCase().compareTo(o1.toString().toLowerCase());
                    }

                };
                datas = sortByPropertyName(datas, criterion.getPojoProperty(), comparator);
            }
        }
        return datas;
    }

    public static List<String> sortStringCollection(List<String> datas, List<Criterion> criteria) {
        if (criteria == null) return datas;

        for (final Criterion criterion : criteria) {
            final SortingType sortingType = criterion.getSortingType();
            if (sortingType != null && sortingType != SortingType.NONE) {
                final Comparator<String> comparator = new Comparator<String>() {

                    @Override
                    public int compare(String o1, String o2) {
                        if (o1 == null && o2 == null) return 0;
                        if (o1 == null) {
                            if (sortingType == SortingType.ASCENDING) return -1;
                            return 1;
                        }
                        if (o2 == null) {
                            if (sortingType == SortingType.ASCENDING) return 1;
                            return -1;
                        }
                        if (o1.equals(o2)) return 0;
                        if (sortingType == SortingType.ASCENDING) { return o1.toLowerCase().compareTo(o2.toLowerCase()); }
                        return o2.toLowerCase().compareTo(o1.toLowerCase());
                    }

                };
                Collections.sort(datas, comparator);
            }
        }
        return datas;
    }

    public static <T> List<T> getPage(int pageSize, int page, List<T> result) {
        if ((result == null) || (result.size() == 0)) { return result; }
        if (result.size() < pageSize) return result;
        if ((page * pageSize) > result.size()) {
            // return last page
            final int lastPage = result.size() / pageSize;
            return result.subList(lastPage * pageSize, result.size());
        }
        return new ArrayList<T>(result.subList(page * pageSize, Math.min(result.size(), page * pageSize + pageSize)));
    }

    public static <T> Result<List<T>> select(Query query, List<T> data) {
        data = FilteringTools.filter(data, query.getCriteria());
        data = FilteringTools.sort(data, query.getCriteria());

        final int count = data.size();

        if (!QueryMode.FULL_RESULT.equals(query.getQueryMode())) {
            data = FilteringTools.getPage(query.getPageSize(), query.getPageNum(), data);
        }

        final Result<List<T>> result = new Result<List<T>>(data);
        result.setFullSize(count);
        return result;
    }

    /**
     * $ => //$
     */
    private static String normalisePattern(String pattern) {
        return pattern.replaceAll("\\$", "\\\\\\$").replaceAll(REGEX_DATABASE_SYNTAXE, REGEX_MEMORY_SYNTAXE);
    }
}
TOP

Related Classes of com.ponysdk.impl.query.memory.FilteringTools

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.