1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-07-23 17:05:26 +02:00

Bug 45203. Support for symbols exported by multiple headers.

This commit is contained in:
Sergey Prigogin 2013-08-02 13:52:42 -07:00
parent 2cb5f8a29a
commit 983f7529a3
12 changed files with 579 additions and 51 deletions

View file

@ -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);

View file

@ -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<String> strings, String delimiter) {
if (strings instanceof Collection) {
int size = ((Collection<String>) 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();
}
}

View file

@ -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<String> errors = new ArrayList<String>(2);
if (!missing.isEmpty()) {
errors.add(MessageFormat.format("{0,choice,1#Binding|1<Bindings} \"{1}\" {0,choice,1#is|1<are} not {2}.",
missing.size(), join(missing, "\", \""), verb));
missing.size(), StringUtil.join(missing, "\", \""), verb));
}
if (!extra.isEmpty()) {
errors.add(MessageFormat.format("{0,choice,1#Binding|1<Bindings} \"{1}\" should not be {2}.",
extra.size(), join(extra, "\", \""), verb));
extra.size(), StringUtil.join(extra, "\", \""), verb));
}
fail(join(errors, " "));
}
private String join(Iterable<String> 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;

View file

@ -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();
}
}

View file

@ -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", "<stdio.h>",
"EOF", "<libio.h>",
"NULL", "<stddef.h>",
"NULL", "<cstddef>",
"NULL", "<cstdio>",
"NULL", "<cstdlib>",
"NULL", "<cstring>",
"NULL", "<ctime>",
"NULL", "<cwchar>",
"NULL", "<string>",
"NULL", "<locale.h>",
"NULL", "<stdio.h>",
"NULL", "<stdlib.h>",
"NULL", "<string.h>",
"NULL", "<time.h>",
"NULL", "<wchar.h>",
"blkcnt_t", "<sys/stat.h>",
"blkcnt_t", "<sys/types.h>",
"blksize_t", "<sys/types.h>",
"blksize_t", "<sys/stat.h>",
"calloc", "<stdlib.h>",
"daddr_t", "<sys/types.h>",
"daddr_t", "<rpc/types.h>",
"dev_t", "<sys/types.h>",
"dev_t", "<sys/stat.h>",
"error_t", "<errno.h>",
"error_t", "<argp.h>",
"error_t", "<argz.h>",
"free", "<stdlib.h>",
"fsblkcnt_t", "<sys/types.h>",
"fsblkcnt_t", "<sys/statvfs.h>",
"fsfilcnt_t", "<sys/types.h>",
"fsfilcnt_t", "<sys/statvfs.h>",
"gid_t", "<sys/types.h>",
"gid_t", "<grp.h>",
"gid_t", "<pwd.h>",
"gid_t", "<stropts.h>",
"gid_t", "<sys/ipc.h>",
"gid_t", "<sys/stat.h>",
"gid_t", "<unistd.h>",
"id_t", "<sys/types.h>",
"id_t", "<sys/resource.h>",
"ino64_t", "<sys/types.h>",
"ino64_t", "<dirent.h>",
"ino_t", "<sys/types.h>",
"ino_t", "<dirent.h>",
"ino_t", "<sys/stat.h>",
"int8_t", "<sys/types.h>",
"int8_t", "<stdint.h>",
"intptr_t", "<stdint.h>",
"intptr_t", "<unistd.h>",
"key_t", "<sys/types.h>",
"key_t", "<sys/ipc.h>",
"malloc", "<stdlib.h>",
"mode_t", "<sys/types.h>",
"mode_t", "<sys/stat.h>",
"mode_t", "<sys/ipc.h>",
"mode_t", "<sys/mman.h>",
"nlink_t", "<sys/types.h>",
"nlink_t", "<sys/stat.h>",
"off64_t", "<sys/types.h>",
"off64_t", "<unistd.h>",
"off_t", "<sys/types.h>",
"off_t", "<unistd.h>",
"off_t", "<sys/stat.h>",
"off_t", "<sys/mman.h>",
"pid_t", "<sys/types.h>",
"pid_t", "<unistd.h>",
"pid_t", "<signal.h>",
"pid_t", "<sys/msg.h>",
"pid_t", "<sys/shm.h>",
"pid_t", "<termios.h>",
"pid_t", "<time.h>",
"pid_t", "<utmpx.h>",
"realloc", "<stdlib.h>",
"sigset_t", "<signal.h>",
"sigset_t", "<sys/epoll.h>",
"sigset_t", "<sys/select.h>",
"size_t", "<stddef.h>",
"socklen_t", "<bits/socket.h>",
"socklen_t", "<unistd.h>",
"socklen_t", "<arpa/inet.h>",
"ssize_t", "<sys/types.h>",
"ssize_t", "<unistd.h>",
"ssize_t", "<monetary.h>",
"ssize_t", "<sys/msg.h>",
"std::allocator", "<memory>",
"std::allocator", "<string>",
"std::allocator", "<vector>",
"std::allocator", "<map>",
"std::allocator", "<set>",
"std::char_traits", "<string>",
"std::char_traits", "<ostream>",
"std::char_traits", "<istream>",
"suseconds_t", "<sys/types.h>",
"suseconds_t", "<sys/time.h>",
"suseconds_t", "<sys/select.h>",
"u_char", "<sys/types.h>",
"u_char", "<rpc/types.h>",
"uid_t", "<sys/types.h>",
"uid_t", "<unistd.h>",
"uid_t", "<pwd.h>",
"uid_t", "<signal.h>",
"uid_t", "<stropts.h>",
"uid_t", "<sys/ipc.h>",
"uid_t", "<sys/stat.h>",
"useconds_t", "<sys/types.h>",
"useconds_t", "<unistd.h>",
"va_list", "<stdarg.h>",
};
@SuppressWarnings("nls")
private static final String[] cIncludeMap = new String[] {
"<asm/errno-base.h>", "<errno.h>",
@ -365,4 +478,8 @@ public class GCCHeaderSubstitutionMaps {
new IncludeMap(false, cppIncludeMapWeak)),
});
}
public static SymbolExportMap getSymbolExportMap() {
return new SymbolExportMap(symbolExportMap);
}
}

