This is an automated email from the ASF dual-hosted git repository.
jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git
The following commit(s) were added to refs/heads/master by this push:
new 6851128ff Replace old PojoQuery with newer APIs
6851128ff is described below
commit 6851128ffb0f3e4da333ceb43df9d69e306f9aea
Author: JamesBognar <[email protected]>
AuthorDate: Sun Jun 12 10:38:46 2022 -0400
Replace old PojoQuery with newer APIs
---
.../java/org/apache/juneau/dto/LinkString.java | 1 -
.../org/apache/juneau/objecttools/PageArgs.java | 7 +-
.../org/apache/juneau/objecttools/SearchArgs.java | 14 +-
.../org/apache/juneau/objecttools/SortArgs.java | 14 +-
.../org/apache/juneau/objecttools/ViewArgs.java | 2 +
.../java/org/apache/juneau/utils/PojoQuery.java | 1222 --------------------
.../java/org/apache/juneau/utils/SearchArgs.java | 349 ------
.../apache/juneau/rest/converter/Queryable.java | 28 +-
.../juneau/rest/httppart/RequestFormParams.java | 53 -
.../juneau/rest/httppart/RequestQueryParams.java | 83 +-
.../org/apache/juneau/utils/PojoQueryTest.java | 418 -------
11 files changed, 73 insertions(+), 2118 deletions(-)
diff --git
a/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/LinkString.java
b/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/LinkString.java
index 426e88a76..eaf589921 100644
--- a/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/LinkString.java
+++ b/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/LinkString.java
@@ -26,7 +26,6 @@ import org.apache.juneau.html.annotation.*;
import org.apache.juneau.httppart.*;
import org.apache.juneau.oapi.*;
import org.apache.juneau.serializer.*;
-import org.apache.juneau.utils.*;
/**
* Simple bean that implements a hyperlink for the HTML serializer.
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/objecttools/PageArgs.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/objecttools/PageArgs.java
index 1f3a91f07..f7bfcfaf2 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/objecttools/PageArgs.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/objecttools/PageArgs.java
@@ -12,6 +12,8 @@
//
***************************************************************************************************************************
package org.apache.juneau.objecttools;
+import static java.util.Optional.*;
+
/**
* Arguments passed to {@link ObjectPaginator}.
*
@@ -34,8 +36,9 @@ public class PageArgs {
*
* @return A new {@link PageArgs} object.
*/
- public static PageArgs create(int position, int limit) {
- return new PageArgs(position, limit);
+ public static PageArgs create(Integer position, Integer limit) {
+ if (position == null && limit == null) return null;
+ return new PageArgs(ofNullable(position).orElse(0),
ofNullable(limit).orElse(-1));
}
//-----------------------------------------------------------------------------------------------------------------
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/objecttools/SearchArgs.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/objecttools/SearchArgs.java
index c2be7a381..2e88eaf16 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/objecttools/SearchArgs.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/objecttools/SearchArgs.java
@@ -36,21 +36,23 @@ public class SearchArgs {
/**
* Static creator.
*
- * @param searchArgs Comma-delimited list of search arguments.
+ * @param args Comma-delimited list of search arguments.
* @return A new {@link SearchArgs} object.
*/
- public static SearchArgs create(String searchArgs) {
- return new SearchArgs(searchArgs);
+ public static SearchArgs create(String args) {
+ if (args == null) return null;
+ return new SearchArgs(args);
}
/**
* Static creator.
*
- * @param searchArgs List of search arguments.
+ * @param args List of search arguments.
* @return A new {@link SearchArgs} object.
*/
- public static SearchArgs create(List<String> searchArgs) {
- return new SearchArgs(searchArgs);
+ public static SearchArgs create(List<String> args) {
+ if (args == null) return null;
+ return new SearchArgs(args);
}
//-----------------------------------------------------------------------------------------------------------------
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/objecttools/SortArgs.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/objecttools/SortArgs.java
index cf6850b18..f78b62672 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/objecttools/SortArgs.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/objecttools/SortArgs.java
@@ -34,7 +34,7 @@ public class SortArgs {
/**
* Static creator.
*
- * @param sortArgs
+ * @param args
* Comma-delimited list of sort arguments.
* <br>Values are of the following forms:
* <ul>
@@ -44,14 +44,15 @@ public class SortArgs {
* </ul>
* @return A new {@link SortArgs} object.
*/
- public static SortArgs create(String sortArgs) {
- return new SortArgs(sortArgs);
+ public static SortArgs create(String args) {
+ if (args == null) return null;
+ return new SortArgs(args);
}
/**
* Static creator.
*
- * @param sortArgs
+ * @param args
* Sort arguments.
* <br>Values are of the following forms:
* <ul>
@@ -61,8 +62,9 @@ public class SortArgs {
* </ul>
* @return A new {@link SortArgs} object.
*/
- public static SortArgs create(List<String> sortArgs) {
- return new SortArgs(sortArgs);
+ public static SortArgs create(List<String> args) {
+ if (args == null) return null;
+ return new SortArgs(args);
}
//-----------------------------------------------------------------------------------------------------------------
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/objecttools/ViewArgs.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/objecttools/ViewArgs.java
index 13b90c975..91ecf399c 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/objecttools/ViewArgs.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/objecttools/ViewArgs.java
@@ -38,6 +38,7 @@ public class ViewArgs {
* @return A new {@link ViewArgs} object.
*/
public static ViewArgs create(String args) {
+ if (args == null) return null;
return new ViewArgs(args);
}
@@ -48,6 +49,7 @@ public class ViewArgs {
* @return A new {@link ViewArgs} object.
*/
public static ViewArgs create(List<String> args) {
+ if (args == null) return null;
return new ViewArgs(args);
}
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/PojoQuery.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/PojoQuery.java
deleted file mode 100644
index 63ac13972..000000000
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/PojoQuery.java
+++ /dev/null
@@ -1,1222 +0,0 @@
-//
***************************************************************************************************************************
-// * Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file *
-// * distributed with this work for additional information regarding copyright
ownership. The ASF licenses this file *
-// * to you 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.apache.juneau.utils;
-
-import static java.util.Calendar.*;
-import static org.apache.juneau.internal.ThrowableUtils.*;
-import static org.apache.juneau.internal.CollectionUtils.*;
-import static org.apache.juneau.internal.StringUtils.*;
-
-import java.lang.reflect.*;
-import java.text.*;
-import java.util.*;
-import java.util.regex.*;
-import java.util.regex.Matcher;
-
-import org.apache.juneau.*;
-import org.apache.juneau.collections.*;
-import org.apache.juneau.internal.*;
-import org.apache.juneau.objecttools.*;
-
-/**
- * Designed to provide search/view/sort/paging filtering on tabular in-memory
POJO models.
- *
- * <p>
- * It can also perform just view filtering on beans/maps.
- *
- * <p>
- * Examples of tabular POJO models:
- * <ul>
- * <li><tt>Collection{@code <Map>}</tt>
- * <li><tt>Collection{@code <Bean>}</tt>
- * <li><tt>Map[]</tt>
- * <li><tt>Bean[]</tt>
- * </ul>
- *
- * <p>
- * Tabular POJO models can be thought of as tables of data. For example, a
list of the following beans...
- * <p class='bjava'>
- * <jk>public</jk> MyBean {
- * <jk>public int</jk> <jf>myInt</jf>;
- * <jk>public</jk> String <jf>myString</jf>;
- * <jk>public</jk> Date <jf>myDate</jf>;
- * }
- * <p>
- * ... can be thought of a table containing the following columns...
- * <p>
- * <table class='styled code'>
- * <tr><th>myInt</th><th>myString</th><th>myDate</th></tr>
- * <tr><td>123</td><td>'foobar'</td><td>yyyy/MM/dd HH:mm:ss</td></tr>
- * <tr><td colspan=3>...</td></tr>
- * </table>
- *
- * <p>
- * From this table, you can perform the following functions:
- * <ul class='spaced-list'>
- * <li>
- * Search - Return only rows where a search pattern matches.
- * <li>
- * View - Return only the specified subset of columns in the
specified order.
- * <li>
- * Sort - Sort the table by one or more columns.
- * <li>
- * Position/limit - Only return a subset of rows.
- * </ul>
- *
- * <h5 class='topic'>Search</h5>
- *
- * The search capabilities allow you to filter based on query patterns against
strings, dates, and numbers.
- * Queries take the form of a Map with column names as keys, and search
patterns as values.
- * <br>Multiple search patterns are ANDed (i.e. all patterns must match for
the row to be returned).
- *
- * <h5 class='section'>Example:</h5>
- * <ul class='spaced-list'>
- * <li>
- * <tt>{myInt:'123'}</tt> - Return only rows where the
<tt>myInt</tt> column is 123.
- * <li>
- * <tt>{myString:'foobar'}</tt> - Return only rows where the
<tt>myString</tt> column is 'foobar'.
- * <li>
- * <tt>{myDate:'2001'}</tt> - Return only rows where the
<tt>myDate</tt> column have dates in the year 2001.
- * </ul>
- *
- * <h5 class='topic'>String Patterns</h5>
- *
- * Any objects can be queried against using string patterns.
- * If the objects being searched are not strings, then the patterns are
matched against whatever is return by the
- * {@code Object#toString()} method.
- *
- * <h5 class='topic'>Example string query patterns:</h5>
- * <ul>
- * <li><tt>foo</tt> - The string 'foo'
- * <li><tt>foo bar</tt> - The string 'foo' or the string 'bar'
- * <li><tt>'foo bar'</tt> - The phrase 'foo bar'
- * <li><tt>"foo bar"</tt> - The phrase 'foo bar'
- * <li><tt>foo*</tt> - <tt>*</tt> matches zero-or-more characters.
- * <li><tt>foo?</tt> - <tt>?</tt> matches exactly one character
- * </ul>
- *
- * <ul class='notes'>
- * <li class='note'>
- * Whitespace is ignored around search patterns.
- * <li class='note'>
- * Prepend <tt>+</tt> to tokens that must match. (e.g. <tt>+foo*
+*bar</tt>)
- * <li class='note'>
- * Prepend <tt>-</tt> to tokens that must not match. (e.g.
<tt>+foo* -*bar</tt>)
- * </ul>
- *
- * <h5 class='topic'>Numeric Patterns</h5>
- *
- * Any object of type {@link Number} (or numeric primitives) can be searched
using numeric patterns.
- *
- * <h5 class='topic'>Example numeric query patterns:</h5>
- * <ul>
- * <li><tt>123</tt> - The single number 123
- * <li><tt>1 2 3</tt> - 1, 2, or 3
- * <li><tt>1-100</tt> - Between 1 and 100
- * <li><tt>1 - 100</tt> - Between 1 and 100
- * <li><tt>1 - 100 200-300</tt> - Between 1 and 100 or between 200 and 300
- * <li><tt>> 100</tt> - Greater than 100
- * <li><tt>>= 100</tt> - Greater than or equal to 100
- * <li><tt>!123</tt> - Not 123
- * </ul>
- *
- * <ul class='notes'>
- * <li class='note'>
- * Whitespace is ignored in search patterns.
- * <li class='note'>
- * Negative numbers are supported.
- * </ul>
- *
- * <h5 class='topic'>Date Patterns</h5>
- *
- * Any object of type {@link Date} or {@link Calendar} can be searched using
date patterns.
- *
- * <p>
- * The default valid input timestamp formats (which can be overridden via the
{@link #setValidTimestampFormats(String...)}
- * method are...
- *
- * <ul>
- * <li><tt>yyyy.MM.dd.HH.mm.ss</tt>
- * <li><tt>yyyy.MM.dd.HH.mm</tt>
- * <li><tt>yyyy.MM.dd.HH</tt>
- * <li><tt>yyyy.MM.dd</tt>
- * <li><tt>yyyy.MM</tt>
- * <li><tt>yyyy</tt>
- * </ul>
- *
- * <h5 class='topic'>Example date query patterns:</h5>
- * <ul>
- * <li><tt>2001</tt> - A specific year.
- * <li><tt>2001.01.01.10.50</tt> - A specific time.
- * <li><tt>>2001</tt> - After a specific year.
- * <li><tt>>=2001</tt> - During or after a specific year.
- * <li><tt>2001 - 2003.06.30</tt> - A date range.
- * <li><tt>2001 2003 2005</tt> - Multiple date patterns are ORed.
- * </ul>
- *
- * <ul class='notes'>
- * <li class='note'>
- * Whitespace is ignored in search patterns.
- * </ul>
- *
- * <h5 class='topic'>View</h5>
- *
- * The view capability allows you to return only the specified subset of
columns in the specified order.
- * <br>The view parameter is a list of either <tt>Strings</tt> or
<tt>Maps</tt>.
- *
- * <h5 class='topic'>Example view parameters:</h5>
- * <ul>
- * <li><tt>column1</tt> - Return only column 'column1'.
- * <li><tt>column2, column1</tt> - Return only columns 'column2' and
'column1' in that order.
- * </ul>
- *
- * <h5 class='topic'>Sort</h5>
- *
- * The sort capability allows you to sort values by the specified rows.
- * <br>The sort parameter is a list of strings with an optional <js>'+'</js>
or <js>'-'</js> suffix representing
- * ascending and descending order accordingly.
- *
- * <h5 class='topic'>Example sort parameters:</h5>
- * <ul>
- * <li><tt>column1</tt> - Sort rows by column 'column1' ascending.
- * <li><tt>column1+</tt> - Sort rows by column 'column1' ascending.
- * <li><tt>column1-</tt> - Sort rows by column 'column1' descending.
- * <li><tt>column1, column2-</tt> - Sort rows by column 'column1'
ascending, then 'column2' descending.
- * </ul>
- *
- * <h5 class='topic'>Paging</h5>
- *
- * Use the <tt>position</tt> and <tt>limit</tt> parameters to specify a subset
of rows to return.
- *
- * <ul class='seealso'>
- * <li class='extlink'>{@source}
- * </ul>
- *
- * @deprecated Use {@link ObjectSearcher}
- */
-@Deprecated
-@SuppressWarnings({"unchecked","rawtypes"})
-public final class PojoQuery {
-
- private Object input;
- private ClassMeta type;
- private BeanSession session;
-
- /**
- * Constructor.
- *
- * @param input The POJO we're going to be filtering.
- * @param session The bean session to use to create bean maps for beans.
- */
- public PojoQuery(Object input, BeanSession session) {
- this.input = input;
- this.type = session.getClassMetaForObject(input);
- this.session = session;
- }
-
- /**
- * Filters the input object as a collection of maps.
- *
- * @param args The search arguments.
- * @return The filtered collection.
- * Returns the unaltered input if the input is not a collection or
array of objects.
- */
- public List filter(SearchArgs args) {
-
- if (input == null)
- return null;
-
- if (! type.isCollectionOrArray())
- throw runtimeException("Cannot call filterCollection()
on class type ''{0}''", type);
-
- // Create a new JsonList
- JsonList l = (JsonList)replaceWithMutables(input);
-
- // Do the search
- CollectionFilter filter = new
CollectionFilter(args.getSearch(), args.isIgnoreCase());
- filter.doQuery(l);
-
- // If sort or view isn't empty, then we need to make sure that
all entries in the
- // list are maps.
- Map<String,Boolean> sort = args.getSort();
- List<String> view = args.getView();
-
- if ((! sort.isEmpty()) || (! view.isEmpty())) {
- if (! sort.isEmpty())
- doSort(l, sort);
- if (! view.isEmpty())
- doView(l, view);
- }
-
- // Do the paging.
- int pos = args.getPosition();
- int limit = args.getLimit();
- if (pos != 0 || limit != 0) {
- int end = (limit == 0 || limit+pos >= l.size()) ?
l.size() : limit + pos;
- pos = Math.min(pos, l.size());
- JsonList l2 = new
DelegateList(((DelegateList)l).getClassMeta());
- l2.addAll(l.subList(pos, end));
- l = l2;
- }
-
- return l;
- }
-
- /*
- * If there are any non-Maps in the specified list, replaces them with
BeanMaps.
- */
- private Object replaceWithMutables(Object o) {
- if (o == null)
- return null;
- ClassMeta cm = session.getClassMetaForObject(o);
- if (cm.isCollection()) {
- JsonList l = new
DelegateList(session.getClassMetaForObject(o));
- ((Collection)o).forEach(x ->
l.add(replaceWithMutables(x)));
- return l;
- }
- if (cm.isMap() && o instanceof BeanMap) {
- BeanMap bm = (BeanMap)o;
- DelegateBeanMap dbm = new DelegateBeanMap(bm.getBean(),
session);
- bm.forEach((k,v) -> dbm.addKey(k.toString()));
- return dbm;
- }
- if (cm.isBean()) {
- BeanMap bm = session.toBeanMap(o);
- DelegateBeanMap dbm = new DelegateBeanMap(bm.getBean(),
session);
- bm.forEach((k,v) -> dbm.addKey(k.toString()));
- return dbm;
- }
- if (cm.isMap()) {
- Map m = (Map)o;
- DelegateMap dm = new DelegateMap(m, session);
- m.forEach((k,v) -> dm.put(k.toString(), v));
- return dm;
- }
- if (cm.isArray()) {
- return replaceWithMutables(alist((Object[])o));
- }
- return o;
- }
-
- /*
- * Sorts the specified list by the sort list.
- */
- private static void doSort(List list, Map<String,Boolean> sortList) {
-
- // We reverse the list and sort last to first.
- List<String> columns = listFrom(sortList.keySet());
- Collections.reverse(columns);
-
- for (final String c : columns) {
- final boolean isDesc = sortList.get(c);
-
- Comparator comp = new Comparator<Map>() {
- @Override /* Comparator */
- public int compare(Map m1, Map m2) {
- Comparable v1 =
toComparable(m1.get(c)), v2 = toComparable(m2.get(c));
- if (v1 == null && v2 == null)
- return 0;
- if (v1 == null)
- return (isDesc ? -1 : 1);
- if (v2 == null)
- return (isDesc ? 1 : -1);
- return (isDesc ? v2.compareTo(v1) :
v1.compareTo(v2));
- }
- };
- Collections.sort(list, comp);
- }
- }
-
- static final Comparable toComparable(Object o) {
- if (o == null)
- return null;
- if (o instanceof Comparable)
- return (Comparable)o;
- if (o instanceof Map)
- return ((Map)o).size();
- if (o.getClass().isArray())
- return Array.getLength(o);
- return o.toString();
- }
-
- /*
- * Filters all but the specified view columns on all entries in the
specified list.
- */
- private static void doView(List list, List<String> view) {
- for (ListIterator i = list.listIterator(); i.hasNext();) {
- Object o = i.next();
- Map m = (Map)o;
- doView(m, view);
- }
- }
-
- /*
- * Creates a new Map with only the entries specified in the view list.
- */
- private static Map doView(Map m, List<String> view) {
- if (m instanceof DelegateMap)
- ((DelegateMap)m).filterKeys(view);
- else
- ((DelegateBeanMap)m).filterKeys(view);
- return m;
- }
-
-
-
//====================================================================================================
- // CollectionFilter
-
//====================================================================================================
- private class CollectionFilter {
- IMatcher entryMatcher;
-
- public CollectionFilter(Map query, boolean ignoreCase) {
- if (query != null && ! query.isEmpty())
- entryMatcher = new MapMatcher(query,
ignoreCase);
- }
-
- public void doQuery(List in) {
- if (in == null || entryMatcher == null)
- return;
- for (Iterator i = in.iterator(); i.hasNext();) {
- Object o = i.next();
- if (! entryMatcher.matches(o))
- i.remove();
- }
- }
- }
-
-
//====================================================================================================
- // IMatcher
-
//====================================================================================================
- private interface IMatcher<E> {
- public boolean matches(E o);
- }
-
-
//====================================================================================================
- // MapMatcher
-
//====================================================================================================
- /*
- * Matches on a Map only if all specified entry matchers match.
- */
- private class MapMatcher implements IMatcher<Map> {
-
- Map<String,IMatcher> entryMatchers = new HashMap<>();
-
- public MapMatcher(Map query, boolean ignoreCase) {
- query.forEach((k,v) -> {
- if (k != null && v != null)
- entryMatchers.put(k.toString(), new
ObjectMatcher(v.toString(), ignoreCase));
- });
- }
-
- @Override /* IMatcher */
- public boolean matches(Map m) {
- if (m == null)
- return false;
- for (Map.Entry<String,IMatcher> e :
entryMatchers.entrySet()) {
- String key = e.getKey();
- Object val = null;
- if (m instanceof BeanMap) {
- val = ((BeanMap)m).getRaw(key);
- } else {
- val = m.get(key);
- }
- if (! e.getValue().matches(val))
- return false;
- }
- return true;
- }
- }
-
-
//====================================================================================================
- // ObjectMatcher
-
//====================================================================================================
- /*
- * Matcher that uses the correct matcher based on object type.
- * Used for objects when we can't determine the object type beforehand.
- */
- private class ObjectMatcher implements IMatcher<Object> {
-
- String searchPattern;
- boolean ignoreCase;
- DateMatcher dateMatcher;
- NumberMatcher numberMatcher;
- StringMatcher stringMatcher;
-
- ObjectMatcher(String searchPattern, boolean ignoreCase) {
- this.searchPattern = searchPattern;
- this.ignoreCase = ignoreCase;
- }
-
- @Override /* IMatcher */
- public boolean matches(Object o) {
- if (o instanceof Collection) {
- for (Object o2 : (Collection)o)
- if (matches(o2))
- return true;
- return false;
- }
- if (o != null && o.getClass().isArray()) {
- for (int i = 0; i < Array.getLength(o); i++)
- if (matches(Array.get(o, i)))
- return true;
- return false;
- }
- if (o instanceof Map) {
- for (Object o2 : ((Map)o).values())
- if (matches(o2))
- return true;
- return false;
- }
- if (o instanceof Number)
- return getNumberMatcher().matches(o);
- if (o instanceof Date || o instanceof Calendar)
- return getDateMatcher().matches(o);
- return getStringMatcher().matches(o);
- }
-
- private IMatcher getNumberMatcher() {
- if (numberMatcher == null)
- numberMatcher = new
NumberMatcher(searchPattern);
- return numberMatcher;
- }
-
- private IMatcher getStringMatcher() {
- if (stringMatcher == null)
- stringMatcher = new
StringMatcher(searchPattern, ignoreCase);
- return stringMatcher;
- }
-
- private IMatcher getDateMatcher() {
- if (dateMatcher == null)
- dateMatcher = new DateMatcher(searchPattern);
- return dateMatcher;
- }
- }
-
-
//====================================================================================================
- // NumberMatcher
-
//====================================================================================================
- private static class NumberMatcher implements IMatcher<Number> {
-
- private NumberPattern[] numberPatterns;
-
- /**
- * Construct a number matcher for the given search pattern.
- *
- * @param searchPattern A date search pattern. See class usage
for a description.
- */
- public NumberMatcher(String searchPattern) {
- numberPatterns = new NumberPattern[1];
- numberPatterns[0] = new NumberPattern(searchPattern);
-
- }
-
- /**
- * Returns 'true' if this integer matches the pattern(s).
- */
- @Override /* IMatcher */
- public boolean matches(Number in) {
- for (int i = 0; i < numberPatterns.length; i++) {
- if (! numberPatterns[i].matches(in))
- return false;
- }
- return true;
- }
-
- }
-
- /**
- * A construct representing a single search pattern.
- */
- private static class NumberPattern {
- NumberRange[] numberRanges;
-
- public NumberPattern(String searchPattern) {
-
- List<NumberRange> l = new LinkedList<>();
-
- for (String s : breakUpTokens(searchPattern)) {
- boolean isNot = (s.charAt(0) == '!');
- String token = s.substring(1);
- Pattern p =
Pattern.compile("(([<>]=?)?)(-?\\d+)(-?(-?\\d+)?)");
-
- // Possible patterns:
- // 123, >123, <123, >=123, <=123, >-123,
>=-123, 123-456, -123--456
- // Regular expression used:
(([<>]=?)?)(-?\d+)(-??(-?\d+))
- Matcher m = p.matcher(token);
-
- // If a non-numeric value was passed in for a
numeric value, just set the value to '0'.
- // (I think this might resolve a workaround in
custom queries).
- if (! m.matches())
- throw runtimeException("Numeric value
didn't match pattern: ''{0}''", token);
-
- String arg1 = m.group(1);
- String start = m.group(3);
- String end = m.group(5);
-
- l.add(new NumberRange(arg1, start, end, isNot));
- }
-
- numberRanges = l.toArray(new NumberRange[l.size()]);
- }
-
- private static String[] breakUpTokens(String s) {
- // Get rid of whitespace in "123 - 456"
- s = s.replaceAll("(-?\\d+)\\s*-\\s*(-?\\d+)", "$1-$2");
- // Get rid of whitespace in ">= 123"
- s = s.replaceAll("([<>]=?)\\s+(-?\\d+)", "$1$2");
- // Get rid of whitespace in "! 123"
- s = s.replaceAll("(!)\\s+(-?\\d+)", "$1$2");
-
- // Replace all commas with whitespace
- // Allows for alternate notation of: 123,456...
- s = s.replaceAll(",", " ");
-
- String[] s2 = s.split("\\s+");
-
- // Make all tokens 'ORed'. There is no way to AND
numeric tokens.
- for (int i = 0; i < s2.length; i++)
- if (! startsWith(s2[i], '!'))
- s2[i] = "^"+s2[i];
-
- return s2;
- }
-
- public boolean matches(Number number) {
- if (numberRanges.length == 0)
- return true;
- for (int i = 0; i < numberRanges.length; i++)
- if (numberRanges[i].matches(number))
- return true;
- return false;
- }
- }
-
- /**
- * A construct representing a single search range in a single search
pattern.
- * All possible forms of search patterns are boiled down to these
number ranges.
- */
- private static class NumberRange {
- int start;
- int end;
- boolean isNot;
-
- public NumberRange(String arg, String start, String end,
boolean isNot) {
-
- this.isNot = isNot;
-
- // 123, >123, <123, >=123, <=123, >-123, >=-123,
123-456, -123--456
- if (arg.equals("") && end == null) { // 123
- this.start = Integer.parseInt(start);
- this.end = this.start;
- } else if (arg.equals(">")) {
- this.start = Integer.parseInt(start)+1;
- this.end = Integer.MAX_VALUE;
- } else if (arg.equals(">=")) {
- this.start = Integer.parseInt(start);
- this.end = Integer.MAX_VALUE;
- } else if (arg.equals("<")) {
- this.start = Integer.MIN_VALUE;
- this.end = Integer.parseInt(start)-1;
- } else if (arg.equals("<=")) {
- this.start = Integer.MIN_VALUE;
- this.end = Integer.parseInt(start);
- } else {
- this.start = Integer.parseInt(start);
- this.end = Integer.parseInt(end);
- }
- }
-
- public boolean matches(Number n) {
- long i = n.longValue();
- boolean b = (i>=start && i<=end);
- if (isNot)
- b = !b;
- return b;
- }
- }
-
-
//====================================================================================================
- // DateMatcher
-
//====================================================================================================
- /** The list of all valid timestamp formats */
- private SimpleDateFormat[] validTimestampFormats = new
SimpleDateFormat[0];
- {
-
setValidTimestampFormats("yyyy.MM.dd.HH.mm.ss","yyyy.MM.dd.HH.mm","yyyy.MM.dd.HH","yyyy.MM.dd","yyyy.MM","yyyy");
- }
-
- /**
- * Use this method to override the allowed search patterns when used in
locales where time formats are different.
- *
- * @param s A comma-delimited list of valid time formats.
- */
- public void setValidTimestampFormats(String...s) {
- validTimestampFormats = new SimpleDateFormat[s.length];
- for (int i = 0; i < s.length; i++)
- validTimestampFormats[i] = new SimpleDateFormat(s[i]);
- }
-
- private class DateMatcher implements IMatcher<Object> {
-
- private TimestampPattern[] patterns;
-
- /**
- * Construct a timestamp matcher for the given search pattern.
- *
- * @param searchPattern The search pattern.
- */
- DateMatcher(String searchPattern) {
- patterns = new TimestampPattern[1];
- patterns[0] = new TimestampPattern(searchPattern);
-
- }
-
- /**
- * Returns <jk>true</jk> if the specified date matches the
pattern passed in through the constructor.
- *
- * <p>
- * <br>The Object can be of type {@link Date} or {@link
Calendar}.
- * <br>Always returns <jk>false</jk> on <jk>null</jk> input.
- */
- @Override /* IMatcher */
- public boolean matches(Object in) {
- if (in == null)
- return false;
-
- Calendar c = null;
- if (in instanceof Calendar)
- c = (Calendar)in;
- else if (in instanceof Date) {
- c = Calendar.getInstance();
- c.setTime((Date)in);
- } else {
- return false;
- }
- for (int i = 0; i < patterns.length; i++) {
- if (! patterns[i].matches(c))
- return false;
- }
- return true;
- }
- }
-
- /**
- * A construct representing a single search pattern.
- */
- private class TimestampPattern {
- TimestampRange[] ranges;
- List<TimestampRange> l = new LinkedList<>();
-
- public TimestampPattern(String s) {
-
- // Handle special case where timestamp is enclosed in
quotes.
- // This can occur on hyperlinks created by group-by
queries.
- // e.g. '2007/01/29 04:17:43 PM'
- if (s.charAt(0) == '\'' && s.charAt(s.length()-1) ==
'\'')
- s = s.substring(1, s.length()-1);
-
- // Pattern for finding <,>,<=,>=
- Pattern p1 =
Pattern.compile("^\\s*([<>](?:=)?)\\s*(\\S+.*)$");
- // Pattern for finding range dash (e.g. xxx - yyy)
- Pattern p2 = Pattern.compile("^(\\s*-\\s*)(\\S+.*)$");
-
- // States are...
- // 1 - Looking for <,>,<=,>=
- // 2 - Looking for single date.
- // 3 - Looking for start date.
- // 4 - Looking for -
- // 5 - Looking for end date.
- int state = 1;
-
- String op = null;
- CalendarP startDate = null;
-
- ParsePosition pp = new ParsePosition(0);
- Matcher m = null;
- String seg = s;
-
- while (! seg.equals("") || state != 1) {
- if (state == 1) {
- m = p1.matcher(seg);
- if (m.matches()) {
- op = m.group(1);
- seg = m.group(2);
- state = 2;
- } else {
- state = 3;
- }
- } else if (state == 2) {
- l.add(new TimestampRange(op,
parseDate(seg, pp)));
- //tokens.add("^"+op +
parseTimestamp(seg, pp));
- seg =
seg.substring(pp.getIndex()).trim();
- pp.setIndex(0);
- state = 1;
- } else if (state == 3) {
- startDate = parseDate(seg, pp);
- seg =
seg.substring(pp.getIndex()).trim();
- pp.setIndex(0);
- state = 4;
- } else if (state == 4) {
- // Look for '-'
- m = p2.matcher(seg);
- if (m.matches()) {
- state = 5;
- seg = m.group(2);
- } else {
- // This is a single date (e.g.
2002/01/01)
- l.add(new
TimestampRange(startDate));
- state = 1;
- }
- } else if (state == 5) {
- l.add(new TimestampRange(startDate,
parseDate(seg, pp)));
- seg =
seg.substring(pp.getIndex()).trim();
- pp.setIndex(0);
- state = 1;
- }
- }
-
- ranges = l.toArray(new TimestampRange[l.size()]);
- }
-
- public boolean matches(Calendar c) {
- if (ranges.length == 0)
- return true;
- for (int i = 0; i < ranges.length; i++)
- if (ranges[i].matches(c))
- return true;
- return false;
- }
- }
-
- /**
- * A construct representing a single search range in a single search
pattern.
- * All possible forms of search patterns are boiled down to these
timestamp ranges.
- */
- private static class TimestampRange {
- Calendar start;
- Calendar end;
-
- public TimestampRange(CalendarP start, CalendarP end) {
- this.start = start.copy().roll(MILLISECOND,
-1).getCalendar();
- this.end = end.roll(1).getCalendar();
- }
-
- public TimestampRange(CalendarP singleDate) {
- this.start = singleDate.copy().roll(MILLISECOND,
-1).getCalendar();
- this.end = singleDate.roll(1).getCalendar();
- }
-
- public TimestampRange(String op, CalendarP singleDate) {
- if (op.equals(">")) {
- this.start =
singleDate.roll(1).roll(MILLISECOND, -1).getCalendar();
- this.end = new CalendarP(new
Date(Long.MAX_VALUE), 0).getCalendar();
- } else if (op.equals("<")) {
- this.start = new CalendarP(new Date(0),
0).getCalendar();
- this.end = singleDate.getCalendar();
- } else if (op.equals(">=")) {
- this.start = singleDate.roll(MILLISECOND,
-1).getCalendar();
- this.end = new CalendarP(new
Date(Long.MAX_VALUE), 0).getCalendar();
- } else if (op.equals("<=")) {
- this.start = new CalendarP(new Date(0),
0).getCalendar();
- this.end = singleDate.roll(1).getCalendar();
- }
- }
-
- public boolean matches(Calendar c) {
- boolean b = (c.after(start) && c.before(end));
- return b;
- }
- }
-
- private static int getPrecisionField(String pattern) {
- if (pattern.indexOf('s') != -1)
- return SECOND;
- if (pattern.indexOf('m') != -1)
- return MINUTE;
- if (pattern.indexOf('H') != -1)
- return HOUR_OF_DAY;
- if (pattern.indexOf('d') != -1)
- return DAY_OF_MONTH;
- if (pattern.indexOf('M') != -1)
- return MONTH;
- if (pattern.indexOf('y') != -1)
- return YEAR;
- return Calendar.MILLISECOND;
- }
-
-
- /**
- * Parses a timestamp string off the beginning of the string segment
'seg'.
- * Goes through each possible valid timestamp format until it finds a
match.
- * The position where the parsing left off is stored in pp.
- *
- * @param seg The string segment being parsed.
- * @param pp Where parsing last left off.
- * @return An object representing a timestamp.
- */
- CalendarP parseDate(String seg, ParsePosition pp) {
-
- CalendarP cal = null;
-
- for (int i = 0; i < validTimestampFormats.length && cal ==
null; i++) {
- pp.setIndex(0);
- SimpleDateFormat f = validTimestampFormats[i];
- Date d = f.parse(seg, pp);
- int idx = pp.getIndex();
- if (idx != 0) {
- // it only counts if the next character is '-',
'space', or end-of-string.
- char c = (seg.length() == idx ? 0 :
seg.charAt(idx));
- if (c == 0 || c == '-' ||
Character.isWhitespace(c))
- cal = new CalendarP(d,
getPrecisionField(f.toPattern()));
- }
- }
-
- if (cal == null)
- throw runtimeException("Invalid date encountered:
''{0}''", seg);
-
- return cal;
- }
-
- /**
- * Combines a Calendar with a precision identifier.
- */
- private static class CalendarP {
- public Calendar c;
- public int precision;
-
- public CalendarP(Date date, int precision) {
- c = Calendar.getInstance();
- c.setTime(date);
- this.precision = precision;
- }
-
- public CalendarP copy() {
- return new CalendarP(c.getTime(), precision);
- }
-
- public CalendarP roll(int field, int amount) {
- c.add(field, amount);
- return this;
- }
-
- public CalendarP roll(int amount) {
- return roll(precision, amount);
- }
-
- public Calendar getCalendar() {
- return c;
- }
- }
-
-
//====================================================================================================
- // StringMatcher
-
//====================================================================================================
- private static class StringMatcher implements IMatcher<Object> {
-
- private SearchPattern[] searchPatterns;
-
- /**
- * Construct a string matcher for the given search pattern.
- *
- * @param searchPattern The search pattern. See class usage
for details.
- * @param ignoreCase If <jk>true</jk>, use case-insensitive
matching.
- */
- public StringMatcher(String searchPattern, boolean ignoreCase) {
- this.searchPatterns = new SearchPattern[1];
- this.searchPatterns[0] = new
SearchPattern(searchPattern, ignoreCase);
- }
-
- /**
- * Returns 'true' if this string matches the pattern(s).
- * Always returns false on null input.
- */
- @Override /* IMatcher */
- public boolean matches(Object in) {
- if (in == null)
- return false;
- for (int i = 0; i < searchPatterns.length; i++) {
- if (! searchPatterns[i].matches(in.toString()))
- return false;
- }
- return true;
- }
-
- }
- /**
- * A construct representing a single search pattern.
- */
- private static class SearchPattern {
- Pattern[] orPatterns, andPatterns, notPatterns;
-
- public SearchPattern(String searchPattern, boolean ignoreCase) {
-
- List<Pattern> ors = new LinkedList<>();
- List<Pattern> ands = new LinkedList<>();
- List<Pattern> nots = new LinkedList<>();
-
- breakUpTokens(searchPattern).forEach(x -> {
- char prefix = x.charAt(0);
- String token = x.substring(1);
-
- token =
token.replaceAll("([\\?\\*\\+\\\\\\[\\]\\{\\}\\(\\)\\^\\$\\.])", "\\\\$1");
- token = token.replace("\u9997", ".*");
- token = token.replace("\u9996", ".?");
-
- if (! token.startsWith(".*"))
- token = "^" + token;
- if (! token.endsWith(".*"))
- token = token + "$";
-
- int flags = Pattern.DOTALL;
- if (ignoreCase)
- flags |= Pattern.CASE_INSENSITIVE;
-
- Pattern p = Pattern.compile(token, flags);
-
- if (prefix == '^')
- ors.add(p);
- else if (prefix == '+')
- ands.add(p);
- else if (prefix == '-')
- nots.add(p);
- });
- orPatterns = ors.toArray(new Pattern[ors.size()]);
- andPatterns = ands.toArray(new Pattern[ands.size()]);
- notPatterns = nots.toArray(new Pattern[nots.size()]);
- }
-
- /**
- * Break up search pattern into separate tokens.
- */
- private static List<String> breakUpTokens(String s) {
-
- // If the string is null or all whitespace, return an
empty vector.
- if (s == null || s.trim().length() == 0)
- return Collections.emptyList();
-
- // Pad with spaces.
- s = " " + s + " ";
-
- // Replace instances of [+] and [-] inside single and
double quotes with
- // \u2001 and \u2002 for later replacement.
- int escapeCount = 0;
- boolean inSingleQuote = false;
- boolean inDoubleQuote = false;
- char[] ca = s.toCharArray();
- for (int i = 0; i < ca.length; i++) {
- if (ca[i] == '\\')
- escapeCount++;
- else if (escapeCount % 2 == 0) {
- if (ca[i] == '\'')
- inSingleQuote = ! inSingleQuote;
- else if (ca[i] == '"')
- inDoubleQuote = ! inDoubleQuote;
- else if (ca[i] == '+' && (inSingleQuote
|| inDoubleQuote))
- ca[i] = '\u9999';
- else if (ca[i] == '-' && (inSingleQuote
|| inDoubleQuote))
- ca[i] = '\u9998';
- }
- if (ca[i] != '\\')
- escapeCount = 0;
- }
- s = new String(ca);
-
- // Remove spaces between '+' or '-' and the keyword.
- //s = perl5Util.substitute("s/([\\+\\-])\\s+/$1/g", s);
- s = s.replaceAll("([\\+\\-])\\s+", "$1");
-
- // Replace: [*]->[\u3001] as placeholder for '%',
ignore escaped.
- s = replace(s, '*', '\u9997', true);
- // Replace: [?]->[\u3002] as placeholder for '_',
ignore escaped.
- s = replace(s, '?', '\u9996', true);
- // Replace: [\*]->[*], [\?]->[?]
- s = unEscapeChars(s, new char[]{'*','?'});
-
- // Remove spaces
- s = s.trim();
-
- // Re-replace the [+] and [-] characters inside quotes.
- s = s.replace('\u9999', '+');
- s = s.replace('\u9998', '-');
-
- String[] sa = splitQuoted(s, ' ');
- List<String> l = list(sa.length);
- int numOrs = 0;
- for (int i = 0; i < sa.length; i++) {
- String token = sa[i];
- int len = token.length();
- if (len > 0) {
- char c = token.charAt(0);
- String s2 = null;
- if ((c == '+' || c == '-') && len > 1)
- s2 = token.substring(1);
- else {
- s2 = token;
- c = '^';
- numOrs++;
- }
- // Trim off leading and trailing single
and double quotes.
- if (s2.matches("\".*\"") ||
s2.matches("'.*'"))
- s2 = s2.substring(1,
s2.length()-1);
-
- // Replace: [\"]->["]
- s2 = unEscapeChars(s2, new
char[]{'"','\''});
-
- // Un-escape remaining escaped
backslashes.
- s2 = unEscapeChars(s2, new
char[]{'\\'});
-
- l.add(c + s2);
- }
- }
-
- // If there's a single OR clause, turn it into an AND
clause (makes the SQL cleaner).
- if (numOrs == 1) {
- int ii = l.size();
- for (int i = 0; i < ii; i++) {
- String x = l.get(i);
- if (x.charAt(0) == '^')
- l.set(i, '+'+x.substring(1));
- }
- }
- return l;
- }
-
- public boolean matches(String input) {
- if (input == null)
- return false;
- for (int i = 0; i < andPatterns.length; i++)
- if (! andPatterns[i].matcher(input).matches())
- return false;
- for (int i = 0; i < notPatterns.length; i++)
- if (notPatterns[i].matcher(input).matches())
- return false;
- for (int i = 0; i < orPatterns.length; i++)
- if (orPatterns[i].matcher(input).matches())
- return true;
- return orPatterns.length == 0;
- }
-
- }
-
- /*
- * Same as split(String, char), but does not split on characters inside
- * single quotes.
- * Does not split on escaped delimiters, and escaped quotes are also
ignored.
- * Example:
- * split("a,b,c",',') -> {"a","b","c"}
- * split("a,'b,b,b',c",',') -> {"a","'b,b,b'","c"}
- */
- static final String[] splitQuoted(String s, char c) {
-
- if (s == null || s.matches("\\s*"))
- return new String[0];
-
- List<String> l = new LinkedList<>();
- char[] sArray = s.toCharArray();
- int x1 = 0;
- int escapeCount = 0;
- boolean inSingleQuote = false;
- boolean inDoubleQuote = false;
- for (int i = 0; i < sArray.length; i++) {
- if (sArray[i] == '\\')
- escapeCount++;
- else if (escapeCount % 2 == 0) {
- if (sArray[i] == '\'' && ! inDoubleQuote)
- inSingleQuote = ! inSingleQuote;
- else if (sArray[i] == '"' && ! inSingleQuote)
- inDoubleQuote = ! inDoubleQuote;
- else if (sArray[i] == c && ! inSingleQuote && !
inDoubleQuote) {
- String s2 = new String(sArray, x1,
i-x1).trim();
- l.add(s2);
- x1 = i+1;
- }
- }
- if (sArray[i] != '\\')
- escapeCount = 0;
- }
- String s2 = new String(sArray, x1, sArray.length-x1).trim();
- l.add(s2);
-
- return l.toArray(new String[l.size()]);
- }
-
- /**
- * Replaces tokens in a string with a different token.
- *
- * <p>
- * replace("A and B and C", "and", "or") -> "A or B or C"
- * replace("andandand", "and", "or") -> "ororor"
- * replace(null, "and", "or") -> null
- * replace("andandand", null, "or") -> "andandand"
- * replace("andandand", "", "or") -> "andandand"
- * replace("A and B and C", "and", null) -> "A B C"
- * @param ignoreEscapedChars Specify 'true' if escaped 'from'
characters should be ignored.
- */
- static String replace(String s, char from, char to, boolean
ignoreEscapedChars) {
- if (s == null)
- return null;
-
- char[] sArray = s.toCharArray();
-
- int escapeCount = 0;
- int singleQuoteCount = 0;
- int doubleQuoteCount = 0;
- for (int i = 0; i < sArray.length; i++) {
- char c = sArray[i];
- if (c == '\\' && ignoreEscapedChars)
- escapeCount++;
- else if (escapeCount % 2 == 0) {
- if (c == from && singleQuoteCount % 2 == 0 &&
doubleQuoteCount % 2 == 0)
- sArray[i] = to;
- }
- if (sArray[i] != '\\')
- escapeCount = 0;
- }
- return new String(sArray);
- }
-
- /**
- * Removes escape characters (specified by escapeChar) from the
specified characters.
- */
- static String unEscapeChars(String s, char[] toEscape) {
- char escapeChar = '\\';
- if (s == null)
- return null;
- if (s.length() == 0)
- return s;
- StringBuffer sb = new StringBuffer(s.length());
- char[] sArray = s.toCharArray();
- for (int i = 0; i < sArray.length; i++) {
- char c = sArray[i];
-
- if (c == escapeChar) {
- if (i+1 != sArray.length) {
- char c2 = sArray[i+1];
- boolean isOneOf = false;
- for (int j = 0; j < toEscape.length &&
! isOneOf; j++)
- isOneOf = (c2 == toEscape[j]);
- if (isOneOf) {
- i++;
- } else if (c2 == escapeChar) {
- sb.append(escapeChar);
- i++;
- }
- }
- }
- sb.append(sArray[i]);
- }
- return sb.toString();
- }
-}
-
-
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/SearchArgs.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/SearchArgs.java
deleted file mode 100644
index 11f4b06aa..000000000
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/utils/SearchArgs.java
+++ /dev/null
@@ -1,349 +0,0 @@
-//
***************************************************************************************************************************
-// * Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file *
-// * distributed with this work for additional information regarding copyright
ownership. The ASF licenses this file *
-// * to you 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.apache.juneau.utils;
-
-import static org.apache.juneau.internal.ThrowableUtils.*;
-import static org.apache.juneau.internal.CollectionUtils.*;
-import static org.apache.juneau.internal.StringUtils.*;
-
-import java.util.*;
-
-import org.apache.juneau.internal.*;
-
-/**
- * Encapsulates arguments for basic search/view/sort/position/limit
functionality.
- *
- * <ul class='seealso'>
- * <li class='extlink'>{@source}
- * </ul>
- */
-public class SearchArgs {
-
- /**
- * Default search args.
- */
- public static SearchArgs DEFAULT = SearchArgs.create().build();
-
- private final Map<String,String> search;
- private final List<String> view;
- private final Map<String,Boolean> sort;
- private final int position, limit;
- private final boolean ignoreCase;
-
- SearchArgs(Builder b) {
- this.search = unmodifiable(copyOf(b.search));
- this.view = unmodifiable(copyOf(b.view));
- this.sort = unmodifiable(copyOf(b.sort));
- this.position = b.position;
- this.limit = b.limit;
- this.ignoreCase = b.ignoreCase;
- }
-
- /**
- * Creates a new builder for {@link SearchArgs}
- *
- * @return A new builder for {@link SearchArgs}
- */
- public static Builder create() {
- return new Builder();
- }
-
- /**
- * Builder for {@link SearchArgs} class.
- */
- public static final class Builder {
- Map<String,String> search = map();
- List<String> view = list();
- Map<String,Boolean> sort = map();
- int position, limit;
- boolean ignoreCase;
-
- /**
- * Adds search terms to this builder.
- *
- * <p>
- * The search terms are a comma-delimited list of key/value
pairs of column-names and search tokens.
- *
- * <p>
- * For example:
- * <p class='bjava'>
- * <jv>builder</jv>.search(<js>"column1=foo*, column2=bar
baz"</js>);
- * </p>
- *
- * <p>
- * It's up to implementers to decide the syntax and meaning of
the search terms.
- *
- * <p>
- * Whitespace is trimmed from column names and search tokens.
- *
- * @param searchTerms
- * The search terms string.
- * Can be <jk>null</jk>.
- * @return This object.
- */
- public Builder search(String searchTerms) {
- split(searchTerms, x -> {
- int i = indexOf(x, '=', '>', '<');
- if (i == -1)
- throw runtimeException("Invalid search
terms: ''{0}''", searchTerms);
- char c = x.charAt(i);
- search(x.substring(0, i).trim(), x.substring(c
== '=' ? i+1 : i).trim());
- });
- return this;
- }
-
- /**
- * Adds a search term to this builder.
- *
- * <p>
- * It's up to implementers to decide the syntax and meaning of
the search term.
- *
- * @param column The column being searched.
- * @param searchTerm The search term.
- * @return This object.
- */
- public Builder search(String column, String searchTerm) {
- this.search.put(column, searchTerm);
- return this;
- }
-
- /**
- * Specifies the list of columns to view.
- *
- * <p>
- * The columns argument is a simple comma-delimited list of
column names.
- *
- * <p>
- * For example:
- * <p class='bjava'>
- * <jv>builder</jv>.view(<js>"column1, column2"</js>);
- * </p>
- *
- * <p>
- * Whitespace is trimmed from column names.
- *
- * <p>
- * Empty view columns imply view all columns.
- *
- * @param columns
- * The columns being viewed.
- * Can be <jk>null</jk>.
- * @return This object.
- */
- public Builder view(String columns) {
- if (columns != null)
- addAll(this.view, StringUtils.split(columns));
- return this;
- }
-
- /**
- * Specifies the list of columns to view.
- *
- * <p>
- * Empty view columns imply view all columns.
- *
- * @param columns The columns being viewed.
- * @return This object.
- */
- public Builder view(Collection<String> columns) {
- this.view.addAll(columns);
- return this;
- }
-
- /**
- * Specifies the sort arguments.
- *
- * <p>
- * The sort argument is a simple comma-delimited list of column
names.
- * <br>Column names can be suffixed with <js>'+'</js> or
<js>'-'</js> to indicate ascending or descending order.
- * <br>No suffix implies ascending order.
- *
- * <p>
- * For example:
- * <p class='bjava'>
- * <jc>// Order by column1 ascending, then column2
descending.</jc>
- * <jv>builder</jv>.sort(<js>"column1, column2-"</js>);
- * </p>
- *
- * <p>
- * Note that the order of the order arguments is important.
- *
- * <p>
- * Whitespace is trimmed from column names.
- *
- * @param sortArgs
- * The columns to sort by.
- * Can be <jk>null</jk>.
- * @return This object.
- */
- public Builder sort(String sortArgs) {
- if (sortArgs != null)
- sort(alist(StringUtils.split(sortArgs)));
- return this;
- }
-
- /**
- * Specifies the sort arguments.
- *
- * <p>
- * Column names can be suffixed with <js>'+'</js> or
<js>'-'</js> to indicate ascending or descending order.
- * <br>No suffix implies ascending order.
- *
- * <p>
- * Note that the order of the sort is important.
- *
- * @param sortArgs
- * The columns to sort by.
- * Can be <jk>null</jk>.
- * @return This object.
- */
- public Builder sort(Collection<String> sortArgs) {
- sortArgs.forEach(x -> {
- boolean isDesc = false;
- if (endsWith(x, '-', '+')) {
- isDesc = endsWith(x, '-');
- x = x.substring(0, x.length()-1);
- }
- this.sort.put(x, isDesc);
- });
- return this;
- }
-
- /**
- * Specifies the starting line number.
- *
- * @param position The zero-indexed position.
- * @return This object.
- */
- public Builder position(int position) {
- this.position = position;
- return this;
- }
-
- /**
- * Specifies the number of rows to return.
- *
- * @param limit
- * The number of rows to return.
- * If <c><=0</c>, all rows should be returned.
- * @return This object.
- */
- public Builder limit(int limit) {
- this.limit = limit;
- return this;
- }
-
- /**
- * Specifies whether case-insensitive search should be used.
- *
- * <p>
- * The default is <jk>false</jk>.
- *
- * @param value The ignore-case flag value.
- * @return This object.
- */
- public Builder ignoreCase(boolean value) {
- this.ignoreCase = value;
- return this;
- }
-
- /**
- * Construct the {@link SearchArgs} object.
- *
- * <p>
- * This method can be called multiple times to construct new
objects.
- *
- * @return A new {@link SearchArgs} object initialized with
values in this builder.
- */
- public SearchArgs build() {
- return new SearchArgs(this);
- }
- }
-
- /**
- * The query search terms.
- *
- * <p>
- * The search terms are key/value pairs consisting of column-names and
search tokens.
- *
- * <p>
- * It's up to implementers to decide the syntax and meaning of the
search term.
- *
- * @return An unmodifiable map of query search terms.
- */
- public Map<String,String> getSearch() {
- return search;
- }
-
- /**
- * The view columns.
- *
- * <p>
- * The view columns are the list of columns that should be displayed.
- * An empty list implies all columns should be displayed.
- *
- * @return An unmodifiable list of columns to view.
- */
- public List<String> getView() {
- return view;
- }
-
- /**
- * The sort columns.
- *
- * <p>
- * The sort columns are key/value pairs consisting of column-names and
direction flags
- * (<jk>false</jk> = ascending, <jk>true</jk> = descending).
- *
- * @return An unmodifiable ordered map of sort columns and directions.
- */
- public Map<String,Boolean> getSort() {
- return sort;
- }
-
- /**
- * The first-row position.
- *
- * @return
- * The zero-indexed row number of the first row to display.
- * Default is <c>0</c>
- */
- public int getPosition() {
- return position;
- }
-
- /**
- * The number of rows to return.
- *
- * @return
- * The number of rows to return in the result.
- * Default is <c>0</c> which means return all rows.
- */
- public int getLimit() {
- return limit;
- }
-
- /**
- * The ignore-case flag.
- *
- * <p>
- * Used in conjunction with {@link #getSearch()} to specify whether
case-insensitive searches should be performed.
- *
- * @return
- * The number of rows to return in the result.
- * Default is <jk>false</jk>.
- */
- public boolean isIgnoreCase() {
- return ignoreCase;
- }
-}
diff --git
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/converter/Queryable.java
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/converter/Queryable.java
index 3fc0e2cf4..6394e0fdc 100644
---
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/converter/Queryable.java
+++
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/converter/Queryable.java
@@ -12,10 +12,10 @@
//
***************************************************************************************************************************
package org.apache.juneau.rest.converter;
+import org.apache.juneau.*;
import org.apache.juneau.objecttools.*;
import org.apache.juneau.rest.*;
-import org.apache.juneau.utils.*;
-import org.apache.juneau.utils.SearchArgs;
+import org.apache.juneau.rest.httppart.*;
/**
* Converter for enabling of search/view/sort/page support on response objects
returned by a <c>@RestOp</c>-annotated method.
@@ -118,16 +118,6 @@ public final class Queryable implements RestConverter {
+ "collectionFormat:'csv',"
+ "examples:{example:'?o=name,birthDate-'}"
+ "},"
- + "{"
- + "in:'query',"
- + "name:'i',"
- + "description:'"
- + "Case-insensitive.\n"
- + "Flag for case-insensitive matching on the
search parameters."
- + "',"
- + "type:'boolean',"
- + "examples:{example:'?i=true'}"
- + "},"
+ "{"
+ "in:'query',"
+ "name:'p',"
@@ -156,9 +146,15 @@ public final class Queryable implements RestConverter {
public Object convert(RestRequest req, Object o) {
if (o == null)
return null;
- SearchArgs searchArgs = req.getQueryParams().getSearchArgs();
- if (searchArgs == null)
- return o;
- return new PojoQuery(o,
req.getBeanSession()).filter(searchArgs);
+
+ Value<Object> v = Value.of(o);
+ RequestQueryParams params = req.getQueryParams();
+ BeanSession bs = req.getBeanSession();
+
+ params.getSearchArgs().ifPresent(x ->
v.set(ObjectSearcher.create().run(bs, v.get(), x)));
+ params.getSortArgs().ifPresent(x ->
v.set(ObjectSorter.create().run(bs, v.get(), x)));
+ params.getViewArgs().ifPresent(x ->
v.set(ObjectViewer.create().run(bs, v.get(), x)));
+ params.getPageArgs().ifPresent(x ->
v.set(ObjectPaginator.create().run(bs, v.get(), x)));
+ return v.get();
}
}
diff --git
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/httppart/RequestFormParams.java
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/httppart/RequestFormParams.java
index 96dca48c3..44808f727 100644
---
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/httppart/RequestFormParams.java
+++
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/httppart/RequestFormParams.java
@@ -34,7 +34,6 @@ import org.apache.juneau.internal.*;
import org.apache.juneau.rest.*;
import org.apache.juneau.rest.util.*;
import org.apache.juneau.svl.*;
-import org.apache.juneau.utils.*;
/**
* Represents the parsed form-data parameters in an HTTP request.
@@ -80,7 +79,6 @@ import org.apache.juneau.utils.*;
* <li class='jm'>{@link RequestFormParams#getAll(String)
getAll(String)}
* <li class='jm'>{@link
RequestFormParams#getFirst(String) getFirst(String)}
* <li class='jm'>{@link RequestFormParams#getLast(String)
getLast(String)}
- * <li class='jm'>{@link RequestFormParams#getSearchArgs()
getSearchArgs()}
* </ul>
* <li>Methods overridding form data parameters:
* <ul class='javatreec'>
@@ -551,57 +549,6 @@ public class RequestFormParams {
return new RequestFormParams(this);
}
- /**
- * Locates the special search query arguments in the query and returns
them as a {@link SearchArgs} object.
- *
- * <p>
- * The query arguments are as follows:
- * <ul class='spaced-list'>
- * <li>
- * <js>"&s="</js> - A comma-delimited list of
column-name/search-token pairs.
- * <br>Example: <js>"&s=column1=foo*,column2=*bar"</js>
- * <li>
- * <js>"&v="</js> - A comma-delimited list column
names to view.
- * <br>Example: <js>"&v=column1,column2"</js>
- * <li>
- * <js>"&o="</js> - A comma-delimited list column
names to sort by.
- * <br>Column names can be suffixed with <js>'-'</js> to
indicate descending order.
- * <br>Example: <js>"&o=column1,column2-"</js>
- * <li>
- * <js>"&p="</js> - The zero-index row number of the
first row to display.
- * <br>Example: <js>"&p=100"</js>
- * <li>
- * <js>"&l="</js> - The number of rows to return.
- * <br><c>0</c> implies return all rows.
- * <br>Example: <js>"&l=100"</js>
- * <li>
- * <js>"&i="</js> - The case-insensitive search flag.
- * <br>Example: <js>"&i=true"</js>
- * </ul>
- *
- * <ul class='notes'>
- * <li class='note'>
- * Whitespace is trimmed in the parameters.
- * </ul>
- *
- * @return
- * A new {@link SearchArgs} object initialized with the special
search query arguments.
- * <br>Returns <jk>null</jk> if no search arguments were found.
- */
- public SearchArgs getSearchArgs() {
- if (contains("s","v","o","p","l","i")) {
- return new SearchArgs.Builder()
- .search(get("s").asString().orElse(null))
- .view(get("v").asString().orElse(null))
- .sort(get("o").asString().orElse(null))
- .position(get("p").asInteger().orElse(null))
- .limit(get("l").asInteger().orElse(null))
- .ignoreCase(get("i").asBoolean().orElse(null))
- .build();
- }
- return null;
- }
-
/**
* Converts the parameters to a readable string.
*
diff --git
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/httppart/RequestQueryParams.java
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/httppart/RequestQueryParams.java
index 0c0749861..1f8069c09 100644
---
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/httppart/RequestQueryParams.java
+++
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/httppart/RequestQueryParams.java
@@ -18,15 +18,16 @@ import static org.apache.juneau.internal.StringUtils.*;
import static org.apache.juneau.internal.ThrowableUtils.*;
import static org.apache.juneau.assertions.Assertions.*;
import static org.apache.juneau.httppart.HttpPartType.*;
+import static java.util.Optional.*;
import java.util.*;
import org.apache.http.*;
import org.apache.juneau.httppart.*;
import org.apache.juneau.internal.*;
+import org.apache.juneau.objecttools.*;
import org.apache.juneau.rest.*;
import org.apache.juneau.svl.*;
-import org.apache.juneau.utils.*;
import org.apache.juneau.*;
import org.apache.juneau.collections.*;
import org.apache.juneau.http.*;
@@ -81,6 +82,9 @@ import org.apache.juneau.http.part.*;
* <li class='jm'>{@link
RequestQueryParams#getFirst(String) getFirst(String)}
* <li class='jm'>{@link
RequestQueryParams#getLast(String) getLast(String)}
* <li class='jm'>{@link
RequestQueryParams#getSearchArgs() getSearchArgs()}
+ * <li class='jm'>{@link RequestQueryParams#getViewArgs()
getViewArgs()}
+ * <li class='jm'>{@link RequestQueryParams#getSortArgs()
getSortArgs()}
+ * <li class='jm'>{@link RequestQueryParams#getPageArgs()
getPageArgs()}
* </ul>
* <li>Methods overridding query parameters:
* <ul class='javatreec'>
@@ -515,54 +519,43 @@ public class RequestQueryParams {
}
/**
- * Locates the special search query arguments in the query and returns
them as a {@link SearchArgs} object.
+ * Locates the search query argument ({@code &s=}) in the query
string and returns them as a {@link SearchArgs} object.
*
- * <p>
- * The query arguments are as follows:
- * <ul class='spaced-list'>
- * <li>
- * <js>"&s="</js> - A comma-delimited list of
column-name/search-token pairs.
- * <br>Example: <js>"&s=column1=foo*,column2=*bar"</js>
- * <li>
- * <js>"&v="</js> - A comma-delimited list column
names to view.
- * <br>Example: <js>"&v=column1,column2"</js>
- * <li>
- * <js>"&o="</js> - A comma-delimited list column
names to sort by.
- * <br>Column names can be suffixed with <js>'-'</js> to
indicate descending order.
- * <br>Example: <js>"&o=column1,column2-"</js>
- * <li>
- * <js>"&p="</js> - The zero-index row number of the
first row to display.
- * <br>Example: <js>"&p=100"</js>
- * <li>
- * <js>"&l="</js> - The number of rows to return.
- * <br><c>0</c> implies return all rows.
- * <br>Example: <js>"&l=100"</js>
- * <li>
- * <js>"&i="</js> - The case-insensitive search flag.
- * <br>Example: <js>"&i=true"</js>
- * </ul>
- *
- * <ul class='notes'>
- * <li class='note'>
- * Whitespace is trimmed in the parameters.
- * </ul>
+ * @return
+ * A new {@link SearchArgs} object initialized with the query
arguments, or {@link Optional#empty()} if not found.
+ */
+ public Optional<SearchArgs> getSearchArgs() {
+ return
ofNullable(SearchArgs.create(get("s").asString().orElse(null)));
+ }
+
+ /**
+ * Locates the view query argument ({@code &v=}) in the query
string and returns them as a {@link ViewArgs} object.
*
* @return
- * A new {@link SearchArgs} object initialized with the special
search query arguments.
- * <br>Returns <jk>null</jk> if no search arguments were found.
+ * A new {@link ViewArgs} object initialized with the query
arguments, or {@link Optional#empty()} if not found.
*/
- public SearchArgs getSearchArgs() {
- if (contains("s","v","o","p","l","i")) {
- return new SearchArgs.Builder()
- .search(get("s").asString().orElse(null))
- .view(get("v").asString().orElse(null))
- .sort(get("o").asString().orElse(null))
- .position(get("p").asInteger().orElse(null))
- .limit(get("l").asInteger().orElse(null))
- .ignoreCase(get("i").asBoolean().orElse(null))
- .build();
- }
- return null;
+ public Optional<ViewArgs> getViewArgs() {
+ return
ofNullable(ViewArgs.create(get("v").asString().orElse(null)));
+ }
+
+ /**
+ * Locates the sort query argument ({@code &o=}) in the query
string and returns them as a {@link SortArgs} object.
+ *
+ * @return
+ * A new {@link SortArgs} object initialized with the query
arguments, or {@link Optional#empty()} if not found.
+ */
+ public Optional<SortArgs> getSortArgs() {
+ return
ofNullable(SortArgs.create(get("o").asString().orElse(null)));
+ }
+
+ /**
+ * Locates the position/limit query arguments ({@code &p=}, {@code
&l=}) in the query string and returns them as a {@link PageArgs} object.
+ *
+ * @return
+ * A new {@link PageArgs} object initialized with the query
arguments, or {@link Optional#empty()} if not found.
+ */
+ public Optional<PageArgs> getPageArgs() {
+ return
ofNullable(PageArgs.create(get("p").asInteger().orElse(null),
get("l").asInteger().orElse(null)));
}
/**
diff --git
a/juneau-utest/src/test/java/org/apache/juneau/utils/PojoQueryTest.java
b/juneau-utest/src/test/java/org/apache/juneau/utils/PojoQueryTest.java
deleted file mode 100755
index f755cd251..000000000
--- a/juneau-utest/src/test/java/org/apache/juneau/utils/PojoQueryTest.java
+++ /dev/null
@@ -1,418 +0,0 @@
-//
***************************************************************************************************************************
-// * Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file *
-// * distributed with this work for additional information regarding copyright
ownership. The ASF licenses this file *
-// * to you 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.apache.juneau.utils;
-
-import static org.apache.juneau.assertions.Assertions.*;
-import static org.apache.juneau.internal.CollectionUtils.*;
-import static org.junit.Assert.*;
-import static org.junit.runners.MethodSorters.*;
-
-import java.util.*;
-
-import org.apache.juneau.*;
-import org.apache.juneau.json.*;
-import org.apache.juneau.serializer.*;
-import org.apache.juneau.swaps.*;
-import org.junit.*;
-
-@SuppressWarnings("rawtypes")
-@FixMethodOrder(NAME_ASCENDING)
-public class PojoQueryTest {
-
-
//====================================================================================================
- // filterCollection, string search, 1 level
-
//====================================================================================================
- @Test
- public void testFilterCollectionStringSearchOneLevel() throws Exception
{
- SearchArgs sa;
- List results;
-
- List<A> in = list(new A("foo"),new A("bar"),new A("baz"));
-
- PojoQuery q = new PojoQuery(in, BeanContext.DEFAULT_SESSION);
-
- sa = SearchArgs.create().search("f=foo").build();
- results = q.filter(sa);
- assertObject(results).asJson().is("[{f:'foo'}]");
-
- sa = SearchArgs.create().search("f=fo*").build();
- results = q.filter(sa);
- assertObject(results).asJson().is("[{f:'foo'}]");
-
- sa = SearchArgs.create().search("f=*ar").build();
- results = q.filter(sa);
- assertObject(results).asJson().is("[{f:'bar'}]");
-
- sa = SearchArgs.create().search("f=foo bar").build();
- results = q.filter(sa);
- assertObject(results).asJson().is("[{f:'foo'},{f:'bar'}]");
- }
-
- public class A {
- public String f;
-
- A() {}
-
- A(String f) {
- this.f = f;
- }
- }
-
-
//====================================================================================================
- // filterCollection, date search, 1 level
-
//====================================================================================================
- @Test
- public void testFilterCollectionDateSearchOneLevel() throws Exception {
- BeanSession session = BeanContext.DEFAULT_SESSION;
- WriterSerializer s =
JsonSerializer.create().ssq().swaps(TemporalCalendarSwap.IsoLocalDateTime.class).build();
- B[] in;
- PojoQuery q;
- SearchArgs sa;
- List results;
-
- in = new B[] {
- new B(2010, 0, 1),
- new B(2011, 0, 1),
- new B(2011, 0, 31),
- new B(2012, 0, 1)
- };
- q = new PojoQuery(in, session);
-
- sa = SearchArgs.create().search("f=2011").build();
- results = q.filter(sa);
-
assertEquals("[{f:'2011-01-01T00:00:00'},{f:'2011-01-31T00:00:00'}]",
s.serialize(results));
-
- sa = SearchArgs.create().search("f=2011.01").build();
- results = q.filter(sa);
-
assertEquals("[{f:'2011-01-01T00:00:00'},{f:'2011-01-31T00:00:00'}]",
s.serialize(results));
-
- sa = SearchArgs.create().search("f=2011.01.01").build();
- results = q.filter(sa);
- assertEquals("[{f:'2011-01-01T00:00:00'}]",
s.serialize(results));
-
- in = new B[] {
- new B(2011, 00, 01, 11, 59, 59),
- new B(2011, 00, 01, 12, 00, 00),
- new B(2011, 00, 01, 12, 59, 59),
- new B(2011, 00, 01, 13, 00, 00)
- };
- q = new PojoQuery(in, session);
-
- sa = SearchArgs.create().search("f=2011.01.01.12").build();
- results = q.filter(sa);
-
assertEquals("[{f:'2011-01-01T12:00:00'},{f:'2011-01-01T12:59:59'}]",
s.serialize(results));
-
- in = new B[] {
- new B(2011, 00, 01, 12, 29, 59),
- new B(2011, 00, 01, 12, 30, 00),
- new B(2011, 00, 01, 12, 30, 59),
- new B(2011, 00, 01, 12, 31, 00)
- };
- q = new PojoQuery(in, session);
- sa = SearchArgs.create().search("f=2011.01.01.12.30").build();
- results = q.filter(sa);
-
assertEquals("[{f:'2011-01-01T12:30:00'},{f:'2011-01-01T12:30:59'}]",
s.serialize(results));
-
- in = new B[] {
- new B(2011, 00, 01, 12, 30, 29),
- new B(2011, 00, 01, 12, 30, 30),
- new B(2011, 00, 01, 12, 30, 31)
- };
- q = new PojoQuery(in, session);
- sa =
SearchArgs.create().search("f=2011.01.01.12.30.30").build();
- results = q.filter(sa);
- assertEquals("[{f:'2011-01-01T12:30:30'}]",
s.serialize(results));
-
- // Open-ended ranges
-
- in = new B[] {
- new B(2000, 11, 31),
- new B(2001, 00, 01)
- };
- q = new PojoQuery(in, session);
-
- sa = SearchArgs.create().search("f>2000").build();
- results = q.filter(sa);
- assertEquals("[{f:'2001-01-01T00:00:00'}]",
s.serialize(results));
-
- sa = SearchArgs.create().search("f>=2001").build();
- results = q.filter(sa);
- assertEquals("[{f:'2001-01-01T00:00:00'}]",
s.serialize(results));
-
- sa = SearchArgs.create().search("f<2001").build();
- results = q.filter(sa);
- assertEquals("[{f:'2000-12-31T00:00:00'}]",
s.serialize(results));
-
- sa = SearchArgs.create().search("f<=2000").build();
- results = q.filter(sa);
- assertEquals("[{f:'2000-12-31T00:00:00'}]",
s.serialize(results));
-
- in = new B[] {
- new B(2011, 00, 01, 12, 29, 59),
- new B(2011, 00, 01, 12, 30, 00)
- };
- q = new PojoQuery(in, session);
-
- sa = SearchArgs.create().search("f>=2011.01.01.12.30").build();
- results = q.filter(sa);
- assertEquals("[{f:'2011-01-01T12:30:00'}]",
s.serialize(results));
-
- sa = SearchArgs.create().search("f<2011.01.01.12.30").build();
- results = q.filter(sa);
- assertEquals("[{f:'2011-01-01T12:29:59'}]",
s.serialize(results));
-
- in = new B[] {
- new B(2011, 00, 01, 12, 30, 59),
- new B(2011, 00, 01, 12, 31, 00)
- };
- q = new PojoQuery(in, session);
-
- sa = SearchArgs.create().search("f>2011.01.01.12.30").build();
- results = q.filter(sa);
- assertEquals("[{f:'2011-01-01T12:31:00'}]",
s.serialize(results));
-
- sa = SearchArgs.create().search("f<=2011.01.01.12.30").build();
- results = q.filter(sa);
- assertEquals("[{f:'2011-01-01T12:30:59'}]",
s.serialize(results));
-
- // Closed range
-
- in = new B[] {
- new B(2000, 11, 31, 23, 59, 59),
- new B(2001, 00, 01, 00, 00, 00),
- new B(2003, 05, 30, 23, 59, 59),
- new B(2003, 06, 01, 00, 00, 00)
- };
- q = new PojoQuery(in, session);
-
- sa = SearchArgs.create().search("f=2001 - 2003.06.30").build();
- results = q.filter(sa);
-
assertEquals("[{f:'2001-01-01T00:00:00'},{f:'2003-06-30T23:59:59'}]",
s.serialize(results));
-
- // ORed timestamps
-
- in = new B[] {
- new B(2000, 11, 31),
- new B(2001, 00, 01),
- new B(2001, 11, 31),
- new B(2002, 00, 01)
- };
- q = new PojoQuery(in, session);
-
- sa = SearchArgs.create().search("f=2001 2003 2005").build();
- results = q.filter(sa);
-
assertEquals("[{f:'2001-01-01T00:00:00'},{f:'2001-12-31T00:00:00'}]",
s.serialize(results));
-
- in = new B[] {
- new B(2002, 11, 31),
- new B(2003, 00, 01),
- new B(2003, 11, 31),
- new B(2004, 00, 01)
- };
- q = new PojoQuery(in, session);
-
- sa = SearchArgs.create().search("f=2001 2003 2005").build();
- results = q.filter(sa);
-
assertEquals("[{f:'2003-01-01T00:00:00'},{f:'2003-12-31T00:00:00'}]",
s.serialize(results));
-
- in = new B[] {
- new B(2004, 11, 31),
- new B(2005, 00, 01),
- new B(2005, 11, 31),
- new B(2006, 00, 01)
- };
- q = new PojoQuery(in, session);
-
- sa = SearchArgs.create().search("f=2001 2003 2005").build();
- results = q.filter(sa);
-
assertEquals("[{f:'2005-01-01T00:00:00'},{f:'2005-12-31T00:00:00'}]",
s.serialize(results));
- }
-
- public class B {
- public Calendar f;
-
- B() {}
-
- B(int year, int month, int day) {
- this.f = new GregorianCalendar(year, month, day);
- }
-
- B(int year, int month, int day, int hour, int minute, int
second) {
- this.f = new GregorianCalendar(year, month, day, hour,
minute, second);
- }
- }
-
-
//====================================================================================================
- // filterCollection, int search, 1 level
-
//====================================================================================================
- @Test
- public void testFilterCollectionIntSearchOneLevel() throws Exception {
- BeanSession session = BeanContext.DEFAULT_SESSION;
- SearchArgs sa;
- List results;
-
- List<C> in = list(new C(1),new C(2),new C(3));
-
- PojoQuery q = new PojoQuery(in, session);
-
- sa = SearchArgs.create().search("f=1").build();
- results = q.filter(sa);
- assertObject(results).asJson().is("[{f:1}]");
-
- sa = SearchArgs.create().search("f>1").build();
- results = q.filter(sa);
- assertObject(results).asJson().is("[{f:2},{f:3}]");
-
- sa = SearchArgs.create().search("f>=2").build();
- results = q.filter(sa);
- assertObject(results).asJson().is("[{f:2},{f:3}]");
-
- sa = SearchArgs.create().search("f<=2").build();
- results = q.filter(sa);
- assertObject(results).asJson().is("[{f:1},{f:2}]");
-
- sa = SearchArgs.create().search("f<2").build();
- results = q.filter(sa);
- assertObject(results).asJson().is("[{f:1}]");
-
- sa = SearchArgs.create().search("f=1 3").build();
- results = q.filter(sa);
- assertObject(results).asJson().is("[{f:1},{f:3}]");
- }
-
- public class C {
- public int f;
-
- C() {}
-
- C(int f) {
- this.f = f;
- }
- }
-
-
//====================================================================================================
- // filterCollection, view, 1 level
-
//====================================================================================================
- @Test
- public void testFilterCollectionViewOneLevel() throws Exception {
- BeanSession session = BeanContext.DEFAULT_SESSION;
- SearchArgs sa;
- List results;
-
- List<E> in = list(new E("foo", 1, true),new E("bar", 2,
false),new E("baz", 3, true));
-
- PojoQuery q = new PojoQuery(in, session);
-
- sa = SearchArgs.create().view("f1").build();
- results = q.filter(sa);
-
assertObject(results).asJson().is("[{f1:'foo'},{f1:'bar'},{f1:'baz'}]");
-
- sa = SearchArgs.create().view("f2").build();
- results = q.filter(sa);
- assertObject(results).asJson().is("[{f2:1},{f2:2},{f2:3}]");
-
- sa = SearchArgs.create().view("f3").build();
- results = q.filter(sa);
-
assertObject(results).asJson().is("[{f3:true},{f3:false},{f3:true}]");
-
- sa = SearchArgs.create().view("f3,f2,f1").build();
- results = q.filter(sa);
-
assertObject(results).asJson().is("[{f3:true,f2:1,f1:'foo'},{f3:false,f2:2,f1:'bar'},{f3:true,f2:3,f1:'baz'}]");
- }
-
- public class E {
- public String f1;
- public int f2;
- public boolean f3;
-
- E() {}
-
- E(String f1, int f2, boolean f3) {
- this.f1 = f1;
- this.f2 = f2;
- this.f3 = f3;
- }
- }
-
-
-
//====================================================================================================
- // testSorting
-
//====================================================================================================
- @Test
- public void testSorting() throws Exception {
- BeanSession session = BeanContext.DEFAULT_SESSION;
- WriterSerializer s =
JsonSerializer.create().ssq().swaps(TemporalCalendarSwap.IsoLocalDateTime.class).build();
- SearchArgs sa;
- List results;
-
- I[] in = new I[] {
- new I(1, "foo", true, 2010, 1, 1),
- new I(2, "bar", false, 2011, 1, 1),
- new I(3, "baz", true, 2012, 1, 1),
- };
-
- PojoQuery q = new PojoQuery(in, session);
-
- sa = SearchArgs.create().sort("f2").view("f1, f2").build();
- results = q.filter(sa);
-
assertEquals("[{f1:2,f2:'bar'},{f1:3,f2:'baz'},{f1:1,f2:'foo'}]",
s.serialize(results));
-
- sa = SearchArgs.create().sort("f2-").view("f1,f2").build();
- results = q.filter(sa);
-
assertEquals("[{f1:1,f2:'foo'},{f1:3,f2:'baz'},{f1:2,f2:'bar'}]",
s.serialize(results));
-
- sa = SearchArgs.create().sort("f3").view("f1,f3").build();
- results = q.filter(sa);
- assertEquals("[{f1:2,f3:false},{f1:1,f3:true},{f1:3,f3:true}]",
s.serialize(results));
-
- sa = SearchArgs.create().sort("f3,f1+").view("f1,f3").build();
- results = q.filter(sa);
- assertEquals("[{f1:2,f3:false},{f1:1,f3:true},{f1:3,f3:true}]",
s.serialize(results));
-
- sa = SearchArgs.create().sort("f3,f1-").view("f1,f3").build();
- results = q.filter(sa);
- assertEquals("[{f1:2,f3:false},{f1:3,f3:true},{f1:1,f3:true}]",
s.serialize(results));
-
- sa =
SearchArgs.create().sort("f1").view("f1").limit(1).position(0).build();
- results = q.filter(sa);
- assertEquals("[{f1:1}]", s.serialize(results));
-
- sa =
SearchArgs.create().sort("f1").view("f1").limit(3).position(0).build();
- results = q.filter(sa);
- assertEquals("[{f1:1},{f1:2},{f1:3}]", s.serialize(results));
-
- sa =
SearchArgs.create().sort("f1").view("f1").limit(1).position(2).build();
- results = q.filter(sa);
- assertEquals("[{f1:3}]", s.serialize(results));
-
- sa =
SearchArgs.create().sort("f1").view("f1").limit(100).position(2).build();
- results = q.filter(sa);
- assertEquals("[{f1:3}]", s.serialize(results));
- }
-
- public class I {
- public int f1;
- public String f2;
- public boolean f3;
- public Calendar f4;
-
- I() {}
-
- I(int f1, String f2, boolean f3, int year, int month, int day) {
- this.f1 = f1;
- this.f2 = f2;
- this.f3 = f3;
- this.f4 = new GregorianCalendar(year, month, day);
- }
- }
-}
\ No newline at end of file