/*******************************************************************************
 * Copyright (c) 2000, 2019 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
 *     Microsoft Corporation - copied to jdt.core.manipulation
 *******************************************************************************/
package org.eclipse.jdt.internal.corext.refactoring;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.SubProgressMonitor;

import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;

import org.eclipse.ltk.core.refactoring.RefactoringStatus;
import org.eclipse.ltk.core.refactoring.RefactoringStatusEntry;

import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.WorkingCopyOwner;
import org.eclipse.jdt.core.search.IJavaSearchScope;
import org.eclipse.jdt.core.search.SearchEngine;
import org.eclipse.jdt.core.search.SearchMatch;
import org.eclipse.jdt.core.search.SearchPattern;
import org.eclipse.jdt.core.search.SearchRequestor;

import org.eclipse.jdt.internal.core.manipulation.util.BasicElementLabels;
import org.eclipse.jdt.internal.corext.util.Messages;
import org.eclipse.jdt.internal.corext.util.SearchUtils;

/**
 * Helper class to use the search engine in refactorings.
 *
 * TODO:
 * - is inefficient: uses sets instead of lists, creates useless intermediate collections
 * - destroys locality by doing multiple passes over result sets instead of processing results in a pipeline
 * - does not allow users to modify search matches
 * - generates boilerplate error messages and has no way to configure them
 *
 * @since 3.1
 */
public final class RefactoringSearchEngine2 {

	/** Default implementation of a search requestor */
	private static class DefaultSearchRequestor implements IRefactoringSearchRequestor {

		@Override
		public final SearchMatch acceptSearchMatch(final SearchMatch match) {
			return match;
		}
	}

	/** Search requestor which only collects compilation units */
	private class RefactoringCompilationUnitCollector extends RefactoringSearchCollector {

		/** The collected compilation units */
		private final Set<ICompilationUnit> fCollectedUnits= new HashSet<>();

		/** The inaccurate matches */
		private final Set<SearchMatch> fInaccurateMatches= new HashSet<>();

		@Override
		public final void acceptSearchMatch(final SearchMatch match) throws CoreException {
			final SearchMatch accepted= fRequestor.acceptSearchMatch(match);
			if (accepted != null) {
				final IResource resource= accepted.getResource();
				if (!resource.equals(fLastResource)) {
					final IJavaElement element= JavaCore.create(resource);
					if (element instanceof ICompilationUnit)
						fCollectedUnits.add((ICompilationUnit) element);
				}
				if (fInaccurate && accepted.getAccuracy() == SearchMatch.A_INACCURATE && !fInaccurateMatches.contains(accepted)) {
					fStatus.addEntry(fSeverity, Messages.format(RefactoringCoreMessages.RefactoringSearchEngine_inaccurate_match, BasicElementLabels.getResourceName(accepted.getResource())), null, null, RefactoringStatusEntry.NO_CODE);
					fInaccurateMatches.add(accepted);
				}
			}
		}

		@Override
		public final void clearResults() {
			super.clearResults();
			fCollectedUnits.clear();
			fInaccurateMatches.clear();
		}

		@Override
		public final Collection<IResource> getBinaryResources() {
			return Collections.emptySet();
		}

		@Override
		public final Collection<ICompilationUnit> getCollectedMatches() {
			return fCollectedUnits;
		}

		@Override
		public final Collection<SearchMatch> getInaccurateMatches() {
			return fInaccurateMatches;
		}
	}

	private abstract class RefactoringSearchCollector extends SearchRequestor {

		protected IResource fLastResource= null;

		public void clearResults() {
			fLastResource= null;
		}

		public abstract Collection<IResource> getBinaryResources();

		public abstract Collection<?> getCollectedMatches();

		public abstract Collection<SearchMatch> getInaccurateMatches();
	}

	/** Search requestor which collects every search match */
	private class RefactoringSearchMatchCollector extends RefactoringSearchCollector {

		/** The binary resources */
		private final Set<IResource> fBinaryResources= new HashSet<>();

		/** The collected matches */
		private final List<SearchMatch> fCollectedMatches= new ArrayList<>();

		/** The inaccurate matches */
		private final Set<SearchMatch> fInaccurateMatches= new HashSet<>();