View file

@ -137,6 +137,20 @@ public class HeaderSubstitutionMap {
return fromMemento(memento);
}
public static String serializeMaps(List<HeaderSubstitutionMap> 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<HeaderSubstitutionMap> deserializeMaps(String str) {
StringReader reader = new StringReader(str);
XMLMemento memento;
@ -153,20 +167,6 @@ public class HeaderSubstitutionMap {
return maps;
}
public static String serializeMaps(List<HeaderSubstitutionMap> 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;
}

View file

@ -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<HeaderSubstitutionMap> 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<HeaderSubstitutionMap> 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<SymbolExportMap> 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<IncludeInfo> candidates = new ArrayList<IncludeInfo>();
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<IncludeInfo> getExportingHeaders(String symbol) {
Set<IncludeInfo> headers = fSymbolExportMap.getMapping(symbol);
if (headers == null)
return Collections.emptySet();
return headers;
}
/**
* Returns whether the given URI points within the workspace.
*/

View file

@ -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<IPath> candidatePaths = request.getCandidatePaths();
Set<IPath> representativeHeaders = new HashSet<IPath>();
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<IPath> 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<IPath> 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<IncludeInfo> 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<IPath> 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<IncludeInfo> 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);
}

View file

@ -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())));
}
}

View file

@ -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<IIndexFile, IPath> fDeclaringFiles;
private final boolean fReachable;
private List<IPath> 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<IIndexFile, IPath> getDeclaringFiles() {
return fDeclaringFiles;
}

View file

@ -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<String, Set<IncludeInfo>> map;
public SymbolExportMap() {
this.map = new HashMap<String, Set<IncludeInfo>>();
}
/**
* @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<String, Set<IncludeInfo>>(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<String, Set<IncludeInfo>>(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<IncludeInfo> list = map.get(symbol);
if (list == null) {
list = new LinkedHashSet<IncludeInfo>();
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<IncludeInfo> getMapping(String from) {
Set<IncludeInfo> 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<IncludeInfo> removeMapping(String symbol) {
return map.remove(symbol);
}
/**
* Writes the map to a memento.
*/
public void saveToMemento(IMemento memento) {
for (Entry<String, Set<IncludeInfo>> 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<String, Set<IncludeInfo>> entry : other.map.entrySet()) {
String source = entry.getKey();
Set<IncludeInfo> otherTargets = entry.getValue();
Set<IncludeInfo> targets = map.get(source);
if (targets == null) {
targets = new LinkedHashSet<IncludeInfo>(otherTargets);
map.put(source, targets);
} else {
targets.addAll(otherTargets);
}
}
}
/** For debugging only. */
@Override
public String toString() {
StringBuilder buf = new StringBuilder();
ArrayList<String> symbols = new ArrayList<String>(map.keySet());
Collections.sort(symbols);
for (String symbol : symbols) {
buf.append('\n');
buf.append(symbol);
buf.append(" exported by "); //$NON-NLS-1$
List<IncludeInfo> targets = new ArrayList<IncludeInfo>(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<String, Set<IncludeInfo>> getMap() {
return Collections.unmodifiableMap(map);
}
public static String serializeMaps(List<SymbolExportMap> 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<SymbolExportMap> deserializeMaps(String str) {
StringReader reader = new StringReader(str);
XMLMemento memento;
try {
memento = XMLMemento.createReadRoot(reader);
} catch (WorkbenchException e) {
return Collections.emptyList();
}
List<SymbolExportMap> maps = new ArrayList<SymbolExportMap>();
for (IMemento element : memento.getChildren(TAG_SYMBOL_EXPORT_MAP)) {
maps.add(fromMemento(element));
}
return maps;
}
}

View file

@ -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