Package org.eclipse.core.internal.resources

Source Code of org.eclipse.core.internal.resources.ProjectPreferences

/*******************************************************************************
* Copyright (c) 2004, 2006 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.core.internal.resources;

import java.io.*;
import java.util.*;
import org.eclipse.core.internal.preferences.EclipsePreferences;
import org.eclipse.core.internal.preferences.ExportedPreferences;
import org.eclipse.core.internal.utils.*;
import org.eclipse.core.resources.*;
import org.eclipse.core.runtime.*;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.core.runtime.jobs.MultiRule;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.core.runtime.preferences.IExportedPreferences;
import org.eclipse.osgi.util.NLS;
import org.osgi.service.prefs.BackingStoreException;
import org.osgi.service.prefs.Preferences;

/**
* Represents a node in the Eclipse preference hierarchy which stores preference
* values for projects.
*
* @since 3.0
*/
public class ProjectPreferences extends EclipsePreferences {

  class SortedProperties extends Properties {

    class IteratorWrapper implements Enumeration {
      Iterator iterator;

      public IteratorWrapper(Iterator iterator) {
        this.iterator = iterator;
      }

      public boolean hasMoreElements() {
        return iterator.hasNext();
      }

      public Object nextElement() {
        return iterator.next();
      }
    }

    private static final long serialVersionUID = 1L;

    /* (non-Javadoc)
     * @see java.util.Hashtable#keys()
     */
    public synchronized Enumeration keys() {
      TreeSet set = new TreeSet();
      for (Enumeration e = super.keys(); e.hasMoreElements();)
        set.add(e.nextElement());
      return new IteratorWrapper(set.iterator());
    }
  }

  /**
   * Cache which nodes have been loaded from disk
   */
  protected static Set loadedNodes = new HashSet();
  private IFile file;
  private boolean initialized = false;
  /**
   * Flag indicating that this node is currently reading values from disk,
   * to avoid flushing during a read.
   */
  private boolean isReading;
  /**
   * Flag indicating that this node is currently writing values to disk,
   * to avoid re-reading after the write completes.
   */
  private boolean isWriting;
  private IEclipsePreferences loadLevel;
  private IProject project;
  private String qualifier;

  // cache
  private int segmentCount;

  static void deleted(IFile file) throws CoreException {
    IPath path = file.getFullPath();
    int count = path.segmentCount();
    if (count != 3)
      return;
    // check if we are in the .settings directory
    if (!EclipsePreferences.DEFAULT_PREFERENCES_DIRNAME.equals(path.segment(1)))
      return;
    Preferences root = Platform.getPreferencesService().getRootNode();
    String project = path.segment(0);
    String qualifier = path.removeFileExtension().lastSegment();
    ProjectPreferences projectNode = (ProjectPreferences) root.node(ProjectScope.SCOPE).node(project);
    // if the node isn't known then just return
    try {
      if (!projectNode.nodeExists(qualifier))
        return;
    } catch (BackingStoreException e) {
      // ignore
    }

    // if the node was loaded we need to clear the values and remove
    // its reference from the parent (don't save it though)
    // otherwise just remove the reference from the parent
    String childPath = projectNode.absolutePath() + IPath.SEPARATOR + qualifier;
    if (projectNode.isAlreadyLoaded(childPath))
      removeNode(projectNode.node(qualifier));
    else
      projectNode.removeNode(qualifier);

    // notifies the CharsetManager if needed
    if (qualifier.equals(ResourcesPlugin.PI_RESOURCES))
      preferencesChanged(file.getProject());
  }

  static void deleted(IFolder folder) throws CoreException {
    IPath path = folder.getFullPath();
    int count = path.segmentCount();
    if (count != 2)
      return;
    // check if we are the .settings directory
    if (!EclipsePreferences.DEFAULT_PREFERENCES_DIRNAME.equals(path.segment(1)))
      return;
    Preferences root = Platform.getPreferencesService().getRootNode();
    // The settings dir has been removed/moved so remove all project prefs
    // for the resource.
    String project = path.segment(0);
    Preferences projectNode = root.node(ProjectScope.SCOPE).node(project);
    // check if we need to notify the charset manager
    boolean hasResourcesSettings = getFile(folder, ResourcesPlugin.PI_RESOURCES).exists();
    // remove the preferences
    removeNode(projectNode);
    // notifies the CharsetManager    
    if (hasResourcesSettings)
      preferencesChanged(folder.getProject());
  }

