/******************************************************************************* * Copyright (c) 2004, 2011 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 * James Blackburn (Broadcom Corp.) - ongoing development *******************************************************************************/ package org.eclipse.core.internal.resources; import org.eclipse.core.internal.utils.*; import org.eclipse.core.internal.watson.*; import org.eclipse.core.resources.*; import org.eclipse.core.runtime.*; import org.eclipse.core.runtime.content.IContentTypeManager; import org.eclipse.core.runtime.content.IContentTypeManager.ContentTypeChangeEvent; import org.eclipse.core.runtime.jobs.Job; import org.osgi.framework.Bundle; /** * Detects changes to content types/project preferences and * broadcasts any corresponding encoding changes as resource deltas. */ public class CharsetDeltaJob extends Job implements IContentTypeManager.IContentTypeChangeListener { // this is copied in the runtime tests - if changed here, has to be changed there too public final static String FAMILY_CHARSET_DELTA = ResourcesPlugin.PI_RESOURCES + "charsetJobFamily"; //$NON-NLS-1$ interface ICharsetListenerFilter { /** * Returns the path for the node in the tree we are interested in. */ IPath getRoot(); /** * Returns whether the corresponding resource is affected by this change. */ boolean isAffected(ResourceInfo info, IPathRequestor requestor); } private ThreadLocal<Boolean> disabled = new ThreadLocal<Boolean>(); private final Bundle systemBundle = Platform.getBundle("org.eclipse.osgi"); //$NON-NLS-1$ private Queue<ICharsetListenerFilter> work = new Queue<ICharsetListenerFilter>(); Workspace workspace; private static final int CHARSET_DELTA_DELAY = 500; public CharsetDeltaJob(Workspace workspace) { super(Messages.resources_charsetBroadcasting); this.workspace = workspace; } private void addToQueue(ICharsetListenerFilter filter) { synchronized (work) { work.add(filter); } schedule(CHARSET_DELTA_DELAY); } public boolean belongsTo(Object family) { return FAMILY_CHARSET_DELTA.equals(family); } public void charsetPreferencesChanged(final IProject project) { // avoid reacting to changes made by ourselves if (isDisabled()) return; // ensure all resources under the affected project are // reported as having encoding changes ICharsetListenerFilter filter = new ICharsetListenerFilter() { public IPath getRoot() { // visit the project subtree return project.getFullPath(); } public boolean isAffected(ResourceInfo info, IPathRequestor requestor) { // for now, mark all resources in the project as potential encoding resource changes return true; } }; addToQueue(filter); } public void contentTypeChanged(final ContentTypeChangeEvent event) { // check all files that may be affected by this change (taking // only the current content type state into account // dispatch a job to generate the deltas ICharsetListenerFilter filter = new ICharsetListenerFilter() { public IPath getRoot() { // visit all resources in the workspace return Path.ROOT; } public boolean isAffected(ResourceInfo info, IPathRequestor requestor) { if (info.getType() != IResource.FILE) return false; return event.getContentType().isAssociatedWith(requestor.requestName()); } }; addToQueue(filter); } private boolean isDisabled() { return disabled.get() != null; } private void processNextEvent(final ICharsetListenerFilter filter, IProgressMonitor monitor) throws CoreException { IElementContentVisitor visitor = new IElementContentVisitor() { public boolean visitElement(ElementTree tree, IPathRequestor requestor, Object elementContents) { ResourceInfo info = (ResourceInfo) elementContents; if (!filter.isAffected(info, requestor)) return true; info = workspace.getResourceInfo(requestor.requestPath(), false, true); if (info == null) return false; info.incrementCharsetGenerationCount(); return true; } }; try { new ElementTreeIterator(workspace.getElementTree(), filter.getRoot()).iterate(visitor); } catch (WrappedRuntimeException e) { throw (CoreException) e.getTargetException(); } if (monitor.isCanceled()) throw new OperationCanceledException(); } private ICharsetListenerFilter removeFromQueue() { synchronized (work) { return work.remove(); } } /* (non-Javadoc) * @see org.eclipse.core.internal.jobs.InternalJob#run(org.eclipse.core.runtime.IProgressMonitor) */ public IStatus run(IProgressMonitor monitor) { monitor = Policy.monitorFor(monitor); try { String message = Messages.resources_charsetBroadcasting; monitor.beginTask(message, Policy.totalWork); try { workspace.prepareOperation(null, monitor); workspace.beginOperation(true); ICharsetListenerFilter next; //if the system is shutting down, don't broadcast while (systemBundle.getState() != Bundle.STOPPING && (next = removeFromQueue()) != null) processNextEvent(next, monitor); } catch (OperationCanceledException e) { workspace.getWorkManager().operationCanceled(); return Status.CANCEL_STATUS; } finally { workspace.endOperation(null, true, Policy.subMonitorFor(monitor, Policy.endOpWork)); } monitor.worked(Policy.opWork); } catch (CoreException sig) { return sig.getStatus(); } finally { monitor.done(); } return Status.OK_STATUS; } /** * Turns off reaction to changes in the preference file. */ public void setDisabled(boolean disabled) { // using a thread local because this can be called by multiple threads concurrently this.disabled.set(disabled ? Boolean.TRUE : null); } public void shutdown() { Platform.getContentTypeManager().removeContentTypeChangeListener(this); } public void startup() { Platform.getContentTypeManager().addContentTypeChangeListener(this); } }