/******************************************************************************* * Copyright (c) 2004, 2015 IBM Corporation 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: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.core.commands.contexts; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import org.eclipse.core.commands.common.NamedHandleObject; import org.eclipse.core.commands.common.NotDefinedException; import org.eclipse.core.internal.commands.util.Util; /** * <p> * A context is an answer to the question "when". Other services can listen for * the activation and deactivation of contexts, and change their own state in * response to these changes. For example, Eclipse's key binding service listens * to context activation and deactivation to determine which key bindings should * be active. * </p> * <p> * An instance of this interface can be obtained from an instance of * <code>ContextManager</code> for any identifier, whether or not an context * with that identifier is defined in the extension registry. * </p> * <p> * The handle-based nature of this API allows it to work well with runtime * plugin activation and deactivation. If a context is defined, that means that * its corresponding plug-in is active. If the plug-in is then deactivated, the * context will still exist but it will be undefined. An attempts to use an * undefined context will result in a <code>NotDefinedException</code> being * thrown. * </p> * <p> * This class is not intended to be extended by clients. * </p> * * @since 3.1 * @see ContextManager */ @SuppressWarnings("rawtypes") public final class Context extends NamedHandleObject implements Comparable { /** * The collection of all objects listening to changes on this context. This * value is <code>null</code> if there are no listeners. */ private Set<IContextListener> listeners; /** * The parent identifier for this context. The meaning of a parent is * dependent on the system using contexts. This value can be * <code>null</code> if the context has no parent. */ private String parentId; /** * Constructs a new instance of <code>Context</code>. * * @param id * The id for this context; must not be <code>null</code>. */ Context(final String id) { super(id); } /** * Registers an instance of <code>IContextListener</code> to listen for * changes to properties of this instance. * * @param listener * the instance to register. Must not be <code>null</code>. If * an attempt is made to register an instance which is already * registered with this instance, no operation is performed. */ public final void addContextListener(final IContextListener listener) { if (listener == null) { throw new NullPointerException(); } if (listeners == null) { listeners = new HashSet<>(); } listeners.add(listener); } @Override public final int compareTo(final Object object) { final Context scheme = (Context) object; int compareTo = Util.compare(this.id, scheme.id); if (compareTo == 0) { compareTo = Util.compare(this.name, scheme.name); if (compareTo == 0) { compareTo = Util.compare(this.parentId, scheme.parentId); if (compareTo == 0) { compareTo = Util.compare(this.description, scheme.description); if (compareTo == 0) { compareTo = Util.compare(this.defined, scheme.defined); } } } } return compareTo; } /** * <p> * Defines this context by giving it a name, and possibly a description and * a parent identifier as well. The defined property automatically becomes * <code>true</code>. * </p> * <p> * Notification is sent to all listeners that something has changed. * </p> * * @param name * The name of this context; must not be <code>null</code>. * @param description * The description for this context; may be <code>null</code>. * @param parentId * The parent identifier for this context; may be * <code>null</code>. */ public final void define(final String name, final String description, final String parentId) { if (name == null) { throw new NullPointerException("The name of a context cannot be null"); //$NON-NLS-1$ } final boolean definedChanged = !this.defined; this.defined = true; final boolean nameChanged = !Util.equals(this.name, name); this.name = name; final boolean descriptionChanged = !Util.equals(this.description, description); this.description = description; final boolean parentIdChanged = !Util.equals(this.parentId, parentId); this.parentId = parentId; fireContextChanged(new ContextEvent(this, definedChanged, nameChanged, descriptionChanged, parentIdChanged)); } /** * Notifies all listeners that this context has changed. This sends the * given event to all of the listeners, if any. * * @param event * The event to send to the listeners; must not be * <code>null</code>. */ private final void fireContextChanged(final ContextEvent event) { if (event == null) { throw new NullPointerException("Cannot send a null event to listeners."); //$NON-NLS-1$ } if (listeners == null) { return; } final Iterator<IContextListener> listenerItr = listeners.iterator(); while (listenerItr.hasNext()) { final IContextListener listener = listenerItr.next(); listener.contextChanged(event); } } /** * Returns the identifier of the parent of this instance. * <p> * Notification is sent to all registered listeners if this property * changes. * </p> * * @return the identifier of the parent of this instance. May be * <code>null</code>. * @throws NotDefinedException * if this instance is not defined. */ public final String getParentId() throws NotDefinedException { if (!defined) { throw new NotDefinedException("Cannot get the parent identifier from an undefined context. " //$NON-NLS-1$ + id); } return parentId; } /** * Unregisters an instance of <code>IContextListener</code> listening for * changes to properties of this instance. * * @param contextListener * the instance to unregister. Must not be <code>null</code>. * If an attempt is made to unregister an instance which is not * already registered with this instance, no operation is * performed. */ public final void removeContextListener(final IContextListener contextListener) { if (contextListener == null) { throw new NullPointerException("Cannot remove a null listener."); //$NON-NLS-1$ } if (listeners == null) { return; } listeners.remove(contextListener); if (listeners.isEmpty()) { listeners = null; } } /** * The string representation of this context -- for debugging purposes only. * This string should not be shown to an end user. * * @return The string representation; never <code>null</code>. */ @Override public final String toString() { if (string == null) { final StringBuffer stringBuffer = new StringBuffer(); stringBuffer.append("Context("); //$NON-NLS-1$ stringBuffer.append(id); stringBuffer.append(','); stringBuffer.append(name); stringBuffer.append(','); stringBuffer.append(description); stringBuffer.append(','); stringBuffer.append(parentId); stringBuffer.append(','); stringBuffer.append(defined); stringBuffer.append(')'); string = stringBuffer.toString(); } return string; } /** * Makes this context become undefined. This has the side effect of changing * the name, description and parent identifier to <code>null</code>. * Notification is sent to all listeners. */ @Override public final void undefine() { string = null; final boolean definedChanged = defined; defined = false; final boolean nameChanged = name != null; name = null; final boolean descriptionChanged = description != null; description = null; final boolean parentIdChanged = parentId != null; parentId = null; fireContextChanged(new ContextEvent(this, definedChanged, nameChanged, descriptionChanged, parentIdChanged)); } }