/*******************************************************************************
* Copyright (c) 2000, 2008 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
* Anton Leherbauer (Wind River Systems)
*******************************************************************************/
package org.eclipse.cdt.internal.ui.workingsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.IJobChangeEvent;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.core.runtime.jobs.JobChangeAdapter;
import org.eclipse.ui.IWorkingSet;
import org.eclipse.ui.IWorkingSetUpdater;
import org.eclipse.cdt.core.model.CoreModel;
import org.eclipse.cdt.core.model.ElementChangedEvent;
import org.eclipse.cdt.core.model.ICElement;
import org.eclipse.cdt.core.model.ICElementDelta;
import org.eclipse.cdt.core.model.ICProject;
import org.eclipse.cdt.core.model.IElementChangedListener;
public class CElementWorkingSetUpdater implements IWorkingSetUpdater, IElementChangedListener {
private static class SingletonRule implements ISchedulingRule {
public static final ISchedulingRule INSTANCE = new SingletonRule();
public boolean contains(ISchedulingRule rule) {
return rule == this;
}
public boolean isConflicting(ISchedulingRule rule) {
return rule == this;
}
}
private static class WorkingSetCheck extends Job {
private final IWorkingSet fWorkingSet;
WorkingSetCheck(final IWorkingSet workingSet) {
super("Check WorkingSet"); //$NON-NLS-1$
fWorkingSet= workingSet;
}
/*
* @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
*/
@Override
protected IStatus run(IProgressMonitor monitor) {
synchronized (fWorkingSet) {
checkElementExistence(fWorkingSet);
}
return Status.OK_STATUS;
}
}
public static final String ID= "org.eclipse.cdt.ui.CElementWorkingSetPage"; //$NON-NLS-1$
private List<IWorkingSet> fWorkingSets;
private static class WorkingSetDelta {
private IWorkingSet fWorkingSet;
private List<Object> fElements;
private boolean fChanged;
public WorkingSetDelta(IWorkingSet workingSet) {
fWorkingSet= workingSet;
synchronized (fWorkingSet) {
fElements= new ArrayList<Object>(Arrays.asList(fWorkingSet.getElements()));
}
}
public int indexOf(Object element) {
return fElements.indexOf(element);
}
public void set(int index, Object element) {
if (element == null) {
remove(index);
} else {
fElements.set(index, element);
fChanged= true;
}
}
public void remove(int index) {
if (fElements.remove(index) != null) {
fChanged= true;
}
}
public void process() {
if (fChanged) {
fWorkingSet.setElements(fElements.toArray(new IAdaptable[fElements.size()]));
}
}
}
public CElementWorkingSetUpdater() {
fWorkingSets= new ArrayList<IWorkingSet>();
CoreModel.getDefault().addElementChangedListener(this);
}
/**
* {@inheritDoc}
*/
public void add(final IWorkingSet workingSet) {
// delay the check for existence - this may be called very early in the bootstrap
// otherwise it is causing all kinds of weird exceptions
Job check= new WorkingSetCheck(workingSet);
check.setUser(false);
check.setPriority(Job.SHORT);
// make jobs run sequential
check.setRule(SingletonRule.INSTANCE);
check.schedule(2000 + fWorkingSets.size() * 100);
check.addJobChangeListener(new JobChangeAdapter() {
@Override
public void done(IJobChangeEvent event) {
synchronized (fWorkingSets) {
fWorkingSets.add(workingSet);
}
}});
}
/**
* {@inheritDoc}
*/
public boolean remove(IWorkingSet workingSet) {
boolean result;
synchronized(fWorkingSets) {
result= fWorkingSets.remove(workingSet);
}
return result;
}
/**
* {@inheritDoc}
*/
public boolean contains(IWorkingSet workingSet) {
synchronized(fWorkingSets) {
return fWorkingSets.contains(workingSet);
}
}
/**
* {@inheritDoc}
*/
public void dispose() {
synchronized(fWorkingSets) {
fWorkingSets.clear();
}
CoreModel.getDefault().removeElementChangedListener(this);
}
/**
* {@inheritDoc}
*/
public void elementChanged(ElementChangedEvent event) {
IWorkingSet[] workingSets;
synchronized(fWorkingSets) {
workingSets= fWorkingSets.toArray(new IWorkingSet[fWorkingSets.size()]);
}
for (int w= 0; w < workingSets.length; w++) {
WorkingSetDelta workingSetDelta= new WorkingSetDelta(workingSets[w]);
processCElementDelta(workingSetDelta, event.getDelta());
IResourceDelta[] resourceDeltas= event.getDelta().getResourceDeltas();
if (resourceDeltas != null) {
for (int r= 0; r < resourceDeltas.length; r++) {
processResourceDelta(workingSetDelta, resourceDeltas[r]);
}
}
workingSetDelta.process();
}
}
private void processCElementDelta(WorkingSetDelta result, ICElementDelta delta) {
ICElement cElement= delta.getElement();
int index= result.indexOf(cElement);
int type= cElement.getElementType();
int kind= delta.getKind();
int flags= delta.getFlags();
if (type == ICElement.C_PROJECT && kind == ICElementDelta.CHANGED) {
if (index != -1 && (flags & ICElementDelta.F_CLOSED) != 0) {
result.set(index, ((ICProject)cElement).getProject());
} else if ((flags & ICElementDelta.F_OPENED) != 0) {
index= result.indexOf(((ICProject)cElement).getProject());
if (index != -1)
result.set(index, cElement);
}
} else if (type == ICElement.C_PROJECT && kind == ICElementDelta.REMOVED) {
if (index != -1)
result.set(index, ((ICProject)cElement).getProject());
} else if (type == ICElement.C_PROJECT && kind == ICElementDelta.ADDED) {
index= result.indexOf(((ICProject)cElement).getProject());
if (index != -1)
result.set(index, cElement);
} else if (index != -1) {
if (kind == ICElementDelta.REMOVED) {
if ((flags & ICElementDelta.F_MOVED_TO) != 0) {
result.set(index, delta.getMovedToElement());
} else {
result.remove(index);
}
}
}
IResourceDelta[] resourceDeltas= delta.getResourceDeltas();
if (resourceDeltas != null) {
for (int i= 0; i < resourceDeltas.length; i++) {
processResourceDelta(result, resourceDeltas[i]);
}
}
ICElementDelta[] children= delta.getAffectedChildren();
for (int i= 0; i < children.length; i++) {
processCElementDelta(result, children[i]);
}
}
private void processResourceDelta(WorkingSetDelta result, IResourceDelta delta) {
IResource resource= delta.getResource();
int type= resource.getType();
int index= result.indexOf(resource);
int kind= delta.getKind();
int flags= delta.getFlags();
if (kind == IResourceDelta.CHANGED && type == IResource.PROJECT && index != -1) {
if ((flags & IResourceDelta.OPEN) != 0) {
result.set(index, resource);
}
}
if (index != -1 && kind == IResourceDelta.REMOVED) {
if ((flags & IResourceDelta.MOVED_TO) != 0) {
result.set(index,
ResourcesPlugin.getWorkspace().getRoot().findMember(delta.getMovedToPath()));
} else {
result.remove(index);
}
}
// Don't dive into closed or opened projects
if (projectGotClosedOrOpened(resource, kind, flags))
return;
IResourceDelta[] children= delta.getAffectedChildren();
for (int i= 0; i < children.length; i++) {
processResourceDelta(result, children[i]);
}
}
private boolean projectGotClosedOrOpened(IResource resource, int kind, int flags) {
return resource.getType() == IResource.PROJECT
&& kind == IResourceDelta.CHANGED
&& (flags & IResourceDelta.OPEN) != 0;
}
private static void checkElementExistence(IWorkingSet workingSet) {
List<IAdaptable> elements= new ArrayList<IAdaptable>(Arrays.asList(workingSet.getElements()));
boolean changed= false;
for (Iterator<IAdaptable> iter= elements.iterator(); iter.hasNext();) {
IAdaptable element= iter.next();
boolean remove= false;
if (element instanceof ICElement) {
ICElement cElement= (ICElement)element;
// If we have directly a project then remove it when it
// doesn't exist anymore. However if we have a sub element
// under a project only remove the element if the parent
// project is open. Otherwise we would remove all elements
// in closed projects.
if (cElement instanceof ICProject) {
remove= !cElement.exists();
} else {
IProject project= cElement.getCProject().getProject();
remove= project.isOpen() && !cElement.exists();
}
} else if (element instanceof IResource) {
IResource resource= (IResource)element;
// See comments above
if (resource instanceof IProject) {
remove= !resource.exists();
} else {
IProject project= resource.getProject();
remove= (project != null ? project.isOpen() : true) && !resource.exists();
}
} else if (element == null) {
// should not happen anyway, but who knows?
remove= true;
}
if (remove) {
iter.remove();
changed= true;
}
}
if (changed) {
workingSet.setElements(elements.toArray(new IAdaptable[elements.size()]));
}
}
}