All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.adobe.xfa.EventManager Maven / Gradle / Ivy

The newest version!
/*
 * ADOBE CONFIDENTIAL
 *
 * Copyright 2005 Adobe Systems Incorporated All Rights Reserved.
 *
 * NOTICE: All information contained herein is, and remains the property of
 * Adobe Systems Incorporated and its suppliers, if any. The intellectual and
 * technical concepts contained herein are proprietary to Adobe Systems
 * Incorporated and its suppliers and may be covered by U.S. and Foreign
 * Patents, patents in process, and are protected by trade secret or copyright
 * law. Dissemination of this information or reproduction of this material
 * is strictly forbidden unless prior written permission is obtained from
 * Adobe Systems Incorporated.
 */
package com.adobe.xfa;


import java.util.ArrayList;
import java.util.Collection;
import java.util.List;


import com.adobe.xfa.ut.ExFull;
import com.adobe.xfa.ut.MsgFormatPos;
import com.adobe.xfa.ut.ResId;
import com.adobe.xfa.ut.StringUtils;
import com.adobe.xfa.ut.trace.Trace;


/**
 *
 * @exclude from published api.
 */
public class EventManager {

//	// CalloutEntry class
//	
//	private static final class CalloutEntry {
//		public boolean mbOnlyIfDispatcherExists;
//
//		public int mnCalloutID;
//
//		public Node mNodeImpl;
//	}

	
	/**
	 * EventID class
	 * @exclude from public api.
	 */
	public static final class EventID { // list of event names (index is ID)
		
//		public List mCallouts; // (normally null) list of callouts attached to this event

		public final String msEventName;

		/** unInitialized registered nodes and their events */
		public final List mUnInitializedEvents = new ArrayList(); 
		
		/** has an event ever been registered that listens to descendents? */
		public boolean mbListenToDescendentsRegistered;
		
		public boolean mbDispatcherOrCalloutRegistered;
		
		public EventID(String sEventName) {
			msEventName = sEventName;
		}
	}
	
	/**
	 * Represents a collection of Dispatchers associated with a particular Obj.
	 * In the C++ implementation, the SortedArray used a Integer keys
	 * and Object (Dispatcher) values in a sort of Map structure.
	 * Since we know that we only contain Dispatcher instances and that
	 * a Dispatcher contains its own event id, we can simplify this
	 * to an ArrayList. Typically, we expect this list to be very short - 
	 * perhaps only a single item.
	 * @exclude from published api.
	 */
	public static final class EventTable extends ArrayList {

		private static final long serialVersionUID = -127450503214773632L;

		public boolean add(Dispatcher dispatcher) {
			// Insert this new dispatcher in order of eventID,
			// and after any existing entries with the same eventId.
			
			for (int i = 0; i < size(); i++) {
				if (dispatcher.getEventID() > get(i).getEventID()) {
					super.add(i, dispatcher);
					return true;
				}
			}
			
			return super.add(dispatcher);
		}
		
		public void add(int index, Dispatcher element) {
			throw new UnsupportedOperationException();
		}
		
		public boolean addAll(Collection eventTable) {
			boolean bChanged = false;
			for (Dispatcher dispatcher : eventTable) {
				add(dispatcher);
				bChanged = true;
			}
			
			return bChanged;
		}
		
		public boolean addAll(int index, Collection eventTable) {
			throw new UnsupportedOperationException();
		}
		
		public Dispatcher set(int index, Dispatcher element) {
			throw new UnsupportedOperationException();
		}
	}

	// Dispatcher class

	/**
	 * Reset an event table
	 * @param eventTable the event table to reset
	 */
	public static void resetEventTable(EventTable eventTable) {
	    if (eventTable != null) {
	        eventTable.clear();
	    }
	}

//	private final List mCalloutIDs = new ArrayList();

	private final List mEventIDs = new ArrayList();

//	private int mnCurrentCalloutID = -1; // ID of currently-executing callout

	private final AppModel mAppModel;

//	private List mCurrentDispatcherList; // points to a Dispatcher list on the stack

