com.adobe.xfa.EventManager Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of aem-sdk-api Show documentation
Show all versions of aem-sdk-api Show documentation
The Adobe Experience Manager SDK
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 extends Dispatcher> eventTable) {
boolean bChanged = false;
for (Dispatcher dispatcher : eventTable) {
add(dispatcher);
bChanged = true;
}
return bChanged;
}
public boolean addAll(int index, Collection extends Dispatcher> 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.
*/
}