Package org.eclipse.core.internal.resources

Source Code of org.eclipse.core.internal.resources.CharsetManager$Listener

/*******************************************************************************
* 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.util.*;
import org.eclipse.core.internal.utils.Messages;
import org.eclipse.core.internal.utils.Policy;
import org.eclipse.core.resources.*;
import org.eclipse.core.runtime.*;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.core.runtime.jobs.Job;
import org.osgi.framework.Bundle;
import org.osgi.service.prefs.BackingStoreException;
import org.osgi.service.prefs.Preferences;

/**
* Manages user-defined encodings as preferences in the project content area.
*
* @since 3.0
*/
public class CharsetManager implements IManager {
  /**
   * This job implementation is used to allow the resource change listener
   * to schedule operations that need to modify the workspace.
   */
  private class CharsetManagerJob extends Job {
    private static final int CHARSET_UPDATE_DELAY = 500;
    private List asyncChanges = new ArrayList();

    public CharsetManagerJob() {
      super(Messages.resources_charsetUpdating);
      setSystem(true);
      setPriority(Job.INTERACTIVE);
    }

    public void addChanges(Set newChanges) {
      if (newChanges.isEmpty())
        return;
      synchronized (asyncChanges) {
        asyncChanges.addAll(newChanges);
        asyncChanges.notify();
      }
      schedule(CHARSET_UPDATE_DELAY);
    }

    public IProject getNextChange() {
      synchronized (asyncChanges) {
        return asyncChanges.isEmpty() ? null : (IProject) asyncChanges.remove(asyncChanges.size() - 1);
      }
    }

    /* (non-Javadoc)
     * @see org.eclipse.core.internal.jobs.InternalJob#run(org.eclipse.core.runtime.IProgressMonitor)
     */
    protected IStatus run(IProgressMonitor monitor) {
      MultiStatus result = new MultiStatus(ResourcesPlugin.PI_RESOURCES, IResourceStatus.FAILED_SETTING_CHARSET, Messages.resources_updatingEncoding, null);
      monitor = Policy.monitorFor(monitor);
      try {
        monitor.beginTask(Messages.resources_charsetUpdating, Policy.totalWork);
        final ISchedulingRule rule = workspace.getRuleFactory().modifyRule(workspace.getRoot());
        try {
          workspace.prepareOperation(rule, monitor);
          workspace.beginOperation(true);
          IProject next;
          while ((next = getNextChange()) != null) {
            //just exit if the system is shutting down or has been shut down
            //it is too late to change the workspace at this point anyway
            if (systemBundle.getState() != Bundle.ACTIVE)
              return Status.OK_STATUS;
            try {
              if (next.isAccessible()) {
                Preferences projectPrefs = getPreferences(next, false);
                if (projectPrefs != null)
                  projectPrefs.flush();
              }
            } catch (BackingStoreException e) {
              // we got an error saving         
              String detailMessage = Messages.resources_savingEncoding;
              result.add(new ResourceStatus(IResourceStatus.FAILED_SETTING_CHARSET, next.getFullPath(), detailMessage, e));
            }
          }
          monitor.worked(Policy.opWork);
        } catch (OperationCanceledException e) {
          workspace.getWorkManager().operationCanceled();
          throw e;
        } finally {
          workspace.endOperation(rule, true, Policy.subMonitorFor(monitor, Policy.endOpWork));
        }
      } catch (CoreException ce) {
        return ce.getStatus();
      } finally {
        monitor.done();
      }
      return result;
    }

    /* (non-Javadoc)
     * @see org.eclipse.core.runtime.jobs.Job#shouldRun()
     */
    public boolean shouldRun() {
      synchronized (asyncChanges) {
        return !asyncChanges.isEmpty();
      }
    }
  }

  class Listener implements IResourceChangeListener {

