Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
// ***************************************************************************************************************************
// * 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.StringUtils.*;
import java.lang.reflect.*;
import java.text.*;
import java.util.*;
import java.util.regex.*;
import org.apache.juneau.*;
import org.apache.juneau.internal.*;
/**
* Designed to provide search/view/sort/paging filtering on tabular in-memory POJO models.
*
*
* It can also perform just view filtering on beans/maps.
*
*
* Examples of tabular POJO models:
*
*
Collection{@code
*
Collection{@code }
*
Map[]
*
Bean[]
*
*
*
* Tabular POJO models can be thought of as tables of data. For example, a list of the following beans...
*
* public MyBean {
* public int myInt;
* public String myString;
* public Date myDate;
* }
*
* ... can be thought of a table containing the following columns...
*
*
*
myInt
myString
myDate
*
123
'foobar'
yyyy/MM/dd HH:mm:ss
*
...
*
*
*
* From this table, you can perform the following functions:
*
*
* Search - Return only rows where a search pattern matches.
*
* View - Return only the specified subset of columns in the specified order.
*
* Sort - Sort the table by one or more columns.
*
* Position/limit - Only return a subset of rows.
*
*
*
Search
*
* 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.
* Multiple search patterns are ANDed (i.e. all patterns must match for the row to be returned).
*
*
Example:
*
*
* {myInt:'123'} - Return only rows where the myInt column is 123.
*
* {myString:'foobar'} - Return only rows where the myString column is 'foobar'.
*
* {myDate:'2001'} - Return only rows where the myDate column have dates in the year 2001.
*
*
*
String Patterns
*
* 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.
*
*
Example string query patterns:
*
*
foo - The string 'foo'
*
foo bar - The string 'foo' or the string 'bar'
*
'foo bar' - The phrase 'foo bar'
*
"foo bar" - The phrase 'foo bar'
*
foo* - * matches zero-or-more characters.
*
foo? - ? matches exactly one character
*
*
*
Notes:
*
*
* Whitespace is ignored around search patterns.
*
* Prepend + to tokens that must match. (e.g. +foo* +*bar)
*
* Prepend - to tokens that must not match. (e.g. +foo* -*bar)
*
*
*
Numeric Patterns
*
* Any object of type {@link Number} (or numeric primitives) can be searched using numeric patterns.
*
*
Example numeric query patterns:
*
*
123 - The single number 123
*
1 2 3 - 1, 2, or 3
*
1-100 - Between 1 and 100
*
1 - 100 - Between 1 and 100
*
1 - 100 200-300 - Between 1 and 100 or between 200 and 300
*
> 100 - Greater than 100
*
>= 100 - Greater than or equal to 100
*
!123 - Not 123
*
*
*
Notes:
*
*
* Whitespace is ignored in search patterns.
*
* Negative numbers are supported.
*
*
*
Date Patterns
*
* Any object of type {@link Date} or {@link Calendar} can be searched using date patterns.
*
*
* The default valid input timestamp formats (which can be overridden via the {@link #setValidTimestampFormats(String...)}
* method are...
*
*
*
yyyy.MM.dd.HH.mm.ss
*
yyyy.MM.dd.HH.mm
*
yyyy.MM.dd.HH
*
yyyy.MM.dd
*
yyyy.MM
*
yyyy
*
*
*
Example date query patterns:
*
*
2001 - A specific year.
*
2001.01.01.10.50 - A specific time.
*
>2001 - After a specific year.
*
>=2001 - During or after a specific year.
*
2001 - 2003.06.30 - A date range.
*
2001 2003 2005 - Multiple date patterns are ORed.
*
*
*
Notes:
*
*
* Whitespace is ignored in search patterns.
*
*
*
View
*
* The view capability allows you to return only the specified subset of columns in the specified order.
* The view parameter is a list of either Strings or Maps.
*
*
Example view parameters:
*
*
column1 - Return only column 'column1'.
*
column2, column1 - Return only columns 'column2' and 'column1' in that order.
*
*
*
Sort
*
* The sort capability allows you to sort values by the specified rows.
* The sort parameter is a list of strings with an optional '+' or '-' suffix representing
* ascending and descending order accordingly.
*
*
Example sort parameters:
*
*
column1 - Sort rows by column 'column1' ascending.
*
column1+ - Sort rows by column 'column1' ascending.
*
column1- - Sort rows by column 'column1' descending.
*
column1, column2- - Sort rows by column 'column1' ascending, then 'column2' descending.
*
*
*
Paging
*
* Use the position and limit parameters to specify a subset of rows to return.
*/
@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 new FormattedRuntimeException("Cannot call filterCollection() on class type ''{0}''", type);
// Create a new ObjectList
ObjectList l = (ObjectList)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 sort = args.getSort();
List 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());
ObjectList 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()) {
ObjectList l = new DelegateList(session.getClassMetaForObject(o));
for (Object o2 : (Collection)o)
l.add(replaceWithMutables(o2));
return l;
}
if (cm.isMap() && o instanceof BeanMap) {
BeanMap bm = (BeanMap)o;
DelegateBeanMap dbm = new DelegateBeanMap(bm.getBean(), session);
for (Object key : bm.keySet())
dbm.addKey(key.toString());
return dbm;
}
if (cm.isBean()) {
BeanMap bm = session.toBeanMap(o);
DelegateBeanMap dbm = new DelegateBeanMap(bm.getBean(), session);
for (Object key : bm.keySet())
dbm.addKey(key.toString());
return dbm;
}
if (cm.isMap()) {
Map m = (Map)o;
DelegateMap dm = new DelegateMap(session.getClassMetaForObject(m));
for (Map.Entry e : (Set)m.entrySet())
dm.put(e.getKey().toString(), e.getValue());
return dm;
}
if (cm.isArray()) {
return replaceWithMutables(Arrays.asList((Object[])o));
}
return o;
}
/*
* Sorts the specified list by the sort list.
*/
private static void doSort(List list, Map sortList) {
// We reverse the list and sort last to first.
List columns = new ArrayList<>(sortList.keySet());
Collections.reverse(columns);
for (final String c : columns) {
final boolean isDesc = sortList.get(c);
Comparator comp = new Comparator