/*******************************************************************************
 * Copyright (c) 2000, 2015 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.ltk.core.refactoring;

import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Platform;

/**
 * An abstract base implementation for object representing a generic change
 * to the workbench. A <code>Change</code> object is typically created by
 * calling {@link Refactoring#createChange(IProgressMonitor)}. This class should be
 * subclassed by clients wishing to provide new changes.
 * <p>
 * Changes are best executed by using a {@link PerformChangeOperation}. If clients
 * execute a change directly then the following life cycle has to be honored:</p>
 * <ul>
 *   <li>After a single change or a tree of changes has been created, the
 *       method <code>initializeValidationData</code> has to be called.</li>
 *   <li>The method <code>isValid</code> can be used to determine if a change
 *       can still be applied to the workspace. If the method returns a {@link
 *       RefactoringStatus} with a severity of FATAL then the change has to be
 *       treated as invalid. Performing an invalid change isn't allowed and
 *       results in an unspecified result. This method can be called multiple
 *       times.
 *   <li>Then the method <code>perform</code> can be called. A disabled change
 *       must not be executed. The <code>perform</code> method can only be called
 *       once. After a change has been executed, only the method <code>dispose</code>
 *       must be called.</li>
 *   <li>the method <code>dispose</code> has to be called either after the
 *       <code>perform</code> method
 *       has been called or if a change is no longer needed. The second case
 *       for example occurs when the undo stack gets flushed and all change
 *       objects managed by the undo stack are no longer needed. The method
 *       <code>dispose</code> is typically implemented to unregister listeners
 *       registered during the
 *       method <code>initializeValidationData</code>. There is no guarantee
 *       that <code>initializeValidationData</code>, <code>isValid</code>,
 *       or <code>perform</code> has been called before <code>dispose</code>
 *       is called.
 * </ul>
 * Here is a code snippet that can be used to execute a change:
 * <pre>
 *   Change change= createChange();
 *   try {
 *     change.initializeValidationData(pm);
 *
 *     ....
 *
 *     if (!change.isEnabled())
 *         return;
 *     RefactoringStatus valid= change.isValid(new SubProgressMonitor(pm, 1));
 *     if (valid.hasFatalError())
 *         return;
 *     Change undo= change.perform(new SubProgressMonitor(pm, 1));
 *     if (undo != null) {
 *        undo.initializeValidationData(new SubProgressMonitor(pm, 1));
 *        // do something with the undo object
 *     }
 *   } finally {
 *     change.dispose();
 *   }
 * </pre>
 * <p>
 * It is important that implementors of this abstract class provide an adequate
 * implementation of <code>isValid</code> and that they provide an undo change
 * via the return value of the method <code>perform</code>. If no undo can be
 * provided then the <code>perform</code> method is allowed to return <code>null</code>. But
 * implementors should be aware that not providing an undo object for a change
 * object that is part of a larger change tree will result in the fact that for
 * the whole change tree no undo object will be present.
 * </p>
 * <p>
 * Changes which are returned as top-level changes (e.g. by <code>Refactoring.createChange()</code>)
 * can optionally return a descriptor object of the refactoring which created this change object.
 * </p>
 * <p>
 * Clients may subclass this class.
 * </p>
 *
 * @since 3.0
 */
public abstract class Change implements IAdaptable {

	private Change fParent;
	private boolean fIsEnabled= true;

	/**
	 * Constructs a new change object.
	 */
	protected Change() {
	}

	/**
	 * Returns a descriptor of this change.
	 * <p>
	 * Subclasses of changes created by
	 * {@link Refactoring#createChange(IProgressMonitor)} should override this
	 * method to return a {@link RefactoringChangeDescriptor}. A change tree
	 * created by a particular refactoring is supposed to contain at most one
	 * change which returns a refactoring descriptor. Refactorings usually
	 * return an instance of {@link CompositeChange} in their
	 * {@link Refactoring#createChange(IProgressMonitor)} method which
	 * implements this method. The refactoring framework searches the change
	 * tree top-down until a refactoring descriptor is found.
	 * </p>
	 *
	 * @return a descriptor of this change, or <code>null</code> if this
	 *         change does not provide a change descriptor.
	 *
	 * @since 3.2
	 */
	public ChangeDescriptor getDescriptor() {
		return null;
	}

	/**
	 * Returns the human readable name of this change. The
	 * name <em>MUST</em> not be <code>null</code>.
	 *
	 * @return the human readable name of this change
	 */
	public abstract String getName();

	/**
	 * Returns whether this change is enabled or not. Disabled changes
	 * must not be executed.
	 *
	 * @return <code>true</code> if the change is enabled; <code>false</code>
	 *  otherwise.
	 */
	public boolean isEnabled() {
		return fIsEnabled;
	}

	/**
	 * Sets whether this change is enabled or not.
	 *
	 * @param enabled <code>true</code> to enable this change; <code>
	 *  false</code> otherwise
	 */
	public void setEnabled(boolean enabled) {
		fIsEnabled= enabled;
	}

