001: /*
002: * hgcommons 7
003: * Hammurapi Group Common Library
004: * Copyright (C) 2003 Hammurapi Group
005: *
006: * This program is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation; either
009: * version 2 of the License, or (at your option) any later version.
010: *
011: * This program is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: * You should have received a copy of the GNU Lesser General Public
017: * License along with this library; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
019: *
020: * URL: http://www.hammurapi.biz/hammurapi-biz/ef/xmenu/hammurapi-group/products/products/hgcommons/index.html
021: * e-Mail: support@hammurapi.biz
022: */
023: package biz.hammurapi.util;
024:
025: import java.lang.reflect.Array;
026: import java.lang.reflect.InvocationTargetException;
027: import java.lang.reflect.Method;
028: import java.util.ArrayList;
029: import java.util.Arrays;
030: import java.util.Collection;
031: import java.util.Collections;
032: import java.util.HashMap;
033: import java.util.IdentityHashMap;
034: import java.util.Iterator;
035: import java.util.LinkedList;
036: import java.util.List;
037: import java.util.Map;
038:
039: import biz.hammurapi.config.Command;
040:
041: /**
042: * Dispatching visitor navigates through Visitables hierarchy and
043: * invokes visit(<I>Type</I>) method of targets with compatible <I>Type</I>.
044: * @author Pavel Vlasov
045: * @version $Revision: 1.9 $
046: */
047: public class DispatchingVisitor implements PoliteVisitor,
048: VisitorStackSource, Command {
049: private Collection targets;
050: private VisitorExceptionSink exceptionSink;
051: private Map handlers = new HashMap();
052: private Map leaveHandlers = new HashMap();
053: private Map verifyHandlers = new HashMap();
054: private Map filterHandlers = new HashMap();
055: private Collection listeners = new ArrayList();
056: private Collection classesOfInterest = new ArrayList();
057: private Collection unmodifiableClassesOfInterest = Collections
058: .unmodifiableCollection(classesOfInterest);
059:
060: private Collection handlerList = new ArrayList();
061: private Collection leaveHandlerList = new ArrayList();
062: private Collection verifyHandlerList = new ArrayList();
063: private Collection filterHandlerList = new ArrayList();
064:
065: /**
066: * @param clazz
067: * @return handlers for class.
068: */
069: private Collection getHandlers(Class clazz) {
070: synchronized (handlers) {
071: List ret = (List) handlers.get(clazz);
072: if (ret == null) {
073: ret = new ArrayList();
074: handlers.put(clazz, ret);
075: Iterator it = handlerList.iterator();
076: while (it.hasNext()) {
077: InvocationHandler handler = (InvocationHandler) it
078: .next();
079: if (handler.method.getParameterTypes()[0]
080: .isAssignableFrom(clazz)) {
081: ret.add(handler);
082: }
083: }
084: Collections.sort(ret);
085: }
086: return ret;
087: }
088: }
089:
090: /**
091: * @param clazz
092: * @return leave handlers for class
093: */
094: private Collection getLeaveHandlers(Class clazz) {
095: synchronized (leaveHandlers) {
096: List ret = (List) leaveHandlers.get(clazz);
097: if (ret == null) {
098: ret = new ArrayList();
099: leaveHandlers.put(clazz, ret);
100: Iterator it = leaveHandlerList.iterator();
101: while (it.hasNext()) {
102: InvocationHandler handler = (InvocationHandler) it
103: .next();
104: if (handler.method.getParameterTypes()[0]
105: .isAssignableFrom(clazz)) {
106: ret.add(handler);
107: }
108: }
109: Collections.sort(ret);
110: Collections.reverse(ret);
111: }
112: return ret;
113: }
114: }
115:
116: /**
117: * @param clazz
118: * @return leave handlers for class
119: */
120: private Collection getVerifyHandlers(Class clazz) {
121: synchronized (verifyHandlers) {
122: List ret = (List) verifyHandlers.get(clazz);
123: if (ret == null) {
124: ret = new ArrayList();
125: verifyHandlers.put(clazz, ret);
126: Iterator it = verifyHandlerList.iterator();
127: while (it.hasNext()) {
128: InvocationHandler handler = (InvocationHandler) it
129: .next();
130: if (handler.method.getParameterTypes()[0]
131: .isAssignableFrom(clazz)) {
132: ret.add(handler);
133: }
134: }
135: Collections.sort(ret);
136: Collections.reverse(ret);
137: }
138: return ret;
139: }
140: }
141:
142: /**
143: * @param clazz
144: * @return filter handlers for class.
145: */
146: private Collection getFilterHandlers(Class clazz) {
147: synchronized (filterHandlers) {
148: List ret = (List) filterHandlers.get(clazz);
149: if (ret == null) {
150: ret = new ArrayList();
151: filterHandlers.put(clazz, ret);
152: Iterator it = filterHandlerList.iterator();
153: while (it.hasNext()) {
154: InvocationHandler handler = (InvocationHandler) it
155: .next();
156: if (handler.method.getParameterTypes()[0]
157: .isAssignableFrom(clazz)) {
158: ret.add(handler);
159: }
160: }
161: }
162: return ret;
163: }
164: }
165:
166: /**
167: * Targets which want to listen to invocations of self should implement
168: * this interface.
169: * @author Pavel Vlasov
170: * @revision $Revision: 1.9 $
171: */
172: public interface SelfListener {
173: void onInvocation(Method method, Object visitable);
174: }
175:
176: public interface Listener {
177: void onInvocationRegistration(Object target, Method method);
178:
179: void onTargetRegistration(Object target);
180:
181: void onFilterRegistration(Method filter, Method target);
182:
183: void noInvocationsWarning(Object target);
184:
185: void onInvocation(Object target, Method method, Object visitable);
186:
187: void onVisit(Object target);
188:
189: void onLeave(Object target);
190: }
191:
192: /**
193: * If target implements this insterface then it is used to
194: * filter invocations to other targets. approve() method(s) of
195: * this target, if any, will be invoked before invocation of
196: * visit()/leave() methods of filtered target.
197: *
198: * @author Pavel Vlasov
199: * @version $Revision: 1.9 $
200: */
201: public interface Filter {
202: Collection getTargets();
203: }
204:
205: public interface Stats {
206: long getVisits();
207:
208: long getInvocations();
209:
210: void reset();
211: }
212:
213: private static class StatsImpl implements Stats {
214: private long visits;
215: private long invocations;
216:
217: public long getVisits() {
218: return visits;
219: }
220:
221: public long getInvocations() {
222: return invocations;
223: }
224:
225: public void reset() {
226: visits = 0;
227: invocations = 0;
228: }
229:
230: public String toString() {
231: return "[" + Thread.currentThread() + "] " + visits
232: + " visits, " + invocations + " invocations";
233: }
234:
235: synchronized void addVisit() {
236: visits++;
237: }
238:
239: synchronized void addInvocation() {
240: invocations++;
241: }
242:
243: }
244:
245: private int handlerCounter;
246: private StatsImpl stats = new StatsImpl();
247: private ThreadLocal threadStats = new ThreadLocal() {
248: protected Object initialValue() {
249: return new StatsImpl();
250: }
251: };
252:
253: private class InvocationHandler implements Comparable {
254: boolean active = true;
255: Method method;
256: Object object;
257: int position = handlerCounter++;
258: int order;
259: List filtersList = new LinkedList();
260: ApproveInvocationHandler[] filters;
261: boolean returnsValue;
262:
263: public String toString() {
264: return getClass().getName() + "[" + order + " " + method
265: + "]";
266: }
267:
268: void addFilter(ApproveInvocationHandler filter) {
269: filtersList.add(filter);
270: Iterator it = listeners.iterator();
271: while (it.hasNext()) {
272: ((Listener) it.next()).onFilterRegistration(
273: filter.method, method);
274: }
275: }
276:
277: void commitFilters() {
278: filters = null;
279: if (!filtersList.isEmpty()) {
280: filters = (ApproveInvocationHandler[]) filtersList
281: .toArray(new ApproveInvocationHandler[filtersList
282: .size()]);
283: }
284: }
285:
286: InvocationHandler(Method method, Object object) {
287: Iterator it = listeners.iterator();
288: while (it.hasNext()) {
289: ((Listener) it.next()).onInvocationRegistration(object,
290: method);
291: }
292:
293: this .method = method;
294: this .object = object;
295: this .returnsValue = !(void.class.equals(method
296: .getReturnType())
297: || boolean.class.equals(method.getReturnType()) || Boolean.class
298: .equals(method.getReturnType()));
299:
300: addClassOfInterest(method.getParameterTypes()[0]);
301:
302: if (object instanceof OrderedTarget) {
303: Integer ret = ((OrderedTarget) object).getOrder();
304: if (ret != null) {
305: order = ret.intValue();
306: }
307: }
308:
309: }
310:
311: Object invoke(Object arg) {
312: if (active) {
313: for (int i = 0; filters != null && i < filters.length; i++) {
314: if (Boolean.FALSE.equals(filters[i].invoke(arg))) {
315: return null;
316: }
317: }
318: try {
319: Iterator it = listeners.iterator();
320: while (it.hasNext()) {
321: ((Listener) it.next()).onInvocation(object,
322: method, arg);
323: }
324: stats.addInvocation();
325: ((StatsImpl) threadStats.get()).addInvocation();
326: if (object instanceof SelfListener) {
327: ((SelfListener) object).onInvocation(method,
328: arg);
329: }
330:
331: if (!method.getParameterTypes()[0].isInstance(arg)) {
332: System.err.println(method + ", "
333: + arg.getClass());
334: }
335:
336: return method.invoke(object, new Object[] { arg });
337: } catch (IllegalArgumentException e) {
338: if (exceptionSink == null) {
339: e.printStackTrace();
340: } else {
341: exceptionSink.consume(DispatchingVisitor.this ,
342: object, method, arg, e);
343: }
344: } catch (IllegalAccessException e) {
345: if (exceptionSink == null) {
346: e.printStackTrace();
347: } else {
348: exceptionSink.consume(DispatchingVisitor.this ,
349: object, method, arg, e);
350: }
351: } catch (InvocationTargetException e) {
352: Throwable targetException = e.getTargetException();
353: if (targetException instanceof Error) {
354: throw (Error) targetException;
355: } else if (exceptionSink == null) {
356: targetException.printStackTrace();
357: } else {
358: exceptionSink
359: .consume(
360: DispatchingVisitor.this ,
361: object,
362: method,
363: arg,
364: targetException instanceof Exception ? (Exception) targetException
365: : e);
366: }
367: }
368: }
369: return null;
370: }
371:
372: public int compareTo(Object o) {
373: if (this == o) {
374: return 0;
375: }
376: InvocationHandler ih = (InvocationHandler) o;
377: if (order == ih.order) {
378: if (position == ih.position) {
379: Class c = method.getParameterTypes()[0];
380: Class ihc = ih.method.getParameterTypes()[0];
381: if (c.equals(ihc)) {
382: return method.getDeclaringClass().getName()
383: .compareTo(
384: ih.method.getDeclaringClass()
385: .getName());
386: }
387: if (c.isAssignableFrom(ihc)) {
388: return -1;
389: } else if (ihc.isAssignableFrom(c)) {
390: return 1;
391: } else {
392: int idc = inheritanceDepth(c);
393: int ihidc = inheritanceDepth(ihc);
394: if (idc == ihidc) {
395: return method
396: .getDeclaringClass()
397: .getName()
398: .compareTo(
399: ih.method
400: .getDeclaringClass()
401: .getName());
402: }
403: return idc - ihidc;
404: }
405: }
406: return position - ih.position;
407: }
408: return order == ih.order ? 0 : order > ih.order ? 1 : -1;
409: }
410: }
411:
412: private int inheritanceDepth(Class clazz) {
413: if (clazz == null || Object.class.equals(clazz)) {
414: return 0;
415: }
416: int ret = 0;
417: ret = Math.max(ret, inheritanceDepth(clazz.getSuperclass()));
418: for (int i = 0, j = clazz.getInterfaces().length; i < j; i++) {
419: ret = Math.max(ret,
420: inheritanceDepth(clazz.getInterfaces()[i]));
421: }
422: return ret + 1;
423: }
424:
425: private class ApproveInvocationHandler extends InvocationHandler {
426: private Class parameterType;
427:
428: ApproveInvocationHandler(Method method, Object object) {
429: super (method, object);
430: parameterType = method.getParameterTypes()[0];
431: }
432:
433: Map results = new IdentityHashMap();
434:
435: Object invoke(Object arg) {
436: if (!parameterType.isInstance(arg)) {
437: return null; // Incompatible parameter.
438: }
439:
440: if (results.containsKey(arg)) {
441: return results.get(arg);
442: }
443: Object ret = super .invoke(arg);
444: results.put(arg, ret);
445: return ret;
446: }
447:
448: void remove(Object key) {
449: results.remove(key);
450: }
451: }
452:
453: private ThreadLocal visitorStackTL = new ThreadLocal() {
454:
455: protected Object initialValue() {
456: return new VisitorStack();
457: }
458: };
459:
460: private int size;
461:
462: public VisitorStack getVisitorStack() {
463: return (VisitorStack) visitorStackTL.get();
464: }
465:
466: public boolean visit(Object target) {
467: getVisitorStack().push(target);
468: Iterator lit = listeners.iterator();
469: while (lit.hasNext()) {
470: ((Listener) lit.next()).onVisit(target);
471: }
472:
473: if (handlerList.isEmpty()) {
474: return false;
475: }
476:
477: stats.addVisit();
478: ((StatsImpl) threadStats.get()).addVisit();
479:
480: if (target != null) {
481: // Verify target first.
482: Iterator it = getVerifyHandlers(target.getClass())
483: .iterator();
484: while (it.hasNext()) {
485: if (Boolean.FALSE
486: .equals(((InvocationHandler) it.next())
487: .invoke(target))) {
488: return false;
489: }
490: }
491:
492: // Invoke visit methods.
493: it = getHandlers(target.getClass()).iterator();
494: while (it.hasNext()) {
495: InvocationHandler ih = (InvocationHandler) it.next();
496: Object ret = ih.invoke(target);
497: if (Boolean.FALSE.equals(ret)) {
498: return false;
499: }
500:
501: if (ret != null && ih.returnsValue) {
502: processReturnValue(ih.object, ih.method, target,
503: ret);
504: }
505: }
506: }
507:
508: return true;
509: }
510:
511: public Collection getTargets() {
512: return targets;
513: }
514:
515: /**
516: * Passes return values back to visitor.
517: * Arrays and collections are iterated and individual elements are passed to the visitor.
518: * Override this method to process return values in a different way.
519: * @param target - Object which method was invoked
520: * @param method - Method which was invoked
521: * @param argument - Method argument
522: * @param returnValue - Return value
523: */
524: protected void processReturnValue(Object target, Method method,
525: Object argument, Object returnValue) {
526: if (returnValue instanceof Collection) {
527: Iterator it = ((Collection) returnValue).iterator();
528: while (it.hasNext()) {
529: VisitableBase.object2visitor(it.next(), this );
530: }
531: } else if (returnValue.getClass().isArray()) {
532: for (int i = 0, l = Array.getLength(returnValue); i < l; i++) {
533: VisitableBase.object2visitor(Array.get(returnValue, i),
534: this );
535: }
536: } else {
537: VisitableBase.object2visitor(returnValue, this );
538: }
539: }
540:
541: /**
542: *
543: * @return total number of handlers
544: */
545: public int size() {
546: return size;
547: }
548:
549: public Collection getClassesOfInterest() {
550: return unmodifiableClassesOfInterest;
551: }
552:
553: private void addClassOfInterest(Class clazz) {
554: Iterator it = classesOfInterest.iterator();
555: while (it.hasNext()) {
556: Class cls = (Class) it.next();
557: if (cls.isAssignableFrom(clazz)) {
558: return;
559: }
560:
561: if (clazz.isAssignableFrom(cls)) {
562: it.remove();
563: }
564: }
565: classesOfInterest.add(clazz);
566: }
567:
568: /**
569: * @param targets
570: * @param exceptionSink
571: */
572: public DispatchingVisitor(Collection targets,
573: VisitorExceptionSink exceptionSink, Listener listener) {
574: if (listener != null) {
575: listeners.add(listener);
576: }
577: this .targets = Collections.unmodifiableList(new LinkedList(
578: targets));
579: this .exceptionSink = exceptionSink;
580: Iterator it = targets.iterator();
581: while (it.hasNext()) {
582: Object target = it.next();
583: if (target instanceof DispatcherAware) {
584: ((DispatcherAware) target).setDispatcher(this );
585: }
586:
587: Iterator lit = listeners.iterator();
588: while (lit.hasNext()) {
589: ((Listener) lit.next()).onTargetRegistration(target);
590: }
591:
592: boolean hasInvocations = false;
593: Method[] methods = target.getClass().getMethods();
594: for (int i = 0; i < methods.length; i++) {
595: if (methods[i].getParameterTypes().length == 1) {
596: Class returnType = methods[i].getReturnType();
597: if ("visit".equals(methods[i].getName())
598: && (void.class.equals(returnType) || boolean.class
599: .equals(returnType))) {
600: handlerList.add(new InvocationHandler(
601: methods[i], target));
602: hasInvocations = true;
603: } else if ("leave".equals(methods[i].getName())
604: && void.class.equals(returnType)) {
605: leaveHandlerList.add(new InvocationHandler(
606: methods[i], target));
607: hasInvocations = true;
608: } else if (boolean.class.equals(returnType)
609: && "verify".equals(methods[i].getName())) {
610: verifyHandlerList.add(new InvocationHandler(
611: methods[i], target));
612: hasInvocations = true;
613: } else if (target instanceof Filter
614: && !((Filter) target).getTargets()
615: .isEmpty()
616: && "approve".equals(methods[i].getName())
617: && boolean.class.equals(returnType)) {
618: filterHandlerList
619: .add(new ApproveInvocationHandler(
620: methods[i], target));
621: hasInvocations = true;
622: }
623: }
624: }
625:
626: if (!hasInvocations) {
627: lit = listeners.iterator();
628: while (lit.hasNext()) {
629: ((Listener) lit.next())
630: .noInvocationsWarning(target);
631: }
632: }
633:
634: if (target instanceof Listener) {
635: listeners.add(target);
636: }
637: }
638:
639: assignFilters(handlerList);
640: assignFilters(leaveHandlerList);
641:
642: size = handlerList.size() + leaveHandlerList.size();
643: }
644:
645: /**
646: *
647: */
648: private void assignFilters(Collection handlers) {
649: Iterator it = handlers.iterator();
650: while (it.hasNext()) {
651: InvocationHandler handler = (InvocationHandler) it.next();
652: Iterator fit = filterHandlerList.iterator();
653: while (fit.hasNext()) {
654: ApproveInvocationHandler approveHandler = (ApproveInvocationHandler) fit
655: .next();
656: if (((Filter) approveHandler.object).getTargets()
657: .contains(handler.object)) {
658: handler.addFilter(approveHandler);
659: }
660: }
661: handler.commitFilters();
662: }
663: }
664:
665: public DispatchingVisitor(Collection targets,
666: VisitorExceptionSink exceptionSink) {
667: this (targets, exceptionSink, null);
668: }
669:
670: /**
671: * @param target
672: * @param exceptionSink
673: */
674: public DispatchingVisitor(Object target,
675: VisitorExceptionSink exceptionSink) {
676: this (Arrays.asList(new Object[] { target }), exceptionSink);
677: }
678:
679: public DispatchingVisitor(Object target,
680: VisitorExceptionSink exceptionSink, Listener listener) {
681: this (Arrays.asList(new Object[] { target }), exceptionSink,
682: listener);
683: }
684:
685: /**
686: * @return Returns visitor statistics.
687: */
688: public Stats getStats() {
689: return stats;
690: }
691:
692: public Stats getThreadStats() {
693: Stats ret = (Stats) threadStats.get();
694: return ret;
695: }
696:
697: public void leave(Object target) {
698: Iterator lit = listeners.iterator();
699: while (lit.hasNext()) {
700: ((Listener) lit.next()).onLeave(target);
701: }
702:
703: if (target != null) {
704: Iterator it = getLeaveHandlers(target.getClass())
705: .iterator();
706: while (it.hasNext()) {
707: InvocationHandler ih = (InvocationHandler) it.next();
708: Object ret = ih.invoke(target);
709:
710: if (ret != null && ih.returnsValue) {
711: processReturnValue(ih.object, ih.method, target,
712: ret);
713: }
714: }
715:
716: it = getFilterHandlers(target.getClass()).iterator();
717: while (it.hasNext()) {
718: ((ApproveInvocationHandler) it.next()).remove(target);
719: }
720: }
721: getVisitorStack().pop(target);
722: }
723:
724: /**
725: * Removes object from targets collection.
726: * @param target
727: */
728: public void remove(Object target) {
729: Iterator it = handlerList.iterator();
730: while (it.hasNext()) {
731: InvocationHandler h = (InvocationHandler) it.next();
732: if (h.object == target) {
733: h.active = false;
734: }
735: }
736:
737: it = leaveHandlerList.iterator();
738: while (it.hasNext()) {
739: InvocationHandler h = (InvocationHandler) it.next();
740: if (h.object == target) {
741: h.active = false;
742: }
743: }
744:
745: it = verifyHandlerList.iterator();
746: while (it.hasNext()) {
747: InvocationHandler h = (InvocationHandler) it.next();
748: if (h.object == target) {
749: h.active = false;
750: }
751: }
752: }
753:
754: /**
755: * Passes executionContext for visiting.
756: * Subclasses can override this method and
757: * queue object for execution and invoke
758: * accept/visit in a different thread.
759: */
760: public void execute(Object executionContext) {
761: if (executionContext instanceof Visitable) {
762: ((Visitable) executionContext).accept(this);
763: } else {
764: visit(executionContext);
765: }
766: }
767: }
|