/******************************************************************************* * Copyright (c) 2000, 2015 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 * Anton Leherbauer (Wind River Systems) *******************************************************************************/ package org.eclipse.cdt.internal.core.model; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; 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.CoreModelUtil; import org.eclipse.cdt.core.model.IArchiveContainer; import org.eclipse.cdt.core.model.IBinaryContainer; import org.eclipse.cdt.core.model.ICContainer; import org.eclipse.cdt.core.model.ICElement; import org.eclipse.cdt.core.model.ICModelStatusConstants; import org.eclipse.cdt.core.model.ICProject; import org.eclipse.cdt.core.model.IIncludeEntry; import org.eclipse.cdt.core.model.IIncludeReference; import org.eclipse.cdt.core.model.ILibraryEntry; import org.eclipse.cdt.core.model.ILibraryReference; import org.eclipse.cdt.core.model.IOutputEntry; import org.eclipse.cdt.core.model.IPathEntry; import org.eclipse.cdt.core.model.ISourceEntry; import org.eclipse.cdt.core.model.ISourceRoot; import org.eclipse.cdt.core.settings.model.CSourceEntry; import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; import org.eclipse.cdt.core.settings.model.ICProjectDescription; import org.eclipse.cdt.core.settings.model.ICSourceEntry; import org.eclipse.cdt.internal.core.settings.model.CProjectDescriptionManager; import org.eclipse.cdt.internal.core.util.MementoTokenizer; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.ProjectScope; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Preferences; import org.eclipse.core.runtime.QualifiedName; import org.eclipse.core.runtime.preferences.IEclipsePreferences; import org.eclipse.core.runtime.preferences.IScopeContext; import org.osgi.service.prefs.BackingStoreException; public class CProject extends Openable implements ICProject { private static final String CUSTOM_DEFAULT_OPTION_VALUE = "#\r\n\r#custom-non-empty-default-value#\r\n\r#"; //$NON-NLS-1$ public CProject(ICElement parent, IProject project) { super(parent, project, ICElement.C_PROJECT); } @Override public IBinaryContainer getBinaryContainer() throws CModelException { return ((CProjectInfo) getElementInfo()).getBinaryContainer(); } @Override public IArchiveContainer getArchiveContainer() throws CModelException { return ((CProjectInfo) getElementInfo()).getArchiveContainer(); } @Override public IProject getProject() { return getUnderlyingResource().getProject(); } @Override public ICElement findElement(IPath path) throws CModelException { ICElement celem = null; if (path.isAbsolute()) { celem = CModelManager.getDefault().create(path); } else { IProject project = getProject(); if (project != null) { IPath p = project.getFullPath().append(path); celem = CModelManager.getDefault().create(p); } } if (celem == null) { CModelStatus status = new CModelStatus(ICModelStatusConstants.INVALID_PATH, path); throw new CModelException(status); } return celem; } public static boolean hasCNature(IProject p) { try { return p.hasNature(CProjectNature.C_NATURE_ID); } catch (CoreException e) { //throws exception if the project is not open. } return false; } public static boolean hasCCNature(IProject p) { try { return p.hasNature(CCProjectNature.CC_NATURE_ID); } catch (CoreException e) { //throws exception if the project is not open. } return false; } private boolean isCProject() { return hasCNature(getProject()) || hasCCNature(getProject()); } /** * Returns true if this handle represents the same C project * as the given handle. Two handles represent the same * project if they are identical or if they represent a project with * the same underlying resource and occurrence counts. * * @see CElement#equals(Object) */ @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof CProject)) return false; CProject other = (CProject) o; return getProject().equals(other.getProject()); } @Override protected CElementInfo createElementInfo() { return new CProjectInfo(this); } // CHECKPOINT: CProjects will return the hash code of their underlying IProject @Override public int hashCode() { return getProject().hashCode(); } @Override public IIncludeReference[] getIncludeReferences() throws CModelException { CProjectInfo pinfo = (CProjectInfo) CModelManager.getDefault().peekAtInfo(this); IIncludeReference[] incRefs = null; if (pinfo != null) { incRefs = pinfo.incReferences; } if (incRefs == null) { IPathEntry[] entries = getResolvedPathEntries(); ArrayList<IncludeReference> list = new ArrayList<>(entries.length); for (IPathEntry entrie : entries) { if (entrie.getEntryKind() == IPathEntry.CDT_INCLUDE) { IIncludeEntry entry = (IIncludeEntry) entrie; list.add(new IncludeReference(this, entry)); } } incRefs = list.toArray(new IIncludeReference[list.size()]); if (pinfo != null) { pinfo.incReferences = incRefs; } } return incRefs; } @Override public ILibraryReference[] getLibraryReferences() throws CModelException { CProjectInfo pinfo = (CProjectInfo) CModelManager.getDefault().peekAtInfo(this); ILibraryReference[] libRefs = null; if (pinfo != null) { libRefs = pinfo.libReferences; } if (libRefs == null) { BinaryParserConfig[] binConfigs = CModelManager.getDefault().getBinaryParser(getProject()); IPathEntry[] entries = getResolvedPathEntries(); ArrayList<ILibraryReference> list = new ArrayList<>(entries.length); for (IPathEntry entrie : entries) { if (entrie.getEntryKind() == IPathEntry.CDT_LIBRARY) { ILibraryEntry entry = (ILibraryEntry) entrie; ILibraryReference lib = getLibraryReference(this, binConfigs, entry); if (lib != null) { list.add(lib); } } } libRefs = list.toArray(new ILibraryReference[list.size()]); if (pinfo != null) { pinfo.libReferences = libRefs; } } return libRefs; } private static ILibraryReference getLibraryReference(ICProject cproject, BinaryParserConfig[] binConfigs, ILibraryEntry entry) { if (binConfigs == null) { binConfigs = CModelManager.getDefault().getBinaryParser(cproject.getProject()); } ILibraryReference lib = null; if (binConfigs != null) { for (BinaryParserConfig binConfig : binConfigs) { IBinaryFile bin; try { IBinaryParser parser = binConfig.getBinaryParser(); bin = parser.getBinary(entry.getFullLibraryPath()); if (bin != null) { if (bin.getType() == IBinaryFile.ARCHIVE) { lib = new LibraryReferenceArchive(cproject, entry, (IBinaryArchive) bin); } else if (bin instanceof IBinaryObject) { lib = new LibraryReferenceShared(cproject, entry, (IBinaryObject) bin); } break; } } catch (IOException | CoreException e) { } } } if (lib == null) { lib = new LibraryReference(cproject, entry); } return lib; } /** * @see ICProject#getRequiredProjectNames() */ @Override public String[] getRequiredProjectNames() throws CModelException { return projectPrerequisites(getResolvedPathEntries()); } public String[] projectPrerequisites(IPathEntry[] entries) throws CModelException { return PathEntryManager.getDefault().projectPrerequisites(entries); } /** * @see org.eclipse.cdt.core.model.ICProject#getOption(String, boolean) */ @Override public String getOption(String optionName, boolean inheritCCoreOptions) { if (CModelManager.OptionNames.contains(optionName)) { IEclipsePreferences preferences = getPreferences(); final String cCoreDefault= inheritCCoreOptions ? CCorePlugin.getOption(optionName) : null; if (preferences == null) { return cCoreDefault; } String value= preferences.get(optionName, cCoreDefault).trim(); return value == null ? null : value.trim(); } return null; } /** * @see org.eclipse.cdt.core.model.ICProject#getOptions(boolean) */ @Override public Map<String, String> getOptions(boolean inheritCCoreOptions) { // initialize to the defaults from CCorePlugin options pool Map<String, String> options= inheritCCoreOptions ? CCorePlugin.getOptions() : new HashMap<String, String>(5); IEclipsePreferences preferences = getPreferences(); if (preferences == null) return options; HashSet<String> optionNames= CModelManager.OptionNames; // create project options try { String[] propertyNames= preferences.keys(); for (String propertyName : propertyNames) { String value= preferences.get(propertyName, null); if (value != null && optionNames.contains(propertyName)) { options.put(propertyName, value.trim()); } } } catch (BackingStoreException e) { // ignore silently } return options; } @Override public void setOption(String optionName, String optionValue) { if (!CModelManager.OptionNames.contains(optionName)) return; // unrecognized option IEclipsePreferences projectPreferences= getPreferences(); if (optionValue == null) { // remove preference projectPreferences.remove(optionName); } else { projectPreferences.put(optionName, optionValue); } // Dump changes try { projectPreferences.flush(); } catch (BackingStoreException e) { // problem with pref store - quietly ignore } } @Override public void setOptions(Map<String, String> newOptions) { Preferences preferences = new Preferences(); setPreferences(preferences); // always reset (26255) if (newOptions != null) { for (Map.Entry<String, String> e : newOptions.entrySet()) { String key = e.getKey(); if (!CModelManager.OptionNames.contains(key)) continue; // unrecognized option // no filtering for encoding (custom encoding for project is allowed) String value = e.getValue(); preferences.setDefault(key, CUSTOM_DEFAULT_OPTION_VALUE); // empty string isn't the default (26251) preferences.setValue(key, value); } } // persist options savePreferences(preferences); } /** * Returns the project custom preference pool. * Project preferences may include custom encoding. * @return IEclipsePreferences or <code>null</code> if the project * does not have a C nature. */ private IEclipsePreferences getPreferences() { if (!(isCProject())) { return null; } IScopeContext context= new ProjectScope(getProject()); final IEclipsePreferences preferences= context.getNode(CCorePlugin.PLUGIN_ID); return preferences; } /** * Save project custom preferences to persistent properties */ private void savePreferences(Preferences preferences) { if (preferences == null) return; if (!isCProject()) { return; // ignore } Iterator<String> iter = CModelManager.OptionNames.iterator(); while (iter.hasNext()) { String qualifiedName = iter.next(); String dequalifiedName = qualifiedName.substring(CCorePlugin.PLUGIN_ID.length() + 1); String value = null; try { value = preferences.getString(qualifiedName); if (value != null && !value.equals(preferences.getDefaultString(qualifiedName))) { resource.setPersistentProperty(new QualifiedName(CCorePlugin.PLUGIN_ID, dequalifiedName), value); } else { resource.setPersistentProperty(new QualifiedName(CCorePlugin.PLUGIN_ID, dequalifiedName), null); } } catch (CoreException e) { } } } /** * Sets cached preferences, no preferences are saved, only info is updated */ private void setPreferences(Preferences preferences) { if (!isCProject()) { return; // ignore } // Do nothing } @Override public IPathEntry[] getResolvedPathEntries() throws CModelException { return CoreModel.getResolvedPathEntries(this); } @Override public IPathEntry[] getRawPathEntries() throws CModelException { return CoreModel.getRawPathEntries(this); } @Override public void setRawPathEntries(IPathEntry[] newEntries, IProgressMonitor monitor) throws CModelException { CoreModel.setRawPathEntries(this, newEntries, monitor); } @Override public ISourceRoot getSourceRoot(ISourceEntry entry) throws CModelException { return getSourceRoot(new CSourceEntry(entry.getPath(), entry.getExclusionPatterns(), 0)); } public ISourceRoot getSourceRoot(ICSourceEntry entry) throws CModelException { IPath p = getPath(); IPath sp = entry.getFullPath(); if (p.isPrefixOf(sp)) { int count = sp.matchingFirstSegments(p); sp = sp.removeFirstSegments(count); IResource res = null; if (sp.isEmpty()) { res = getProject(); } else { res = getProject().findMember(sp); } if (res != null) { return new SourceRoot(this, res, entry); } } return null; } @Override public ISourceRoot findSourceRoot(IResource res) { try { ISourceRoot[] roots = getAllSourceRoots(); for (ISourceRoot root : roots) { if (root.isOnSourceEntry(res)) { return root; } } } catch (CModelException e) { } return null; } @Override public ISourceRoot findSourceRoot(IPath path) { try { ISourceRoot[] roots = getAllSourceRoots(); for (ISourceRoot root : roots) { if (root.getPath().equals(path)) { return root; } } } catch (CModelException e) { } return null; } @Override public ISourceRoot[] getSourceRoots() throws CModelException { Object[] children = getChildren(); ArrayList<ISourceRoot> result = new ArrayList<>(children.length); for (Object element : children) { if (element instanceof ISourceRoot) { result.add((ISourceRoot) element); } } return result.toArray(new ISourceRoot[result.size()]); } /** * Returns all source roots. * * @return all source roots * @throws CModelException */ @Override public ISourceRoot[] getAllSourceRoots() throws CModelException { CProjectInfo pinfo = (CProjectInfo) CModelManager.getDefault().peekAtInfo(this); ISourceRoot[] roots = null; if (pinfo != null) { if (pinfo.sourceRoots != null) { roots = pinfo.sourceRoots; } else { List<ISourceRoot> list = computeSourceRoots(); roots = pinfo.sourceRoots = list.toArray(new ISourceRoot[list.size()]); } } else { List<ISourceRoot> list = computeSourceRoots(); roots = list.toArray(new ISourceRoot[list.size()]); } return roots; } @Override public IOutputEntry[] getOutputEntries() throws CModelException { CProjectInfo pinfo = (CProjectInfo) CModelManager.getDefault().peekAtInfo(this); IOutputEntry[] outs = null; if (pinfo != null) { if (pinfo.outputEntries != null) { outs = pinfo.outputEntries; } else { IPathEntry[] entries = getResolvedPathEntries(); outs = pinfo.outputEntries = getOutputEntries(entries); } } else { IPathEntry[] entries = getResolvedPathEntries(); outs = getOutputEntries(entries); } return outs; } public IOutputEntry[] getOutputEntries(IPathEntry[] entries) throws CModelException { ArrayList<IPathEntry> list = new ArrayList<>(entries.length); for (IPathEntry entrie : entries) { if (entrie.getEntryKind() == IPathEntry .CDT_OUTPUT) { list.add(entrie); } } IOutputEntry[] outputs = new IOutputEntry[list.size()]; list.toArray(outputs); return outputs; } private boolean isParentOfOutputEntry(IResource resource) { IPath path = resource.getFullPath(); // ensure that folders are only excluded if all of their children are excluded if (resource.getType() == IResource.FOLDER || resource.getType() == IResource.PROJECT) { try { IOutputEntry[] entries = getOutputEntries(); for (IOutputEntry entry : entries) { if (path.isPrefixOf(entry.getPath())) { return true; } } } catch (CModelException e) { // } return false; } return false; } @Override public boolean isOnOutputEntry(IResource resource) { IPath path = resource.getFullPath(); // ensure that folders are only excluded if all of their children are excluded if (resource.getType() == IResource.FOLDER || resource.getType() == IResource.PROJECT) { path = path.append("*"); //$NON-NLS-1$ } try { IOutputEntry[] entries = getOutputEntries(); for (IOutputEntry entrie : entries) { boolean on = isOnOutputEntry(entrie, path); if (on) { return on; } } } catch (CModelException e) { // } return false; } private boolean isOnOutputEntry(IOutputEntry entry, IPath path) { if (entry.getPath().isPrefixOf(path) && !CoreModelUtil.isExcluded(path, entry.fullExclusionPatternChars())) { return true; } return false; } @Override protected boolean buildStructure(OpenableInfo info, IProgressMonitor pm, Map<ICElement, CElementInfo> newElements, IResource underlyingResource) throws CModelException { boolean validInfo = false; try { IResource res = getResource(); if (res != null && res.isAccessible()) { validInfo = computeChildren(info, res); } else { throw newNotPresentException(); } } finally { if (!validInfo) { CModelManager.getDefault().removeInfo(this); } } return validInfo; } protected List<ISourceRoot> computeSourceRoots() throws CModelException { //IPathEntry[] entries = getResolvedPathEntries(); ICSourceEntry[] entries = null; ICProjectDescription des = CProjectDescriptionManager.getInstance().getProjectDescription(getProject(), false); if (des != null) { ICConfigurationDescription cfg = des.getDefaultSettingConfiguration(); if (cfg != null) entries = cfg.getResolvedSourceEntries(); } if (entries != null) { ArrayList<ISourceRoot> list = new ArrayList<>(entries.length); for (ICSourceEntry sourceEntry : entries) { ISourceRoot root = getSourceRoot(sourceEntry); if (root != null) { list.add(root); } } return list; } return Collections.emptyList(); } protected boolean computeChildren(OpenableInfo info, IResource res) throws CModelException { List<ISourceRoot> sourceRoots = computeSourceRoots(); List<ICContainer> children = new ArrayList<ICContainer>(sourceRoots.size()); children.addAll(sourceRoots); boolean projectIsSourceRoot = false; for (ISourceRoot sourceRoot : sourceRoots) { if (sourceRoot.getResource().equals(getProject())) { projectIsSourceRoot = true; break; } } // Now look for output folders try { IResource[] resources = getProject().members(); for (IResource child : resources) { if (child.getType() == IResource.FOLDER) { boolean found = false; for (ISourceRoot sourceRoot : sourceRoots) { if (sourceRoot.isOnSourceEntry(child)) { found = true; break; } } // Not in source folder, check if it's a container on output entry // Also make sure I'm not a source root since my SourceRoot object would // have already added this. if (!found && !projectIsSourceRoot && (isParentOfOutputEntry(child) || isOnOutputEntry(child))) children.add(new CContainer(this, child)); } } } catch (CoreException e) { // ignore } info.setChildren(children); if (info instanceof CProjectInfo) { CProjectInfo pinfo = (CProjectInfo)info; pinfo.sourceRoots= sourceRoots.toArray(new ISourceRoot[sourceRoots.size()]); pinfo.setNonCResources(null); } return true; } @Override public boolean isOnSourceRoot(ICElement element) { try { ISourceRoot[] roots = getSourceRoots(); for (ISourceRoot root : roots) { if (root.isOnSourceEntry(element)) { return true; } } } catch (CModelException e) { // .. } return false; } @Override public boolean isOnSourceRoot(IResource resource) { try { ISourceRoot[] roots = getSourceRoots(); for (ISourceRoot root : roots) { if (root.isOnSourceEntry(resource)) { return true; } } } catch (CModelException e) { // } return false; } @Override public boolean exists() { if (!isCProject()) { return false; } return true; } @Override public Object[] getNonCResources() throws CModelException { return ((CProjectInfo) getElementInfo()).getNonCResources(getResource()); } @Override protected void closing(Object info) throws CModelException { if (info instanceof CProjectInfo) { CModelManager.getDefault().removeBinaryRunner(this); CProjectInfo pinfo = (CProjectInfo)info; if (pinfo.vBin != null) { pinfo.vBin.close(); } if (pinfo.vLib != null) { pinfo.vLib.close(); } pinfo.resetCaches(); } super.closing(info); } /** * Resets this project's caches */ public void resetCaches() { CProjectInfo pinfo = (CProjectInfo) CModelManager.getDefault().peekAtInfo(this); if (pinfo != null) { pinfo.resetCaches(); } } @Override public ICElement getHandleFromMemento(String token, MementoTokenizer memento) { switch (token.charAt(0)) { case CEM_SOURCEROOT: IPath rootPath = Path.EMPTY; token = null; while (memento.hasMoreTokens()) { token = memento.nextToken(); char firstChar = token.charAt(0); if (firstChar != CEM_SOURCEFOLDER && firstChar != CEM_TRANSLATIONUNIT) { rootPath= rootPath.append(token); token= null; } else { break; } } if (!rootPath.isAbsolute()) { rootPath= getProject().getFullPath().append(rootPath); } CElement root = (CElement) findSourceRoot(rootPath); if (root != null) { if (token != null) { return root.getHandleFromMemento(token, memento); } else { return root.getHandleFromMemento(memento); } } break; case CEM_TRANSLATIONUNIT: if (!memento.hasMoreTokens()) return this; String tuName = memento.nextToken(); final IPath path= Path.fromPortableString(tuName); CElement tu= null; if (!path.isAbsolute()) { final IProject project= getProject(); if (project != null) { IResource resource= project.findMember(path); if (resource != null && resource.getType() == IResource.FILE) { final IFile file= (IFile) resource; tu= (CElement) CModelManager.getDefault().create(file, this); if (tu == null) { String contentTypeId= CoreModel.getRegistedContentTypeId(project, file.getName()); if (contentTypeId != null) { tu= new TranslationUnit(this, file, contentTypeId); } } } } } else { tu= (CElement) CoreModel.getDefault().createTranslationUnitFrom(this, path); } if (tu != null) { return tu.getHandleFromMemento(memento); } break; } return null; } @Override protected char getHandleMementoDelimiter() { return CEM_CPROJECT; } }