/*******************************************************************************
* Copyright (c) 2004, 2005 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 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 disabled= new ThreadLocal();
private final Bundle systemBundle= Platform.getBundle("org.eclipse.osgi"); //$NON-NLS-1$
private Queue work= new Queue();
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 (ICharsetListenerFilter)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);
}
}