		@Override
		public final void acceptSearchMatch(final SearchMatch match) throws CoreException {
			final SearchMatch accepted= fRequestor.acceptSearchMatch(match);
			if (accepted != null) {
				fCollectedMatches.add(accepted);
				final IResource resource= accepted.getResource();
				if (!resource.equals(fLastResource)) {
					if (fBinary) {
						final IJavaElement element= JavaCore.create(resource);
						if (!(element instanceof ICompilationUnit)) {
							final IProject project= resource.getProject();
							if (!fGrouping)
								fStatus.addEntry(fSeverity, Messages.format(RefactoringCoreMessages.RefactoringSearchEngine_binary_match_ungrouped, BasicElementLabels.getResourceName(project)), null, null, RefactoringStatusEntry.NO_CODE);
							else if (!fBinaryResources.contains(resource))
								fStatus.addEntry(fSeverity, Messages.format(RefactoringCoreMessages.RefactoringSearchEngine_binary_match_grouped, BasicElementLabels.getResourceName(project)), null, null, RefactoringStatusEntry.NO_CODE);
							fBinaryResources.add(resource);
						}
					}
					if (fInaccurate && accepted.getAccuracy() == SearchMatch.A_INACCURATE && !fInaccurateMatches.contains(accepted)) {
						fStatus.addEntry(fSeverity, Messages.format(RefactoringCoreMessages.RefactoringSearchEngine_inaccurate_match, BasicElementLabels.getResourceName(resource)), null, null, RefactoringStatusEntry.NO_CODE);
						fInaccurateMatches.add(accepted);
					}
				}
			}
		}

		@Override
		public final void clearResults() {
			super.clearResults();
			fCollectedMatches.clear();
			fInaccurateMatches.clear();
			fBinaryResources.clear();
		}

		@Override
		public final Collection<IResource> getBinaryResources() {
			return fBinaryResources;
		}

		@Override
		public final Collection<SearchMatch> getCollectedMatches() {
			return fCollectedMatches;
		}

		@Override
		public final Collection<SearchMatch> getInaccurateMatches() {
			return fInaccurateMatches;
		}
	}

	/** The compilation unit granularity */
	public static final int GRANULARITY_COMPILATION_UNIT= 2;

	/** The search match granularity */
	public static final int GRANULARITY_SEARCH_MATCH= 1;

	/** Should binary matches be filtered? */
	private boolean fBinary= false;

	/** The refactoring search collector */
	private RefactoringSearchCollector fCollector= null;

	/** The search granularity */
	private int fGranularity= GRANULARITY_SEARCH_MATCH;

	/** Should the matches be grouped by resource? */
	private boolean fGrouping= true;

	/** Should inaccurate matches be filtered? */
	private boolean fInaccurate= true;

	/** The working copy owner, or <code>null</code> */
	private WorkingCopyOwner fOwner= null;

	/** The search pattern, or <code>null</code> */
	private SearchPattern fPattern= null;

	/** The search requestor */
	private IRefactoringSearchRequestor fRequestor= new DefaultSearchRequestor();

	/** The search scope */
	private IJavaSearchScope fScope= SearchEngine.createWorkspaceScope();

	/** The severity */
	private int fSeverity= RefactoringStatus.WARNING;

	/** The search status */
	private RefactoringStatus fStatus= new RefactoringStatus();

	/** The working copies */
	private ICompilationUnit[] fWorkingCopies= {};

	/**
	 * Creates a new refactoring search engine.
	 */
	public RefactoringSearchEngine2() {
		// Do nothing
	}

	/**
	 * Creates a new refactoring search engine.
	 *
	 * @param pattern the search pattern
	 */
	public RefactoringSearchEngine2(final SearchPattern pattern) {
		Assert.isNotNull(pattern);
		fPattern= pattern;
	}

	/**
	 * Clears all results found so far, and sets resets the status to {@link RefactoringStatus#OK}.
	 */
	public final void clearResults() {
		getCollector().clearResults();
		fStatus= new RefactoringStatus();
	}

