/*******************************************************************************
* Copyright (c) 2000, 2017 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.dltk.internal.corext.refactoring;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.dltk.core.DLTKCore;
import org.eclipse.dltk.core.IBuildpathEntry;
import org.eclipse.dltk.core.IMember;
import org.eclipse.dltk.core.IModelElement;
import org.eclipse.dltk.core.IProjectFragment;
import org.eclipse.dltk.core.IScriptProject;
import org.eclipse.dltk.core.ModelException;
import org.eclipse.dltk.core.search.IDLTKSearchScope;
import org.eclipse.dltk.core.search.SearchEngine;
public class RefactoringScopeFactory {
/*
* Adds to <code> projects </code> ImodelProject objects for all projects
* directly or indirectly referencing focus. @param projects ImodelProjects will
* be added to this set
*/
private static void addReferencingProjects(IScriptProject focus, Set<IScriptProject> projects)
throws ModelException {
IProject[] referencingProjects = focus.getProject().getReferencingProjects();
for (int i = 0; i < referencingProjects.length; i++) {
IScriptProject candidate = DLTKCore.create(referencingProjects[i]);
if (candidate == null || projects.contains(candidate) || !candidate.exists())
continue; // break cycle
IBuildpathEntry entry = getReferencingClassPathEntry(candidate, focus);
if (entry != null) {
projects.add(candidate);
if (entry.isExported())
addReferencingProjects(candidate, projects);
}
}
}
private static void addRelatedReferencing(IScriptProject focus, Set<IScriptProject> projects) throws CoreException {
IProject[] referencingProjects = focus.getProject().getReferencingProjects();
for (int i = 0; i < referencingProjects.length; i++) {
IScriptProject candidate = DLTKCore.create(referencingProjects[i]);
if (candidate == null || projects.contains(candidate) || !candidate.exists())
continue; // break cycle
IBuildpathEntry entry = getReferencingClassPathEntry(candidate, focus);
if (entry != null) {
projects.add(candidate);
if (entry.isExported()) {
addRelatedReferencing(candidate, projects);
addRelatedReferenced(candidate, projects);
}
}
}
}
private static void addRelatedReferenced(IScriptProject focus, Set<IScriptProject> projects) throws CoreException {
IProject[] referencedProjects = focus.getProject().getReferencedProjects();
for (int i = 0; i < referencedProjects.length; i++) {
IScriptProject candidate = DLTKCore.create(referencedProjects[i]);
if (candidate == null || projects.contains(candidate) || !candidate.exists())
continue; // break cycle
IBuildpathEntry entry = getReferencingClassPathEntry(focus, candidate);
if (entry != null) {
projects.add(candidate);
if (entry.isExported()) {
addRelatedReferenced(candidate, projects);
addRelatedReferencing(candidate, projects);
}
}
}
}
/**
* Creates a new search scope with all compilation units possibly referencing
* <code>modelElement</code>, considering the visibility of the element,
* references only from source
*
* @param modelElement
* the model element
* @return the search scope
* @throws ModelException
* if an error occurs
*/
public static IDLTKSearchScope create(IModelElement modelElement) throws ModelException {
return RefactoringScopeFactory.create(modelElement, true, true);
}
/**
* Creates a new search scope with all compilation units possibly referencing
* <code>modelElement</code>, references only from source
*
* @param modelElement
* the model element
* @param considerVisibility
* consider visibility of modelElement iff <code>true</code>
* @return the search scope
* @throws ModelException
* if an error occurs
*/
public static IDLTKSearchScope create(IModelElement modelElement, boolean considerVisibility)
throws ModelException {
return RefactoringScopeFactory.create(modelElement, considerVisibility, true);
}
/**
* Creates a new search scope with all compilation units possibly referencing
* <code>modelElement</code>.
*
* @param modelElement
* the model element
* @param considerVisibility
* consider visibility of modelElement iff <code>true</code>
* @param sourceReferencesOnly
* consider references in source only (no references in binary)
* @return the search scope
* @throws ModelException
* if an error occurs
*/
public static IDLTKSearchScope create(IModelElement modelElement, boolean considerVisibility,
boolean sourceReferencesOnly) throws ModelException {
if (considerVisibility & modelElement instanceof IMember) {
/*
* IMember member= (IMember) modelElement; if (JdtFlags.isPrivate(member)) { if
* (member.getCompilationUnit() != null) return
* SearchEngine.createmodelSearchScope(new ImodelElement[] {
* member.getCompilationUnit()}); else return
* SearchEngine.createmodelSearchScope(new ImodelElement[] { member}); }
*/
// Removed code that does some optimizations regarding package visible members.
// The problem is that
// there can be a package fragment with the same name in a different source
// folder or project. So we
// have to treat package visible members like public or protected members.
}
IScriptProject modelProject = modelElement.getScriptProject();
return SearchEngine.createSearchScope(getAllScopeElements(modelProject, sourceReferencesOnly), false,
modelProject.getLanguageToolkit());
}
/**
* Creates a new search scope comprising <code>members</code>.
*
* @param members
* the members
* @return the search scope
* @throws modelModelException
* if an error occurs
*/
public static IDLTKSearchScope create(IMember[] members) throws ModelException {
return create(members, true);
}
/**
* Creates a new search scope comprising <code>members</code>.
*
* @param members
* the members
* @param sourceReferencesOnly
* consider references in source only (no references in binary)
* @return the search scope
* @throws modelModelException
* if an error occurs
*/
public static IDLTKSearchScope create(IMember[] members, boolean sourceReferencesOnly) throws ModelException {
/*
* Assert.isTrue(members != null && members.length > 0); IMember candidate=
* members[0]; int visibility= getVisibility(candidate); for (int i= 1; i <
* members.length; i++) { int mv= getVisibility(members[i]); if (mv >
* visibility) { visibility= mv; candidate= members[i]; } } return
* create(candidate, true, sourceReferencesOnly);
*/
return create(members[0], true, sourceReferencesOnly);
}
/**
* Creates a new search scope with all projects possibly referenced from the
* given <code>modelElements</code>.
*
* @param modelElements
* the model elements
* @return the search scope
*/
public static IDLTKSearchScope createReferencedScope(IModelElement[] modelElements) {
Set<IScriptProject> projects = new HashSet<>();
for (int i = 0; i < modelElements.length; i++) {
projects.add(modelElements[i].getScriptProject());
}
IScriptProject[] prj = projects.toArray(new IScriptProject[projects.size()]);
return SearchEngine.createSearchScope(prj, true, prj.length > 0 ? prj[0].getLanguageToolkit() : null);
}
/**
* Creates a new search scope with all projects possibly referenced from the
* given <code>modelElements</code>.
*
* @param modelElements
* the model elements
* @param includeMask
* the include mask
* @return the search scope
*/
public static IDLTKSearchScope createReferencedScope(IModelElement[] modelElements, int includeMask) {
Set<IScriptProject> projects = new HashSet<>();
for (int i = 0; i < modelElements.length; i++) {
projects.add(modelElements[i].getScriptProject());
}
IScriptProject[] prj = projects.toArray(new IScriptProject[projects.size()]);
return SearchEngine.createSearchScope(prj, true, prj.length > 0 ? prj[0].getLanguageToolkit() : null);
}
/**
* Creates a new search scope containing all projects which reference or are
* referenced by the specified project.
*
* @param project
* the project
* @param includeMask
* the include mask
* @return the search scope
* @throws CoreException
* if a referenced project could not be determined
*/
public static IDLTKSearchScope createRelatedProjectsScope(IScriptProject project, int includeMask)
throws CoreException {
IScriptProject[] projects = getRelatedProjects(project);
return SearchEngine.createSearchScope(projects, true,
projects.length > 0 ? projects[0].getLanguageToolkit() : null);
}
/*
* @param projects a collection of ImodelProject
*
* @return Array of IPackageFragmentRoot, one element for each
* packageFragmentRoot which lies within a project in <code> projects </code> .
*/
private static IProjectFragment[] getAllScopeElements(IScriptProject project, boolean onlySourceRoots)
throws ModelException {
Collection<IScriptProject> referencingProjects = getReferencingProjects(project);
List<IProjectFragment> result = new ArrayList<>();
for (IScriptProject scriptProject : referencingProjects) {
IProjectFragment[] roots = scriptProject.getProjectFragments();
// Add all package fragment roots except archives
for (int i = 0; i < roots.length; i++) {
IProjectFragment root = roots[i];
if (!onlySourceRoots || root.getKind() == IProjectFragment.K_SOURCE)
result.add(root);
}
}
return result.toArray(new IProjectFragment[result.size()]);
}
/*
* Finds, if possible, a classpathEntry in one given project such that this
* classpath entry references another given project. If more than one entry
* exists for the referenced project and at least one is exported, then an
* exported entry will be returned.
*/
private static IBuildpathEntry getReferencingClassPathEntry(IScriptProject referencingProject,
IScriptProject referencedProject) throws ModelException {
IBuildpathEntry result = null;
IPath path = referencedProject.getProject().getFullPath();
IBuildpathEntry[] classpath = referencingProject.getResolvedBuildpath(true);
for (int i = 0; i < classpath.length; i++) {
IBuildpathEntry entry = classpath[i];
if (entry.getEntryKind() == IBuildpathEntry.BPE_PROJECT && path.equals(entry.getPath())) {
if (entry.isExported())
return entry;
// Consider it as a candidate. May be there is another entry that is
// exported.
result = entry;
}
}
return result;
}
private static IScriptProject[] getRelatedProjects(IScriptProject focus) throws CoreException {
final Set<IScriptProject> projects = new HashSet<>();
addRelatedReferencing(focus, projects);
addRelatedReferenced(focus, projects);
projects.add(focus);
return projects.toArray(new IScriptProject[projects.size()]);
}
private static Collection<IScriptProject> getReferencingProjects(IScriptProject focus) throws ModelException {
Set<IScriptProject> projects = new HashSet<>();
addReferencingProjects(focus, projects);
projects.add(focus);
return projects;
}
/*
* private static int getVisibility(IMember member) throws ModelException { if
* (JdtFlags.isPrivate(member)) return 0; if (JdtFlags.isPackageVisible(member))
* return 1; if (JdtFlags.isProtected(member)) return 2; return 4; }
*/
private RefactoringScopeFactory() {
// no instances
}
}