/* * Copyright 2015 Red Hat, Inc. and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * * 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.jbpm.query.jpa.data; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlType; import org.jbpm.query.jpa.data.QueryWhere.QueryCriteriaType; import org.kie.internal.query.QueryParameterIdentifiers; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.annotation.JsonTypeInfo.As; import com.fasterxml.jackson.annotation.JsonTypeInfo.Id; /** * This object contains the following information: * <ol> * <li>The listId, which refers to the field that this criteria applies to<ul> * <li>See {@link QueryParameterIdentifiers}</li></ul> * </li> * <li>The values of the criteria, which will be applied to the listId field<ul> * <li>For example, it could be a list of numbers "1, 22, 3"</li></ul> * </li> * <li>Whether this is a union ("OR") or intersection ("AND") critieria</li> * <li>The type of criteria: normal, like (JPQL regex) or range</li> * <li>The grouping information of the phrase (see below)</li> * </ol> * </p> * With regard to the grouping information in this class, we treat JPQL/SQL as a "prefix" language here, which means that * this class represents the following regular expression/BNF string: * <pre> * [)]{0,} [OR|AND] [(]{0,} <CRITERIA> * </pre> * This structure is then represented by the following fields: * <pre> * [endGroups] [union] [startGroupos] [values] * </pre> * * The main reason to include the grouping status in this object is that other data structures (nested lists, etc) * are much harder to de/serialize correctly. */ @XmlRootElement @XmlType @XmlAccessorType(XmlAccessType.FIELD) @JsonIgnoreProperties(value="parameters") public class QueryCriteria { @XmlAttribute private String listId; @XmlAttribute private boolean union = true; @XmlAttribute private boolean first = false; @XmlAttribute private QueryCriteriaType type = QueryCriteriaType.NORMAL; @XmlElement(name="parameter") @JsonTypeInfo(use=Id.CLASS, include=As.PROPERTY, property="class") private List<Object> values; @XmlElement(name="date-parameter") private List<Date> dateValues; @XmlElement private List<QueryCriteria> criteria; public QueryCriteria() { // default (JAXB/JSON) constructor } /** * Used when creating a group criteria * @param union Whether or not the group is part of an intersection or disjunction */ public QueryCriteria(boolean union) { this.union = union; this.type = QueryCriteriaType.GROUP; } private QueryCriteria(String listId, QueryCriteriaType type) { this.listId = listId; this.type = type; } /** * Used for all other criteria * @param listId The {@link QueryParameterIdentifiers} list id * @param union Whether or not the criteria is part of an intersection or disjunction * @param type The type: {@link QueryCriteriaType#NORMAL}, {@link QueryCriteriaType#REGEXP}, or {@link QueryCriteriaType#RANGE}, * @param valueListSize The size of the value list */ public QueryCriteria(String listId, boolean union, QueryCriteriaType type, int valueListSize) { this(listId, type); this.union = union; this.values = new ArrayList<Object>(valueListSize); } public String getListId() { return listId; } public void setListId( String listId ) { this.listId = listId; } public boolean isUnion() { return union; } public void setUnion( boolean union ) { this.union = union; } public boolean isFirst() { return first; } public void setFirst( boolean first ) { this.first = first; } public QueryCriteriaType getType() { return type; } public void setType( QueryCriteriaType type ) { this.type = type; } public List<Object> getValues() { if( this.values == null ) { this.values = new ArrayList<Object>(); } return values; } public void setValues( List<Object> values ) { this.values = values; } public List<Date> getDateValues() { if( this.dateValues == null ) { this.dateValues = new ArrayList<Date>(); } return dateValues; } public void setDateValues( List<Date> dateValues ) { this.dateValues = dateValues; } // other methods @JsonIgnore public boolean isGroupCriteria() { return this.type.equals(QueryCriteriaType.GROUP); } @JsonIgnore public boolean hasValues() { return ( this.values != null && ! this.values.isEmpty() ); } @JsonIgnore public boolean hasDateValues() { return ( this.dateValues != null && ! this.dateValues.isEmpty() ); } @JsonIgnore public boolean hasCriteria() { return ( this.criteria != null && ! this.criteria.isEmpty() ); } public List<QueryCriteria> getCriteria() { if( this.criteria == null ) { this.criteria = new ArrayList<QueryCriteria>(); } return criteria; } public void setCriteria( List<QueryCriteria> criteria ) { this.criteria = criteria; } /** * This method returns a list that should only be read * @return */ public List<Object> getParameters() { List<Object> parameters = new ArrayList<Object>(getValues()); if( this.dateValues != null && ! this.dateValues.isEmpty() ) { parameters.addAll(this.dateValues); } if( parameters.isEmpty() ) { return parameters; } return parameters; } void addParameter( Object value ) { if( value instanceof Date ) { getDateValues().add((Date) value); } else { getValues().add(value); } } @SuppressWarnings("unchecked") void setParameter( int index, Object value, int listSize ) { List addValues; if( value instanceof Date ) { addValues = getDateValues(); } else { addValues = getValues(); } while( addValues.size() <= index ) { addValues.add(null); } addValues.set(index, value); // throws NPE for (index > 1) if (list < index) while( addValues.size() < listSize ) { addValues.add(null); } } public void addCriteria( QueryCriteria criteria ) { getCriteria().add(criteria); } public QueryCriteria(QueryCriteria queryCriteria) { this.listId = queryCriteria.listId; this.union = queryCriteria.union; this.first = queryCriteria.first; this.type = queryCriteria.type; if( queryCriteria.values != null ) { this.values = new ArrayList<Object>(queryCriteria.values); } if( queryCriteria.dateValues != null ) { this.dateValues = new ArrayList<Date>(queryCriteria.dateValues); } if( queryCriteria.criteria != null ) { this.criteria = new ArrayList<QueryCriteria>(queryCriteria.criteria); } } private static SimpleDateFormat toStringSdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); @Override public String toString() { StringBuilder out = new StringBuilder(); if( ! first ) { out.append(union ? "OR" : "AND").append(" "); } if( listId != null ) { out.append(listId); } if( this.values != null && ! this.values.isEmpty() ) { out.append(" ="); if( type.equals(QueryCriteriaType.REGEXP) ) { out.append("~"); } out.append(" "); if ( type.equals(QueryCriteriaType.RANGE) ) { out.append("["); } out.append(this.values.get(0)); for( int i = 1; i < this.values.size(); ++i ) { out.append(", ") .append(this.values.get(i)); } if ( type.equals(QueryCriteriaType.RANGE) ) { out.append("]"); } } else if( this.dateValues != null && ! this.dateValues.isEmpty() ) { out.append(" ="); if( type.equals(QueryCriteriaType.REGEXP) ) { out.append("~"); } out.append(" "); if ( type.equals(QueryCriteriaType.RANGE) ) { out.append("["); } Date date = this.dateValues.get(0); String dateStr = date != null ? toStringSdf.format(date) : "null"; out.append(dateStr); for( int i = 1; i < this.dateValues.size(); ++i ) { date = this.dateValues.get(i); dateStr = date != null ? toStringSdf.format(date) : "null"; out.append(", ") .append(dateStr); } if ( type.equals(QueryCriteriaType.RANGE) ) { out.append("]"); } } if( criteria != null ) { if( out.length() > 0 ) { out.append(" "); } out.append("("); int size = criteria.size(); if( size > 0 ) { out.append(criteria.get(0).toString()); } for( int i = 1; i < size; ++i ) { out.append(", "); out.append(criteria.get(i).toString()); } out.append(")"); } return out.toString(); } }