package siena; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import siena.core.QueryFilterEmbedded; import siena.core.options.QueryOption; import siena.core.options.QueryOptionFetchType; import siena.core.options.QueryOptionOffset; import siena.core.options.QueryOptionPage; import siena.core.options.QueryOptionState; import siena.embed.EmbeddedMap; /** * The base data container of Query<T>/QueryAsync<T> where T is the model being queried (not necessarily inheriting siena.Model) * * @author mandubian <pascal.voitot@mandubian.org> * * @param <T> */ @EmbeddedMap public class BaseQueryData<T> implements QueryData<T> { private static final long serialVersionUID = -5112648712321740542L; protected Class<T> clazz; protected List<QueryFilter> filters; protected List<QueryOrder> orders; protected List<QueryFilterSearch> searches; protected List<QueryJoin> joins; protected List<QueryAggregated> aggregatees; protected List<QueryOwned> ownees; protected Map<Integer, QueryOption> options = defaultOptions(); public static Map<Integer, QueryOption> defaultOptions() { return new HashMap<Integer, QueryOption>() { private static final long serialVersionUID = -7438657296637379900L; { put(QueryOptionPage.ID, new QueryOptionPage(0)); put(QueryOptionOffset.ID, new QueryOptionOffset(0)); put(QueryOptionState.ID, new QueryOptionState()); //the fetch type is activated by default and set to NORMAL put(QueryOptionFetchType.ID, (new QueryOptionFetchType()).activate()); }}; } public BaseQueryData() { filters = new ArrayList<QueryFilter>(); orders = new ArrayList<QueryOrder>(); searches = new ArrayList<QueryFilterSearch>(); joins = new ArrayList<QueryJoin>(); aggregatees = new ArrayList<QueryAggregated>(); ownees = new ArrayList<QueryOwned>(); } public BaseQueryData(Class<T> clazz) { this.clazz = clazz; filters = new ArrayList<QueryFilter>(); orders = new ArrayList<QueryOrder>(); searches = new ArrayList<QueryFilterSearch>(); joins = new ArrayList<QueryJoin>(); aggregatees = new ArrayList<QueryAggregated>(); ownees = new ArrayList<QueryOwned>(); } public BaseQueryData(BaseQueryData<T> data) { this.clazz = data.clazz; /* NO COPY TO KEEP DATA AND STATEFUL QUERIES this.filters = new ArrayList<QueryFilter>(); this.orders = new ArrayList<QueryOrder>(); this.searches = new ArrayList<QueryFilterSearch>(); this.joins = new ArrayList<QueryJoin>(); Collections.copy(this.filters, data.filters); Collections.copy(this.orders, data.orders); Collections.copy(this.searches, data.searches); Collections.copy(this.joins, data.joins); for(Integer key : data.options.keySet()){ this.options.put(key, data.options.get(key).clone()); }*/ this.filters = data.filters; this.orders = data.orders; this.searches = data.searches; this.joins = data.joins; this.aggregatees = data.aggregatees; this.ownees = data.ownees; for(Integer key : data.options.keySet()){ this.options.put(key, data.options.get(key)); } } public Class<T> getQueriedClass(){ return clazz; } public List<QueryFilter> getFilters() { return filters; } public List<QueryOrder> getOrders() { return orders; } public List<QueryFilterSearch> getSearches() { return searches; } public List<QueryJoin> getJoins() { return joins; } public List<QueryAggregated> getAggregatees() { return aggregatees; } public List<QueryOwned> getOwnees() { return ownees; } public QueryOption option(int option) { return options.get(option); } public Map<Integer, QueryOption> options() { return options; } /* * PROTECTED FUNCTIONS AVAILABLE FOR BASEQUERY */ protected void addFilter(String fieldName, Object value, String[] supportedOperators) { String op = "="; for (String s : supportedOperators) { if(fieldName.endsWith(s)) { op = s; fieldName = fieldName.substring(0, fieldName.length() - op.length());; break; } } fieldName = fieldName.trim(); // an embedded field can be a field containing "." or ":" if(fieldName.contains(".")){ String[] parts = fieldName.split("\\."); if(parts.length == 0) { throw new SienaException("Filter field cannot have 0 fields to filter"); } List<Field> fields = new ArrayList<Field>(); Class<?> cl = clazz; for(int i=0; i<parts.length; i++) { String fName = parts[i]; Field f = Util.getField(cl, fName); if(f==null) { throw new SienaException("Filter field '"+fName+"' not found"); } fields.add(f); cl = f.getType(); } filters.add(new QueryFilterEmbedded(fields, op, ".", value)); }else if(fieldName.contains(":")) { String[] parts = fieldName.split("\\."); if(parts.length == 0) { throw new SienaException("Filter field cannot have 0 fields to filter"); } List<Field> fields = new ArrayList<Field>(); Class<?> cl = clazz; for(int i=0; i<parts.length; i++) { String fName = parts[i]; Field f = Util.getField(cl, fName); if(f==null) { throw new SienaException("Filter field '"+fName+"' not found"); } fields.add(f); cl = f.getType(); } filters.add(new QueryFilterEmbedded(fields, op, ".", value)); }else { Field field = Util.getField(clazz, fieldName); if(field==null) { throw new SienaException("Filter field '"+fieldName+"' not found"); } filters.add(new QueryFilterSimple(field, op, value)); } } protected void addOrder(String fieldName) { boolean ascending = true; if(fieldName.startsWith("-")) { fieldName = fieldName.substring(1); ascending = false; } Field field = Util.getField(clazz, fieldName); if(field==null) { throw new SienaException("Order field '"+fieldName+"' not found"); } orders.add(new QueryOrder(field, ascending)); } protected void addSearch(String match, String... fields) { QueryFilterSearch q = new QueryFilterSearch(match, fields); filters.add(q); searches.add(q); } protected void addSearch(String match, QueryOption opt, String... fields) { QueryFilterSearch q = new QueryFilterSearch(match, opt, fields); filters.add(q); searches.add(q); } protected void addJoin(String fieldName, String... sortFields) { try { Field field = Util.getField(clazz, fieldName); joins.add(new QueryJoin(field, sortFields)); // add immediately orders to keep order of orders // sets joined field as parent field to manage order on the right joined table for ex for(String sortFieldName: sortFields){ boolean ascending = true; if(sortFieldName.startsWith("-")) { sortFieldName = sortFieldName.substring(1); ascending = false; } try { Field sortField = field.getType().getField(sortFieldName); orders.add(new QueryOrder(sortField, ascending, field)); } catch(NoSuchFieldException ex){ throw new SienaException("Join not possible: join sort field "+sortFieldName+" is not a known field of "+fieldName, ex); } } } catch(Exception e) { throw new SienaException(e); } } protected void addAggregated(Object aggregator, String fieldName){ Field field = Util.getField(aggregator.getClass(), fieldName); // removes existing aggregatee (not very nice I know :) ) if(!aggregatees.isEmpty()) aggregatees.remove(0); aggregatees.add(0, new QueryAggregated(aggregator, field)); } protected void addAggregated(Object aggregator, Field field){ if(!aggregatees.isEmpty()) aggregatees.remove(0); aggregatees.add(0, new QueryAggregated(aggregator, field)); } protected void addOwned(Object owner, String fieldName){ Field field = Util.getField(this.getQueriedClass(), fieldName); // removes existing ownee (not very nice I know :) ) if(!ownees.isEmpty()) ownees.remove(0); ownees.add(0, new QueryOwned(owner, field)); } protected void addOwned(Object owner, Field field){ // removes existing ownee (not very nice I know :) ) if(!ownees.isEmpty()) ownees.remove(0); ownees.add(0, new QueryOwned(owner, field)); } protected void optionPaginate(int pageSize) { // sets the pagination QueryOptionPage opt = (QueryOptionPage)(options.get(QueryOptionPage.ID)); QueryOptionOffset offOpt = (QueryOptionOffset)options.get(QueryOptionOffset.ID); //QueryOptionState stateOpt = (QueryOptionState)(options.get(QueryOptionState.ID)).activate(); // can't change pagination after it has been initialized because it breaks all the cursor mechanism /*if(opt.isActive() && opt.isPaginating()){ throw new SienaException("Can't change pagination after it has been initialized..."); }*/ opt.activate(); opt.pageSize=pageSize; opt.pageType = QueryOptionPage.PageType.PAGINATING; // resets offset to be sure nothing changes the pagination mechanism offOpt.offsetType = QueryOptionOffset.OffsetType.PAGINATING; //offOpt.offset = 0; /*if(stateOpt.isStateful()){ offOpt.passivate(); }else { offOpt.activate(); }*/ } protected void optionLimit(int limit) { // sets the pagination QueryOptionPage pagOpt = (QueryOptionPage)(options.get(QueryOptionPage.ID)); //QueryOptionOffset offOpt = (QueryOptionOffset)options.get(QueryOptionOffset.ID); //QueryOptionState stateOpt = (QueryOptionState)(options.get(QueryOptionState.ID)); pagOpt.activate(); pagOpt.pageSize = limit; pagOpt.pageType = QueryOptionPage.PageType.MANUAL; // in stateless mode, we must reset the offset as we don't want it to be stateful //if(stateOpt.isStateless() && !offOpt.isManual()){ // offOpt.offset = 0; //} } protected void optionOffset(int offset) { QueryOptionPage pagOpt = (QueryOptionPage)(options.get(QueryOptionPage.ID)); QueryOptionOffset offOpt = (QueryOptionOffset)options.get(QueryOptionOffset.ID); //QueryOptionState stateOpt = (QueryOptionState)(options.get(QueryOptionState.ID)); offOpt.activate(); offOpt.offsetType = QueryOptionOffset.OffsetType.MANUAL; offOpt.offset = offset; // deactivates the pagination in any case pagOpt.pageType = QueryOptionPage.PageType.MANUAL; //if(offset!=0){ // if stateful mode, adds the offset to current offset //if(stateOpt.isStateful()){ // offOpt.offset += offset; //} // if stateless mode, simply replaces the offset // else { // offOpt.offset = offset; //if(!pagOpt.isManual()){ // pagOpt.pageSize = 0; //} //} //} } protected void optionStateful() { QueryOptionState opt = (QueryOptionState)(options.get(QueryOptionState.ID)).activate(); opt.lifeCycle = QueryOptionState.LifeCycle.STATEFUL; } protected void optionStateless() { QueryOptionState opt = (QueryOptionState)(options.get(QueryOptionState.ID)).activate(); opt.lifeCycle = QueryOptionState.LifeCycle.STATELESS; } protected void addOptions(QueryOption... options) { for(QueryOption option: options){ this.options.put(option.type, option); } } protected void reset() { options.clear(); filters.clear(); orders.clear(); searches.clear(); joins.clear(); aggregatees.clear(); ownees.clear(); options = defaultOptions(); } public void resetOptions() { options = defaultOptions(); } }