blob: 0ae3fae7e0e6c2604a1997b071a4b7989c11b7e0 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
* Mike Kucera (IBM)
*******************************************************************************/
package org.eclipse.ptp.internal.rdt.core;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.dom.ILinkage;
import org.eclipse.cdt.core.model.CoreModelUtil;
import org.eclipse.cdt.core.model.ICElement;
import org.eclipse.cdt.core.model.ILanguage;
import org.eclipse.cdt.core.model.ITranslationUnit;
import org.eclipse.cdt.core.model.LanguageManager;
import org.eclipse.cdt.core.parser.IScannerInfo;
import org.eclipse.cdt.core.parser.IScannerInfoProvider;
import org.eclipse.cdt.internal.core.pdom.indexer.IndexerPreferences;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceVisitor;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.content.IContentType;
import org.eclipse.ptp.rdt.core.RDTLog;
/**
* Used to get instances of RemoteIndexerInfoProvider.
*
* The factory is not part of the remote JAR because it depends on
* eclipse and CDT APIs.
*/
public class RemoteIndexerInfoProviderFactory {
// These are the linkages supported by RDT
private static final int[] LINKAGE_IDS = new int[] { ILinkage.C_LINKAGE_ID, ILinkage.CPP_LINKAGE_ID };
/**
* Returns a RemoteIndexerInfoProvider that contains all the IScannerInfos and language mapping info
* for all the translation units in the given scope (project name).
* @param scopeName the name of a project
*/
public static RemoteIndexerInfoProvider getProvider(String scopeName) {
IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(scopeName);
return getProvider(project);
}
/**
* Returns a RemoteIndexerInfoProvider that contains all the IScannerInfos and language mapping info
* for all the translation units in the given project.
* Returns an empty provider if the project is null.
*/
public static RemoteIndexerInfoProvider getProvider(IProject project) {
if(project == null)
return new RemoteIndexerInfoProvider();
final List<ICElement> elements = new ArrayList<ICElement>();
// TODO replace with ICElementVisitor
IResourceVisitor resourceVisitor = new IResourceVisitor() {
public boolean visit(IResource resource) throws CoreException {
if (!(resource instanceof IFile))
return true;
ITranslationUnit tu = CoreModelUtil.findTranslationUnit((IFile) resource);
if(tu != null)
elements.add(tu);
return true;
}
};
try {
project.accept(resourceVisitor);
} catch (CoreException e) {
RDTLog.logError(e);
}
return getProvider(elements);
}
/**
* This class is used to guarantee a consistent implementation
* of equals() and hashCode() for using IScannerInfo objects as
* keys in a Map.
*/
private static class ScannerInfoKey {
private IScannerInfo scannerInfo;
private int hashCode = 0;
ScannerInfoKey(IScannerInfo scannerInfo) {
this.scannerInfo = scannerInfo;
}
public boolean equals(Object obj) {
// no need for instanceof check
ScannerInfoKey other = (ScannerInfoKey)obj;
return scannerInfo.getDefinedSymbols().equals(other.scannerInfo.getDefinedSymbols())
&& Arrays.equals(scannerInfo.getIncludePaths(), other.scannerInfo.getIncludePaths());
}
public int hashCode() {
if(hashCode == 0) {
hashCode = scannerInfo.getDefinedSymbols().hashCode()
+ Arrays.hashCode(scannerInfo.getIncludePaths());
}
return hashCode;
}
}
/**
* Maintains a cache of shared instances of RemoteScannerInfo.
*/
private static class RemoteScannerInfoCache {
Map<ScannerInfoKey,RemoteScannerInfo> cache = new HashMap<ScannerInfoKey,RemoteScannerInfo>();
RemoteScannerInfo get(IScannerInfo localScannerInfo) {
// If we have already seen a IScannerInfo that is identical then just reuse the
// existing remote version of it.
ScannerInfoKey key = new ScannerInfoKey(localScannerInfo);
RemoteScannerInfo remoteScannerInfo = cache.get(key);
if(remoteScannerInfo == null) {
remoteScannerInfo = new RemoteScannerInfo(localScannerInfo);
cache.put(key, remoteScannerInfo);
}
return remoteScannerInfo;
}
}
/**
* Returns a RemoteIndexerInfoProvider that contains IScannerInfos for every
* translation unit in the given list of ICElements. The returned RemoteIndexerInfoProvider
* is optimized to not contain any duplicate scanner infos.
*
* It is assumed that all the elements are from the same project.
*
* @throws NullPointerException if the given list is null or contains a null element
*/
public static RemoteIndexerInfoProvider getProvider(List<ICElement> elements) {
if(elements.isEmpty())
return new RemoteIndexerInfoProvider();
Map<String,RemoteScannerInfo> scannerInfoMap = new HashMap<String,RemoteScannerInfo>();
Map<Integer,RemoteScannerInfo> linkageMap = new HashMap<Integer,RemoteScannerInfo>();
Map<String,String> languageMap = new HashMap<String,String>();
Map<String,Boolean> isHeaderMap = new HashMap<String,Boolean>();
// we assume all the elements are from the same project
IProject project = elements.get(0).getCProject().getProject();
IScannerInfoProvider provider = CCorePlugin.getDefault().getScannerInfoProvider(project);
RemoteScannerInfoCache cache = new RemoteScannerInfoCache();
for(ICElement element : elements) {
if(element instanceof ITranslationUnit) {
ITranslationUnit tu = (ITranslationUnit) element;
// compute the scanner info, share identical scanner infos
IScannerInfo localScannerInfo = provider.getScannerInformation(tu.getResource());
RemoteScannerInfo remoteScannerInfo = cache.get(localScannerInfo);
String path = element.getLocationURI().getPath();
scannerInfoMap.put(path, remoteScannerInfo);
// compute the language
try {
ILanguage language = tu.getLanguage();
languageMap.put(path, language.getId());
} catch (CoreException e) {
RDTLog.logError(e);
}
// is it a header file?
isHeaderMap.put(path, tu.isHeaderUnit());
}
}
for(int id : LINKAGE_IDS) {
IScannerInfo defaultScannerInfo = getDefaultScannerInfo(project, id);
RemoteScannerInfo remoteScannerInfo = cache.get(defaultScannerInfo);
linkageMap.put(id, remoteScannerInfo);
}
// compute the indexer preferences
Properties props = IndexerPreferences.getProperties(project);
Map<String,Boolean> preferences = computeIndexerPreferences(props);
String filePref = (String) props.get(IndexerPreferences.KEY_FILES_TO_PARSE_UP_FRONT);
List<String> filesToParseUpFront = Arrays.asList(filePref.split(",")); //$NON-NLS-1$
// need to compute the languages of the files to parse up front
// TODO there are two things wrong with this:
// 1) The file may need to be parsed as more than one language, see PDOMIndexerTask.getLanguages()
// 2) If there is a file in the project with the same name as a file to parse up front it may get the wrong scanner info
for(String filename : filesToParseUpFront) {
IContentType ct= CCorePlugin.getContentType(project, filename);
if (ct != null) {
ILanguage language = LanguageManager.getInstance().getLanguage(ct);
languageMap.put(filename, language.getId());
}
}
return new RemoteIndexerInfoProvider(scannerInfoMap, linkageMap, languageMap, isHeaderMap, preferences, filesToParseUpFront);
}
private static Map<String,Boolean> computeIndexerPreferences(Properties props) {
Map<String,Boolean> prefs = new HashMap<String,Boolean>(props.size());
//prefs.put(IRemoteIndexerInfoProvider.KEY_INDEX_UNUSED_HEADERS_WITH_DEFAULT_LANG, Boolean.valueOf(props.getProperty(IndexerPreferences.KEY_INDEX_UNUSED_HEADERS_WITH_DEFAULT_LANG)));
//prefs.put(IRemoteIndexerInfoProvider.KEY_INDEX_UNUSED_HEADERS_WITH_ALTERNATE_LANG, Boolean.valueOf(props.getProperty(IndexerPreferences.KEY_INDEX_UNUSED_HEADERS_WITH_ALTERNATE_LANG)));
prefs.put(IRemoteIndexerInfoProvider.KEY_INDEX_ALL_FILES, Boolean.valueOf(props.getProperty(IndexerPreferences.KEY_INDEX_ALL_FILES)));
//prefs.put(IRemoteIndexerInfoProvider.KEY_INCLUDE_HEURISTICS, Boolean.valueOf(props.getProperty(IndexerPreferences.KEY_INCLUDE_HEURISTICS)));
prefs.put(IRemoteIndexerInfoProvider.KEY_SKIP_ALL_REFERENCES, Boolean.valueOf(props.getProperty(IndexerPreferences.KEY_SKIP_ALL_REFERENCES)));
//prefs.put(IRemoteIndexerInfoProvider.KEY_SKIP_IMPLICIT_REFERENCES, Boolean.valueOf(props.getProperty(IndexerPreferences.KEY_SKIP_IMPLICIT_REFERENCES)));
prefs.put(IRemoteIndexerInfoProvider.KEY_SKIP_TYPE_REFERENCES, Boolean.valueOf(props.getProperty(IndexerPreferences.KEY_SKIP_TYPE_REFERENCES)));
prefs.put(IRemoteIndexerInfoProvider.KEY_SKIP_MACRO_REFERENCES, Boolean.valueOf(props.getProperty(IndexerPreferences.KEY_SKIP_MACRO_REFERENCES)));
return prefs;
}
/*
* This code was copied from PDOMIndexerTask.createDefaultScannerConfig(int)
*/
private static IScannerInfo getDefaultScannerInfo(IProject project, int linkageID) {
IScannerInfoProvider provider = CCorePlugin.getDefault().getScannerInfoProvider(project);
if(provider == null)
return null;
String filename = linkageID == ILinkage.C_LINKAGE_ID ? "__cdt__.c" : "__cdt__.cpp"; //$NON-NLS-1$//$NON-NLS-2$
IFile file = project.getFile(filename);
IScannerInfo scanInfo = provider.getScannerInformation(file);
if(scanInfo == null || scanInfo.getDefinedSymbols().isEmpty()) {
scanInfo = provider.getScannerInformation(project);
if(linkageID == ILinkage.C_LINKAGE_ID) {
final Map<String, String> definedSymbols = scanInfo.getDefinedSymbols();
definedSymbols.remove("__cplusplus__"); //$NON-NLS-1$
definedSymbols.remove("__cplusplus"); //$NON-NLS-1$
}
}
return scanInfo;
}
/**
* Convenience method for getting a RemoteScannerInfo for a resource.
*/
public static RemoteScannerInfo getScannerInfo(IResource resource) {
final IScannerInfoProvider provider = CCorePlugin.getDefault().getScannerInfoProvider(resource.getProject());
IScannerInfo scannerInfo = provider.getScannerInformation(resource);
return new RemoteScannerInfo(scannerInfo);
}
}