Copyright (c) 2000, 2018 IBM Corporation and others. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 which accompanies this distribution, and is available at https://www.eclipse.org/legal/epl-2.0/ SPDX-License-Identifier: EPL-2.0 Contributors: IBM Corporation - initial API and implementation
/******************************************************************************* * Copyright (c) 2000, 2018 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * https://www.eclipse.org/legal/epl-2.0/ * * SPDX-License-Identifier: EPL-2.0 * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/
package org.eclipse.debug.internal.core; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Vector; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.TransformerException; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.IExtensionPoint; import org.eclipse.core.runtime.ISafeRunnable; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.ListenerList; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.PlatformObject; import org.eclipse.core.runtime.SafeRunner; import org.eclipse.core.runtime.Status; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.IExpressionListener; import org.eclipse.debug.core.IExpressionManager; import org.eclipse.debug.core.IExpressionsListener; import org.eclipse.debug.core.model.IExpression; import org.eclipse.debug.core.model.IWatchExpression; import org.eclipse.debug.core.model.IWatchExpressionDelegate; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import com.ibm.icu.text.MessageFormat;
The expression manager manages all registered expressions for the debug plug-in. It is instantiated by the debug plug-in at startup.
See Also:
  • IExpressionManager
/** * The expression manager manages all registered expressions * for the debug plug-in. It is instantiated by the debug plug-in * at startup. * * @see IExpressionManager */
public class ExpressionManager extends PlatformObject implements IExpressionManager {
Ordered collection of registered expressions.
/** * Ordered collection of registered expressions. */
private Vector<IExpression> fExpressions = null;
List of expression listeners
/** * List of expression listeners */
private ListenerList<IExpressionListener> fListeners = null;
List of expressions listeners (plural)
/** * List of expressions listeners (plural) */
private ListenerList<IExpressionsListener> fExpressionsListeners = null;
Mapping of debug model identifiers (String) to expression delegate extensions (IConfigurationElement)
/** * Mapping of debug model identifiers (String) to * expression delegate extensions (IConfigurationElement) */
private Map<String, IConfigurationElement> fWatchExpressionDelegates = new HashMap<>(); // Constants for add/remove/change/insert/move notification private static final int ADDED = 1; private static final int CHANGED = 2; private static final int REMOVED = 3; private static final int INSERTED = 4; private static final int MOVED = 5; // Preference for persisted watch expressions private static final String PREF_WATCH_EXPRESSIONS= "prefWatchExpressions"; //$NON-NLS-1$ // Persisted watch expression XML tags private static final String WATCH_EXPRESSIONS_TAG= "watchExpressions"; //$NON-NLS-1$ private static final String EXPRESSION_TAG= "expression"; //$NON-NLS-1$ private static final String TEXT_TAG= "text"; //$NON-NLS-1$ private static final String ENABLED_TAG= "enabled"; //$NON-NLS-1$ // XML values private static final String TRUE_VALUE= "true"; //$NON-NLS-1$ private static final String FALSE_VALUE= "false"; //$NON-NLS-1$ public ExpressionManager() { loadPersistedExpressions(); loadWatchExpressionDelegates(); }
Loads the mapping of debug models to watch expression delegates from the org.eclipse.debug.core.watchExpressionDelegates extension point.
/** * Loads the mapping of debug models to watch expression delegates * from the org.eclipse.debug.core.watchExpressionDelegates * extension point. */
private void loadWatchExpressionDelegates() { IExtensionPoint extensionPoint = Platform.getExtensionRegistry().getExtensionPoint(DebugPlugin.getUniqueIdentifier(), "watchExpressionDelegates"); //$NON-NLS-1$ IConfigurationElement[] configurationElements = extensionPoint.getConfigurationElements(); for (int i = 0; i < configurationElements.length; i++) { IConfigurationElement element = configurationElements[i]; if (element.getName().equals("watchExpressionDelegate")) { //$NON-NLS-1$ String debugModel = element.getAttribute("debugModel"); //$NON-NLS-1$ if (debugModel == null || debugModel.length() == 0) { continue; } fWatchExpressionDelegates.put(debugModel, element); } } } @Override public IWatchExpressionDelegate newWatchExpressionDelegate(String debugModel) { try { IConfigurationElement element= fWatchExpressionDelegates.get(debugModel); if (element != null) { return (IWatchExpressionDelegate) element.createExecutableExtension(IConfigurationElementConstants.DELEGATE_CLASS); } return null; } catch (CoreException e) { DebugPlugin.log(e); return null; } } @Override public boolean hasWatchExpressionDelegate(String id) { IConfigurationElement element= fWatchExpressionDelegates.get(id); return element != null; }
Loads any persisted watch expressions from the preferences. NOTE: It's important that no setter methods are called on the watchpoints which will fire change events as this will cause an infinite loop (see Bug 27281).
/** * Loads any persisted watch expressions from the preferences. * NOTE: It's important that no setter methods are called on * the watchpoints which will fire change events as this * will cause an infinite loop (see Bug 27281). */
private void loadPersistedExpressions() { String expressionsString = Platform.getPreferencesService().getString(DebugPlugin.getUniqueIdentifier(), PREF_WATCH_EXPRESSIONS, IInternalDebugCoreConstants.EMPTY_STRING, null); if (expressionsString.length() == 0) { return; } Element root; try { root = DebugPlugin.parseDocument(expressionsString); } catch (CoreException e) { DebugPlugin.logMessage("An exception occurred while loading watch expressions.", e); //$NON-NLS-1$ return; } if (!root.getNodeName().equals(WATCH_EXPRESSIONS_TAG)) { DebugPlugin.logMessage("Invalid format encountered while loading watch expressions.", null); //$NON-NLS-1$ return; } NodeList list= root.getChildNodes(); for (int i= 0, numItems= list.getLength(); i < numItems; i++) { Node node= list.item(i); if (node.getNodeType() == Node.ELEMENT_NODE) { Element element= (Element) node; if (!element.getNodeName().equals(EXPRESSION_TAG)) { DebugPlugin.logMessage(MessageFormat.format("Invalid XML element encountered while loading watch expressions: {0}", new Object[] { node.getNodeName() }), null); //$NON-NLS-1$ continue; } String expressionText= element.getAttribute(TEXT_TAG); if (expressionText.length() > 0) { boolean enabled= TRUE_VALUE.equals(element.getAttribute(ENABLED_TAG)); IWatchExpression expression= newWatchExpression(expressionText, enabled); if (fExpressions == null) { fExpressions = new Vector<>(list.getLength()); } fExpressions.add(expression); } else { DebugPlugin.logMessage("Invalid expression entry encountered while loading watch expressions. Expression text is empty.", null); //$NON-NLS-1$ } } } }
Creates a new watch expression with the given expression and the given enablement;
Params:
  • expressionText – the text of the expression to be evaluated
  • enabled – whether or not the new expression should be enabled
Returns:the new watch expression
/** * Creates a new watch expression with the given expression * and the given enablement; * * @param expressionText the text of the expression to be evaluated * @param enabled whether or not the new expression should be enabled * @return the new watch expression */
private IWatchExpression newWatchExpression(String expressionText, boolean enabled) { return new WatchExpression(expressionText, enabled); } @Override public IWatchExpression newWatchExpression(String expressionText) { return new WatchExpression(expressionText); }
Persists this manager's watch expressions as XML in the preference store.
/** * Persists this manager's watch expressions as XML in the * preference store. */
public void storeWatchExpressions() { String expressionString = IInternalDebugCoreConstants.EMPTY_STRING; try { expressionString= getWatchExpressionsAsXML(); } catch (IOException e) { DebugPlugin.log(e); } catch (ParserConfigurationException e) { DebugPlugin.log(e); } catch (TransformerException e) { DebugPlugin.log(e); } Preferences.setString(DebugPlugin.getUniqueIdentifier(), PREF_WATCH_EXPRESSIONS, expressionString, null); }
Returns this manager's watch expressions as XML.
Throws:
Returns:this manager's watch expressions as XML
/** * Returns this manager's watch expressions as XML. * @return this manager's watch expressions as XML * @throws IOException if an exception occurs while creating * the XML document. * @throws ParserConfigurationException if an exception occurs while creating * the XML document. * @throws TransformerException if an exception occurs while creating * the XML document. */
private String getWatchExpressionsAsXML() throws IOException, ParserConfigurationException, TransformerException { IExpression[] expressions= getExpressions(); Document document= LaunchManager.getDocument(); Element rootElement= document.createElement(WATCH_EXPRESSIONS_TAG); document.appendChild(rootElement); for (int i = 0; i < expressions.length; i++) { IExpression expression= expressions[i]; if (expression instanceof IWatchExpression) { Element element= document.createElement(EXPRESSION_TAG); element.setAttribute(TEXT_TAG, expression.getExpressionText()); element.setAttribute(ENABLED_TAG, ((IWatchExpression) expression).isEnabled() ? TRUE_VALUE : FALSE_VALUE); rootElement.appendChild(element); } } return LaunchManager.serializeDocument(document); } @Override public void addExpression(IExpression expression) { addExpressions(new IExpression[]{expression}); } @Override public void addExpressions(IExpression[] expressions) { List<IExpression> added = doAdd(expressions); if (!added.isEmpty()) { fireUpdate(added.toArray(new IExpression[added.size()]), ADDED); } }
Adds the given expressions to the list of managed expressions, and returns a list of expressions that were actually added. Expressions that already exist in the managed list are not added.
Params:
  • expressions – expressions to add
Returns:list of expressions that were actually added.
/** * Adds the given expressions to the list of managed expressions, and returns a list * of expressions that were actually added. Expressions that already exist in the * managed list are not added. * * @param expressions expressions to add * @return list of expressions that were actually added. */
private List<IExpression> doAdd(IExpression[] expressions) { List<IExpression> added = new ArrayList<>(expressions.length); synchronized (this) { if (fExpressions == null) { fExpressions = new Vector<>(expressions.length); } for (int i = 0; i < expressions.length; i++) { IExpression expression = expressions[i]; if (fExpressions.indexOf(expression) == -1) { added.add(expression); fExpressions.add(expression); } } } return added; } @Override public synchronized IExpression[] getExpressions() { if (fExpressions == null) { return new IExpression[0]; } IExpression[] temp= new IExpression[fExpressions.size()]; fExpressions.copyInto(temp); return temp; } @Override public synchronized IExpression[] getExpressions(String modelIdentifier) { if (fExpressions == null) { return new IExpression[0]; } ArrayList<IExpression> temp = new ArrayList<>(fExpressions.size()); for (IExpression expression : fExpressions) { String id = expression.getModelIdentifier(); if (id != null && id.equals(modelIdentifier)) { temp.add(expression); } } return temp.toArray(new IExpression[temp.size()]); }
Adds the given expressions to the collection of registered expressions in the workspace and notifies all registered listeners. The expressions are inserted in the same order as the passed array at the index of the specified expressions (before or after it depending on the boolean argument). If no valid insertion location could be found, the expressions are added to the end of the collection. Has no effect on expressions already registered.
Params:
  • expressions – expressions to insert into the collection
  • insertionLocation – the expression at the location where expressions will be inserted
  • insertBefore – whether to insert the expressions before or after the given insertion location
Since:3.4
/** * Adds the given expressions to the collection of registered expressions * in the workspace and notifies all registered listeners. The expressions * are inserted in the same order as the passed array at the index of the * specified expressions (before or after it depending on the boolean argument). * If no valid insertion location could be found, the expressions are added * to the end of the collection. Has no effect on expressions already registered. * * @param expressions expressions to insert into the collection * @param insertionLocation the expression at the location where expressions will be inserted * @param insertBefore whether to insert the expressions before or after the given insertion location * @since 3.4 */
public void insertExpressions(IExpression[] expressions, IExpression insertionLocation, boolean insertBefore){ List<IExpression> added = null; List<IExpression> inserted = null; int insertionIndex = -1; synchronized (this) { if (fExpressions == null || ((insertionIndex = fExpressions.indexOf(insertionLocation)) < 0)) { added = doAdd(expressions); } else { if (!insertBefore){ insertionIndex++; } inserted = new ArrayList<>(expressions.length); for (int i = 0; i < expressions.length; i++) { IExpression expression = expressions[i]; if (fExpressions.indexOf(expression) == -1) { //Insert in the same order as the array is passed fExpressions.add(insertionIndex+inserted.size(), expression); inserted.add(expression); } } } } if (added != null) { if (!added.isEmpty()) { fireUpdate(added.toArray(new IExpression[added.size()]), ADDED); } return; } if (inserted != null) { if (!inserted.isEmpty()) { fireUpdate(inserted.toArray(new IExpression[inserted.size()]), INSERTED, insertionIndex); } } }
Moves the given expressions from their location in the collection of registered expressions in the workspace to the specified insertion location. Notifies all registered listeners. This method has no effect if an expression does not exist in the collection or if no valid insertion location could be determined.
Params:
  • expressions – expressions to move
  • insertionLocation – the expression at the location to insert the moved expressions
  • insertBefore – whether to insert the moved expressions before or after the given insertion location
Since:3.4
/** * Moves the given expressions from their location in the collection * of registered expressions in the workspace to the specified insertion * location. Notifies all registered listeners. This method has no effect * if an expression does not exist in the collection or if no valid insertion * location could be determined. * * @param expressions expressions to move * @param insertionLocation the expression at the location to insert the moved expressions * @param insertBefore whether to insert the moved expressions before or after the given insertion location * @since 3.4 */
public void moveExpressions(IExpression[] expressions, IExpression insertionLocation, boolean insertBefore){ List<IExpression> movedExpressions = new ArrayList<>(expressions.length); int insertionIndex = -1; IExpression[] movedExpressionsArray = null; synchronized (this) { if (fExpressions == null){ return; } insertionIndex = fExpressions.indexOf(insertionLocation); if (insertionIndex < 0){ return; } if (!insertBefore){ insertionIndex++; } for (int i = 0; i < expressions.length; i++) { int removeIndex = fExpressions.indexOf(expressions[i]); if (removeIndex >= 0){ movedExpressions.add(expressions[i]); if (removeIndex < insertionIndex){ insertionIndex--; } fExpressions.remove(removeIndex); } } movedExpressionsArray = movedExpressions.toArray(new IExpression[movedExpressions.size()]); for (int i = 0; i < movedExpressionsArray.length; i++) { // Insert the expressions in the same order as the passed array fExpressions.add(insertionIndex+i,movedExpressionsArray[i]); } } if (!movedExpressions.isEmpty()) { fireUpdate(movedExpressionsArray, MOVED, insertionIndex); } } @Override public void removeExpression(IExpression expression) { removeExpressions(new IExpression[] {expression}); } @Override public void removeExpressions(IExpression[] expressions) { List<IExpression> removed = new ArrayList<>(expressions.length); synchronized (this) { if (fExpressions == null) { return; } for (int i = 0; i < expressions.length; i++) { IExpression expression = expressions[i]; if (fExpressions.remove(expression)) { removed.add(expression); } } } // dispose outside of the synchronized block if (!removed.isEmpty()) { for (IExpression expression : removed) { expression.dispose(); } fireUpdate(removed.toArray(new IExpression[removed.size()]), REMOVED); } } @Override public void addExpressionListener(IExpressionListener listener) { if (fListeners == null) { fListeners = new ListenerList<>(); } fListeners.add(listener); } @Override public void removeExpressionListener(IExpressionListener listener) { if (fListeners == null) { return; } fListeners.remove(listener); }
The given watch expression has changed. Update the persisted expressions to store this change as indicated
Params:
  • expression – the changed expression
/** * The given watch expression has changed. Update the persisted * expressions to store this change as indicated * * @param expression the changed expression */
protected void watchExpressionChanged(IWatchExpression expression) { boolean notify = false; synchronized (this) { if (fExpressions != null && fExpressions.contains(expression)) { notify = true; } } if (notify) { fireUpdate(new IExpression[]{expression}, CHANGED); } }
Notifies listeners of the adds/removes/changes
Params:
  • expressions – expressions that were modified
  • update – update flags
/** * Notifies listeners of the adds/removes/changes * * @param expressions expressions that were modified * @param update update flags */
private void fireUpdate(IExpression[] expressions, int update){ fireUpdate(expressions, update, -1); }
Notifies listeners of the adds/removes/changes/insertions/moves
Params:
  • expressions – expressions that were modified
  • update – update flags
  • index – index where expressions were inserted/moved to or -1
/** * Notifies listeners of the adds/removes/changes/insertions/moves * * @param expressions expressions that were modified * @param update update flags * @param index index where expressions were inserted/moved to or <code>-1</code> */
private void fireUpdate(IExpression[] expressions, int update, int index){ // single listeners getExpressionNotifier().notify(expressions, update); // multi listeners getExpressionsNotifier().notify(expressions, update, index); } @Override public synchronized boolean hasExpressions() { return fExpressions != null && !fExpressions.isEmpty(); } @Override public void addExpressionListener(IExpressionsListener listener) { if (fExpressionsListeners == null) { fExpressionsListeners = new ListenerList<>(); } fExpressionsListeners.add(listener); } @Override public void removeExpressionListener(IExpressionsListener listener) { if (fExpressionsListeners == null) { return; } fExpressionsListeners.remove(listener); } private ExpressionNotifier getExpressionNotifier() { return new ExpressionNotifier(); }
Notifies an expression listener (single expression) in a safe runnable to handle exceptions.
/** * Notifies an expression listener (single expression) in a safe runnable to * handle exceptions. */
class ExpressionNotifier implements ISafeRunnable { private IExpressionListener fListener; private int fType; private IExpression fExpression; @Override public void handleException(Throwable exception) { IStatus status = new Status(IStatus.ERROR, DebugPlugin.getUniqueIdentifier(), DebugPlugin.INTERNAL_ERROR, "An exception occurred during expression change notification.", exception); //$NON-NLS-1$ DebugPlugin.log(status); } @Override public void run() throws Exception { switch (fType) { case ADDED: case INSERTED: fListener.expressionAdded(fExpression); break; case REMOVED: fListener.expressionRemoved(fExpression); break; case CHANGED: fListener.expressionChanged(fExpression); break; default: break; } }
Notifies listeners of the add/change/remove
Params:
  • expressions – the expressions that have changed
  • update – the type of change
/** * Notifies listeners of the add/change/remove * * @param expressions the expressions that have changed * @param update the type of change */
public void notify(IExpression[] expressions, int update) { if (fListeners != null) { fType = update; for (IExpressionListener iExpressionListener : fListeners) { fListener = iExpressionListener; for (int j = 0; j < expressions.length; j++) { fExpression = expressions[j]; SafeRunner.run(this); } } } fListener = null; fExpression = null; } }
Returns the expressions notifier
Returns:the expressions notifier
/** * Returns the expressions notifier * @return the expressions notifier */
private ExpressionsNotifier getExpressionsNotifier() { return new ExpressionsNotifier(); }
Notifies an expression listener (multiple expressions) in a safe runnable to handle exceptions.
/** * Notifies an expression listener (multiple expressions) in a safe runnable * to handle exceptions. */
class ExpressionsNotifier implements ISafeRunnable { private IExpressionsListener fListener; private int fType; private int fIndex; private IExpression[] fNotifierExpressions; @Override public void handleException(Throwable exception) { IStatus status = new Status(IStatus.ERROR, DebugPlugin.getUniqueIdentifier(), DebugPlugin.INTERNAL_ERROR, "An exception occurred during expression change notification.", exception); //$NON-NLS-1$ DebugPlugin.log(status); } @Override public void run() throws Exception { switch (fType) { case MOVED: // If the listener doesn't know about moves or the insertion location is unknown, do nothing. if (fIndex >= 0 && fListener instanceof IExpressionsListener2){ ((IExpressionsListener2)fListener).expressionsMoved(fNotifierExpressions, fIndex); } break; case INSERTED: // If the listener doesn't know about insertions or the insertion location is unknown, notify of an ADD if (fIndex >= 0 && fListener instanceof IExpressionsListener2){ ((IExpressionsListener2)fListener).expressionsInserted(fNotifierExpressions, fIndex); } else { fListener.expressionsAdded(fNotifierExpressions); } break; case ADDED: fListener.expressionsAdded(fNotifierExpressions); break; case REMOVED: fListener.expressionsRemoved(fNotifierExpressions); break; case CHANGED: fListener.expressionsChanged(fNotifierExpressions); break; default: break; } }
Notifies listeners of the adds/changes/removes
Params:
  • expressions – the expressions that changed
  • update – the type of change
  • index – the index of the first change
/** * Notifies listeners of the adds/changes/removes * * @param expressions the expressions that changed * @param update the type of change * @param index the index of the first change */
public void notify(IExpression[] expressions, int update, int index) { if (fExpressionsListeners != null) { fNotifierExpressions = expressions; fType = update; fIndex = index; for (IExpressionsListener iExpressionsListener : fExpressionsListeners) { fListener = iExpressionsListener; SafeRunner.run(this); } } fNotifierExpressions = null; fListener = null; } } }