/******************************************************************************* * Copyright (c) 2000, 2010 QNX Software Systems 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: * QNX Software Systems - Initial API and implementation * James Blackburn - Modified patch for 149428 * Markus Schorn (Wind River Systems) * Anton Leherbauer (Wind River Systems) * Warren Paul (Nokia) * IBM Corporation (EFS Support) *******************************************************************************/ package org.eclipse.cdt.internal.core.model; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; 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.IBinaryArchive; import org.eclipse.cdt.core.IBinaryParser.IBinaryFile; import org.eclipse.cdt.core.IBinaryParser.IBinaryObject; import org.eclipse.cdt.core.model.CModelException; import org.eclipse.cdt.core.model.CoreModel; import org.eclipse.cdt.core.model.ElementChangedEvent; 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.IIncludeReference; import org.eclipse.cdt.core.model.IParent; import org.eclipse.cdt.core.model.IProblemRequestor; import org.eclipse.cdt.core.model.ISourceRoot; import org.eclipse.cdt.core.model.ITranslationUnit; import org.eclipse.cdt.core.model.IWorkingCopy; import org.eclipse.cdt.core.settings.model.CProjectDescriptionEvent; import org.eclipse.cdt.core.settings.model.ICConfigExtensionReference; import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; import org.eclipse.cdt.core.settings.model.ICDescriptionDelta; import org.eclipse.cdt.core.settings.model.ICProjectDescription; import org.eclipse.cdt.core.settings.model.ICProjectDescriptionListener; import org.eclipse.cdt.core.settings.model.ICSettingObject; import org.eclipse.cdt.internal.core.CCoreInternals; import org.eclipse.cdt.internal.core.LocalProjectScope; import org.eclipse.cdt.internal.core.resources.ResourceLookup; import org.eclipse.cdt.internal.core.settings.model.CProjectDescription; import org.eclipse.cdt.internal.core.settings.model.CProjectDescriptionManager; import org.eclipse.core.filesystem.EFS; import org.eclipse.core.filesystem.IFileInfo; import org.eclipse.core.filesystem.IFileStore; import org.eclipse.core.filesystem.URIUtil; 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.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; import org.eclipse.core.runtime.ISafeRunnable; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.SafeRunner; import org.eclipse.core.runtime.content.IContentType; import org.eclipse.core.runtime.content.IContentTypeManager; import org.eclipse.core.runtime.content.IContentTypeManager.ContentTypeChangeEvent; import org.eclipse.core.runtime.content.IContentTypeManager.IContentTypeChangeListener; public class CModelManager implements IResourceChangeListener, IContentTypeChangeListener, ICProjectDescriptionListener { public static boolean VERBOSE = false; /** * Unique handle onto the CModel */ final CModel cModel = new CModel(); public static HashSet<String> OptionNames = new HashSet<String>(20); public static final int DEFAULT_CHANGE_EVENT = 0; // must not collide with ElementChangedEvent event masks /** * Used to convert <code>IResourceDelta</code>s into <code>ICElementDelta</code>s. */ protected final DeltaProcessor fDeltaProcessor = new DeltaProcessor(); /** * Queue of deltas created explicitly by the C Model that * have yet to be fired. */ List<ICElementDelta> fCModelDeltas = Collections.synchronizedList(new ArrayList<ICElementDelta>()); /** * Queue of reconcile deltas on working copies that have yet to be fired. * This is a table form IWorkingCopy to ICElementDelta */ HashMap<IWorkingCopy, ICElementDelta> reconcileDeltas = new HashMap<IWorkingCopy, ICElementDelta>(); /** * Turns delta firing on/off. By default it is on. */ protected boolean fFire = true; /** * Collection of listeners for C element deltas */ protected List<IElementChangedListener> fElementChangedListeners = Collections.synchronizedList(new ArrayList<IElementChangedListener>()); /** * A map from ITranslationUnit to IWorkingCopy of the shared working copies. */ private Map<IBufferFactory, Map<ITranslationUnit, WorkingCopy>> sharedWorkingCopies = new HashMap<IBufferFactory, Map<ITranslationUnit, WorkingCopy>>(); /** * Set of elements which are out of sync with their buffers. */ protected Map<ICElement,ICElement> elementsOutOfSynchWithBuffers = new HashMap<ICElement, ICElement>(11); /* * Temporary cache of newly opened elements */ private ThreadLocal<Map<ICElement, CElementInfo>> temporaryCache = new ThreadLocal<Map<ICElement, CElementInfo>>(); /** * 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 final Map<IProject, BinaryRunner> binaryRunners = new HashMap<IProject, BinaryRunner>(); /** * Map of the binary parser for each project. */ private final Map<IProject, BinaryParserConfig[]> binaryParsersMap = Collections.synchronizedMap(new HashMap<IProject, BinaryParserConfig[]>()); /** * The lis of the SourceMappers on projects. */ private HashMap<ICProject, SourceMapper> sourceMappers = new HashMap<ICProject, SourceMapper>(); public static final IWorkingCopy[] NoWorkingCopy = new IWorkingCopy[0]; static volatile CModelManager factory = null; private CModelManager() { } public static CModelManager getDefault() { if (factory == null) { synchronized (CModelManager.class) { if (factory != null) return factory; factory = new CModelManager(); // Register to the workspace; ResourcesPlugin.getWorkspace().addResourceChangeListener(factory, IResourceChangeEvent.POST_CHANGE | IResourceChangeEvent.PRE_DELETE | IResourceChangeEvent.PRE_CLOSE); // Register the Core Model on the Descriptor // Manager, it needs to know about changes. // CCorePlugin.getDefault().getCDescriptorManager().addDescriptorListener(factory); // Register as project description listener CProjectDescriptionManager.getInstance().addCProjectDescriptionListener(factory, CProjectDescriptionEvent.APPLIED); // Register the Core Model on the ContentTypeManager // it needs to know about changes. Platform.getContentTypeManager().addContentTypeChangeListener(factory); } } return factory; } /** * Returns the CModel for the given workspace, creating * it if it does not yet exist. */ public ICModel getCModel(IWorkspaceRoot root) { return getCModel(); } public CModel 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; } } // In case this is an external resource see if we can find // a file for it. if (res == null) { res= ResourceLookup.selectFileForLocation(path, null); } return create(res, null); } public ICElement create(IResource resource, ICProject cproject) { 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, cproject); case IResource.FOLDER: return create((IFolder)resource, cproject); case IResource.ROOT: return getCModel((IWorkspaceRoot)resource); default: return null; } } public ICProject create(IProject project) { if (project == null) { return null; } return cModel.getCProject(project); } public ICContainer create(IFolder folder, ICProject cproject) { if (folder == null) { return null; } if (cproject == null) { cproject = create(folder.getProject()); } try { ICElement[] children = cproject.getChildren(); for (int i = 0; i < children.length; ++i) { if (children[i] instanceof ISourceRoot) { ISourceRoot root = (ISourceRoot)children[i]; if (root.isOnSourceEntry(folder)) { // Get the container IPath path = folder.getFullPath(); path = path.removeFirstSegments(root.getPath().segmentCount()); String[] segments = path.segments(); ICContainer cfolder = root; for (int j = 0; j < segments.length; ++j) { cfolder = cfolder.getCContainer(segments[j]); } return cfolder; } } else if (children[i] instanceof ICContainer) { ICContainer root = (ICContainer)children[i]; IPath rootPath = root.getPath(); IPath path = folder.getFullPath(); if (rootPath.isPrefixOf(path) && cproject.isOnOutputEntry(folder)) { path = path.removeFirstSegments(root.getPath().segmentCount()); String[] segments = path.segments(); ICContainer cfolder = root; for (int j = 0; j < segments.length; ++j) { cfolder = cfolder.getCContainer(segments[j]); } return cfolder; } } } } catch (CModelException e) { // } return null; } public ICElement create(IFile file, ICProject cproject) { if (file == null) { return null; } if (cproject == null) { cproject = create(file.getProject()); } ICElement celement = null; try { // First look for TU's IPath resourcePath = file.getFullPath(); ISourceRoot[] roots = cproject.getSourceRoots(); for (int i = 0; i < roots.length; ++i) { ISourceRoot root = roots[i]; if (root.isOnSourceEntry(file)) { IPath rootPath = root.getPath(); IPath path = resourcePath.removeFirstSegments(rootPath.segmentCount()); String fileName = path.lastSegment(); path = path.removeLastSegments(1); String[] segments = path.segments(); ICContainer cfolder = root; for (String segment : segments) { cfolder = cfolder.getCContainer(segment); } if (CoreModel.isValidTranslationUnitName(cproject.getProject(), fileName)) { celement = cfolder.getTranslationUnit(fileName); } break; } } // check for binary on output entry if (celement == null && cproject.isOnOutputEntry(file)) { IBinaryFile bin = createBinaryFile(file); if (bin != null) celement = create(file, bin, cproject); } } catch (CModelException e) { // } return celement; } public ICElement create(IFile file, IBinaryFile bin, ICProject cproject) { if (file == null) { return null; } if (bin == null) { return create(file, cproject); } if (cproject == null) { cproject = create(file.getProject()); } ICElement celement = null; try { if (cproject.isOnOutputEntry(file)) { IPath resourcePath = file.getParent().getFullPath(); ICElement cfolder = cproject.findElement(resourcePath); // Check if folder is a source root and use that instead ISourceRoot sourceRoot = cproject.findSourceRoot(resourcePath); if (sourceRoot != null) cfolder = sourceRoot; if (bin.getType() == IBinaryFile.ARCHIVE) { ArchiveContainer vlib = (ArchiveContainer)cproject.getArchiveContainer(); celement = new Archive(cfolder, file, (IBinaryArchive)bin); vlib.addChild(celement); } else { BinaryContainer vbin = (BinaryContainer)cproject.getBinaryContainer(); celement = new Binary(cfolder, file, (IBinaryObject)bin); vbin.addChild(celement); } } } catch (CModelException e) { // } return celement; } public ITranslationUnit createTranslationUnitFrom(ICProject cproject, IPath path) { if (path == null || cproject == null) { return null; } final IProject project= cproject.getProject(); final String contentTypeId = CoreModel.getRegistedContentTypeId(project, path.lastSegment()); if (path.isAbsolute()) { if (!Util.isNonZeroLengthFile(path)) { return null; } try { IIncludeReference[] includeReferences = cproject.getIncludeReferences(); for (IIncludeReference includeReference : includeReferences) { if (includeReference.isOnIncludeEntry(path)) { String headerContentTypeId= contentTypeId; if (headerContentTypeId == null) { headerContentTypeId= CoreModel.hasCCNature(project) ? CCorePlugin.CONTENT_TYPE_CXXHEADER : CCorePlugin.CONTENT_TYPE_CHEADER; } // TODO: use URI return new ExternalTranslationUnit(includeReference, URIUtil.toURI(path), headerContentTypeId); } } } catch (CModelException e) { } // if the file exists and it has a known C/C++ file extension then just create // an external translation unit for it. if (contentTypeId != null && path.toFile().exists()) { // TODO: use URI return new ExternalTranslationUnit(cproject, URIUtil.toURI(path), contentTypeId); } } else { // !path.isAbsolute() try { IIncludeReference[] includeReferences = cproject.getIncludeReferences(); for (IIncludeReference includeReference : includeReferences) { IPath includePath = includeReference.getPath().append(path); if (Util.isNonZeroLengthFile(includePath)) { String headerContentTypeId= contentTypeId; if (headerContentTypeId == null) { headerContentTypeId= CoreModel.hasCCNature(project) ? CCorePlugin.CONTENT_TYPE_CXXHEADER : CCorePlugin.CONTENT_TYPE_CHEADER; } // TODO: use URI return new ExternalTranslationUnit(includeReference, URIUtil.toURI(includePath), headerContentTypeId); } } } catch (CModelException e) { } } return null; } /** * Creates a translation unit in the given project for the given location. * * @param cproject * @param locationURI * @return ITranslationUnit */ public ITranslationUnit createTranslationUnitFrom(ICProject cproject, URI locationURI) { if (locationURI == null || cproject == null) { return null; } if(!locationURI.isAbsolute()) { throw new IllegalArgumentException(); } final IProject project= cproject.getProject(); IFileStore fileStore = null; try { fileStore = EFS.getStore(locationURI); } catch (CoreException e1) { CCorePlugin.log(e1); return null; } final String contentTypeId = CoreModel.getRegistedContentTypeId(project, fileStore.getName()); if (!Util.isNonZeroLengthFile(locationURI)) { return null; } try { IIncludeReference[] includeReferences = cproject.getIncludeReferences(); for (IIncludeReference includeReference : includeReferences) { // crecoskie // TODO FIXME: include entries don't handle URIs yet IPath path = URIUtil.toPath(locationURI); if (path != null && includeReference.isOnIncludeEntry(path)) { String headerContentTypeId= contentTypeId; if (headerContentTypeId == null) { headerContentTypeId= CoreModel.hasCCNature(project) ? CCorePlugin.CONTENT_TYPE_CXXHEADER : CCorePlugin.CONTENT_TYPE_CHEADER; } return new ExternalTranslationUnit(includeReference, locationURI, headerContentTypeId); } } } catch (CModelException e) { } // if the file exists and it has a known C/C++ file extension then just create // an external translation unit for it. IFileInfo info = fileStore.fetchInfo(); if (contentTypeId != null && info != null && info.exists()) { return new ExternalTranslationUnit(cproject, locationURI, contentTypeId); } return null; } public void releaseCElement(ICElement celement) { // Guard. if (celement == null) return; //System.out.println("RELEASE " + celement.getElementName()); // Remove from the containers. if (celement instanceof IParent) { CElementInfo info = (CElementInfo)peekAtInfo(celement); if (info != null) { ICElement[] children = info.getChildren(); for (ICElement element : children) { releaseCElement(element); } } // Make sure any object specifics not part of the children be destroy // For example the CProject needs to destroy the BinaryContainer and ArchiveContainer if (celement instanceof CElement) { try { ((CElement)celement).closing(info); } catch (CModelException e) { // } } // If an entire folder was deleted we need to update the // BinaryContainer/ArchiveContainer also. if (celement.getElementType() == ICElement.C_CCONTAINER) { ICProject cproject = celement.getCProject(); CProjectInfo pinfo = (CProjectInfo)peekAtInfo(cproject); ArrayList<ICElement> list = new ArrayList<ICElement>(5); if (pinfo != null && pinfo.vBin != null) { if (peekAtInfo(pinfo.vBin) != null) { try { ICElement[] bins = pinfo.vBin.getChildren(); for (ICElement bin : bins) { if (celement.getPath().isPrefixOf(bin.getPath())) { //pinfo.vBin.removeChild(bins[i]); list.add(bin); } } } catch (CModelException e) { // .. } } } if (pinfo != null && pinfo.vLib != null) { if (peekAtInfo(pinfo.vLib) != null) { try { ICElement[] ars = pinfo.vLib.getChildren(); for (ICElement ar : ars) { if (celement.getPath().isPrefixOf(ar.getPath())) { //pinfo.vLib.removeChild(ars[i]); list.add(ar); } } } catch (CModelException e) { // .. } } } // release any binary/archive that was in the path for (int i = 0; i < list.size(); i++) { ICElement b = list.get(i); releaseCElement(b); } } } // Remove the child from the parent list. //Parent parent = (Parent)celement.getParent(); //if (parent != null && peekAtInfo(parent) != null) { // parent.removeChild(celement); //} removeInfo(celement); } public BinaryParserConfig[] getBinaryParser(IProject project) { BinaryParserConfig[] parsers = binaryParsersMap.get(project); if (parsers == null) { ICProjectDescription desc = CCorePlugin.getDefault().getProjectDescription(project, false); if (desc != null) { ICConfigurationDescription cfgDesc = desc.getDefaultSettingConfiguration(); if (cfgDesc != null) { ICConfigExtensionReference[] refs = cfgDesc.get(CCorePlugin.BINARY_PARSER_UNIQ_ID); if (refs.length > 0) { ArrayList<BinaryParserConfig> list = new ArrayList<BinaryParserConfig>(refs.length); for (ICConfigExtensionReference ref : refs) { BinaryParserConfig config = new BinaryParserConfig(ref); list.add(config); } parsers = new BinaryParserConfig[list.size()]; list.toArray(parsers); } else { // no parser configured parsers = new BinaryParserConfig[0]; } } } if (parsers == null) { try { BinaryParserConfig config = new BinaryParserConfig(CCorePlugin.getDefault().getDefaultBinaryParser(), CCorePlugin.DEFAULT_BINARY_PARSER_UNIQ_ID); parsers = new BinaryParserConfig[]{config}; } catch (CoreException e1) { } } } if (parsers != null) { binaryParsersMap.put(project, parsers); return parsers; } return new BinaryParserConfig[0]; } public IBinaryFile createBinaryFile(IFile file) { BinaryParserConfig[] parsers = getBinaryParser(file.getProject()); if (parsers.length == 0) { return null; } // Only if file has no extension, has an extension that is an integer // or is a binary file content type String ext = file.getFileExtension(); if (ext != null && ext.length() > 0) { // shared libraries often have a version number // strip version extension: libc.so.3.2.1 -> libc.so IPath baseFileName = new Path(file.getName()); outer: do { for (int i = 0; i < ext.length(); ++i) { if (!Character.isDigit(ext.charAt(i))) { break outer; } } // extension is a number -> remove it baseFileName = baseFileName.removeFileExtension(); ext = baseFileName.getFileExtension(); } while (ext != null && ext.length() > 0); boolean isBinary= false; final IContentTypeManager ctm = Platform.getContentTypeManager(); final IContentType ctbin = ctm.getContentType(CCorePlugin.CONTENT_TYPE_BINARYFILE); final IContentType[] cts= ctm.findContentTypesFor(baseFileName.toString()); for (int i=0; !isBinary && i < cts.length; i++) { isBinary= cts[i].isKindOf(ctbin); } if (!isBinary) { return null; } } URI fileUri = file.getLocationURI(); //Avoid name special devices, empty files and the like if (!Util.isNonZeroLengthFile(fileUri)) { // PR:xxx the EFS does not seem to work for newly created file // so before bailing out give another try? //Avoid name special devices, empty files and the like if("file".equals(fileUri.getScheme())) { //$NON-NLS-1$ File f = new File(fileUri); if (f.length() == 0) { return null; } } //return null; } int hints = 0; for (BinaryParserConfig parser2 : parsers) { IBinaryParser parser = null; try { parser = parser2.getBinaryParser(); if (parser.getHintBufferSize() > hints) { hints = Math.max(hints, parser.getHintBufferSize()); } } catch (CoreException e) { } } byte[] bytes = new byte[hints]; if (hints > 0) { InputStream is = null; try { is = file.getContents(); int count = 0; // Make sure we read up to 'hints' bytes if we possibly can while (count < hints) { int bytesRead = is.read(bytes, count, hints - count); if (bytesRead < 0) break; count += bytesRead; } if (count > 0 && count < bytes.length) { byte[] array = new byte[count]; System.arraycopy(bytes, 0, array, 0, count); bytes = array; } } catch (CoreException e) { return null; } catch (IOException e) { return null; } finally { if (is != null) { try { is.close(); } catch (IOException e) { // ignore } } } } IPath location = file.getLocation(); for (BinaryParserConfig parser2 : parsers) { try { IBinaryParser parser = parser2.getBinaryParser(); if (parser.isBinary(bytes, location)) { IBinaryFile binFile = parser.getBinary(bytes, location); if (binFile != null) { return binFile; } } } catch (IOException e) { } catch (CoreException e) { } } return null; } public void resetBinaryParser(IProject project) { if (project != null) { ICProject cproject = create(project); if (cproject != 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. try { cproject.close(); } catch (CModelException e) { CCorePlugin.log(e); } binaryParsersMap.remove(project); // Fired and ICElementDelta.PARSER_CHANGED CElementDelta delta = new CElementDelta(getCModel()); delta.binaryParserChanged(cproject); registerCModelDelta(delta); fire(ElementChangedEvent.POST_CHANGE); } } } public BinaryRunner getBinaryRunner(ICProject cproject) { BinaryRunner runner = null; IProject project = cproject.getProject(); synchronized (binaryRunners) { runner = binaryRunners.get(project); } if (runner == null) { // creation of BinaryRunner must occur outside the synchronized block runner = new BinaryRunner(project); synchronized (binaryRunners) { if (binaryRunners.get(project) == null) { binaryRunners.put(project, runner); runner.start(); } else { // another thread was faster runner = binaryRunners.get(project); } } } return runner; } public void removeBinaryRunner(ICProject cproject) { removeBinaryRunner(cproject.getProject()); } public void removeBinaryRunner(IProject project) { BinaryRunner runner; synchronized (binaryRunners) { runner = binaryRunners.remove(project); } if (runner != null) { runner.stop(); } } public SourceMapper getSourceMapper(ICProject cProject) { SourceMapper mapper = null; synchronized (sourceMappers) { mapper = sourceMappers.get(cProject); if (mapper == null) { mapper = new SourceMapper(cProject); sourceMappers.put(cProject, mapper); } } return mapper; } /** * addElementChangedListener method comment. */ public void addElementChangedListener(IElementChangedListener listener) { synchronized (fElementChangedListeners) { if (!fElementChangedListeners.contains(listener)) { fElementChangedListeners.add(listener); } } } /** * removeElementChangedListener method comment. */ public void removeElementChangedListener(IElementChangedListener listener) { synchronized (fElementChangedListeners) { 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 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.preDeleteProject((IProject) resource);} } catch (CoreException e) { } break; case IResourceChangeEvent.PRE_CLOSE: try { if (resource.getType() == IResource.PROJECT && ( ((IProject)resource).hasNature(CProjectNature.C_NATURE_ID) || ((IProject)resource).hasNature(CCProjectNature.CC_NATURE_ID) )){ this.preCloseProject((IProject) resource);} } catch (CoreException e) { } break; case IResourceChangeEvent.POST_CHANGE: try { if (delta != null) { checkForProjectRename(delta); ICElementDelta[] translatedDeltas = fDeltaProcessor.processResourceDelta(delta); if (translatedDeltas.length > 0) { for (ICElementDelta translatedDelta : translatedDeltas) { registerCModelDelta(translatedDelta); } } fire(ElementChangedEvent.POST_CHANGE); } } catch (Exception e) { CCorePlugin.log(e); } break; } } } public void handleEvent(CProjectDescriptionEvent event) { switch(event.getEventType()) { case CProjectDescriptionEvent.APPLIED: CProjectDescription newDes = (CProjectDescription)event.getNewCProjectDescription(); CProjectDescription oldDes = (CProjectDescription)event.getOldCProjectDescription(); if(oldDes != null && newDes != null) { ICConfigurationDescription newCfg = newDes.getDefaultSettingConfiguration(); ICConfigurationDescription oldCfg = oldDes.getDefaultSettingConfiguration(); int flags = 0; if(oldCfg != null && newCfg != null){ if(newCfg.getId().equals(oldCfg.getId())){ ICDescriptionDelta cfgDelta = findCfgDelta(event.getProjectDelta(), newCfg.getId()); if(cfgDelta != null){ flags = cfgDelta.getChangeFlags() & (ICDescriptionDelta.EXT_REF | ICDescriptionDelta.OWNER); } } else { flags = CProjectDescriptionManager.getInstance().calculateDescriptorFlags(newCfg, oldCfg); } } if ((flags & ICDescriptionDelta.EXT_REF) != 0) { // update binary parsers IProject project = newDes.getProject(); try { ICConfigExtensionReference[] newExts = CCorePlugin.getDefault().getDefaultBinaryParserExtensions(project); BinaryParserConfig[] currentConfigs = binaryParsersMap.get(project); // anything added/removed if (currentConfigs != null) { if (newExts.length != currentConfigs.length) { resetBinaryParser(project); } else { // may reorder for (int i = 0; i < newExts.length; i++) { if (!newExts[i].getID().equals(currentConfigs[i].getId())) { resetBinaryParser(project); break; } } } } } catch (CoreException e) { resetBinaryParser(project); } } } break; } } private ICDescriptionDelta findCfgDelta(ICDescriptionDelta delta, String id){ if(delta == null) return null; ICDescriptionDelta children[] = delta.getChildren(); for(int i = 0; i < children.length; i++){ ICSettingObject s = children[i].getNewSetting(); if(s != null && id.equals(s.getId())) return children[i]; } return null; } /* (non-Javadoc) * @see org.eclipse.core.runtime.content.IContentTypeManager.IContentTypeListener#contentTypeChanged() */ public void contentTypeChanged(ContentTypeChangeEvent event) { ContentTypeProcessor.processContentTypeChanges(new ContentTypeChangeEvent[]{ event }); } public void contentTypeChanged(ContentTypeChangeEvent[] events) { ContentTypeProcessor.processContentTypeChanges(events); } public void fire(int eventType) { fire(null, eventType); } public void fireShift(ICElement element, int offset, int size, int lines) { ICElementDelta delta = new CShiftData(element, offset, size, lines); fire(delta, ElementChangedEvent.POST_SHIFT); } /** * Fire C Model deltas, flushing them after the fact. * If the firing mode has been turned off, this has no effect. */ @SuppressWarnings("deprecation") void fire(ICElementDelta customDeltas, int eventType) { if (fFire) { ICElementDelta deltaToNotify; if (customDeltas == null) { deltaToNotify = mergeDeltas(this.fCModelDeltas); } else { deltaToNotify = customDeltas; } IElementChangedListener[] listeners; int listenerCount; int[] listenerMask; // Notification synchronized (fElementChangedListeners) { listeners = new IElementChangedListener[fElementChangedListeners.size()]; fElementChangedListeners.toArray(listeners); listenerCount = listeners.length; listenerMask = null; } switch (eventType) { case DEFAULT_CHANGE_EVENT: firePreAutoBuildDelta(deltaToNotify, listeners, listenerMask, listenerCount); firePostChangeDelta(deltaToNotify, listeners, listenerMask, listenerCount); fireReconcileDelta(listeners, listenerMask, listenerCount); break; case ElementChangedEvent.PRE_AUTO_BUILD: firePreAutoBuildDelta(deltaToNotify, listeners, listenerMask, listenerCount); break; case ElementChangedEvent.POST_CHANGE: firePostChangeDelta(deltaToNotify, listeners, listenerMask, listenerCount); fireReconcileDelta(listeners, listenerMask, listenerCount); break; case ElementChangedEvent.POST_RECONCILE: fireReconcileDelta(listeners, listenerMask, listenerCount); break; case ElementChangedEvent.POST_SHIFT: fireShiftEvent(deltaToNotify, listeners, listenerMask, listenerCount); return; } } } @SuppressWarnings("deprecation") private void firePreAutoBuildDelta(ICElementDelta deltaToNotify, IElementChangedListener[] listeners, int[] listenerMask, int listenerCount) { if (Util.VERBOSE_DELTA) { System.out.println("FIRING PRE_AUTO_BUILD Delta [" + Thread.currentThread() + "]:"); //$NON-NLS-1$//$NON-NLS-2$ System.out.println(deltaToNotify == null ? "<NONE>" : deltaToNotify.toString()); //$NON-NLS-1$ } if (deltaToNotify != null) { notifyListeners(deltaToNotify, ElementChangedEvent.PRE_AUTO_BUILD, listeners, listenerMask, listenerCount); } } private void firePostChangeDelta(ICElementDelta deltaToNotify, IElementChangedListener[] listeners, int[] listenerMask, int listenerCount) { // post change deltas if (Util.VERBOSE_DELTA) { System.out.println("FIRING POST_CHANGE Delta [" + Thread.currentThread() + "]:"); //$NON-NLS-1$//$NON-NLS-2$ System.out.println(deltaToNotify == null ? "<NONE>" : deltaToNotify.toString()); //$NON-NLS-1$ } if (deltaToNotify != null) { // flush now so as to keep listener reactions to post their own deltas for subsequent iteration this.flush(); notifyListeners(deltaToNotify, ElementChangedEvent.POST_CHANGE, listeners, listenerMask, listenerCount); } } private void fireReconcileDelta(IElementChangedListener[] listeners, int[] listenerMask, int listenerCount) { ICElementDelta deltaToNotify = mergeDeltas(this.reconcileDeltas.values()); if (Util.VERBOSE_DELTA) { System.out.println("FIRING POST_RECONCILE Delta [" + Thread.currentThread() + "]:"); //$NON-NLS-1$//$NON-NLS-2$ System.out.println(deltaToNotify == null ? "<NONE>" : deltaToNotify.toString()); //$NON-NLS-1$ } if (deltaToNotify != null) { // flush now so as to keep listener reactions to post their own deltas for subsequent iteration this.reconcileDeltas = new HashMap<IWorkingCopy, ICElementDelta>(); notifyListeners(deltaToNotify, ElementChangedEvent.POST_RECONCILE, listeners, listenerMask, listenerCount); } } private void fireShiftEvent(ICElementDelta deltaToNotify, IElementChangedListener[] listeners, int[] listenerMask, int listenerCount) { // post change deltas if (Util.VERBOSE_DELTA) { System.out.println("FIRING POST_SHIFT event [" + Thread.currentThread() + "]:"); //$NON-NLS-1$//$NON-NLS-2$ System.out.println(deltaToNotify == null ? "<NONE>" : deltaToNotify.toString()); //$NON-NLS-1$ } if (deltaToNotify != null) { this.flush(); notifyListeners(deltaToNotify, ElementChangedEvent.POST_SHIFT, listeners, listenerMask, listenerCount); } } public void notifyListeners(ICElementDelta deltaToNotify, int eventType, IElementChangedListener[] listeners, int[] listenerMask, int listenerCount) { final ElementChangedEvent extraEvent = new ElementChangedEvent(deltaToNotify, eventType); for (int i = 0; i < listenerCount; i++) { if (listenerMask == null || (listenerMask[i] & eventType) != 0) { final IElementChangedListener listener = listeners[i]; long start = -1; if (Util.VERBOSE_DELTA) { System.out.print("Listener #" + (i + 1) + "=" + listener.toString());//$NON-NLS-1$//$NON-NLS-2$ start = System.currentTimeMillis(); } // wrap callbacks with Safe runnable for subsequent listeners to be called when some are causing grief SafeRunner.run(new ISafeRunnable() { public void handleException(Throwable exception) { //CCorePlugin.log(exception, "Exception occurred in listener of C element change notification"); //$NON-NLS-1$ CCorePlugin.log(exception); } public void run() throws Exception { listener.elementChanged(extraEvent); } }); if (Util.VERBOSE_DELTA) { System.out.println(" -> " + (System.currentTimeMillis() - start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ } } } } /** * Flushes all deltas without firing them. */ protected void flush() { fCModelDeltas.clear(); } private ICElementDelta mergeDeltas(Collection<ICElementDelta> deltas) { synchronized (deltas) { if (deltas.size() == 0) return null; if (deltas.size() == 1) return deltas.iterator().next(); if (deltas.size() <= 1) return null; ICElement cRoot = getCModel(); CElementDelta rootDelta = new CElementDelta(cRoot); boolean insertedTree = false; for (ICElementDelta delta : deltas) { ICElement element = delta.getElement(); if (cRoot.equals(element)) { for (ICElementDelta child : delta.getAffectedChildren()) { CElementDelta projectDelta = (CElementDelta)child; rootDelta.insertDeltaTree(projectDelta.getElement(), projectDelta); insertedTree = true; } IResourceDelta[] resourceDeltas = delta.getResourceDeltas(); if (resourceDeltas != null) { for (IResourceDelta resourceDelta : resourceDeltas) { rootDelta.addResourceDelta(resourceDelta); insertedTree = true; } } } else { rootDelta.insertDeltaTree(element, (CElementDelta)delta); insertedTree = true; } } if (insertedTree) { return rootDelta; } return null; } } /** * Returns the set of elements which are out of synch with their buffers. */ protected Map<ICElement,ICElement> getElementsOutOfSynchWithBuffers() { return this.elementsOutOfSynchWithBuffers; } /** * Returns the info for the element. */ public synchronized Object getInfo(ICElement element) { Map<ICElement, CElementInfo> tempCache = this.temporaryCache.get(); if (tempCache != null) { Object result = tempCache.get(element); if (result != null) return result; } return this.cache.getInfo(element); } /** * Returns the info for this element without * disturbing the cache ordering. */ protected synchronized Object peekAtInfo(ICElement element) { Map<ICElement, CElementInfo> tempCache = this.temporaryCache.get(); if (tempCache != null) { Object result = tempCache.get(element); if (result != null) { return result; } } return this.cache.peekAtInfo(element); } /* * Puts the infos in the given map (keys are ICElements and values are CElementInfos) * in the C model cache in an atomic way. * First checks that the info for the opened element (or one of its ancestors) has not been * added to the cache. If it is the case, another thread has opened the element (or one of * its ancestors). So returns without updating the cache. */ protected synchronized void putInfos(ICElement openedElement, Map<ICElement, CElementInfo> newElements) { // remove children Object existingInfo = this.cache.peekAtInfo(openedElement); if (openedElement instanceof IParent && existingInfo instanceof CElementInfo) { ICElement[] children = ((CElementInfo)existingInfo).getChildren(); for (int i = 0, size = children.length; i < size; ++i) { CElement child = (CElement)children[i]; try { child.close(); } catch (CModelException e) { // ignore } } } for (Map.Entry<ICElement, CElementInfo> element : newElements.entrySet()) this.cache.putInfo(element.getKey(), element.getValue()); } /** * Removes all cached info from the C Model, including all children, * but does not close this element. */ protected synchronized void removeChildrenInfo(ICElement openedElement) { // remove children Object existingInfo = this.cache.peekAtInfo(openedElement); if (openedElement instanceof IParent && existingInfo instanceof CElementInfo) { ICElement[] children = ((CElementInfo)existingInfo).getChildren(); for (int i = 0, size = children.length; i < size; ++i) { CElement child = (CElement)children[i]; try { child.close(); } catch (CModelException e) { // ignore } } } } /** * Removes the info of this model element. */ protected synchronized void removeInfo(ICElement element) { this.cache.removeInfo(element); } /* * Returns the temporary cache for newly opened elements for the current thread. * Creates it if not already created. */ public Map<ICElement, CElementInfo> getTemporaryCache() { Map<ICElement, CElementInfo> result = this.temporaryCache.get(); if (result == null) { result = new HashMap<ICElement, CElementInfo>(); this.temporaryCache.set(result); } return result; } /* * Returns whether there is a temporary cache for the current thread. */ public boolean hasTemporaryCache() { return this.temporaryCache.get() != null; } /* * Resets the temporary cache for newly created elements to null. */ public void resetTemporaryCache() { this.temporaryCache.set(null); } public void startup() { // Initialization is performed on the first getDefault()... } public void shutdown() { // Remove ourself from the DescriptorManager. CProjectDescriptionManager.getInstance().removeCProjectDescriptionListener(this); // CCorePlugin.getDefault().getCDescriptorManager().removeDescriptorListener(factory); // Remove ourself from the ContentTypeManager Platform.getContentTypeManager().removeContentTypeChangeListener(factory); // Do any shutdown of services. ResourcesPlugin.getWorkspace().removeResourceChangeListener(factory); BinaryRunner[] runners; synchronized (binaryRunners) { runners = binaryRunners.values().toArray(new BinaryRunner[0]); } for (BinaryRunner runner : runners) { runner.stop(); } // Nullify the static factory factory = null; } private void checkForProjectRename(IResourceDelta delta) { IResourceDelta[] rem= delta.getAffectedChildren(IResourceDelta.REMOVED); for (IResourceDelta element : rem) { delta = element; IResource res= delta.getResource(); if (res.getType() == IResource.PROJECT) { IPath movedTo= null; if ((delta.getFlags() & IResourceDelta.MOVED_TO) != 0) { movedTo= delta.getMovedToPath(); } LocalProjectScope.deletePreferences(res.getFullPath(), movedTo); } } } private void preDeleteProject(IProject project) { // remove binary parsers binaryParsersMap.remove(project); // stop the binary runner for this project removeBinaryRunner(project); // stop indexing jobs for this project CCoreInternals.getPDOMManager().preDeleteProject(create(project)); } private void preCloseProject(IProject project) { // remove binary parsers binaryParsersMap.remove(project); // stop the binary runner for this project removeBinaryRunner(project); // stop indexing jobs for this project CCoreInternals.getPDOMManager().preCloseProject(create(project)); } public IWorkingCopy[] getSharedWorkingCopies(IBufferFactory factory) { // if factory is null, default factory must be used if (factory == null) factory = BufferManager.getDefaultBufferManager().getDefaultBufferFactory(); Map<ITranslationUnit, WorkingCopy> perFactoryWorkingCopies = sharedWorkingCopies.get(factory); if (perFactoryWorkingCopies == null) return NoWorkingCopy; Collection<WorkingCopy> copies = perFactoryWorkingCopies.values(); return copies.toArray(new IWorkingCopy[copies.size()]); } public IWorkingCopy findSharedWorkingCopy(IBufferFactory factory, ITranslationUnit tu) { // if factory is null, default factory must be used if (factory == null) factory = BufferManager.getDefaultBufferManager(); Map<ITranslationUnit, WorkingCopy> perFactoryWorkingCopies = sharedWorkingCopies.get(factory); if (perFactoryWorkingCopies == null) return null; return perFactoryWorkingCopies.get(tu); } public IWorkingCopy getSharedWorkingCopy(IBufferFactory factory, ITranslationUnit tu, IProblemRequestor requestor, IProgressMonitor monitor) throws CModelException { // if factory is null, default factory must be used if (factory == null) factory = BufferManager.getDefaultBufferManager(); Map<ITranslationUnit, WorkingCopy> perFactoryWorkingCopies = sharedWorkingCopies.get(factory); if (perFactoryWorkingCopies == null) { perFactoryWorkingCopies = new HashMap<ITranslationUnit, WorkingCopy>(); sharedWorkingCopies.put(factory, perFactoryWorkingCopies); } WorkingCopy workingCopy = perFactoryWorkingCopies.get(tu); if (workingCopy != null) { workingCopy.useCount++; return workingCopy; } CreateWorkingCopyOperation op = new CreateWorkingCopyOperation(tu, perFactoryWorkingCopies, factory, requestor); op.runOperation(monitor); return (IWorkingCopy) op.getResultElements()[0]; } public IWorkingCopy removeSharedWorkingCopy(final IBufferFactory bufferFactory, ITranslationUnit originalElement) { // In order to be shared, working copies have to denote the same compilation unit // AND use the same buffer factory. // Assuming there is a little set of buffer factories, then use a 2 level Map cache. Map<ITranslationUnit, WorkingCopy> perFactoryWorkingCopies = sharedWorkingCopies.get(bufferFactory); if (perFactoryWorkingCopies != null) { return perFactoryWorkingCopies.remove(originalElement); } return null; } }