PageRenderTime 26ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/plug-ins/helios/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/CharsetManager.java

https://github.com/vazexqi/CodingSpectator
Java | 310 lines | 218 code | 28 blank | 64 comment | 48 complexity | b6ca4c72e53e723f9ee36190cc3c79eb MD5 | raw file
  1. /*******************************************************************************
  2. * Copyright (c) 2004, 2006 IBM Corporation and others.
  3. * All rights reserved. This program and the accompanying materials
  4. * are made available under the terms of the Eclipse Public License v1.0
  5. * which accompanies this distribution, and is available at
  6. * http://www.eclipse.org/legal/epl-v10.html
  7. *
  8. * Contributors:
  9. * IBM Corporation - initial API and implementation
  10. *******************************************************************************/
  11. package org.eclipse.core.internal.resources;
  12. import java.util.*;
  13. import org.eclipse.core.internal.utils.Messages;
  14. import org.eclipse.core.internal.utils.Policy;
  15. import org.eclipse.core.resources.*;
  16. import org.eclipse.core.runtime.*;
  17. import org.eclipse.core.runtime.jobs.ISchedulingRule;
  18. import org.eclipse.core.runtime.jobs.Job;
  19. import org.osgi.framework.Bundle;
  20. import org.osgi.service.prefs.BackingStoreException;
  21. import org.osgi.service.prefs.Preferences;
  22. /**
  23. * Manages user-defined encodings as preferences in the project content area.
  24. *
  25. * @since 3.0
  26. */
  27. public class CharsetManager implements IManager {
  28. /**
  29. * This job implementation is used to allow the resource change listener to schedule operations
  30. * that need to modify the workspace.
  31. */
  32. private class CharsetManagerJob extends Job {
  33. private static final int CHARSET_UPDATE_DELAY= 500;
  34. private List asyncChanges= new ArrayList();
  35. public CharsetManagerJob() {
  36. super(Messages.resources_charsetUpdating);
  37. setSystem(true);
  38. setPriority(Job.INTERACTIVE);
  39. }
  40. public void addChanges(Set newChanges) {
  41. if (newChanges.isEmpty())
  42. return;
  43. synchronized (asyncChanges) {
  44. asyncChanges.addAll(newChanges);
  45. asyncChanges.notify();
  46. }
  47. schedule(CHARSET_UPDATE_DELAY);
  48. }
  49. public IProject getNextChange() {
  50. synchronized (asyncChanges) {
  51. return asyncChanges.isEmpty() ? null : (IProject)asyncChanges.remove(asyncChanges.size() - 1);
  52. }
  53. }
  54. /* (non-Javadoc)
  55. * @see org.eclipse.core.internal.jobs.InternalJob#run(org.eclipse.core.runtime.IProgressMonitor)
  56. */
  57. protected IStatus run(IProgressMonitor monitor) {
  58. MultiStatus result= new MultiStatus(ResourcesPlugin.PI_RESOURCES, IResourceStatus.FAILED_SETTING_CHARSET, Messages.resources_updatingEncoding, null);
  59. monitor= Policy.monitorFor(monitor);
  60. try {
  61. monitor.beginTask(Messages.resources_charsetUpdating, Policy.totalWork);
  62. final ISchedulingRule rule= workspace.getRuleFactory().modifyRule(workspace.getRoot());
  63. try {
  64. workspace.prepareOperation(rule, monitor);
  65. workspace.beginOperation(true);
  66. IProject next;
  67. while ((next= getNextChange()) != null) {
  68. //just exit if the system is shutting down or has been shut down
  69. //it is too late to change the workspace at this point anyway
  70. if (systemBundle.getState() != Bundle.ACTIVE)
  71. return Status.OK_STATUS;
  72. try {
  73. if (next.isAccessible()) {
  74. Preferences projectPrefs= getPreferences(next, false);
  75. if (projectPrefs != null)
  76. projectPrefs.flush();
  77. }
  78. } catch (BackingStoreException e) {
  79. // we got an error saving
  80. String detailMessage= Messages.resources_savingEncoding;
  81. result.add(new ResourceStatus(IResourceStatus.FAILED_SETTING_CHARSET, next.getFullPath(), detailMessage, e));
  82. }
  83. }
  84. monitor.worked(Policy.opWork);
  85. } catch (OperationCanceledException e) {
  86. workspace.getWorkManager().operationCanceled();
  87. throw e;
  88. } finally {
  89. workspace.endOperation(rule, true, Policy.subMonitorFor(monitor, Policy.endOpWork));
  90. }
  91. } catch (CoreException ce) {
  92. return ce.getStatus();
  93. } finally {
  94. monitor.done();
  95. }
  96. return result;
  97. }
  98. /* (non-Javadoc)
  99. * @see org.eclipse.core.runtime.jobs.Job#shouldRun()
  100. */
  101. public boolean shouldRun() {
  102. synchronized (asyncChanges) {
  103. return !asyncChanges.isEmpty();
  104. }
  105. }
  106. }
  107. class Listener implements IResourceChangeListener {
  108. private void processEntryChanges(IResourceDelta projectDelta, Set projectsToSave) {
  109. // check each resource with user-set encoding to see if it has
  110. // been moved/deleted
  111. boolean resourceChanges= false;
  112. IProject currentProject= (IProject)projectDelta.getResource();
  113. Preferences projectPrefs= getPreferences(currentProject, false);
  114. if (projectPrefs == null)
  115. // no preferences for this project, just bail
  116. return;
  117. String[] affectedResources;
  118. try {
  119. affectedResources= projectPrefs.keys();
  120. } catch (BackingStoreException e) {
  121. // problems with the project scope... we gonna miss the changes (but will log)
  122. String message= Messages.resources_readingEncoding;
  123. Policy.log(new ResourceStatus(IResourceStatus.FAILED_GETTING_CHARSET, currentProject.getFullPath(), message, e));
  124. return;
  125. }
  126. for (int i= 0; i < affectedResources.length; i++) {
  127. IResourceDelta memberDelta= projectDelta.findMember(new Path(affectedResources[i]));
  128. // no changes for the given resource
  129. if (memberDelta == null)
  130. continue;
  131. if (memberDelta.getKind() == IResourceDelta.REMOVED) {
  132. resourceChanges= true;
  133. // remove the setting for the original location - save its value though
  134. String currentValue= projectPrefs.get(affectedResources[i], null);
  135. projectPrefs.remove(affectedResources[i]);
  136. if ((memberDelta.getFlags() & IResourceDelta.MOVED_TO) != 0) {
  137. // if moving, copy the setting for the new location
  138. IProject targetProject= workspace.getRoot().getProject(memberDelta.getMovedToPath().segment(0));
  139. Preferences targetPrefs= getPreferences(targetProject, true);
  140. targetPrefs.put(getKeyFor(memberDelta.getMovedToPath()), currentValue);
  141. if (targetProject != currentProject)
  142. projectsToSave.add(targetProject);
  143. }
  144. }
  145. }
  146. if (resourceChanges)
  147. projectsToSave.add(currentProject);
  148. }
  149. /**
  150. * For any change to the encoding file or any resource with encoding set, just discard the
  151. * cache for the corresponding project.
  152. */
  153. public void resourceChanged(IResourceChangeEvent event) {
  154. IResourceDelta delta= event.getDelta();
  155. if (delta == null)
  156. return;
  157. IResourceDelta[] projectDeltas= delta.getAffectedChildren();
  158. // process each project in the delta
  159. Set projectsToSave= new HashSet();
  160. for (int i= 0; i < projectDeltas.length; i++)
  161. //nothing to do if a project has been added/removed/moved
  162. if (projectDeltas[i].getKind() == IResourceDelta.CHANGED && (projectDeltas[i].getFlags() & IResourceDelta.OPEN) == 0)
  163. processEntryChanges(projectDeltas[i], projectsToSave);
  164. job.addChanges(projectsToSave);
  165. }
  166. }
  167. public static final String ENCODING_PREF_NODE= "encoding"; //$NON-NLS-1$
  168. private static final String PROJECT_KEY= "<project>"; //$NON-NLS-1$
  169. private CharsetDeltaJob charsetListener;
  170. CharsetManagerJob job;
  171. private IResourceChangeListener listener;
  172. protected final Bundle systemBundle= Platform.getBundle("org.eclipse.osgi"); //$NON-NLS-1$
  173. Workspace workspace;
  174. public CharsetManager(Workspace workspace) {
  175. this.workspace= workspace;
  176. }
  177. /**
  178. * Returns the charset explicitly set by the user for the given resource, or <code>null</code>.
  179. * If no setting exists for the given resource and <code>recurse</code> is <code>true</code>,
  180. * every parent up to the workspace root will be checked until a charset setting can be found.
  181. *
  182. * @param resourcePath the path for the resource
  183. * @param recurse whether the parent should be queried
  184. * @return the charset setting for the given resource
  185. */
  186. public String getCharsetFor(IPath resourcePath, boolean recurse) {
  187. Assert.isLegal(resourcePath.segmentCount() >= 1);
  188. IProject project= workspace.getRoot().getProject(resourcePath.segment(0));
  189. Preferences encodingSettings= getPreferences(project, false);
  190. if (encodingSettings == null)
  191. // no preferences found - for performance reasons, short-circuit
  192. // lookup by falling back to workspace's default setting
  193. return recurse ? ResourcesPlugin.getEncoding() : null;
  194. return internalGetCharsetFor(resourcePath, encodingSettings, recurse);
  195. }
  196. String getKeyFor(IPath resourcePath) {
  197. return resourcePath.segmentCount() > 1 ? resourcePath.removeFirstSegments(1).toString() : PROJECT_KEY;
  198. }
  199. Preferences getPreferences(IProject project, boolean create) {
  200. if (create)
  201. // create all nodes down to the one we are interested in
  202. return new ProjectScope(project).getNode(ResourcesPlugin.PI_RESOURCES).node(ENCODING_PREF_NODE);
  203. // be careful looking up for our node so not to create any nodes as side effect
  204. Preferences node= Platform.getPreferencesService().getRootNode().node(ProjectScope.SCOPE);
  205. try {
  206. //TODO once bug 90500 is fixed, should be as simple as this:
  207. // String path = project.getName() + IPath.SEPARATOR + ResourcesPlugin.PI_RESOURCES + IPath.SEPARATOR + ENCODING_PREF_NODE;
  208. // return node.nodeExists(path) ? node.node(path) : null;
  209. // for now, take the long way
  210. if (!node.nodeExists(project.getName()))
  211. return null;
  212. node= node.node(project.getName());
  213. if (!node.nodeExists(ResourcesPlugin.PI_RESOURCES))
  214. return null;
  215. node= node.node(ResourcesPlugin.PI_RESOURCES);
  216. if (!node.nodeExists(ENCODING_PREF_NODE))
  217. return null;
  218. return node.node(ENCODING_PREF_NODE);
  219. } catch (BackingStoreException e) {
  220. // nodeExists failed
  221. String message= Messages.resources_readingEncoding;
  222. Policy.log(new ResourceStatus(IResourceStatus.FAILED_GETTING_CHARSET, project.getFullPath(), message, e));
  223. }
  224. return null;
  225. }
  226. private String internalGetCharsetFor(IPath resourcePath, Preferences encodingSettings, boolean recurse) {
  227. String charset= encodingSettings.get(getKeyFor(resourcePath), null);
  228. if (!recurse)
  229. return charset;
  230. while (charset == null && resourcePath.segmentCount() > 1) {
  231. resourcePath= resourcePath.removeLastSegments(1);
  232. charset= encodingSettings.get(getKeyFor(resourcePath), null);
  233. }
  234. // ensure we default to the workspace encoding if none is found
  235. return charset == null ? ResourcesPlugin.getEncoding() : charset;
  236. }
  237. public void projectPreferencesChanged(IProject project) {
  238. charsetListener.charsetPreferencesChanged(project);
  239. }
  240. public void setCharsetFor(IPath resourcePath, String newCharset) throws CoreException {
  241. // for the workspace root we just set a preference in the instance scope
  242. if (resourcePath.segmentCount() == 0) {
  243. org.eclipse.core.runtime.Preferences resourcesPreferences= ResourcesPlugin.getPlugin().getPluginPreferences();
  244. if (newCharset != null)
  245. resourcesPreferences.setValue(ResourcesPlugin.PREF_ENCODING, newCharset);
  246. else
  247. resourcesPreferences.setToDefault(ResourcesPlugin.PREF_ENCODING);
  248. ResourcesPlugin.getPlugin().savePluginPreferences();
  249. return;
  250. }
  251. // for all other cases, we set a property in the corresponding project
  252. IProject project= workspace.getRoot().getProject(resourcePath.segment(0));
  253. Preferences encodingSettings= getPreferences(project, true);
  254. if (newCharset == null || newCharset.trim().length() == 0)
  255. encodingSettings.remove(getKeyFor(resourcePath));
  256. else
  257. encodingSettings.put(getKeyFor(resourcePath), newCharset);
  258. try {
  259. // disable the listener so we don't react to changes made by ourselves
  260. charsetListener.setDisabled(true);
  261. // save changes
  262. encodingSettings.flush();
  263. } catch (BackingStoreException e) {
  264. String message= Messages.resources_savingEncoding;
  265. throw new ResourceException(IResourceStatus.FAILED_SETTING_CHARSET, project.getFullPath(), message, e);
  266. } finally {
  267. charsetListener.setDisabled(false);
  268. }
  269. }
  270. public void shutdown(IProgressMonitor monitor) {
  271. workspace.removeResourceChangeListener(listener);
  272. if (charsetListener != null)
  273. charsetListener.shutdown();
  274. }
  275. public void startup(IProgressMonitor monitor) {
  276. job= new CharsetManagerJob();
  277. listener= new Listener();
  278. workspace.addResourceChangeListener(listener, IResourceChangeEvent.POST_CHANGE);
  279. charsetListener= new CharsetDeltaJob(workspace);
  280. charsetListener.startup();
  281. }
  282. }