Package org.eclipse.jdt.internal.core

Source Code of org.eclipse.jdt.internal.core.ExternalFoldersManager$RefreshJob

/*******************************************************************************
* Copyright (c) 2000, 2011 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
*     Stephan Herrmann <stephan@cs.tu-berlin.de> - inconsistent initialization of classpath container backed by external class folder, see https://bugs.eclipse.org/320618
*******************************************************************************/
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.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.Vector;

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.core.JavaModelException;
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 Set pendingFolders; // subset of keys of 'folders', for which linked folders haven't been created yet.
  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;
    String firstSegment = externalPath.segment(0);
    if (firstSegment != null && ResourcesPlugin.getWorkspace().getRoot().getProject(firstSegment).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, boolean scheduleForCreation) {
    return addFolder(externalFolderPath, getExternalFoldersProject(), scheduleForCreation);
  }

  private IFolder addFolder(IPath externalFolderPath, IProject externalFoldersProject, boolean scheduleForCreation) {
    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());
    if (scheduleForCreation) {
      if (this.pendingFolders == null)
        this.pendingFolders = new HashSet();
      this.pendingFolders.add(externalFolderPath);
    }
    knownFolders.put(externalFolderPath, result);
    return result;
  }
 
  /**
   * Try to remove the argument from the list of folders pending for creation.
   * @param externalPath to link to
   * @return true if the argument was found in the list of pending folders and could be removed from it.
   */
  public boolean removePendingFolder(Object externalPath) {
    if (this.pendingFolders == null)
      return false;
    return this.pendingFolders.remove(externalPath);
  }

  public IFolder createLinkFolder(IPath externalFolderPath, boolean refreshIfExistAlready, IProgressMonitor monitor) throws CoreException {
    IProject externalFoldersProject = createExternalFoldersProject(monitor); // run outside synchronized as this can create a resource
    return createLinkFolder(externalFolderPath, refreshIfExistAlready, externalFoldersProject, monitor);
  }

  private IFolder createLinkFolder(IPath externalFolderPath, boolean refreshIfExistAlready,
                  IProject externalFoldersProject, IProgressMonitor monitor) throws CoreException {
   
    IFolder result = addFolder(externalFolderPath, externalFoldersProject, false);
    if (!result.exists())
      result.createLink(externalFolderPath, IResource.ALLOW_MISSING_LOCAL, monitor);
    else if (refreshIfExistAlready)
      result.refreshLocal(IResource.DEPTH_INFINITE,  monitor);
    return result;
  }

  public void createPendingFolders(IProgressMonitor monitor) throws JavaModelException{
    if (this.pendingFolders == null || this.pendingFolders.isEmpty()) return;
   
    IProject externalFoldersProject = null;
    try {
      externalFoldersProject = createExternalFoldersProject(monitor);
    }
    catch(CoreException e) {
      throw new JavaModelException(e);
    }
    Iterator iterator = this.pendingFolders.iterator();
    while (iterator.hasNext()) {
      Object folderPath = iterator.next();
      try {
        createLinkFolder((IPath) folderPath, false, externalFoldersProject, monitor);
      } catch (CoreException e) {
        Util.log(e, "Error while creating a link for external folder :" + folderPath); //$NON-NLS-1$
      }
    }
    this.pendingFolders.clear();
  }
 
  public void cleanUp(IProgressMonitor monitor) throws CoreException {
    ArrayList toDelete = getFoldersToCleanUp(monitor);
    if (toDelete == null)
      return;
    for (Iterator iterator = toDelete.iterator(); iterator.hasNext();) {
      Map.Entry entry = (Map.Entry) iterator.next();
      IFolder folder = (IFolder) entry.getValue();
      folder.delete(true, monitor);
      IPath key = (IPath) entry.getKey();
      this.folders.remove(key);
    }
    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))) {
          if (entry.getValue() != null) {
            if (result == null)
              result = new ArrayList();
            result.add(entry);
          }
        }
      }
    }
    return result;
  }

  public IProject getExternalFoldersProject() {
    return ResourcesPlugin.getWorkspace().getRoot().getProject(EXTERNAL_PROJECT_NAME);
  }
  public 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;
  }
 
  // https://bugs.eclipse.org/bugs/show_bug.cgi?id=313153
  // Use the same RefreshJob if the job is still available
  private void runRefreshJob(Collection paths) {
    Job[] jobs = Job.getJobManager().find(ResourcesPlugin.FAMILY_MANUAL_REFRESH);
    RefreshJob refreshJob = null;
    if (jobs != null) {
      for (int index = 0; index < jobs.length; index++) {
        // We are only concerned about ExternalFolderManager.RefreshJob
        if(jobs[index] instanceof RefreshJob) {
          refreshJob =  (RefreshJob) jobs[index];
          refreshJob.addFoldersToRefresh(paths);
          if (refreshJob.getState() == Job.NONE) {
            refreshJob.schedule();
          }
          break;
        }
      }
    }
    if (refreshJob == null) {
      refreshJob = new RefreshJob(new Vector(paths));
      refreshJob.schedule();
    }
  }
  /*
   * 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;

      runRefreshJob(externalFolders);

    } 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;
     
      runRefreshJob(externalFolders);
    } 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 {
    Vector externalFolders = null;
    RefreshJob(Vector externalFolders){
      super(Messages.refreshing_external_folders);
      this.externalFolders = externalFolders;
    }
   
    public boolean belongsTo(Object family) {
      return family == ResourcesPlugin.FAMILY_MANUAL_REFRESH;
    }
   
    /*
     * Add the collection of paths to be refreshed to the already
     * existing list of paths. 
     */
    public void addFoldersToRefresh(Collection paths) {
      if (!paths.isEmpty() && this.externalFolders == null) {
        this.externalFolders = new Vector();
      }
      Iterator it = paths.iterator();
      while(it.hasNext()) {
        Object path = it.next();
        if (!this.externalFolders.contains(path)) {
          this.externalFolders.add(path);
        }
      }
    }
   
    protected IStatus run(IProgressMonitor pm) {
      try {
        if (this.externalFolders == null)
          return Status.OK_STATUS;
        IPath externalPath = null;
        for (int index = 0; index < this.externalFolders.size(); index++ ) {
          if ((externalPath = (IPath)this.externalFolders.get(index)) != null) {
            IFolder folder = getFolder(externalPath);
            // https://bugs.eclipse.org/bugs/show_bug.cgi?id=321358
            if (folder != null)
              folder.refreshLocal(IResource.DEPTH_INFINITE, pm);
          }
          // Set the processed ones to null instead of removing the element altogether,
          // so that they will not be considered as duplicates.
          // This will also avoid elements being shifted to the left every time an element
          // is removed. However, there is a risk of Collection size to be increased more often.
          this.externalFolders.setElementAt(null, index);
        }
      } catch (CoreException e) {
        return e.getStatus();
      }
      return Status.OK_STATUS;
    }
  }
 
}
TOP

Related Classes of org.eclipse.jdt.internal.core.ExternalFoldersManager$RefreshJob

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.