  /*
   * The whole project has been removed so delete all of the project settings
   */
  static void deleted(IProject project) throws CoreException {
    // The settings dir has been removed/moved so remove all project prefs
    // for the resource. We have to do this now because (since we aren't
    // synchronizing) there is short-circuit code that doesn't visit the
    // children.
    Preferences root = Platform.getPreferencesService().getRootNode();
    Preferences projectNode = root.node(ProjectScope.SCOPE).node(project.getName());
    // check if we need to notify the charset manager
    boolean hasResourcesSettings = getFile(project, ResourcesPlugin.PI_RESOURCES).exists();
    // remove the preferences
    removeNode(projectNode);
    // notifies the CharsetManager    
    if (hasResourcesSettings)
      preferencesChanged(project);
  }

  static void deleted(IResource resource) throws CoreException {
    switch (resource.getType()) {
      case IResource.FILE :
        deleted((IFile) resource);
        return;
      case IResource.FOLDER :
        deleted((IFolder) resource);
        return;
      case IResource.PROJECT :
        deleted((IProject) resource);
        return;
    }
  }

  /*
   * Return the preferences file for the given folder and qualifier.
   */
  static IFile getFile(IFolder folder, String qualifier) {
    Assert.isLegal(folder.getName().equals(DEFAULT_PREFERENCES_DIRNAME));
    return folder.getFile(new Path(qualifier).addFileExtension(PREFS_FILE_EXTENSION));
  }

  /*
   * Return the preferences file for the given project and qualifier.
   */
  static IFile getFile(IProject project, String qualifier) {
    return project.getFile(new Path(DEFAULT_PREFERENCES_DIRNAME).append(qualifier).addFileExtension(PREFS_FILE_EXTENSION));
  }

  private static Properties loadProperties(IFile file) throws BackingStoreException {
    if (Policy.DEBUG_PREFERENCES)
      Policy.debug("Loading preferences from file: " + file.getFullPath()); //$NON-NLS-1$
    Properties result = new Properties();
    InputStream input = null;
    try {
      input = new BufferedInputStream(file.getContents(true));
      result.load(input);
    } catch (CoreException e) {
      String message = NLS.bind(Messages.preferences_loadException, file.getFullPath());
      log(new Status(IStatus.ERROR, ResourcesPlugin.PI_RESOURCES, IStatus.ERROR, message, e));
      throw new BackingStoreException(message);
    } catch (IOException e) {
      String message = NLS.bind(Messages.preferences_loadException, file.getFullPath());
      log(new Status(IStatus.ERROR, ResourcesPlugin.PI_RESOURCES, IStatus.ERROR, message, e));
      throw new BackingStoreException(message);
    } finally {
      FileUtil.safeClose(input);
    }
    return result;
  }

  private static void preferencesChanged(IProject project) {
    Workspace workspace = ((Workspace) ResourcesPlugin.getWorkspace());
    workspace.getCharsetManager().projectPreferencesChanged(project);
    workspace.getContentDescriptionManager().projectPreferencesChanged(project);
  }

  private static void read(ProjectPreferences node, IFile file) throws BackingStoreException, CoreException {
    if (file == null || !file.exists()) {
      if (Policy.DEBUG_PREFERENCES)
        Policy.debug("Unable to determine preference file or file does not exist for node: " + node.absolutePath()); //$NON-NLS-1$
      return;
    }
    Properties fromDisk = loadProperties(file);
    // no work to do
    if (fromDisk.isEmpty())
      return;
    // create a new node to store the preferences in.
    IExportedPreferences myNode = (IExportedPreferences) ExportedPreferences.newRoot().node(node.absolutePath());
    convertFromProperties((EclipsePreferences) myNode, fromDisk, false);
    //flag that we are currently reading, to avoid unnecessary writing
    boolean oldIsReading = node.isReading;
    node.isReading = true;
    try {
      Platform.getPreferencesService().applyPreferences(myNode);
    } finally {
      node.isReading = oldIsReading;
    }
  }

