1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package net.wotonomy.control;
20
21 import java.lang.reflect.InvocationTargetException;
22 import java.lang.reflect.Method;
23 import java.util.Collection;
24 import java.util.Iterator;
25 import java.util.LinkedList;
26 import java.util.List;
27 import java.util.ListIterator;
28 import java.util.Map;
29 import java.util.Set;
30 import java.util.StringTokenizer;
31
32 import net.wotonomy.foundation.NSArray;
33 import net.wotonomy.foundation.NSMutableArray;
34 import net.wotonomy.foundation.NSMutableDictionary;
35 import net.wotonomy.foundation.NSSelector;
36 import net.wotonomy.foundation.NSSet;
37 import net.wotonomy.foundation.internal.ValueConverter;
38 import net.wotonomy.foundation.internal.WotonomyException;
39
40 /***
41 * EOQualifiers are used to perform property-based
42 * qualifications on objects: for a set of criteria,
43 * a qualifier either qualifies or disqualifies an
44 * given object. EOKeyValueQualifiers can be joined
45 * by EOAndQualifier and EOOrQualifier, and so can
46 * form a tree of qualifications. <br><br>
47 *
48 * Certain qualifiers
49 * can accept a variable in place of a value; variable
50 * names are marked by a "$", as in "$name". Variables
51 * are resolved with the qualifierWithBindings() method.
52 *
53 * @author michael@mpowers.net
54 * @author yjcheung@intersectsoft.com
55 * @author $Author: cgruber $
56 * @version $Revision: 894 $
57 */
58 public abstract class EOQualifier
59 {
60 public static final NSSelector
61 QualifierOperatorCaseInsensitiveLike = new OperatorCaseInsensitiveLike();
62 public static final NSSelector
63 QualifierOperatorContains = new OperatorContains();
64 public static final NSSelector
65 QualifierOperatorEqual = new OperatorEqual();
66 public static final NSSelector
67 QualifierOperatorGreaterThan = new OperatorGreaterThan();
68 public static final NSSelector
69 QualifierOperatorGreaterThanOrEqualTo = new OperatorGreaterThanOrEqualTo();
70 public static final NSSelector
71 QualifierOperatorLessThan = new OperatorLessThan();
72 public static final NSSelector
73 QualifierOperatorLessThanOrEqualTo = new OperatorLessThanOrEqualTo();
74 public static final NSSelector
75 QualifierOperatorLike = new OperatorLike();
76 public static final NSSelector
77 QualifierOperatorNotEqual = new OperatorNotEqual();
78
79 /***
80 * Default constructor.
81 */
82 public EOQualifier ()
83 {
84 }
85
86 /***
87 * Adds all qualifier keys in this qualifier to
88 * the specified Set, which is expected to be
89 * mutable. The tree of qualifiers is traversed
90 * and the left-hand-side of each expression is
91 * added to the set.
92 */
93 public void addQualifierKeysToSet ( Set aSet )
94 {
95 throw new RuntimeException( "Not implemented yet." );
96 }
97
98 /***
99 * Returns a Set of all property names used for
100 * comparisons by this qualifier. The tree of
101 * qualifiers is traversed and the left-hand-side
102 * of each expression is added to the set.
103 */
104 public NSSet allQualifierKeys ()
105 {
106 throw new RuntimeException( "Not implemented yet." );
107 }
108
109 /***
110 * Returns a List containing the variables used
111 * at compare-time by this qualifier. Each variable
112 * will appear only once in the list.
113 */
114 public NSArray bindingKeys ()
115 {
116 throw new RuntimeException( "Not implemented yet." );
117 }
118
119 /***
120 * Returns whether the specified object meets the
121 * criteria defined by this qualifier.
122 */
123 public boolean evaluateWithObject ( Object anObject )
124 {
125 return true;
126 }
127
128 /***
129 * Returns the key (which can be a key path) that
130 * is tested against the specified binding variable.
131 * The tree is traversed looking for the first instance
132 * of the specified variable, and the corresponding
133 * left-hand-side of the expression is returned.
134 */
135 public String keyPathForBindingKey ( String aVariable )
136 {
137 throw new RuntimeException( "Not implemented yet." );
138 }
139
140 /***
141 * Returns a qualifier that is like this qualifier,
142 * except all variables will be replaced with values
143 * from the specified Map whose keys match the variable
144 * names. If requireAll is true, an exception will be
145 * thrown if there is no key that matches on of the
146 * variables in the tree; otherwise, the qualifier
147 * containing the unmatched variable is removed.
148 */
149 public EOQualifier qualifierWithBindings (
150 Map aMap,
151 boolean requireAll )
152 {
153 throw new WotonomyException( "Not implemented yet." );
154 }
155
156
157 /***
158 * Tests whether all the keys in this qualifier can
159 * be applied to an object of the specified class.
160 * @return A Throwable if the validation fails,
161 * otherwise returns null if the class can be used
162 * with this qualifier.
163 */
164 public Throwable validateKeysWithRootClassDescription (
165 Class aClass )
166 {
167 throw new WotonomyException( "Not implemented yet." );
168 }
169
170
171
172 /***
173 * Convenience to retain only those objects from the specified
174 * List that meet the specified qualifier's requirements.
175 */
176 public static void filterArrayWithQualifier (
177 List anObjectList, EOQualifier aQualifier )
178 {
179 ListIterator iterator = anObjectList.listIterator();
180 while ( iterator.hasNext() )
181 {
182 if ( ! aQualifier.evaluateWithObject( iterator.next() ) )
183 {
184 iterator.remove();
185 }
186 }
187 }
188
189 /***
190 * Convenience to return a List consisting only
191 * of those objects in the specified List that meet
192 * the specified qualifier's requirements.
193 */
194 public static NSArray filteredArrayWithQualifier (
195 List anObjectList, EOQualifier aQualifier )
196 {
197 Object o;
198 List result = new LinkedList();
199 Iterator iterator = anObjectList.iterator();
200 while ( iterator.hasNext() )
201 {
202 o = iterator.next();
203 if ( aQualifier.evaluateWithObject( o ) )
204 {
205 result.add( o );
206 }
207 }
208 return new NSArray( (Collection) result );
209 }
210
211 /***
212 * Convenience to create a set of EOKeyValueQualifiers
213 * joined by an EOAndQualifier. Each pair of keys and
214 * values are used to create EOKeyValueQualifiers.
215 */
216 public static EOQualifier
217 qualifierToMatchAllValues ( Map aMap )
218 {
219 Object key, value;
220 List qualifierList = new LinkedList();
221 Iterator iterator = aMap.keySet().iterator();
222 while ( iterator.hasNext() )
223 {
224 key = iterator.next();
225 value = aMap.get( key );
226 qualifierList.add( new EOKeyValueQualifier(
227 key.toString(), QualifierOperatorEqual, value ) );
228 }
229 return new EOAndQualifier( qualifierList );
230 }
231
232 /***
233 * Convenience to create a set of EOKeyValueQualifiers
234 * joined by an EOOrQualifier. Each pair of keys and
235 * values are used to create EOKeyValueQualifiers.
236 */
237 public static EOQualifier
238 qualifierToMatchAnyValue ( Map aMap )
239 {
240 Object key, value;
241 List qualifierList = new LinkedList();
242 Iterator iterator = aMap.keySet().iterator();
243 while ( iterator.hasNext() )
244 {
245 key = iterator.next();
246 value = aMap.get( key );
247 qualifierList.add( new EOKeyValueQualifier(
248 key.toString(), QualifierOperatorEqual, value ) );
249 }
250 return new EOOrQualifier( qualifierList );
251 }
252
253 /***
254 * Returns an EOQualifier that meets the criteria
255 * represented by the specified string and variable
256 * length argument list. This method parses the string
257 * and returns a tree of qualifiers. Each token beginning
258 * with "%" is replaced with the corresponding object
259 * from the argument list. The parser recognizes the
260 * operation tokens returned from the allQualifierOperators()
261 * method.
262 */
263 public static EOQualifier qualifierWithQualifierFormat (
264 String aString, List anArgumentList )
265 {
266 throw new RuntimeException( "Not implemented yet." );
267 }
268
269 /***
270 * Returns a List of operators that are supported for
271 * relational operations. This excludes string comparison
272 * operators.
273 */
274 public static NSArray relationalQualifierOperators ()
275 {
276 NSMutableArray result = new NSMutableArray();
277 BaseSelector selector;
278 Iterator iterator = allQualifierOperators().iterator();
279 while ( iterator.hasNext() )
280 {
281 selector = (BaseSelector) iterator.next();
282 if ( selector.isRelationalOperator() )
283 {
284 result.addObject( selector );
285 }
286 }
287 return result;
288 }
289
290 /***
291 * Returns a List of valid operators.
292 */
293 public static NSArray allQualifierOperators ()
294 {
295 return operators.allKeys();
296 }
297
298 /***
299 * Returns a selector the corresponds to the operation
300 * represented by the specified string. For example,
301 * ">" represents QualifierOperatorGreaterThan.
302 */
303 public static NSSelector operatorSelectorForString (
304 String anOperatorString )
305 {
306 return (NSSelector)
307 operators.objectForKey( anOperatorString );
308 }
309
310 /***
311 * Returns a string the corresponds to the operation
312 * represented by the specified selector. For example,
313 * QualifierOperatorGreaterThan is represented with ">".
314 */
315 public static String stringForOperatorSelector (
316 NSSelector aSelector )
317 {
318 return (String)
319 operators.allKeysForObject( aSelector ).lastObject();
320 }
321
322 /***
323 * Returns a string representation of this qualifier.
324 */
325 public String toString()
326 {
327
328 return super.toString();
329 }
330
331
332
333 private static NSMutableDictionary operators;
334 static
335 {
336 operators = new NSMutableDictionary();
337 operators.setObjectForKey(
338 QualifierOperatorCaseInsensitiveLike,
339 QualifierOperatorCaseInsensitiveLike.toString() );
340 operators.setObjectForKey(
341 QualifierOperatorContains,
342 QualifierOperatorContains.toString() );
343 operators.setObjectForKey(
344 QualifierOperatorEqual,
345 QualifierOperatorEqual.toString() );
346 operators.setObjectForKey(
347 QualifierOperatorGreaterThan,
348 QualifierOperatorGreaterThan.toString() );
349 operators.setObjectForKey(
350 QualifierOperatorGreaterThanOrEqualTo,
351 QualifierOperatorGreaterThanOrEqualTo.toString() );
352 operators.setObjectForKey(
353 QualifierOperatorLessThan,
354 QualifierOperatorLessThan.toString() );
355 operators.setObjectForKey(
356 QualifierOperatorLessThanOrEqualTo,
357 QualifierOperatorLessThanOrEqualTo.toString() );
358 operators.setObjectForKey(
359 QualifierOperatorLike,
360 QualifierOperatorLike.toString() );
361 operators.setObjectForKey(
362 QualifierOperatorNotEqual,
363 QualifierOperatorNotEqual.toString() );
364 }
365
366 static private abstract class BaseSelector extends NSSelector
367 {
368 public String name ()
369 {
370 return "BaseSelector";
371 }
372
373 public Class[] parameterTypes ()
374 {
375 return new Class[] { Object.class, Object.class };
376 }
377
378 public Method methodOnClass (Class aClass)
379 throws NoSuchMethodException
380 {
381 throw new NoSuchMethodException();
382 }
383
384 public Method methodOnObject (Object anObject)
385 throws NoSuchMethodException
386 {
387 throw new NoSuchMethodException();
388 }
389
390 public boolean implementedByClass (Class aClass)
391 {
392 return true;
393 }
394
395 public boolean implementedByObject (Object anObject)
396 {
397 return true;
398 }
399
400 public boolean isRelationalOperator()
401 {
402 return true;
403 }
404
405 public Object invoke (Object anObject, Object[] parameters)
406 throws IllegalAccessException, IllegalArgumentException,
407 InvocationTargetException, NoSuchMethodException
408 {
409 return qualify( anObject, parameters[0] );
410 }
411
412 abstract protected Boolean qualify( Object target, Object parameter );
413
414 protected int doCompare(Object o1, Object o2)
415 {
416 Class firstClass = o1.getClass();
417 Class secondClass = o2.getClass();
418
419 if ( ! ( secondClass.equals( firstClass ) ) )
420 {
421 Object converted = null;
422 if ( o2 instanceof Comparable )
423 {
424 converted = ValueConverter.convertObjectToClass( o1, secondClass );
425 if (converted != null)
426 {
427 o1 = converted;
428 }
429 }
430
431 if (converted == null && (o1 instanceof Comparable))
432 {
433 converted = ValueConverter.convertObjectToClass( o2, firstClass );
434 if ( converted != null )
435 {
436 o2 = converted;
437 }
438 }
439
440 if (converted == null)
441 {
442 throw new WotonomyException("Qualifier: Not Comparable Objects");
443
444 }
445 }
446
447 return ((Comparable)o2).compareTo( o1 );
448 }
449
450 }
451
452
453 static class OperatorCaseInsensitiveLike extends BaseSelector {
454
455 public boolean isRelationalOperator()
456 {
457 return false;
458 }
459
460 protected Boolean qualify( Object o1, Object o2 )
461 {
462 String myString1 = o1.toString();
463 String myString2 = o2.toString();
464 myString1 = myString1.toLowerCase();
465 myString2 = myString2.toLowerCase();
466 StringTokenizer st = new StringTokenizer(myString1, "%");
467
468 while (st.hasMoreTokens()) {
469 String part = st.nextToken();
470 int index = myString2.indexOf(part);
471 if (index > -1)
472 {
473 myString2 = myString2.substring(index + part.length());
474 }
475 else
476 {
477 return Boolean.FALSE;
478 }
479 }
480 return Boolean.TRUE;
481
482 }
483
484 public String toString()
485 {
486 return "caseInsensitiveLike";
487 }
488 }
489
490 static class OperatorContains extends BaseSelector {
491
492 public boolean isRelationalOperator()
493 {
494 return false;
495 }
496
497 protected Boolean qualify( Object o1, Object o2 )
498 {
499 String myString1 = o1.toString();
500 String myString2 = o2.toString();
501 return new Boolean(
502 myString2.indexOf(myString1) > -1 );
503 }
504
505 public String toString()
506 {
507 return "contains";
508 }
509 }
510
511 static class OperatorEqual extends BaseSelector {
512
513 protected Boolean qualify( Object o1, Object o2 )
514 {
515 return new Boolean( doCompare(o1, o2) == 0 );
516 }
517
518 public String toString()
519 {
520 return "=";
521 }
522 }
523
524 static class OperatorGreaterThan extends BaseSelector {
525
526 protected Boolean qualify( Object o1, Object o2 )
527 {
528 return new Boolean( doCompare(o1, o2) > 0 );
529 }
530
531 public String toString()
532 {
533 return ">";
534 }
535 }
536
537 static class OperatorGreaterThanOrEqualTo extends BaseSelector {
538
539 protected Boolean qualify( Object o1, Object o2 )
540 {
541 return new Boolean( doCompare(o1, o2) >= 0 );
542 }
543
544 public String toString()
545 {
546 return new String(" >= ");
547 }
548 }
549
550 static class OperatorLessThan extends BaseSelector {
551
552 protected Boolean qualify( Object o1, Object o2 )
553 {
554 return new Boolean( doCompare(o1, o2) < 0 );
555 }
556
557 public String toString()
558 {
559 return ">";
560 }
561 }
562
563 static class OperatorLessThanOrEqualTo extends BaseSelector {
564
565 protected Boolean qualify( Object o1, Object o2 )
566 {
567 return new Boolean (doCompare(o1, o2) <= 0);
568 }
569
570 public String toString()
571 {
572 return "<=";
573 }
574 }
575
576 static class OperatorLike extends BaseSelector {
577
578 public boolean isRelationalOperator()
579 {
580 return false;
581 }
582
583 protected Boolean qualify( Object o1, Object o2 )
584 {
585 String myString1 = o1.toString();
586 String myString2 = o2.toString();
587 StringTokenizer st = new StringTokenizer(myString1, "%");
588 while (st.hasMoreTokens()) {
589 String part = st.nextToken();
590 int index = myString2.indexOf(part);
591 if (index > -1)
592 {
593 myString2 = myString2.substring(index + part.length());
594 }
595 else
596 {
597 return Boolean.FALSE;
598 }
599 }
600 return Boolean.TRUE;
601 }
602
603 public String toString()
604 {
605 return "like";
606 }
607 }
608
609 static class OperatorNotEqual extends BaseSelector {
610
611 protected Boolean qualify( Object o1, Object o2 )
612 {
613 return new Boolean(!(o1.equals(o2)));
614 }
615
616 public String toString()
617 {
618 return "!=";
619 }
620 }
621
622
623 public static Object decodeWithKeyValueUnarchiver(EOKeyValueUnarchiver ua) {
624 String cname = (String)ua.decodeObjectForKey("class");
625 if (cname.equals("EOKeyValueQualifier"))
626 return (EOQualifier)EOKeyValueQualifier.decodeWithKeyValueUnarchiver(ua);
627 if (cname.equals("EOAndQualifier"))
628 return (EOQualifier)EOAndQualifier.decodeWithKeyValueUnarchiver(ua);
629 if (cname.equals("EOOrQualifier"))
630 return (EOQualifier)EOOrQualifier.decodeWithKeyValueUnarchiver(ua);
631 if (cname.equals("EONotQualifier"))
632 return (EOQualifier)EONotQualifier.decodeWithKeyValueUnarchiver(ua);
633 return null;
634 }
635
636 }
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680