/*******************************************************************************
* Copyright (c) 2014 Willink Transformations 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:
* E.D.Willink - initial API and implementation
*******************************************************************************/
package org.eclipse.ocl.xtext.base.ui.commands;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.commands.HandlerEvent;
import org.eclipse.core.commands.IHandler2;
import org.eclipse.core.commands.IHandlerListener;
import org.eclipse.core.runtime.ListenerList;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.handlers.HandlerUtil;
/**
* <p>
* This class adapts instances of <code>IAction</code> to <code>IHandler</code>.
* </p>
*
* Based on ActionHandler,AbstractHandler,EventManager
*/
public final class ValidateHandler implements IHandler2
{
/**
* An empty array that can be returned from a call to
* {@link #getListeners()} when {@link #listenerList} is <code>null</code>.
*/
private static final Object[] EMPTY_ARRAY = new Object[0];
/**
* A collection of objects listening to changes to this manager. This
* collection is <code>null</code> if there are no listeners.
*/
@SuppressWarnings("rawtypes") // pre-Neon compatibility; Neon adds generics
private transient ListenerList listenerList = null;
/**
* Track this base class enabled state.
*/
private boolean baseEnabled = true;
/**
* The wrapped action. This value is never <code>null</code>.
*/
private final ValidateCommand action;
/**
* The property change listener hooked on to the action. This is initialized
* when the first listener is attached to this handler, and is removed when
* the handler is disposed or the last listener is removed.
*/
private IPropertyChangeListener propertyChangeListener;
/**
* Creates a new instance of this class given an instance of
* <code>IAction</code>.
*/
public ValidateHandler() {
this.action = new ValidateCommand();
}
@Override
public final void addHandlerListener(final IHandlerListener handlerListener) {
if (!hasListeners()) {
attachListener();
}
addListenerObject(handlerListener);
}
/**
* Adds a listener to this manager that will be notified when this manager's
* state changes.
*
* @param listener
* The listener to be added; must not be <code>null</code>.
*/
@SuppressWarnings({"rawtypes", "unchecked"}) // pre-Neon compatibility; Neon adds generics
protected synchronized final void addListenerObject(final Object listener) {
if (listenerList == null) {
listenerList = new ListenerList(ListenerList.IDENTITY);
}
listenerList.add(listener);
}
/**
* When a listener is attached to this handler, then this registers a
* listener with the underlying action.
*/
private final void attachListener() {
if (propertyChangeListener == null) {
propertyChangeListener = new IPropertyChangeListener() {
// @Override
@Override
public final void propertyChange(
final PropertyChangeEvent propertyChangeEvent) {
final String property = propertyChangeEvent.getProperty();
fireHandlerChanged(new HandlerEvent(ValidateHandler.this,
IAction.ENABLED.equals(property),
IAction.HANDLED.equals(property)));
}
};
}
this.action.addPropertyChangeListener(propertyChangeListener);
}
/**
* Clears all of the listeners from the listener list.
*/
protected synchronized final void clearListeners() {
if (listenerList != null) {
listenerList.clear();
}
}
/**
* When no more listeners are registered, then this is used to removed the
* property change listener from the underlying action.
*/
private final void detachListener() {
this.action.removePropertyChangeListener(propertyChangeListener);
propertyChangeListener = null;
}
/**
* Removes the property change listener from the action.
*
* @see org.eclipse.core.commands.IHandler#dispose()
*/
@Override
public final void dispose() {
if (hasListeners()) {
action.removePropertyChangeListener(propertyChangeListener);
}
}
/* @Override
public final Object execute(final ExecutionEvent event)
throws ExecutionException {
if ((action.getStyle() == IAction.AS_CHECK_BOX)
|| (action.getStyle() == IAction.AS_RADIO_BUTTON)) {
action.setChecked(!action.isChecked());
}
final Object trigger = event.getTrigger();
try {
ISelection currentSelection = HandlerUtil.getCurrentSelection(event);
if (trigger instanceof Event) {
action.runWithEvent((Event) trigger);
} else {
action.runWithEvent(new Event());
}
} catch (Exception e) {
throw new ExecutionException(
"While executing the action, an exception occurred", e); //$NON-NLS-1$
}
return null;
} */
@Override
public Object execute(ExecutionEvent event) throws ExecutionException {
IWorkbenchPart part = HandlerUtil.getActivePart(event);
action.setActiveWorkbenchPart(part);
ISelection selection = HandlerUtil.getCurrentSelection(event);
if (selection instanceof IStructuredSelection) {
setEnabled(action.updateSelection((IStructuredSelection) selection));
} else {
setEnabled(false);
}
action.run();
return null;
}
/**
* Fires an event to all registered listeners describing changes to this
* instance.
* <p>
* Subclasses may extend the definition of this method (i.e., if a different
* type of listener can be attached to a subclass). This is used primarily
* for support of <code>AbstractHandler</code> in
* <code>org.eclipse.ui.workbench</code>, and clients should be wary of
* overriding this behaviour. If this method is overridden, then the first
* line of the method should be "<code>super.fireHandlerChanged(handlerEvent);</code>".
* </p>
*
* @param handlerEvent
* the event describing changes to this instance. Must not be
* <code>null</code>.
*/
protected void fireHandlerChanged(final HandlerEvent handlerEvent) {
if (handlerEvent == null) {
throw new NullPointerException();
}
final Object[] listeners = getListeners();
for (int i = 0; i < listeners.length; i++) {
final IHandlerListener listener = (IHandlerListener) listeners[i];
listener.handlerChanged(handlerEvent);
}
}
/**
* Returns the action associated with this handler
*
* @return the action associated with this handler (not null)
*/
public final IAction getAction() {
return action;
}
/**
* Returns the listeners attached to this event manager.
*
* @return The listeners currently attached; may be empty, but never
* <code>null</code>
*/
protected final Object[] getListeners() {
@SuppressWarnings("rawtypes") // pre-Neon compatibility; Neon adds generics
final ListenerList list = listenerList;
if (list == null) {
return EMPTY_ARRAY;
}
return list.getListeners();
}
@Override
public final boolean isEnabled() {
return action.isEnabled();
}
@Override
public final boolean isHandled() {
return action.isHandled();
}
/**
* <p>
* Returns true iff there is one or more IHandlerListeners attached to this
* AbstractHandler.
* </p>
* <p>
* Subclasses may extend the definition of this method (i.e., if a different
* type of listener can be attached to a subclass). This is used primarily
* for support of <code>AbstractHandler</code> in
* <code>org.eclipse.ui.workbench</code>, and clients should be wary of
* overriding this behaviour. If this method is overridden, then the return
* value should include "<code>super.hasListeners() ||</code>".
* </p>
*
* @return true iff there is one or more IHandlerListeners attached to this
* AbstractHandler
*/
protected boolean hasListeners() {
return isListenerAttached();
}
/**
* Whether one or more listeners are attached to the manager.
*
* @return <code>true</code> if listeners are attached to the manager;
* <code>false</code> otherwise.
*/
protected final boolean isListenerAttached() {
return listenerList != null;
}
@Override
public final void removeHandlerListener(final IHandlerListener handlerListener) {
removeListenerObject(handlerListener);
if (!hasListeners()) {
detachListener();
}
}
/**
* Removes a listener from this manager.
*
* @param listener
* The listener to be removed; must not be <code>null</code>.
*/
protected synchronized final void removeListenerObject(final Object listener) {
if (listenerList != null) {
listenerList.remove(listener);
if (listenerList.isEmpty()) {
listenerList = null;
}
}
}
/**
* Allow the default {@link #isEnabled()} to answer our enabled state. It
* will fire a HandlerEvent if necessary. If clients use this method they
* should also consider overriding {@link #setEnabled(Object)} so they can
* be notified about framework execution contexts.
*
* @param state
* the enabled state
*/
protected void setBaseEnabled(boolean state) {
if (baseEnabled == state) {
return;
}
baseEnabled = state;
fireHandlerChanged(new HandlerEvent(this, true, false));
}
/**
* Called by the framework to allow the handler to update its enabled state
* by extracting the same information available at execution time. Clients
* may override if they need to extract information from the application
* context.
*
* @param evaluationContext
* the application context. May be <code>null</code>
* @see #setBaseEnabled(boolean)
*/
@Override
public void setEnabled(Object evaluationContext) {
}
@Override
public final String toString() {
final StringBuffer buffer = new StringBuffer();
buffer.append("ActionHandler("); //$NON-NLS-1$
buffer.append(action);
buffer.append(')');
return buffer.toString();
}
}