blob: 6645bf2a08eba7c3ad5a4fc2cb55f43e3c930ba9 [file] [log] [blame]
package org.eclipse.cdt.internal.core.model;
/*
* (c) Copyright IBM Corp. 2000, 2001.
* All Rights Reserved.
*/
import org.eclipse.cdt.core.model.CModelException;
import org.eclipse.cdt.core.model.CoreModel;
import org.eclipse.cdt.core.model.IArchiveContainer;
import org.eclipse.cdt.core.model.IBinaryContainer;
import org.eclipse.cdt.core.model.ICElement;
import org.eclipse.cdt.core.model.ICElementDelta;
import org.eclipse.cdt.core.model.ICModel;
import org.eclipse.cdt.core.model.ICProject;
import org.eclipse.cdt.core.model.IParent;
import org.eclipse.cdt.internal.core.search.indexing.IndexManager;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.runtime.IPath;
/**
* This class is used by <code>CModelManager</code> to convert
* <code>IResourceDelta</code>s into <code>ICElementDelta</code>s.
* It also does some processing on the <code>CElement</code>s involved
* (e.g. closing them or updating classpaths).
*/
public class DeltaProcessor {
/**
* The <code>CElementDelta</code> corresponding to the <code>IResourceDelta</code> being translated.
*/
protected CElementDelta fCurrentDelta;
protected IndexManager indexManager = new IndexManager();
/* The C element that was last created (see createElement(IResource).
* This is used as a stack of C elements (using getParent() to pop it, and
* using the various get*(...) to push it. */
ICElement currentElement;
static final ICElementDelta[] NO_DELTA = new ICElementDelta[0];
public static boolean VERBOSE = false;
// Hold on the element bein renamed.
ICElement movedFromElement = null;
/**
* Creates the create corresponding to this resource.
* Returns null if none was found.
*/
protected ICElement createElement(IResource resource) {
CModelManager manager = CModelManager.getDefault();
if (resource == null)
return null;
ICElement celement = manager.create(resource);
if (celement == null) {
ICElement parent = manager.create(resource.getParent());
// Probably it was deleted, find it
if (parent instanceof IParent) {
ICElement[] children;
if ( CModelManager.getDefault().peekAtInfo(parent) != null ) {
children = ((CElement)parent).getElementInfo().getChildren();
for (int i = 0; i < children.length; i++) {
IResource res = children[i].getResource();
if (res != null && res.equals(resource)) {
celement = children[i];
break;
}
}
}
// BUG 36424:
// The Binary may only be visible in the BinaryContainers
if (celement == null) {
ICProject cproj = parent.getCProject();
if (cproj != null) {
IBinaryContainer bin = cproj.getBinaryContainer();
children = ((CElement)bin).getElementInfo().getChildren();
for (int i = 0; i < children.length; i++) {
IResource res = children[i].getResource();
if (res != null && res.equals(resource)) {
celement = children[i];
break;
}
}
}
}
// BUG 36424:
// The Archive may only be visible in the ArchiveContainers
if (celement == null) {
ICProject cproj = parent.getCProject();
if (cproj != null) {
IArchiveContainer bin = cproj.getArchiveContainer();
children = ((CElement)bin).getElementInfo().getChildren();
for (int i = 0; i < children.length; i++) {
IResource res = children[i].getResource();
if (res != null && res.equals(resource)) {
celement = children[i];
break;
}
}
}
}
}
}
return celement;
}
/**
* Creates the create corresponding to this resource.
* Returns null if none was found.
*/
protected ICElement createElement(IPath path) {
return CModelManager.getDefault().create(path);
}
/**
* Release the Element from the CModel hastable.
* Returns null if none was found.
*/
protected void releaseCElement(ICElement celement) {
CModelManager.getDefault().releaseCElement(celement);
}
/**
* Adds the given child handle to its parent's cache of children.
*/
protected void addToParentInfo(Openable child) {
Openable parent = (Openable) child.getParent();
if (parent != null && parent.isOpen()) {
CElementInfo info = (CElementInfo)parent.getElementInfo();
info.addChild(child);
}
}
/**
* Processing for an element that has been added:<ul>
* <li>If the element is a project, do nothing, and do not process
* children, as when a project is created it does not yet have any
* natures - specifically a java nature.
* <li>If the elemet is not a project, process it as added (see
* <code>basicElementAdded</code>.
* </ul>
*/
protected void elementAdded(ICElement element, IResourceDelta delta) {
if (element instanceof Openable) {
addToParentInfo((Openable)element);
}
if ((delta.getFlags() & IResourceDelta.MOVED_FROM) != 0) {
//ICElement movedFromElement = createElement(delta.getMovedFromPath());
if (movedFromElement == null)
movedFromElement = createElement(delta.getMovedFromPath());
fCurrentDelta.movedTo(element, movedFromElement);
movedFromElement = null;
} else {
fCurrentDelta.added(element);
}
}
/**
* Processing for the closing of an element - there are two cases:<ul>
* <li>when a project is closed (in the platform sense), the
* CModel reports this as if the CProject has been removed.
* <li>otherwise, the CModel reports this
* as a the element being closed (CHANGED + F_CLOSED).
* </ul>
* <p>In both cases, the children of the element are not processed. When
* a resource is closed, the platform reports all children as removed. This
* would effectively delete the classpath if we processed children.
*/
protected void elementClosed(ICElement element, IResourceDelta delta) {
if (element.getElementType() == ICElement.C_PROJECT) {
// treat project closing as removal
elementRemoved(element, delta);
} else {
fCurrentDelta.closed(element);
}
}
/**
* Processing for the opening of an element - there are two cases:<ul>
* <li>when a project is opened (in the platform sense), the
* CModel reports this as if the CProject has been added.
* <li>otherwise, the CModel reports this
* as a the element being opened (CHANGED + F_CLOSED).
* </ul>
*/
protected void elementOpened(ICElement element, IResourceDelta delta) {
if (element.getElementType() == ICElement.C_PROJECT) {
// treat project opening as addition
if (hasCNature(delta.getResource())) {
elementAdded(element, delta);
}
} else {
fCurrentDelta.opened(element);
}
}
/*
* Closes the given element, which removes it from the cache of open elements.
*/
private void close(Openable element) {
try {
element.close();
} catch (CModelException e) {
// do nothing
}
}
/**
* Generic processing for elements with changed contents:<ul>
* <li>The element is closed such that any subsequent accesses will re-open
* the element reflecting its new structure.
* <li>An entry is made in the delta reporting a content change (K_CHANGE with F_CONTENT flag set).
* </ul>
*/
protected void elementChanged(ICElement element, IResourceDelta delta) {
if (element instanceof Openable) {
close((Openable)element);
}
fCurrentDelta.changed(element, ICElementDelta.F_CONTENT);
}
/**
* Generic processing for a removed element:<ul>
* <li>Close the element, removing its structure from the cache
* <li>Remove the element from its parent's cache of children
* <li>Add a REMOVED entry in the delta
* </ul>
*/
protected void elementRemoved(ICElement element, IResourceDelta delta) {
if ((delta.getFlags() & IResourceDelta.MOVED_TO) != 0) {
IPath movedToPath = delta.getMovedToPath();
// create the moved to element
ICElement movedToElement = createElement(movedToPath);
if (movedToElement == null) {
// moved outside
fCurrentDelta.removed(element);
} else {
movedFromElement = element;
fCurrentDelta.movedFrom(element, movedToElement);
}
} else {
fCurrentDelta.removed(element);
}
releaseCElement(element);
}
/**
* Filters the generated <code>CElementDelta</code>s to remove those
* which should not be fired (because they don't represent a real change
* in the C Model).
*/
protected ICElementDelta[] filterRealDeltas(ICElementDelta[] deltas) {
int length = deltas.length;
ICElementDelta[] realDeltas = null;
int index = 0;
for (int i = 0; i < length; i++) {
CElementDelta delta = (CElementDelta)deltas[i];
if (delta == null) {
continue;
}
if (delta.getAffectedChildren().length > 0
|| delta.getKind() == ICElementDelta.ADDED
|| delta.getKind() == ICElementDelta.REMOVED
|| (delta.getFlags() & ICElementDelta.F_CLOSED) != 0
|| (delta.getFlags() & ICElementDelta.F_OPENED) != 0
|| delta.resourceDeltasCounter > 0) {
if (realDeltas == null) {
realDeltas = new ICElementDelta[length];
}
realDeltas[index++] = delta;
}
}
if (index > 0) {
ICElementDelta[] result = new ICElementDelta[index];
System.arraycopy(realDeltas, 0, result, 0, index);
return result;
} else {
return NO_DELTA;
}
}
/**
* Returns true if the given resource is contained in an open project
* with a java nature, otherwise false.
*/
protected boolean hasCNature(IResource resource) {
// ensure the project has a C nature (if open)
IProject project = resource.getProject();
if (project.isOpen()) {
return CoreModel.getDefault().hasCNature(project);
}
return false;
}
/**
* Converts a <code>IResourceDelta</code> rooted in a <code>Workspace</code> into
* the corresponding set of <code>ICElementDelta</code>, rooted in the
* relevant <code>CModel</code>s.
*/
public ICElementDelta[] processResourceDelta(IResourceDelta changes) {
try {
ICElement root = (ICModel)CModelManager.getDefault().getCModel();
/*
try {
changes.accept(new IResourceDeltaVisitor() {
public boolean visit(IResourceDelta delta) {
switch (delta.getKind()) {
case IResourceDelta.ADDED :
// handle added resource
System.out.print("ADDED ");
break;
case IResourceDelta.REMOVED :
// handle removed resource
System.out.print("REMOVED ");
break;
case IResourceDelta.CHANGED :
// handle changed resource
System.out.print("CHANGED ");
break;
}
System.out.println(delta.getResource());
return true;
}
});
} catch (CoreException e) {
}
*/
// get the workspace delta, and start processing there.
IResourceDelta[] deltas = changes.getAffectedChildren();
ICElementDelta[] translatedDeltas = new CElementDelta[deltas.length];
//System.out.println("delta.length: " + deltas.length);
for (int i = 0; i < deltas.length; i++) {
IResourceDelta delta = deltas[i];
fCurrentDelta = new CElementDelta(root);
traverseDelta(root, delta); // traverse delta
translatedDeltas[i] = fCurrentDelta;
}
return filterRealDeltas(translatedDeltas);
} finally {
}
}
/**
* Converts an <code>IResourceDelta</code> and its children into
* the corresponding <code>ICElementDelta</code>s.
* Return whether the delta corresponds to a resource on the classpath.
* If it is not a resource on the classpath, it will be added as a non-java
* resource by the sender of this method.
*/
protected void traverseDelta(ICElement parent, IResourceDelta delta) {
try {
ICElement current = updateCurrentDeltaAndIndex(delta);
if (current == null) {
nonCResourcesChanged(parent, delta);
} else {
parent = current;
}
} catch (CModelException e) {
}
IResourceDelta [] children = delta.getAffectedChildren();
for (int i = 0; i < children.length; i++) {
traverseDelta(parent, children[i]);
}
}
protected void nonCResourcesChanged(ICElement parent, IResourceDelta delta) {
CElementDelta elementDelta = fCurrentDelta.find(parent);
if (elementDelta == null) {
fCurrentDelta.changed(parent, ICElementDelta.F_CONTENT);
} else {
elementDelta.addResourceDelta(delta);
}
if (parent instanceof CContainer) {
// if info not created yet no need to null NonCResources...
if (CModelManager.getDefault().peekAtInfo(parent) != null) {
CElementInfo info = ((CContainer)parent).getElementInfo();
if (info instanceof CContainerInfo) {
((CContainerInfo)info).setNonCResources(null);
}
}
}
}
/*
* Update the current delta (ie. add/remove/change the given element) and update the
* correponding index.
* Returns whether the children of the given delta must be processed.
* @throws a CModelException if the delta doesn't correspond to a c element of the given type.
*/
private ICElement updateCurrentDeltaAndIndex(IResourceDelta delta) throws CModelException {
IResource resource = delta.getResource();
ICElement element = createElement(resource);
switch (delta.getKind()) {
case IResourceDelta.ADDED :
if (element != null) {
updateIndexAddResource(element, delta);
elementAdded(element, delta);
}
break;
case IResourceDelta.REMOVED :
if (element != null) {
updateIndexRemoveResource(element, delta);
elementRemoved(element, delta);
}
break;
case IResourceDelta.CHANGED :
int flags = delta.getFlags();
if ((flags & IResourceDelta.CONTENT) != 0) {
// content has changed
if (element != null) {
elementChanged(element, delta);
updateIndexAddResource(element, delta);
//check to see if any projects need to be reindexed
updateDependencies(element);
}
} else if (resource.getType() == IResource.PROJECT) {
if ((flags & IResourceDelta.OPEN) != 0) {
// project has been opened or closed
IProject res = (IProject)resource;
if (element != null) {
if (res.isOpen()) {
elementOpened(element, delta);
updateIndexAddResource(element, delta);
} else {
elementClosed(element, delta);
updateIndexRemoveResource(element, delta);
}
}
} else if ((flags & IResourceDelta.DESCRIPTION) != 0) {
if (element != null) {
elementAdded(element, delta);
}
} else if (element != null) {
elementChanged(element, delta);
}
} else if (element != null) {
elementChanged(element, delta);
}
break;
}
return element;
}
protected void updateIndexAddResource(ICElement element, IResourceDelta delta) {
if (indexManager == null)
return;
switch (element.getElementType()) {
case ICElement.C_PROJECT :
this.indexManager.indexAll(element.getCProject().getProject());
break;
case ICElement.C_UNIT:
IFile file = (IFile) delta.getResource();
IProject filesProject = file.getProject();
indexManager.addSource(file, filesProject.getFullPath());
break;
}
}
protected void updateIndexRemoveResource(ICElement element, IResourceDelta delta) {
if (indexManager == null)
return;
switch (element.getElementType()) {
case ICElement.C_PROJECT :
this.indexManager.removeIndexFamily(element.getCProject().getProject().getFullPath());
// NB: Discarding index jobs belonging to this project was done during PRE_DELETE
break;
// NB: Update of index if project is opened, closed, or its c nature is added or removed
// is done in updateCurrentDeltaAndIndex
case ICElement.C_UNIT:
IFile file = (IFile) delta.getResource();
indexManager.remove(file.getFullPath().toString(), file.getProject().getProject().getFullPath());
break;
}
}
private void updateDependencies(ICElement element){
IResource resource = element.getResource();
if (resource == null)
return;
String fileExtension = resource.getFileExtension();
if ((fileExtension != null) &&
(isValidHeader(fileExtension))){
indexManager.updateDependencies(resource);
}
}
private boolean isValidHeader(String fileExtension) {
String[] supportedTypes = CModelManager.headerExtensions;
for (int i = 0; i < supportedTypes.length; ++i) {
if (supportedTypes[i].equals(fileExtension))
return true;
}
return false;
}
}