	/**
	 * Returns the affected compilation units of the previous search queries.
	 * <p>
	 * In order to retrieve the compilation units, grouping by resource must have been enabled before searching.
	 *
	 * @return the compilation units of the previous queries
	 */
	public final ICompilationUnit[] getAffectedCompilationUnits() {
		if (fGranularity == GRANULARITY_COMPILATION_UNIT) {
			final Collection<?> collection= getCollector().getCollectedMatches();
			final ICompilationUnit[] units= new ICompilationUnit[collection.size()];
			int index= 0;
			for (final Iterator<?> iterator= collection.iterator(); iterator.hasNext(); index++)
				units[index]= (ICompilationUnit) iterator.next();
			return units;
		} else {
			final SearchResultGroup[] groups= getGroupedMatches();
			final ICompilationUnit[] units= new ICompilationUnit[groups.length];
			for (int index= 0; index < groups.length; index++)
				units[index]= groups[index].getCompilationUnit();
			return units;
		}
	}

	/**
	 * Returns the affected java projects of the previous search queries.
	 * <p>
	 * In order to retrieve the java projects, grouping by resource must have been enabled before searching.
	 *
	 * @return the java projects of the previous queries (element type:
	 * <code>&lt;IJavaProject, Collection&lt;SearchResultGroup&gt;&gt;</code> if granularity is {@link #GRANULARITY_SEARCH_MATCH} or
	 * <code>&lt;IJavaProject, Collection&lt;ICompilationUnit&gt;&gt;</code> if it is {@link #GRANULARITY_COMPILATION_UNIT}).
	 */
	public final Map<IJavaProject, ? extends Set<?>> getAffectedProjects() {
		IJavaProject project= null;
		ICompilationUnit unit= null;
		if (fGranularity == GRANULARITY_COMPILATION_UNIT) {
			final Map<IJavaProject, Set<ICompilationUnit>> map= new HashMap<>();
			final ICompilationUnit[] units= getAffectedCompilationUnits();
			for (int index= 0; index < units.length; index++) {
				unit= units[index];
				project= unit.getJavaProject();
				if (project != null) {
					Set<ICompilationUnit> set= map.get(project);
					if (set == null) {
						set= new HashSet<>();
						map.put(project, set);
					}
					set.add(unit);
				}
			}
			return map;
		} else {
			final Map<IJavaProject, Set<SearchResultGroup>> map= new HashMap<>();
			final SearchResultGroup[] groups= getGroupedMatches();
			SearchResultGroup group= null;
			for (int index= 0; index < groups.length; index++) {
				group= groups[index];
				unit= group.getCompilationUnit();
				if (unit != null) {
					project= unit.getJavaProject();
					if (project != null) {
						Set<SearchResultGroup> set= map.get(project);
						if (set == null) {
							set= new HashSet<>();
							map.put(project, set);
						}
						set.add(group);
					}
				}
			}
			return map;
		}
	}

	/**
	 * Returns the refactoring search collector.
	 *
	 * @return the refactoring search collector
	 */
	private RefactoringSearchCollector getCollector() {
		if (fCollector == null) {
			if (fGranularity == GRANULARITY_COMPILATION_UNIT)
				fCollector= new RefactoringCompilationUnitCollector();
			else if (fGranularity == GRANULARITY_SEARCH_MATCH)
				fCollector= new RefactoringSearchMatchCollector();
			else
				Assert.isTrue(false);
		}
		return fCollector;
	}

	/**
	 * Returns the found search matches in grouped by their containing resource.
	 *
	 * @return the found search matches
	 */
	private SearchResultGroup[] getGroupedMatches() {
		final Map<IResource, List<SearchMatch>> grouped= new HashMap<>();
		List<SearchMatch> matches= null;
		IResource resource= null;
		SearchMatch match= null;
		for (final Iterator<?> iterator= getSearchMatches().iterator(); iterator.hasNext();) {
			match= (SearchMatch) iterator.next();
			resource= match.getResource();
			if (!grouped.containsKey(resource))
				grouped.put(resource, new ArrayList<SearchMatch>(4));
			matches= grouped.get(resource);
			matches.add(match);
		}
		if (fBinary) {
			final Collection<IResource> collection= getCollector().getBinaryResources();
			for (final Iterator<IResource> iterator= grouped.keySet().iterator(); iterator.hasNext();) {
				resource= iterator.next();
				if (collection.contains(resource))
					iterator.remove();
			}
		}
		final SearchResultGroup[] result= new SearchResultGroup[grouped.keySet().size()];
		int index= 0;
		for (final Iterator<IResource> iterator= grouped.keySet().iterator(); iterator.hasNext();) {
			resource= iterator.next();
			matches= grouped.get(resource);
			result[index++]= new SearchResultGroup(resource, matches.toArray(new SearchMatch[matches.size()]));
		}
		return result;
	}

