blob: 674695cb2465c0911014b2fa7c788c0428535be3 [file] [log] [blame]
/*
* 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.usergrid.mq;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.EnumSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import org.antlr.runtime.ANTLRStringStream;
import org.antlr.runtime.CommonTokenStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.usergrid.persistence.Entity;
import org.apache.usergrid.persistence.Results;
import org.apache.usergrid.utils.JsonUtils;
import org.springframework.util.Assert;
import org.apache.commons.lang.StringUtils;
import static org.apache.commons.codec.binary.Base64.decodeBase64;
import static org.apache.commons.lang.StringUtils.isBlank;
import static org.apache.commons.lang.StringUtils.split;
import static org.apache.usergrid.persistence.Schema.PROPERTY_TYPE;
import static org.apache.usergrid.persistence.Schema.PROPERTY_UUID;
import org.apache.usergrid.persistence.index.query.CounterResolution;
import org.apache.usergrid.persistence.index.query.Identifier;
import org.apache.usergrid.persistence.Query.Level;
import static org.apache.usergrid.utils.ClassUtils.cast;
import static org.apache.usergrid.utils.ConversionUtils.uuid;
import static org.apache.usergrid.utils.ListUtils.first;
import static org.apache.usergrid.utils.ListUtils.firstBoolean;
import static org.apache.usergrid.utils.ListUtils.firstInteger;
import static org.apache.usergrid.utils.ListUtils.firstLong;
import static org.apache.usergrid.utils.ListUtils.firstUuid;
import static org.apache.usergrid.utils.ListUtils.isEmpty;
import static org.apache.usergrid.utils.MapUtils.toMapList;
public class Query {
private static final Logger logger = LoggerFactory.getLogger( Query.class );
public static final int DEFAULT_LIMIT = 10;
protected String type;
protected List<SortPredicate> sortPredicates = new ArrayList<SortPredicate>();
protected List<FilterPredicate> filterPredicates = new ArrayList<FilterPredicate>();
protected UUID startResult;
protected String cursor;
protected int limit = 0;
protected boolean limitSet = false;
protected Map<String, String> selectSubjects = new LinkedHashMap<String, String>();
protected boolean mergeSelectResults = false;
protected Level level = Level.ALL_PROPERTIES;
protected String connection;
protected List<String> permissions;
protected boolean reversed;
protected boolean reversedSet = false;
protected Long startTime;
protected Long finishTime;
protected boolean pad;
protected CounterResolution resolution = CounterResolution.ALL;
protected List<Identifier> users;
protected List<Identifier> groups;
protected List<Identifier> identifiers;
protected List<String> categories;
protected List<CounterFilterPredicate> counterFilters;
public Query() {
}
public Query( String type ) {
this.type = type;
}
public Query( Query q ) {
if ( q != null ) {
type = q.type;
sortPredicates = q.sortPredicates != null ? new ArrayList<SortPredicate>( q.sortPredicates ) : null;
filterPredicates = q.filterPredicates != null ? new ArrayList<FilterPredicate>( q.filterPredicates ) : null;
startResult = q.startResult;
cursor = q.cursor;
limit = q.limit;
limitSet = q.limitSet;
selectSubjects = q.selectSubjects != null ? new LinkedHashMap<String, String>( q.selectSubjects ) : null;
mergeSelectResults = q.mergeSelectResults;
level = q.level;
connection = q.connection;
permissions = q.permissions != null ? new ArrayList<String>( q.permissions ) : null;
reversed = q.reversed;
reversedSet = q.reversedSet;
startTime = q.startTime;
finishTime = q.finishTime;
resolution = q.resolution;
pad = q.pad;
users = q.users != null ? new ArrayList<Identifier>( q.users ) : null;
groups = q.groups != null ? new ArrayList<Identifier>( q.groups ) : null;
identifiers = q.identifiers != null ? new ArrayList<Identifier>( q.identifiers ) : null;
categories = q.categories != null ? new ArrayList<String>( q.categories ) : null;
counterFilters =
q.counterFilters != null ? new ArrayList<CounterFilterPredicate>( q.counterFilters ) : null;
}
}
public static Query fromQL( String ql ) {
if ( ql == null ) {
return null;
}
ql = ql.trim();
String qlt = ql.toLowerCase();
if ( !qlt.startsWith( "select" ) && !qlt.startsWith( "insert" ) && !qlt.startsWith( "update" ) && !qlt
.startsWith( "delete" ) ) {
if ( qlt.startsWith( "order by" ) ) {
ql = "select * " + ql;
}
else {
ql = "select * where " + ql;
}
}
try {
ANTLRStringStream in = new ANTLRStringStream( ql.trim() );
QueryFilterLexer lexer = new QueryFilterLexer( in );
CommonTokenStream tokens = new CommonTokenStream( lexer );
QueryFilterParser parser = new QueryFilterParser( tokens );
Query q = parser.ql();
return q;
}
catch ( Exception e ) {
logger.error( "Unable to parse \"" + ql + "\"", e );
}
return null;
}
public static Query newQueryIfNull( Query query ) {
if ( query == null ) {
query = new Query();
}
return query;
}
public static Query fromJsonString( String json ) {
Object o = JsonUtils.parse( json );
if ( o instanceof Map ) {
@SuppressWarnings({ "unchecked", "rawtypes" }) Map<String, List<String>> params =
cast( toMapList( ( Map ) o ) );
return fromQueryParams( params );
}
return null;
}
public static Query fromQueryParams( Map<String, List<String>> params ) {
String type = null;
Query q = null;
String ql = null;
String connection = null;
UUID start = null;
String cursor = null;
Integer limit = null;
List<String> permissions = null;
Boolean reversed = null;
Long startTime = null;
Long finishTime = null;
Boolean pad = null;
CounterResolution resolution = null;
List<Identifier> users = null;
List<Identifier> groups = null;
List<Identifier> identifiers = null;
List<String> categories = null;
List<CounterFilterPredicate> counterFilters = null;
List<String> l = null;
ql = first( params.get( "ql" ) );
type = first( params.get( "type" ) );
reversed = firstBoolean( params.get( "reversed" ) );
connection = first( params.get( "connection" ) );
start = firstUuid( params.get( "start" ) );
cursor = first( params.get( "cursor" ) );
limit = firstInteger( params.get( "limit" ) );
permissions = params.get( "permission" );
startTime = firstLong( params.get( "start_time" ) );
finishTime = firstLong( params.get( "end_time" ) );
l = params.get( "resolution" );
if ( !isEmpty( l ) ) {
resolution = CounterResolution.fromString( l.get( 0 ) );
}
users = Identifier.fromList( params.get( "user" ) );
groups = Identifier.fromList( params.get( "group" ) );
categories = params.get( "category" );
l = params.get( "counter" );
if ( !isEmpty( l ) ) {
counterFilters = CounterFilterPredicate.fromList( l );
}
pad = firstBoolean( params.get( "pad" ) );
for ( Entry<String, List<String>> param : params.entrySet() ) {
if ( ( param.getValue() == null ) || ( param.getValue().size() == 0 ) ) {
Identifier identifier = Identifier.from( param.getKey() );
if ( identifier != null ) {
if ( identifiers == null ) {
identifiers = new ArrayList<Identifier>();
}
identifiers.add( identifier );
}
}
}
if ( ql != null ) {
q = Query.fromQL( ql );
}
l = params.get( "filter" );
if ( !isEmpty( l ) ) {
q = newQueryIfNull( q );
for ( String s : l ) {
q.addFilter( s );
}
}
l = params.get( "sort" );
if ( !isEmpty( l ) ) {
q = newQueryIfNull( q );
for ( String s : l ) {
q.addSort( s );
}
}
if ( type != null ) {
q = newQueryIfNull( q );
q.setEntityType( type );
}
if ( connection != null ) {
q = newQueryIfNull( q );
q.setConnectionType( connection );
}
if ( permissions != null ) {
q = newQueryIfNull( q );
q.setPermissions( permissions );
}
if ( start != null ) {
q = newQueryIfNull( q );
q.setStartResult( start );
}
if ( cursor != null ) {
q = newQueryIfNull( q );
q.setCursor( cursor );
}
if ( limit != null ) {
q = newQueryIfNull( q );
q.setLimit( limit );
}
if ( startTime != null ) {
q = newQueryIfNull( q );
q.setStartTime( startTime );
}
if ( finishTime != null ) {
q = newQueryIfNull( q );
q.setFinishTime( finishTime );
}
if ( resolution != null ) {
q = newQueryIfNull( q );
q.setResolution( resolution );
}
if ( categories != null ) {
q = newQueryIfNull( q );
q.setCategories( categories );
}
if ( counterFilters != null ) {
q = newQueryIfNull( q );
q.setCounterFilters( counterFilters );
}
if ( pad != null ) {
q = newQueryIfNull( q );
q.setPad( pad );
}
if ( users != null ) {
q = newQueryIfNull( q );
q.setUsers( users );
}
if ( groups != null ) {
q = newQueryIfNull( q );
q.setGroups( groups );
}
if ( identifiers != null ) {
q = newQueryIfNull( q );
q.setIdentifiers( identifiers );
}
if ( reversed != null ) {
q = newQueryIfNull( q );
q.setReversed( reversed );
}
return q;
}
public static Query searchForProperty( String propertyName, Object propertyValue ) {
Query q = new Query();
q.addEqualityFilter( propertyName, propertyValue );
return q;
}
public static Query findForProperty( String propertyName, Object propertyValue ) {
Query q = new Query();
q.addEqualityFilter( propertyName, propertyValue );
q.setLimit( 1 );
return q;
}
public static Query fromUUID( UUID uuid ) {
Query q = new Query();
q.addIdentifier( Identifier.fromUUID( uuid ) );
return q;
}
public static Query fromName( String name ) {
Query q = new Query();
q.addIdentifier( Identifier.fromName( name ) );
return q;
}
public static Query fromEmail( String email ) {
Query q = new Query();
q.addIdentifier( Identifier.fromEmail( email ) );
return q;
}
public static Query fromIdentifier( Object id ) {
Query q = new Query();
q.addIdentifier( Identifier.from( id ) );
return q;
}
public boolean isIdsOnly() {
if ( ( selectSubjects.size() == 1 ) && selectSubjects.containsKey( PROPERTY_UUID ) ) {
level = Level.IDS;
return true;
}
return false;
}
public void setIdsOnly( boolean idsOnly ) {
if ( idsOnly ) {
selectSubjects = new LinkedHashMap<String, String>();
selectSubjects.put( PROPERTY_UUID, PROPERTY_UUID );
level = Level.IDS;
}
else if ( isIdsOnly() ) {
selectSubjects = new LinkedHashMap<String, String>();
level = Level.ALL_PROPERTIES;
}
}
public Level getResultsLevel() {
isIdsOnly();
return level;
}
public void setResultsLevel( Level level ) {
setIdsOnly( level == Level.IDS );
this.level = level;
}
public Query withResultsLevel( Level level ) {
setIdsOnly( level == Level.IDS );
this.level = level;
return this;
}
public String getEntityType() {
return type;
}
public void setEntityType( String type ) {
this.type = type;
}
public Query withEntityType( String type ) {
this.type = type;
return this;
}
public String getConnectionType() {
return connection;
}
public void setConnectionType( String connection ) {
this.connection = connection;
}
public Query withConnectionType( String connection ) {
this.connection = connection;
return this;
}
public List<String> getPermissions() {
return permissions;
}
public void setPermissions( List<String> permissions ) {
this.permissions = permissions;
}
public Query withPermissions( List<String> permissions ) {
this.permissions = permissions;
return this;
}
public Query addSelect( String select ) {
return addSelect( select, null );
}
public Query addSelect( String select, String output ) {
// be paranoid with the null checks because
// the query parser sometimes flakes out
if ( select == null ) {
return this;
}
select = select.trim();
if ( select.equals( "*" ) ) {
return this;
}
mergeSelectResults = StringUtils.isNotEmpty(output);
if ( output == null ) {
output = "";
}
selectSubjects.put( select, output );
return this;
}
public boolean hasSelectSubjects() {
return !selectSubjects.isEmpty();
}
public Set<String> getSelectSubjects() {
return selectSubjects.keySet();
}
public Map<String, String> getSelectAssignments() {
return selectSubjects;
}
public void setMergeSelectResults( boolean mergeSelectResults ) {
this.mergeSelectResults = mergeSelectResults;
}
public Query withMergeSelectResults( boolean mergeSelectResults ) {
this.mergeSelectResults = mergeSelectResults;
return this;
}
public boolean isMergeSelectResults() {
return mergeSelectResults;
}
public Query addSort( String propertyName ) {
if ( isBlank( propertyName ) ) {
return this;
}
propertyName = propertyName.trim();
if ( propertyName.indexOf( ',' ) >= 0 ) {
String[] propertyNames = split( propertyName, ',' );
for ( String s : propertyNames ) {
addSort( s );
}
return this;
}
SortDirection direction = SortDirection.ASCENDING;
if ( propertyName.indexOf( ' ' ) >= 0 ) {
String[] parts = split( propertyName, ' ' );
if ( parts.length > 1 ) {
propertyName = parts[0];
direction = SortDirection.find( parts[1] );
}
}
else if ( propertyName.startsWith( "-" ) ) {
propertyName = propertyName.substring( 1 );
direction = SortDirection.DESCENDING;
}
else if ( propertyName.startsWith( "+" ) ) {
propertyName = propertyName.substring( 1 );
direction = SortDirection.ASCENDING;
}
return addSort( propertyName, direction );
}
public Query addSort( String propertyName, SortDirection direction ) {
if ( isBlank( propertyName ) ) {
return this;
}
propertyName = propertyName.trim();
for ( SortPredicate s : sortPredicates ) {
if ( s.getPropertyName().equals( propertyName ) ) {
logger.error(
"Attempted to set sort order for " + s.getPropertyName() + " more than once, discardng..." );
return this;
}
}
sortPredicates.add( new SortPredicate( propertyName, direction ) );
return this;
}
public Query addSort( SortPredicate sort ) {
if ( sort == null ) {
return this;
}
for ( SortPredicate s : sortPredicates ) {
if ( s.getPropertyName().equals( sort.getPropertyName() ) ) {
logger.error(
"Attempted to set sort order for " + s.getPropertyName() + " more than once, discardng..." );
return this;
}
}
sortPredicates.add( sort );
return this;
}
public List<SortPredicate> getSortPredicates() {
return sortPredicates;
}
public boolean hasSortPredicates() {
return !sortPredicates.isEmpty();
}
public Query addEqualityFilter( String propertyName, Object value ) {
return addFilter( propertyName, FilterOperator.EQUAL, value );
}
public Query addFilter( String propertyName, FilterOperator operator, Object value ) {
if ( ( propertyName == null ) || ( operator == null ) || ( value == null ) ) {
return this;
}
if ( PROPERTY_TYPE.equalsIgnoreCase( propertyName ) && ( value != null ) ) {
if ( operator == FilterOperator.EQUAL ) {
type = value.toString();
}
}
else if ( "connection".equalsIgnoreCase( propertyName ) && ( value != null ) ) {
if ( operator == FilterOperator.EQUAL ) {
connection = value.toString();
}
}
else {
for ( FilterPredicate f : filterPredicates ) {
if ( f.getPropertyName().equals( propertyName ) && f.getValue().equals( value ) && "*"
.equals( value ) ) {
logger.error( "Attempted to set wildcard wilder for " + f.getPropertyName()
+ " more than once, discardng..." );
return this;
}
}
filterPredicates.add( FilterPredicate.normalize( new FilterPredicate( propertyName, operator, value ) ) );
}
return this;
}
public Query addFilter( String filterStr ) {
if ( filterStr == null ) {
return this;
}
FilterPredicate filter = FilterPredicate.valueOf( filterStr );
if ( ( filter != null ) && ( filter.propertyName != null ) && ( filter.operator != null ) && ( filter.value
!= null ) ) {
if ( PROPERTY_TYPE.equalsIgnoreCase( filter.propertyName ) ) {
if ( filter.operator == FilterOperator.EQUAL ) {
type = filter.value.toString();
}
}
else if ( "connection".equalsIgnoreCase( filter.propertyName ) ) {
if ( filter.operator == FilterOperator.EQUAL ) {
connection = filter.value.toString();
}
}
else {
for ( FilterPredicate f : filterPredicates ) {
if ( f.getPropertyName().equals( filter.getPropertyName() ) && f.getValue()
.equals( filter.getValue() ) && "*"
.equals( filter.getValue() ) ) {
logger.error( "Attempted to set wildcard wilder for " + f.getPropertyName()
+ " more than once, discardng..." );
return this;
}
}
filterPredicates.add( filter );
}
}
else {
logger.error( "Unable to add filter to query: " + filterStr );
}
return this;
}
public Query addFilter( FilterPredicate filter ) {
filter = FilterPredicate.normalize( filter );
if ( ( filter != null ) && ( filter.propertyName != null ) && ( filter.operator != null ) && ( filter.value
!= null ) ) {
if ( PROPERTY_TYPE.equalsIgnoreCase( filter.propertyName ) ) {
if ( filter.operator == FilterOperator.EQUAL ) {
type = filter.value.toString();
}
}
else if ( "connection".equalsIgnoreCase( filter.propertyName ) ) {
if ( filter.operator == FilterOperator.EQUAL ) {
connection = filter.value.toString();
}
}
else {
filterPredicates.add( filter );
}
}
return this;
}
public List<FilterPredicate> getFilterPredicates() {
return filterPredicates;
}
public boolean hasFilterPredicates() {
return !filterPredicates.isEmpty();
}
public Map<String, Object> getEqualityFilters() {
Map<String, Object> map = new LinkedHashMap<String, Object>();
for ( FilterPredicate f : filterPredicates ) {
if ( f.operator == FilterOperator.EQUAL ) {
Object val = f.getStartValue();
if ( val != null ) {
map.put( f.getPropertyName(), val );
}
}
}
return map.size() > 0 ? map : null;
}
public boolean hasFiltersForProperty( String name ) {
return hasFiltersForProperty( FilterOperator.EQUAL, name );
}
public boolean hasFiltersForProperty( FilterOperator operator, String name ) {
return getFilterForProperty( operator, name ) != null;
}
public FilterPredicate getFilterForProperty( FilterOperator operator, String name ) {
if ( name == null ) {
return null;
}
ListIterator<FilterPredicate> iterator = filterPredicates.listIterator();
while ( iterator.hasNext() ) {
FilterPredicate f = iterator.next();
if ( f.propertyName.equalsIgnoreCase( name ) ) {
if ( operator != null ) {
if ( operator == f.operator ) {
return f;
}
}
else {
return f;
}
}
}
return null;
}
public void removeFiltersForProperty( String name ) {
if ( name == null ) {
return;
}
ListIterator<FilterPredicate> iterator = filterPredicates.listIterator();
while ( iterator.hasNext() ) {
FilterPredicate f = iterator.next();
if ( f.propertyName.equalsIgnoreCase( name ) ) {
iterator.remove();
}
}
}
public void setStartResult( UUID startResult ) {
this.startResult = startResult;
}
public Query withStartResult( UUID startResult ) {
this.startResult = startResult;
return this;
}
public UUID getStartResult() {
if ( ( startResult == null ) && ( cursor != null ) ) {
byte[] cursorBytes = decodeBase64( cursor );
if ( ( cursorBytes != null ) && ( cursorBytes.length == 16 ) ) {
startResult = uuid( cursorBytes );
}
}
return startResult;
}
public String getCursor() {
return cursor;
}
public void setCursor( String cursor ) {
if ( cursor != null ) {
if ( cursor.length() == 22 ) {
byte[] cursorBytes = decodeBase64( cursor );
if ( ( cursorBytes != null ) && ( cursorBytes.length == 16 ) ) {
startResult = uuid( cursorBytes );
cursor = null;
}
}
}
this.cursor = cursor;
}
public Query withCursor( String cursor ) {
setCursor( cursor );
return this;
}
public int getLimit() {
return getLimit( DEFAULT_LIMIT );
}
public int getLimit( int defaultLimit ) {
if ( limit <= 0 ) {
if ( defaultLimit > 0 ) {
return defaultLimit;
}
else {
return DEFAULT_LIMIT;
}
}
return limit;
}
public void setLimit( int limit ) {
limitSet = true;
this.limit = limit;
}
public Query withLimit( int limit ) {
limitSet = true;
this.limit = limit;
return this;
}
public boolean isLimitSet() {
return limitSet;
}
public boolean isReversed() {
return reversed;
}
public void setReversed( boolean reversed ) {
reversedSet = true;
this.reversed = reversed;
}
public boolean isReversedSet() {
return reversedSet;
}
public Long getStartTime() {
return startTime;
}
public void setStartTime( Long startTime ) {
this.startTime = startTime;
}
public Long getFinishTime() {
return finishTime;
}
public void setFinishTime( Long finishTime ) {
this.finishTime = finishTime;
}
public boolean isPad() {
return pad;
}
public void setPad( boolean pad ) {
this.pad = pad;
}
public void setResolution( CounterResolution resolution ) {
this.resolution = resolution;
}
public CounterResolution getResolution() {
return resolution;
}
public List<Identifier> getUsers() {
return users;
}
public void addUser( Identifier user ) {
if ( users == null ) {
users = new ArrayList<Identifier>();
}
users.add( user );
}
public void setUsers( List<Identifier> users ) {
this.users = users;
}
public List<Identifier> getGroups() {
return groups;
}
public void addGroup( Identifier group ) {
if ( groups == null ) {
groups = new ArrayList<Identifier>();
}
groups.add( group );
}
public void setGroups( List<Identifier> groups ) {
this.groups = groups;
}
public List<Identifier> getIdentifiers() {
return identifiers;
}
public void addIdentifier( Identifier identifier ) {
if ( identifiers == null ) {
identifiers = new ArrayList<Identifier>();
}
identifiers.add( identifier );
}
public void setIdentifiers( List<Identifier> identifiers ) {
this.identifiers = identifiers;
}
public boolean containsUuidIdentifersOnly() {
if ( hasFilterPredicates() ) {
return false;
}
if ( ( identifiers == null ) || identifiers.isEmpty() ) {
return false;
}
for ( Identifier identifier : identifiers ) {
if ( !identifier.isUUID() ) {
return false;
}
}
return true;
}
public boolean containsSingleUuidIdentifier() {
return containsUuidIdentifersOnly() && ( identifiers.size() == 1 );
}
public List<UUID> getUuidIdentifiers() {
if ( ( identifiers == null ) || identifiers.isEmpty() ) {
return null;
}
List<UUID> ids = new ArrayList<UUID>();
for ( Identifier identifier : identifiers ) {
if ( identifier.isUUID() ) {
ids.add( identifier.getUUID() );
}
}
return ids;
}
public UUID getSingleUuidIdentifier() {
if ( !containsSingleUuidIdentifier() ) {
return null;
}
return ( identifiers.get( 0 ).getUUID() );
}
public boolean containsNameIdentifiersOnly() {
if ( hasFilterPredicates() ) {
return false;
}
if ( ( identifiers == null ) || identifiers.isEmpty() ) {
return false;
}
for ( Identifier identifier : identifiers ) {
if ( !identifier.isName() ) {
return false;
}
}
return true;
}
public boolean containsSingleNameIdentifier() {
return containsNameIdentifiersOnly() && ( identifiers.size() == 1 );
}
public List<String> getNameIdentifiers() {
if ( ( identifiers == null ) || identifiers.isEmpty() ) {
return null;
}
List<String> names = new ArrayList<String>();
for ( Identifier identifier : identifiers ) {
if ( identifier.isName() ) {
names.add( identifier.getName() );
}
}
return names;
}
public String getSingleNameIdentifier() {
if ( !containsSingleNameIdentifier() ) {
return null;
}
return ( identifiers.get( 0 ).toString() );
}
public boolean containsEmailIdentifiersOnly() {
if ( hasFilterPredicates() ) {
return false;
}
if ( ( identifiers == null ) || identifiers.isEmpty() ) {
return false;
}
for ( Identifier identifier : identifiers ) {
if ( identifier.isEmail() ) {
return false;
}
}
return true;
}
public boolean containsSingleEmailIdentifier() {
return containsEmailIdentifiersOnly() && ( identifiers.size() == 1 );
}
public List<String> getEmailIdentifiers() {
if ( ( identifiers == null ) || identifiers.isEmpty() ) {
return null;
}
List<String> emails = new ArrayList<String>();
for ( Identifier identifier : identifiers ) {
if ( identifier.isEmail() ) {
emails.add( identifier.getEmail() );
}
}
return emails;
}
public String getSingleEmailIdentifier() {
if ( !containsSingleEmailIdentifier() ) {
return null;
}
return ( identifiers.get( 0 ).toString() );
}
public boolean containsNameOrEmailIdentifiersOnly() {
if ( hasFilterPredicates() ) {
return false;
}
if ( ( identifiers == null ) || identifiers.isEmpty() ) {
return false;
}
for ( Identifier identifier : identifiers ) {
if ( !identifier.isEmail() && !identifier.isName() ) {
return false;
}
}
return true;
}
public boolean containsSingleNameOrEmailIdentifier() {
return containsNameOrEmailIdentifiersOnly() && ( identifiers.size() == 1 );
}
public List<String> getNameAndEmailIdentifiers() {
if ( ( identifiers == null ) || identifiers.isEmpty() ) {
return null;
}
List<String> ids = new ArrayList<String>();
for ( Identifier identifier : identifiers ) {
if ( identifier.isEmail() ) {
ids.add( identifier.getEmail() );
}
else if ( identifier.isName() ) {
ids.add( identifier.getName() );
}
}
return ids;
}
public String getSingleNameOrEmailIdentifier() {
if ( !containsSingleNameOrEmailIdentifier() ) {
return null;
}
return ( identifiers.get( 0 ).toString() );
}
public List<String> getCategories() {
return categories;
}
public void addCategory( String category ) {
if ( categories == null ) {
categories = new ArrayList<String>();
}
categories.add( category );
}
public void setCategories( List<String> categories ) {
this.categories = categories;
}
public List<CounterFilterPredicate> getCounterFilters() {
return counterFilters;
}
public void addCounterFilter( String counter ) {
CounterFilterPredicate p = CounterFilterPredicate.fromString( counter );
if ( p == null ) {
return;
}
if ( counterFilters == null ) {
counterFilters = new ArrayList<CounterFilterPredicate>();
}
counterFilters.add( p );
}
public void setCounterFilters( List<CounterFilterPredicate> counterFilters ) {
this.counterFilters = counterFilters;
}
@Override
public String toString() {
if ( selectSubjects.isEmpty() && filterPredicates.isEmpty() ) {
return "";
}
StringBuilder s = new StringBuilder( "select " );
if ( type == null ) {
if ( selectSubjects.isEmpty() ) {
s.append( "*" );
}
else {
if ( mergeSelectResults ) {
s.append( "{ " );
boolean first = true;
for ( Map.Entry<String, String> select : selectSubjects.entrySet() ) {
if ( !first ) {
s.append( ", " );
}
s.append( select.getValue() + " : " + select.getKey() );
first = false;
}
s.append( " }" );
}
else {
boolean first = true;
for ( String select : selectSubjects.keySet() ) {
if ( !first ) {
s.append( ", " );
}
s.append( select );
first = false;
}
}
}
}
else {
s.append( type );
}
if ( !filterPredicates.isEmpty() ) {
s.append( " where " );
boolean first = true;
for ( FilterPredicate f : filterPredicates ) {
if ( !first ) {
s.append( " and " );
}
s.append( f.toString() );
first = false;
}
}
return s.toString();
}
public static enum FilterOperator {
LESS_THAN( "<", "lt" ), LESS_THAN_OR_EQUAL( "<=", "lte" ), GREATER_THAN( ">", "gt" ),
GREATER_THAN_OR_EQUAL( ">=", "gte" ), EQUAL( "=", "eq" ), NOT_EQUAL( "!=", "ne" ), IN( "in", null ),
CONTAINS( "contains", null ), WITHIN( "within", null );
private final String shortName;
private final String textName;
FilterOperator( String shortName, String textName ) {
this.shortName = shortName;
this.textName = textName;
}
static Map<String, FilterOperator> nameMap = new ConcurrentHashMap<String, FilterOperator>();
static {
for ( FilterOperator op : EnumSet.allOf( FilterOperator.class ) ) {
if ( op.shortName != null ) {
nameMap.put( op.shortName, op );
}
if ( op.textName != null ) {
nameMap.put( op.textName, op );
}
}
}
public static FilterOperator find( String s ) {
if ( s == null ) {
return null;
}
return nameMap.get( s );
}
@Override
public String toString() {
return shortName;
}
}
public static enum SortDirection {
ASCENDING, DESCENDING;
public static SortDirection find( String s ) {
if ( s == null ) {
return ASCENDING;
}
s = s.toLowerCase();
if ( s.startsWith( "asc" ) ) {
return ASCENDING;
}
if ( s.startsWith( "des" ) ) {
return DESCENDING;
}
if ( s.equals( "+" ) ) {
return ASCENDING;
}
if ( s.equals( "-" ) ) {
return DESCENDING;
}
return ASCENDING;
}
}
public static final class SortPredicate implements Serializable {
private static final long serialVersionUID = 1L;
private final String propertyName;
private final Query.SortDirection direction;
public SortPredicate( String propertyName, Query.SortDirection direction ) {
if ( propertyName == null ) {
throw new NullPointerException( "Property name was null" );
}
if ( direction == null ) {
direction = SortDirection.ASCENDING;
}
this.propertyName = propertyName.trim();
this.direction = direction;
}
public SortPredicate( String propertyName, String direction ) {
this( propertyName, SortDirection.find( direction ) );
}
public String getPropertyName() {
return propertyName;
}
public Query.SortDirection getDirection() {
return direction;
}
public FilterPredicate toFilter() {
return new FilterPredicate( propertyName, FilterOperator.EQUAL, "*" );
}
@Override
public boolean equals( Object o ) {
if ( this == o ) {
return true;
}
if ( ( o == null ) || ( super.getClass() != o.getClass() ) ) {
return false;
}
SortPredicate that = ( SortPredicate ) o;
if ( direction != that.direction ) {
return false;
}
return ( propertyName.equals( that.propertyName ) );
}
@Override
public int hashCode() {
int result = propertyName.hashCode();
result = ( 31 * result ) + direction.hashCode();
return result;
}
@Override
public String toString() {
return propertyName + ( ( direction == Query.SortDirection.DESCENDING ) ? " DESC" : "" );
}
}
public static final class FilterPredicate implements Serializable {
private static final long serialVersionUID = 1L;
private final String propertyName;
private final Query.FilterOperator operator;
private final Object value;
private String cursor;
@SuppressWarnings({ "rawtypes", "unchecked" })
public FilterPredicate( String propertyName, Query.FilterOperator operator, Object value ) {
Assert.notNull(propertyName, "Property name was null");
Assert.notNull(operator, "Operator was null");
if ( ( operator == Query.FilterOperator.IN ) || ( operator == Query.FilterOperator.WITHIN ) ) {
if ( ( !( value instanceof Collection ) ) && ( value instanceof Iterable ) ) {
List newValue = new ArrayList();
for (Object val : ((Iterable) value)) {
newValue.add(val);
}
value = newValue;
}
// DataTypeUtils.checkSupportedValue(propertyName, value, true,
// true);
}
else {
// DataTypeUtils.checkSupportedValue(propertyName, value, false,
// false);
}
this.propertyName = propertyName;
this.operator = operator;
this.value = value;
}
public FilterPredicate( String propertyName, String operator, String value, String secondValue,
String thirdValue ) {
this.propertyName = propertyName;
this.operator = FilterOperator.find( operator );
Object first_obj = parseValue( value, 0 );
Object second_obj = parseValue( secondValue, 0 );
Object third_obj = parseValue( thirdValue, 0 );
if ( second_obj != null ) {
if ( third_obj != null ) {
this.value = Arrays.asList( first_obj, second_obj, third_obj );
}
else {
this.value = Arrays.asList( first_obj, second_obj );
}
}
else {
this.value = first_obj;
}
}
public FilterPredicate( String propertyName, String operator, String value, int valueType, String secondValue,
int secondValueType, String thirdValue, int thirdValueType ) {
this.propertyName = propertyName;
this.operator = FilterOperator.find( operator );
Object first_obj = parseValue( value, valueType );
Object second_obj = parseValue( secondValue, secondValueType );
Object third_obj = parseValue( thirdValue, thirdValueType );
if ( second_obj != null ) {
if ( third_obj != null ) {
this.value = Arrays.asList( first_obj, second_obj, third_obj );
}
else {
this.value = Arrays.asList( first_obj, second_obj );
}
}
else {
this.value = first_obj;
}
}
private static Object parseValue( String val, int valueType ) {
if ( val == null ) {
return null;
}
if ( val.startsWith( "'" ) && ( val.length() > 1 ) ) {
return val.substring( 1, val.length() - 1 );
}
if ( val.equalsIgnoreCase( "true" ) || val.equalsIgnoreCase( "false" ) ) {
return Boolean.valueOf( val );
}
if ( val.length() == 36 ) {
try {
return UUID.fromString( val );
}
catch ( IllegalArgumentException e ) {
}
}
try {
return Long.valueOf( val );
}
catch ( NumberFormatException e ) {
}
try {
return Float.valueOf( val );
}
catch ( NumberFormatException e ) {
}
return null;
}
public static FilterPredicate valueOf( String str ) {
if ( str == null ) {
return null;
}
try {
ANTLRStringStream in = new ANTLRStringStream( str.trim() );
QueryFilterLexer lexer = new QueryFilterLexer( in );
CommonTokenStream tokens = new CommonTokenStream( lexer );
QueryFilterParser parser = new QueryFilterParser( tokens );
FilterPredicate filter = parser.filter();
return normalize( filter );
}
catch ( Exception e ) {
logger.error( "Unable to parse \"" + str + "\"", e );
}
return null;
}
public static FilterPredicate normalize( FilterPredicate p ) {
if ( p == null ) {
return null;
}
if ( p.operator == FilterOperator.CONTAINS ) {
String propertyName = appendSuffix( p.propertyName, "keywords" );
return new FilterPredicate( propertyName, FilterOperator.EQUAL, p.value );
}
else if ( p.operator == FilterOperator.WITHIN ) {
String propertyName = appendSuffix( p.propertyName, "coordinates" );
return new FilterPredicate( propertyName, FilterOperator.WITHIN, p.value );
}
return p;
}
private static String appendSuffix( String str, String suffix ) {
if ( StringUtils.isNotEmpty( str ) ) {
if ( !str.endsWith( "." + suffix ) ) {
str += "." + suffix;
}
}
else {
str = suffix;
}
return str;
}
public String getPropertyName() {
return propertyName;
}
public Query.FilterOperator getOperator() {
return operator;
}
public Object getValue() {
return value;
}
@SuppressWarnings("unchecked")
public Object getStartValue() {
if ( value instanceof List ) {
List<Object> l = ( List<Object> ) value;
return l.get( 0 );
}
if ( ( operator == FilterOperator.GREATER_THAN ) || ( operator == FilterOperator.GREATER_THAN_OR_EQUAL )
|| ( operator == FilterOperator.EQUAL ) ) {
return value;
}
else {
return null;
}
}
@SuppressWarnings("unchecked")
public Object getFinishValue() {
if ( value instanceof List ) {
List<Object> l = ( List<Object> ) value;
if ( l.size() > 1 ) {
return l.get( 1 );
}
return null;
}
if ( ( operator == FilterOperator.LESS_THAN ) || ( operator == FilterOperator.LESS_THAN_OR_EQUAL ) || (
operator == FilterOperator.EQUAL ) ) {
return value;
}
else {
return null;
}
}
public void setCursor( String cursor ) {
this.cursor = cursor;
}
public String getCursor() {
return cursor;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = ( prime * result ) + ( ( operator == null ) ? 0 : operator.hashCode() );
result = ( prime * result ) + ( ( propertyName == null ) ? 0 : propertyName.hashCode() );
result = ( prime * result ) + ( ( value == null ) ? 0 : value.hashCode() );
return result;
}
@Override
public boolean equals( Object obj ) {
if ( this == obj ) {
return true;
}
if ( obj == null ) {
return false;
}
if ( getClass() != obj.getClass() ) {
return false;
}
FilterPredicate other = ( FilterPredicate ) obj;
if ( operator != other.operator ) {
return false;
}
if ( propertyName == null ) {
if ( other.propertyName != null ) {
return false;
}
}
else if ( !propertyName.equals( other.propertyName ) ) {
return false;
}
if ( value == null ) {
if ( other.value != null ) {
return false;
}
}
else if ( !value.equals( other.value ) ) {
return false;
}
return true;
}
@Override
public String toString() {
String valueStr = "\'\'";
if ( value != null ) {
if ( value instanceof String ) {
valueStr = "\'" + value + "\'";
}
else {
valueStr = value.toString();
}
}
return propertyName + " " + operator.toString() + " " + valueStr;
}
}
public static final class CounterFilterPredicate implements Serializable {
private static final long serialVersionUID = 1L;
private final String name;
private final Identifier user;
private final Identifier group;
private final String queue;
private final String category;
public CounterFilterPredicate( String name, Identifier user, Identifier group, String queue, String category ) {
this.name = name;
this.user = user;
this.group = group;
this.queue = queue;
this.category = category;
}
public Identifier getUser() {
return user;
}
public Identifier getGroup() {
return group;
}
public String getQueue() {
return queue;
}
public String getCategory() {
return category;
}
public String getName() {
return name;
}
public static CounterFilterPredicate fromString( String s ) {
Identifier user = null;
Identifier group = null;
String category = null;
String name = null;
String[] l = split( s, ':' );
if ( l.length > 0 ) {
if ( !"*".equals( l[0] ) ) {
name = l[0];
}
}
if ( l.length > 1 ) {
if ( !"*".equals( l[1] ) ) {
user = Identifier.from( l[1] );
}
}
if ( l.length > 2 ) {
if ( !"*".equals( l[2] ) ) {
group = Identifier.from( l[3] );
}
}
if ( l.length > 3 ) {
if ( !"*".equals( l[3] ) ) {
category = l[3];
}
}
if ( ( user == null ) && ( group == null ) && ( category == null ) && ( name == null ) ) {
return null;
}
return new CounterFilterPredicate( name, user, group, null, category );
}
public static List<CounterFilterPredicate> fromList( List<String> l ) {
if ( ( l == null ) || ( l.size() == 0 ) ) {
return null;
}
List<CounterFilterPredicate> counterFilters = new ArrayList<CounterFilterPredicate>();
for ( String s : l ) {
CounterFilterPredicate filter = CounterFilterPredicate.fromString( s );
if ( filter != null ) {
counterFilters.add( filter );
}
}
if ( counterFilters.size() == 0 ) {
return null;
}
return counterFilters;
}
}
public List<Object> getSelectionResults( Results rs ) {
List<Entity> entities = rs.getEntities();
if ( entities == null ) {
return null;
}
if ( !hasSelectSubjects() ) {
return cast( entities );
}
List<Object> results = new ArrayList<Object>();
for ( Entity entity : entities ) {
if ( isMergeSelectResults() ) {
boolean include = false;
Map<String, Object> result = new LinkedHashMap<String, Object>();
Map<String, String> selects = getSelectAssignments();
for ( Map.Entry<String, String> select : selects.entrySet() ) {
Object obj = JsonUtils.select( entity, select.getKey(), false );
if ( obj == null ) {
obj = "";
}
else {
include = true;
}
result.put( select.getValue(), obj );
}
if ( include ) {
results.add( result );
}
}
else {
boolean include = false;
List<Object> result = new ArrayList<Object>();
Set<String> selects = getSelectSubjects();
for ( String select : selects ) {
Object obj = JsonUtils.select( entity, select );
if ( obj == null ) {
obj = "";
}
else {
include = true;
}
result.add( obj );
}
if ( include ) {
results.add( result );
}
}
}
if ( results.size() == 0 ) {
return null;
}
return results;
}
public Object getSelectionResult( Results rs ) {
List<Object> r = getSelectionResults( rs );
if ( ( r != null ) && ( r.size() > 0 ) ) {
return r.get( 0 );
}
return null;
}
}