/******************************************************************************* * Copyright (c) 2000, 2010 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.jdt.internal.core; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IProjectDescription; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceStatus; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jdt.core.IClasspathEntry; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.internal.core.util.Messages; import org.eclipse.jdt.internal.core.util.Util; public class ExternalFoldersManager { private static final String EXTERNAL_PROJECT_NAME= ".org.eclipse.jdt.core.external.folders"; //$NON-NLS-1$ private static final String LINKED_FOLDER_NAME= ".link"; //$NON-NLS-1$ private Map folders; private int counter= 0; /* Singleton instance */ private static ExternalFoldersManager MANAGER= new ExternalFoldersManager(); private ExternalFoldersManager() { // Prevent instantiation } public static ExternalFoldersManager getExternalFoldersManager() { return MANAGER; } /* * Returns a set of external path to external folders referred to on the given classpath. * Returns null if none. */ public static HashSet getExternalFolders(IClasspathEntry[] classpath) { if (classpath == null) return null; HashSet folders= null; for (int i= 0; i < classpath.length; i++) { IClasspathEntry entry= classpath[i]; if (entry.getEntryKind() == IClasspathEntry.CPE_LIBRARY) { IPath entryPath= entry.getPath(); if (isExternalFolderPath(entryPath)) { if (folders == null) folders= new HashSet(); folders.add(entryPath); } IPath attachmentPath= entry.getSourceAttachmentPath(); if (isExternalFolderPath(attachmentPath)) { if (folders == null) folders= new HashSet(); folders.add(attachmentPath); } } } return folders; } public static boolean isExternalFolderPath(IPath externalPath) { if (externalPath == null) return false; if (ResourcesPlugin.getWorkspace().getRoot().getProject(externalPath.segment(0)).exists()) return false; File externalFolder= externalPath.toFile(); if (externalFolder.isFile()) return false; if (externalPath.getFileExtension() != null/*likely a .jar, .zip, .rar or other file*/&& !externalFolder.exists()) return false; return true; } public static boolean isInternalPathForExternalFolder(IPath resourcePath) { return EXTERNAL_PROJECT_NAME.equals(resourcePath.segment(0)); } public IFolder addFolder(IPath externalFolderPath) { return addFolder(externalFolderPath, getExternalFoldersProject()); } private IFolder addFolder(IPath externalFolderPath, IProject externalFoldersProject) { Map knownFolders= getFolders(); Object existing= knownFolders.get(externalFolderPath); if (existing != null) { return (IFolder)existing; } IFolder result; do { result= externalFoldersProject.getFolder(LINKED_FOLDER_NAME + this.counter++); } while (result.exists()); knownFolders.put(externalFolderPath, result); return result; } public IFolder createLinkFolder(IPath externalFolderPath, boolean refreshIfExistAlready, IProgressMonitor monitor) throws CoreException { IProject externalFoldersProject= createExternalFoldersProject(monitor); // run outside synchronized as this can create a resource IFolder result= addFolder(externalFolderPath, externalFoldersProject); if (!result.exists()) result.createLink(externalFolderPath, IResource.ALLOW_MISSING_LOCAL, monitor); else if (refreshIfExistAlready) result.refreshLocal(IResource.DEPTH_INFINITE, monitor); return result; } public void cleanUp(IProgressMonitor monitor) throws CoreException { ArrayList toDelete= getFoldersToCleanUp(monitor); if (toDelete == null) return; for (Iterator iterator= toDelete.iterator(); iterator.hasNext();) { IFolder folder= (IFolder)iterator.next(); folder.delete(true, monitor); } IProject project= getExternalFoldersProject(); if (project.isAccessible() && project.members().length == 1/*remaining member is .project*/) project.delete(true, monitor); } private ArrayList getFoldersToCleanUp(IProgressMonitor monitor) throws CoreException { DeltaProcessingState state= JavaModelManager.getDeltaState(); HashMap roots= state.roots; HashMap sourceAttachments= state.sourceAttachments; if (roots == null && sourceAttachments == null) return null; Map knownFolders= getFolders(); ArrayList result= null; synchronized (knownFolders) { Iterator iterator= knownFolders.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry entry= (Map.Entry)iterator.next(); IPath path= (IPath)entry.getKey(); if ((roots != null && !roots.containsKey(path)) && (sourceAttachments != null && !sourceAttachments.containsKey(path))) { IFolder folder= (IFolder)entry.getValue(); if (folder != null) { if (result == null) result= new ArrayList(); result.add(folder); } } } } return result; } public IProject getExternalFoldersProject() { return ResourcesPlugin.getWorkspace().getRoot().getProject(EXTERNAL_PROJECT_NAME); } private IProject createExternalFoldersProject(IProgressMonitor monitor) throws CoreException { IProject project= getExternalFoldersProject(); if (!project.isAccessible()) { if (!project.exists()) { createExternalFoldersProject(project, monitor); } openExternalFoldersProject(project, monitor); } return project; } /* * Attempt to open the given project (assuming it exists). * If failing to open, make all attempts to recreate the missing pieces. */ private void openExternalFoldersProject(IProject project, IProgressMonitor monitor) throws CoreException { try { project.open(monitor); } catch (CoreException e1) { if (e1.getStatus().getCode() == IResourceStatus.FAILED_READ_METADATA) { // workspace was moved // (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=241400 and https://bugs.eclipse.org/bugs/show_bug.cgi?id=252571 ) project.delete(false/*don't delete content*/, true/*force*/, monitor); createExternalFoldersProject(project, monitor); } else { // .project or folder on disk have been deleted, recreate them IPath stateLocation= JavaCore.getPlugin().getStateLocation(); IPath projectPath= stateLocation.append(EXTERNAL_PROJECT_NAME); projectPath.toFile().mkdirs(); try { FileOutputStream output= new FileOutputStream(projectPath.append(".project").toOSString()); //$NON-NLS-1$ try { output.write(( "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + //$NON-NLS-1$ "<projectDescription>\n" + //$NON-NLS-1$ " <name>" + EXTERNAL_PROJECT_NAME + "</name>\n" + //$NON-NLS-1$ //$NON-NLS-2$ " <comment></comment>\n" + //$NON-NLS-1$ " <projects>\n" + //$NON-NLS-1$ " </projects>\n" + //$NON-NLS-1$ " <buildSpec>\n" + //$NON-NLS-1$ " </buildSpec>\n" + //$NON-NLS-1$ " <natures>\n" + //$NON-NLS-1$ " </natures>\n" + //$NON-NLS-1$ "</projectDescription>").getBytes()); //$NON-NLS-1$ } finally { output.close(); } } catch (IOException e) { // fallback to re-creating the project project.delete(false/*don't delete content*/, true/*force*/, monitor); createExternalFoldersProject(project, monitor); } } project.open(monitor); } } private void createExternalFoldersProject(IProject project, IProgressMonitor monitor) throws CoreException { IProjectDescription desc= project.getWorkspace().newProjectDescription(project.getName()); IPath stateLocation= JavaCore.getPlugin().getStateLocation(); desc.setLocation(stateLocation.append(EXTERNAL_PROJECT_NAME)); project.create(desc, IResource.HIDDEN, monitor); } public IFolder getFolder(IPath externalFolderPath) { return (IFolder)getFolders().get(externalFolderPath); } private Map getFolders() { if (this.folders == null) { Map tempFolders= new HashMap(); IProject project= getExternalFoldersProject(); try { if (!project.isAccessible()) { if (project.exists()) { // workspace was moved (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=252571 ) openExternalFoldersProject(project, null/*no progress*/); } else { // if project doesn't exist, do not open and recreate it as it means that there are no external folders return this.folders= Collections.synchronizedMap(tempFolders); } } IResource[] members= project.members(); for (int i= 0, length= members.length; i < length; i++) { IResource member= members[i]; if (member.getType() == IResource.FOLDER && member.isLinked() && member.getName().startsWith(LINKED_FOLDER_NAME)) { IPath externalFolderPath= member.getLocation(); tempFolders.put(externalFolderPath, member); } } } catch (CoreException e) { Util.log(e, "Exception while initializing external folders"); //$NON-NLS-1$ } this.folders= Collections.synchronizedMap(tempFolders); } return this.folders; } /* * Refreshes the external folders referenced on the classpath of the given source project */ public void refreshReferences(final IProject[] sourceProjects, IProgressMonitor monitor) { IProject externalProject= getExternalFoldersProject(); try { HashSet externalFolders= null; for (int index= 0; index < sourceProjects.length; index++) { if (sourceProjects[index].equals(externalProject)) continue; if (!JavaProject.hasJavaNature(sourceProjects[index])) continue; HashSet foldersInProject= getExternalFolders(((JavaProject)JavaCore.create(sourceProjects[index])).getResolvedClasspath()); if (foldersInProject == null || foldersInProject.size() == 0) continue; if (externalFolders == null) externalFolders= new HashSet(); externalFolders.addAll(foldersInProject); } if (externalFolders == null) return; Iterator iterator= externalFolders.iterator(); Job refreshJob= new RefreshJob(iterator); refreshJob.schedule(); } catch (CoreException e) { Util.log(e, "Exception while refreshing external project"); //$NON-NLS-1$ } return; } public void refreshReferences(IProject source, IProgressMonitor monitor) { IProject externalProject= getExternalFoldersProject(); if (source.equals(externalProject)) return; if (!JavaProject.hasJavaNature(source)) return; try { HashSet externalFolders= getExternalFolders(((JavaProject)JavaCore.create(source)).getResolvedClasspath()); if (externalFolders == null) return; Iterator iterator= externalFolders.iterator(); Job refreshJob= new RefreshJob(iterator); refreshJob.schedule(); } catch (CoreException e) { Util.log(e, "Exception while refreshing external project"); //$NON-NLS-1$ } return; } public IFolder removeFolder(IPath externalFolderPath) { return (IFolder)getFolders().remove(externalFolderPath); } class RefreshJob extends Job { Iterator externalFolders= null; RefreshJob(Iterator externalFolders) { super(Messages.refreshing_external_folders); this.externalFolders= externalFolders; } public boolean belongsTo(Object family) { return family == ResourcesPlugin.FAMILY_MANUAL_REFRESH; } protected IStatus run(IProgressMonitor pm) { try { while (this.externalFolders.hasNext()) { IPath externalPath= (IPath)this.externalFolders.next(); IFolder folder= getFolder(externalPath); if (folder != null) { folder.refreshLocal(IResource.DEPTH_INFINITE, pm); } } } catch (CoreException e) { return e.getStatus(); } return Status.OK_STATUS; } } }