	/**
	 * Returns the search pattern currently used for searching.
	 *
	 * @return the search pattern
	 */
	public final SearchPattern getPattern() {
		return fPattern;
	}

	/**
	 * Returns the results of the previous search queries.
	 * <p>
	 * The result depends on the following conditions:
	 * <ul>
	 * <li>If the search granularity is {@link #GRANULARITY_COMPILATION_UNIT}, the results are elements of type {@link ICompilationUnit}.</li>
	 * <li>If grouping by resource is enabled, the results are elements of type {@link SearchResultGroup}, otherwise the elements are of type {@link SearchMatch}.</li>
	 * </ul>
	 *
	 * @return the results of the previous queries
	 */
	public final Object[] getResults() {
		if (fGranularity == GRANULARITY_COMPILATION_UNIT)
			return getAffectedCompilationUnits();
		else {
			if (fGrouping)
				return getGroupedMatches();
			else
				return getUngroupedMatches();
		}
	}

	/**
	 * Returns the search matches filtered by their accuracy.
	 *
	 * @return the filtered search matches
	 */
	private Collection<?> getSearchMatches() {
		Collection<?> results= null;
		if (fInaccurate) {
			results= new LinkedList<>(getCollector().getCollectedMatches());
			final Collection<SearchMatch> collection= getCollector().getInaccurateMatches();
			SearchMatch match= null;
			for (final Iterator<?> iterator= results.iterator(); iterator.hasNext();) {
				match= (SearchMatch) iterator.next();
				if (collection.contains(match))
					iterator.remove();
			}
		} else
			results= getCollector().getCollectedMatches();
		return results;
	}

	/**
	 * Returns the refactoring status of this search engine.
	 *
	 * @return the refactoring status
	 */
	public final RefactoringStatus getStatus() {
		return fStatus;
	}

	/**
	 * Returns the found search matches in no particular order.
	 *
	 * @return the found search matches
	 */
	private SearchMatch[] getUngroupedMatches() {
		Collection<?> results= null;
		if (fBinary) {
			results= new LinkedList<>(getSearchMatches());
			final Collection<IResource> collection= getCollector().getBinaryResources();
			SearchMatch match= null;
			for (final Iterator<?> iterator= results.iterator(); iterator.hasNext();) {
				match= (SearchMatch) iterator.next();
				if (collection.contains(match.getResource()))
					iterator.remove();
			}
		} else
			results= getSearchMatches();
		final SearchMatch[] matches= new SearchMatch[results.size()];
		results.toArray(matches);
		return matches;
	}

	/**
	 * Performs the search according to the specified pattern.
	 *
	 * @param monitor the progress monitor, or <code>null</code>
	 * @throws JavaModelException if an error occurs during search
	 */
	public final void searchPattern(IProgressMonitor monitor) throws JavaModelException {
		Assert.isNotNull(fPattern);
		if (monitor == null)
			monitor=  new NullProgressMonitor();
		try {
			monitor.beginTask("", 1); //$NON-NLS-1$
			monitor.setTaskName(RefactoringCoreMessages.RefactoringSearchEngine_searching_occurrences);
			try {
				SearchEngine engine= null;
				if (fOwner != null)
					engine= new SearchEngine(fOwner);
				else
					engine= new SearchEngine(fWorkingCopies);
				engine.search(fPattern, SearchUtils.getDefaultSearchParticipants(), fScope, getCollector(), new SubProgressMonitor(monitor, 1, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL));
			} catch (CoreException exception) {
				throw new JavaModelException(exception);
			}
		} finally {
			monitor.done();
		}
	}

