mirror of
https://github.com/eclipse-cdt/cdt
synced 2025-08-23 08:13:50 +02:00
Bug 45203. Added transitive closing of include maps.
This commit is contained in:
parent
ae409bedd6
commit
54919b0124
10 changed files with 320 additions and 46 deletions
|
@ -33,6 +33,9 @@ import org.eclipse.cdt.ui.testplugin.CTestPlugin;
|
||||||
import org.eclipse.cdt.internal.ui.refactoring.includes.BindingClassifier;
|
import org.eclipse.cdt.internal.ui.refactoring.includes.BindingClassifier;
|
||||||
import org.eclipse.cdt.internal.ui.refactoring.includes.InclusionContext;
|
import org.eclipse.cdt.internal.ui.refactoring.includes.InclusionContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link BindingClassifier}.
|
||||||
|
*/
|
||||||
public class BindingClassifierTest extends OneSourceMultipleHeadersTestCase {
|
public class BindingClassifierTest extends OneSourceMultipleHeadersTestCase {
|
||||||
private IIndex fIndex;
|
private IIndex fIndex;
|
||||||
private InclusionContext fContext;
|
private InclusionContext fContext;
|
||||||
|
|
|
@ -0,0 +1,103 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* 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.ui.tests.refactoring.includes;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
import org.eclipse.cdt.internal.ui.refactoring.includes.IncludeMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link IncludeMap}.
|
||||||
|
*/
|
||||||
|
public class IncludeMapTest extends TestCase {
|
||||||
|
|
||||||
|
private void assertEqualMaps(IncludeMap expected, IncludeMap actual) {
|
||||||
|
assertEquals(expected.toString(), actual.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testWeakCyclicMap() {
|
||||||
|
IncludeMap map = new IncludeMap(false, false, new String[] {
|
||||||
|
"a", "b",
|
||||||
|
"b", "c",
|
||||||
|
"c", "d",
|
||||||
|
"d", "b",
|
||||||
|
});
|
||||||
|
map.transitivelyClose();
|
||||||
|
IncludeMap expected = new IncludeMap(false, false, new String[] {
|
||||||
|
"a", "c",
|
||||||
|
"a", "b",
|
||||||
|
"a", "d",
|
||||||
|
"b", "d",
|
||||||
|
"b", "c",
|
||||||
|
"c", "d",
|
||||||
|
"c", "b",
|
||||||
|
"d", "b",
|
||||||
|
"d", "c",
|
||||||
|
});
|
||||||
|
assertEqualMaps(expected, map);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testStrongCyclicMap() {
|
||||||
|
IncludeMap map = new IncludeMap(true, false, new String[] {
|
||||||
|
"a", "b",
|
||||||
|
"b", "c",
|
||||||
|
"c", "d",
|
||||||
|
"d", "b",
|
||||||
|
});
|
||||||
|
map.transitivelyClose();
|
||||||
|
IncludeMap expected = new IncludeMap(true, false, new String[] {
|
||||||
|
"a", "c",
|
||||||
|
"c", "b",
|
||||||
|
"d", "b",
|
||||||
|
});
|
||||||
|
assertEqualMaps(expected, map);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testWeakMap() {
|
||||||
|
IncludeMap map = new IncludeMap(false, false, new String[] {
|
||||||
|
"a", "b",
|
||||||
|
"a", "c",
|
||||||
|
"c", "d",
|
||||||
|
"c", "e",
|
||||||
|
"d", "f",
|
||||||
|
});
|
||||||
|
map.transitivelyClose();
|
||||||
|
IncludeMap expected = new IncludeMap(false, false, new String[] {
|
||||||
|
"a", "b",
|
||||||
|
"a", "f",
|
||||||
|
"a", "d",
|
||||||
|
"a", "e",
|
||||||
|
"a", "c",
|
||||||
|
"c", "f",
|
||||||
|
"c", "d",
|
||||||
|
"c", "e",
|
||||||
|
"d", "f",
|
||||||
|
});
|
||||||
|
assertEqualMaps(expected, map);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testStrongMap() {
|
||||||
|
IncludeMap map = new IncludeMap(true, false, new String[] {
|
||||||
|
"a", "b",
|
||||||
|
"a", "c",
|
||||||
|
"c", "d",
|
||||||
|
"c", "e",
|
||||||
|
"d", "f",
|
||||||
|
});
|
||||||
|
map.transitivelyClose();
|
||||||
|
IncludeMap expected = new IncludeMap(true, false, new String[] {
|
||||||
|
"a", "b",
|
||||||
|
"c", "f",
|
||||||
|
"d", "f",
|
||||||
|
});
|
||||||
|
assertEqualMaps(expected, map);
|
||||||
|
}
|
||||||
|
}
|
|
@ -26,7 +26,7 @@ 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.IncludeOrganizer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for IncludeOrganizer.
|
* Tests for {@link IncludeOrganizer}.
|
||||||
*/
|
*/
|
||||||
public class IncludeOrganizerTest extends IncludesTestBase {
|
public class IncludeOrganizerTest extends IncludesTestBase {
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ public class IncludesTestSuite extends TestSuite {
|
||||||
|
|
||||||
public static Test suite() throws Exception {
|
public static Test suite() throws Exception {
|
||||||
IncludesTestSuite suite = new IncludesTestSuite();
|
IncludesTestSuite suite = new IncludesTestSuite();
|
||||||
|
suite.addTestSuite(IncludeMapTest.class);
|
||||||
suite.addTest(BindingClassifierTest.suite());
|
suite.addTest(BindingClassifierTest.suite());
|
||||||
suite.addTest(IncludeOrganizerTest.suite());
|
suite.addTest(IncludeOrganizerTest.suite());
|
||||||
return suite;
|
return suite;
|
||||||
|
|
|
@ -8,3 +8,6 @@ org.eclipse.cdt.ui/debug/SemanticHighlighting=false
|
||||||
|
|
||||||
# Enables debug information related to folding
|
# Enables debug information related to folding
|
||||||
org.eclipse.cdt.ui/debug/folding=false
|
org.eclipse.cdt.ui/debug/folding=false
|
||||||
|
|
||||||
|
# Enables debug information for header substitution in Organize Includes command.
|
||||||
|
org.eclipse.cdt.ui/debug/includeOrganizer/headerSubstitution=false
|
||||||
|
|
|
@ -474,7 +474,6 @@ public class HeaderSubstitutor {
|
||||||
"<ios>", "<iostream>",
|
"<ios>", "<iostream>",
|
||||||
"<ios>", "<istream>",
|
"<ios>", "<istream>",
|
||||||
"<ios>", "<ostream>",
|
"<ios>", "<ostream>",
|
||||||
"<iosfwd>", "<iostream>", // TODO(sprigogin): This should already be covered by <ios> -> <iostream> mapping
|
|
||||||
"<iosfwd>", "<ios>",
|
"<iosfwd>", "<ios>",
|
||||||
"<iosfwd>", "<streambuf>",
|
"<iosfwd>", "<streambuf>",
|
||||||
"<istream>", "<iostream>",
|
"<istream>", "<iostream>",
|
||||||
|
@ -489,10 +488,27 @@ public class HeaderSubstitutor {
|
||||||
|
|
||||||
private final InclusionContext fContext;
|
private final InclusionContext fContext;
|
||||||
|
|
||||||
private List<IncludeMap> fIncludeMaps;
|
private final IncludeMap fIncludeMap;
|
||||||
|
private final IncludeMap fIncludeMapWeak;
|
||||||
|
private IncludeMap[] fIncludeMaps;
|
||||||
|
|
||||||
public HeaderSubstitutor(InclusionContext context) {
|
public HeaderSubstitutor(InclusionContext context) {
|
||||||
fContext = context;
|
fContext = context;
|
||||||
|
fIncludeMap = new IncludeMap(cIncludeMap);
|
||||||
|
if (fContext.isCXXLanguage())
|
||||||
|
fIncludeMap.addAllMappings(cppIncludeMap);
|
||||||
|
fIncludeMapWeak = new IncludeMap(cIncludeMapWeak);
|
||||||
|
if (fContext.isCXXLanguage())
|
||||||
|
fIncludeMapWeak.addAllMappings(cppIncludeMapWeak);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addIncludeMap(IncludeMap map) {
|
||||||
|
if (fIncludeMaps != null)
|
||||||
|
throw new IllegalStateException("Modifications are not allowed after maps have been finalized"); //$NON-NLS-1$
|
||||||
|
if (map.isCppOnly() && !fContext.isCXXLanguage())
|
||||||
|
return;
|
||||||
|
IncludeMap receiver = map.isForcedReplacement() ? fIncludeMap : fIncludeMapWeak;
|
||||||
|
receiver.addAllMappings(map);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -507,7 +523,7 @@ public class HeaderSubstitutor {
|
||||||
IncludeInfo includeInfo = fContext.getIncludeForHeaderFile(path);
|
IncludeInfo includeInfo = fContext.getIncludeForHeaderFile(path);
|
||||||
if (includeInfo == null)
|
if (includeInfo == null)
|
||||||
return null;
|
return null;
|
||||||
List<IncludeMap> maps = getAllIncludeMaps();
|
IncludeMap[] maps = getAllIncludeMaps();
|
||||||
for (IncludeMap map : maps) {
|
for (IncludeMap map : maps) {
|
||||||
if (map.isForcedReplacement()) {
|
if (map.isForcedReplacement()) {
|
||||||
List<IncludeInfo> replacements = map.getMapping(includeInfo);
|
List<IncludeInfo> replacements = map.getMapping(includeInfo);
|
||||||
|
@ -535,7 +551,7 @@ public class HeaderSubstitutor {
|
||||||
// TODO(sprigogin): Take symbolIncludeMap into account.
|
// TODO(sprigogin): Take symbolIncludeMap into account.
|
||||||
List<IncludeInfo> candidates = new ArrayList<IncludeInfo>();
|
List<IncludeInfo> candidates = new ArrayList<IncludeInfo>();
|
||||||
candidates.add(includeInfo);
|
candidates.add(includeInfo);
|
||||||
List<IncludeMap> maps = getAllIncludeMaps();
|
IncludeMap[] maps = getAllIncludeMaps();
|
||||||
for (IncludeMap map : maps) {
|
for (IncludeMap map : maps) {
|
||||||
for (int i = 0; i < candidates.size();) {
|
for (int i = 0; i < candidates.size();) {
|
||||||
IncludeInfo candidate = candidates.get(i);
|
IncludeInfo candidate = candidates.get(i);
|
||||||
|
@ -641,21 +657,17 @@ public class HeaderSubstitutor {
|
||||||
return request.getCandidatePaths().iterator().next();
|
return request.getCandidatePaths().iterator().next();
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<IncludeMap> getAllIncludeMaps() {
|
private IncludeMap[] getAllIncludeMaps() {
|
||||||
if (fIncludeMaps == null) {
|
if (fIncludeMaps == null) {
|
||||||
fIncludeMaps = new ArrayList<IncludeMap>();
|
fIncludeMap.transitivelyClose();
|
||||||
for (IncludeMap map : INCLUDE_MAPS) {
|
fIncludeMapWeak.transitivelyClose();
|
||||||
if (fContext.isCXXLanguage() || !map.isCppOnly())
|
fIncludeMaps = new IncludeMap[] { fIncludeMap, fIncludeMapWeak };
|
||||||
fIncludeMaps.add(map);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return fIncludeMaps;
|
return fIncludeMaps;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether the given URI is an URI within the workspace
|
* Returns whether the given URI points within the workspace.
|
||||||
* @param uri
|
|
||||||
* @return
|
|
||||||
*/
|
*/
|
||||||
private static boolean isWorkspaceFile(URI uri) {
|
private static boolean isWorkspaceFile(URI uri) {
|
||||||
for (IFile file : ResourceLookup.findFilesForLocationURI(uri)) {
|
for (IFile file : ResourceLookup.findFilesForLocationURI(uri)) {
|
||||||
|
@ -668,8 +680,6 @@ public class HeaderSubstitutor {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether the given path has a file suffix, or not.
|
* Returns whether the given path has a file suffix, or not.
|
||||||
* @param path
|
|
||||||
* @return
|
|
||||||
*/
|
*/
|
||||||
private static boolean hasExtension(String path) {
|
private static boolean hasExtension(String path) {
|
||||||
return path.indexOf('.', path.lastIndexOf('/') + 1) >= 0;
|
return path.indexOf('.', path.lastIndexOf('/') + 1) >= 0;
|
||||||
|
@ -677,8 +687,6 @@ public class HeaderSubstitutor {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the filename of the given path, without extension.
|
* Returns the filename of the given path, without extension.
|
||||||
* @param path
|
|
||||||
* @return
|
|
||||||
*/
|
*/
|
||||||
private static String getFilename(String path) {
|
private static String getFilename(String path) {
|
||||||
int startPos = path.lastIndexOf('/') + 1;
|
int startPos = path.lastIndexOf('/') + 1;
|
||||||
|
|
|
@ -10,7 +10,14 @@
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package org.eclipse.cdt.internal.ui.refactoring.includes;
|
package org.eclipse.cdt.internal.ui.refactoring.includes;
|
||||||
|
|
||||||
public class IncludeInfo {
|
import org.eclipse.core.runtime.IPath;
|
||||||
|
import org.eclipse.core.runtime.Path;
|
||||||
|
|
||||||
|
import com.ibm.icu.text.Collator;
|
||||||
|
|
||||||
|
public class IncludeInfo implements Comparable<IncludeInfo> {
|
||||||
|
private static final Collator COLLATOR = Collator.getInstance();
|
||||||
|
|
||||||
private final String name;
|
private final String name;
|
||||||
private final boolean isSystem;
|
private final boolean isSystem;
|
||||||
|
|
||||||
|
@ -84,4 +91,21 @@ public class IncludeInfo {
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return (isSystem ? '<' : '"') + name + (isSystem ? '>' : '"');
|
return (isSystem ? '<' : '"') + name + (isSystem ? '>' : '"');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(IncludeInfo other) {
|
||||||
|
if (isSystem != other.isSystem) {
|
||||||
|
return isSystem ? -1 : 1;
|
||||||
|
}
|
||||||
|
IPath path1 = Path.fromOSString(name);
|
||||||
|
IPath path2 = Path.fromOSString(other.name);
|
||||||
|
int length1 = path1.segmentCount();
|
||||||
|
int length2 = path2.segmentCount();
|
||||||
|
for (int i = 0; i < length1 && i < length2; i++) {
|
||||||
|
int c = COLLATOR.compare(path1.segment(i), path2.segment(i));
|
||||||
|
if (c != 0)
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
return length1 - length2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,9 +11,12 @@
|
||||||
package org.eclipse.cdt.internal.ui.refactoring.includes;
|
package org.eclipse.cdt.internal.ui.refactoring.includes;
|
||||||
|
|
||||||
import java.io.StringReader;
|
import java.io.StringReader;
|
||||||
|
import java.util.ArrayDeque;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
@ -22,7 +25,7 @@ import org.eclipse.ui.IMemento;
|
||||||
import org.eclipse.ui.WorkbenchException;
|
import org.eclipse.ui.WorkbenchException;
|
||||||
import org.eclipse.ui.XMLMemento;
|
import org.eclipse.ui.XMLMemento;
|
||||||
|
|
||||||
class IncludeMap {
|
public class IncludeMap {
|
||||||
private static final String TAG_CPP_ONLY = "cpp_only"; //$NON-NLS-1$
|
private static final String TAG_CPP_ONLY = "cpp_only"; //$NON-NLS-1$
|
||||||
private static final String TAG_FORCED_REPLACEMENT = "forced_replacement"; //$NON-NLS-1$
|
private static final String TAG_FORCED_REPLACEMENT = "forced_replacement"; //$NON-NLS-1$
|
||||||
private static final String TAG_MAPPING = "mapping"; //$NON-NLS-1$
|
private static final String TAG_MAPPING = "mapping"; //$NON-NLS-1$
|
||||||
|
@ -56,6 +59,13 @@ class IncludeMap {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IncludeMap(IncludeMap other) {
|
||||||
|
this.forcedReplacement = other.forcedReplacement;
|
||||||
|
this.cppOnly = other.cppOnly;
|
||||||
|
this.map = new HashMap<IncludeInfo, List<IncludeInfo>>(other.map.size());
|
||||||
|
addAllMappings(other);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates that header file {@code to} should be used instead of {@code from}.
|
* Indicates that header file {@code to} should be used instead of {@code from}.
|
||||||
*
|
*
|
||||||
|
@ -63,6 +73,8 @@ class IncludeMap {
|
||||||
* @param to The header file to be used.
|
* @param to The header file to be used.
|
||||||
*/
|
*/
|
||||||
protected void addMapping(IncludeInfo from, IncludeInfo to) {
|
protected void addMapping(IncludeInfo from, IncludeInfo to) {
|
||||||
|
if (from.equals(to))
|
||||||
|
return; // Don't allow mapping to itself.
|
||||||
List<IncludeInfo> list = map.get(from);
|
List<IncludeInfo> list = map.get(from);
|
||||||
if (list == null) {
|
if (list == null) {
|
||||||
list = new ArrayList<IncludeInfo>(2);
|
list = new ArrayList<IncludeInfo>(2);
|
||||||
|
@ -150,7 +162,7 @@ class IncludeMap {
|
||||||
return includeMap;
|
return includeMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IncludeMap fromString(String str) {
|
public static IncludeMap fromSerializedMemento(String str) {
|
||||||
StringReader reader = new StringReader(str);
|
StringReader reader = new StringReader(str);
|
||||||
XMLMemento memento;
|
XMLMemento memento;
|
||||||
try {
|
try {
|
||||||
|
@ -160,4 +172,101 @@ class IncludeMap {
|
||||||
}
|
}
|
||||||
return fromMemento(memento);
|
return fromMemento(memento);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addAllMappings(IncludeMap other) {
|
||||||
|
if (other.forcedReplacement != forcedReplacement)
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
for (Entry<IncludeInfo, List<IncludeInfo>> entry : other.map.entrySet()) {
|
||||||
|
IncludeInfo source = entry.getKey();
|
||||||
|
List<IncludeInfo> otherTargets = entry.getValue();
|
||||||
|
List<IncludeInfo> targets = map.get(source);
|
||||||
|
if (targets == null) {
|
||||||
|
targets = new ArrayList<IncludeInfo>(otherTargets);
|
||||||
|
map.put(source, targets);
|
||||||
|
} else {
|
||||||
|
targets.addAll(otherTargets);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void transitivelyClose() {
|
||||||
|
for (Entry<IncludeInfo, List<IncludeInfo>> entry : map.entrySet()) {
|
||||||
|
IncludeInfo source = entry.getKey();
|
||||||
|
List<IncludeInfo> targets = entry.getValue();
|
||||||
|
ArrayDeque<IncludeInfo> queue = new ArrayDeque<IncludeInfo>(targets);
|
||||||
|
targets.clear();
|
||||||
|
HashSet<IncludeInfo> seen = new HashSet<IncludeInfo>();
|
||||||
|
if (!forcedReplacement)
|
||||||
|
seen.add(source); // Don't allow mapping to itself.
|
||||||
|
int iterationsWithoutProgress = 0;
|
||||||
|
IncludeInfo target;
|
||||||
|
queueLoop: while ((target = queue.pollFirst()) != null) {
|
||||||
|
if (seen.contains(target))
|
||||||
|
continue;
|
||||||
|
List<IncludeInfo> newTargets = map.get(target);
|
||||||
|
if (newTargets != null) {
|
||||||
|
queue.addFirst(target);
|
||||||
|
boolean added = false;
|
||||||
|
for (int i = newTargets.size(); --i >=0;) {
|
||||||
|
IncludeInfo newTarget = newTargets.get(i);
|
||||||
|
if (!seen.contains(newTarget)) {
|
||||||
|
if (forcedReplacement && newTarget.equals(source)) {
|
||||||
|
break queueLoop; // Leave the mapping empty.
|
||||||
|
}
|
||||||
|
queue.addFirst(newTarget);
|
||||||
|
added = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// The second condition protects against an infinite loop.
|
||||||
|
if (!added || ++iterationsWithoutProgress >= map.size()) {
|
||||||
|
target = queue.pollFirst();
|
||||||
|
targets.add(target);
|
||||||
|
if (forcedReplacement)
|
||||||
|
break;
|
||||||
|
seen.add(target);
|
||||||
|
iterationsWithoutProgress = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
targets.add(target);
|
||||||
|
if (forcedReplacement)
|
||||||
|
break;
|
||||||
|
seen.add(target);
|
||||||
|
iterationsWithoutProgress = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (forcedReplacement) {
|
||||||
|
// Remove trivial mappings.
|
||||||
|
for (Iterator<Entry<IncludeInfo, List<IncludeInfo>>> iter = map.entrySet().iterator(); iter.hasNext();) {
|
||||||
|
Entry<IncludeInfo, List<IncludeInfo>> entry = iter.next();
|
||||||
|
IncludeInfo source = entry.getKey();
|
||||||
|
List<IncludeInfo> targets = entry.getValue();
|
||||||
|
if (targets.isEmpty() || (targets.size() == 1 && source.equals(targets.get(0)))) {
|
||||||
|
iter.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** For debugging only. */
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder buf = new StringBuilder();
|
||||||
|
buf.append("forcedReplacement = ").append(forcedReplacement); //$NON-NLS-1$
|
||||||
|
buf.append(", cppOnly = ").append(cppOnly); //$NON-NLS-1$
|
||||||
|
ArrayList<IncludeInfo> sources = new ArrayList<IncludeInfo>(map.keySet());
|
||||||
|
Collections.sort(sources);
|
||||||
|
for (IncludeInfo source : sources) {
|
||||||
|
buf.append('\n');
|
||||||
|
buf.append(source);
|
||||||
|
buf.append(" -> "); //$NON-NLS-1$
|
||||||
|
List<IncludeInfo> targets = map.get(source);
|
||||||
|
for (int i = 0; i < targets.size(); i++) {
|
||||||
|
if (i > 0)
|
||||||
|
buf.append(", "); //$NON-NLS-1$
|
||||||
|
buf.append(targets.get(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return buf.toString();
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -19,7 +19,6 @@ import static org.eclipse.cdt.internal.core.dom.parser.ASTTranslationUnit.getSta
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -32,6 +31,7 @@ import org.eclipse.core.runtime.CoreException;
|
||||||
import org.eclipse.core.runtime.IPath;
|
import org.eclipse.core.runtime.IPath;
|
||||||
import org.eclipse.core.runtime.OperationCanceledException;
|
import org.eclipse.core.runtime.OperationCanceledException;
|
||||||
import org.eclipse.core.runtime.Path;
|
import org.eclipse.core.runtime.Path;
|
||||||
|
import org.eclipse.core.runtime.Platform;
|
||||||
import org.eclipse.jface.text.IRegion;
|
import org.eclipse.jface.text.IRegion;
|
||||||
import org.eclipse.jface.text.Region;
|
import org.eclipse.jface.text.Region;
|
||||||
import org.eclipse.text.edits.DeleteEdit;
|
import org.eclipse.text.edits.DeleteEdit;
|
||||||
|
@ -93,8 +93,9 @@ import org.eclipse.cdt.internal.ui.refactoring.includes.IncludeGroupStyle.Includ
|
||||||
* Organizes the include directives and forward declarations of a source or header file.
|
* Organizes the include directives and forward declarations of a source or header file.
|
||||||
*/
|
*/
|
||||||
public class IncludeOrganizer {
|
public class IncludeOrganizer {
|
||||||
|
private static boolean DEBUG_HEADER_SUBSTITUTION = "true".equalsIgnoreCase(Platform.getDebugOption(CUIPlugin.PLUGIN_ID + "/debug/includeOrganizer/headerSubstitution")); //$NON-NLS-1$ //$NON-NLS-2$
|
||||||
|
|
||||||
private static class IncludePrototype {
|
private static class IncludePrototype implements Comparable<IncludePrototype> {
|
||||||
final IPath header; // null for existing unresolved includes
|
final IPath header; // null for existing unresolved includes
|
||||||
final IncludeInfo includeInfo; // never null
|
final IncludeInfo includeInfo; // never null
|
||||||
IASTPreprocessorIncludeStatement existingInclude; // null for newly added includes
|
IASTPreprocessorIncludeStatement existingInclude; // null for newly added includes
|
||||||
|
@ -158,31 +159,15 @@ public class IncludeOrganizer {
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return header != null ? header.toPortableString() : includeInfo.toString();
|
return header != null ? header.toPortableString() : includeInfo.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(IncludePrototype other) {
|
||||||
|
return includeInfo.compareTo(other.includeInfo);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final Collator COLLATOR = Collator.getInstance();
|
private static final Collator COLLATOR = Collator.getInstance();
|
||||||
|
|
||||||
private static final Comparator<IncludePrototype> INCLUDE_COMPARATOR = new Comparator<IncludePrototype>() {
|
|
||||||
@Override
|
|
||||||
public int compare(IncludePrototype include1, IncludePrototype include2) {
|
|
||||||
IncludeInfo includeInfo1 = include1.includeInfo;
|
|
||||||
IncludeInfo includeInfo2 = include2.includeInfo;
|
|
||||||
if (includeInfo1.isSystem() != includeInfo2.isSystem()) {
|
|
||||||
return includeInfo1.isSystem() ? -1 : 1;
|
|
||||||
}
|
|
||||||
IPath path1 = Path.fromOSString(includeInfo1.getName());
|
|
||||||
IPath path2 = Path.fromOSString(includeInfo2.getName());
|
|
||||||
int length1 = path1.segmentCount();
|
|
||||||
int length2 = path2.segmentCount();
|
|
||||||
for (int i = 0; i < length1 && i < length2; i++) {
|
|
||||||
int c = COLLATOR.compare(path1.segment(i), path2.segment(i));
|
|
||||||
if (c != 0)
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
return length1 - length2;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private final IHeaderChooser fHeaderChooser;
|
private final IHeaderChooser fHeaderChooser;
|
||||||
private final InclusionContext fContext;
|
private final InclusionContext fContext;
|
||||||
private final String fLineDelimiter;
|
private final String fLineDelimiter;
|
||||||
|
@ -291,7 +276,7 @@ public class IncludeOrganizer {
|
||||||
IncludeGroupStyle previousParentStyle = null;
|
IncludeGroupStyle previousParentStyle = null;
|
||||||
for (List<IncludePrototype> prototypes : groupedPrototypes) {
|
for (List<IncludePrototype> prototypes : groupedPrototypes) {
|
||||||
if (prototypes != null && !prototypes.isEmpty()) {
|
if (prototypes != null && !prototypes.isEmpty()) {
|
||||||
Collections.sort(prototypes, INCLUDE_COMPARATOR);
|
Collections.sort(prototypes);
|
||||||
IncludeGroupStyle style = prototypes.get(0).style;
|
IncludeGroupStyle style = prototypes.get(0).style;
|
||||||
IncludeGroupStyle groupingStyle = getGroupingStyle(style);
|
IncludeGroupStyle groupingStyle = getGroupingStyle(style);
|
||||||
IncludeGroupStyle parentStyle = getParentStyle(groupingStyle);
|
IncludeGroupStyle parentStyle = getParentStyle(groupingStyle);
|
||||||
|
@ -912,6 +897,10 @@ public class IncludeOrganizer {
|
||||||
for (IPath path : candidatePaths) {
|
for (IPath path : candidatePaths) {
|
||||||
if (fContext.isIncluded(path)) {
|
if (fContext.isIncluded(path)) {
|
||||||
request.resolve(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;
|
break;
|
||||||
} else {
|
} else {
|
||||||
IPath header = headerSubstitutor.getUniqueRepresentativeHeader(path);
|
IPath header = headerSubstitutor.getUniqueRepresentativeHeader(path);
|
||||||
|
@ -926,6 +915,8 @@ public class IncludeOrganizer {
|
||||||
if (!request.isResolved() && allRepresented && representativeHeaders.size() == 1) {
|
if (!request.isResolved() && allRepresented && representativeHeaders.size() == 1) {
|
||||||
IPath path = representativeHeaders.iterator().next();
|
IPath path = representativeHeaders.iterator().next();
|
||||||
request.resolve(path);
|
request.resolve(path);
|
||||||
|
if (DEBUG_HEADER_SUBSTITUTION)
|
||||||
|
System.out.println(request.toString() + " (unique representative)"); //$NON-NLS-1$
|
||||||
if (!fContext.isAlreadyIncluded(path))
|
if (!fContext.isAlreadyIncluded(path))
|
||||||
fContext.addHeaderToInclude(path);
|
fContext.addHeaderToInclude(path);
|
||||||
}
|
}
|
||||||
|
@ -940,12 +931,19 @@ public class IncludeOrganizer {
|
||||||
IPath path = candidatePaths.iterator().next();
|
IPath path = candidatePaths.iterator().next();
|
||||||
if (fContext.isIncluded(path)) {
|
if (fContext.isIncluded(path)) {
|
||||||
request.resolve(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$
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
IPath header = headerSubstitutor.getPreferredRepresentativeHeader(path);
|
IPath header = headerSubstitutor.getPreferredRepresentativeHeader(path);
|
||||||
if (header.equals(path) && fContext.getPreferences().heuristicHeaderSubstitution) {
|
if (header.equals(path) && fContext.getPreferences().heuristicHeaderSubstitution) {
|
||||||
header = headerSubstitutor.getPreferredRepresentativeHeaderByHeuristic(request);
|
header = headerSubstitutor.getPreferredRepresentativeHeaderByHeuristic(request);
|
||||||
}
|
}
|
||||||
request.resolve(header);
|
request.resolve(header);
|
||||||
|
if (DEBUG_HEADER_SUBSTITUTION) {
|
||||||
|
System.out.println(request.toString() + " (preferred representative)"); //$NON-NLS-1$
|
||||||
|
}
|
||||||
if (!fContext.isAlreadyIncluded(header))
|
if (!fContext.isAlreadyIncluded(header))
|
||||||
fContext.addHeaderToInclude(header);
|
fContext.addHeaderToInclude(header);
|
||||||
}
|
}
|
||||||
|
@ -960,6 +958,10 @@ public class IncludeOrganizer {
|
||||||
for (IPath path : candidatePaths) {
|
for (IPath path : candidatePaths) {
|
||||||
if (fContext.isIncluded(path)) {
|
if (fContext.isIncluded(path)) {
|
||||||
request.resolve(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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -969,6 +971,9 @@ public class IncludeOrganizer {
|
||||||
throw new OperationCanceledException();
|
throw new OperationCanceledException();
|
||||||
|
|
||||||
request.resolve(header);
|
request.resolve(header);
|
||||||
|
if (DEBUG_HEADER_SUBSTITUTION) {
|
||||||
|
System.out.println(request.toString() + " (user's choice)"); //$NON-NLS-1$
|
||||||
|
}
|
||||||
if (!fContext.isAlreadyIncluded(header))
|
if (!fContext.isAlreadyIncluded(header))
|
||||||
fContext.addHeaderToInclude(header);
|
fContext.addHeaderToInclude(header);
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,4 +75,22 @@ class InclusionRequest {
|
||||||
public boolean isResolved() {
|
public boolean isResolved() {
|
||||||
return fResolvedPath != null;
|
return fResolvedPath != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** For debugging only */
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder buf = new StringBuilder();
|
||||||
|
buf.append(fBinding.getName());
|
||||||
|
buf.append(" defined in "); //$NON-NLS-1$
|
||||||
|
for (int i = 0; i < fCandidatePaths.size(); i++) {
|
||||||
|
if (i != 0)
|
||||||
|
buf.append(", "); //$NON-NLS-1$
|
||||||
|
buf.append(fCandidatePaths.get(i).toOSString());
|
||||||
|
}
|
||||||
|
if (fResolvedPath != null) {
|
||||||
|
buf.append(" represented by "); //$NON-NLS-1$
|
||||||
|
buf.append(fResolvedPath.toOSString());
|
||||||
|
}
|
||||||
|
return buf.toString();
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Add table
Reference in a new issue