Merge "BUG 2509 : Removing all journal entries from a Followers in-memory journal...
[controller.git] / opendaylight / adsal / northbound / commons / src / main / java / org / opendaylight / controller / northbound / commons / query / QueryImpl.java
1 /**
2  * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.controller.northbound.commons.query;
9
10 import java.util.ArrayList;
11 import java.util.Collection;
12 import java.util.Iterator;
13 import java.util.List;
14 import java.util.regex.Pattern;
15
16 import org.opendaylight.controller.northbound.commons.query.CompareExpression.OP;
17 import org.slf4j.Logger;
18 import org.slf4j.LoggerFactory;
19
20 /**
21  *
22  */
23 /*package*/ class QueryImpl<T> implements Query<T> {
24     public static final Logger LOGGER = LoggerFactory.getLogger(QueryImpl.class);
25     private static final boolean ALLOW_OBJECT_STRING_COMPARE = true;
26
27     private final Expression expression;
28     private final TypeInfo rootType ;
29     /**
30      * Set the expression and cache
31      * @param type
32      * @param expression
33      */
34     public QueryImpl(Class<T> type, Expression expression) {
35         this.expression = expression;
36         this.rootType = TypeInfo.createRoot(null, type);
37     }
38
39     @Override
40     public List<T> find(Collection<T> collection) throws QueryException {
41         // new arraylist for result
42         List<T> result = new ArrayList<T>();
43         for (T item : collection) {
44             if (match(item, rootType)) {
45                 result.add(item);
46             }
47         }
48         return result;
49     }
50
51     @Override
52     public int filter(Collection<T> collection) throws QueryException {
53         // find items
54         List<T> matched = new ArrayList<T>();
55         for (T item : collection) {
56             if (match(item, rootType)) {
57                 matched.add(item);
58             }
59         }
60         collection.clear();
61         collection.addAll(matched);
62         return matched.size();
63     }
64
65     @SuppressWarnings({ "rawtypes", "unchecked" })
66     @Override
67     public int filter(T rootObject, Class<?> childClass) throws QueryException {
68         // retrieve underlying collection
69         TypeInfo childType = rootType.getCollectionChild(childClass);
70         if (childType == null || !(childType instanceof IteratableTypeInfo)) {
71             return 0;
72         }
73         Collection collection = (Collection)
74                 childType.getAccessor().getValue(rootObject);
75         // get the child type of the collection type
76         TypeInfo ti = childType.getCollectionChild(childClass);
77         List matched = new ArrayList();
78         for (Object item : collection) {
79             if (match(item, ti)) {
80                 matched.add(item);
81             }
82         }
83         collection.clear();
84         collection.addAll(matched);
85         return matched.size();
86     }
87
88     private boolean match(final Object object, final TypeInfo rootType)
89             throws QueryException {
90         return expression.accept(new Visitor () {
91             @Override
92             public boolean visit(LogicalExpression le) throws QueryException {
93                 if (LOGGER.isDebugEnabled()) {
94                     LOGGER.debug("Logical exp {}|{}|{}", le.getOperator(), le.getFirst(),
95                             le.getSecond());
96                 }
97                 return (le.getOperator() == LogicalExpression.OP.AND) ?
98                         le.getFirst().accept(this) && le.getSecond().accept(this) :
99                             le.getFirst().accept(this) || le.getSecond().accept(this);
100             }
101
102             @Override
103             public boolean visit(CompareExpression ce) throws QueryException {
104                 boolean result = visitInternal(ce);
105                 if (LOGGER.isDebugEnabled()) {
106                     LOGGER.debug("=== Compare exp {}|{}|{} == {}", ce.getOperator(),
107                             ce.getSelector(), ce.getArgument(), result);
108                 }
109                 return result;
110             }
111
112             public boolean visitInternal(CompareExpression ce) throws QueryException {
113                 String[] selector = ce.getSelector().split("\\.");
114                 if (!rootType.getName().equals(selector[0])) {
115                     if (LOGGER.isDebugEnabled()) {
116                         LOGGER.debug("Root name mismatch: {} != {}",
117                                 rootType.getName(), selector[0]);
118                     }
119                     return false;
120                 }
121                 Object value = rootType.retrieve(object, selector, 1);
122                 if(value == null){ // nothing to compare against
123                     return false;
124                 }
125                 if (LOGGER.isDebugEnabled()) {
126                     LOGGER.debug("Comparing [{}] {} [{}]", ce.getArgument(),
127                             ce.getOperator(), value.toString());
128                 }
129                 if (value instanceof Collection) {
130                     Collection<?> collection = (Collection<?>) value;
131                     if(collection.size() == 0 && ce.getOperator() == OP.NE) {
132                         // collection doesn't contain query string
133                         return true;
134                     }
135                     // If there are elements iterate
136                     Iterator<?> it = collection.iterator();
137                     OP operator = ce.getOperator();
138                     if (operator == OP.NE) {
139                         // negate the operator
140                         operator = OP.EQ;
141                     }
142                     while (it.hasNext()) {
143                         Object item = it.next();
144                         if (compare(parse(ce.getArgument(), item), item, operator)) {
145                             // if match found check the operator and return false for NE
146                             return (ce.getOperator() != OP.NE);
147                         }
148                     }
149                     // return true for NE and false for rest
150                     return (ce.getOperator() == OP.NE);
151                 } else {
152                     return compare(parse(ce.getArgument(), value), value,
153                             ce.getOperator());
154                 }
155             }
156
157         });
158     }
159
160     private boolean compare(Object valueToMatch, Object actualValue, OP operator) {
161         if (valueToMatch == null || actualValue == null) {
162             return false;
163         }
164         if (ALLOW_OBJECT_STRING_COMPARE && (valueToMatch instanceof String)
165                 && !(actualValue instanceof String)) {
166             actualValue = actualValue.toString();
167         }
168
169         int compareResult = -1;
170         if (valueToMatch instanceof Comparable) {
171             compareResult = ((Comparable)actualValue).compareTo(valueToMatch);
172         } else {
173             if (LOGGER.isDebugEnabled()) {
174                 LOGGER.debug("Not a comparable type: {} {}",
175                         valueToMatch.getClass().getName(),
176                         actualValue.getClass().getName());
177             }
178             return false;
179         }
180         switch(operator) {
181             case EQ :
182                 return compareResult == 0;
183             case RE :
184                 // Regex match,
185                 if (valueToMatch instanceof String) {
186                     return Pattern.matches((String)valueToMatch, actualValue.toString());
187                 } else {
188                     return compareResult == 0;
189                 }
190             case NE:
191                 return compareResult != 0;
192             case GT :
193                 return compareResult > 0;
194             case GE :
195                 return compareResult >= 0;
196             case LT :
197                 return compareResult < 0;
198             case LE :
199                 return compareResult <= 0;
200             default:
201                 if (LOGGER.isDebugEnabled()) {
202                     LOGGER.debug("Unrecognized comparator - {}", operator);
203                 }
204             return false;
205         }
206     }
207     private Object parse(String arg, Object value) {
208         if (value == null) {
209             return null;
210         }
211
212         try {
213             if (value instanceof String) {
214                 return arg;
215             } else if (value instanceof Byte) {
216                 return Byte.decode(arg);
217             } else if (value instanceof Double) {
218                 return Double.parseDouble(arg);
219             } else if (value instanceof Float) {
220                 return Float.parseFloat(arg);
221             } else if (value instanceof Integer) {
222                 return Integer.parseInt(arg);
223             } else if (value instanceof Long) {
224                 return Long.parseLong(arg);
225             } else if (value instanceof Short) {
226                 return Short.parseShort(arg);
227             }
228         } catch (NumberFormatException ignore) {
229             if (LOGGER.isDebugEnabled()) {
230                 LOGGER.debug("Exception parsing {}", arg, value);
231             }
232         }
233         if (LOGGER.isDebugEnabled()) {
234             LOGGER.debug("Using string comparision for type - {}",
235                     value.getClass().getName());
236         }
237         // Not a number or string. Convert to a string and compare as last resort
238         return ALLOW_OBJECT_STRING_COMPARE ? arg.toString() : null;
239     }
240
241 }