/** * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ package org.opendaylight.controller.northbound.commons.query; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.regex.Pattern; import org.opendaylight.controller.northbound.commons.query.CompareExpression.OP; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * */ /*package*/ class QueryImpl implements Query { public static final Logger LOGGER = LoggerFactory.getLogger(QueryImpl.class); private static final boolean ALLOW_OBJECT_STRING_COMPARE = true; private final Expression expression; private final TypeInfo rootType ; /** * Set the expression and cache * @param type * @param expression */ public QueryImpl(Class type, Expression expression) { this.expression = expression; this.rootType = TypeInfo.createRoot(null, type); } @Override public List find(Collection collection) throws QueryException { // new arraylist for result List result = new ArrayList(); for (T item : collection) { if (match(item, rootType)) { result.add(item); } } return result; } @Override public int filter(Collection collection) throws QueryException { // find items List matched = new ArrayList(); for (T item : collection) { if (match(item, rootType)) { matched.add(item); } } collection.clear(); collection.addAll(matched); return matched.size(); } @SuppressWarnings({ "rawtypes", "unchecked" }) @Override public int filter(T rootObject, Class childClass) throws QueryException { // retrieve underlying collection TypeInfo childType = rootType.getCollectionChild(childClass); if (childType == null || !(childType instanceof IteratableTypeInfo)) { return 0; } Collection collection = (Collection) childType.getAccessor().getValue(rootObject); // get the child type of the collection type TypeInfo ti = childType.getCollectionChild(childClass); List matched = new ArrayList(); for (Object item : collection) { if (match(item, ti)) { matched.add(item); } } collection.clear(); collection.addAll(matched); return matched.size(); } private boolean match(final Object object, final TypeInfo rootType) throws QueryException { return expression.accept(new Visitor () { @Override public boolean visit(LogicalExpression le) throws QueryException { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Logical exp {}|{}|{}", le.getOperator(), le.getFirst(), le.getSecond()); } return (le.getOperator() == LogicalExpression.OP.AND) ? le.getFirst().accept(this) && le.getSecond().accept(this) : le.getFirst().accept(this) || le.getSecond().accept(this); } @Override public boolean visit(CompareExpression ce) throws QueryException { boolean result = visitInternal(ce); if (LOGGER.isDebugEnabled()) { LOGGER.debug("=== Compare exp {}|{}|{} == {}", ce.getOperator(), ce.getSelector(), ce.getArgument(), result); } return result; } public boolean visitInternal(CompareExpression ce) throws QueryException { String[] selector = ce.getSelector().split("\\."); if (!rootType.getName().equals(selector[0])) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Root name mismatch: {} != {}", rootType.getName(), selector[0]); } return false; } Object value = rootType.retrieve(object, selector, 1); if(value == null){ // nothing to compare against return false; } if (LOGGER.isDebugEnabled()) { LOGGER.debug("Comparing [{}] {} [{}]", ce.getArgument(), ce.getOperator(), value.toString()); } if (value instanceof Collection) { Collection collection = (Collection) value; if(collection.size() == 0 && ce.getOperator() == OP.NE) { // collection doesn't contain query string return true; } // If there are elements iterate Iterator it = collection.iterator(); OP operator = ce.getOperator(); if (operator == OP.NE) { // negate the operator operator = OP.EQ; } while (it.hasNext()) { Object item = it.next(); if (compare(parse(ce.getArgument(), item), item, operator)) { // if match found check the operator and return false for NE return (ce.getOperator() != OP.NE); } } // return true for NE and false for rest return (ce.getOperator() == OP.NE); } else { return compare(parse(ce.getArgument(), value), value, ce.getOperator()); } } }); } private boolean compare(Object valueToMatch, Object actualValue, OP operator) { if (valueToMatch == null || actualValue == null) { return false; } if (ALLOW_OBJECT_STRING_COMPARE && (valueToMatch instanceof String) && !(actualValue instanceof String)) { actualValue = actualValue.toString(); } int compareResult = -1; if (valueToMatch instanceof Comparable) { compareResult = ((Comparable)actualValue).compareTo(valueToMatch); } else { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Not a comparable type: {} {}", valueToMatch.getClass().getName(), actualValue.getClass().getName()); } return false; } switch(operator) { case EQ : return compareResult == 0; case RE : // Regex match, if (valueToMatch instanceof String) { return Pattern.matches((String)valueToMatch, actualValue.toString()); } else { return compareResult == 0; } case NE: return compareResult != 0; case GT : return compareResult > 0; case GE : return compareResult >= 0; case LT : return compareResult < 0; case LE : return compareResult <= 0; default: if (LOGGER.isDebugEnabled()) { LOGGER.debug("Unrecognized comparator - {}", operator); } return false; } } private Object parse(String arg, Object value) { if (value == null) { return null; } try { if (value instanceof String) { return arg; } else if (value instanceof Byte) { return Byte.decode(arg); } else if (value instanceof Double) { return Double.parseDouble(arg); } else if (value instanceof Float) { return Float.parseFloat(arg); } else if (value instanceof Integer) { return Integer.parseInt(arg); } else if (value instanceof Long) { return Long.parseLong(arg); } else if (value instanceof Short) { return Short.parseShort(arg); } } catch (NumberFormatException ignore) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Exception parsing {}", arg, value); } } if (LOGGER.isDebugEnabled()) { LOGGER.debug("Using string comparision for type - {}", value.getClass().getName()); } // Not a number or string. Convert to a string and compare as last resort return ALLOW_OBJECT_STRING_COMPARE ? arg.toString() : null; } }