/******************************************************************************* * Copyright (c) 2011, 2014 Wind River Systems, Inc. and others. All rights reserved. * This program and the accompanying materials are made available under the terms * of the Eclipse Public License v1.0 which accompanies this distribution, and is * available at http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Wind River Systems - initial API and implementation *******************************************************************************/ package org.eclipse.tcf.te.runtime.callback; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Vector; import org.eclipse.core.runtime.Platform; import org.eclipse.tcf.te.runtime.interfaces.IConditionTester; import org.eclipse.tcf.te.runtime.interfaces.callback.ICallback; /** * Common implementation to handle and wait for asynchronous callback's. */ public class AsyncCallbackHandler { private final List<ICallback> callbacks = new Vector<ICallback>(); private final IConditionTester conditionTester; private Throwable error; private final static boolean TRACING_ENABLED = Boolean.parseBoolean(Platform.getDebugOption("org.eclipse.tcf.te.runtime/trace/callbacks")); //$NON-NLS-1$ // This map will track who have added a specific callback to the handler. // This is for internal debugging purpose only. private final Map<ICallback, Throwable> callbackAdders = new Hashtable<ICallback, Throwable>(); /** * Constructor. */ public AsyncCallbackHandler() { this(null); } /** * Constructor. */ public AsyncCallbackHandler(IConditionTester tester) { super(); error = null; conditionTester = new AsyncCallbackConditionTester(tester); } /** * Adds the given callback to the list of pending sequent's or callback's. In case of adding a * callback to the list, the caller must remove the callback from list as well. * * @param callback The callback to add to the list. */ public void addCallback(ICallback callback) { if (callback != null && !callbacks.contains(callback)) { callbacks.add(callback); if (TRACING_ENABLED) { try { throw new Exception("Pending callback added!"); //$NON-NLS-1$ } catch (Exception e) { callbackAdders.put(callback, e.fillInStackTrace()); } } } } /** * Removes the given callback from the list of pending sequent's or callback's. * * @param callback The callback to remove from the list. */ public void removeCallback(ICallback callback) { if (callback != null) { callbacks.remove(callback); if (TRACING_ENABLED) { callbackAdders.remove(callback); } } } /** * Returns if or if not all previously added callback's have been removed again. * * @return <code>true</code> if the callback handler is empty, <code>false</code> otherwise. */ public final boolean isEmpty() { if (TRACING_ENABLED) { System.err.println(this.getClass().getName() + ": size = " + callbacks.size()); //$NON-NLS-1$ if (callbacks.size() < 4) { synchronized (callbacks) { Iterator<?> iterator = callbacks.iterator(); System.err.println("Remaining adders: "); //$NON-NLS-1$ while (iterator.hasNext()) { Throwable adder = callbackAdders.get(iterator.next()); adder.printStackTrace(); System.err.println("*****"); //$NON-NLS-1$ } } } } return callbacks.isEmpty(); } /** * Remove all remaining callback's from the callback handler. */ public final void clear() { callbacks.clear(); } /** * Sets the given error for this handler. In case an error had been set before, the previous * error will be preserved and the current error will be dropped. * * @param error The error to set or <code>null</code>. */ public final void setError(Throwable error) { if (error != null) { // The first error is the most precious. // Don't override it by subsequent errors. if (this.error == null) { this.error = error; } } else { // Reset the error to null this.error = null; } } /** * Returns the associated error. * * @return The error or <code>null</code>. */ public final Throwable getError() { return error; } /** * Returns the condition tester to use for waiting for the callback handler * until all callbacks have been invoked and the external condition tester * is fulfilled too. * * @return The condition tester instance. */ public IConditionTester getConditionTester() { return conditionTester; } final class AsyncCallbackConditionTester implements IConditionTester { private final IConditionTester externalTester; /** * Constructor. * <p> * If an external condition tester is passed in, {@link #isConditionFulfilled()} will return * <code>true</code> if either the callback handler is empty or the external tester's * {@link #isConditionFulfilled()} returns <code>true</code>. * * @param tester An external condition tester or <code>null</code>. */ public AsyncCallbackConditionTester(IConditionTester tester) { externalTester = tester; } /* (non-Javadoc) * @see org.eclipse.tcf.te.runtime.interfaces.IConditionTester#cleanup() */ @Override public void cleanup() { if (!isEmpty()) { clear(); } } /* (non-Javadoc) * @see org.eclipse.tcf.te.runtime.interfaces.IConditionTester#isConditionFulfilled() */ @Override public boolean isConditionFulfilled() { // the condition is fulfilled if no remaining callback's are registered! return isEmpty() || (externalTester != null && externalTester.isConditionFulfilled()); } } }