From 9522ff887e5da816efd121a22cc38a9145e07ca6 Mon Sep 17 00:00:00 2001 From: Mike Kucera Date: Mon, 27 May 2013 14:22:22 -0400 Subject: Bug 409173 - cannot navigate from unused header to another header in the project --- .../LanguageSettingsScannerInfoProvider_Hack.java | 382 +++++++++++++++++++++ .../rdt/core/RemoteIndexerInfoProviderFactory.java | 15 +- 2 files changed, 393 insertions(+), 4 deletions(-) create mode 100644 rdt/org.eclipse.ptp.rdt.core/src/org/eclipse/ptp/internal/rdt/core/LanguageSettingsScannerInfoProvider_Hack.java diff --git a/rdt/org.eclipse.ptp.rdt.core/src/org/eclipse/ptp/internal/rdt/core/LanguageSettingsScannerInfoProvider_Hack.java b/rdt/org.eclipse.ptp.rdt.core/src/org/eclipse/ptp/internal/rdt/core/LanguageSettingsScannerInfoProvider_Hack.java new file mode 100644 index 000000000..46480efc7 --- /dev/null +++ b/rdt/org.eclipse.ptp.rdt.core/src/org/eclipse/ptp/internal/rdt/core/LanguageSettingsScannerInfoProvider_Hack.java @@ -0,0 +1,382 @@ +/******************************************************************************* + * Copyright (c) 2010, 2013 Andrew Gvozdev 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: + * Andrew Gvozdev - initial API and implementation + *******************************************************************************/ + +package org.eclipse.ptp.internal.rdt.core; + +import java.net.URI; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.Vector; + +import org.eclipse.cdt.core.CCorePlugin; +import org.eclipse.cdt.core.cdtvariables.CdtVariableException; +import org.eclipse.cdt.core.cdtvariables.ICdtVariableManager; +import org.eclipse.cdt.core.language.settings.providers.ILanguageSettingsChangeEvent; +import org.eclipse.cdt.core.language.settings.providers.ILanguageSettingsChangeListener; +import org.eclipse.cdt.core.language.settings.providers.LanguageSettingsManager; +import org.eclipse.cdt.core.parser.ExtendedScannerInfo; +import org.eclipse.cdt.core.parser.IScannerInfo; +import org.eclipse.cdt.core.parser.IScannerInfoChangeListener; +import org.eclipse.cdt.core.parser.IScannerInfoProvider; +import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; +import org.eclipse.cdt.core.settings.model.ICLanguageSettingEntry; +import org.eclipse.cdt.core.settings.model.ICMacroEntry; +import org.eclipse.cdt.core.settings.model.ICPathEntry; +import org.eclipse.cdt.core.settings.model.ICProjectDescription; +import org.eclipse.cdt.core.settings.model.ICSettingEntry; +import org.eclipse.cdt.core.settings.model.util.CDataUtil; +import org.eclipse.cdt.internal.core.language.settings.providers.LanguageSettingsProvidersSerializer; +import org.eclipse.cdt.internal.core.settings.model.CProjectDescriptionManager; +import org.eclipse.cdt.internal.core.settings.model.SettingsModelMessages; +import org.eclipse.cdt.utils.EFSExtensionManager; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Status; +import org.eclipse.osgi.util.NLS; + +/** + * This class is a massive hack to work around a problem in CDT. + * + */ +public class LanguageSettingsScannerInfoProvider_Hack implements IScannerInfoProvider, ILanguageSettingsChangeListener { + private static final String FRAMEWORK_PRIVATE_HEADERS_INCLUDE = "/__framework__.framework/PrivateHeaders/__header__"; //$NON-NLS-1$ + private static final String FRAMEWORK_HEADERS_INCLUDE = "/__framework__.framework/Headers/__header__"; //$NON-NLS-1$ + private static final ExtendedScannerInfo DUMMY_SCANNER_INFO = new ExtendedScannerInfo(); + + private static List C_IDS = Arrays.asList("org.eclipse.cdt.core.gcc", "org.eclipse.cdt.core.lrparser.xlc.c"); //$NON-NLS-1$ //$NON-NLS-2$ + private static List CPP_IDS = Arrays.asList("org.eclipse.cdt.core.g++", "org.eclipse.cdt.core.lrparser.xlc.cpp"); //$NON-NLS-1$ //$NON-NLS-2$ + + + + private Map> listenersMap = null; + + public ExtendedScannerInfo getScannerInformation(IResource rc) { + IProject project = rc.getProject(); + if (project==null) + return DUMMY_SCANNER_INFO; + + ICProjectDescription prjDescription = CProjectDescriptionManager.getInstance().getProjectDescription(project, false); + if (prjDescription==null) + return DUMMY_SCANNER_INFO; + + ICConfigurationDescription cfgDescription = prjDescription.getDefaultSettingConfiguration(); + if (cfgDescription==null) + return DUMMY_SCANNER_INFO; + + List languageIds = LanguageSettingsManager.getLanguages(rc, cfgDescription); + + // This part is a hack to add more language Ids, if the file is gnu it will add xlc and vice versa + Set allLanguageIds = new HashSet(); + allLanguageIds.addAll(languageIds); + for(String id : languageIds) { + if(CPP_IDS.contains(id)) + allLanguageIds.addAll(CPP_IDS); + else if(C_IDS.contains(id)) + allLanguageIds.addAll(C_IDS); + } + languageIds = new ArrayList(allLanguageIds); + + if (languageIds.isEmpty()) { + String msg = NLS.bind(SettingsModelMessages.getString("LanguageSettingsScannerInfoProvider.UnableToDetermineLanguage"), rc.toString()); //$NON-NLS-1$ + IStatus status = new Status(IStatus.WARNING, CCorePlugin.PLUGIN_ID, msg, new Exception()); + CCorePlugin.log(status); + return DUMMY_SCANNER_INFO; + } + + LinkedHashSet includePathEntries = new LinkedHashSet(); + LinkedHashSet includePathLocalEntries = new LinkedHashSet(); + LinkedHashSet includeFileEntries = new LinkedHashSet(); + LinkedHashSet macroFileEntries = new LinkedHashSet(); + LinkedHashSet macroEntries = new LinkedHashSet(); + + + for (String langId : languageIds) { + List incSys = LanguageSettingsProvidersSerializer.getSystemSettingEntriesByKind(cfgDescription, rc, langId, + ICSettingEntry.INCLUDE_PATH); + includePathEntries.addAll(incSys); + + List incLocal = LanguageSettingsProvidersSerializer.getLocalSettingEntriesByKind(cfgDescription, rc, langId, + ICSettingEntry.INCLUDE_PATH); + includePathLocalEntries.addAll(incLocal); + + List incFiles = LanguageSettingsProvidersSerializer.getSettingEntriesByKind(cfgDescription, rc, langId, + ICSettingEntry.INCLUDE_FILE); + includeFileEntries.addAll(incFiles); + + List macroFiles = LanguageSettingsProvidersSerializer.getSettingEntriesByKind(cfgDescription, rc, langId, + ICSettingEntry.MACRO_FILE); + macroFileEntries.addAll(macroFiles); + + List macros = LanguageSettingsProvidersSerializer.getSettingEntriesByKind(cfgDescription, rc, langId, + ICSettingEntry.MACRO); + macroEntries.addAll(macros); + } + + String[] includePaths = convertToLocations(includePathEntries, cfgDescription); + String[] includePathsLocal = convertToLocations(includePathLocalEntries, cfgDescription); + String[] includeFiles = convertToLocations(includeFileEntries, cfgDescription); + String[] macroFiles = convertToLocations(macroFileEntries, cfgDescription); + + Map definedMacros = new HashMap(); + for (ICLanguageSettingEntry entry : macroEntries) { + ICMacroEntry macroEntry = (ICMacroEntry)entry; + String name = macroEntry.getName(); + String value = macroEntry.getValue(); + definedMacros.put(name, value); + } + + return new ExtendedScannerInfo(definedMacros, includePaths, macroFiles, includeFiles, includePathsLocal); + } + + private String expandVariables(String pathStr, ICConfigurationDescription cfgDescription) { + try { + ICdtVariableManager varManager = CCorePlugin.getDefault().getCdtVariableManager(); + pathStr = varManager.resolveValue(pathStr, "", null, cfgDescription); //$NON-NLS-1$ + } catch (Exception e) { + // Swallow exceptions but also log them + CCorePlugin.log(e); + } + return pathStr; + } + + /** + * Get build working directory for the provided configuration. Returns + * project location if none defined. + */ + private static IPath getBuildCWD(ICConfigurationDescription cfgDescription) { + IPath buildCWD = cfgDescription.getBuildSetting().getBuilderCWD(); + if (buildCWD == null) { + IProject project = cfgDescription.getProjectDescription().getProject(); + buildCWD = project.getLocation(); + } else { + ICdtVariableManager mngr = CCorePlugin.getDefault().getCdtVariableManager(); + try { + // Note that IPath buildCWD holding variables is mis-constructed, + // i.e. ${workspace_loc:/path} gets split into 2 path segments + // still, MBS does that and we need to handle that + String buildPathString = buildCWD.toString(); + buildPathString = mngr.resolveValue(buildPathString, "", null, cfgDescription); //$NON-NLS-1$ + if (!buildPathString.isEmpty()) { + buildCWD = new Path(buildPathString); + } else { + IProject project = cfgDescription.getProjectDescription().getProject(); + URI locationURI = project.getLocationURI(); + String path = EFSExtensionManager.getDefault().getPathFromURI(locationURI); + buildCWD = new Path(path); + } + } catch (CdtVariableException e) { + CCorePlugin.log(e); + } + + } + buildCWD = buildCWD.addTrailingSeparator(); + return buildCWD; + } + + /** + * Resolve location to file system location in a configuration context. + * Resolving includes replacing build/environment variables with values, making relative path absolute etc. + * + * @param location - location to resolve. If relative, it is taken to be rooted in build working directory. + * @param cfgDescription - the configuration context. + * @return resolved file system location. + */ + private static String resolveEntry(String location, ICConfigurationDescription cfgDescription) { + // Substitute build/environment variables + ICdtVariableManager varManager = CCorePlugin.getDefault().getCdtVariableManager(); + try { + location = varManager.resolveValue(location, "", null, cfgDescription); //$NON-NLS-1$ + } catch (CdtVariableException e) { + // Swallow exceptions but also log them + CCorePlugin.log(e); + } + // use OS file separators (i.e. '\' on Windows) + if (java.io.File.separatorChar != IPath.SEPARATOR) { + location = location.replace(IPath.SEPARATOR, java.io.File.separatorChar); + } + + IPath locPath = new Path(location); + if (locPath.isAbsolute() && locPath.getDevice() == null) { + IPath buildCWD = getBuildCWD(cfgDescription); + // prepend device (C:) for Windows + String device = buildCWD.getDevice(); + if (device != null) { + // note that we avoid using org.eclipse.core.runtime.Path for manipulations being careful + // to preserve "../" segments and not let collapsing them which is not correct for symbolic links. + location = device + location; + } + } + + if (!locPath.isAbsolute()) { + // consider relative path to be from build working directory + IPath buildCWD = getBuildCWD(cfgDescription); + // again, we avoid using org.eclipse.core.runtime.Path for manipulations being careful + // to preserve "../" segments and not let collapsing them which is not correct for symbolic links. + location = buildCWD.addTrailingSeparator().toOSString() + location; + } + return location; + } + + /** + * Convert path delimiters to OS representation avoiding using org.eclipse.core.runtime.Path + * being careful to preserve "../" segments and not let collapsing them which is not correct for symbolic links. + */ + private String toOSString(String loc) { + // use OS file separators (i.e. '\' on Windows) + if (java.io.File.separatorChar != IPath.SEPARATOR) { + loc = loc.replace(IPath.SEPARATOR, java.io.File.separatorChar); + } + return loc; + } + + /** + * Convert the path entries to absolute file system locations represented as String array. + * Resolve the entries which are not resolved. + * + * @param entriesPath - language settings path entries. + * @param cfgDescription - configuration description for resolving entries. + * @return array of the locations. + */ + private String[] convertToLocations(LinkedHashSet entriesPath, ICConfigurationDescription cfgDescription) { + List locations = new ArrayList(entriesPath.size()); + for (ICLanguageSettingEntry entry : entriesPath) { + ICPathEntry entryPath = (ICPathEntry)entry; + if (entryPath.isValueWorkspacePath()) { + ICLanguageSettingEntry[] entries = new ICLanguageSettingEntry[] {entry}; + if (!entry.isResolved()) { + entries = CDataUtil.resolveEntries(entries, cfgDescription); + } + + for (ICLanguageSettingEntry resolved : entries) { + IPath loc = ((ICPathEntry) resolved).getLocation(); + if (loc != null) { + if (checkBit(resolved.getFlags(), ICSettingEntry.FRAMEWORKS_MAC)) { + // handle frameworks, see IScannerInfo.getIncludePaths() + locations.add(loc.append(FRAMEWORK_HEADERS_INCLUDE).toOSString()); + locations.add(loc.append(FRAMEWORK_PRIVATE_HEADERS_INCLUDE).toOSString()); + } else { + locations.add(loc.toOSString()); + } + } + } + } else { + // have to use getName() rather than getLocation() to avoid collapsing ".." + String loc = entryPath.getName(); + if (entryPath.isResolved()) { + locations.add(loc); + } else { + loc = resolveEntry(loc, cfgDescription); + if (loc != null) { + if (checkBit(entryPath.getFlags(), ICSettingEntry.FRAMEWORKS_MAC)) { + // handle frameworks, see IScannerInfo.getIncludePaths() + locations.add(toOSString(loc + FRAMEWORK_HEADERS_INCLUDE)); + locations.add(toOSString(loc + FRAMEWORK_PRIVATE_HEADERS_INCLUDE)); + } else { + locations.add(toOSString(loc)); + String unresolvedPath = entryPath.getName(); + if (!new Path(unresolvedPath).isAbsolute()) { + // add relative paths again for indexer to resolve from source file location + String expandedPath = expandVariables(unresolvedPath, cfgDescription); + if (!expandedPath.isEmpty() && !new Path(expandedPath).isAbsolute()) { + locations.add(toOSString(expandedPath)); + } + } + } + } + } + } + } + + return locations.toArray(new String[locations.size()]); + } + + private static boolean checkBit(int flags, int bit) { + return (flags & bit) == bit; + } + + public void subscribe(IResource resource, IScannerInfoChangeListener listener) { + if (resource == null || listener == null) { + return; + } + + if (listenersMap == null) { + listenersMap = Collections.synchronizedMap(new HashMap>()); + } + + IProject project = resource.getProject(); + List list = listenersMap.get(project); + if (list == null) { + list = new Vector(); + listenersMap.put(project, list); + } + if (!list.contains(listener)) { + list.add(listener); + } + } + + public void unsubscribe(IResource resource, IScannerInfoChangeListener listener) { + if (resource == null || listener == null) { + return; + } + + IProject project = resource.getProject(); + if (listenersMap != null) { + List list = listenersMap.get(project); + if (list != null) { + list.remove(listener); + } + } + } + + public void handleEvent(ILanguageSettingsChangeEvent event) { + if (listenersMap == null || listenersMap.isEmpty()) { + return; + } + + IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(event.getProjectName()); + if (project != null) { + ICProjectDescription prjDescription = CCorePlugin.getDefault().getProjectDescription(project); + if (prjDescription != null) { + ICConfigurationDescription indexedCfgDescription = prjDescription.getDefaultSettingConfiguration(); + String indexedCfgId = indexedCfgDescription.getId(); + + for (String cfgId : event.getConfigurationDescriptionIds()) { + if (cfgId.equals(indexedCfgId)) { + for (Entry> entry : listenersMap.entrySet()) { + IResource rc = entry.getKey(); + List listeners = listenersMap.get(rc); + if (listeners != null && !listeners.isEmpty()) { + IScannerInfo info = getScannerInformation(rc); + for (IScannerInfoChangeListener listener : listeners) { + listener.changeNotification(rc, info); + } + } + } + break; + } + } + + } + } + } +} diff --git a/rdt/org.eclipse.ptp.rdt.core/src/org/eclipse/ptp/internal/rdt/core/RemoteIndexerInfoProviderFactory.java b/rdt/org.eclipse.ptp.rdt.core/src/org/eclipse/ptp/internal/rdt/core/RemoteIndexerInfoProviderFactory.java index 20f9562fc..c63135924 100644 --- a/rdt/org.eclipse.ptp.rdt.core/src/org/eclipse/ptp/internal/rdt/core/RemoteIndexerInfoProviderFactory.java +++ b/rdt/org.eclipse.ptp.rdt.core/src/org/eclipse/ptp/internal/rdt/core/RemoteIndexerInfoProviderFactory.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2009, 2012 IBM Corporation and others. + * Copyright (c) 2009, 2013 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 @@ -229,6 +229,13 @@ public class RemoteIndexerInfoProviderFactory { } + private static IScannerInfoProvider getLocalScannerInfoProvider(IProject project) { + // return CCorePlugin.getDefault().getScannerInfoProvider(project); + // Warning: this is here to work around a defect in CDT + return new LanguageSettingsScannerInfoProvider_Hack(); + } + + /** * Returns a RemoteIndexerInfoProvider that contains IScannerInfos for every @@ -252,7 +259,7 @@ public class RemoteIndexerInfoProviderFactory { // we assume all the elements are from the same project IProject project = elements.get(0).getCProject().getProject(); - IScannerInfoProvider provider = CCorePlugin.getDefault().getScannerInfoProvider(project); + IScannerInfoProvider provider = getLocalScannerInfoProvider(project); RemoteScannerInfoCache cache = new RemoteScannerInfoCache(); @@ -391,7 +398,7 @@ public class RemoteIndexerInfoProviderFactory { * This code was copied from PDOMIndexerTask.createDefaultScannerConfig(int) */ private static IScannerInfo getDefaultScannerInfo(IProject project, int linkageID) { - IScannerInfoProvider provider = CCorePlugin.getDefault().getScannerInfoProvider(project); + IScannerInfoProvider provider = getLocalScannerInfoProvider(project); if(provider == null) return null; @@ -421,7 +428,7 @@ public class RemoteIndexerInfoProviderFactory { * Convenience method for getting a RemoteScannerInfo for a resource. */ public static RemoteScannerInfo getScannerInfo(IResource resource) { - final IScannerInfoProvider provider = CCorePlugin.getDefault().getScannerInfoProvider(resource.getProject()); + final IScannerInfoProvider provider = getLocalScannerInfoProvider(resource.getProject()); IScannerInfo scannerInfo = provider.getScannerInformation(resource); return new RemoteScannerInfo(scannerInfo); } -- cgit v1.2.3