	/**
	 * Performs the search of referenced fields.
	 *
	 * @param element the java element whose referenced fields have to be found
	 * @param monitor the progress monitor, or <code>null</code>
	 * @throws JavaModelException if an error occurs during search
	 */
	public final void searchReferencedFields(final IJavaElement element, IProgressMonitor monitor) throws JavaModelException {
		Assert.isNotNull(element);
		if (monitor == null)
			monitor=  new NullProgressMonitor();
		try {
			monitor.beginTask("", 1); //$NON-NLS-1$
			monitor.setTaskName(RefactoringCoreMessages.RefactoringSearchEngine_searching_referenced_fields);
			try {
				SearchEngine engine= null;
				if (fOwner != null)
					engine= new SearchEngine(fOwner);
				else
					engine= new SearchEngine(fWorkingCopies);
				engine.searchDeclarationsOfAccessedFields(element, getCollector(), new SubProgressMonitor(monitor, 1, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL));
			} catch (CoreException exception) {
				throw new JavaModelException(exception);
			}
		} finally {
			monitor.done();
		}
	}

	/**
	 * Performs the search of referenced methods.
	 *
	 * @param element the java element whose referenced methods have to be found
	 * @param monitor the progress monitor, or <code>null</code>
	 * @throws JavaModelException if an error occurs during search
	 */
	public final void searchReferencedMethods(final IJavaElement element, IProgressMonitor monitor) throws JavaModelException {
		Assert.isNotNull(element);
		if (monitor == null)
			monitor=  new NullProgressMonitor();
		try {
			monitor.beginTask("", 1); //$NON-NLS-1$
			monitor.setTaskName(RefactoringCoreMessages.RefactoringSearchEngine_searching_referenced_methods);
			try {
				SearchEngine engine= null;
				if (fOwner != null)
					engine= new SearchEngine(fOwner);
				else
					engine= new SearchEngine(fWorkingCopies);
				engine.searchDeclarationsOfSentMessages(element, getCollector(), new SubProgressMonitor(monitor, 1, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL));
			} catch (CoreException exception) {
				throw new JavaModelException(exception);
			}
		} finally {
			monitor.done();
		}
	}

	/**
	 * Performs the search of referenced types.
	 *
	 * @param element the java element whose referenced types have to be found
	 * @param monitor the progress monitor, or <code>null</code>
	 * @throws JavaModelException if an error occurs during search
	 */
	public final void searchReferencedTypes(final IJavaElement element, IProgressMonitor monitor) throws JavaModelException {
		Assert.isNotNull(element);
		if (monitor == null)
			monitor=  new NullProgressMonitor();
		try {
			monitor.beginTask("", 1); //$NON-NLS-1$
			monitor.setTaskName(RefactoringCoreMessages.RefactoringSearchEngine_searching_referenced_types);
			try {
				SearchEngine engine= null;
				if (fOwner != null)
					engine= new SearchEngine(fOwner);
				else
					engine= new SearchEngine(fWorkingCopies);
				engine.searchDeclarationsOfReferencedTypes(element, getCollector(), new SubProgressMonitor(monitor, 1, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL));
			} catch (CoreException exception) {
				throw new JavaModelException(exception);
			}
		} finally {
			monitor.done();
		}
	}

	/**
	 * Determines how search matches are filtered.
	 * <p>
	 * This method must be called before start searching. The default is to filter inaccurate matches only.
	 *
	 * @param inaccurate <code>true</code> to filter inaccurate matches, <code>false</code> otherwise
	 * @param binary <code>true</code> to filter binary matches, <code>false</code> otherwise
	 */
	public final void setFiltering(final boolean inaccurate, final boolean binary) {
		fInaccurate= inaccurate;
		fBinary= binary;
	}

	/**
	 * Sets the granularity to use during the searches.
	 * <p>
	 * This method must be called before start searching. The default is a granularity of {@link #GRANULARITY_SEARCH_MATCH}.
	 *
	 * @param granularity The granularity to use. Must be one of the <code>GRANULARITY_XXX</code> constants.
	 */
	public final void setGranularity(final int granularity) {
		Assert.isTrue(granularity == GRANULARITY_COMPILATION_UNIT || granularity == GRANULARITY_SEARCH_MATCH);
		fGranularity= granularity;
	}

	/**
	 * Sets the working copies to take precedence during the searches.
	 * <p>
	 * This method must be called before start searching. The default is to use no working copies
	 *
	 * @param copies the working copies to use
	 */
	public final void setWorkingCopies(final ICompilationUnit[] copies) {
		Assert.isNotNull(copies);
		fWorkingCopies= new ICompilationUnit[copies.length];
		System.arraycopy(copies, 0, fWorkingCopies, 0, copies.length);
	}

