blob: bd2e77feee40c672c20ccbc23b916b369aac4983 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009, 2010 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.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
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.indexer.FileEncodingRegistry;
import org.eclipse.cdt.internal.core.pdom.indexer.IndexerPreferences;
import org.eclipse.cdt.utils.EFSExtensionManager;
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.IConfigurationElement;
import org.eclipse.core.runtime.IExtension;
import org.eclipse.core.runtime.IExtensionPoint;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.content.IContentType;
import org.eclipse.ptp.rdt.core.ILanguagePropertyProvider;
import org.eclipse.ptp.rdt.core.RDTLog;
import org.eclipse.ptp.rdt.core.activator.Activator;
/**
* 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 {
private static final String LANGUAGE_PROPERTIES_EXTENSION_ID = "languageProperties"; //$NON-NLS-1$
private static final String ATTR_CLASS = "class"; //$NON-NLS-1$
private static final String ATTR_ID = "id"; //$NON-NLS-1$
// These are the linkages supported by RDT
private static final int[] LINKAGE_IDS = new int[] { ILinkage.C_LINKAGE_ID, ILinkage.CPP_LINKAGE_ID };
// indexer preference keys that the remote indexer cares about
private static final String[] INDEXER_PREFERENCE_KEYS = {
//IRemoteIndexerInfoProvider.KEY_INDEX_UNUSED_HEADERS_WITH_DEFAULT_LANG,
//IRemoteIndexerInfoProvider.KEY_INDEX_UNUSED_HEADERS_WITH_ALTERNATE_LANG,
IRemoteIndexerInfoProvider.KEY_INDEX_ALL_FILES,
//IRemoteIndexerInfoProvider.KEY_INCLUDE_HEURISTICS,
IRemoteIndexerInfoProvider.KEY_SKIP_ALL_REFERENCES,
//IRemoteIndexerInfoProvider.KEY_SKIP_IMPLICIT_REFERENCES,
IRemoteIndexerInfoProvider.KEY_SKIP_TYPE_REFERENCES,
IRemoteIndexerInfoProvider.KEY_SKIP_MACRO_REFERENCES
};
private static Map<String, List<ILanguagePropertyProvider>> languagePropertyProviderMap =
new HashMap<String, List<ILanguagePropertyProvider>>();
/**
* Creates instances of ILanguagePropertyProvider from the languageProperties extension point.
* Will not activate a contributing plug-in if the language it provides properties for is never used.
*/
private static List<ILanguagePropertyProvider> getLanguagePropertyProviders(String languageId) {
List<ILanguagePropertyProvider> providers = languagePropertyProviderMap.get(languageId);
if(providers == null) {
providers = new ArrayList<ILanguagePropertyProvider>();
languagePropertyProviderMap.put(languageId, providers);
IExtensionPoint extensionPoint =
Platform.getExtensionRegistry().getExtensionPoint(Activator.PLUGIN_ID, LANGUAGE_PROPERTIES_EXTENSION_ID);
if(extensionPoint == null)
return providers;
for(IExtension extension : extensionPoint.getExtensions()) {
for(IConfigurationElement providerElement : extension.getConfigurationElements()) {
for(IConfigurationElement languageElement : providerElement.getChildren()) {
String languageIdAttr = languageElement.getAttribute(ATTR_ID);
if(languageId.equals(languageIdAttr)) {
try {
providers.add((ILanguagePropertyProvider)providerElement.createExecutableExtension(ATTR_CLASS));
} catch (CoreException e) {
RDTLog.logError(e);
}
break;
}
}
}
}
}
return providers;
}
public static Map<String, String> getLanguageProperties(String languageId, IProject project) {
List<ILanguagePropertyProvider> providers = getLanguagePropertyProviders(languageId);
Map<String,String> properties = new HashMap<String,String>();
for(ILanguagePropertyProvider provider : providers) {
Map<String,String> languageProperties = provider.getProperties(languageId, project);
if(languageProperties != null)
properties.putAll(languageProperties);
}
return properties;
}
/**
* 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>();
Set<String> headerSet = new HashSet<String>();
Map<String,Map<String,String>> languagePropertyMap = new HashMap<String, Map<String,String>>();
// 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();
FileEncodingRegistry fileEncodingRegistry = null;
try {
fileEncodingRegistry = new FileEncodingRegistry(project.getDefaultCharset());
} catch (CoreException e) {
RDTLog.logError(e);
}
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 = EFSExtensionManager.getDefault().getPathFromURI(element.getLocationURI());
scannerInfoMap.put(path, remoteScannerInfo);
// compute the language
try {
ILanguage language = tu.getLanguage();
String id = language.getId();
languageMap.put(path, id);
if(!languagePropertyMap.containsKey(id))
languagePropertyMap.put(id, getLanguageProperties(id, project));
if(fileEncodingRegistry != null){
registerFileEncoding(fileEncodingRegistry, tu, path);
}
} catch (CoreException e) {
RDTLog.logError(e);
}
if(tu.isHeaderUnit())
headerSet.add(path);
}
}
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);
Set<String> preferences = computeIndexerPreferences(props);
String filePref = (String) props.get(IndexerPreferences.KEY_FILES_TO_PARSE_UP_FRONT);
List<String> filesToParseUpFront = Arrays.asList(filePref.split("\\s*,\\s*")); //$NON-NLS-1$
// 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, languagePropertyMap,
headerSet, preferences, filesToParseUpFront, fileEncodingRegistry);
}
private static void registerFileEncoding(FileEncodingRegistry fileEncodingRegistry, ITranslationUnit tu, String filePath) throws CoreException {
IResource resource = tu.getResource();
String specificEncoding = null;
if (resource instanceof IFile) {
specificEncoding = ((IFile) resource).getCharset(false);
}
if (filePath != null && specificEncoding != null) {
fileEncodingRegistry.registerFileEncoding(filePath, specificEncoding);
}
}
private static Set<String> computeIndexerPreferences(Properties props) {
Set<String> prefs = new HashSet<String>(props.size());
for(String key : INDEXER_PREFERENCE_KEYS) {
if(Boolean.valueOf(props.getProperty(key))) {
prefs.add(key);
}
}
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$
}
}
String[] includePaths = scanInfo.getIncludePaths();
for(String include : includePaths) {
// HACK: convert backslashes to slashes to compensate for CDT bug 315632
include = include.replaceAll("\\\\", "/"); //$NON-NLS-1$ //$NON-NLS-2$
}
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);
}
}