001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing,
013: * software distributed under the License is distributed on an
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: */
019: package org.apache.openjpa.kernel;
020:
021: import java.lang.reflect.Method;
022: import java.math.BigDecimal;
023: import java.math.BigInteger;
024: import java.security.AccessController;
025: import java.security.PrivilegedActionException;
026: import java.util.ArrayList;
027: import java.util.Calendar;
028: import java.util.Collection;
029: import java.util.Date;
030: import java.util.List;
031:
032: import org.apache.openjpa.enhance.Reflection;
033: import org.apache.openjpa.kernel.exps.AggregateListener;
034: import org.apache.openjpa.kernel.exps.FilterListener;
035: import org.apache.openjpa.lib.util.J2DoPrivHelper;
036: import org.apache.openjpa.lib.util.Localizer;
037: import org.apache.openjpa.meta.ClassMetaData;
038: import org.apache.openjpa.meta.JavaTypes;
039: import org.apache.openjpa.util.InternalException;
040: import org.apache.openjpa.util.UserException;
041: import serp.util.Numbers;
042: import serp.util.Strings;
043:
044: /**
045: * Helper methods for dealing with query filters.
046: *
047: * @author Abe White
048: * @nojavadoc
049: */
050: public class Filters {
051:
052: private static final BigDecimal ZERO_BIGDECIMAL = new BigDecimal(0D);
053: private static final BigInteger ZERO_BIGINTEGER = new BigInteger(
054: "0");
055:
056: private static final int OP_ADD = 0;
057: private static final int OP_SUBTRACT = 1;
058: private static final int OP_MULTIPLY = 2;
059: private static final int OP_DIVIDE = 3;
060: private static final int OP_MOD = 4;
061:
062: private static final Localizer _loc = Localizer
063: .forPackage(Filters.class);
064:
065: /**
066: * Return the correct wrapper type for the given class.
067: */
068: public static Class wrap(Class c) {
069: if (!c.isPrimitive())
070: return c;
071: if (c == int.class)
072: return Integer.class;
073: if (c == float.class)
074: return Float.class;
075: if (c == double.class)
076: return Double.class;
077: if (c == long.class)
078: return Long.class;
079: if (c == boolean.class)
080: return Boolean.class;
081: if (c == short.class)
082: return Short.class;
083: if (c == byte.class)
084: return Byte.class;
085: if (c == char.class)
086: return Character.class;
087: return c;
088: }
089:
090: /**
091: * Return the correct primitive type for the given class, if it is a
092: * wrapper.
093: */
094: public static Class unwrap(Class c) {
095: if (c.isPrimitive() || c == String.class)
096: return c;
097: if (c == Integer.class)
098: return int.class;
099: if (c == Float.class)
100: return float.class;
101: if (c == Double.class)
102: return double.class;
103: if (c == Long.class)
104: return long.class;
105: if (c == Boolean.class)
106: return boolean.class;
107: if (c == Short.class)
108: return short.class;
109: if (c == Byte.class)
110: return byte.class;
111: if (c == Character.class)
112: return char.class;
113: return c;
114: }
115:
116: /**
117: * Given two types, return type they should both be converted
118: * to before performing any operations between them.
119: */
120: public static Class promote(Class c1, Class c2) {
121: if (c1 == c2)
122: return unwrap(c1);
123: Class w1 = wrap(c1);
124: Class w2 = wrap(c2);
125: if (w1 == w2)
126: return unwrap(c1);
127:
128: // not numbers?
129: boolean w1Number = Number.class.isAssignableFrom(w1);
130: boolean w2Number = Number.class.isAssignableFrom(w2);
131: if (!w1Number || !w2Number) {
132: // the only non-numeric promotion we do is string to char,
133: // or from char/string to number
134: if (!w1Number) {
135: if (w2Number
136: && (w1 == Character.class || w1 == String.class))
137: return (w2 == Byte.class || w2 == Short.class) ? Integer.class
138: : unwrap(c2);
139: if (!w2Number && w1 == Character.class
140: && w2 == String.class)
141: return String.class;
142: if (w2Number)
143: return unwrap(c2);
144: }
145: if (!w2Number) {
146: if (w1Number
147: && (w2 == Character.class || w2 == String.class))
148: return (w1 == Byte.class || w1 == Short.class) ? Integer.class
149: : unwrap(c1);
150: if (!w1Number && w2 == Character.class
151: && w1 == String.class)
152: return String.class;
153: if (w1Number)
154: return unwrap(c1);
155: }
156:
157: // if neither are numbers, use least-derived of the two. if neither
158: // is assignable from the other but one is a standard type, assume
159: // the other can be converted to that standard type
160: if (!w1Number && !w2Number) {
161: if (w1 == Object.class)
162: return unwrap(c2);
163: if (w2 == Object.class)
164: return unwrap(c1);
165: if (w1.isAssignableFrom(w2))
166: return unwrap(c1);
167: if (w2.isAssignableFrom(w1))
168: return unwrap(c2);
169: if (isNonstandardType(w1))
170: return (isNonstandardType(w2)) ? Object.class
171: : unwrap(c2);
172: if (isNonstandardType(w2))
173: return (isNonstandardType(w1)) ? Object.class
174: : unwrap(c1);
175: }
176: return Object.class;
177: }
178:
179: if (w1 == BigDecimal.class || w2 == BigDecimal.class)
180: return BigDecimal.class;
181: if (w1 == BigInteger.class) {
182: if (w2 == Float.class || w2 == Double.class)
183: return BigDecimal.class;
184: return BigInteger.class;
185: }
186: if (w2 == BigInteger.class) {
187: if (w1 == Float.class || w1 == Double.class)
188: return BigDecimal.class;
189: return BigInteger.class;
190: }
191: if (w1 == Double.class || w2 == Double.class)
192: return double.class;
193: if (w1 == Float.class || w2 == Float.class)
194: return float.class;
195: if (w1 == Long.class || w2 == Long.class)
196: return long.class;
197: return int.class;
198: }
199:
200: /**
201: * Return whether the given type is not a standard persistent type.
202: */
203: private static boolean isNonstandardType(Class c) {
204: switch (JavaTypes.getTypeCode(c)) {
205: case JavaTypes.ARRAY:
206: case JavaTypes.COLLECTION:
207: case JavaTypes.MAP:
208: case JavaTypes.PC:
209: case JavaTypes.PC_UNTYPED:
210: case JavaTypes.OID:
211: case JavaTypes.OBJECT:
212: return true;
213: default:
214: return false;
215: }
216: }
217:
218: /**
219: * Return whether an instance of the first class can be converted to
220: * an instance of the second.
221: */
222: public static boolean canConvert(Class c1, Class c2, boolean strict) {
223: c1 = wrap(c1);
224: c2 = wrap(c2);
225: if (c2.isAssignableFrom(c1))
226: return true;
227:
228: boolean c1Number = Number.class.isAssignableFrom(c1);
229: boolean c2Number = Number.class.isAssignableFrom(c2);
230: if (c1Number && c2Number)
231: return true;
232: if ((c1Number && (c2 == Character.class || (!strict && c2 == String.class)))
233: || (c2Number && (c1 == Character.class || (!strict && c1 == String.class))))
234: return true;
235: if (c1 == String.class && c2 == Character.class)
236: return true;
237: if (c2 == String.class)
238: return !strict;
239: return false;
240: }
241:
242: /**
243: * Convert the given value to the given type.
244: */
245: public static Object convert(Object o, Class type) {
246: if (o == null)
247: return null;
248: if (o.getClass() == type)
249: return o;
250:
251: type = wrap(type);
252: if (type.isAssignableFrom(o.getClass()))
253: return o;
254:
255: // the only non-numeric conversions we do are to string, or from
256: // string/char to number, or calendar/date
257: boolean num = o instanceof Number;
258: if (!num) {
259: if (type == String.class)
260: return o.toString();
261: else if (type == Character.class) {
262: String str = o.toString();
263: if (str != null && str.length() == 1)
264: return new Character(str.charAt(0));
265: } else if (Calendar.class.isAssignableFrom(type)
266: && o instanceof Date) {
267: Calendar cal = Calendar.getInstance();
268: cal.setTime((Date) o);
269: return cal;
270: } else if (Date.class.isAssignableFrom(type)
271: && o instanceof Calendar) {
272: return ((Calendar) o).getTime();
273: } else if (Number.class.isAssignableFrom(type)) {
274: Integer i = null;
275: if (o instanceof Character)
276: i = Numbers.valueOf(((Character) o).charValue());
277: else if (o instanceof String
278: && ((String) o).length() == 1)
279: i = Numbers.valueOf(((String) o).charAt(0));
280:
281: if (i != null) {
282: if (type == Integer.class)
283: return i;
284: num = true;
285: }
286: }
287: }
288: if (!num)
289: throw new ClassCastException(_loc.get("cant-convert", o,
290: o.getClass(), type).getMessage());
291:
292: if (type == Integer.class) {
293: return Numbers.valueOf(((Number) o).intValue());
294: } else if (type == Float.class) {
295: return new Float(((Number) o).floatValue());
296: } else if (type == Double.class) {
297: return new Double(((Number) o).doubleValue());
298: } else if (type == Long.class) {
299: return Numbers.valueOf(((Number) o).longValue());
300: } else if (type == BigDecimal.class) {
301: // the BigDecimal constructor doesn't handle the
302: // "NaN" string version of Double.NaN and Float.NaN, nor
303: // does it handle infinity; we need to instead use the Double
304: // and Float versions, despite wanting to cast it to BigDecimal
305: double dval = ((Number) o).doubleValue();
306: if (Double.isNaN(dval) || Double.isInfinite(dval))
307: return new Double(dval);
308:
309: float fval = ((Number) o).floatValue();
310: if (Float.isNaN(fval) || Float.isInfinite(fval))
311: return new Float(fval);
312:
313: return new BigDecimal(o.toString());
314: } else if (type == BigInteger.class) {
315: return new BigInteger(o.toString());
316: } else if (type == Short.class) {
317: return new Short(((Number) o).shortValue());
318: } else if (type == Byte.class) {
319: return new Byte(((Number) o).byteValue());
320: } else {
321: return Numbers.valueOf(((Number) o).intValue());
322: }
323: }
324:
325: /**
326: * Add the given values.
327: */
328: public static Object add(Object o1, Class c1, Object o2, Class c2) {
329: return op(o1, c1, o2, c2, OP_ADD);
330: }
331:
332: /**
333: * Subtract the given values.
334: */
335: public static Object subtract(Object o1, Class c1, Object o2,
336: Class c2) {
337: return op(o1, c1, o2, c2, OP_SUBTRACT);
338: }
339:
340: /**
341: * Multiply the given values.
342: */
343: public static Object multiply(Object o1, Class c1, Object o2,
344: Class c2) {
345: return op(o1, c1, o2, c2, OP_MULTIPLY);
346: }
347:
348: /**
349: * Divide the given values.
350: */
351: public static Object divide(Object o1, Class c1, Object o2, Class c2) {
352: return op(o1, c1, o2, c2, OP_DIVIDE);
353: }
354:
355: /**
356: * Mod the given values.
357: */
358: public static Object mod(Object o1, Class c1, Object o2, Class c2) {
359: return op(o1, c1, o2, c2, OP_MOD);
360: }
361:
362: /**
363: * Perform the given operation on two numbers.
364: */
365: private static Object op(Object o1, Class c1, Object o2, Class c2,
366: int op) {
367: Class promote = promote(c1, c2);
368: if (promote == int.class) {
369: int n1 = (o1 == null) ? 0 : ((Number) o1).intValue();
370: int n2 = (o2 == null) ? 0 : ((Number) o2).intValue();
371: return op(n1, n2, op);
372: }
373: if (promote == float.class) {
374: float n1 = (o1 == null) ? 0F : ((Number) o1).floatValue();
375: float n2 = (o2 == null) ? 0F : ((Number) o2).floatValue();
376: return op(n1, n2, op);
377: }
378: if (promote == double.class) {
379: double n1 = (o1 == null) ? 0D : ((Number) o1).doubleValue();
380: double n2 = (o2 == null) ? 0D : ((Number) o2).doubleValue();
381: return op(n1, n2, op);
382: }
383: if (promote == long.class) {
384: long n1 = (o1 == null) ? 0L : ((Number) o1).longValue();
385: long n2 = (o2 == null) ? 0L : ((Number) o2).longValue();
386: return op(n1, n2, op);
387: }
388: if (promote == BigDecimal.class) {
389: BigDecimal n1 = (o1 == null) ? ZERO_BIGDECIMAL
390: : (BigDecimal) convert(o1, promote);
391: BigDecimal n2 = (o2 == null) ? ZERO_BIGDECIMAL
392: : (BigDecimal) convert(o2, promote);
393: return op(n1, n2, op);
394: }
395: if (promote == BigInteger.class) {
396: BigInteger n1 = (o1 == null) ? ZERO_BIGINTEGER
397: : (BigInteger) convert(o1, promote);
398: BigInteger n2 = (o2 == null) ? ZERO_BIGINTEGER
399: : (BigInteger) convert(o2, promote);
400: return op(n1, n2, op);
401: }
402: // default to int
403: int n1 = (o1 == null) ? 0 : ((Number) o1).intValue();
404: int n2 = (o2 == null) ? 0 : ((Number) o2).intValue();
405: return op(n1, n2, op);
406: }
407:
408: /**
409: * Return the result of a mathematical operation.
410: */
411: private static Object op(int n1, int n2, int op) {
412: int tot;
413: switch (op) {
414: case OP_ADD:
415: tot = n1 + n2;
416: break;
417: case OP_SUBTRACT:
418: tot = n1 - n2;
419: break;
420: case OP_MULTIPLY:
421: tot = n1 * n2;
422: break;
423: case OP_DIVIDE:
424: tot = n1 / n2;
425: break;
426: case OP_MOD:
427: tot = n1 % n2;
428: break;
429: default:
430: throw new InternalException();
431: }
432: return Numbers.valueOf(tot);
433: }
434:
435: /**
436: * Return the result of a mathematical operation.
437: */
438: private static Object op(float n1, float n2, int op) {
439: float tot;
440: switch (op) {
441: case OP_ADD:
442: tot = n1 + n2;
443: break;
444: case OP_SUBTRACT:
445: tot = n1 - n2;
446: break;
447: case OP_MULTIPLY:
448: tot = n1 * n2;
449: break;
450: case OP_DIVIDE:
451: tot = n1 / n2;
452: break;
453: case OP_MOD:
454: tot = n1 % n2;
455: break;
456: default:
457: throw new InternalException();
458: }
459: return new Float(tot);
460: }
461:
462: /**
463: * Return the result of a mathematical operation.
464: */
465: private static Object op(double n1, double n2, int op) {
466: double tot;
467: switch (op) {
468: case OP_ADD:
469: tot = n1 + n2;
470: break;
471: case OP_SUBTRACT:
472: tot = n1 - n2;
473: break;
474: case OP_MULTIPLY:
475: tot = n1 * n2;
476: break;
477: case OP_DIVIDE:
478: tot = n1 / n2;
479: break;
480: case OP_MOD:
481: tot = n1 % n2;
482: break;
483: default:
484: throw new InternalException();
485: }
486: return new Double(tot);
487: }
488:
489: /**
490: * Return the result of a mathematical operation.
491: */
492: private static Object op(long n1, long n2, int op) {
493: long tot;
494: switch (op) {
495: case OP_ADD:
496: tot = n1 + n2;
497: break;
498: case OP_SUBTRACT:
499: tot = n1 - n2;
500: break;
501: case OP_MULTIPLY:
502: tot = n1 * n2;
503: break;
504: case OP_DIVIDE:
505: tot = n1 / n2;
506: break;
507: case OP_MOD:
508: tot = n1 % n2;
509: break;
510: default:
511: throw new InternalException();
512: }
513: return Numbers.valueOf(tot);
514: }
515:
516: /**
517: * Return the result of a mathematical operation.
518: */
519: private static Object op(BigDecimal n1, BigDecimal n2, int op) {
520: switch (op) {
521: case OP_ADD:
522: return n1.add(n2);
523: case OP_SUBTRACT:
524: return n1.subtract(n2);
525: case OP_MULTIPLY:
526: return n1.multiply(n2);
527: case OP_DIVIDE:
528: int scale = Math.max(n1.scale(), n2.scale());
529: return n1.divide(n2, scale, BigDecimal.ROUND_HALF_UP);
530: case OP_MOD:
531: throw new UserException(_loc.get("mod-bigdecimal"));
532: default:
533: throw new InternalException();
534: }
535: }
536:
537: /**
538: * Return the result of a mathematical operation.
539: */
540: private static Object op(BigInteger n1, BigInteger n2, int op) {
541: switch (op) {
542: case OP_ADD:
543: return n1.add(n2);
544: case OP_SUBTRACT:
545: return n1.subtract(n2);
546: case OP_MULTIPLY:
547: return n1.multiply(n2);
548: case OP_DIVIDE:
549: return n1.divide(n2);
550: default:
551: throw new InternalException();
552: }
553: }
554:
555: /**
556: * Parses the given declarations into a list of type, name, type, name...
557: * Returns null if no declarations. Assumes declaration is not an empty
558: * string and is already trimmed (valid assumptions given the checks made
559: * in our setters).
560: *
561: * @param decType the type of declaration being parsed, for use in
562: * error messages
563: */
564: public static List parseDeclaration(String dec, char split,
565: String decType) {
566: if (dec == null)
567: return null;
568:
569: // watch for common mixups between commas and semis
570: char bad = (char) 0;
571: if (split == ',')
572: bad = ';';
573: else if (split == ';')
574: bad = ',';
575:
576: char sentinal = ' ';
577: char cur;
578: int start = 0;
579: boolean skipSpace = false;
580: List results = new ArrayList(6);
581: for (int i = 0; i < dec.length(); i++) {
582: cur = dec.charAt(i);
583: if (cur == bad)
584: throw new UserException(_loc.get("bad-dec", dec,
585: decType));
586: if (cur == ' ' && skipSpace) {
587: start++;
588: continue;
589: }
590:
591: skipSpace = false;
592: if (cur != sentinal)
593: continue;
594:
595: // if looking for spaces, look for split char, or vice versa
596: sentinal = (sentinal == ' ') ? split : ' ';
597: results.add(dec.substring(start, i).trim());
598: start = i + 1;
599: skipSpace = true;
600: }
601:
602: // add last token, if any
603: if (start < dec.length())
604: results.add(dec.substring(start));
605:
606: // if not an even number of elements, something is wrong
607: if (results.isEmpty() || results.size() % 2 != 0)
608: throw new UserException(_loc.get("bad-dec", dec, decType));
609:
610: return results;
611: }
612:
613: /**
614: * Split the given expression list into distinct expressions. Assumes the
615: * given string is not null or of zero length and is already trimmed
616: * (valid assumptions given the checks in our setters and before
617: * this method call).
618: */
619: public static List splitExpressions(String str, char split,
620: int expected) {
621: if (str == null)
622: return null;
623:
624: List exps = null;
625: int parenDepth = 0;
626: int begin = 0, pos = 0;
627: boolean escape = false;
628: boolean string = false;
629: boolean nonspace = false;
630: char quote = 0;
631: for (char c; pos < str.length(); pos++) {
632: c = str.charAt(pos);
633: if (c == '\\') {
634: escape = !escape;
635: continue;
636: }
637: if (escape) {
638: escape = false;
639: continue;
640: }
641:
642: switch (c) {
643: case '\'':
644: case '"':
645: if (string && quote == c)
646: string = false;
647: else if (!string) {
648: quote = c;
649: string = true;
650: }
651: nonspace = true;
652: break;
653: case '(':
654: if (!string)
655: parenDepth++;
656: nonspace = true;
657: break;
658: case ')':
659: if (!string)
660: parenDepth--;
661: nonspace = true;
662: break;
663: case ' ':
664: case '\t':
665: case '\n':
666: case '\r':
667: if (c == split && !string && parenDepth == 0
668: && nonspace) {
669: if (exps == null)
670: exps = new ArrayList(expected);
671: exps.add(str.substring(begin, pos).trim());
672: begin = pos + 1;
673: nonspace = false;
674: }
675: break;
676: default:
677: if (c == split && !string && parenDepth == 0) {
678: if (exps == null)
679: exps = new ArrayList(expected);
680: exps.add(str.substring(begin, pos).trim());
681: begin = pos + 1;
682: }
683: nonspace = true;
684: }
685: escape = false;
686: }
687:
688: if (exps == null) {
689: // Collections.singletonList wasn't added until 1.3
690: exps = new ArrayList(1);
691: exps.add(str);
692: return exps;
693: }
694:
695: // add last exp and return array
696: String last = str.substring(begin).trim();
697: if (last.length() > 0)
698: exps.add(last);
699: return exps;
700: }
701:
702: /**
703: * Add the given access path metadatas to the full path list, making sure
704: * to maintain only base metadatas in the list. The given list may be null.
705: */
706: public static List addAccessPathMetaDatas(List metas,
707: ClassMetaData[] path) {
708: if (path == null || path.length == 0)
709: return metas;
710:
711: // create set of base class metadatas in access path
712: if (metas == null)
713: metas = new ArrayList();
714: int last = metas.size();
715:
716: // for every element in the path of this executor, compare it
717: // to already-gathered elements to see if it should replace
718: // a subclass in the list or should be added as a new base;
719: // at least it's n^2 of a small n...
720: ClassMetaData meta;
721: boolean add;
722: for (int i = 0; i < path.length; i++) {
723: add = true;
724: for (int j = 0; add && j < last; j++) {
725: meta = (ClassMetaData) metas.get(j);
726:
727: if (meta.getDescribedType().isAssignableFrom(
728: path[i].getDescribedType())) {
729: // list already contains base class
730: add = false;
731: } else if (path[i].getDescribedType().isAssignableFrom(
732: meta.getDescribedType())) {
733: // this element replaces its subclass
734: add = false;
735: metas.set(j, path[i]);
736: }
737: }
738:
739: // if no base class of current path element already in
740: // list and path element didn't replace a subclass in the
741: // list, then add it now as a new base
742: if (add)
743: metas.add(path[i]);
744: }
745: return metas;
746: }
747:
748: /**
749: * Convert the user-given hint value to an aggregate listener.
750: * The hint can be an aggregate listener instance or class name.
751: */
752: public static AggregateListener hintToAggregateListener(
753: Object hint, ClassLoader loader) {
754: if (hint == null)
755: return null;
756: if (hint instanceof AggregateListener)
757: return (AggregateListener) hint;
758:
759: Exception cause = null;
760: if (hint instanceof String) {
761: try {
762: return (AggregateListener) AccessController
763: .doPrivileged(J2DoPrivHelper
764: .newInstanceAction(Class.forName(
765: (String) hint, true, loader)));
766: } catch (Exception e) {
767: if (e instanceof PrivilegedActionException)
768: e = ((PrivilegedActionException) e).getException();
769: cause = e;
770: }
771: }
772: throw new UserException(_loc.get("bad-agg-listener-hint", hint,
773: hint.getClass())).setCause(cause);
774: }
775:
776: /**
777: * Convert the user-given hint value to an array of aggregate listeners.
778: * The hint can be an aggregate listener, aggregate listener array,
779: * collection, or comma-separated class names.
780: */
781: public static AggregateListener[] hintToAggregateListeners(
782: Object hint, ClassLoader loader) {
783: if (hint == null)
784: return null;
785: if (hint instanceof AggregateListener[])
786: return (AggregateListener[]) hint;
787: if (hint instanceof AggregateListener)
788: return new AggregateListener[] { (AggregateListener) hint };
789: if (hint instanceof Collection) {
790: Collection c = (Collection) hint;
791: return (AggregateListener[]) c
792: .toArray(new AggregateListener[c.size()]);
793: }
794:
795: Exception cause = null;
796: if (hint instanceof String) {
797: String[] clss = Strings.split((String) hint, ",", 0);
798: AggregateListener[] aggs = new AggregateListener[clss.length];
799: try {
800: for (int i = 0; i < clss.length; i++)
801: aggs[i] = (AggregateListener) AccessController
802: .doPrivileged(J2DoPrivHelper
803: .newInstanceAction(Class.forName(
804: clss[i], true, loader)));
805: return aggs;
806: } catch (Exception e) {
807: if (e instanceof PrivilegedActionException)
808: e = ((PrivilegedActionException) e).getException();
809: cause = e;
810: }
811: }
812: throw new UserException(_loc.get("bad-agg-listener-hint", hint,
813: hint.getClass())).setCause(cause);
814: }
815:
816: /**
817: * Convert the user-given hint value to a filter listener.
818: * The hint can be a filter listener instance or class name.
819: */
820: public static FilterListener hintToFilterListener(Object hint,
821: ClassLoader loader) {
822: if (hint == null)
823: return null;
824: if (hint instanceof FilterListener)
825: return (FilterListener) hint;
826:
827: Exception cause = null;
828: if (hint instanceof String) {
829: try {
830: return (FilterListener) AccessController
831: .doPrivileged(J2DoPrivHelper
832: .newInstanceAction(Class.forName(
833: (String) hint, true, loader)));
834: } catch (Exception e) {
835: if (e instanceof PrivilegedActionException)
836: e = ((PrivilegedActionException) e).getException();
837: cause = e;
838: }
839: }
840: throw new UserException(_loc.get("bad-filter-listener-hint",
841: hint, hint.getClass())).setCause(cause);
842: }
843:
844: /**
845: * Convert the user-given hint value to an array of filter listeners.
846: * The hint can be a filter listener, filter listener array,
847: * collection, or comma-separated class names.
848: */
849: public static FilterListener[] hintToFilterListeners(Object hint,
850: ClassLoader loader) {
851: if (hint == null)
852: return null;
853: if (hint instanceof FilterListener[])
854: return (FilterListener[]) hint;
855: if (hint instanceof FilterListener)
856: return new FilterListener[] { (FilterListener) hint };
857: if (hint instanceof Collection) {
858: Collection c = (Collection) hint;
859: return (FilterListener[]) c.toArray(new FilterListener[c
860: .size()]);
861: }
862:
863: Exception cause = null;
864: if (hint instanceof String) {
865: String[] clss = Strings.split((String) hint, ",", 0);
866: FilterListener[] filts = new FilterListener[clss.length];
867: try {
868: for (int i = 0; i < clss.length; i++)
869: filts[i] = (FilterListener) AccessController
870: .doPrivileged(J2DoPrivHelper
871: .newInstanceAction(Class.forName(
872: clss[i], true, loader)));
873: return filts;
874: } catch (Exception e) {
875: if (e instanceof PrivilegedActionException)
876: e = ((PrivilegedActionException) e).getException();
877: cause = e;
878: }
879: }
880: throw new UserException(_loc.get("bad-filter-listener-hint",
881: hint, hint.getClass())).setCause(cause);
882: }
883:
884: /**
885: * Return the value of the property named by the hint key.
886: */
887: public static Object hintToGetter(Object target, String hintKey) {
888: if (target == null || hintKey == null)
889: return null;
890:
891: Method getter = Reflection.findGetter(target.getClass(),
892: hintKey, true);
893: return Reflection.get(target, getter);
894: }
895:
896: /**
897: * Set the value of the property named by the hint key.
898: */
899: public static void hintToSetter(Object target, String hintKey,
900: Object value) {
901: if (target == null || hintKey == null)
902: return;
903:
904: Method setter = Reflection.findSetter(target.getClass(),
905: hintKey, true);
906: if (value instanceof String) {
907: if ("null".equals(value))
908: value = null;
909: else {
910: try {
911: value = Strings.parse((String) value, setter
912: .getParameterTypes()[0]);
913: } catch (Exception e) {
914: throw new UserException(_loc.get(
915: "bad-setter-hint-arg", hintKey, value,
916: setter.getParameterTypes()[0])).setCause(e);
917: }
918: }
919: }
920: Reflection.set(target, setter, value);
921: }
922: }
|