| /******************************************************************************* |
| * Copyright (c) 2000, 2016 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 |
| * |
| *******************************************************************************/ |
| package org.eclipse.dltk.internal.core.search; |
| |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.resources.IWorkspaceRoot; |
| import org.eclipse.core.resources.ResourcesPlugin; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.dltk.compiler.env.IDependent; |
| import org.eclipse.dltk.core.DLTKCore; |
| import org.eclipse.dltk.core.IBuildpathEntry; |
| import org.eclipse.dltk.core.IDLTKLanguageToolkit; |
| import org.eclipse.dltk.core.IMember; |
| import org.eclipse.dltk.core.IModelElement; |
| import org.eclipse.dltk.core.IModelElementDelta; |
| import org.eclipse.dltk.core.IProjectFragment; |
| import org.eclipse.dltk.core.IScriptProject; |
| import org.eclipse.dltk.core.IType; |
| import org.eclipse.dltk.core.ITypeHierarchy; |
| import org.eclipse.dltk.core.ModelException; |
| import org.eclipse.dltk.core.WorkingCopyOwner; |
| import org.eclipse.dltk.core.environment.IFileHandle; |
| import org.eclipse.dltk.internal.core.ArchiveProjectFragment; |
| import org.eclipse.dltk.internal.core.ExternalProjectFragment; |
| import org.eclipse.dltk.internal.core.Model; |
| import org.eclipse.dltk.internal.core.ModelElement; |
| import org.eclipse.dltk.internal.core.ModelManager; |
| import org.eclipse.dltk.internal.core.ScriptProject; |
| import org.eclipse.dltk.internal.core.hierarchy.TypeHierarchy; |
| |
| /** |
| * Scope limited to the subtype and supertype hierarchy of a given type. |
| */ |
| public class HierarchyScope extends DLTKSearchScope { |
| |
| public IType focusType; |
| protected String focusPath; |
| protected WorkingCopyOwner owner; |
| |
| protected ITypeHierarchy hierarchy; |
| protected IType[] types; |
| protected HashSet resourcePaths; |
| protected IPath[] enclosingProjectsAndZips; |
| |
| protected IResource[] elements; |
| protected int elementCount; |
| |
| public boolean needsRefresh; |
| |
| /* |
| * (non-Javadoc) Adds the given resource to this search scope. |
| */ |
| public void add(IResource element) { |
| if (this.elementCount == this.elements.length) { |
| System.arraycopy(this.elements, 0, |
| this.elements = new IResource[this.elementCount * 2], 0, |
| this.elementCount); |
| } |
| elements[elementCount++] = element; |
| |
| IModelElement modelElement = DLTKCore.create(element); |
| try { |
| add(modelElement); |
| } catch (ModelException e) { |
| if (DLTKCore.DEBUG) { |
| e.printStackTrace(); |
| } |
| } |
| } |
| |
| /* |
| * (non-Javadoc) Creates a new hiearchy scope for the given type. |
| */ |
| public HierarchyScope(IDLTKLanguageToolkit languageToolkit, IType type, |
| WorkingCopyOwner owner) throws ModelException { |
| super(languageToolkit); |
| |
| this.focusType = type; |
| this.owner = owner; |
| |
| this.enclosingProjectsAndZips = this.computeProjectsAndZips(type); |
| |
| // resource path |
| IProjectFragment root = (IProjectFragment) type.getScriptFolder() |
| .getParent(); |
| if (root.isArchive()) { |
| IPath jarPath = root.getPath(); |
| Object target = Model.getTarget(ResourcesPlugin.getWorkspace() |
| .getRoot(), jarPath, true); |
| String zipFileName; |
| if (target instanceof IFile) { |
| // internal jar |
| zipFileName = jarPath.toString(); |
| } else if (target instanceof IFileHandle) { |
| // external jar |
| // TODO Check this |
| zipFileName = ((IFileHandle) target).toOSString(); |
| } else { |
| return; // unknown target |
| } |
| this.focusPath = zipFileName |
| + IDependent.ARCHIVE_FILE_ENTRY_SEPARATOR |
| + type.getFullyQualifiedName().replace('.', '/') |
| /* + SUFFIX_STRING_class */; |
| } else { |
| this.focusPath = type.getPath().toString(); |
| } |
| |
| this.needsRefresh = true; |
| |
| add((ScriptProject) type.getScriptProject(), |
| DLTKSearchScope.APPLICATION_LIBRARIES | DLTKSearchScope.SOURCES |
| | DLTKSearchScope.SYSTEM_LIBRARIES |
| | DLTKSearchScope.REFERENCED_PROJECTS, new HashSet()); |
| |
| // disabled for now as this could be expensive |
| // JavaModelManager.getJavaModelManager().rememberScope(this); |
| } |
| |
| private void buildResourceVector() { |
| HashMap resources = new HashMap(); |
| HashMap paths = new HashMap(); |
| this.types = this.hierarchy.getAllTypes(); |
| IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot(); |
| for (int i = 0; i < this.types.length; i++) { |
| IType type = this.types[i]; |
| IResource resource = type.getResource(); |
| if (resource != null && resources.get(resource) == null) { |
| resources.put(resource, resource); |
| add(resource); |
| } |
| IProjectFragment root = (IProjectFragment) type.getScriptFolder() |
| .getParent(); |
| if (root instanceof ArchiveProjectFragment) { |
| // type in a jar |
| ArchiveProjectFragment jar = (ArchiveProjectFragment) root; |
| IPath jarPath = jar.getPath(); |
| Object target = Model.getTarget(workspaceRoot, jarPath, true); |
| String zipFileName; |
| if (target instanceof IFile) { |
| // internal jar |
| zipFileName = jarPath.toString(); |
| } else if (target instanceof IFileHandle) { |
| // external jar |
| // TODO Check this |
| zipFileName = ((IFileHandle) target).toOSString(); |
| } else { |
| continue; // unknown target |
| } |
| String resourcePath = zipFileName |
| + IDependent.ARCHIVE_FILE_ENTRY_SEPARATOR |
| + type.getFullyQualifiedName().replace('.', '/') |
| /* + SUFFIX_STRING_class */; |
| |
| this.resourcePaths.add(resourcePath); |
| paths.put(jarPath, type); |
| } else if (root instanceof ExternalProjectFragment) { |
| paths.put(root.getPath(), type); |
| } else { |
| // type is a project |
| paths.put(type.getScriptProject().getProject().getFullPath(), |
| type); |
| } |
| } |
| this.enclosingProjectsAndZips = new IPath[paths.size()]; |
| int i = 0; |
| for (Iterator iter = paths.keySet().iterator(); iter.hasNext();) { |
| this.enclosingProjectsAndZips[i++] = (IPath) iter.next(); |
| } |
| } |
| |
| /* |
| * Computes the paths of projects and jars that the hierarchy on the given |
| * type could contain. This is a super set of the project and jar paths once |
| * the hierarchy is computed. |
| */ |
| private IPath[] computeProjectsAndZips(IType type) throws ModelException { |
| HashSet set = new HashSet(); |
| IProjectFragment root = (IProjectFragment) type.getScriptFolder() |
| .getParent(); |
| if (root.isArchive()) { |
| // add the root |
| set.add(root.getPath()); |
| // add all projects that reference this archive and their dependents |
| IPath rootPath = root.getPath(); |
| Model model = ModelManager.getModelManager().getModel(); |
| IScriptProject[] projects = model.getScriptProjects(); |
| HashSet visited = new HashSet(); |
| for (int i = 0; i < projects.length; i++) { |
| IScriptProject project = projects[i]; |
| IBuildpathEntry entry = ((ScriptProject) project) |
| .getBuildpathEntryFor(rootPath); |
| if (entry != null) { |
| // add the project and its binary pkg fragment roots |
| IProjectFragment[] roots = project.getAllProjectFragments(); |
| set.add(project.getPath()); |
| for (int j = 0; j < roots.length; j++) { |
| IProjectFragment pkgFragmentRoot = roots[j]; |
| if (pkgFragmentRoot.getKind() == IProjectFragment.K_BINARY) { |
| set.add(pkgFragmentRoot.getPath()); |
| } |
| } |
| // add the dependent projects |
| this.computeDependents(project, set, visited); |
| } |
| } |
| } else { |
| // add all the project's pkg fragment roots |
| IScriptProject project = (IScriptProject) root.getParent(); |
| IProjectFragment[] roots = project.getProjectFragments(); |
| for (int j = 0; j < roots.length; j++) { |
| IProjectFragment pkgFragmentRoot = roots[j]; |
| if (pkgFragmentRoot.getKind() == IProjectFragment.K_BINARY) { |
| set.add(pkgFragmentRoot.getPath()); |
| } else { |
| set.add(pkgFragmentRoot.getParent().getPath()); |
| } |
| } |
| // add the dependent projects |
| this.computeDependents(project, set, new HashSet()); |
| } |
| IPath[] result = new IPath[set.size()]; |
| set.toArray(result); |
| return result; |
| } |
| |
| private void computeDependents(IScriptProject project, HashSet set, |
| HashSet visited) { |
| if (visited.contains(project)) { |
| return; |
| } |
| visited.add(project); |
| IProject[] dependents = project.getProject().getReferencingProjects(); |
| for (int i = 0; i < dependents.length; i++) { |
| IProject dependent2 = dependents[i]; |
| try { |
| IScriptProject dependent = DLTKCore.create(dependent2); |
| IProjectFragment[] roots = dependent.getProjectFragments(); |
| set.add(dependent.getPath()); |
| for (int j = 0; j < roots.length; j++) { |
| IProjectFragment pkgFragmentRoot = roots[j]; |
| if (pkgFragmentRoot.isArchive()) { |
| set.add(pkgFragmentRoot.getPath()); |
| } |
| } |
| this.computeDependents(dependent, set, visited); |
| } catch (ModelException e) { |
| // project is not a java project |
| } |
| } |
| } |
| |
| @Override |
| public boolean encloses(String resourcePath) { |
| if (this.hierarchy == null) { |
| if (resourcePath.equals(this.focusPath)) { |
| return true; |
| } else { |
| if (this.needsRefresh) { |
| try { |
| this.initialize(); |
| } catch (ModelException e) { |
| return false; |
| } |
| } else { |
| // the scope is used only to find enclosing projects and |
| // jars |
| // clients is responsible for filtering out elements not in |
| // the hierarchy (see SearchEngine) |
| return true; |
| } |
| } |
| } |
| if (this.needsRefresh) { |
| try { |
| this.refresh(); |
| } catch (ModelException e) { |
| return false; |
| } |
| } |
| int separatorIndex = resourcePath |
| .indexOf(IDependent.ARCHIVE_FILE_ENTRY_SEPARATOR); |
| if (separatorIndex != -1) { |
| return this.resourcePaths.contains(resourcePath); |
| } else { |
| for (int i = 0; i < this.elementCount; i++) { |
| if (resourcePath.startsWith(this.elements[i].getFullPath() |
| .toString())) { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| @Override |
| public boolean encloses(IModelElement element) { |
| if (this.hierarchy == null) { |
| if (this.focusType.equals(element.getAncestor(IModelElement.TYPE))) { |
| return true; |
| } else { |
| if (this.needsRefresh) { |
| try { |
| this.initialize(); |
| } catch (ModelException e) { |
| return false; |
| } |
| } else { |
| // the scope is used only to find enclosing projects and |
| // jars |
| // clients is responsible for filtering out elements not in |
| // the hierarchy (see SearchEngine) |
| return true; |
| } |
| } |
| } |
| if (this.needsRefresh) { |
| try { |
| this.refresh(); |
| } catch (ModelException e) { |
| return false; |
| } |
| } |
| IType type = null; |
| if (element instanceof IType) { |
| type = (IType) element; |
| } else if (element instanceof IMember) { |
| type = ((IMember) element).getDeclaringType(); |
| } |
| if (type != null) { |
| if (this.hierarchy.contains(type)) { |
| return true; |
| } else { |
| // be flexible: look at original element (see bug 14106 |
| // Declarations in Hierarchy does not find declarations in |
| // hierarchy) |
| IType original; |
| if (/* |
| * !type.isBinary() && |
| */(original = (IType) type.getPrimaryElement()) != null) { |
| return this.hierarchy.contains(original); |
| } |
| } |
| } |
| return false; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see IJavaSearchScope#enclosingProjectsAndZips() |
| * |
| * @deprecated |
| */ |
| @Override |
| public IPath[] enclosingProjectsAndZips() { |
| if (this.needsRefresh) { |
| try { |
| this.refresh(); |
| } catch (ModelException e) { |
| return new IPath[0]; |
| } |
| } |
| return this.enclosingProjectsAndZips; |
| } |
| |
| /** |
| * This implementation creates a full (supertype and subtype) hierarchy for |
| * the given focus type. |
| * |
| * @param owner |
| * the owner of working copies that take precedence over their |
| * original compilation units |
| * @param monitor |
| * the given progress monitor |
| * @return a type hierarchy for this type containing this type, all of its |
| * supertypes, and all its subtypes in the workspace |
| * @throws ModelException |
| */ |
| protected ITypeHierarchy createHierarchy(IType focusType, |
| WorkingCopyOwner owner, IProgressMonitor monitor) |
| throws ModelException { |
| return focusType.newTypeHierarchy(owner, monitor); |
| } |
| |
| protected void initialize() throws ModelException { |
| this.resourcePaths = new HashSet(); |
| this.elements = new IResource[5]; |
| this.elementCount = 0; |
| this.needsRefresh = false; |
| if (this.hierarchy == null) { |
| this.hierarchy = createHierarchy(this.focusType, this.owner, null); |
| } else { |
| this.hierarchy.refresh(null); |
| } |
| this.buildResourceVector(); |
| } |
| |
| /* |
| * @see AbstractSearchScope#processDelta(IModelElementDelta) |
| */ |
| public void processDelta(IModelElementDelta delta, int eventType) { |
| if (this.needsRefresh) { |
| return; |
| } |
| this.needsRefresh = this.hierarchy == null ? false |
| : ((TypeHierarchy) this.hierarchy).isAffected(delta/* |
| * , |
| * eventType |
| */); |
| } |
| |
| protected void refresh() throws ModelException { |
| // if (this.hierarchy != null) { |
| this.initialize(); |
| // } |
| } |
| |
| @Override |
| public String toString() { |
| return "HierarchyScope on " + ((ModelElement) this.focusType).toStringWithAncestors(); //$NON-NLS-1$ |
| } |
| |
| } |