diff --git a/codan/org.eclipse.cdt.codan.checkers/src/org/eclipse/cdt/codan/internal/checkers/AbstractClassInstantiationChecker.java b/codan/org.eclipse.cdt.codan.checkers/src/org/eclipse/cdt/codan/internal/checkers/AbstractClassInstantiationChecker.java index f07a4c9fe16..e31479df2c8 100644 --- a/codan/org.eclipse.cdt.codan.checkers/src/org/eclipse/cdt/codan/internal/checkers/AbstractClassInstantiationChecker.java +++ b/codan/org.eclipse.cdt.codan.checkers/src/org/eclipse/cdt/codan/internal/checkers/AbstractClassInstantiationChecker.java @@ -41,6 +41,7 @@ import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassType; import org.eclipse.cdt.core.dom.ast.cpp.ICPPConstructor; import org.eclipse.cdt.core.dom.ast.cpp.ICPPMethod; import org.eclipse.cdt.core.dom.ast.cpp.SemanticQueries; +import org.eclipse.cdt.core.parser.util.StringUtil; /** * Reports a problem if object of a class cannot be created because @@ -170,13 +171,7 @@ public class AbstractClassInstantiationChecker extends AbstractIndexAstChecker { private String resolveName(ICPPBinding binding) { try { if (binding.isGloballyQualified()) { - StringBuilder buf = new StringBuilder(); - for (String item : binding.getQualifiedName()) { - if (buf.length() != 0) - buf.append("::"); //$NON-NLS-1$ - buf.append(item); - } - return buf.toString(); + return StringUtil.join(binding.getQualifiedName(), "::"); //$NON-NLS-1$ } } catch (DOMException e) { CodanCheckersActivator.log(e); diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/parser/util/StringUtil.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/parser/util/StringUtil.java new file mode 100644 index 00000000000..705b6bb96ef --- /dev/null +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/parser/util/StringUtil.java @@ -0,0 +1,62 @@ +/******************************************************************************* + * Copyright (c) 2013 Google, Inc 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: + * Sergey Prigogin (Google) - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.core.parser.util; + +import java.util.Collection; + +/** + * Static methods for working with strings. + * + * @since 5.6 + */ +public class StringUtil { + + private StringUtil() {} + + /** + * Joins strings using the given delimiter. + */ + public static String join(Iterable strings, String delimiter) { + if (strings instanceof Collection) { + int size = ((Collection) strings).size(); + if (size == 1) + return strings.iterator().next(); + if (size == 0) + return ""; //$NON-NLS-1$ + } + + StringBuilder buf = new StringBuilder(); + for (String str : strings) { + if (buf.length() != 0) + buf.append(delimiter); + buf.append(str); + } + return buf.toString(); + } + + /** + * Joins strings using the given delimiter. + */ + public static String join(String[] strings, String delimiter) { + if (strings.length == 1) + return strings[0]; + if (strings.length == 0) + return ""; //$NON-NLS-1$ + + StringBuilder buf = new StringBuilder(); + for (String str : strings) { + if (buf.length() != 0) + buf.append(delimiter); + buf.append(str); + } + return buf.toString(); + } +} diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/BindingClassifierTest.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/BindingClassifierTest.java index 6c61b7098a1..644733078cb 100644 --- a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/BindingClassifierTest.java +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/BindingClassifierTest.java @@ -26,6 +26,7 @@ import org.eclipse.cdt.core.dom.ast.IBinding; import org.eclipse.cdt.core.index.IIndex; import org.eclipse.cdt.core.index.IIndexManager; import org.eclipse.cdt.core.model.ITranslationUnit; +import org.eclipse.cdt.core.parser.util.StringUtil; import org.eclipse.cdt.core.testplugin.util.OneSourceMultipleHeadersTestCase; import org.eclipse.cdt.core.testplugin.util.TestSourceReader; import org.eclipse.cdt.ui.testplugin.CTestPlugin; @@ -90,23 +91,13 @@ public class BindingClassifierTest extends OneSourceMultipleHeadersTestCase { List errors = new ArrayList(2); if (!missing.isEmpty()) { errors.add(MessageFormat.format("{0,choice,1#Binding|1 strings, String delimiter) { - StringBuilder buf = new StringBuilder(); - for (String str : strings) { - if (buf.length() != 0) - buf.append(delimiter); - buf.append(str); - } - return buf.toString(); + fail(StringUtil.join(errors, " ")); } // class A; diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/IncludeOrganizerTest.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/IncludeOrganizerTest.java index 1dbe289e9c7..7fc10f9c008 100644 --- a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/IncludeOrganizerTest.java +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/IncludeOrganizerTest.java @@ -10,6 +10,7 @@ *******************************************************************************/ package org.eclipse.cdt.ui.tests.refactoring.includes; +import java.util.Collections; import java.util.List; import junit.framework.Test; @@ -26,6 +27,7 @@ import org.eclipse.cdt.ui.PreferenceConstants; import org.eclipse.cdt.internal.ui.refactoring.includes.IHeaderChooser; import org.eclipse.cdt.internal.ui.refactoring.includes.IncludeOrganizer; import org.eclipse.cdt.internal.ui.refactoring.includes.IncludePreferences.UnusedStatementDisposition; +import org.eclipse.cdt.internal.ui.refactoring.includes.SymbolExportMap; /** * Tests for {@link IncludeOrganizer}. @@ -357,4 +359,34 @@ public class IncludeOrganizerTest extends IncludesTestBase { UnusedStatementDisposition.REMOVE.toString()); assertExpectedResults(); } + + //string.h + //#include "stddef.h" + //extern char* strchr(char* s, int c); + + //stddef.h + //#define NULL 0 + + //source.cpp + //#include "stddef.h" + //char* test() { + // int* p = NULL; + // return strchr("aaa", '*'); + //} + //==================== + //#include "string.h" + // + //char* test() { + // int* p = NULL; + // return strchr("aaa", '*'); + //} + public void testExportedSymbol() throws Exception { + IPreferenceStore preferenceStore = getPreferenceStore(); + preferenceStore.setValue(PreferenceConstants.INCLUDES_UNUSED_STATEMENTS_DISPOSITION, + UnusedStatementDisposition.REMOVE.toString()); + SymbolExportMap symbolExportMap = new SymbolExportMap(new String[] { "NULL", "string.h" }); + preferenceStore.setValue(PreferenceConstants.INCLUDES_SYMBOL_EXPORTING_HEADERS, + SymbolExportMap.serializeMaps(Collections.singletonList(symbolExportMap))); + assertExpectedResults(); + } } \ No newline at end of file diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/GCCHeaderSubstitutionMaps.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/GCCHeaderSubstitutionMaps.java index c83205b6536..b77a66a990f 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/GCCHeaderSubstitutionMaps.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/GCCHeaderSubstitutionMaps.java @@ -14,6 +14,119 @@ import java.util.Arrays; import java.util.List; public class GCCHeaderSubstitutionMaps { + @SuppressWarnings("nls") + private static final String[] symbolExportMap = new String[] { + "EOF", "", + "EOF", "", + "NULL", "", + "NULL", "", + "NULL", "", + "NULL", "", + "NULL", "", + "NULL", "", + "NULL", "", + "NULL", "", + "NULL", "", + "NULL", "", + "NULL", "", + "NULL", "", + "NULL", "", + "NULL", "", + "blkcnt_t", "", + "blkcnt_t", "", + "blksize_t", "", + "blksize_t", "", + "calloc", "", + "daddr_t", "", + "daddr_t", "", + "dev_t", "", + "dev_t", "", + "error_t", "", + "error_t", "", + "error_t", "", + "free", "", + "fsblkcnt_t", "", + "fsblkcnt_t", "", + "fsfilcnt_t", "", + "fsfilcnt_t", "", + "gid_t", "", + "gid_t", "", + "gid_t", "", + "gid_t", "", + "gid_t", "", + "gid_t", "", + "gid_t", "", + "id_t", "", + "id_t", "", + "ino64_t", "", + "ino64_t", "", + "ino_t", "", + "ino_t", "", + "ino_t", "", + "int8_t", "", + "int8_t", "", + "intptr_t", "", + "intptr_t", "", + "key_t", "", + "key_t", "", + "malloc", "", + "mode_t", "", + "mode_t", "", + "mode_t", "", + "mode_t", "", + "nlink_t", "", + "nlink_t", "", + "off64_t", "", + "off64_t", "", + "off_t", "", + "off_t", "", + "off_t", "", + "off_t", "", + "pid_t", "", + "pid_t", "", + "pid_t", "", + "pid_t", "", + "pid_t", "", + "pid_t", "", + "pid_t", "", + "pid_t", "", + "realloc", "", + "sigset_t", "", + "sigset_t", "", + "sigset_t", "", + "size_t", "", + "socklen_t", "", + "socklen_t", "", + "socklen_t", "", + "ssize_t", "", + "ssize_t", "", + "ssize_t", "", + "ssize_t", "", + "std::allocator", "", + "std::allocator", "", + "std::allocator", "", + "std::allocator", "", + "std::allocator", "", + "std::char_traits", "", + "std::char_traits", "", + "std::char_traits", "", + "suseconds_t", "", + "suseconds_t", "", + "suseconds_t", "", + "u_char", "", + "u_char", "", + "uid_t", "", + "uid_t", "", + "uid_t", "", + "uid_t", "", + "uid_t", "", + "uid_t", "", + "uid_t", "", + "useconds_t", "", + "useconds_t", "", + "va_list", "", + }; + @SuppressWarnings("nls") private static final String[] cIncludeMap = new String[] { "", "", @@ -365,4 +478,8 @@ public class GCCHeaderSubstitutionMaps { new IncludeMap(false, cppIncludeMapWeak)), }); } + + public static SymbolExportMap getSymbolExportMap() { + return new SymbolExportMap(symbolExportMap); + } } diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/HeaderSubstitutionMap.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/HeaderSubstitutionMap.java index eaa53848ad0..4982b05b619 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/HeaderSubstitutionMap.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/HeaderSubstitutionMap.java @@ -137,6 +137,20 @@ public class HeaderSubstitutionMap { return fromMemento(memento); } + public static String serializeMaps(List maps) { + XMLMemento memento = XMLMemento.createWriteRoot(TAG_HEADER_SUBSTITUTION_MAPS); + for (HeaderSubstitutionMap element : maps) { + element.saveToMemento(memento.createChild(TAG_HEADER_SUBSTITUTION_MAP)); + } + StringWriter writer = new StringWriter(); + try { + memento.save(writer); + } catch (IOException e) { + CUIPlugin.log(e); + } + return writer.toString(); + } + public static List deserializeMaps(String str) { StringReader reader = new StringReader(str); XMLMemento memento; @@ -153,20 +167,6 @@ public class HeaderSubstitutionMap { return maps; } - public static String serializeMaps(List maps) { - XMLMemento memento = XMLMemento.createWriteRoot(TAG_HEADER_SUBSTITUTION_MAPS); - for (HeaderSubstitutionMap element : maps) { - element.saveToMemento(memento.createChild(TAG_HEADER_SUBSTITUTION_MAP)); - } - StringWriter writer = new StringWriter(); - try { - memento.save(writer); - } catch (IOException e) { - CUIPlugin.log(e); - } - return writer.toString(); - } - public IncludeMap getUnconditionalSubstitutionMap() { return unconditionalSubstitutionMap; } diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/HeaderSubstitutor.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/HeaderSubstitutor.java index f9cade0f567..4a214d527ad 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/HeaderSubstitutor.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/HeaderSubstitutor.java @@ -13,6 +13,7 @@ package org.eclipse.cdt.internal.ui.refactoring.includes; import java.net.URI; import java.util.ArrayDeque; import java.util.ArrayList; +import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -35,23 +36,34 @@ import org.eclipse.cdt.internal.core.resources.ResourceLookup; public class HeaderSubstitutor { private final InclusionContext fContext; private IncludeMap[] fIncludeMaps; + private SymbolExportMap fSymbolExportMap; public HeaderSubstitutor(InclusionContext context) { fContext = context; fIncludeMaps = new IncludeMap[] { new IncludeMap(true), new IncludeMap(false) }; - IPreferencesService preferences = Platform.getPreferencesService(); - IScopeContext[] scopes = PreferenceConstants.getPreferenceScopes(context.getProject()); - String str = preferences.getString(CUIPlugin.PLUGIN_ID, - PreferenceConstants.INCLUDES_HEADER_SUBSTITUTION, null, scopes); - if (str != null) { - List maps = HeaderSubstitutionMap.deserializeMaps(str); - for (HeaderSubstitutionMap map : maps) { - if (!map.isCppOnly() || fContext.isCXXLanguage()) { - fIncludeMaps[0].addAllMappings(map.getUnconditionalSubstitutionMap()); - fIncludeMaps[1].addAllMappings(map.getOptionalSubstitutionMap()); - } - } - } + IPreferencesService preferences = Platform.getPreferencesService(); + IScopeContext[] scopes = PreferenceConstants.getPreferenceScopes(context.getProject()); + String str = preferences.getString(CUIPlugin.PLUGIN_ID, + PreferenceConstants.INCLUDES_HEADER_SUBSTITUTION, null, scopes); + if (str != null) { + List maps = HeaderSubstitutionMap.deserializeMaps(str); + for (HeaderSubstitutionMap map : maps) { + if (!map.isCppOnly() || fContext.isCXXLanguage()) { + fIncludeMaps[0].addAllMappings(map.getUnconditionalSubstitutionMap()); + fIncludeMaps[1].addAllMappings(map.getOptionalSubstitutionMap()); + } + } + } + + fSymbolExportMap = new SymbolExportMap(); + str = preferences.getString(CUIPlugin.PLUGIN_ID, + PreferenceConstants.INCLUDES_SYMBOL_EXPORTING_HEADERS, null, scopes); + if (str != null) { + List maps = SymbolExportMap.deserializeMaps(str); + for (SymbolExportMap map : maps) { + fSymbolExportMap.addAllMappings(map); + } + } } /** @@ -92,7 +104,7 @@ public class HeaderSubstitutor { IncludeInfo includeInfo = fContext.getIncludeForHeaderFile(path); if (includeInfo == null) return path; - // TODO(sprigogin): Take symbolIncludeMap into account. + // TODO(sprigogin): Take fSymbolExportMap into account. List candidates = new ArrayList(); candidates.add(includeInfo); IncludeMap[] maps = fIncludeMaps; @@ -201,6 +213,16 @@ public class HeaderSubstitutor { return request.getCandidatePaths().iterator().next(); } + /** + * Returns the set of headers exporting the given symbol. + */ + public Set getExportingHeaders(String symbol) { + Set headers = fSymbolExportMap.getMapping(symbol); + if (headers == null) + return Collections.emptySet(); + return headers; + } + /** * Returns whether the given URI points within the workspace. */ diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeOrganizer.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeOrganizer.java index 14add4372c7..036435b3f49 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeOrganizer.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeOrganizer.java @@ -894,7 +894,7 @@ public class IncludeOrganizer { // Process headers that are either indirectly included or have unique representatives. for (InclusionRequest request : requests) { - if (!request.isResolved()) { + if (!request.isResolved() && !isExportedBinding(request, headerSubstitutor)) { List candidatePaths = request.getCandidatePaths(); Set representativeHeaders = new HashSet(); boolean allRepresented = true; @@ -929,7 +929,7 @@ public class IncludeOrganizer { // Process remaining unambiguous inclusion requests. for (InclusionRequest request : requests) { - if (!request.isResolved()) { + if (!request.isResolved() && !isExportedBinding(request, headerSubstitutor)) { List candidatePaths = request.getCandidatePaths(); if (candidatePaths.size() == 1) { IPath path = candidatePaths.iterator().next(); @@ -956,8 +956,54 @@ public class IncludeOrganizer { } // Resolve ambiguous inclusion requests. + for (InclusionRequest request : requests) { + if (!request.isResolved() && !isExportedBinding(request, headerSubstitutor)) { + List candidatePaths = request.getCandidatePaths(); + for (IPath path : candidatePaths) { + if (fContext.isIncluded(path)) { + request.resolve(path); + if (DEBUG_HEADER_SUBSTITUTION) { + System.out.println(request.toString() + + (fContext.isToBeIncluded(path) ? " (decided earlier)" : " (was previously included)")); //$NON-NLS-1$ //$NON-NLS-2$ + } + break; + } + } + if (!request.isResolved()) { + IPath header = fHeaderChooser.chooseHeader(request.getBinding().getName(), candidatePaths); + if (header == null) + throw new OperationCanceledException(); + + request.resolve(header); + if (DEBUG_HEADER_SUBSTITUTION) { + System.out.println(request.toString() + " (user's choice)"); //$NON-NLS-1$ + } + if (!fContext.isAlreadyIncluded(header)) + fContext.addHeaderToInclude(header); + } + } + } + + // Resolve requests for exported symbols. for (InclusionRequest request : requests) { if (!request.isResolved()) { + Set exportingHeaders = getExportingHeaders(request, headerSubstitutor); + for (IncludeInfo header : exportingHeaders) { + IPath path = fContext.resolveInclude(header); + if (path != null) { + if (fContext.isIncluded(path)) { + request.resolve(path); + if (DEBUG_HEADER_SUBSTITUTION) { + System.out.println(request.toString() + + (fContext.isToBeIncluded(path) ? " (decided earlier)" : " (was previously included)")); //$NON-NLS-1$ //$NON-NLS-2$ + } + break; + } + } + } + if (request.isResolved()) + continue; + List candidatePaths = request.getCandidatePaths(); for (IPath path : candidatePaths) { if (fContext.isIncluded(path)) { @@ -988,6 +1034,17 @@ public class IncludeOrganizer { fContext.removeExportedHeaders(); } + private boolean isExportedBinding(InclusionRequest request, HeaderSubstitutor headerSubstitutor) { + return !getExportingHeaders(request, headerSubstitutor).isEmpty(); + } + + private Set getExportingHeaders(InclusionRequest request, HeaderSubstitutor headerSubstitutor) { + String symbol = request.getBindingQualifiedName(); + if (symbol == null) + return Collections.emptySet(); + return headerSubstitutor.getExportingHeaders(symbol); + } + private static IPath getPath(IIndexFileLocation location) { return IndexLocationFactory.getAbsolutePath(location); } diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludePreferences.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludePreferences.java index 027fdea6c1f..f10e4947b5b 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludePreferences.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludePreferences.java @@ -174,5 +174,7 @@ public class IncludePreferences { store.setDefault(PreferenceConstants.INCLUDES_HEADER_SUBSTITUTION, HeaderSubstitutionMap.serializeMaps(GCCHeaderSubstitutionMaps.getDefaultMaps())); + store.setDefault(PreferenceConstants.INCLUDES_SYMBOL_EXPORTING_HEADERS, + SymbolExportMap.serializeMaps(Collections.singletonList(GCCHeaderSubstitutionMaps.getSymbolExportMap()))); } } diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/InclusionRequest.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/InclusionRequest.java index 96fe7165ef3..eadeea46d2d 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/InclusionRequest.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/InclusionRequest.java @@ -18,15 +18,22 @@ import java.util.Map; import org.eclipse.core.runtime.IPath; +import org.eclipse.cdt.core.dom.ast.DOMException; import org.eclipse.cdt.core.dom.ast.IBinding; +import org.eclipse.cdt.core.dom.ast.IMacroBinding; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPBinding; import org.eclipse.cdt.core.index.IIndexFile; +import org.eclipse.cdt.core.parser.util.StringUtil; class InclusionRequest { + private static final String UNINITIALIZED = "uninitialized"; //$NON-NLS-1$ + private final IBinding fBinding; private final Map fDeclaringFiles; private final boolean fReachable; private List fCandidatePaths; private IPath fResolvedPath; + private String fQualifiedName = UNINITIALIZED; /** * @param binding the binding that requires inclusion @@ -45,7 +52,30 @@ class InclusionRequest { public IBinding getBinding() { return fBinding; } - + + /** + * Returns the qualified name of the binding, or {@code null} if the binding doesn't have + * a qualified name. + */ + public String getBindingQualifiedName() { + if (fQualifiedName == UNINITIALIZED) { + fQualifiedName = null; + if (fBinding instanceof ICPPBinding) { + ICPPBinding cppBinding = (ICPPBinding) fBinding; + try { + if (cppBinding.isGloballyQualified()) { + fQualifiedName = StringUtil.join(cppBinding.getQualifiedName(), "::"); //$NON-NLS-1$ + } + } catch (DOMException e) { + // Leave null; + } + } else if (fBinding instanceof IMacroBinding || fBinding.getOwner() == null) { + fQualifiedName = fBinding.getName(); + } + } + return fQualifiedName; + } + public Map getDeclaringFiles() { return fDeclaringFiles; } diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/SymbolExportMap.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/SymbolExportMap.java new file mode 100644 index 00000000000..74cfe9ba92a --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/SymbolExportMap.java @@ -0,0 +1,211 @@ +/******************************************************************************* + * Copyright (c) 2013 Google, Inc 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: + * Sergey Prigogin (Google) - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.ui.refactoring.includes; + +import java.io.IOException; +import java.io.StringReader; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import org.eclipse.ui.IMemento; +import org.eclipse.ui.WorkbenchException; +import org.eclipse.ui.XMLMemento; + +import org.eclipse.cdt.ui.CUIPlugin; + +/** + * A set of header file substitution rules. + */ +public class SymbolExportMap { + private static final String TAG_SYMBOL_EXPORT_MAPS = "maps"; //$NON-NLS-1$ + private static final String TAG_SYMBOL_EXPORT_MAP = "map"; //$NON-NLS-1$ + private static final String TAG_MAPPING = "mapping"; //$NON-NLS-1$ + private static final String TAG_KEY = "key"; //$NON-NLS-1$ + private static final String TAG_VALUE = "value"; //$NON-NLS-1$ + + private final Map> map; + + public SymbolExportMap() { + this.map = new HashMap>(); + } + + /** + * @param keysAndValues an array of keys and values: [key1, value1, key2, value2, ...]. + * Keys and values may be optionally surrounded by double quotes or angle brackets. + * Angle brackets indicate a system include. + */ + public SymbolExportMap(String[] keysAndValues) { + if (keysAndValues.length % 2 != 0) + throw new IllegalArgumentException("More keys than values"); //$NON-NLS-1$ + this.map = new HashMap>(keysAndValues.length / 2); + for (int i = 0; i < keysAndValues.length;) { + String key = keysAndValues[i++]; + addMapping(key, keysAndValues[i++]); + } + } + + public SymbolExportMap(SymbolExportMap other) { + this.map = new HashMap>(other.map.size()); + addAllMappings(other); + } + + /** + * Indicates that the given symbol is exported by the given header. + + * @param symbol The symbol represented by its fully qualified name. + * @param header The header file exporting the symbol. + */ + protected void addMapping(String symbol, IncludeInfo header) { + if (symbol.equals(header)) + return; // Don't allow mapping to itself. + Set list = map.get(symbol); + if (list == null) { + list = new LinkedHashSet(); + map.put(symbol, list); + } + list.add(header); + } + + /** + * Indicates that the given symbol is exported by the given header. + + * @param symbol The symbol represented by its fully qualified name. + * @param header The header file exporting the symbol. The header is represented by an include + * name optionally surrounded by double quotes or angle brackets. Angle brackets indicate + * a system include. + */ + public void addMapping(String symbol, String header) { + addMapping(symbol, new IncludeInfo(header)); + } + + /** + * Returns header files that should be used instead of the given one. + * + * @param from The header file to be replaced. A system header has to match exactly. + * A non-system header matches both, non-system and system headers. + * @return The list of header files ordered by decreasing preference. + */ + public Set getMapping(String from) { + Set list = map.get(from); + if (list == null) + return Collections.emptySet(); + return list; + } + + /** + * Removes exporting headers for a given symbol. + * + * @param symbol the header file to remove exporting headers for + * @return the previous header associated with the symbol, or {@code null} if there were no + * exporting headers. + */ + public Set removeMapping(String symbol) { + return map.remove(symbol); + } + + /** + * Writes the map to a memento. + */ + public void saveToMemento(IMemento memento) { + for (Entry> entry : map.entrySet()) { + String key = entry.getKey().toString(); + for (IncludeInfo value : entry.getValue()) { + IMemento mapping = memento.createChild(TAG_MAPPING); + mapping.putString(TAG_KEY, key); + mapping.putString(TAG_VALUE, value.toString()); + } + } + } + + public static SymbolExportMap fromMemento(IMemento memento) { + SymbolExportMap includeMap = new SymbolExportMap(); + for (IMemento mapping : memento.getChildren(TAG_MAPPING)) { + String key = mapping.getString(TAG_KEY); + includeMap.addMapping(key, mapping.getString(TAG_VALUE)); + } + return includeMap; + } + + public void addAllMappings(SymbolExportMap other) { + for (Entry> entry : other.map.entrySet()) { + String source = entry.getKey(); + Set otherTargets = entry.getValue(); + Set targets = map.get(source); + if (targets == null) { + targets = new LinkedHashSet(otherTargets); + map.put(source, targets); + } else { + targets.addAll(otherTargets); + } + } + } + + /** For debugging only. */ + @Override + public String toString() { + StringBuilder buf = new StringBuilder(); + ArrayList symbols = new ArrayList(map.keySet()); + Collections.sort(symbols); + for (String symbol : symbols) { + buf.append('\n'); + buf.append(symbol); + buf.append(" exported by "); //$NON-NLS-1$ + List targets = new ArrayList(map.get(symbol)); + for (int i = 0; i < targets.size(); i++) { + if (i > 0) + buf.append(", "); //$NON-NLS-1$ + buf.append(targets.get(i)); + } + } + return buf.toString(); + } + + public Map> getMap() { + return Collections.unmodifiableMap(map); + } + + public static String serializeMaps(List maps) { + XMLMemento memento = XMLMemento.createWriteRoot(TAG_SYMBOL_EXPORT_MAPS); + for (SymbolExportMap element : maps) { + element.saveToMemento(memento.createChild(TAG_SYMBOL_EXPORT_MAP)); + } + StringWriter writer = new StringWriter(); + try { + memento.save(writer); + } catch (IOException e) { + CUIPlugin.log(e); + } + return writer.toString(); + } + + public static List deserializeMaps(String str) { + StringReader reader = new StringReader(str); + XMLMemento memento; + try { + memento = XMLMemento.createReadRoot(reader); + } catch (WorkbenchException e) { + return Collections.emptyList(); + } + + List maps = new ArrayList(); + for (IMemento element : memento.getChildren(TAG_SYMBOL_EXPORT_MAP)) { + maps.add(fromMemento(element)); + } + return maps; + } +} \ No newline at end of file diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/PreferenceConstants.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/PreferenceConstants.java index 3c43d789754..c9e44edfd22 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/PreferenceConstants.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/PreferenceConstants.java @@ -2064,6 +2064,15 @@ public class PreferenceConstants { */ public static final String INCLUDES_HEADER_SUBSTITUTION = "organizeIncludes.headerSubstitution"; //$NON-NLS-1$ + /** + * Symbol exporting rules. + * The value of the preference is an XML representation of one or more + * {@link org.eclipse.cdt.internal.ui.refactoring.includes.SymbolExportMap}s. + * + * @since 5.7 + */ + public static final String INCLUDES_SYMBOL_EXPORTING_HEADERS = "organizeIncludes.symbolExportingHeaders"; //$NON-NLS-1$ + /** * Include style for headers closely related to the including file. * The value of the preference is an XML representation of