001: /*******************************************************************************
002: * Copyright (c) 2000, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.ui.internal.handlers;
011:
012: import java.util.Map;
013:
014: import org.eclipse.core.commands.AbstractHandler;
015: import org.eclipse.core.commands.ExecutionEvent;
016: import org.eclipse.core.commands.ExecutionException;
017: import org.eclipse.core.commands.HandlerEvent;
018: import org.eclipse.core.commands.IHandler;
019: import org.eclipse.core.commands.IHandlerListener;
020: import org.eclipse.core.expressions.EvaluationResult;
021: import org.eclipse.core.expressions.Expression;
022: import org.eclipse.core.expressions.IEvaluationContext;
023: import org.eclipse.core.runtime.CoreException;
024: import org.eclipse.core.runtime.IConfigurationElement;
025: import org.eclipse.core.runtime.IStatus;
026: import org.eclipse.core.runtime.Status;
027: import org.eclipse.jface.util.IPropertyChangeListener;
028: import org.eclipse.jface.util.PropertyChangeEvent;
029: import org.eclipse.ui.commands.IElementUpdater;
030: import org.eclipse.ui.internal.WorkbenchPlugin;
031: import org.eclipse.ui.internal.services.IEvaluationReference;
032: import org.eclipse.ui.internal.services.IEvaluationService;
033: import org.eclipse.ui.internal.util.BundleUtility;
034: import org.eclipse.ui.menus.UIElement;
035:
036: /**
037: * <p>
038: * A proxy for a handler that has been defined in XML. This delays the class
039: * loading until the handler is really asked for information (besides the
040: * priority or the command identifier). Asking a proxy for anything but the
041: * attributes defined publicly in this class will cause the proxy to instantiate
042: * the proxied handler.
043: * </p>
044: *
045: * @since 3.0
046: */
047: public final class HandlerProxy extends AbstractHandler implements
048: IElementUpdater {
049:
050: /**
051: *
052: */
053: private static final String PROP_ENABLED = "enabled"; //$NON-NLS-1$
054:
055: /**
056: * The configuration element from which the handler can be created. This
057: * value will exist until the element is converted into a real class -- at
058: * which point this value will be set to <code>null</code>.
059: */
060: private IConfigurationElement configurationElement;
061:
062: /**
063: * The <code>enabledWhen</code> expression for the handler. Only if this
064: * expression evaluates to <code>true</code> (or the value is
065: * <code>null</code>) should we consult the handler.
066: */
067: private final Expression enabledWhenExpression;
068:
069: /**
070: * The real handler. This value is <code>null</code> until the proxy is
071: * forced to load the real handler. At this point, the configuration element
072: * is converted, nulled out, and this handler gains a reference.
073: */
074: private IHandler handler = null;
075:
076: /**
077: * The name of the configuration element attribute which contains the
078: * information necessary to instantiate the real handler.
079: */
080: private final String handlerAttributeName;
081:
082: private IHandlerListener handlerListener;
083:
084: /**
085: * The evaluation service to use when evaluating
086: * <code>enabledWhenExpression</code>. This value may be
087: * <code>null</code> only if the <code>enabledWhenExpression</code> is
088: * <code>null</code>.
089: */
090: private IEvaluationService evaluationService;
091:
092: private IPropertyChangeListener enablementListener;
093:
094: private IEvaluationReference enablementRef;
095:
096: private boolean proxyEnabled;
097:
098: /**
099: * Constructs a new instance of <code>HandlerProxy</code> with all the
100: * information it needs to try to avoid loading until it is needed.
101: *
102: * @param configurationElement
103: * The configuration element from which the real class can be
104: * loaded at run-time; must not be <code>null</code>.
105: * @param handlerAttributeName
106: * The name of the attibute or element containing the handler
107: * executable extension; must not be <code>null</code>.
108: */
109: public HandlerProxy(
110: final IConfigurationElement configurationElement,
111: final String handlerAttributeName) {
112: this (configurationElement, handlerAttributeName, null, null);
113: }
114:
115: /**
116: * Constructs a new instance of <code>HandlerProxy</code> with all the
117: * information it needs to try to avoid loading until it is needed.
118: *
119: * @param configurationElement
120: * The configuration element from which the real class can be
121: * loaded at run-time; must not be <code>null</code>.
122: * @param handlerAttributeName
123: * The name of the attribute or element containing the handler
124: * executable extension; must not be <code>null</code>.
125: * @param enabledWhenExpression
126: * The name of the element containing the enabledWhen expression.
127: * This should be a child of the
128: * <code>configurationElement</code>. If this value is
129: * <code>null</code>, then there is no enablement expression
130: * (i.e., enablement will be delegated to the handler when
131: * possible).
132: * @param evaluationService
133: * The evaluation service to manage enabledWhen expressions
134: * trying to evaluate the <code>enabledWhenExpression</code>.
135: * This value may be <code>null</code> only if the
136: * <code>enabledWhenExpression</code> is <code>null</code>.
137: */
138: public HandlerProxy(
139: final IConfigurationElement configurationElement,
140: final String handlerAttributeName,
141: final Expression enabledWhenExpression,
142: final IEvaluationService evaluationService) {
143: if (configurationElement == null) {
144: throw new NullPointerException(
145: "The configuration element backing a handler proxy cannot be null"); //$NON-NLS-1$
146: }
147:
148: if (handlerAttributeName == null) {
149: throw new NullPointerException(
150: "The attribute containing the handler class must be known"); //$NON-NLS-1$
151: }
152:
153: if ((enabledWhenExpression != null)
154: && (evaluationService == null)) {
155: throw new NullPointerException(
156: "We must have a handler service and evaluation service to support the enabledWhen expression"); //$NON-NLS-1$
157: }
158:
159: this .configurationElement = configurationElement;
160: this .handlerAttributeName = handlerAttributeName;
161: this .enabledWhenExpression = enabledWhenExpression;
162: this .evaluationService = evaluationService;
163: if (enabledWhenExpression != null) {
164: setProxyEnabled(false);
165: registerEnablement();
166: } else {
167: setProxyEnabled(true);
168: }
169: }
170:
171: /**
172: *
173: */
174: private void registerEnablement() {
175: enablementRef = evaluationService.addEvaluationListener(
176: enabledWhenExpression, getEnablementListener(),
177: PROP_ENABLED, null);
178: }
179:
180: void setEnabledFor(IEvaluationContext context)
181: throws ExecutionException {
182: if (enabledWhenExpression != null) {
183: try {
184: setProxyEnabled(enabledWhenExpression.evaluate(context) == EvaluationResult.TRUE);
185: } catch (CoreException e) {
186: throw new ExecutionException(e.getMessage(), e);
187: }
188: }
189: }
190:
191: void setProxyEnabled(boolean enabled) {
192: proxyEnabled = enabled;
193: }
194:
195: boolean getProxyEnabled() {
196: return proxyEnabled;
197: }
198:
199: /**
200: * @return
201: */
202: private IPropertyChangeListener getEnablementListener() {
203: if (enablementListener == null) {
204: enablementListener = new IPropertyChangeListener() {
205: public void propertyChange(PropertyChangeEvent event) {
206: if (event.getProperty() == PROP_ENABLED) {
207: setProxyEnabled(event.getNewValue() == null ? false
208: : ((Boolean) event.getNewValue())
209: .booleanValue());
210: fireHandlerChanged(new HandlerEvent(
211: HandlerProxy.this , true, false));
212: }
213: }
214: };
215: }
216: return enablementListener;
217: }
218:
219: /**
220: * Passes the dipose on to the proxied handler, if it has been loaded.
221: */
222: public final void dispose() {
223: if (handler != null) {
224: if (handlerListener != null) {
225: handler.removeHandlerListener(handlerListener);
226: handlerListener = null;
227: }
228: handler.dispose();
229: handler = null;
230: }
231: if (enablementListener != null) {
232: evaluationService.removeEvaluationListener(enablementRef);
233: enablementRef = null;
234: enablementListener = null;
235: }
236: }
237:
238: public final Object execute(final ExecutionEvent event)
239: throws ExecutionException {
240: if (loadHandler()) {
241: return handler.execute(event);
242: }
243:
244: return null;
245: }
246:
247: public final boolean isEnabled() {
248: if (enabledWhenExpression != null) {
249: // proxyEnabled reflects the enabledWhen clause
250: if (!getProxyEnabled()) {
251: return false;
252: }
253: if (isOkToLoad() && loadHandler()) {
254: return handler.isEnabled();
255: }
256:
257: return true;
258: }
259:
260: /*
261: * There is no enabled when expression, so we just need to consult the
262: * handler.
263: */
264: if (isOkToLoad() && loadHandler()) {
265: return handler.isEnabled();
266: }
267: return true;
268: }
269:
270: public final boolean isHandled() {
271: if (configurationElement != null) {
272: return true;
273: }
274:
275: if (isOkToLoad() && loadHandler()) {
276: return handler.isHandled();
277: }
278:
279: return false;
280: }
281:
282: /**
283: * Loads the handler, if possible. If the handler is loaded, then the member
284: * variables are updated accordingly.
285: *
286: * @return <code>true</code> if the handler is now non-null;
287: * <code>false</code> otherwise.
288: */
289: private final boolean loadHandler() {
290: if (handler == null) {
291: // Load the handler.
292: try {
293: if (configurationElement != null) {
294: handler = (IHandler) configurationElement
295: .createExecutableExtension(handlerAttributeName);
296: configurationElement = null;
297: handler.addHandlerListener(getHandlerListener());
298: return true;
299: }
300:
301: } catch (final ClassCastException e) {
302: final String message = "The proxied handler was the wrong class"; //$NON-NLS-1$
303: final IStatus status = new Status(IStatus.ERROR,
304: WorkbenchPlugin.PI_WORKBENCH, 0, message, e);
305: WorkbenchPlugin.log(message, status);
306: configurationElement = null;
307:
308: } catch (final CoreException e) {
309: final String message = "The proxied handler for '" + configurationElement.getAttribute(handlerAttributeName) //$NON-NLS-1$
310: + "' could not be loaded"; //$NON-NLS-1$
311: IStatus status = new Status(IStatus.ERROR,
312: WorkbenchPlugin.PI_WORKBENCH, 0, message, e);
313: WorkbenchPlugin.log(message, status);
314: configurationElement = null;
315: }
316: return false;
317: }
318:
319: return true;
320: }
321:
322: /**
323: * @return
324: */
325: private IHandlerListener getHandlerListener() {
326: if (handlerListener == null) {
327: handlerListener = new IHandlerListener() {
328: public void handlerChanged(HandlerEvent handlerEvent) {
329: fireHandlerChanged(new HandlerEvent(
330: HandlerProxy.this , handlerEvent
331: .isEnabledChanged(), handlerEvent
332: .isHandledChanged()));
333: }
334: };
335: }
336: return handlerListener;
337: }
338:
339: public final String toString() {
340: if (handler == null) {
341: if (configurationElement != null) {
342: return configurationElement
343: .getAttribute(handlerAttributeName);
344: }
345: return "HandlerProxy()"; //$NON-NLS-1$
346: }
347:
348: return handler.toString();
349: }
350:
351: private boolean isOkToLoad() {
352: if (configurationElement != null) {
353: final String bundleId = configurationElement
354: .getContributor().getName();
355: return BundleUtility.isActive(bundleId);
356: }
357: return true;
358: }
359:
360: /*
361: * (non-Javadoc)
362: *
363: * @see org.eclipse.ui.commands.IElementUpdater#updateElement(org.eclipse.ui.menus.UIElement,
364: * java.util.Map)
365: */
366: public void updateElement(UIElement element, Map parameters) {
367: if (handler != null && handler instanceof IElementUpdater) {
368: ((IElementUpdater) handler).updateElement(element,
369: parameters);
370: }
371: }
372: }
|