	/**
	 * Sets the enablement state of this change in a shallow way.
	 * For changes having children this means that only this change's
	 * enablement state changes. The children are left untouched.
	 *
	 * @param enabled <code>true</code> to enable this change; <code>
	 *  false</code> otherwise
	 *
	 * @since 3.1
	 */
	public final void setEnabledShallow(boolean enabled) {
		fIsEnabled= enabled;
	}

	/**
	 * Returns the parent change. Returns <code>null</code> if no
	 * parent exists.
	 *
	 * @return the parent change
	 */
	public Change getParent() {
		return fParent;
	}

	/**
	 * Sets the parent of this change. Requires that this change isn't already
	 * connected to a parent. The parent can be <code>null</code> to disconnect
	 * this change from a parent.
	 *
	 * @param parent the parent of this change or <code>null</code>
	 */
	/* package */ void setParent(Change parent) {
		if (parent != null)
			Assert.isTrue(fParent == null);
		fParent= parent;
	}

	/**
	 * Hook method to initialize some internal state to provide an adequate answer
	 * for the <code>isValid</code> method. This method gets called after a change
	 * or a whole change tree has been created.
	 * <p>
	 * Typically this method is implemented in one of the following ways:
	 * <ul>
	 *   <li>the change hooks up a listener on some delta notification mechanism
	 *       and marks itself as invalid if it receives a certain delta. Is this
	 *       the case the implementor must take care of unhooking the listener
	 *       in <code>dispose</code>.</li>
	 *   <li>the change remembers some information allowing to decide if a change
	 *       object is still valid when <code>isValid</code> is called.</li>
	 * </ul>
	 * <p>
	 * For example, a change object that manipulates the content of an <code>IFile</code>
	 * could either listen to resource changes and detect that the file got changed or
	 * it could remember some content stamp and compare it with the actual content stamp
	 * when <code>isValid</code> is called.
	 * </p>
	 *
	 * @param pm a progress monitor
	 */
	public abstract void initializeValidationData(IProgressMonitor pm);

	/**
	 * Verifies that this change object is still valid and can be executed by calling
	 * <code>perform</code>. If a refactoring status  with a severity of {@link
	 * RefactoringStatus#FATAL} is returned then the change has to be treated as invalid
	 * and can no longer be executed. Performing such a change produces an unspecified
	 * result and will very likely throw an exception.
	 * <p>
	 * This method is also called by the {@link IUndoManager UndoManager} to decide if
	 * an undo or redo change is still valid and therefore can be executed.
	 * </p>
	 *
	 * @param pm a progress monitor.
	 *
	 * @return a refactoring status describing the outcome of the validation check
	 *
	 * @throws CoreException if an error occurred during validation check. The change
	 *  is to be treated as invalid if an exception occurs
	 *
	 * @throws OperationCanceledException if the validation check got canceled
	 */
	public abstract RefactoringStatus isValid(IProgressMonitor pm) throws CoreException, OperationCanceledException;

	/**
	 * Performs this change. If this method is called on an invalid or disabled change
	 * object the result is unspecified. Changes should in general not respond to
	 * {@link IProgressMonitor#isCanceled()} since canceling a change tree in the
	 * middle of its execution leaves the workspace in a half changed state.
	 *
	 * @param pm a progress monitor
	 *
	 * @return the undo change for this change object or <code>null</code> if no
	 *  undo is provided
	 *
	 * @throws CoreException if an error occurred during change execution
	 */
	public abstract Change perform(IProgressMonitor pm) throws CoreException;

	/**
	 * Disposes this change. Subclasses that override this method typically unregister listeners
	 * which got registered during the call to <code>
	 * initializeValidationData</code>.
	 * <p>
	 * Subclasses may override this method.
	 * </p>
	 */
	public void dispose() {
		// empty default implementation
	}

	/**
	 * Returns the element modified by this <code>Change</code>. The method may return
	 * <code>null</code> if the change isn't related to an element.
	 *
	 * @return the element modified by this change
	 */
	public abstract Object getModifiedElement();

	/**
	 * Returns the elements affected by this change or <code>null</code> if
	 * the affected elements cannot be determined. Returns an empty array
	 * if the change doesn't modify any elements.
	 * <p>
	 * This default implementation returns <code>null</code> to indicate that
	 * the affected elements are unknown. Subclasses should reimplement this method
	 * if they can compute the set of affected elements.
	 * </p>
	 *
	 * @return the elements affected by this change or <code>null</code> if
	 *  the affected elements cannot be determined
     *
     * @since 3.1
	 */
	public Object[] getAffectedObjects() {
		return null;
	}

	@Override
	public <T> T getAdapter(Class<T> adapter) {
		T result= Platform.getAdapterManager().getAdapter(this, adapter);
		if (result != null)
			return result;
		if (fParent != null)
			return fParent.getAdapter(adapter);
		return null;
	}
}
