| /******************************************************************************* |
| * Copyright (c) 2017 Google, Inc 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: |
| * Stefan Xenos (Google) - Initial implementation |
| *******************************************************************************/ |
| package org.eclipse.jdt.internal.core.nd.indexer; |
| |
| import java.util.ArrayDeque; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Objects; |
| import java.util.Set; |
| |
| import org.eclipse.core.resources.IContainer; |
| 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.runtime.CoreException; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.Path; |
| import org.eclipse.core.runtime.SubMonitor; |
| import org.eclipse.jdt.core.IClassFile; |
| import org.eclipse.jdt.core.IClasspathEntry; |
| import org.eclipse.jdt.core.IJavaElement; |
| import org.eclipse.jdt.core.IJavaProject; |
| import org.eclipse.jdt.core.IPackageFragmentRoot; |
| import org.eclipse.jdt.core.IParent; |
| import org.eclipse.jdt.core.JavaCore; |
| import org.eclipse.jdt.internal.core.nd.java.JavaIndex; |
| |
| /** |
| * Represents a snapshot of all the indexable objects in the workspace at a given moment in time. |
| */ |
| public final class WorkspaceSnapshot { |
| private Map<IPath, List<IJavaElement>> allIndexables; |
| |
| /** |
| * Enable this to index the content of output folders, in cases where that content exists and is up-to-date. This is |
| * much faster than indexing source files directly. |
| */ |
| public static boolean EXPERIMENTAL_INDEX_OUTPUT_FOLDERS; |
| |
| private WorkspaceSnapshot(Map<IPath, List<IJavaElement>> allIndexables) { |
| this.allIndexables = allIndexables; |
| } |
| |
| public Map<IPath, List<IJavaElement>> getAllIndexables() { |
| return this.allIndexables; |
| } |
| |
| public Set<IPath> allLocations() { |
| return this.allIndexables.keySet(); |
| } |
| |
| public List<IJavaElement> get(IPath next) { |
| List<IJavaElement> result = this.allIndexables.get(next); |
| if (result == null) { |
| return Collections.emptyList(); |
| } |
| return result; |
| } |
| |
| public static WorkspaceSnapshot create(IWorkspaceRoot root, IProgressMonitor monitor) throws CoreException { |
| SubMonitor subMonitor = SubMonitor.convert(monitor); |
| |
| List<IJavaElement> unfilteredIndexables = getAllIndexableObjectsInWorkspace(root, subMonitor.split(3)); |
| |
| // Remove all duplicate indexables (jars which are referenced by more than one project) |
| Map<IPath, List<IJavaElement>> allIndexables = removeDuplicatePaths(unfilteredIndexables); |
| |
| return new WorkspaceSnapshot(allIndexables); |
| } |
| |
| private static IPath getWorkspacePathForRoot(IJavaElement next) { |
| IResource resource = next.getResource(); |
| if (resource != null) { |
| return resource.getFullPath(); |
| } |
| return Path.EMPTY; |
| } |
| |
| private static Map<IPath, List<IJavaElement>> removeDuplicatePaths(List<IJavaElement> allIndexables) { |
| Map<IPath, List<IJavaElement>> paths = new HashMap<>(); |
| |
| HashSet<IPath> workspacePaths = new HashSet<IPath>(); |
| for (IJavaElement next : allIndexables) { |
| IPath nextPath = JavaIndex.getLocationForElement(next); |
| IPath workspacePath = getWorkspacePathForRoot(next); |
| |
| List<IJavaElement> value = paths.get(nextPath); |
| |
| if (value == null) { |
| value = new ArrayList<IJavaElement>(); |
| paths.put(nextPath, value); |
| } else { |
| if (workspacePath != null) { |
| if (workspacePaths.contains(workspacePath)) { |
| continue; |
| } |
| if (!workspacePath.isEmpty()) { |
| Package.logInfo("Found duplicate workspace path for " + workspacePath.toString()); //$NON-NLS-1$ |
| } |
| workspacePaths.add(workspacePath); |
| } |
| } |
| |
| value.add(next); |
| } |
| |
| return paths; |
| } |
| |
| private static List<IJavaElement> getAllIndexableObjectsInWorkspace(IWorkspaceRoot root, IProgressMonitor monitor) |
| throws CoreException { |
| SubMonitor subMonitor = SubMonitor.convert(monitor, 2); |
| List<IJavaElement> allIndexables = new ArrayList<>(); |
| IProject[] projects = root.getProjects(); |
| |
| List<IProject> projectsToScan = new ArrayList<>(); |
| |
| for (IProject next : projects) { |
| if (next.isOpen()) { |
| projectsToScan.add(next); |
| } |
| } |
| |
| Set<IPath> scannedPaths = new HashSet<>(); |
| Set<IResource> resourcesToScan = new HashSet<>(); |
| SubMonitor projectLoopMonitor = subMonitor.split(1).setWorkRemaining(projectsToScan.size()); |
| for (IProject project : projectsToScan) { |
| SubMonitor iterationMonitor = projectLoopMonitor.split(1); |
| try { |
| if (project.isOpen() && project.isNatureEnabled(JavaCore.NATURE_ID)) { |
| IJavaProject javaProject = JavaCore.create(project); |
| |
| IClasspathEntry[] entries = javaProject.getRawClasspath(); |
| |
| if (EXPERIMENTAL_INDEX_OUTPUT_FOLDERS) { |
| IPath defaultOutputLocation = javaProject.getOutputLocation(); |
| for (IClasspathEntry next : entries) { |
| IPath nextOutputLocation = next.getOutputLocation(); |
| |
| if (nextOutputLocation == null) { |
| nextOutputLocation = defaultOutputLocation; |
| } |
| |
| IResource resource = root.findMember(nextOutputLocation); |
| if (resource != null) { |
| resourcesToScan.add(resource); |
| } |
| } |
| } |
| |
| IPackageFragmentRoot[] projectRoots = javaProject.getAllPackageFragmentRoots(); |
| SubMonitor rootLoopMonitor = iterationMonitor.setWorkRemaining(projectRoots.length); |
| for (IPackageFragmentRoot nextRoot : projectRoots) { |
| rootLoopMonitor.split(1); |
| if (!nextRoot.exists()) { |
| continue; |
| } |
| IPath filesystemPath = JavaIndex.getLocationForElement(nextRoot); |
| if (scannedPaths.contains(filesystemPath)) { |
| continue; |
| } |
| scannedPaths.add(filesystemPath); |
| if (nextRoot.getKind() == IPackageFragmentRoot.K_BINARY) { |
| if (nextRoot.isArchive()) { |
| allIndexables.add(nextRoot); |
| } else { |
| collectAllClassFiles(root, allIndexables, nextRoot); |
| } |
| } else { |
| collectAllClassFiles(root, allIndexables, nextRoot); |
| } |
| } |
| } |
| } catch (CoreException e) { |
| Package.log(e); |
| } |
| } |
| |
| collectAllClassFiles(root, allIndexables, resourcesToScan, subMonitor.split(1)); |
| return allIndexables; |
| } |
| |
| private static void collectAllClassFiles(IWorkspaceRoot root, List<? super IClassFile> result, |
| Collection<? extends IResource> toScan, IProgressMonitor monitor) { |
| SubMonitor subMonitor = SubMonitor.convert(monitor); |
| |
| ArrayDeque<IResource> resources = new ArrayDeque<>(); |
| resources.addAll(toScan); |
| |
| while (!resources.isEmpty()) { |
| subMonitor.setWorkRemaining(Math.max(resources.size(), 3000)).split(1); |
| IResource next = resources.removeFirst(); |
| |
| if (next instanceof IContainer) { |
| IContainer container = (IContainer)next; |
| |
| try { |
| for (IResource nextChild : container.members()) { |
| resources.addLast(nextChild); |
| } |
| } catch (CoreException e) { |
| // If an error occurs in one resource, skip it and move on to the next |
| Package.log(e); |
| } |
| } else if (next instanceof IFile) { |
| IFile file = (IFile) next; |
| |
| String extension = file.getFileExtension(); |
| if (Objects.equals(extension, "class")) { //$NON-NLS-1$ |
| IJavaElement element = JavaCore.create(file); |
| |
| if (element instanceof IClassFile) { |
| result.add((IClassFile)element); |
| } |
| } |
| } |
| } |
| } |
| |
| private static void collectAllClassFiles(IWorkspaceRoot root, List<? super IClassFile> result, |
| IParent nextRoot) throws CoreException { |
| for (IJavaElement child : nextRoot.getChildren()) { |
| try { |
| int type = child.getElementType(); |
| if (type == IJavaElement.COMPILATION_UNIT) { |
| continue; |
| } |
| if (!child.exists()) { |
| continue; |
| } |
| |
| if (type == IJavaElement.CLASS_FILE) { |
| result.add((IClassFile)child); |
| } else if (child instanceof IParent) { |
| IParent parent = (IParent) child; |
| |
| collectAllClassFiles(root, result, parent); |
| } |
| } catch (CoreException e) { |
| // Log exceptions, then continue with the next child |
| Package.log(e); |
| } |
| } |
| } |
| } |