    private void processEntryChanges(IResourceDelta projectDelta, Set projectsToSave) {
      // check each resource with user-set encoding to see if it has
      // been moved/deleted
      boolean resourceChanges = false;
      IProject currentProject = (IProject) projectDelta.getResource();
      Preferences projectPrefs = getPreferences(currentProject, false);
      if (projectPrefs == null)
        // no preferences for this project, just bail
        return;
      String[] affectedResources;
      try {
        affectedResources = projectPrefs.keys();
      } catch (BackingStoreException e) {
        // problems with the project scope... we gonna miss the changes (but will log)
        String message = Messages.resources_readingEncoding;
        Policy.log(new ResourceStatus(IResourceStatus.FAILED_GETTING_CHARSET, currentProject.getFullPath(), message, e));
        return;
      }
      for (int i = 0; i < affectedResources.length; i++) {
        IResourceDelta memberDelta = projectDelta.findMember(new Path(affectedResources[i]));
        // no changes for the given resource
        if (memberDelta == null)
          continue;
        if (memberDelta.getKind() == IResourceDelta.REMOVED) {
          resourceChanges = true;
          // remove the setting for the original location - save its value though
          String currentValue = projectPrefs.get(affectedResources[i], null);
          projectPrefs.remove(affectedResources[i]);
          if ((memberDelta.getFlags() & IResourceDelta.MOVED_TO) != 0) {
            // if moving, copy the setting for the new location
            IProject targetProject = workspace.getRoot().getProject(memberDelta.getMovedToPath().segment(0));
            Preferences targetPrefs = getPreferences(targetProject, true);
            targetPrefs.put(getKeyFor(memberDelta.getMovedToPath()), currentValue);
            if (targetProject != currentProject)
              projectsToSave.add(targetProject);
          }
        }
      }
      if (resourceChanges)
        projectsToSave.add(currentProject);
    }

    /**
     * For any change to the encoding file or any resource with encoding
     * set, just discard the cache for the corresponding project.
     */
    public void resourceChanged(IResourceChangeEvent event) {
      IResourceDelta delta = event.getDelta();
      if (delta == null)
        return;
      IResourceDelta[] projectDeltas = delta.getAffectedChildren();
      // process each project in the delta
      Set projectsToSave = new HashSet();
      for (int i = 0; i < projectDeltas.length; i++)
        //nothing to do if a project has been added/removed/moved       
        if (projectDeltas[i].getKind() == IResourceDelta.CHANGED && (projectDeltas[i].getFlags() & IResourceDelta.OPEN) == 0)
          processEntryChanges(projectDeltas[i], projectsToSave);
      job.addChanges(projectsToSave);
    }
  }

  public static final String ENCODING_PREF_NODE = "encoding"; //$NON-NLS-1$   
  private static final String PROJECT_KEY = "<project>"; //$NON-NLS-1$
  private CharsetDeltaJob charsetListener;
  CharsetManagerJob job;
  private IResourceChangeListener listener;
  protected final Bundle systemBundle = Platform.getBundle("org.eclipse.osgi"); //$NON-NLS-1$
  Workspace workspace;

  public CharsetManager(Workspace workspace) {
    this.workspace = workspace;
  }

  /**
   * Returns the charset explicitly set by the user for the given resource,
   * or <code>null</code>. If no setting exists for the given resource and
   * <code>recurse</code> is <code>true</code>, every parent up to the
   * workspace root will be checked until a charset setting can be found.
   *
   * @param resourcePath the path for the resource
   * @param recurse whether the parent should be queried
   * @return the charset setting for the given resource
   */
  public String getCharsetFor(IPath resourcePath, boolean recurse) {
    Assert.isLegal(resourcePath.segmentCount() >= 1);
    IProject project = workspace.getRoot().getProject(resourcePath.segment(0));
    Preferences encodingSettings = getPreferences(project, false);
    if (encodingSettings == null)
      // no preferences found - for performance reasons, short-circuit
      // lookup by falling back to workspace's default setting     
      return recurse ? ResourcesPlugin.getEncoding() : null;
    return internalGetCharsetFor(resourcePath, encodingSettings, recurse);
  }

  String getKeyFor(IPath resourcePath) {
    return resourcePath.segmentCount() > 1 ? resourcePath.removeFirstSegments(1).toString() : PROJECT_KEY;
  }