	private final Trace scriptTrace = Element.oScriptTrace;
	
	private String msCurrentEvent;

	protected  EventManager(AppModel appModel) {
		mAppModel = appModel;
		//mCurrentDispatcherList = null;
//		mnCurrentCalloutID = -1;

	}

//	/**
//	 * attaches a binary callout (ie. DLL on Windows) to be associated with a
//	 * particular event
//	 * 
//	 * @param nEventID
//	 * @param nCalloutID
//	 * @param node
//	 * @param bOnlyIfDispatcherExists
//	 */
//	private void attachCallout(int nEventID, int nCalloutID,
//						Node node /* = null */,
//						boolean bOnlyIfDispatcherExists /* = false */) {
//		assert (nEventID < mEventIDs.size());
//		assert (nCalloutID < mCalloutIDs.size());
//
//		EventID event = mEventIDs.get(nEventID);
//      event.mbDispatcherOrCalloutRegistered = true;
//		if (event.mCallouts == null)
//			event.mCallouts = new ArrayList();
//
//		CalloutEntry ce = new CalloutEntry();
//		ce.mnCalloutID = nCalloutID;
//		ce.mNodeImpl = node;
//		ce.mbOnlyIfDispatcherExists = bOnlyIfDispatcherExists;
//		event.mCallouts.add(ce);
//	}
//
//	private void callCallout(int nCalloutID, String sCurrentEventName) {
//		// TODO Auto-generated method stub
//		throw new ExFull(ResId.UNSUPPORTED_OPERATION);
//	}
	
	public boolean cancelAction(String sAction) {

	    EventPseudoModel poEventPseudoModel = getEventPseudoModel();
	    if (poEventPseudoModel != null)
	        return poEventPseudoModel.cancelAction(sAction);

	    return false;
	}
	
	public boolean cancelEvent(String sEvent) {

	    EventPseudoModel poEventPseudoModel = getEventPseudoModel();
	    if (poEventPseudoModel != null)
	        return poEventPseudoModel.cancelEvent(sEvent);

	    return false;
	}

//	private int currentCalloutID() {
//		return mnCurrentCalloutID;
//	}

//	List currentDispatcherList() {
//		return mCurrentDispatcherList;
//	}

