001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.modules.uml.core.eventframework;
043:
044: import java.util.ArrayList;
045: import java.util.HashMap;
046: import java.util.Vector;
047: import org.dom4j.Document;
048: import org.dom4j.Node;
049: import java.util.List; //import org.apache.xpath.XPathAPI;
050:
051: import org.netbeans.modules.uml.core.support.umlsupport.IResultCell;
052: import org.netbeans.modules.uml.core.support.umlsupport.ResultCell;
053: import org.netbeans.modules.uml.core.support.umlsupport.XMLManip;
054: import org.netbeans.modules.uml.core.support.umlsupport.Log;
055:
056: public class EventDispatcher implements IEventDispatcher {
057: private static HashMap<String, Class> payloadCache = new HashMap<String, Class>();
058: private boolean m_preventAllEvents = false;
059: private Vector<IEventContext> m_ContextStack = new Vector<IEventContext>();
060: private EventManager<IEventFrameworkEventsSink> m_manager = new EventManager<IEventFrameworkEventsSink>();
061: private EventFunctor m_CancelFunc = null;
062: private EventFunctor m_PrePopFunc = null;
063: private EventFunctor m_PostPopFunc = null;
064: private EventFunctor m_PrePushFunc = null;
065: private EventFunctor m_PostPushFunc = null;
066:
067: /**
068: * Registers an event sink to handle frameework events.
069: */
070: public void registerForEventFrameworkEvents(
071: IEventFrameworkEventsSink handler) {
072: try {
073: m_manager.addListener(handler, null);
074: m_manager.setDispatcher(this );
075: } catch (Exception e) {
076: }
077: }
078:
079: /**
080: * Removes a sink listening for framework events.
081: */
082: public void revokeEventFrameworkSink(
083: IEventFrameworkEventsSink handler) {
084: m_manager.removeListener(handler);
085: }
086:
087: /**
088: *
089: * Fired right after a context has been pushed on this dispatcher.
090: *
091: * @param pContext[in] The context pushed
092: * @param payload[in] The payload
093: *
094: * @return HRESULT
095: *
096: */
097: public void fireEventContextPushed(IEventContext pContext,
098: IEventPayload payLoad) {
099:
100: if (validateEvent("EventContextPushed", pContext)) {
101: IResultCell cell = prepareResultCell(payLoad);
102: if (m_PostPushFunc == null) {
103: m_PostPushFunc = new EventFunctor(
104: "org.netbeans.modules.uml.core.eventframework.IEventFrameworkEventsSink",
105: "onEventContextPushed");
106: }
107: Object[] parms = new Object[2];
108: parms[0] = pContext;
109: parms[1] = cell;
110: m_PostPushFunc.setParameters(parms);
111: m_manager.notifyListeners(m_PostPushFunc);
112: }
113: }
114:
115: /**
116: *
117: * Fired before an EventContext is pushed onto this Dispatcher.
118: *
119: * @param pContext[in] The context about to be pushed
120: * @param payload[in] The EventPayload to include with the event dispatch. Can be 0.
121: * @param proceed[out] true if the event was fully dispatched, else
122: * false if a listener cancelled full dispatch.
123: *
124: * @return HRESULT
125: *
126: */
127: public boolean firePreEventContextPushed(IEventContext pContext,
128: IEventPayload payLoad) {
129: boolean proceed = true;
130:
131: //proceed = validateEvent("PreEventContextPushed", pContext);
132:
133: if (validateEvent("PreEventContextPushed", pContext) == true) {
134: IResultCell cell = prepareResultCell(payLoad);
135: if (m_PrePushFunc == null) {
136: m_PrePushFunc = new EventFunctor(
137: "org.netbeans.modules.uml.core.eventframework.IEventFrameworkEventsSink",
138: "onPreEventContextPushed");
139: }
140: Object[] parms = new Object[2];
141: parms[0] = pContext;
142: parms[1] = cell;
143: m_PrePushFunc.setParameters(parms);
144: m_manager
145: .notifyListenersWithQualifiedProceed(m_PrePushFunc);
146:
147: proceed = cell.canContinue();
148: }
149:
150: return proceed;
151: }
152:
153: /**
154: *
155: * Fired before an EventContext is popped from this Dispatcher.
156: *
157: * @param pContext[in] The context about to be popped
158: * @param payload[in] The EventPayload to include with the event dispatch. Can be 0.
159: * @param proceed[out] true if the event was fully dispatched, else
160: * false if a listener cancelled full dispatch.
161: *
162: * @return HRESULT
163: *
164: */
165: public boolean firePreEventContextPopped(IEventContext pContext,
166: IEventPayload payLoad) {
167: boolean proceed = true;
168:
169: if (validateEvent("PreEventContextPopped", pContext)) {
170: IResultCell cell = prepareResultCell(payLoad);
171: if (m_PrePopFunc == null) {
172: m_PrePopFunc = new EventFunctor(
173: "org.netbeans.modules.uml.core.eventframework.IEventFrameworkEventsSink",
174: "onPreEventContextPopped");
175: }
176: Object[] parms = new Object[2];
177: parms[0] = pContext;
178: parms[1] = cell;
179: m_PrePopFunc.setParameters(parms);
180: m_manager.notifyListenersWithQualifiedProceed(m_PrePopFunc);
181: proceed = cell.canContinue();
182:
183: /* TwoParmFunctor< IEventFrameworkEventsSink, IEventContext*, IResultCell* > func( 0,
184: &IEventFrameworkEventsSink::OnPreEventContextPopped,
185: pContext,
186: cell );
187: ResultCellFunctor< IEventFrameworkEventsSink > cellFunc( 0, cell, &func );
188:
189: _VH( m_FrameworkSink.NotifyListenersWithQualifiedProceed( cellFunc ));
190: _VH( cell->get_Continue( proceed )); */
191: }
192: return proceed;
193: }
194:
195: /**
196: *
197: * Fired right after a context has been popped from this dispatcher.
198: *
199: * @param pContext[in] The context popped
200: * @param payload[in] The payload
201: *
202: * @return HRESULT
203: *
204: */
205: public void fireEventContextPopped(IEventContext pContext,
206: IEventPayload payLoad) {
207:
208: if (validateEvent("EventContextPopped", pContext)) {
209: IResultCell cell = prepareResultCell(payLoad);
210: if (m_PostPopFunc == null) {
211: m_PostPopFunc = new EventFunctor(
212: "org.netbeans.modules.uml.core.eventframework.IEventFrameworkEventsSink",
213: "onEventContextPopped");
214: }
215: Object[] parms = new Object[2];
216: parms[0] = pContext;
217: parms[1] = cell;
218: m_PostPopFunc.setParameters(parms);
219: m_manager.notifyListeners(m_PostPopFunc);
220:
221: /* TwoParmFunctor< IEventFrameworkEventsSink,
222: IEventContext*,
223: IResultCell* > func ( 0,
224: &IEventFrameworkEventsSink::OnEventContextPopped,
225: pContext,
226: cell );
227: _VH( m_FrameworkSink.NotifyListeners( func )); */
228: }
229: }
230:
231: /**
232: *
233: * Fired when a dispatch of a particular event is cancelled by a listener.
234: *
235: * @param pListeners[in] The collection of listeners that were already dispatched to.
236: * @param listenerWhoCancelled[in] The listener that cancelled the dispatch
237: * @param payload[in] EventPayload
238: *
239: * @return HRESULT
240: *
241: */
242: public void fireEventDispatchCancelled(Object[] pListeners,
243: Object listenerWhoCancelled, IEventPayload payLoad) {
244:
245: ArrayList<Object> var = new ArrayList<Object>();
246:
247: // Collect the additional parameters for the EventContext to use
248: // during the validation pass of the trigger
249: var.add(pListeners);
250: var.add(listenerWhoCancelled);
251:
252: //prepareVariant( collection, var ));
253:
254: if (validateEvent("EventEventDispatchCancelled", var)) {
255: IResultCell cell = prepareResultCell(payLoad);
256:
257: if (m_CancelFunc == null) {
258: m_CancelFunc = new EventFunctor(
259: "org.netbeans.modules.uml.core.eventframework.IEventFrameworkEventsSink",
260: "onEventDispatchCancelled");
261: }
262: Object[] parms = new Object[3];
263: parms[0] = pListeners;
264: parms[1] = listenerWhoCancelled;
265: parms[2] = cell;
266: m_CancelFunc.setParameters(parms);
267: m_manager.notifyListeners(m_CancelFunc);
268: }
269: }
270:
271: /**
272: *
273: * Establishes a new EventContext of the given name on the internal
274: * stack of EventContext objects.
275: *
276: * @param context[in] The name of the context to push on the stack
277: # @param pVal[out] The context that was created, else 0.
278: *
279: * @return HRESULT
280: *
281: */
282: public void pushEventContext(String name) {
283: IEventContext con = createEventContext(name);
284: if (con != null) {
285: pushEventContext3(con);
286: }
287: }
288:
289: /**
290: *
291: * Establishes a new EventContext of the given name on the internal
292: * stack of EventContext objects.
293: *
294: * @param context[in] The name of the context to push on the stack
295: *
296: * @return HRESULT
297: *
298: */
299: public IEventContext pushEventContext2(String context) {
300: IEventContext con = createEventContext(context);
301: if (con != null) {
302: pushEventContext3(con);
303: }
304: return con;
305: }
306:
307: /**
308: *
309: * Establishes the passed in EventContext as the current context.
310: *
311: * @param pContext[in] The EventContext to push on the stack
312: *
313: * @return HRESULT
314: *
315: */
316: public void pushEventContext3(IEventContext pContext) {
317: boolean proceed = true;
318: IEventPayload payload = createPayload("PreEventContextPushed");
319: proceed = firePreEventContextPushed(pContext, payload);
320: if (proceed) {
321: m_ContextStack.add(pContext);
322: payload = createPayload("EventContextPushed");
323: fireEventContextPushed(pContext, payload);
324: }
325: }
326:
327: /**
328: *
329: * Removes the top context on the stack.
330: *
331: * @return S_OK
332: *
333: */
334: public void popEventContext() {
335: if (m_ContextStack.size() > 0) {
336: internalPopEventContext();
337: }
338: }
339:
340: /**
341: *
342: * Pops the current context off our internal stack, firing appropriate events.
343: *
344: * @param context[out] The event context popped
345: *
346: * @return HRESULT
347: *
348: */
349: private IEventContext internalPopEventContext() {
350: IEventContext retContext = null;
351: boolean proceed = true;
352: IEventPayload payload = createPayload("PreEventContextPopped");
353: if (m_ContextStack.size() > 0) {
354: IEventContext curContext = m_ContextStack.lastElement();
355: proceed = firePreEventContextPopped(curContext, payload);
356: if (proceed) {
357: m_ContextStack.remove(curContext);
358: payload = createPayload("EventContextPopped");
359: fireEventContextPopped(curContext, payload);
360: retContext = curContext;
361: }
362: }
363: return retContext;
364: }
365:
366: /**
367: *
368: * Pops the current event context off the stack and returns it.
369: *
370: * @param pContext[out] The context just popped
371: *
372: * @return HRESULT
373: *
374: */
375: public IEventContext popEventContext2() {
376: IEventContext context = null;
377: if (m_ContextStack.size() > 0) {
378: context = internalPopEventContext();
379: }
380: return context;
381: }
382:
383: /**
384: *
385: * Retrieves the current context on this dispatcher.
386: *
387: * @param pContext[out] The current EventContext
388: *
389: * @return HRESULT
390: *
391: */
392: public IEventContext getCurrentContext() {
393: IEventContext context = null;
394: if (m_ContextStack.size() > 0) {
395: context = m_ContextStack.lastElement();
396: }
397: return context;
398: }
399:
400: /**
401: *
402: * Retrieves the name of the current context on this dispatcher.
403: *
404: * @param pContext[out] The current EventContext
405: *
406: * @return HRESULT
407: *
408: */
409: public String getCurrentContextName() {
410: String name = null;
411: if (m_ContextStack.size() > 0) {
412: IEventContext context = m_ContextStack.lastElement();
413: name = context.getName();
414: }
415: return name;
416: }
417:
418: /**
419: *
420: * Removes an EventContext by name.
421: *
422: * @param name[in] The name of the context to remove
423: *
424: * @return HRESULT
425: *
426: */
427: public void removeEventContextByName(String name) {
428: int count = m_ContextStack.size();
429: if (name.length() > 0 && count > 0) {
430: for (int i = 0; i < count; i++) {
431: IEventContext context = m_ContextStack.elementAt(i);
432: String contName = context.getName();
433: if (contName.equals(name)) {
434: internalEraseEventContext(context);
435: break;
436: }
437: }
438: }
439: }
440:
441: /**
442: *
443: * Erases a context off our internal stack, firing appropriate events.
444: *
445: * @param iter[in] The iterator to be used to erase the context from the stack
446: *
447: * @return HRESULT
448: *
449: */
450: private void internalEraseEventContext(IEventContext context) {
451: boolean proceed = true;
452: IEventPayload payload = createPayload("PreEventContextPopped");
453: if (m_ContextStack.size() > 0) {
454: proceed = firePreEventContextPopped(context, payload);
455: if (proceed) {
456: m_ContextStack.remove(context);
457: payload = createPayload("EventContextPopped");
458: fireEventContextPopped(context, payload);
459: }
460: }
461: }
462:
463: /**
464: *
465: * Removes the context that contains a filter with the passed in ID.
466: *
467: * @param filterID[in] The ID to match against
468: *
469: * @return HRESULT
470: *
471: */
472: public void removeEventContextByFilterID(String filterID) {
473: int count = m_ContextStack.size();
474: if (filterID.length() > 0 && count > 0) {
475: for (int i = 0; i < count; i++) {
476: IEventContext context = m_ContextStack.elementAt(i);
477: IEventFilter filter = context.getFilter();
478: if (filter != null) {
479: String curID = filter.getFilterID();
480: if (curID != null && curID.equals(filterID)) {
481: internalEraseEventContext(context);
482: break;
483: }
484: }
485: }
486: }
487: }
488:
489: /**
490: *
491: * Creates the appropriate payload as dictated by the mechanism file.
492: *
493: * @param triggerName[in] Name of the trigger by which the appropriate payload can
494: * be determined.
495: * @param payLoad[out] The new payload
496: *
497: * @return HRESULT
498: *
499: */
500: public IEventPayload createPayload(String triggerName) {
501: IEventPayload payload = null;
502: try {
503: if (!m_preventAllEvents) {
504: if (payloadCache.containsKey(triggerName)) {
505: Class pc = payloadCache.get(triggerName);
506: if (pc != null) {
507: try {
508: return (IEventPayload) pc.newInstance();
509: } catch (Exception e) {
510: Log.stackTrace(e);
511: }
512: }
513: return null;
514: }
515:
516: boolean resolved = false;
517: Document mech = getMechanism();
518: if (mech != null) {
519: String query = "//EMBT:TriggerPoint[@name='";
520: query += triggerName;
521: query += "']";
522: org.dom4j.Node node = XMLManip.selectSingleNode(
523: mech, query);
524: if (node != null) {
525: String value = XMLManip.getAttributeValue(node,
526: "payLoad");
527: // It is very valid to NOT have a payLoad attribute
528: if (value != null && value.length() > 0) {
529: try {
530:
531: Class c = Class.forName(value);
532: payloadCache.put(triggerName, c);
533: resolved = true;
534: payload = (IEventPayload) c
535: .newInstance();
536: } catch (Exception e) {
537: }
538: }
539: }
540: }
541: if (!resolved && payload == null)
542: payloadCache.put(triggerName, null);
543: }
544: } catch (Exception e) {
545: }
546: return payload;
547: }
548:
549: public boolean getPreventAllEvents() {
550: return m_preventAllEvents;
551: }
552:
553: public void setPreventAllEvents(boolean value) {
554: m_preventAllEvents = value;
555: }
556:
557: /**
558: *
559: * Returns how many listeners are associated with this dispatcher
560: *
561: * @param pVal[out] The number of listeners on this dispatcher
562: *
563: * @return HRESULT
564: *
565: */
566: public int getNumRegisteredSinks() {
567: return m_manager.getNumListeners();
568: }
569:
570: // /**
571: // *
572: // * Validates the trigger that is about to trigger the event, and returns a prepared
573: // * result cell to go with the event, if the trigger validated.
574: // *
575: // * @param triggerName[in] The name of the trigger causing the event
576: // * @param var[in] The VARIANT to pass to the EventContext for event validation purposes
577: // * @param payload[in] The EventPayload to include with the result cell. Can be 0
578: // * @param cell[out] The IResultCell, else 0 if the event did not validate
579: // *
580: // * @return true if the trigger validated, and the resultant event can proceed, else false.
581: // *
582: // */
583: // protected boolean validateAndPrepareResultCell( String triggerName, Object var, IEventPayload payload, IResultCell cell)
584: // {
585: // boolean validated = false;
586: //
587: // cell = null;
588: // validated = validateEvent( triggerName, var ) ? true : false;
589: //
590: // if( validated )
591: // {
592: // cell = prepareResultCell( payload );
593: // }
594: // return validated;
595: // }
596:
597: /**
598: * Creates and prepares the IResultCell object that will be included with the event.
599: *
600: * @param payload[in] The payload to include with the event cell. Can be 0
601: * @param cell[out] The ResultCell
602: *
603: * @return HRESULT
604: */
605: protected IResultCell prepareResultCell(IEventPayload payload) {
606: IResultCell cell = new ResultCell();
607:
608: if (payload != null) {
609: // Set the payLoad on the ResultCell
610: cell.setContextData(payload);
611: }
612: return cell;
613: }
614:
615: /**
616: * Validates the trigger, preventing or allowing the resultant event
617: * to occur or not.
618: *
619: * @param triggerName[in] Name of the trigger currently getting pulled
620: * @param payLoad[in] The Variant holding the payload of the event
621: * @param proceed[out] true if the event is allowed, else false
622: *
623: * @return HRESULT
624: */
625:
626: protected boolean validateEvent(String triggerName, Object payLoad) {
627: boolean valid = true;
628:
629: if (m_preventAllEvents) {
630: valid = false;
631: } else {
632: if (m_ContextStack.size() > 0) {
633: IEventContext curContext = m_ContextStack.lastElement();
634: valid = curContext.validateEvent(triggerName, payLoad);
635: }
636: }
637: return valid;
638: }
639:
640: /**
641: * Retrieves the prog id for the default EventContext implementation.
642: * It does this by retrieving the setting of the eventContextProgID
643: * xml attribute off the EventFramework element itself.
644: *
645: * @return The progID. If this comes back empty, something is
646: * very wrong.
647: */
648: private String retrieveDefaultContextProgID() {
649: String progID = null;
650:
651: Document mech = getMechanism();
652: if (mech != null) {
653:
654: List list = mech.selectNodes("EventMechanism");
655: if (list != null && list.size() > 0) {
656: Node node = (Node) list.get(0);
657: progID = XMLManip.getAttributeValue(node,
658: "eventContextProgID");
659: }
660: }
661: return progID;
662: }
663:
664: /**
665: * Creates the EventContext object that corresponds to the passed
666: * in context state.
667: *
668: * @param context[in] The name of the Context to create
669: * @param pVal[out] The created Context
670: *
671: * @return HRESULT
672: */
673: protected IEventContext createEventContext(String sContext) {
674: IEventContext context = null;
675: try {
676: org.dom4j.Node node = null;
677: String contextProgID = retrieveContextProgID(sContext, node);
678:
679: if (contextProgID.length() > 0) {
680: context = (IEventContext) Class.forName(contextProgID)
681: .newInstance();
682:
683: if (node != null) {
684: context.setDom4JNode(node);
685: } else {
686: context.setName(sContext);
687: }
688: }
689: } catch (Exception err) {
690: }
691: return context;
692: }
693:
694: /**
695: * Retrieves the ProgID of the EventContext object.
696: *
697: * @return The progID
698: */
699: private String retrieveContextProgID(String sContext,
700: org.dom4j.Node node) {
701: String progID = null;
702: try {
703: Document mech = getMechanism();
704: if (mech != null) {
705: // First check to see if the specific context has a progID associated with it.
706: // If it does, use that. If it doesn't, pull the progID off the EventFramework's
707: // eventContextProgID attribute
708: String query = "//EMBT:EventContext[@name=\"";
709: query += sContext;
710: query += "\"]";
711:
712: node = XMLManip.selectSingleNode(mech, query);
713: if (node != null) {
714: progID = XMLManip.getAttributeValue(node, "progID");
715: }
716: if (progID == null || progID.length() == 0) {
717: progID = retrieveDefaultContextProgID();
718: }
719: }
720: } catch (Exception e) {
721: }
722: return progID;
723: }
724:
725: /**
726: * Retrieves the document that represents the event
727: * mechanism framework configuration file.
728: *
729: * @param doc[out] The document, else 0
730: *
731: * @return HRESULT
732: */
733: protected Document getMechanism() {
734: Document doc = null;
735:
736: try {
737: EventMechanism mech = EventMechanism.instance();
738: doc = mech.mechanism();
739: } catch (Exception err) {
740: }
741: return doc;
742: }
743:
744: protected Object prepareVariant(Vector<Object> col) {
745: if (col != null && col.size() > 0) {
746: return col;
747: }
748: return null;
749: }
750: }
|