blob: 080f830872bc7775aef1f73c751d2a34ca257249 [file] [log] [blame]
package org.eclipse.cdt.internal.core.model;
/*
* (c) Copyright IBM Corp. 2000, 2001.
* All Rights Reserved.
*/
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import org.eclipse.cdt.core.CCProjectNature;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.CProjectNature;
import org.eclipse.cdt.core.IBinaryParser;
import org.eclipse.cdt.core.IBinaryParser.IBinaryFile;
import org.eclipse.cdt.core.model.CModelException;
import org.eclipse.cdt.core.model.ElementChangedEvent;
import org.eclipse.cdt.core.model.IArchive;
import org.eclipse.cdt.core.model.IBinary;
import org.eclipse.cdt.core.model.ICContainer;
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.IElementChangedListener;
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.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
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.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
public class CModelManager implements IResourceChangeListener {
/**
* Unique handle onto the CModel
*/
final CModel cModel = new CModel();
public static HashSet OptionNames = new HashSet(20);
/**
* Used to convert <code>IResourceDelta</code>s into <code>ICElementDelta</code>s.
*/
protected DeltaProcessor fDeltaProcessor = new DeltaProcessor();
/**
* Queue of deltas created explicily by the C Model that
* have yet to be fired.
*/
private ArrayList fCModelDeltas = new ArrayList();
/**
* Turns delta firing on/off. By default it is on.
*/
protected boolean fFire = true;
/**
* Collection of listeners for C element deltas
*/
protected ArrayList fElementChangedListeners = new ArrayList();
/**
* A map from ITranslationUnit to IWorkingCopy of the shared working copies.
*/
public Map sharedWorkingCopies = new HashMap();
/**
* Set of elements which are out of sync with their buffers.
*/
protected Map elementsOutOfSynchWithBuffers = new HashMap(11);
/**
* Infos cache.
*/
protected CModelCache cache = new CModelCache();
/**
* This is a cache of the projects before any project addition/deletion has started.
*/
public ICProject[] cProjectsCache;
/**
* The list of started BinaryRunners on projects.
*/
private HashMap binaryRunners = new HashMap();
/**
* Map of the binary parser for each project.
*/
private HashMap binaryParsersMap = new HashMap();
/**
* The lis of the SourceMappers on projects.
*/
private HashMap sourceMappers = new HashMap();
public static final String [] sourceExtensions = {"c", "cxx", "cc", "C", "cpp"};
public static final String [] headerExtensions = {"h", "hh", "hpp", "H"};
public static final IWorkingCopy[] NoWorkingCopy = new IWorkingCopy[0];
static CModelManager factory = null;
private CModelManager() {
}
public static CModelManager getDefault() {
if (factory == null) {
factory = new CModelManager();
// Register to the workspace;
ResourcesPlugin.getWorkspace().addResourceChangeListener(factory,
IResourceChangeEvent.PRE_AUTO_BUILD
| IResourceChangeEvent.POST_CHANGE
| IResourceChangeEvent.PRE_DELETE
| IResourceChangeEvent.PRE_CLOSE);
}
return factory;
}
/**
* Returns the CModel for the given workspace, creating
* it if it does not yet exist.
*/
public ICModel getCModel(IWorkspaceRoot root) {
return getCModel();
//return create(root);
}
public ICModel getCModel() {
return cModel;
}
public ICElement create (IPath path) {
IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
// Assume it is fullpath relative to workspace
IResource res = root.findMember(path);
if (res == null) {
IPath rootPath = root.getLocation();
if (path.equals(rootPath))
return getCModel(root);
res = root.getContainerForLocation(path);
if (res == null || !res.exists())
res = root.getFileForLocation(path);
if (res != null && !res.exists())
res = null;
}
// TODO: for extenal resources ??
return create(res);
}
public ICElement create (IResource resource) {
if (resource == null) {
return null;
}
int type = resource.getType();
switch (type) {
case IResource.PROJECT :
return create((IProject)resource);
case IResource.FILE :
return create((IFile)resource);
case IResource.FOLDER :
return create((IFolder)resource);
case IResource.ROOT :
return create((IWorkspaceRoot)resource);
default :
return null;
}
}
public ICElement create(ICElement parent, IResource resource) {
int type = resource.getType();
switch (type) {
case IResource.PROJECT :
return create(parent, (IProject)resource);
case IResource.FILE :
return create(parent, (IFile)resource);
case IResource.FOLDER :
return create(parent, (IFolder)resource);
case IResource.ROOT :
return create((IWorkspaceRoot)resource);
default :
return null;
}
}
public ICElement create(IFile file) {
IResource parent = file.getParent();
ICElement cparent = null;
if (parent instanceof IFolder) {
cparent = create((IFolder)parent);
} else if (parent instanceof IProject) {
cparent = create((IProject)parent);
}
if (cparent != null)
return create(cparent, file);
return null;
}
public ICElement create(ICElement parent, IFile file) {
return create(parent, file, null);
}
public synchronized ICElement create(ICElement parent, IFile file, IBinaryFile bin) {
ICElement cfile = null;
if (isTranslationUnit(file)) {
cfile = new TranslationUnit(parent, file);
} else if (file.exists()) {
// Try to create the binaryFile first.
if (bin == null) {
bin = createBinaryFile(file);
}
if (bin != null) {
if (bin.getType() == IBinaryFile.ARCHIVE) {
cfile = new Archive(parent, file);
} else {
cfile = new Binary(parent, file, bin);
}
}
}
// Added also to the Containers
if (cfile != null && (cfile instanceof IBinary || cfile instanceof IArchive)) {
if (bin == null) {
bin = createBinaryFile(file);
}
if (bin != null) {
if (bin.getType() == IBinaryFile.ARCHIVE) {
CProject cproj = (CProject)cfile.getCProject();
ArchiveContainer container = (ArchiveContainer)cproj.getArchiveContainer();
container.addChild(cfile);
} else if (bin.getType() == IBinaryFile.EXECUTABLE || bin.getType() == IBinaryFile.SHARED) {
CProject cproj = (CProject)cfile.getCProject();
BinaryContainer container = (BinaryContainer)cproj.getBinaryContainer();
container.addChild(cfile);
}
}
}
return cfile;
}
public ICContainer create(IFolder folder) {
IResource parent = folder.getParent();
ICElement cparent = null;
if (parent instanceof IFolder) {
cparent = create ((IFolder)parent);
} else if (parent instanceof IProject) {
cparent = create ((IProject)parent);
}
if (cparent != null)
return (ICContainer) create (cparent, folder);
return null;
}
public ICContainer create(ICElement parent, IFolder folder) {
return new CContainer(parent, folder);
}
public ICProject create(IProject project) {
IResource parent = project.getParent();
ICElement celement = null;
if (parent instanceof IWorkspaceRoot) {
celement = create ((IWorkspaceRoot)parent);
}
return create(celement, project);
}
public ICProject create(ICElement parent, IProject project) {
if (hasCNature(project)){
return new CProject(parent, project);
}
return null;
}
public ICModel create(IWorkspaceRoot root) {
return getCModel();
//return new CModel(root);
}
public void releaseCElement(ICElement celement) {
// Guard.
if (celement == null)
return;
//System.out.println("RELEASE " + celement.getElementName());
// Remove from the containers.
int type = celement.getElementType();
if (type == ICElement.C_ARCHIVE) {
//System.out.println("RELEASE Archive " + cfile.getElementName());
CProject cproj = (CProject)celement.getCProject();
ArchiveContainer container = (ArchiveContainer)cproj.getArchiveContainer();
container.removeChild(celement);
} else if (type == ICElement.C_BINARY) {
if (! ((IBinary)celement).isObject()) {
//System.out.println("RELEASE Binary " + cfile.getElementName());
CProject cproj = (CProject)celement.getCProject();
BinaryContainer container = (BinaryContainer)cproj.getBinaryContainer();
container.removeChild(celement);
}
}
if (celement instanceof IParent) {
if ( peekAtInfo(celement) != null ) {
CElementInfo info = ((CElement)celement).getElementInfo();
if (info != null) {
ICElement[] children = info.getChildren();
for (int i = 0; i < children.length; i++) {
releaseCElement(children[i]);
}
// Make sure we destroy the BinaryContainer and ArchiveContainer
// Since they are not part of the children.
if (info instanceof CProjectInfo) {
CProjectInfo pinfo = (CProjectInfo) info;
if (pinfo.vBin != null) {
releaseCElement(pinfo.vBin);
}
if (pinfo.vLib != null) {
releaseCElement(pinfo.vLib);
}
}
}
} else {
ICProject cproject = celement.getCProject();
CProjectInfo info = (CProjectInfo)peekAtInfo(cproject);
if (info != null && info.vBin != null) {
if (peekAtInfo(info.vBin) != null) {
ICElement[] bins = info.getChildren();
for (int i = 0; i < bins.length; i++) {
if (celement.getPath().isPrefixOf(bins[i].getPath())) {
CElementDelta delta = new CElementDelta(getCModel());
delta.changed(info.vBin, ICElementDelta.CHANGED);
registerCModelDelta(delta);
info.vBin.removeChild(bins[i]);
}
}
}
}
if (info != null && info.vLib != null) {
if (peekAtInfo(info.vLib) != null) {
ICElement[] ars = info.vLib.getChildren();
for (int i = 0; i < ars.length; i++) {
if (celement.getPath().isPrefixOf(ars[i].getPath())) {
CElementDelta delta = new CElementDelta(getCModel());
delta.changed(info.vLib, ICElementDelta.CHANGED);
registerCModelDelta(delta);
info.vLib.removeChild(ars[i]);
}
}
}
}
}
}
// Remove the child from the parent list.
Parent parent = (Parent)celement.getParent();
if (parent != null) {
parent.removeChild(celement);
}
removeInfo(celement);
}
public IBinaryParser getBinaryParser(IProject project) {
try {
IBinaryParser parser = (IBinaryParser)binaryParsersMap.get(project);
if (parser == null) {
parser = CCorePlugin.getDefault().getBinaryParser(project);
}
if (parser != null) {
binaryParsersMap.put(project, parser);
return parser;
}
} catch (CoreException e) {
}
return new NullBinaryParser();
}
public IBinaryFile createBinaryFile(IFile file) {
try {
IBinaryParser parser = getBinaryParser(file.getProject());
InputStream is = file.getContents();
byte[] bytes = new byte[128];
int count = is.read(bytes);
is.close();
if (count > 0 && count < bytes.length) {
byte[] array = new byte[count];
System.arraycopy(bytes, 0, array, 0, count);
bytes = array;
}
IPath location = file.getLocation();
if (parser.isBinary(bytes, location)) {
return parser.getBinary(location);
}
} catch (IOException e) {
} catch (CoreException e) {
//e.printStackTrace();
}
return null;
}
/**
* TODO: this is a temporary hack until, the CDescriptor manager is
* in place and could fire deltas of Parser change.
*/
public void resetBinaryParser(IProject project) {
if (project != null) {
ICElement celement = create(project);
if (celement != null) {
// Let the function remove the children
// but it has the side of effect of removing the CProject also
// so we have to recall create again.
releaseCElement(celement);
binaryParsersMap.remove(project);
celement = create(project);
Parent parent = (Parent)celement.getParent();
CElementInfo info = (CElementInfo)parent.getElementInfo();
info.addChild(celement);
// Fired and ICElementDelta.PARSER_CHANGED
CElementDelta delta = new CElementDelta(getCModel());
delta.binaryParserChanged(celement);
registerCModelDelta(delta);
fire();
}
}
}
public boolean isSharedLib(IFile file) {
try {
IBinaryParser parser = getBinaryParser(file.getProject());
IBinaryFile bin = parser.getBinary(file.getLocation());
return (bin.getType() == IBinaryFile.SHARED);
} catch (IOException e) {
}
return false;
}
public boolean isObject(IFile file) {
try {
IBinaryParser parser = getBinaryParser(file.getProject());
IBinaryFile bin = parser.getBinary(file.getLocation());
return (bin.getType() == IBinaryFile.OBJECT);
} catch (IOException e) {
}
return false;
}
public boolean isExecutable(IFile file) {
try {
IBinaryParser parser = getBinaryParser(file.getProject());
IBinaryFile bin = parser.getBinary(file.getLocation());
return (bin.getType() == IBinaryFile.EXECUTABLE);
} catch (IOException e) {
//e.printStackTrace();
}
return false;
}
public boolean isBinary(IFile file) {
try {
IBinaryParser parser = getBinaryParser(file.getProject());
IBinaryFile bin = parser.getBinary(file.getLocation());
return (bin.getType() == IBinaryFile.EXECUTABLE
|| bin.getType() == IBinaryFile.OBJECT
|| bin.getType() == IBinaryFile.SHARED
|| bin.getType() == IBinaryFile.CORE);
} catch (IOException e) {
}
return false;
}
public boolean isArchive(IFile file) {
try {
IBinaryParser parser = getBinaryParser(file.getProject());
IBinaryFile bin = parser.getBinary(file.getLocation());
return (bin.getType() == IBinaryFile.ARCHIVE);
} catch (IOException e) {
}
return false;
}
public boolean isTranslationUnit(IFile file) {
return isValidTranslationUnitName(file.getName());
}
public boolean isValidTranslationUnitName(String name){
if (name == null) {
return false;
}
int index = name.lastIndexOf('.');
if (index == -1) {
return false;
}
String ext = name.substring(index + 1);
String[] cexts = getTranslationUnitExtensions();
for (int i = 0; i < cexts.length; i++) {
if (ext.equals(cexts[i]))
return true;
}
return false;
}
public String[] getHeaderExtensions() {
return headerExtensions;
}
public String[] getSourceExtensions() {
return sourceExtensions;
}
public String[] getTranslationUnitExtensions() {
String[] headers = getHeaderExtensions();
String[] sources = getSourceExtensions();
String[] cexts = new String[headers.length + sources.length];
System.arraycopy(sources, 0, cexts, 0, sources.length);
System.arraycopy(headers, 0, cexts, sources.length, headers.length);
return cexts;
}
/* Only project with C nature and Open. */
public boolean hasCNature (IProject p) {
boolean ok = false;
try {
ok = (p.isOpen() && p.hasNature(CProjectNature.C_NATURE_ID));
} catch (CoreException e) {
//throws exception if the project is not open.
//System.out.println (e);
//e.printStackTrace();
}
return ok;
}
/* Only project with C++ nature and Open. */
public boolean hasCCNature (IProject p) {
boolean ok = false;
try {
ok = (p.isOpen() && p.hasNature(CCProjectNature.CC_NATURE_ID));
} catch (CoreException e) {
//throws exception if the project is not open.
//System.out.println (e);
//e.printStackTrace();
}
return ok;
}
public BinaryRunner getBinaryRunner(ICProject project) {
BinaryRunner runner = null;
synchronized(binaryRunners) {
runner = (BinaryRunner)binaryRunners.get(project.getProject());
if (runner == null) {
runner = new BinaryRunner(project.getProject());
binaryRunners.put(project.getProject(), runner);
runner.start();
}
}
return runner;
}
public SourceMapper getSourceMapper(ICProject cProject) {
SourceMapper mapper = null;
synchronized(sourceMappers) {
mapper = (SourceMapper) sourceMappers.get(cProject);
if (mapper == null) {
mapper = new SourceMapper(cProject);
sourceMappers.put(cProject, mapper);
}
}
return mapper;
}
/**
* addElementChangedListener method comment.
*/
public synchronized void addElementChangedListener(IElementChangedListener listener) {
if (fElementChangedListeners.indexOf(listener) < 0) {
fElementChangedListeners.add(listener);
}
}
/**
* removeElementChangedListener method comment.
*/
public synchronized void removeElementChangedListener(IElementChangedListener listener) {
int i = fElementChangedListeners.indexOf(listener);
if (i != -1) {
fElementChangedListeners.remove(i);
}
}
/**
* Registers the given delta with this manager. This API is to be
* used to registerd deltas that are created explicitly by the C
* Model. Deltas created as translations of <code>IResourceDeltas</code>
* are to be registered with <code>#registerResourceDelta</code>.
*/
public synchronized void registerCModelDelta(ICElementDelta delta) {
fCModelDeltas.add(delta);
}
/**
* Notifies this C Model Manager that some resource changes have happened
* on the platform, and that the C Model should update any required
* internal structures such that its elements remain consistent.
* Translates <code>IResourceDeltas</code> into <code>ICElementDeltas</code>.
*
* @see IResourceDelta
* @see IResource
*/
public void resourceChanged(IResourceChangeEvent event) {
if (event.getSource() instanceof IWorkspace) {
IResourceDelta delta = event.getDelta();
IResource resource = event.getResource();
switch(event.getType()){
case IResourceChangeEvent.PRE_DELETE :
try{
if (resource.getType() == IResource.PROJECT &&
( ((IProject)resource).hasNature(CProjectNature.C_NATURE_ID) ||
((IProject)resource).hasNature(CCProjectNature.CC_NATURE_ID) )){
this.deleting((IProject) resource);}
}catch (CoreException e){
}
break;
case IResourceChangeEvent.PRE_AUTO_BUILD :
// No need now.
if(delta != null) {
this.checkProjectsBeingAddedOrRemoved(delta);
}
break;
case IResourceChangeEvent.POST_CHANGE :
try {
if (delta != null) {
ICElementDelta[] translatedDeltas = fDeltaProcessor.processResourceDelta(delta);
if (translatedDeltas.length > 0) {
for (int i= 0; i < translatedDeltas.length; i++) {
registerCModelDelta(translatedDeltas[i]);
}
}
fire();
}
} catch (Exception e) {
e.printStackTrace();
}
break;
}
}
}
/**
* Fire C Model deltas, flushing them after the fact.
* If the firing mode has been turned off, this has no effect.
*/
public synchronized void fire() {
if (fFire) {
mergeDeltas();
try {
Iterator iterator = fCModelDeltas.iterator();
while (iterator.hasNext()) {
ICElementDelta delta= (ICElementDelta) iterator.next();
// Refresh internal scopes
ElementChangedEvent event= new ElementChangedEvent(delta);
// Clone the listeners since they could remove themselves when told about the event
// (eg. a type hierarchy becomes invalid (and thus it removes itself) when the type is removed
ArrayList listeners= (ArrayList) fElementChangedListeners.clone();
for (int i= 0; i < listeners.size(); i++) {
IElementChangedListener listener= (IElementChangedListener) listeners.get(i);
listener.elementChanged(event);
}
}
} finally {
// empty the queue
this.flush();
}
}
}
/**
* Flushes all deltas without firing them.
*/
protected synchronized void flush() {
fCModelDeltas= new ArrayList();
}
/**
* Merged all awaiting deltas.
*/
private void mergeDeltas() {
if (fCModelDeltas.size() <= 1)
return;
Iterator deltas = fCModelDeltas.iterator();
ICElement cRoot = getCModel();
CElementDelta rootDelta = new CElementDelta(cRoot);
boolean insertedTree = false;
while (deltas.hasNext()) {
CElementDelta delta = (CElementDelta)deltas.next();
ICElement element = delta.getElement();
if (cRoot.equals(element)) {
ICElementDelta[] children = delta.getAffectedChildren();
for (int j = 0; j < children.length; j++) {
CElementDelta projectDelta = (CElementDelta) children[j];
rootDelta.insertDeltaTree(projectDelta.getElement(), projectDelta);
insertedTree = true;
}
} else {
rootDelta.insertDeltaTree(element, delta);
insertedTree = true;
}
}
if (insertedTree) {
fCModelDeltas = new ArrayList(1);
fCModelDeltas.add(rootDelta);
} else {
fCModelDeltas = new ArrayList(0);
}
}
/**
* Runs a C Model Operation
*/
public void runOperation(CModelOperation operation, IProgressMonitor monitor) throws CModelException {
boolean hadAwaitingDeltas = !fCModelDeltas.isEmpty();
try {
if (operation.isReadOnly()) {
operation.run(monitor);
} else {
// use IWorkspace.run(...) to ensure that a build will be done in autobuild mode
getCModel().getUnderlyingResource().getWorkspace().run(operation, monitor);
}
} catch (CoreException ce) {
if (ce instanceof CModelException) {
throw (CModelException)ce;
} else {
if (ce.getStatus().getCode() == IResourceStatus.OPERATION_FAILED) {
Throwable e= ce.getStatus().getException();
if (e instanceof CModelException) {
throw (CModelException) e;
}
}
throw new CModelException(ce);
}
} finally {
// fire only if there were no awaiting deltas (if there were, they would come from a resource modifying operation)
// and the operation has not modified any resource
if (!hadAwaitingDeltas && !operation.hasModifiedResource()) {
fire();
} // else deltas are fired while processing the resource delta
}
}
/**
* Process the given delta and look for projects being added, opened,
* or closed
*/
public void checkProjectsBeingAddedOrRemoved(IResourceDelta delta) {
IResource resource = delta.getResource();
switch (resource.getType()) {
case IResource.ROOT :
if (this.cProjectsCache == null) {
this.cProjectsCache = this.getCModel().getCProjects();
}
IResourceDelta[] children = delta.getAffectedChildren();
for (int i = 0, length = children.length; i < length; i++) {
this.checkProjectsBeingAddedOrRemoved(children[i]);
}
break;
case IResource.PROJECT :
if (0 != (delta.getFlags() & IResourceDelta.OPEN)) {
IProject project = (IProject) resource;
if (!project.isOpen()) {
// project closing... stop the runner.
BinaryRunner runner = (BinaryRunner)binaryRunners.get(project);
if (runner != null ) {
runner.stop();
}
} else {
if ( binaryRunners.get(project) == null ) {
// project opening... lets add the runner to the
// map but no need to start it since the deltas
// will populate containers
binaryRunners.put(project, new BinaryRunner(project));
}
}
} else if (0 != (delta.getFlags() & IResourceDelta.REMOVED)) {
IProject project = (IProject) resource;
BinaryRunner runner = (BinaryRunner) binaryRunners.remove(project);
if (runner != null) {
runner.stop();
}
binaryParsersMap.remove(project);
}
break;
}
}
/**
* Returns the set of elements which are out of synch with their buffers.
*/
protected Map getElementsOutOfSynchWithBuffers() {
return this.elementsOutOfSynchWithBuffers;
}
/**
* Returns the info for the element.
*/
public Object getInfo(ICElement element) {
return this.cache.getInfo(element);
}
/**
* Returns the info for this element without
* disturbing the cache ordering.
*/
protected Object peekAtInfo(ICElement element) {
return this.cache.peekAtInfo(element);
}
/**
* Puts the info for a C Model Element
*/
protected void putInfo(ICElement element, Object info) {
this.cache.putInfo(element, info);
}
/**
* Removes the info of this model element.
*/
protected void removeInfo(ICElement element) {
this.cache.removeInfo(element);
}
/**
*
*/
public void startup() {
// Do any initialization.
}
/**
*
*/
public void shutdown() {
if (this.fDeltaProcessor.indexManager != null){ // no more indexing
this.fDeltaProcessor.indexManager.shutdown();
}
// Do any shutdown of services.
ResourcesPlugin.getWorkspace().removeResourceChangeListener(factory);
BinaryRunner[] runners = (BinaryRunner[])binaryRunners.values().toArray(new BinaryRunner[0]);
for (int i = 0; i < runners.length; i++) {
runners[i].stop();
}
}
public IndexManager getIndexManager() {
return this.fDeltaProcessor.indexManager;
}
public void deleting(IProject project){
// discard all indexing jobs for this project
this.getIndexManager().discardJobs(project.getName());
}
}