/******************************************************************************* * Copyright (c) 2015, 2017 itemis AG 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: * Alexander Nyßen (itemis AG) - initial API and implementation * *******************************************************************************/ package org.eclipse.gef.mvc.fx.policies; import org.eclipse.gef.common.adapt.IAdaptable; import org.eclipse.gef.mvc.fx.operations.ITransactionalOperation; import org.eclipse.gef.mvc.fx.parts.IVisualPart; import javafx.scene.Node; /** * Abstract base implementation of {@link IPolicy} that is transactional. * * @author anyssen * */ public abstract class AbstractPolicy extends IAdaptable.Bound.Impl<IVisualPart<? extends Node>> implements IPolicy { private ITransactionalOperation operation; private boolean initialized; /** * Checks whether this {@link AbstractPolicy} is initialized and throws an * IllegalStateException if not. */ protected void checkInitialized() { if (!initialized) { throw new IllegalStateException("Not yet initialized!"); } } private void checkUninitialized() { if (initialized) { throw new IllegalStateException("Already initialized"); } } /** * Returns an {@link ITransactionalOperation} that performs all * manipulations applied by the policy since the previous {@link #init()} * call. * * @return An {@link ITransactionalOperation} that performs all * manipulations applied by the policy since the last * {@link #init()} call. */ @Override public ITransactionalOperation commit() { checkInitialized(); // XXX: We need to locally execute the operation first to ensure // the visuals and content reflect the target state of the operation. // After this, we may safely omit the operation as commit operation in // case its a no-op. ITransactionalOperation commit = null; try { locallyExecuteOperation(); // clear operation and return current one (and formerly pushed // operations) commit = getOperation(); } catch (Exception e) { throw e; } finally { // after commit, we need to be re-initialized initialized = false; operation = null; } if (commit != null) { if (!commit.isNoOp()) { return commit; } } return null; } /** * Creates an {@link ITransactionalOperation} that is used to encapsulate * the changes that are applied by this {@link AbstractPolicy} through its * "work" methods. The created operation should allow for * {@link #locallyExecuteOperation() local execution} at each time. * * @return A new {@link ITransactionalOperation} to encapsulate all applied * changes. */ protected abstract ITransactionalOperation createOperation(); /** * Returns the {@link ITransactionalOperation} that is used to encapsulate * the changes that are applied by this {@link AbstractPolicy} through its * "work" methods. * * @return A new {@link ITransactionalOperation} to encapsulate all applied * changes. */ protected final ITransactionalOperation getOperation() { return operation; } /** * Initializes the policy, so that the policy's "work" methods can be used. * Calling a "work" method while the policy is not initialized will result * in an {@link IllegalStateException}, as well as re-initializing before * committing or rolling back. */ @Override public void init() { checkUninitialized(); initialized = true; operation = createOperation(); } /** * Returns whether this {@link AbstractPolicy} is initialized or not. * * @return <code>true</code> if this {@link AbstractPolicy} is initialized, * <code>false</code> otherwise. */ protected boolean isInitialized() { return initialized; } /** * Locally executes the {@link ITransactionalOperation} that is updated by * this policy, i.e. not on the operation history. Maybe used in the "work" * operations of subclasses. */ protected void locallyExecuteOperation() { try { // XXX: We may not skip the local execution of the operation // if it is a no-op, because the visual or content // might already be in a state that is diverse from the initial // state of the operation (so execute might have an actual affect). if (operation != null) { operation.execute(null, null); } } catch (Exception e) { if (e instanceof RuntimeException) { throw (RuntimeException) e; } throw new IllegalArgumentException(e); } } /** * Locally undoes the {@link ITransactionalOperation} that is updated by * this policy, i.e. not on the operation history. */ private void locallyUndoOperation() { try { // XXX: We may not skip undo in case the operation is a // no-op when executing it locally, because the visual or content // might already be in a state that is diverse from the initial // state (so undo might have an actual affect). if (operation != null) { operation.undo(null, null); } } catch (Exception e) { if (e instanceof RuntimeException) { throw (RuntimeException) e; } throw new IllegalArgumentException(e); } } /** * Puts back this policy into an uninitialized state, reverting any changes * that have been applied via the policy's work methods since the preceding * {@link #init()} call. */ @Override public void rollback() { // after rollback, we need to be re-initialized initialized = false; // clear operation and return current one (and formerly pushed // operations) try { locallyUndoOperation(); } catch (Exception e) { throw e; } finally { operation = null; } } }