  static void removeNode(Preferences node) throws CoreException {
    String message = NLS.bind(Messages.preferences_removeNodeException, node.absolutePath());
    try {
      node.removeNode();
    } catch (BackingStoreException e) {
      IStatus status = new Status(IStatus.ERROR, ResourcesPlugin.PI_RESOURCES, IStatus.ERROR, message, e);
      throw new CoreException(status);
    }
    String path = node.absolutePath();
    for (Iterator i = loadedNodes.iterator(); i.hasNext();) {
      String key = (String) i.next();
      if (key.startsWith(path))
        i.remove();
    }
  }

  public static void updatePreferences(IFile file) throws CoreException {
    IPath path = file.getFullPath();
    // if we made it this far we are inside /project/.settings and might
    // have a change to a preference file
    if (!PREFS_FILE_EXTENSION.equals(path.getFileExtension()))
      return;

    String project = path.segment(0);
    String qualifier = path.removeFileExtension().lastSegment();
    Preferences root = Platform.getPreferencesService().getRootNode();
    Preferences node = root.node(ProjectScope.SCOPE).node(project).node(qualifier);
    String message = null;
    try {
      message = NLS.bind(Messages.preferences_syncException, node.absolutePath());
      if (!(node instanceof ProjectPreferences))
        return;
      ProjectPreferences projectPrefs = (ProjectPreferences) node;
      if (projectPrefs.isWriting)
        return;
      read(projectPrefs, file);
      // make sure that we generate the appropriate resource change events
      // if encoding settings have changed
      if (ResourcesPlugin.PI_RESOURCES.equals(qualifier))
        preferencesChanged(file.getProject());
    } catch (BackingStoreException e) {
      IStatus status = new Status(IStatus.ERROR, ResourcesPlugin.PI_RESOURCES, IStatus.ERROR, message, e);
      throw new CoreException(status);
    }
  }

  /**
   * Default constructor. Should only be called by #createExecutableExtension.
   */
  public ProjectPreferences() {
    super(null, null);
  }

  private ProjectPreferences(EclipsePreferences parent, String name) {
    super(parent, name);

    // cache the segment count
    String path = absolutePath();
    segmentCount = getSegmentCount(path);

    if (segmentCount == 1)
      return;

    // cache the project name
    String projectName = getSegment(path, 1);
    if (projectName != null)
      project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName);

    // cache the qualifier
    if (segmentCount > 2)
      qualifier = getSegment(path, 2);

    if (segmentCount != 2)
      return;