	/**
	 * Determines how search matches are grouped.
	 * <p>
	 * This method must be called before start searching. The default is to group by containing resource.
	 *
	 * @param grouping <code>true</code> to group matches by their containing resource, <code>false</code> otherwise
	 */
	public final void setGrouping(final boolean grouping) {
		fGrouping= grouping;
	}

	/**
	 * Sets the disjunction of search patterns to be used during search.
	 * <p>
	 * This method must be called before {@link RefactoringSearchEngine2#searchPattern(IProgressMonitor)}
	 *
	 * @param first the first search pattern to set
	 * @param second the second search pattern to set
	 */
	public final void setOrPattern(final SearchPattern first, final SearchPattern second) {
		Assert.isNotNull(first);
		Assert.isNotNull(second);
		fPattern= SearchPattern.createOrPattern(first, second);
	}

	/**
	 * Sets the working copy owner to use during search.
	 * <p>
	 * This method must be called before start searching. The default is to use no working copy owner.
	 *
	 * @param owner the working copy owner to use, or <code>null</code> to use none
	 */
	public final void setOwner(final WorkingCopyOwner owner) {
		fOwner= owner;
	}

	/**
	 * Sets the search pattern to be used during search.
	 * <p>
	 * This method must be called before {@link RefactoringSearchEngine2#searchPattern(IProgressMonitor)}
	 *
	 * @param elements the set of elements
	 * @param limitTo determines the nature of the expected matches. This is a combination of {@link org.eclipse.jdt.core.search.IJavaSearchConstants}.
	 */
	public final void setPattern(final IJavaElement[] elements, final int limitTo) {
		Assert.isNotNull(elements);
		Assert.isTrue(elements.length > 0);
		SearchPattern pattern= SearchPattern.createPattern(elements[0], limitTo, SearchUtils.GENERICS_AGNOSTIC_MATCH_RULE);
		IJavaElement element= null;
		for (int index= 1; index < elements.length; index++) {
			element= elements[index];
			pattern= SearchPattern.createOrPattern(pattern, SearchPattern.createPattern(element, limitTo, SearchUtils.GENERICS_AGNOSTIC_MATCH_RULE));
		}
		setPattern(pattern);
	}

	/**
	 * Sets the search pattern to be used during search.
	 * <p>
	 * This method must be called before {@link RefactoringSearchEngine2#searchPattern(IProgressMonitor)}
	 *
	 * @param pattern the search pattern to set
	 */
	public final void setPattern(final SearchPattern pattern) {
		Assert.isNotNull(pattern);
		fPattern= pattern;
	}

	/**
	 * Sets the search requestor for this search engine.
	 * <p>
	 * This method must be called before start searching. The default is a non-filtering search requestor.
	 *
	 * @param requestor the search requestor to set
	 */
	public final void setRequestor(final IRefactoringSearchRequestor requestor) {
		Assert.isNotNull(requestor);
		fRequestor= requestor;
	}

	/**
	 * Sets the search scope for this search engine.
	 * <p>
	 * This method must be called before start searching. The default is the entire workspace as search scope.
	 *
	 * @param scope the search scope to set
	 */
	public final void setScope(final IJavaSearchScope scope) {
		Assert.isNotNull(scope);
		fScope= scope;
	}

	/**
	 * Sets the severity of the generated status entries.
	 * <p>
	 * This method must be called before start searching. The default is a severity of {@link RefactoringStatus#OK}.
	 *
	 * @param severity the severity to set
	 */
	public final void setSeverity(final int severity) {
		Assert.isTrue(severity == RefactoringStatus.WARNING || severity == RefactoringStatus.INFO || severity == RefactoringStatus.FATAL || severity == RefactoringStatus.ERROR);
		fSeverity= severity;
	}

	/**
	 * Sets the refactoring status for this search engine.
	 * <p>
	 * This method must be called before start searching. The default is an empty status with status {@link RefactoringStatus#OK}.
	 *
	 * @param status the refactoring status to set
	 */
	public final void setStatus(final RefactoringStatus status) {
		Assert.isNotNull(status);
		fStatus= status;
	}
}
