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

import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.PlatformObject;

import org.eclipse.ltk.core.refactoring.Change;
import org.eclipse.ltk.core.refactoring.RefactoringStatus;
import org.eclipse.ltk.core.refactoring.TextChange;

import org.eclipse.ltk.internal.core.refactoring.ParticipantDescriptor;

/**
 * A refactoring participant can participate in the condition checking and
 * change creation of a refactoring processor.
 * <p>
 * If the severity of the condition checking result is {@link RefactoringStatus#FATAL}
 * then the whole refactoring will not be carried out. 
 * </p>
 * <p>
 * The change created from a participant <em>MUST</em> not conflict with any changes
 * provided by other participants or the refactoring itself. To ensure this a participant
 * is only allowed to manipulate resources belonging to its domain. As of 3.1 this got
 * relaxed for textual resources. A participant can now change a textual resource already
 * manipulated by the processor as long as both are manipulating different regions in the
 * file (see {@link #createChange(IProgressMonitor)} and {@link #getTextChange(Object)}). 
 * For example a rename type participant updating launch configuration is only allowed to 
 * update launch configurations or shared textual resources. If a change conflicts with 
 * another change during execution then the participant who created the change will be 
 * disabled for the rest of the eclipse session.
 * </p>
 * <p>
 * A refactoring participant can not assume that all resources are saved before any 
 * methods are called on it. Therefore a participant must be able to deal with unsaved
 * resources.
 * </p>
 * <p>
 * This class should be subclassed by clients wishing to provide special refactoring 
 * participants extension points.
 * </p>
 * 
 * @see org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor
 * 
 * @since 3.0
 */
public abstract class RefactoringParticipant extends PlatformObject {
	
	private RefactoringProcessor fProcessor;
	
	private ParticipantDescriptor fDescriptor;
	
	/**
	 * Returns the processor that is associated with this participant. 
	 * 
	 * @return the processor that is associated with this participant
	 */
	public RefactoringProcessor getProcessor() {
		return fProcessor;
	}
	
	/**
	 * Initializes the participant. This method is called by the framework when a
	 * participant gets instantiated. 
	 * <p>
	 * This method isn't intended to be extended or reimplemented by clients.
	 * </p>
	 * @param processor the processor this participant is associated with
	 * @param element the element to be refactored
	 * @param arguments the refactoring arguments
	 * 
	 * @return <code>true</code> if the participant could be initialized;
	 *  otherwise <code>false</code> is returned. If <code>false</code> is
	 *  returned then the participant will not be added to the refactoring. 
	 * 
	 * @see #initialize(Object)
	 */
	public boolean initialize(RefactoringProcessor processor, Object element, RefactoringArguments arguments) {
		Assert.isNotNull(processor);
		Assert.isNotNull(arguments);
		fProcessor= processor;
		initialize(arguments);
		return initialize(element);
	}
	
	/**
	 * Initializes the participant with the element to be refactored.
	 * If this method returns <code>false</code> then the framework
	 * will consider the participant as not being initialized and the 
	 * participant will be dropped by the framework.
	 * 
	 * @param element the element to be refactored
	 * 
	 * @return <code>true</code> if the participant could be initialized;
	 *  otherwise <code>false</code> is returned.
	 */
	protected abstract boolean initialize(Object element);
	
	/**
	 * Initializes the participant with the refactoring arguments
	 * 
	 * @param arguments the refactoring arguments
	 */
	protected abstract void initialize(RefactoringArguments arguments);
	
	/**
	 * Returns a human readable name of this participant.
	 * 
	 * @return a human readable name
	 */
	public abstract String getName();
	
	/**
	 * Checks the conditions of the refactoring participant. 
	 * <p>
	 * The refactoring is considered as not being executable if the returned status
	 * has the severity of <code>RefactoringStatus#FATAL</code>.
	 * </p>
	 * <p>
	 * This method can be called more than once.
	 * </p>
	 * 
	 * @param pm a progress monitor to report progress
	 * @param context a condition checking context to collect shared condition checks
	 * 
	 * @return a refactoring status. If the status is <code>RefactoringStatus#FATAL</code>
	 *  the refactoring is considered as not being executable.
	 * 
	 * @throws OperationCanceledException if the condition checking got canceled
	 * 
	 * @see org.eclipse.ltk.core.refactoring.Refactoring#checkInitialConditions(IProgressMonitor)
	 * @see RefactoringStatus#FATAL
	 */ 		
	public abstract RefactoringStatus checkConditions(IProgressMonitor pm, CheckConditionsContext context) throws OperationCanceledException;
	
	/**
	 * Creates a {@link Change}object that contains the workspace modifications
	 * of this participant. The changes provided by a participant <em>must</em>
	 * not conflict with any change provided by other participants or by the
	 * refactoring itself.
	 * <p>
	 * If the change conflicts with any change provided by other participants or
	 * by the refactoring itself then change execution will fail and the
	 * participant will be disabled for the rest of the eclipse session.
	 * </p>
	 * <p>
	 * If an exception occurs while creating the change the refactoring can not
	 * be carried out and the participant will be disabled for the rest of the
	 * eclipse session.
	 * </p>
	 * <p>
	 * As of 3.1 a participant can manipulate text resource already manipulated by
	 * the processor as long as the textual manipulations don't conflict (e.g.
	 * the participant manipulates a different region of the text resource).
	 * The method must not return those changes in its change tree since the change 
	 * is already part of another change tree. If the participant only manipulates 
	 * shared changes then it can return <code>null</code> to indicate that it didn't
	 * create own changes. A shared text change can be access via the method 
	 * {@link #getTextChange(Object)}. 
	 * </p>
	 * 
	 * @param pm a progress monitor to report progress
	 * 
	 * @return the change representing the workspace modifications or <code>null</code>
	 *  if no changes are made
	 * 
	 * @throws CoreException if an error occurred while creating the change
	 * 
	 * @throws OperationCanceledException if the condition checking got canceled
	 */
	public abstract Change createChange(IProgressMonitor pm) throws CoreException, OperationCanceledException;

	/**
	 * Returns the text change for the given element or <code>null</code>
	 * if a text change doesn't exist. This method only returns a valid
	 * result during change creation. Outside of change creation always
	 * <code>null</code> is returned.
	 * 
	 * @param element the element to be modified for which a text change
	 *  is requested
	 *  
	 * @return the text change or <code>null</code> if no text change exists
	 *  for the element
	 *  
	 *  @since 3.1
	 */
	public TextChange getTextChange(Object element) {
		return getProcessor().getRefactoring().getTextChange(element);
	}
	
	//---- helper method ----------------------------------------------------
	
	/* package */ void setDescriptor(ParticipantDescriptor descriptor) {
		Assert.isNotNull(descriptor);
		fDescriptor= descriptor;
	}
	
	/* package */ ParticipantDescriptor getDescriptor() {
		return fDescriptor;
	}
}
