001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2002-2006, GeoTools Project Managment Committee (PMC)
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation;
009: * version 2.1 of the License.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: */
016: package org.geotools.filter.text.cql2;
017:
018: import java.util.List;
019:
020: import junit.framework.TestCase;
021:
022: import org.geotools.filter.FilterFactoryImpl;
023: import org.opengis.filter.ExcludeFilter;
024: import org.opengis.filter.Filter;
025: import org.opengis.filter.FilterFactory;
026: import org.opengis.filter.IncludeFilter;
027: import org.opengis.filter.PropertyIsBetween;
028: import org.opengis.filter.PropertyIsEqualTo;
029: import org.opengis.filter.PropertyIsGreaterThan;
030: import org.opengis.filter.PropertyIsLessThan;
031: import org.opengis.filter.expression.Expression;
032: import org.opengis.filter.expression.Function;
033: import org.opengis.filter.expression.Literal;
034: import org.opengis.filter.expression.PropertyName;
035:
036: /**
037: * Test the extensions in CQL.
038: *
039: * <p>
040: * We adds some extension to Common CQL thinking in convenient uses.
041: * In the future we could have different dialects of CQL. That will
042: * required extend the interface to allow selecting the dialect to use.
043: * </p>
044: *
045: * @author Mauricio Pazos (Axios Engineering)
046: * @since 2.4
047: * @version Revision: 1.9
048: */
049: public class CQLExtensionTest extends TestCase {
050: private static final String DELIMITER = ";";
051:
052: /**
053: * An INCLUDE token is parsed as {@link Filter#INCLUDE}
054: *
055: * @throws Exception
056: */
057: public void testIncludeFilter() throws Exception {
058: Filter filter = CQL.toFilter("INCLUDE");
059: assertNotNull(filter);
060: assertTrue(Filter.INCLUDE.equals(filter));
061:
062: filter = CQL.toFilter("INCLUDE and a < 1");
063: assertNotNull(filter);
064: assertTrue(filter instanceof PropertyIsLessThan);
065:
066: filter = CQL.toFilter("INCLUDE or a < 1");
067: assertNotNull(filter);
068: assertTrue(Filter.INCLUDE.equals(filter));
069: }
070:
071: /**
072: * An EXCLUDE token is parsed as {@link Filter#EXCLUDE}
073: *
074: * @throws Exception
075: */
076: public void testExcludeFilter() throws Exception {
077: Filter filter = CQL.toFilter("EXCLUDE");
078: assertNotNull(filter);
079: assertTrue(Filter.EXCLUDE.equals(filter));
080:
081: filter = CQL.toFilter("EXCLUDE and a < 1");
082: assertNotNull(filter);
083: assertTrue(Filter.EXCLUDE.equals(filter));
084:
085: filter = CQL.toFilter("EXCLUDE or a < 1");
086: assertNotNull(filter);
087: assertTrue(filter instanceof PropertyIsLessThan);
088: }
089:
090: /**
091: * Simple test for sequence of search conditions with only one filter [*]
092: * <p>
093: * <pre>
094: * <SequenceOfSearchConditions > ::=
095: * <search condition> [*]
096: * | <SequenceOfSearchConditions> ; <search condition>
097: *
098: * </pre>
099: * <p>
100: *
101: * @throws Exception
102: */
103: public void testSequenceOfSearchConditionsWithOneFilter()
104: throws Exception {
105: String valueWithDelimiter = "text" + DELIMITER + "with"
106: + DELIMITER + "delimiter";
107: final String singleFilterStr = "attr3 = '" + valueWithDelimiter
108: + "'";
109: List filters = CQL.toFilterList(singleFilterStr);
110:
111: assertNotNull(filters);
112: assertEquals(1, filters.size());
113: assertTrue(filters.get(0) instanceof PropertyIsEqualTo);
114:
115: PropertyIsEqualTo filter = (PropertyIsEqualTo) filters.get(0);
116: assertEquals("attr3", ((PropertyName) filter.getExpression1())
117: .getPropertyName());
118: assertEquals(valueWithDelimiter, ((Literal) filter
119: .getExpression2()).getValue());
120: }
121:
122: /**
123: * Simple test for sequence of search conditions with only one filter [*]
124: * <p>
125: * <pre>
126: * <SequenceOfSearchConditions > ::=
127: * <search condition>
128: * | <SequenceOfSearchConditions> ; <search condition> [*]
129: *
130: * </pre>
131: * <p>
132: * Sample: attr1 > 5;attr2 between 1 and 7;attr3
133: *
134: * @throws Exception
135: */
136: public void testSequenceOfSearchConditionsWithManyFilters()
137: throws Exception {
138: String valueWithDelimiter = "text" + DELIMITER + "with"
139: + DELIMITER + "delimiter";
140:
141: // "attr1 > 5; attr2 between 1 and 7; attr3 = 'text;with;delimiter
142: final String filterListStr = "attr1 > 5" + DELIMITER
143: + "attr2 between 1 and 7" + DELIMITER + "attr3 = '"
144: + valueWithDelimiter + "'";
145:
146: List filters = CQL.toFilterList(filterListStr);
147: assertNotNull(filters);
148: assertEquals(3, filters.size());
149: assertTrue(filters.get(0) instanceof PropertyIsGreaterThan);
150: assertTrue(filters.get(1) instanceof PropertyIsBetween);
151: assertTrue(filters.get(2) instanceof PropertyIsEqualTo);
152:
153: PropertyIsGreaterThan gt = (PropertyIsGreaterThan) filters
154: .get(0);
155: assertEquals("attr1", ((PropertyName) gt.getExpression1())
156: .getPropertyName());
157: assertEquals(new Integer(5), ((Literal) gt.getExpression2())
158: .getValue());
159:
160: PropertyIsBetween btw = (PropertyIsBetween) filters.get(1);
161: assertEquals("attr2", ((PropertyName) btw.getExpression())
162: .getPropertyName());
163: assertEquals(new Integer(1), ((Literal) btw.getLowerBoundary())
164: .getValue());
165: assertEquals(new Integer(7), ((Literal) btw.getUpperBoundary())
166: .getValue());
167:
168: PropertyIsEqualTo equals = (PropertyIsEqualTo) filters.get(2);
169: assertEquals("attr3", ((PropertyName) equals.getExpression1())
170: .getPropertyName());
171: assertEquals(valueWithDelimiter, ((Literal) equals
172: .getExpression2()).getValue());
173: }
174:
175: /**
176: * An empty filter int the constraints list shall be parsed as
177: * {@link Filter#INCLUDE}
178: *
179: * @throws Exception
180: */
181: public void testParseFilterListWithEmptyFilter() throws Exception {
182: String valueWithDelimiter = "text" + DELIMITER + "with"
183: + DELIMITER + "delimiter";
184:
185: // "attr1 > 5;INCLUDE;attr3 = 'text;with;delimiter'"
186: String filterListStr = "attr1 > 5" + DELIMITER + "INCLUDE"
187: + DELIMITER + " attr3 = '" + valueWithDelimiter + "'";
188: List filters = CQL.toFilterList(filterListStr);
189: assertNotNull(filters);
190: assertEquals(3, filters.size());
191: assertTrue(filters.get(0) instanceof PropertyIsGreaterThan);
192: assertTrue(filters.get(1) instanceof IncludeFilter);
193: assertTrue(filters.get(2) instanceof PropertyIsEqualTo);
194:
195: PropertyIsGreaterThan gt = (PropertyIsGreaterThan) filters
196: .get(0);
197: assertEquals("attr1", ((PropertyName) gt.getExpression1())
198: .getPropertyName());
199: assertEquals(new Integer(5), ((Literal) gt.getExpression2())
200: .getValue());
201:
202: PropertyIsEqualTo equals = (PropertyIsEqualTo) filters.get(2);
203: assertEquals("attr3", ((PropertyName) equals.getExpression1())
204: .getPropertyName());
205: assertEquals(valueWithDelimiter, ((Literal) equals
206: .getExpression2()).getValue());
207:
208: filterListStr = "EXCLUDE" + DELIMITER + "INCLUDE" + DELIMITER
209: + "attr3 = '" + valueWithDelimiter + "'";
210:
211: filters = CQL.toFilterList(filterListStr);
212: assertTrue(filters.get(0) instanceof ExcludeFilter);
213: assertTrue(filters.get(1) instanceof IncludeFilter);
214: assertTrue(filters.get(2) instanceof PropertyIsEqualTo);
215: }
216:
217: /**
218: * Verify the parser uses the provided FilterFactory implementation
219: * @throws ParseException
220: */
221: public void testUsesProvidedFilterFactory() throws Exception {
222: final boolean[] called = { false };
223: FilterFactory ff = new FilterFactoryImpl() {
224: public PropertyName property(String propName) {
225: called[0] = true;
226:
227: return super .property(propName);
228: }
229: };
230:
231: CQL.toFilter("attName > 20", ff);
232: assertTrue("Provided FilterFactory was not called", called[0]);
233: }
234:
235: /**
236: * Tests null factory as parameter.
237: *
238: * @throws Exception
239: */
240: public void testNullFilterFactory() throws Exception {
241:
242: CQL.toFilter("attName > 20", null);
243:
244: CQL.toExpression("2+2", null);
245: }
246:
247: /**
248: * Test Function Expression
249: *
250: * Note: this solves the bug GEOT-1167
251: *
252: * @throws Exception
253: */
254: public void testFuncitionExpression() throws Exception {
255: Expression arg1;
256: Expression arg2;
257: Expression resultExpr;
258:
259: final String cqlExpression = "strConcat(A, B)";
260:
261: // simple attribute as argument
262: resultExpr = CQL.toExpression(cqlExpression);
263:
264: assertNotNull(resultExpr);
265: assertTrue(resultExpr instanceof Function);
266:
267: Function function1 = (Function) resultExpr;
268: assertEquals(2, function1.getParameters().size());
269:
270: arg1 = (Expression) function1.getParameters().get(0);
271: assertTrue(arg1 instanceof PropertyName);
272: assertEquals("A", ((PropertyName) arg1).getPropertyName());
273:
274: arg2 = (Expression) function1.getParameters().get(1);
275: assertTrue(arg2 instanceof PropertyName);
276: assertEquals("B", ((PropertyName) arg2).getPropertyName());
277:
278: // compound attribute as argument
279: final String arg1Name = "gmd:aa:bb.gmd:cc.gmd:dd";
280: final String arg2Name = "gmd:ee:ff.gmd:gg.gmd:hh";
281:
282: final String cqlExpression2 = "strConcat(" + arg1Name + ", "
283: + arg2Name + ")";
284:
285: resultExpr = CQL.toExpression(cqlExpression2);
286:
287: assertNotNull(resultExpr);
288: assertTrue(resultExpr instanceof Function);
289:
290: Function function = (Function) resultExpr;
291: assertEquals(2, function.getParameters().size());
292:
293: arg1 = (Expression) function.getParameters().get(0);
294: assertTrue(arg1 instanceof PropertyName);
295:
296: final String arg1Expected = arg1Name.replace('.', '/');
297: assertEquals(arg1Expected, ((PropertyName) arg1)
298: .getPropertyName());
299:
300: arg2 = (Expression) function.getParameters().get(1);
301: assertTrue(arg2 instanceof PropertyName);
302:
303: final String arg2Expected = arg2Name.replace('.', '/');
304: assertEquals(arg2Expected, ((PropertyName) arg2)
305: .getPropertyName());
306:
307: }
308:
309: /**
310: * This test the following improvement: GEOT-1169 This is an extension the
311: * CQL specification.
312: *
313: * <pre>
314: * <function> ::= <routine name > <argument list > [*]
315: * <argument list> ::= [*]
316: * <left paren> [<positional arguments>] <right paren>
317: * <positional arguments> ::=
318: * <argument> [ { <comma&gt <argument> }... ]
319: * <argument> ::=
320: * <literal>
321: * | <attribute name>
322: * | <function> [*]
323: * | <binary expression> [*]
324: * </pre>
325: *
326: * @throws Exception
327: */
328: public void testFunctionComposition() throws Exception {
329: String cqlExpression;
330: Expression expression;
331:
332: // Test 1
333: cqlExpression = "strConcat(A, abs(B))";
334: expression = CQL.toExpression(cqlExpression);
335:
336: // Test 2
337: cqlExpression = "strConcat(A, strConcat(B, strConcat(C, '.')))";
338: expression = CQL.toExpression(cqlExpression);
339: assertNotNull(expression);
340: assertTrue(expression instanceof Function);
341:
342: Function function = (Function) expression;
343: assertEquals(2, function.getParameters().size());
344:
345: Expression propertyName = (Expression) function.getParameters()
346: .get(0);
347: assertTrue(propertyName instanceof PropertyName);
348: assertEquals("A", ((PropertyName) propertyName)
349: .getPropertyName());
350:
351: Expression arg2 = (Expression) function.getParameters().get(1);
352: assertTrue(arg2 instanceof Function);
353:
354: function = (Function) arg2;
355: propertyName = (Expression) function.getParameters().get(0);
356: assertTrue(propertyName instanceof PropertyName);
357: assertEquals("B", ((PropertyName) propertyName)
358: .getPropertyName());
359:
360: arg2 = (Expression) function.getParameters().get(1);
361: assertTrue(arg2 instanceof Function);
362:
363: function = (Function) arg2;
364: propertyName = (Expression) function.getParameters().get(0);
365: assertTrue(propertyName instanceof PropertyName);
366: assertEquals("C", ((PropertyName) propertyName)
367: .getPropertyName());
368:
369: arg2 = (Expression) function.getParameters().get(1);
370: assertTrue(arg2 instanceof Literal);
371: assertEquals(".", ((Literal) arg2).getValue());
372: }
373:
374: /**
375: * complex case
376: * Created to analyze http://jira.codehaus.org/browse/GEOT-1655
377: * @throws Exception
378: */
379: public void testFunctionCompositionComplexCase() throws Exception {
380:
381: Expression result = CQL
382: .toExpression("strConcat( strConcat(QS, strConcat('/', RT)), strConcat(strConcat('/', NUMB), strConcat('/', BSUFF)) )");
383:
384: assertFunctionCompositionComplex(result);
385: }
386:
387: public void testFunctionCompositionComplexCaseInFilter()
388: throws Exception {
389:
390: final String propName = "A";
391: Filter result = CQL
392: .toFilter(propName
393: + " = strConcat( strConcat(QS, strConcat('/', RT)), strConcat(strConcat('/', NUMB), strConcat('/', BSUFF)) )");
394:
395: assertTrue(result instanceof PropertyIsEqualTo);
396: PropertyIsEqualTo eq = (PropertyIsEqualTo) result;
397:
398: Expression expr1 = eq.getExpression1();
399: assertTrue(expr1 instanceof PropertyName);
400: PropertyName prop = (PropertyName) expr1;
401: assertEquals(propName, prop.getPropertyName());
402:
403: Expression expr2 = eq.getExpression2();
404: assertFunctionCompositionComplex(expr2);
405:
406: }
407:
408: /**
409: * @param result strConcat( strConcat(QS, strConcat('/', RT)), strConcat(strConcat('/', NUMB), strConcat('/', BSUFF)) )")
410: */
411: private void assertFunctionCompositionComplex(
412: final Expression result) {
413:
414: assertTrue(result instanceof Function);
415:
416: Function function = (Function) result;
417: assertEquals(2, function.getParameters().size());
418:
419: // asserts for strConcat(QS, strConcat('/', RT))
420: Expression arg1 = (Expression) function.getParameters().get(0);
421: assertTrue(arg1 instanceof Function);
422:
423: Function function1 = (Function) arg1;
424: assertEquals(2, function1.getParameters().size());
425:
426: Expression funcion1Arg1 = (Expression) function1
427: .getParameters().get(0);
428: assertTrue(funcion1Arg1 instanceof PropertyName);
429: assertEquals("QS", ((PropertyName) funcion1Arg1)
430: .getPropertyName());
431:
432: Expression arg11 = (Expression) function1.getParameters()
433: .get(1);
434: assertTrue(arg11 instanceof Function);
435: Function function11 = (Function) arg11;
436:
437: Expression funcion11Arg1 = (Expression) function11
438: .getParameters().get(0);
439: assertTrue(funcion11Arg1 instanceof Literal);
440: assertEquals("/", ((Literal) funcion11Arg1).getValue());
441:
442: // asserts for strConcat(strConcat('/', NUMB), strConcat('/', BSUFF)) )"
443: Expression arg2 = (Expression) function.getParameters().get(1);
444: assertTrue(arg2 instanceof Function);
445:
446: }
447:
448: /**
449: * Test for Function Unary Expressions with functions in CQL.
450: * <p>
451: *
452: * <pre>
453: * <unary expression > ::=
454: * <Literal >
455: * | <Function > [*]
456: * | <Attribute >
457: * | ( <Expression >)
458: * | [ <Expression >]
459: * </pre>
460: *
461: * </p>
462: * TODO requires more test
463: */
464: public void testUnaryExpressionFunction() throws Exception {
465: Filter result;
466: Filter expected;
467: String cqlUnaryExp;
468:
469: cqlUnaryExp = FilterSample.FILTER_WITH_FUNCTION_ABS;
470: result = CQL.toFilter(cqlUnaryExp);
471:
472: assertNotNull("filter expected", result);
473:
474: expected = FilterSample.getSample(cqlUnaryExp);
475:
476: assertEquals("Equals Functions is expected", expected, result);
477:
478: // Key: GEOT-1167 type: BUG
479: cqlUnaryExp = FilterSample.FILTER__WITH_FUNCTION_STR_CONCAT;
480: result = CQL.toFilter(cqlUnaryExp);
481: assertNotNull("filter expected", result);
482:
483: expected = FilterSample.getSample(cqlUnaryExp);
484:
485: // TODO BUG in Function equals strConcat
486: //assertEquals( "Functions", expected, result);
487:
488: // test for improvement Key: GEOT-1168
489: cqlUnaryExp = "A = strConcat(B, 'testParam')";
490: result = CQL.toFilter(cqlUnaryExp);
491:
492: assertTrue(result instanceof PropertyIsEqualTo);
493:
494: Expression expression = ((PropertyIsEqualTo) result)
495: .getExpression2();
496: assertNotNull(expression);
497: assertTrue(expression instanceof Function);
498:
499: Function function = (Function) expression;
500: assertEquals(2, function.getParameters().size());
501:
502: Expression arg1 = (Expression) function.getParameters().get(0);
503: Expression arg2 = (Expression) function.getParameters().get(1);
504: assertTrue(arg1 instanceof PropertyName);
505: assertTrue(arg2 instanceof Literal);
506:
507: assertEquals("B", ((PropertyName) arg1).getPropertyName());
508: assertEquals("testParam", ((Literal) arg2).getValue());
509: }
510:
511: }
|