    // else segmentCount == 2 so we initialize the children
    if (initialized)
      return;
    try {
      synchronized (this) {
        String[] names = computeChildren();
        for (int i = 0; i < names.length; i++)
          addChild(names[i], null);
      }
    } finally {
      initialized = true;
    }
  }

  /*
   * Figure out what the children of this node are based on the resources
   * that are in the workspace.
   */
  private String[] computeChildren() {
    if (project == null)
      return EMPTY_STRING_ARRAY;
    IFolder folder = project.getFolder(DEFAULT_PREFERENCES_DIRNAME);
    if (!folder.exists())
      return EMPTY_STRING_ARRAY;
    IResource[] members = null;
    try {
      members = folder.members();
    } catch (CoreException e) {
      return EMPTY_STRING_ARRAY;
    }
    ArrayList result = new ArrayList();
    for (int i = 0; i < members.length; i++) {
      IResource resource = members[i];
      if (resource.getType() == IResource.FILE && PREFS_FILE_EXTENSION.equals(resource.getFullPath().getFileExtension()))
        result.add(resource.getFullPath().removeFileExtension().lastSegment());
    }
    return (String[]) result.toArray(EMPTY_STRING_ARRAY);
  }

  public void flush() throws BackingStoreException {
    if (isReading)
      return;
    isWriting = true;
    try {
      super.flush();
    } finally {
      isWriting = false;
    }
  }

  private IFile getFile() {
    if (file == null) {
      if (project == null || qualifier == null)
        return null;
      file = getFile(project, qualifier);
    }
    return file;
  }

  /*
   * Return the node at which these preferences are loaded/saved.
   */
  protected IEclipsePreferences getLoadLevel() {
    if (loadLevel == null) {
      if (project == null || qualifier == null)
        return null;
      // Make it relative to this node rather than navigating to it from the root.
      // Walk backwards up the tree starting at this node.
      // This is important to avoid a chicken/egg thing on startup.
      EclipsePreferences node = this;
      for (int i = 3; i < segmentCount; i++)
        node = (EclipsePreferences) node.parent();
      loadLevel = node;
    }
    return loadLevel;
  }

  /*
   * Calculate and return the file system location for this preference node.
   * Use the absolute path of the node to find out the project name so
   * we can get its location on disk.
   *
   * NOTE: we cannot cache the location since it may change over the course
   * of the project life-cycle.
   */
  protected IPath getLocation() {
    if (project == null || qualifier == null)
      return null;
    IPath path = project.getLocation();
    return computeLocation(path, qualifier);
  }

  protected EclipsePreferences internalCreate(EclipsePreferences nodeParent, String nodeName, Object context) {
    return new ProjectPreferences(nodeParent, nodeName);
  }

  protected boolean isAlreadyLoaded(IEclipsePreferences node) {
    return loadedNodes.contains(node.absolutePath());
  }

  protected boolean isAlreadyLoaded(String path) {
    return loadedNodes.contains(path);
  }

  protected void load() throws BackingStoreException {
    IFile localFile = getFile();
    if (localFile == null || !localFile.exists()) {
      if (Policy.DEBUG_PREFERENCES)
        Policy.debug("Unable to determine preference file or file does not exist for node: " + absolutePath()); //$NON-NLS-1$
      return;
    }
    if (Policy.DEBUG_PREFERENCES)
      Policy.debug("Loading preferences from file: " + localFile.getFullPath()); //$NON-NLS-1$
    Properties fromDisk = new Properties();
    InputStream input = null;
    try {
      input = new BufferedInputStream(localFile.getContents(true));
      fromDisk.load(input);
    } catch (CoreException e) {
      String message = NLS.bind(Messages.preferences_loadException, localFile.getFullPath());
      log(new Status(IStatus.ERROR, ResourcesPlugin.PI_RESOURCES, IStatus.ERROR, message, e));
      throw new BackingStoreException(message);
    } catch (IOException e) {
      String message = NLS.bind(Messages.preferences_loadException, localFile.getFullPath());
      log(new Status(IStatus.ERROR, ResourcesPlugin.PI_RESOURCES, IStatus.ERROR, message, e));
      throw new BackingStoreException(message);
    } finally {
      FileUtil.safeClose(input);
    }
    convertFromProperties(this, fromDisk, true);
  }

  protected void loaded() {
    loadedNodes.add(absolutePath());
  }

  /* (non-Javadoc)
   * @see org.eclipse.core.internal.preferences.EclipsePreferences#nodeExists(java.lang.String)
   *
   * If we are at the /project node and we are checking for the existance of a child, we
   * want special behaviour. If the child is a single segment name, then we want to
   * return true if the node exists OR if a project with that name exists in the workspace.
   */
  public boolean nodeExists(String path) throws BackingStoreException {
    if (segmentCount != 1)
      return super.nodeExists(path);
    if (path.length() == 0)
      return super.nodeExists(path);
    if (path.charAt(0) == IPath.SEPARATOR)
      return super.nodeExists(path);
    if (path.indexOf(IPath.SEPARATOR) != -1)
      return super.nodeExists(path);
    // if we are checking existance of a single segment child of /project, base the answer on
    // whether or not it exists in the workspace.
    return ResourcesPlugin.getWorkspace().getRoot().getProject(path).exists() || super.nodeExists(path);
  }

  protected void save() throws BackingStoreException {
    final IFile fileInWorkspace = getFile();
    if (fileInWorkspace == null) {
      if (Policy.DEBUG_PREFERENCES)
        Policy.debug("Not saving preferences since there is no file for node: " + absolutePath()); //$NON-NLS-1$
      return;
    }
    Properties table = convertToProperties(new SortedProperties(), ""); //$NON-NLS-1$
    IWorkspace workspace = ResourcesPlugin.getWorkspace();
    IResourceRuleFactory factory = workspace.getRuleFactory();
    try {
      if (table.isEmpty()) {
        IWorkspaceRunnable operation = new IWorkspaceRunnable() {
          public void run(IProgressMonitor monitor) throws CoreException {
            // nothing to save. delete existing file if one exists.
            if (fileInWorkspace.exists()) {
              if (Policy.DEBUG_PREFERENCES)
                Policy.debug("Deleting preference file: " + fileInWorkspace.getFullPath()); //$NON-NLS-1$
              if (fileInWorkspace.isReadOnly()) {
                IStatus status = fileInWorkspace.getWorkspace().validateEdit(new IFile[] {fileInWorkspace}, IWorkspace.VALIDATE_PROMPT);
                if (!status.isOK())
                  throw new CoreException(status);
              }
              try {
                fileInWorkspace.delete(true, null);
              } catch (CoreException e) {
                String message = NLS.bind(Messages.preferences_deleteException, fileInWorkspace.getFullPath());
                log(new Status(IStatus.WARNING, ResourcesPlugin.PI_RESOURCES, IStatus.WARNING, message, null));
              }
            }
          }
        };
        ISchedulingRule rule = factory.deleteRule(fileInWorkspace);
        try {
          ResourcesPlugin.getWorkspace().run(operation, rule, IResource.NONE, null);
        } catch (OperationCanceledException e) {
          throw new BackingStoreException(Messages.preferences_operationCanceled);
        }
        return;
      }
      table.put(VERSION_KEY, VERSION_VALUE);
      ByteArrayOutputStream output = new ByteArrayOutputStream();
      try {
        table.store(output, null);
      } catch (IOException e) {
        String message = NLS.bind(Messages.preferences_saveProblems, absolutePath());
        log(new Status(IStatus.ERROR, Platform.PI_RUNTIME, IStatus.ERROR, message, e));
        throw new BackingStoreException(message);
      } finally {
        try {
          output.close();
        } catch (IOException e) {
          // ignore
        }
      }
      final InputStream input = new BufferedInputStream(new ByteArrayInputStream(output.toByteArray()));
      IWorkspaceRunnable operation = new IWorkspaceRunnable() {
        public void run(IProgressMonitor monitor) throws CoreException {
          if (fileInWorkspace.exists()) {
            if (Policy.DEBUG_PREFERENCES)
              Policy.debug("Setting preference file contents for: " + fileInWorkspace.getFullPath()); //$NON-NLS-1$
            if (fileInWorkspace.isReadOnly()) {
              IStatus status = fileInWorkspace.getWorkspace().validateEdit(new IFile[] {fileInWorkspace}, IWorkspace.VALIDATE_PROMPT);
              if (!status.isOK())
                throw new CoreException(status);
            }
            // set the contents
            fileInWorkspace.setContents(input, IResource.KEEP_HISTORY, null);
          } else {
            // create the file
            IFolder folder = (IFolder) fileInWorkspace.getParent();
            if (!folder.exists()) {
              if (Policy.DEBUG_PREFERENCES)
                Policy.debug("Creating parent preference directory: " + folder.getFullPath()); //$NON-NLS-1$
              folder.create(IResource.NONE, true, null);
            }
            if (Policy.DEBUG_PREFERENCES)
              Policy.debug("Creating preference file: " + fileInWorkspace.getLocation()); //$NON-NLS-1$
            fileInWorkspace.create(input, IResource.NONE, null);
          }
        }
      };
      //don't bother with scheduling rules if we are already inside an operation
      try {
        if (((Workspace) workspace).getWorkManager().isLockAlreadyAcquired()) {
          operation.run(null);
        } else {
          // we might: create the .settings folder, create the file, or modify the file.
          ISchedulingRule rule = MultiRule.combine(factory.createRule(fileInWorkspace.getParent()), factory.modifyRule(fileInWorkspace));
          workspace.run(operation, rule, IResource.NONE, null);
        }
      } catch (OperationCanceledException e) {
        throw new BackingStoreException(Messages.preferences_operationCanceled);
      }
    } catch (CoreException e) {
      String message = NLS.bind(Messages.preferences_saveProblems, fileInWorkspace.getFullPath());
      log(new Status(IStatus.ERROR, ResourcesPlugin.PI_RESOURCES, IStatus.ERROR, message, e));
      throw new BackingStoreException(message);
    }
  }
}
TOP

Related Classes of org.eclipse.core.internal.resources.ProjectPreferences

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.