  Preferences getPreferences(IProject project, boolean create) {
    if (create)
      // create all nodes down to the one we are interested in
      return new ProjectScope(project).getNode(ResourcesPlugin.PI_RESOURCES).node(ENCODING_PREF_NODE);
    // be careful looking up for our node so not to create any nodes as side effect
    Preferences node = Platform.getPreferencesService().getRootNode().node(ProjectScope.SCOPE);
    try {
      //TODO once bug 90500 is fixed, should be as simple as this:
      //      String path = project.getName() + IPath.SEPARATOR + ResourcesPlugin.PI_RESOURCES + IPath.SEPARATOR + ENCODING_PREF_NODE;
      //      return node.nodeExists(path) ? node.node(path) : null;
      // for now, take the long way
      if (!node.nodeExists(project.getName()))
        return null;
      node = node.node(project.getName());
      if (!node.nodeExists(ResourcesPlugin.PI_RESOURCES))
        return null;
      node = node.node(ResourcesPlugin.PI_RESOURCES);
      if (!node.nodeExists(ENCODING_PREF_NODE))
        return null;
      return node.node(ENCODING_PREF_NODE);
    } catch (BackingStoreException e) {
      // nodeExists failed
      String message = Messages.resources_readingEncoding;
      Policy.log(new ResourceStatus(IResourceStatus.FAILED_GETTING_CHARSET, project.getFullPath(), message, e));
    }
    return null;
  }

  private String internalGetCharsetFor(IPath resourcePath, Preferences encodingSettings, boolean recurse) {
    String charset = encodingSettings.get(getKeyFor(resourcePath), null);
    if (!recurse)
      return charset;
    while (charset == null && resourcePath.segmentCount() > 1) {
      resourcePath = resourcePath.removeLastSegments(1);
      charset = encodingSettings.get(getKeyFor(resourcePath), null);
    }
    // ensure we default to the workspace encoding if none is found
    return charset == null ? ResourcesPlugin.getEncoding() : charset;
  }

  public void projectPreferencesChanged(IProject project) {
    charsetListener.charsetPreferencesChanged(project);
  }

  public void setCharsetFor(IPath resourcePath, String newCharset) throws CoreException {
    // for the workspace root we just set a preference in the instance scope
    if (resourcePath.segmentCount() == 0) {
      org.eclipse.core.runtime.Preferences resourcesPreferences = ResourcesPlugin.getPlugin().getPluginPreferences();
      if (newCharset != null)
        resourcesPreferences.setValue(ResourcesPlugin.PREF_ENCODING, newCharset);
      else
        resourcesPreferences.setToDefault(ResourcesPlugin.PREF_ENCODING);
      ResourcesPlugin.getPlugin().savePluginPreferences();
      return;
    }
    // for all other cases, we set a property in the corresponding project
    IProject project = workspace.getRoot().getProject(resourcePath.segment(0));
    Preferences encodingSettings = getPreferences(project, true);
    if (newCharset == null || newCharset.trim().length() == 0)
      encodingSettings.remove(getKeyFor(resourcePath));
    else
      encodingSettings.put(getKeyFor(resourcePath), newCharset);
    try {
      // disable the listener so we don't react to changes made by ourselves
      charsetListener.setDisabled(true);
      // save changes
      encodingSettings.flush();
    } catch (BackingStoreException e) {
      String message = Messages.resources_savingEncoding;
      throw new ResourceException(IResourceStatus.FAILED_SETTING_CHARSET, project.getFullPath(), message, e);
    } finally {
      charsetListener.setDisabled(false);
    }

  }

  public void shutdown(IProgressMonitor monitor) {
    workspace.removeResourceChangeListener(listener);
    if (charsetListener != null)
      charsetListener.shutdown();
  }

  public void startup(IProgressMonitor monitor) {
    job = new CharsetManagerJob();
    listener = new Listener();
    workspace.addResourceChangeListener(listener, IResourceChangeEvent.POST_CHANGE);
    charsetListener = new CharsetDeltaJob(workspace);
    charsetListener.startup();
  }
}
TOP

Related Classes of org.eclipse.core.internal.resources.CharsetManager$Listener

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.