	/**
	 * eventOccurred informs the event manager that a particular event has
	 * occurred. For each entry in mRegisteredEvents, if all required events
	 * have occurred, the corresponding node will have its attached script
	 * invoked.
	 * 
	 * If object is not null, only events registered with that particular
	 * object will be considered.
	 * 
	 * @return true if the event was dispatched to one or more listeners.
	 * 
	 * @exclude from public api.
	 */
	public boolean eventOccurred(int nEventID, Obj object /* = null */) {
		// For each entry in mRegisteredEvents, if all required events have occurred, call
		// dispatch() on the associated node.

		// need a context object
		if (object == null)
			return false;

		assert (nEventID < mEventIDs.size());

		// get the event
		EventID event = mEventIDs.get(nEventID);
		assert (event != null);
		
		// Test whether a dispatcher or callout has ever been registered for this event
		if (!event.mbDispatcherOrCalloutRegistered)
		{
			// porting CL#785734
			// To add to CL#514636 (halifax) -begin-
			// As part of CL 514636 we returned from here if mbDispatcherOrCalloutRegistered==false
			// but that left resetCancelAction part unexecuted (unlike A9). 
			// Adding that part here before the return to make the flow 
			// similar to pre-CL 514636
			if (isPostActionEvent(event.msEventName))
			{
				if (cancelEvent(event.msEventName)) return false;

				// this is a post-* event, attempt to reset $event.cancelAction
				msCurrentEvent = event.msEventName;
				resetCancelAction();
				msCurrentEvent = null;
			}
			// To add to CL#514636 (halifax) --end--
			return false;
		}

		// should this event be executed?
		if (cancelEvent(event.msEventName))
			return false;	// no

		if (scriptTrace.isEnabled(3)) {
			String sClass = object.getClassAtom() + ":";

			MsgFormatPos msg = new MsgFormatPos(ResId.TraceEventOccurred);
			msg.format(sClass + event.msEventName); // eg. data:load
			scriptTrace.trace(3, msg);
		}

		// TRY and init any uninitialized events
		for (int i = 0; i < event.mUnInitializedEvents.size(); i++) {
			Dispatcher dispatcher = event.mUnInitializedEvents.get(i);
			Obj otherEventContext = resolveEventContext(dispatcher);

			// remove the event for the uninitialized list and added to the
			// initialized list
			if (otherEventContext != null) {
				// create a table if we don't have one
				EventTable otherEventTable = otherEventContext.getEventTable(true);
				assert otherEventTable != null;

				otherEventTable.add(dispatcher);

				event.mUnInitializedEvents.remove(i);
				i--;
			}
		}

		List dispatcherList = null;

		EventTable eventTable = object.getEventTable(false);
		// grab the dispatchers for this event on the given object
		if (eventTable != null) {
			boolean bRemoveNonRecurring = false;
			for (int i = 0; i < eventTable.size(); i++) {
				Dispatcher dispatcher = eventTable.get(i);
				
				if (dispatcher.getEventID() == nEventID) {
					if (dispatcherList == null)
						dispatcherList = new ArrayList();
					
					dispatcherList.add(dispatcher);
					
					bRemoveNonRecurring |= !dispatcher.getRecurring();
				}	
			}
			
			if (bRemoveNonRecurring) {
				for (int i = eventTable.size() - 1; i >= 0; i--) {
					Dispatcher dispatcher = eventTable.get(i);
					
					if (dispatcher.getEventID() == nEventID && !dispatcher.getRecurring()) {
						eventTable.remove(i);
					}
				}
			}
		}
		
		// If an event listener that listens to descendents has ever been registered for this event id,
		// search up through the parents of the referenced object for event listeners that should be notified.
		if (event.mbListenToDescendentsRegistered) {
			
			Node contextNode = (Node)object;
			Element parent = contextNode.getXFAParent();

			while (parent != null) {
				
				EventTable parentEventTable = parent.getEventTable(false);
				// grab the dispatchers for this event on the given object
				if (parentEventTable != null) {
					int nDispatchersAtThisLevel = 0;
					for (int i = 0; i < parentEventTable.size(); i++) {
						Dispatcher dispatcher = parentEventTable.get(i);
						if (dispatcher.getEventID() == nEventID && dispatcher.getListenToDescendents()) {
							
							if (dispatcherList == null)
								dispatcherList = new ArrayList();
							
							// Insert the dispatcher at the front of the list so that
							// the events are dispatched in document order (of the element
							// firing the event). If there are multiple listeners at the same
							// level, they are dispatched in the document order that the
							// listeners appear.
							dispatcherList.add(nDispatchersAtThisLevel, dispatcher);
							nDispatchersAtThisLevel++;
							
							// Delete the entry if it's not a recurring event.
							if (!dispatcher.getRecurring()) {
								parentEventTable.remove(i);
								i--;
							}
						}
					}
				}

				parent = parent.getXFAParent();
			}
		}


//		// Call any external callouts that are registered for this event.
//		if (event.mCallouts != null) {
//			// A temporary variable for mEventIDs[nEventID].mpoCallouts.size()
//			// is NOT used here because a callout could potentially mess with
//			// this list.
//			for (int i = 0; i < event.mCallouts.size(); i++) {
//				CalloutEntry calloutEntry = event.mCallouts.get(i);
//				if ((calloutEntry.mNodeImpl != null) && 
//					(calloutEntry.mNodeImpl != object))
//					continue; // callout is not for this node
//
//				if ((calloutEntry.mbOnlyIfDispatcherExists) && 
//					(dispatcherList.size() == 0))
//					continue; // no script exists; don't call callout
//
//				// Set mpoCurrentDispatcherList in case the external library asks for it.
//				// It needs to be set each time in case we get a recursive call
//
//				List previousDispatcherList = mCurrentDispatcherList;
//				mCurrentDispatcherList = dispatcherList;
//				int nPreviousCalloutID = mnCurrentCalloutID;
//				mnCurrentCalloutID = calloutEntry.mnCalloutID;
//				try {
//					callCallout(mnCurrentCalloutID, event.msEventName);
//				} catch (ExFull oEx) {
//					mCurrentDispatcherList = previousDispatcherList; // important to reset this
//					mnCurrentCalloutID = nPreviousCalloutID;
//					throw oEx;
//				}
//				
//				mCurrentDispatcherList = previousDispatcherList;
//				mnCurrentCalloutID = nPreviousCalloutID;
//			}
//		}

		// if this is a pre-* or post-* event, cache the event name
		if (isPreActionEvent(event.msEventName) || isPostActionEvent(event.msEventName))
			msCurrentEvent = event.msEventName;
			
		// Now dispatch our queued-up dispatchers. Note that the callout could have
		// actually modified this list.
		
		boolean bEventDispatched = false;
		
		if (dispatcherList != null) {
			final int nSize = dispatcherList.size();
			
			try {
				bEventDispatched = nSize > 0;
	
				for (int i = 0; i < nSize; i++) {
					Dispatcher d = dispatcherList.get(i);
					d.setOccurring(true);
					d.dispatch();
					d.setOccurring(false);
				}
			} catch (ExFull ex) {
				// remove outstanding references
				for (int i = 0; i < nSize; i++) {
					dispatcherList.get(i).setOccurring(false);
				}
				
				// if this is a post-* event, attempt to reset $event.cancelAction
				if (isPostActionEvent(event.msEventName))
					resetCancelAction();

				// if this is a pre-* or post-* event, reset maCurrentEvent
				if (isPreActionEvent(event.msEventName) || isPostActionEvent(event.msEventName))
					msCurrentEvent = null;
				
				throw ex;
			}
		}
		
		// if this is a post-* event, attempt to reset $event.cancelAction
		if (isPostActionEvent(event.msEventName))
			resetCancelAction();
		
		// if this is a pre-* or post-* event, reset maCurrentEvent
		if (isPreActionEvent(event.msEventName) || isPostActionEvent(event.msEventName))
			msCurrentEvent = null;
		
		return bEventDispatched;
	}

//	/**
//	 * Return an ID for a callout name (ie. a DLL on Windows)
//	 * 
//	 * @param sBinary
//	 * @return
//	 * @exclude from public api.
//	 */
//	private int getCalloutID(String sBinary) {
//		for (int i = 0; i < mCalloutIDs.size(); i++) {
//			if (mCalloutIDs.get(i).equals(sBinary))
//				return i;
//		}
//
//		mCalloutIDs.add(sBinary);
//		return mCalloutIDs.size() - 1;
//	}
	
