/plug-ins/helios/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/CharsetManager.java
Java | 310 lines | 218 code | 28 blank | 64 comment | 48 complexity | b6ca4c72e53e723f9ee36190cc3c79eb MD5 | raw file
- /*******************************************************************************
- * 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();
- }
- }