| /******************************************************************************* |
| * Copyright (c) 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.wst.html.core.internal.contentproperties; |
| |
| import java.util.ArrayList; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Set; |
| |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.resources.IResourceChangeEvent; |
| import org.eclipse.core.resources.IResourceChangeListener; |
| import org.eclipse.core.resources.IResourceDelta; |
| import org.eclipse.core.resources.IResourceStatus; |
| import org.eclipse.core.resources.ResourcesPlugin; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.MultiStatus; |
| import org.eclipse.core.runtime.NullProgressMonitor; |
| import org.eclipse.core.runtime.OperationCanceledException; |
| import org.eclipse.core.runtime.Path; |
| import org.eclipse.core.runtime.Platform; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.core.runtime.jobs.Job; |
| import org.eclipse.osgi.util.NLS; |
| import org.eclipse.wst.html.core.internal.HTMLCoreMessages; |
| import org.eclipse.wst.html.core.internal.Logger; |
| import org.osgi.framework.Bundle; |
| import org.osgi.service.prefs.BackingStoreException; |
| import org.osgi.service.prefs.Preferences; |
| |
| public class HTMLContentPropertiesManager { |
| public HTMLContentPropertiesManager() { |
| super(); |
| fResourceChangeListener = new ResourceChangeListener(); |
| fJob = new ContentPropertiesManagerJob(); |
| } |
| |
| private static HTMLContentPropertiesManager _instance = null; |
| private IResourceChangeListener fResourceChangeListener; |
| ContentPropertiesManagerJob fJob; |
| |
| /** |
| * This job implementation is used to allow the resource change listener |
| * to schedule operations that need to modify the workspace. |
| */ |
| private class ContentPropertiesManagerJob extends Job { |
| private static final int PROPERTIES_UPDATE_DELAY = 500; |
| private List asyncChanges = new ArrayList(); |
| |
| public ContentPropertiesManagerJob() { |
| super(HTMLCoreMessages.HTMLContentPropertiesManager_Updating); |
| setSystem(true); |
| setPriority(Job.INTERACTIVE); |
| } |
| |
| public void addChanges(Set newChanges) { |
| if (newChanges.isEmpty()) |
| return; |
| synchronized (asyncChanges) { |
| asyncChanges.addAll(newChanges); |
| asyncChanges.notify(); |
| } |
| schedule(PROPERTIES_UPDATE_DELAY); |
| } |
| |
| public IProject getNextChange() { |
| synchronized (asyncChanges) { |
| return asyncChanges.isEmpty() ? null : (IProject) asyncChanges.remove(asyncChanges.size() - 1); |
| } |
| } |
| |
| protected IStatus run(IProgressMonitor monitor) { |
| MultiStatus result = new MultiStatus(HTMLContentProperties.HTMLCORE_ID, IResourceStatus.FAILED_SETTING_CHARSET, HTMLCoreMessages.HTMLContentPropertiesManager_Updating, null); |
| monitor = monitor == null ? new NullProgressMonitor() : monitor; |
| try { |
| monitor.beginTask(HTMLCoreMessages.HTMLContentPropertiesManager_Updating, asyncChanges.size()); |
| try { |
| 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 (Platform.getBundle("org.eclipse.osgi").getState() != Bundle.ACTIVE) //$NON-NLS-1$ |
| return Status.OK_STATUS; |
| try { |
| // save the preferences nodes |
| if (next.isAccessible()) { |
| // save document type preferences |
| Preferences projectPrefs = HTMLContentProperties.getPreferences(next, HTMLContentProperties.DOCUMENT_TYPE, false); |
| if (projectPrefs != null) |
| projectPrefs.flush(); |
| } |
| } |
| catch (BackingStoreException e) { |
| // we got an error saving |
| String detailMessage = NLS.bind(HTMLCoreMessages.HTMLContentPropertiesManager_Problems_Updating, next.getFullPath()); |
| result.add(new Status(1 << (IResourceStatus.FAILED_SETTING_CHARSET % 100 / 33), ResourcesPlugin.PI_RESOURCES, IResourceStatus.FAILED_SETTING_CHARSET, detailMessage, e)); |
| } |
| } |
| monitor.worked(1); |
| } |
| catch (OperationCanceledException e) { |
| throw e; |
| } |
| } |
| finally { |
| monitor.done(); |
| } |
| return result; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.core.runtime.jobs.Job#shouldRun() |
| */ |
| public boolean shouldRun() { |
| synchronized (asyncChanges) { |
| return !asyncChanges.isEmpty(); |
| } |
| } |
| } |
| |
| class ResourceChangeListener implements IResourceChangeListener { |
| private void processEntryChanges(IResourceDelta projectDelta, Set projectsToSave) { |
| // check each resource with web content setting to see if it has |
| // been moved/deleted |
| boolean resourceChanges = false; |
| |
| // project affected |
| IProject currentProject = (IProject) projectDelta.getResource(); |
| |
| resourceChanges = processPreferences(currentProject, HTMLContentProperties.DOCUMENT_TYPE, projectDelta, projectsToSave); |
| |
| // if there was a preference key change, need to save preferences |
| if (resourceChanges) |
| projectsToSave.add(currentProject); |
| } |
| |
| /** |
| * Goes through all the resource-dependent preferences associated with |
| * currentProject & key and updates the preference keys if needed |
| * based on projectDelta |
| * |
| * @param currentProject |
| * current project of the preferences to be looked at |
| * @param key |
| * current key/subcategory of the preferences to be looked |
| * at |
| * @param projectDelta |
| * the changes to process the preference keys against |
| * @param projectsToSave |
| * the projects that need to be updated/saved |
| * @return true if currentProject's preferences were modified |
| */ |
| private boolean processPreferences(IProject currentProject, String key, IResourceDelta projectDelta, Set projectsToSave) { |
| boolean resourceChanges = false; |
| |
| // get the project-key preference node |
| Preferences projectPrefs = HTMLContentProperties.getPreferences(currentProject, key, false); |
| if (projectPrefs == null) |
| // no preferences for this project-key, just bail |
| return false; |
| String[] affectedResources; |
| try { |
| affectedResources = projectPrefs.keys(); |
| } |
| catch (BackingStoreException e) { |
| // problems with the project scope... we gonna miss the |
| // changes (but will log) |
| Logger.log(Logger.WARNING_DEBUG, "Problem retreiving JSP Fragment preferences", e); //$NON-NLS-1$ |
| return false; |
| } |
| |
| // go through each preference key (which is really a file name) |
| for (int i = 0; i < affectedResources.length; i++) { |
| // see if preference key/file name was file that was changed |
| 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 |
| 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 = ResourcesPlugin.getWorkspace().getRoot().getProject(memberDelta.getMovedToPath().segment(0)); |
| Preferences targetPrefs = HTMLContentProperties.getPreferences(targetProject, key, true); |
| targetPrefs.put(HTMLContentProperties.getKeyFor(memberDelta.getMovedToPath()), currentValue); |
| if (targetProject != currentProject) |
| projectsToSave.add(targetProject); |
| } |
| } |
| } |
| return resourceChanges; |
| } |
| |
| /** |
| * 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); |
| fJob.addChanges(projectsToSave); |
| } |
| } |
| |
| public synchronized static void startup() { |
| _instance = new HTMLContentPropertiesManager(); |
| ResourcesPlugin.getWorkspace().addResourceChangeListener(_instance.fResourceChangeListener, IResourceChangeEvent.POST_CHANGE); |
| } |
| |
| public synchronized static void shutdown() { |
| ResourcesPlugin.getWorkspace().removeResourceChangeListener(_instance.fResourceChangeListener); |
| _instance = null; |
| } |
| } |