	String getCurrentEvent() {
		return msCurrentEvent;
	}

	/**
	 * return an ID for a event name
	 * 
	 * @param sEventName
	 * @exclude from public api.
	 */
	public int getEventID(String sEventName) {
		for (int i = 0; i < mEventIDs.size(); i++) {
			if (mEventIDs.get(i).msEventName.equals(sEventName))
				return i;
		}

		EventID eventID = new EventID(sEventName);
//		eventID.mCallouts = null;

		mEventIDs.add(eventID);
		return mEventIDs.size() - 1;
	}

	/**
	 * @exclude from public api.
	 */
	public EventID getEventIDByIndex(int nIndex) {
		return mEventIDs.get(nIndex);
	}
	
	private EventPseudoModel getEventPseudoModel() {

	    EventPseudoModel eventPseudoModel = null;

	    assert mAppModel != null;
	    if (mAppModel != null)
	        eventPseudoModel = (EventPseudoModel)(mAppModel.lookupPseudoModel(STRS.DOLLAREVENT));

	    return eventPseudoModel;
	}

	int getNumEventIDs() {
		return mEventIDs.size();
	}
	
	private boolean isPreActionEvent( String sEvent ) {
		EventPseudoModel	oEventPseudoModel = getEventPseudoModel();
		if (oEventPseudoModel != null)
		 	return oEventPseudoModel.isPreActionEvent(sEvent);
		
		return false;
	}
	
