KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > mmbase > storage > search > implementation > BasicSearchQuery


1 /*
2
3 This software is OSI Certified Open Source Software.
4 OSI Certified is a certification mark of the Open Source Initiative.
5
6 The license (Mozilla version 1.0) can be read at the MMBase site.
7 See http://www.MMBase.org/license
8
9 */

10 package org.mmbase.storage.search.implementation;
11
12 import java.util.*;
13 import org.mmbase.module.core.MMObjectBuilder;
14 import org.mmbase.module.core.MMBase;
15 import org.mmbase.module.corebuilders.*;
16 import org.mmbase.core.CoreField;
17 import org.mmbase.storage.search.*;
18 import org.mmbase.util.logging.*;
19
20 /**
21  * Basic implementation.
22  *
23  * @author Rob van Maris
24  * @version $Id: BasicSearchQuery.java,v 1.32 2006/07/25 20:49:56 michiel Exp $
25  * @since MMBase-1.7
26  */

27 public class BasicSearchQuery implements SearchQuery, Cloneable JavaDoc {
28     private static final Logger log = Logging.getLoggerInstance(BasicSearchQuery.class);
29
30     /** Distinct property. */
31     private boolean distinct = false;
32
33     /** MaxNumber property. */
34     private int maxNumber = SearchQuery.DEFAULT_MAX_NUMBER;
35
36     /** Offset property. */
37     private int offset = SearchQuery.DEFAULT_OFFSET;
38
39     /** Step list. */
40     private List steps = new ArrayList();
41
42     /** StepField list. */
43     protected List fields = new ArrayList();
44
45     /** SortOrder list. */
46     private List sortOrders = new ArrayList();
47
48     /** Constraint.. */
49     private Constraint constraint = null;
50
51     /** Aggragating property. */
52     private boolean aggregating = false;
53
54     /** Two variables to speed up hashCode() by caching the result */
55     private boolean hasChangedHashcode = true;
56     private int savedHashcode = -1;
57     
58     /**
59      * Constructor.
60      *
61      * @param aggregating True for an aggregating query, false otherwise.
62      */

63     public BasicSearchQuery(boolean aggregating) {
64         this.aggregating = aggregating;
65         hasChangedHashcode = true;
66     }
67
68     /**
69      * Constructor, constructs non-aggragating query.
70      */

71     public BasicSearchQuery() {
72         this(false);
73     }
74
75
76     public final static int COPY_NORMAL = 0;
77     public final static int COPY_AGGREGATING = 1;
78     public final static int COPY_WITHOUTFIELDS = 2;
79
80     /**
81      * A deep copy, but sets also aggregating, and clear fields if aggregating is true then.
82      */

83
84     public BasicSearchQuery(SearchQuery q, int copyMethod) {
85         distinct = q.isDistinct();
86         copySteps(q);
87         Constraint c = q.getConstraint();
88         if (c != null) {
89             setConstraint(copyConstraint(q, c));
90         }
91         switch(copyMethod) {
92         case COPY_NORMAL:
93             copyFields(q);
94         case COPY_WITHOUTFIELDS:
95             copySortOrders(q);
96             maxNumber = q.getMaxNumber();
97             offset = q.getOffset();
98             aggregating = false;
99             break;
100         case COPY_AGGREGATING:
101             aggregating = true;
102             break;
103         default:
104             log.debug("Unknown copy method " + copyMethod);
105             break;
106         }
107         hasChangedHashcode = true;
108     }
109
110
111     /**
112      * A deep copy. Needed if you want to do multiple queries (and change the query between them).
113      * Used by bridge.Query#clone (so it will be decided that that is not needed, ths can be removed too)
114      * @see org.mmbase.bridge.Query#clone
115      */

116     public BasicSearchQuery(SearchQuery q) {
117         this(q, COPY_NORMAL);
118     }
119
120
121     public Object JavaDoc clone() {
122         try {
123             BasicSearchQuery clone = (BasicSearchQuery) super.clone();
124             clone.copySteps(this);
125             clone.copyFields(this);
126             clone.copySortOrders(this);
127             Constraint c = getConstraint();
128             if (c != null) {
129                 clone.setConstraint(copyConstraint(this, c));
130             }
131             return clone;
132         } catch (CloneNotSupportedException JavaDoc e) {
133             // cannot happen
134
throw new InternalError JavaDoc(e.toString());
135         }
136     }
137
138
139     protected void copySteps(SearchQuery q) {
140         MMBase mmb = MMBase.getMMBase();
141         steps = new ArrayList();
142         Iterator i = q.getSteps().iterator();
143         while (i.hasNext()) {
144             Step step = (Step) i.next();
145             if (step instanceof RelationStep) {
146                 RelationStep relationStep = (RelationStep) step;
147                 MMObjectBuilder dest = mmb.getBuilder(relationStep.getNext().getTableName());
148                 InsRel insrel = (InsRel) mmb.getBuilder(relationStep.getTableName());
149                 BasicRelationStep newRelationStep = addRelationStep(insrel, dest);
150                 newRelationStep.setDirectionality(relationStep.getDirectionality());
151                 newRelationStep.setCheckedDirectionality(relationStep.getCheckedDirectionality());
152                 newRelationStep.setRole(relationStep.getRole());
153                 newRelationStep.setAlias(relationStep.getAlias());
154                 Iterator j = relationStep.getNodes().iterator();
155                 while (j.hasNext()) {
156                     newRelationStep.addNode(((Integer JavaDoc) j.next()).intValue());
157                 }
158                 BasicStep next = (BasicStep) relationStep.getNext();
159                 BasicStep newNext = (BasicStep) newRelationStep.getNext();
160                 newNext.setAlias(next.getAlias());
161                 j = next.getNodes().iterator();
162                 while (j.hasNext()) {
163                     newNext.addNode(((Integer JavaDoc) j.next()).intValue());
164                 }
165                 i.next(); // dealt with that already
166

167             } else {
168                 BasicStep newStep = addStep(mmb.getBuilder(step.getTableName()));
169                 newStep.setAlias(step.getAlias());
170                 Iterator j = step.getNodes().iterator();
171                 while (j.hasNext()) {
172                     newStep.addNode(((Integer JavaDoc) j.next()).intValue());
173                 }
174             }
175         }
176         //log.info("copied steps " + q.getSteps() + " became " + steps);
177
hasChangedHashcode = true;
178     }
179     protected void copyFields(SearchQuery q) {
180         fields = new ArrayList();
181         MMBase mmb = MMBase.getMMBase();
182         Iterator i = q.getFields().iterator();
183         while (i.hasNext()) {
184             StepField field = (StepField) i.next();
185             Step step = field.getStep();
186             MMObjectBuilder bul = mmb.getBuilder(step.getTableName());
187             int j = q.getSteps().indexOf(step);
188             if (j == -1) {
189                 throw new RuntimeException JavaDoc("Step " + step + " could not be found in " + q.getSteps());
190             }
191             Step newStep = (Step) steps.get(j);
192             BasicStepField newField = addField(newStep, bul.getField(field.getFieldName()));
193             newField.setAlias(field.getAlias());
194         }
195         hasChangedHashcode = true;
196         //log.info("copied fields " + q.getFields() + " became " + fields);
197
}
198     protected void copySortOrders(SearchQuery q) {
199         sortOrders = new ArrayList();
200         MMBase mmb = MMBase.getMMBase();
201         Iterator i = q.getSortOrders().iterator();
202         while (i.hasNext()) {
203             SortOrder sortOrder = (SortOrder) i.next();
204             StepField field = sortOrder.getField();
205             int j = q.getFields().indexOf(field);
206             StepField newField;
207             if (j == -1 || j >= fields.size()) { // not sorting on field of field list.
208
Step step = field.getStep();
209                 MMObjectBuilder bul = mmb.getBuilder(step.getTableName());
210                 newField = new BasicStepField(field.getStep(), bul.getField(field.getFieldName()));
211             } else {
212                 newField = (StepField) fields.get(j);
213             }
214             BasicSortOrder newSortOrder = addSortOrder(newField);
215             newSortOrder.setDirection(sortOrder.getDirection());
216         }
217         hasChangedHashcode = true;
218     }
219
220     /**
221      * Creates a new StepField like f for query q.
222      */

223     protected static StepField createNewStepField(SearchQuery q, StepField f) {
224         Step fstep = f.getStep();
225         // find existing step.
226
List steps = q.getSteps();
227         Step step = (Step) steps.get(steps.indexOf(fstep));
228         MMObjectBuilder bul = MMBase.getMMBase().getBuilder(step.getTableName());
229         return new BasicStepField(step, bul.getField(f.getFieldName()));
230     }
231
232
233     /**
234      * Used by copy-constructor. Constraints have to be done recursively.
235      */

236     protected static Constraint copyConstraint(SearchQuery q, Constraint c) {
237         if (c instanceof CompositeConstraint) {
238             CompositeConstraint constraint = (CompositeConstraint) c;
239             BasicCompositeConstraint newConstraint = new BasicCompositeConstraint(constraint.getLogicalOperator());
240             Iterator i = constraint.getChilds().iterator();
241             while (i.hasNext()) {
242                 Constraint cons = (Constraint) i.next();
243                 newConstraint.addChild(copyConstraint(q, cons));
244             }
245             newConstraint.setInverse(constraint.isInverse());
246             return newConstraint;
247         } else if (c instanceof CompareFieldsConstraint) {
248             CompareFieldsConstraint constraint = (CompareFieldsConstraint) c;
249             BasicCompareFieldsConstraint newConstraint = new BasicCompareFieldsConstraint(createNewStepField(q, constraint.getField()), createNewStepField(q, constraint.getField2()));
250             newConstraint.setOperator(constraint.getOperator());
251             newConstraint.setInverse(constraint.isInverse());
252             newConstraint.setCaseSensitive(constraint.isCaseSensitive());
253             return newConstraint;
254         } else if (c instanceof FieldValueDateConstraint) {
255             FieldValueDateConstraint constraint = (FieldValueDateConstraint) c;
256             Object JavaDoc value = constraint.getValue();
257             BasicFieldValueDateConstraint newConstraint = new BasicFieldValueDateConstraint(createNewStepField(q, constraint.getField()), value, constraint.getPart());
258             newConstraint.setOperator(constraint.getOperator());
259             newConstraint.setInverse(constraint.isInverse());
260             newConstraint.setCaseSensitive(constraint.isCaseSensitive());
261             return newConstraint;
262         } else if (c instanceof FieldValueConstraint) {
263             FieldValueConstraint constraint = (FieldValueConstraint) c;
264             Object JavaDoc value = constraint.getValue();
265             BasicFieldValueConstraint newConstraint = new BasicFieldValueConstraint(createNewStepField(q, constraint.getField()), value);
266             newConstraint.setOperator(constraint.getOperator());
267             newConstraint.setInverse(constraint.isInverse());
268             newConstraint.setCaseSensitive(constraint.isCaseSensitive());
269             return newConstraint;
270         } else if (c instanceof FieldNullConstraint) {
271             FieldNullConstraint constraint = (FieldNullConstraint) c;
272             BasicFieldNullConstraint newConstraint = new BasicFieldNullConstraint(createNewStepField(q, constraint.getField()));
273             newConstraint.setInverse(constraint.isInverse());
274             newConstraint.setCaseSensitive(constraint.isCaseSensitive());
275             return newConstraint;
276         } else if (c instanceof FieldValueBetweenConstraint) {
277             FieldValueBetweenConstraint constraint = (FieldValueBetweenConstraint) c;
278             BasicFieldValueBetweenConstraint newConstraint;
279             try {
280                 newConstraint = new BasicFieldValueBetweenConstraint(createNewStepField(q, constraint.getField()), constraint.getLowerLimit(), constraint.getUpperLimit());
281             } catch (NumberFormatException JavaDoc e) {
282                 newConstraint = new BasicFieldValueBetweenConstraint(createNewStepField(q, constraint.getField()), constraint.getLowerLimit(), constraint.getUpperLimit());
283             }
284             newConstraint.setInverse(constraint.isInverse());
285             newConstraint.setCaseSensitive(constraint.isCaseSensitive());
286             return newConstraint;
287         } else if (c instanceof FieldValueInConstraint) {
288             FieldValueInConstraint constraint = (FieldValueInConstraint) c;
289             BasicFieldValueInConstraint newConstraint = new BasicFieldValueInConstraint(createNewStepField(q, constraint.getField()));
290
291             Iterator k = constraint.getValues().iterator();
292             while (k.hasNext()) {
293                 Object JavaDoc value = k.next();
294                 newConstraint.addValue(value);
295             }
296             newConstraint.setInverse(constraint.isInverse());
297             newConstraint.setCaseSensitive(constraint.isCaseSensitive());
298             return newConstraint;
299         } else if (c instanceof LegacyConstraint) {
300             LegacyConstraint constraint = (LegacyConstraint) c;
301             BasicLegacyConstraint newConstraint = new BasicLegacyConstraint(constraint.getConstraint());
302             return newConstraint;
303         }
304         throw new RuntimeException JavaDoc("Could not copy constraint " + c);
305     }
306
307     /**
308      * Sets distinct.
309      *
310      * @param distinct The distinct value.
311      * @return This <code>BasicSearchQuery</code> instance.
312     */

313     public BasicSearchQuery setDistinct(boolean distinct) {
314         this.distinct = distinct;
315         hasChangedHashcode = true;
316         return this;
317     }
318
319     /**
320      * Sets maxNumber.
321      *
322      * @param maxNumber The maxNumber value.
323      * @return This <code>BasicSearchQuery</code> instance.
324      * @throws IllegalArgumentException when an invalid argument is supplied.
325      */

326     public BasicSearchQuery setMaxNumber(int maxNumber) {
327         if (maxNumber < -1) {
328             throw new IllegalArgumentException JavaDoc( "Invalid maxNumber value: " + maxNumber);
329         }
330         this.maxNumber = maxNumber;
331         hasChangedHashcode = true;
332         return this;
333     }
334
335     /**
336      * Sets offset.
337      *
338      * @param offset The offset value.
339      * @return This <code>BasicSearchQuery</code> instance.
340      * @throws IllegalArgumentException when an invalid argument is supplied.
341      */

342     public BasicSearchQuery setOffset(int offset) {
343         if (offset < 0) {
344             throw new IllegalArgumentException JavaDoc(
345             "Invalid offset value: " + offset);
346         }
347         this.offset = offset;
348         hasChangedHashcode = true;
349         return this;
350     }
351
352     /**
353      * Adds new step to this SearchQuery.
354      *
355      * @param builder The builder associated with the step.
356      * @return The new step.
357      * @throws IllegalArgumentException when an invalid argument is supplied.
358      */

359     public BasicStep addStep(MMObjectBuilder builder) {
360         BasicStep step = new BasicStep(builder);
361         steps.add(step);
362         hasChangedHashcode = true;
363         return step;
364     }
365
366     /**
367      * Adds new relationstep to this SearchQuery.
368      * This adds the next step as well, it can be retrieved by calling <code>
369      * {@link org.mmbase.storage.search.RelationStep#getNext getNext()}
370      * </code> on the relationstep, and cast to {@link BasicStep BasicStep}.
371      *
372      * @param builder The builder associated with the relation step.
373      * @param nextBuilder The builder associated with the next step.
374      * @return The new relationstep.
375      * @throws IllegalArgumentException when an invalid argument is supplied.
376      * @throws IllegalStateException when there is no previous step.
377      */

378     public BasicRelationStep addRelationStep(InsRel builder, MMObjectBuilder nextBuilder) {
379         int nrOfSteps = steps.size();
380         if (nrOfSteps == 0) {
381            throw new IllegalStateException JavaDoc("No previous step.");
382         }
383         BasicStep previous = (BasicStep) steps.get(nrOfSteps - 1);
384         BasicStep next = new BasicStep(nextBuilder);
385         BasicRelationStep relationStep = new BasicRelationStep(builder, previous, next);
386         steps.add(relationStep);
387         steps.add(next);
388         hasChangedHashcode = true;
389         return relationStep;
390     }
391
392     /**
393      * Adds new field to this SearchQuery.
394      *
395      * @param step The associated step.
396      * @param fieldDefs The associated fieldDefs.
397      * @return The new field.
398      * @throws IllegalArgumentException when an invalid argument is supplied.
399      * @throws UnsupportedOperationException when called
400      * on an aggregating query.
401      */

402     public BasicStepField addField(Step step, CoreField fieldDefs) {
403         if (aggregating) {
404             throw new UnsupportedOperationException JavaDoc("Adding non-aggregated field to aggregating query.");
405         }
406         BasicStepField field = new BasicStepField(step, fieldDefs);
407         assert ! fields.contains(field);
408         fields.add(field);
409         hasChangedHashcode = true;
410         return field;
411     }
412
413     /**
414      * @since MMBase-1.8.2
415      */

416     public BasicStepField addFieldUnlessPresent(Step step, CoreField fieldDefs) {
417         if (aggregating) {
418             throw new UnsupportedOperationException JavaDoc("Adding non-aggregated field to aggregating query.");
419         }
420         BasicStepField field = new BasicStepField(step, fieldDefs);
421         int i = fields.indexOf(field);
422         if (i == -1) {
423             fields.add(field);
424             hasChangedHashcode = true;
425         } else {
426             field = (BasicStepField) fields.get(i);
427         }
428         return field;
429     }
430
431     // only sensible for NodeSearchQuery
432
protected void mapField(CoreField field, StepField stepField) {
433
434     }
435
436     // MM
437
/**
438      * Add all fields of given step
439      */

440     public void addFields(Step step) {
441         MMBase mmb = MMBase.getMMBase();
442         MMObjectBuilder builder = mmb.getBuilder(step.getTableName());
443         Iterator iFields = builder.getFields().iterator();
444         while (iFields.hasNext()) {
445             CoreField field = (CoreField) iFields.next();
446             if (field.inStorage()) {
447                 BasicStepField stepField = addField(step, field);
448                 mapField(field, stepField);
449             }
450         }
451         hasChangedHashcode = true;
452     }
453
454     public void removeFields() {
455         fields.clear();
456         hasChangedHashcode = true;
457     }
458
459     /**
460      * Adds new aggregated field to this SearchQuery.
461      *
462      * @param step The associated step.
463      * @param field The associated Field.
464      * @param aggregationType The aggregation type.
465      * @return The new field.
466      * @throws IllegalArgumentException when an invalid argument is supplied.
467      * @throws UnsupportedOperationException when called
468      * on an non-aggregating query.
469      */

470     public BasicAggregatedField addAggregatedField(Step step, CoreField field, int aggregationType) {
471         if (!aggregating) {
472             throw new UnsupportedOperationException JavaDoc(
473             "Adding aggregated field to non-aggregating query.");
474         }
475         BasicAggregatedField stepField = new BasicAggregatedField(step, field, aggregationType);
476         fields.add(stepField);
477         hasChangedHashcode = true;
478         return stepField;
479     }
480
481     /**
482      * Creates sortorder for this SearchQuery.
483      *
484      * @param field The associated stepfield.
485      * @return The new sortOrder
486      * @throws IllegalArgumentException when an invalid argument is supplied.
487      */

488     public BasicSortOrder addSortOrder(StepField field) {
489         BasicSortOrder sortOrder = new BasicSortOrder(field);
490         sortOrders.add(sortOrder);
491         hasChangedHashcode = true;
492         return sortOrder;
493     }
494
495     /**
496      * Sets constraint.
497      *
498      * @param constraint The constraint.
499      * @throws IllegalArgumentException when an invalid argument is supplied.
500      */

501     public void setConstraint(Constraint constraint) {
502         this.constraint = constraint;
503         hasChangedHashcode = true;
504     }
505
506     // javadoc is inherited
507
public boolean isDistinct() {
508         return distinct;
509     }
510
511     // javadoc is inherited
512
public boolean isAggregating() {
513         return aggregating;
514     }
515
516     // javadoc is inherited
517
public List getSortOrders() {
518         // return as unmodifiable list
519
return Collections.unmodifiableList(sortOrders);
520     }
521
522     // javadoc is inherited
523
public List getSteps() {
524         // return as unmodifiable list
525
return Collections.unmodifiableList(steps);
526     }
527
528
529     // javadoc is inherited
530
public List getFields() {
531         // return as unmodifiable list
532
return Collections.unmodifiableList(fields);
533     }
534
535     // javadoc is inherited
536
public Constraint getConstraint() {
537         return constraint;
538     }
539
540     // javadoc is inherited
541
public int getMaxNumber() {
542         return maxNumber;
543     }
544
545     //javadoc is inherited
546
public int getOffset() {
547         return offset;
548     }
549
550     // javadoc is inherited
551
public boolean equals(Object JavaDoc obj) {
552         if (obj == this) {
553             return true;
554         }
555         if (obj instanceof SearchQuery) {
556             SearchQuery query = (SearchQuery) obj;
557             return distinct == query.isDistinct()
558                 && maxNumber == query.getMaxNumber()
559                 && offset == query.getOffset()
560                 && steps.equals(query.getSteps())
561                 && fields.equals(query.getFields())
562                 && sortOrders.equals(query.getSortOrders())
563                 && (constraint == null?
564                     query.getConstraint() == null:
565                     constraint.equals(query.getConstraint()));
566         } else {
567             return false;
568         }
569     }
570
571     // javadoc is inherited
572
public int hashCode() {
573         if (hasChangedHashcode) {
574           savedHashcode = (distinct? 0: 101)
575             + maxNumber * 17 + offset * 19
576             + 23 * steps.hashCode()
577             + 29 * fields.hashCode()
578             + 31 * sortOrders.hashCode()
579             + 37 * (constraint == null? 0: constraint.hashCode());
580           hasChangedHashcode = false;
581         }
582         return savedHashcode;
583     }
584
585     // javadoc is inherited
586
public String JavaDoc toString() {
587         return "SearchQuery(distinct:" + isDistinct()
588         + ", steps:" + getSteps()
589         + ", fields:" + getFields()
590         + ", constraint:" + getConstraint()
591         + ", sortorders:" + getSortOrders()
592         + ", max:" + getMaxNumber()
593         + ", offset:" + getOffset() + ")";
594     }
595
596 }
597
Popular Tags