	private boolean isPostActionEvent( String sEvent ) {
		EventPseudoModel	oEventPseudoModel = getEventPseudoModel();
		if (oEventPseudoModel != null)
			return oEventPseudoModel.isPostActionEvent(sEvent);
		
		return false;
	   }

	/**
	 * @exclude from public api.
	 */
	public void registerEvents(Dispatcher dispatcher) {
		int nIndex = dispatcher.getEventID();
		assert nIndex < mEventIDs.size();
		
		// Remember if a dispatcher has been registered for an event so we can
		// do a quick test to avoid event processing for unused events.
		EventID event = mEventIDs.get(nIndex);
		event.mbDispatcherOrCalloutRegistered = true;
		
		// Remember if an event that listens to descendents is ever registered for an EventID.
		// We only need to do the extra processing required for those EventsIDs.
		if (dispatcher.getListenToDescendents()) {
			event.mbListenToDescendentsRegistered = true;
		}

		Obj object = resolveEventContext(dispatcher);

		if (object != null) {
			// add the event to the object for quick lookup
			EventTable eventTable = object.getEventTable(true);
			assert eventTable != null;
			eventTable.add(dispatcher);
		} else {
			// add it to our unInitialized list
			mEventIDs.get(nIndex).mUnInitializedEvents.add(dispatcher);
		}
	}
	
	/**
	 * @exclude from public api.
	 */
	public void reset() {
//		mCurrentDispatcherList = null;
//		mnCurrentCalloutID = -1;

		for (int i = 0; i < mEventIDs.size(); i++) {
			EventID event = mEventIDs.get(i);
//			event.mCallouts = null;

			for (int j = 0; j < event.mUnInitializedEvents.size(); j++) {
				Dispatcher dispatcher = event.mUnInitializedEvents.get(j);
				dispatcher.setEventManager(null);
			}
			event.mUnInitializedEvents.clear();
		}
		
		// ensure we clean up the pseudoModels, if not
		// we may try to access null pointers.
		if (mAppModel != null)
			mAppModel.resetPseudoModelEvents();
	}
	
	private void resetCancelAction() {

	    EventPseudoModel poEventPseudoModel = getEventPseudoModel();
	    if (poEventPseudoModel != null)
	        poEventPseudoModel.setCancelAction(false, null);
	}

	private Obj resolveEventContext(Dispatcher dispatcher) {
		Obj eventContext = null;
		if (dispatcher != null) {
			eventContext = dispatcher.getEventContextObj();
			// the event context has already been resolved
			if (eventContext != null) {
				// if poEventContext is an empty string than it means
				// it has already been resolved
				return eventContext;
			}

			String sEventContext = dispatcher.getEventContext();
			if (StringUtils.isEmpty(sEventContext))
				return eventContext;

			Node actionContext = dispatcher.getActionContextNode();

			// the event context and the script context are the same
			if (sEventContext.equals("$")) {
				dispatcher.setEventContextObj(dispatcher.getActionContextNode());
			} else {

				SOMParser parser = new SOMParser(null);

				List lhsResult = new ArrayList();
				parser.resolve(actionContext, sEventContext, lhsResult);

				Obj eventContextObject = null;
				if (lhsResult.size() > 0) {
					eventContextObject = lhsResult.get(0).object;
				}

				dispatcher.setEventContextObj(eventContextObject);

			}

			eventContext = dispatcher.getEventContextObj();
		}

		return eventContext;
	}

	// void
	// callCallout(int nCalloutID, String sCurrentEventName)
	// {
	// #if defined(__MWERKS__) && !defined(XFA_MacOSX)
	// return; // turn this off for now on Mac - need to revisit this later.
	// #endif
	// String sLibraryName = mCalloutIDs[nCalloutID];
	// void * pCalloutLibraryHandle = null;
	// XFACALLOUT_FN XFACalloutFn = null;
	// #if defined(WIN32)
	// char * sXFACalloutFnName = "_XFACallout@4";
	// #if defined(_DEBUG)
	// sLibraryName += 'd';
	// #elif defined(_PROFILE)
	// // Are we running profile code? If so the names have a 'p'
	// // appended to them.
	// sLibraryName += 'p';
	// #endif
	// pCalloutLibraryHandle = .LoadLibrary(sLibraryName);
	// if (pCalloutLibraryHandle != null)
	// XFACalloutFn = (XFACALLOUT_FN)
	// GetProcAddress((HMODULE)pCalloutLibraryHandle, sXFACalloutFnName);
	// # ifdef _DEBUG
	// else
	// {
	// LPSTR lpMsgBuf;
	// DWORD dw = .FormatMessage(
	// FORMAT_MESSAGE_ALLOCATE_BUFFER |
	// FORMAT_MESSAGE_FROM_SYSTEM |
	// FORMAT_MESSAGE_IGNORE_INSERTS,
	// null,
	// GetLastError(),
	// MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
	// (LPSTR)&lpMsgBuf,
	// 0,
	// null
	// );
	// String sError(lpMsgBuf);
	// // Free the buffer.
	// LocalFree(lpMsgBuf);
	// }
	// # endif // _DEBUG
	// #elif defined(__hpux) && ! defined(__LP64__)
	// char * sXFACalloutFnName = "XFACallout";
	// sLibraryName += ".sl";
	// pCalloutLibraryHandle = shl_load(sLibraryName, BIND_DEFERRED |
	// DYNAMIC_PATH, 0);
	// if (pCalloutLibraryHandle != null)
	// {
	// XFACalloutFn = (XFACALLOUT_FN) null;
	// // need to do this as there is a bug in shl_findsym on 32-bit HP-UX
	// struct shl_symbol *SymList = null;
	// int NumSyms;
	// int i;
	// NumSyms = shl_getsymbols((shl_t)pCalloutLibraryHandle, TYPE_PROCEDURE,
	// EXPORT_SYMBOLS, (void *(*)()) malloc, &SymList);
	// for (i = 0; i < NumSyms; i++)
	// {
	// if (strcmp(sXFACalloutFnName, SymList[i].name) == 0)
	// {
	// XFACalloutFn = (XFACALLOUT_FN) SymList[i].value;
	// break;
	// }
	// }
	// free(SymList);
	// }
	// # ifdef _DEBUG
	// else
	// {
	// int err = errno;
	// if (err == 0) // simulate symbol not found, as shl_getsymbols was used
	// err = ENOSYM;
	// String sError(strerror(err));
	// }
	// # endif
	// #else
	// char * sXFACalloutFnName = "XFACallout";
	// #if !defined(XFA_MacOSX)
	// sLibraryName += ".so";
	// #else
	// #ifndef _DEBUG
	// sLibraryName += ".dylib";
	// #else
	// sLibraryName += ".debug.dylib";
	// #endif
	// #endif
	// pCalloutLibraryHandle = dlopen(sLibraryName, RTLD_LAZY);
	// if (pCalloutLibraryHandle != null)
	// XFACalloutFn = (XFACALLOUT_FN) dlsym(pCalloutLibraryHandle,
	// sXFACalloutFnName);
	// # ifdef _DEBUG
	// else
	// String sError(dlerror());
	// # endif
	// #endif
	//		
	// if (XFACalloutFn != null)
	// {
	// assert(mpoAppModelImpl != null);
	// AppModel app((Object) mpoAppModelImpl);
	// XFACalloutInfo ci(app);
	// ci.msCurrentEventName = sCurrentEventName;
	// XFACalloutFn(ci);
	// }
	//		
	// #if defined(WIN32)
	// FreeLibrary((HMODULE)pCalloutLibraryHandle);
	// #elif defined(__hpux) && ! defined(__LP64__)
	// if (pCalloutLibraryHandle != null)
	// shl_unload((shl_t)pCalloutLibraryHandle);
	// #else
	// if (pCalloutLibraryHandle != null)
	// dlclose(pCalloutLibraryHandle);
	// #endif
	//		
	// }

	/**
	 * set the AppModel that owns this EventManager. This is only needed for
	 * callouts.
	 */
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy