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

Bug 414838 - New class creation doesn't properly handle include path

with directories for non-system headers.
This commit is contained in:
Sergey Prigogin 2013-08-11 20:51:32 -07:00
parent 1524122c03
commit eca8e6004c
19 changed files with 497 additions and 418 deletions

View file

@ -36,7 +36,7 @@ import org.eclipse.cdt.ui.PreferenceConstants;
import org.eclipse.cdt.ui.testplugin.CTestPlugin;
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.IncludeCreationContext;
/**
* Tests for {@link BindingClassifier}.
@ -68,7 +68,7 @@ public class BindingClassifierTest extends OneSourceMultipleHeadersTestCase {
if (fBindingClassifier == null) {
IASTTranslationUnit ast = getAst();
ITranslationUnit tu = ast.getOriginatingTranslationUnit();
InclusionContext context = new InclusionContext(tu, fIndex);
IncludeCreationContext context = new IncludeCreationContext(tu, fIndex);
fBindingClassifier = new BindingClassifier(context);
fBindingClassifier.classifyNodeContents(ast);
}

View file

@ -36,7 +36,6 @@ import org.eclipse.cdt.core.model.ISourceReference;
import org.eclipse.cdt.core.model.ITranslationUnit;
import org.eclipse.cdt.internal.ui.editor.CEditorMessages;
import org.eclipse.cdt.internal.ui.refactoring.includes.IncludeInfo;
/**
* Adds includes and 'using' declarations to a translation unit.

View file

@ -8,7 +8,7 @@
* Contributors:
* Sergey Prigogin (Google) - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.internal.ui.refactoring.includes;
package org.eclipse.cdt.internal.corext.codemanipulation;
import com.ibm.icu.text.Collator;

View file

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2012, 2013 Google, Inc and others.
* 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
@ -8,24 +8,17 @@
* Contributors:
* Sergey Prigogin (Google) - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.internal.ui.refactoring.includes;
package org.eclipse.cdt.internal.corext.codemanipulation;
import java.io.File;
import java.util.ArrayDeque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.cdt.core.index.IIndex;
import org.eclipse.cdt.core.index.IIndexFile;
import org.eclipse.cdt.core.index.IIndexInclude;
import org.eclipse.cdt.core.index.IndexLocationFactory;
import org.eclipse.cdt.core.model.ICProject;
import org.eclipse.cdt.core.model.ITranslationUnit;
import org.eclipse.cdt.core.parser.IScannerInfo;
@ -34,65 +27,51 @@ import org.eclipse.cdt.internal.core.parser.scanner.CPreprocessor;
import org.eclipse.cdt.internal.core.parser.scanner.IncludeSearchPath;
import org.eclipse.cdt.internal.core.parser.scanner.IncludeSearchPathElement;
import org.eclipse.cdt.internal.core.parser.scanner.ScannerUtility;
import org.eclipse.cdt.internal.core.resources.ResourceLookup;
import org.eclipse.cdt.internal.ui.refactoring.includes.IncludeGroupStyle;
import org.eclipse.cdt.internal.ui.refactoring.includes.IncludeGroupStyle.IncludeKind;
import org.eclipse.cdt.internal.ui.refactoring.includes.IncludePreferences;
/**
* Context for managing include statements.
*/
public class InclusionContext {
private static final IPath UNRESOLVED_INCLUDE = Path.EMPTY;
private final ITranslationUnit fTu;
private final IProject fProject;
private final IPath fCurrentDirectory;
private final IncludePreferences fPreferences;
private final IncludeSearchPath fIncludeSearchPath;
private final Map<IncludeInfo, IPath> fIncludeResolutionCache;
private final Map<IPath, IncludeInfo> fInverseIncludeResolutionCache;
private final IIndex fIndex;
private final Set<IPath> fHeadersToInclude;
private final Set<IPath> fHeadersAlreadyIncluded;
private final Set<IPath> fHeadersIncludedPreviously;
private final IncludePreferences fPreferences;
public InclusionContext(ITranslationUnit tu, IIndex index) {
public InclusionContext(ITranslationUnit tu) {
fTu = tu;
fIndex = index;
ICProject cProject = fTu.getCProject();
fPreferences = new IncludePreferences(cProject);
fProject = cProject.getProject();
fCurrentDirectory = fTu.getResource().getParent().getLocation();
IScannerInfo scannerInfo = fTu.getScannerInfo(true);
fIncludeSearchPath = CPreprocessor.configureIncludeSearchPath(fCurrentDirectory.toFile(), scannerInfo);
fIncludeResolutionCache = new HashMap<IncludeInfo, IPath>();
fInverseIncludeResolutionCache = new HashMap<IPath, IncludeInfo>();
fHeadersToInclude = new HashSet<IPath>();
fHeadersAlreadyIncluded = new HashSet<IPath>();
fHeadersIncludedPreviously = new HashSet<IPath>();
fPreferences = new IncludePreferences(cProject);
}
public ITranslationUnit getTranslationUnit() {
public final ITranslationUnit getTranslationUnit() {
return fTu;
}
public IIndex getIndex() {
return fIndex;
public final IProject getProject() {
return fProject;
}
public IProject getProject() {
return fProject;
public final IPath getCurrentDirectory() {
return fCurrentDirectory;
}
public IncludePreferences getPreferences() {
return fPreferences;
}
public boolean isCXXLanguage() {
return fTu.isCXXLanguage();
}
public IPath getCurrentDirectory() {
return fCurrentDirectory;
}
public IPath resolveInclude(IncludeInfo include) {
IPath path = fIncludeResolutionCache.get(include);
if (path == null) {
@ -161,82 +140,89 @@ public class InclusionContext {
return include;
}
/**
* Removes headers that are exported by other headers that will be included
*/
public void removeExportedHeaders() throws CoreException {
// Index files keyed by their absolute paths.
Map<IPath, IIndexFile> filesByPath = new HashMap<IPath, IIndexFile>();
for (IIndexFile file : fIndex.getAllFiles()) {
IPath path = getPath(file);
filesByPath.put(path, file);
}
Set<IPath> exportedHeaders = new HashSet<IPath>();
for (IPath path : fHeadersToInclude) {
if (!exportedHeaders.contains(path)) {
IIndexFile file = filesByPath.get(path);
if (file != null) { // file can be null if the header was not indexed.
ArrayDeque<IIndexFile> queue = new ArrayDeque<IIndexFile>();
queue.add(file);
while ((file = queue.pollFirst()) != null) {
for (IIndexInclude include : file.getIncludes()) {
if (fPreferences.allowIndirectInclusion || include.isIncludedFileExported()) {
file = fIndex.resolveInclude(include);
if (file != null) {
if (exportedHeaders.add(getPath(file)))
queue.add(file);
}
}
public IncludeGroupStyle getIncludeStyle(IPath headerPath) {
IncludeKind includeKind;
IncludeInfo includeInfo = getIncludeForHeaderFile(headerPath);
if (includeInfo != null && includeInfo.isSystem()) {
if (headerPath.getFileExtension() == null) {
includeKind = IncludeKind.SYSTEM_WITHOUT_EXTENSION;
} else {
includeKind = IncludeKind.SYSTEM_WITH_EXTENSION;
}
} else if (isPartnerFile(headerPath)) {
includeKind = IncludeKind.PARTNER;
} else {
IPath dir = getCurrentDirectory();
if (dir.isPrefixOf(headerPath)) {
if (headerPath.segmentCount() == dir.segmentCount() + 1) {
includeKind = IncludeKind.IN_SAME_FOLDER;
} else {
includeKind = IncludeKind.IN_SUBFOLDER;
}
} else {
IFile[] files = ResourceLookup.findFilesForLocation(headerPath);
if (files.length == 0) {
includeKind = IncludeKind.EXTERNAL;
} else {
IProject project = getProject();
includeKind = IncludeKind.IN_OTHER_PROJECT;
for (IFile file : files) {
if (file.getProject().equals(project)) {
includeKind = IncludeKind.IN_SAME_PROJECT;
break;
}
}
}
}
}
fHeadersToInclude.removeAll(exportedHeaders);
}
private static IPath getPath(IIndexFile file) throws CoreException {
return IndexLocationFactory.getAbsolutePath(file.getLocation());
return fPreferences.includeStyles.get(includeKind);
}
public IncludeGroupStyle getIncludeStyle(IncludeInfo includeInfo) {
IncludeKind includeKind;
IPath path = Path.fromPortableString(includeInfo.getName());
if (includeInfo.isSystem()) {
if (path.getFileExtension() == null) {
includeKind = IncludeKind.SYSTEM_WITHOUT_EXTENSION;
} else {
includeKind = IncludeKind.SYSTEM_WITH_EXTENSION;
}
} else if (isPartnerFile(path)) {
includeKind = IncludeKind.PARTNER;
} else {
includeKind = IncludeKind.EXTERNAL;
}
return fPreferences.includeStyles.get(includeKind);
}
private static boolean fileExists(String absolutePath) {
return new File(absolutePath).exists();
}
public Set<IPath> getHeadersToInclude() {
return fHeadersToInclude;
/**
* Checks if the given path points to a partner header of the current translation unit.
* A header is considered a partner if its name without extension is the same as the name of
* the translation unit, or the name of the translation unit differs by one of the suffixes
* used for test files.
*/
public boolean isPartnerFile(IPath path) {
String headerName = path.removeFileExtension().lastSegment();
String sourceName = getTranslationUnit().getLocation().removeFileExtension().lastSegment();
if (headerName.equals(sourceName))
return true;
if (sourceName.startsWith(headerName)) {
int pos = headerName.length();
while (pos < sourceName.length() && !Character.isLetterOrDigit(sourceName.charAt(pos))) {
pos++;
}
if (pos == sourceName.length())
return true;
String suffix = sourceName.substring(pos);
for (String s : fPreferences.partnerFileSuffixes) {
if (suffix.equalsIgnoreCase(s))
return true;
}
}
return false;
}
public final void addHeaderToInclude(IPath header) {
fHeadersToInclude.add(header);
}
public final boolean isToBeIncluded(IPath header) {
return fHeadersToInclude.contains(header);
}
public Set<IPath> getHeadersAlreadyIncluded() {
return fHeadersAlreadyIncluded;
}
public final void addHeaderAlreadyIncluded(IPath header) {
fHeadersAlreadyIncluded.add(header);
}
public final boolean isAlreadyIncluded(IPath header) {
return fHeadersAlreadyIncluded.contains(header);
}
public final boolean isIncluded(IPath header) {
return fHeadersAlreadyIncluded.contains(header) || fHeadersToInclude.contains(header);
}
public final void addHeaderIncludedPreviously(IPath header) {
fHeadersIncludedPreviously.add(header);
}
public final boolean wasIncludedPreviously(IPath header) {
return fHeadersIncludedPreviously.contains(header);
}
}
}

View file

@ -0,0 +1,102 @@
/*******************************************************************************
* Copyright (c) 2012, 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.corext.codemanipulation;
import org.eclipse.core.runtime.IPath;
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorIncludeStatement;
import org.eclipse.cdt.internal.ui.refactoring.includes.IncludeGroupStyle;
/**
* Represents a new or an existing include statement together with the style associated with it.
*/
public class StyledInclude implements Comparable<StyledInclude> {
private final IPath header; // null for existing unresolved includes
private final IncludeInfo includeInfo; // never null
private final IncludeGroupStyle style;
private IASTPreprocessorIncludeStatement existingInclude;
/** Initializes a StyledInclude object for a new include */
public StyledInclude(IPath header, IncludeInfo includeInfo, IncludeGroupStyle style) {
this(header, includeInfo, style, null);
if (header == null)
throw new NullPointerException();
}
/**
* Initializes an include prototype object for an existing include. {@code header} may be
* {@code null} if the include was not resolved.
*/
public StyledInclude(IPath header, IncludeInfo includeInfo, IncludeGroupStyle style,
IASTPreprocessorIncludeStatement existingInclude) {
if (includeInfo == null)
throw new NullPointerException();
this.header = header;
this.includeInfo = includeInfo;
this.style = style;
this.existingInclude = existingInclude;
}
public IPath getHeader() {
return header;
}
public IncludeInfo getIncludeInfo() {
return includeInfo;
}
public IncludeGroupStyle getStyle() {
return style;
}
public IASTPreprocessorIncludeStatement getExistingInclude() {
return existingInclude;
}
public void setExistingInclude(IASTPreprocessorIncludeStatement existingInclude) {
this.existingInclude = existingInclude;
}
@Override
public int compareTo(StyledInclude other) {
return includeInfo.compareTo(other.includeInfo);
}
@Override
public int hashCode() {
if (header != null)
return header.hashCode(); // includeInfo is ignored if header is not null
return includeInfo.hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
StyledInclude other = (StyledInclude) obj;
if (header != null)
return header.equals(other.header); // includeInfo is ignored if header is not null
if (other.header != null)
return false;
return includeInfo.equals(other.includeInfo);
}
/** For debugging only */
@Override
public String toString() {
return header != null ? header.toPortableString() : includeInfo.toString();
}
}

View file

@ -87,12 +87,12 @@ import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPVisitor;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil;
import org.eclipse.cdt.internal.core.resources.ResourceLookup;
import org.eclipse.cdt.internal.corext.codemanipulation.AddIncludesOperation;
import org.eclipse.cdt.internal.corext.codemanipulation.IncludeInfo;
import org.eclipse.cdt.internal.corext.codemanipulation.InclusionContext;
import org.eclipse.cdt.internal.ui.CHelpProviderManager;
import org.eclipse.cdt.internal.ui.ICHelpContextIds;
import org.eclipse.cdt.internal.ui.actions.WorkbenchRunnableAdapter;
import org.eclipse.cdt.internal.ui.refactoring.includes.IncludeInfo;
import org.eclipse.cdt.internal.ui.refactoring.includes.InclusionContext;
import org.eclipse.cdt.internal.ui.util.ExceptionHandler;
/**
@ -205,7 +205,7 @@ public class AddIncludeOnSelectionAction extends TextEditorAction {
*/
private void deduceInclude(ITextSelection selection, IIndex index, IASTTranslationUnit ast, String[] lookupName)
throws CoreException {
fContext = new InclusionContext(fTu, index);
fContext = new InclusionContext(fTu);
IASTNodeSelector selector = ast.getNodeSelector(fTu.getLocation().toOSString());
IASTName name = selector.findEnclosingName(selection.getOffset(), selection.getLength());
if (name == null) {

View file

@ -30,10 +30,12 @@ import org.eclipse.cdt.internal.ui.refactoring.includes.IHeaderChooser;
* Dialog-based header chooser.
*/
public class InteractiveHeaderChooser implements IHeaderChooser {
private final String title;
private final Shell shell;
Map<Collection<IPath>, IPath> userChoiceCache;
private final Map<Collection<IPath>, IPath> userChoiceCache;
public InteractiveHeaderChooser(Shell shell) {
public InteractiveHeaderChooser(String title, Shell shell) {
this.title = title;
this.shell = shell;
userChoiceCache = new HashMap<Collection<IPath>, IPath>();
}
@ -62,7 +64,7 @@ public class InteractiveHeaderChooser implements IHeaderChooser {
ElementListSelectionDialog dialog =
new ElementListSelectionDialog(shell, new LabelProvider());
dialog.setElements(elemArray);
dialog.setTitle(CEditorMessages.OrganizeIncludes_label);
dialog.setTitle(title);
dialog.setMessage(NLS.bind(CEditorMessages.OrganizeIncludes_choose_header, bindingName));
if (dialog.open() == Window.OK) {
selectedElement[0] = (IPath) dialog.getFirstResult();

View file

@ -49,7 +49,7 @@ import org.eclipse.cdt.internal.ui.refactoring.includes.IncludeOrganizer;
public class OrganizeIncludesAction extends TextEditorAction {
/**
* Constructor
* @param editor The editor on which this organize includes action should operate.
* @param editor The editor on which this Organize Includes action should operate.
*/
public OrganizeIncludesAction(ITextEditor editor) {
super(CEditorMessages.getBundleForConstructedKeys(), "OrganizeIncludes.", editor); //$NON-NLS-1$
@ -67,7 +67,8 @@ public class OrganizeIncludesAction extends TextEditorAction {
return;
}
final IHeaderChooser headerChooser = new InteractiveHeaderChooser(editor.getSite().getShell());
final IHeaderChooser headerChooser = new InteractiveHeaderChooser(
CEditorMessages.OrganizeIncludes_label, editor.getSite().getShell());
final String lineDelimiter = getLineDelimiter(editor);
final List<TextEdit> edits = new ArrayList<TextEdit>();
SharedASTJob job = new SharedASTJob(CEditorMessages.OrganizeIncludes_action, tu) {

View file

@ -56,11 +56,12 @@ import org.eclipse.ui.XMLMemento;
import com.ibm.icu.text.Collator;
import org.eclipse.cdt.internal.corext.codemanipulation.IncludeInfo;
import org.eclipse.cdt.internal.ui.ICHelpContextIds;
import org.eclipse.cdt.internal.ui.dialogs.ResizableStatusDialog;
import org.eclipse.cdt.internal.ui.dialogs.StatusInfo;
import org.eclipse.cdt.internal.ui.refactoring.includes.HeaderSubstitutionMap;
import org.eclipse.cdt.internal.ui.refactoring.includes.IncludeInfo;
import org.eclipse.cdt.internal.ui.refactoring.includes.IncludeMap;
import org.eclipse.cdt.internal.ui.wizards.dialogfields.ComboDialogField;
import org.eclipse.cdt.internal.ui.wizards.dialogfields.DialogField;

View file

@ -111,7 +111,7 @@ import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil;
* must be defined and a set of bindings that must be declared.
*/
public class BindingClassifier {
private final InclusionContext fContext;
private final IncludeCreationContext fContext;
private final IncludePreferences fPreferences;
/** The bindings which require a full definition. */
private final Set<IBinding> fBindingsToDefine;
@ -126,7 +126,7 @@ public class BindingClassifier {
/**
* @param context the context for binding classification
*/
public BindingClassifier(InclusionContext context) {
public BindingClassifier(IncludeCreationContext context) {
fContext = context;
fPreferences = context.getPreferences();
fBindingsToDefine = new HashSet<IBinding>();

View file

@ -24,6 +24,8 @@ import org.eclipse.ui.XMLMemento;
import org.eclipse.cdt.ui.CUIPlugin;
import org.eclipse.cdt.internal.corext.codemanipulation.IncludeInfo;
/**
* A set of header file substitution rules.
*/

View file

@ -32,13 +32,14 @@ import org.eclipse.cdt.ui.CUIPlugin;
import org.eclipse.cdt.ui.PreferenceConstants;
import org.eclipse.cdt.internal.core.resources.ResourceLookup;
import org.eclipse.cdt.internal.corext.codemanipulation.IncludeInfo;
public class HeaderSubstitutor {
private final InclusionContext fContext;
private final IncludeCreationContext fContext;
private IncludeMap[] fIncludeMaps;
private SymbolExportMap fSymbolExportMap;
public HeaderSubstitutor(InclusionContext context) {
public HeaderSubstitutor(IncludeCreationContext context) {
fContext = context;
fIncludeMaps = new IncludeMap[] { new IncludeMap(true), new IncludeMap(false) };
IPreferencesService preferences = Platform.getPreferencesService();

View file

@ -0,0 +1,142 @@
/*******************************************************************************
* Copyright (c) 2012, 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.util.ArrayDeque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorIncludeStatement;
import org.eclipse.cdt.core.index.IIndex;
import org.eclipse.cdt.core.index.IIndexFile;
import org.eclipse.cdt.core.index.IIndexInclude;
import org.eclipse.cdt.core.index.IndexLocationFactory;
import org.eclipse.cdt.core.model.ITranslationUnit;
import org.eclipse.cdt.internal.corext.codemanipulation.InclusionContext;
/**
* Context for managing include statements.
*/
public class IncludeCreationContext extends InclusionContext {
private final IIndex fIndex;
private final Set<IPath> fHeadersToInclude;
private final Set<IPath> fHeadersAlreadyIncluded;
private final Set<IPath> fHeadersIncludedPreviously;
public IncludeCreationContext(ITranslationUnit tu, IIndex index) {
super(tu);
fIndex = index;
fHeadersToInclude = new HashSet<IPath>();
fHeadersAlreadyIncluded = new HashSet<IPath>();
fHeadersIncludedPreviously = new HashSet<IPath>();
}
public char[] getSourceContents() {
return getTranslationUnit().getContents();
}
public IIndex getIndex() {
return fIndex;
}
public boolean isCXXLanguage() {
return getTranslationUnit().isCXXLanguage();
}
/**
* Removes headers that are exported by other headers that will be included
*/
public void removeExportedHeaders() throws CoreException {
// Index files keyed by their absolute paths.
Map<IPath, IIndexFile> filesByPath = new HashMap<IPath, IIndexFile>();
for (IIndexFile file : fIndex.getAllFiles()) {
IPath path = getPath(file);
filesByPath.put(path, file);
}
Set<IPath> exportedHeaders = new HashSet<IPath>();
for (IPath path : fHeadersToInclude) {
if (!exportedHeaders.contains(path)) {
IIndexFile file = filesByPath.get(path);
if (file != null) { // file can be null if the header was not indexed.
ArrayDeque<IIndexFile> queue = new ArrayDeque<IIndexFile>();
queue.add(file);
while ((file = queue.pollFirst()) != null) {
for (IIndexInclude include : file.getIncludes()) {
if (getPreferences().allowIndirectInclusion || include.isIncludedFileExported()) {
file = fIndex.resolveInclude(include);
if (file != null) {
if (exportedHeaders.add(getPath(file)))
queue.add(file);
}
}
}
}
}
}
}
fHeadersToInclude.removeAll(exportedHeaders);
}
private static IPath getPath(IIndexFile file) throws CoreException {
return IndexLocationFactory.getAbsolutePath(file.getLocation());
}
public Set<IPath> getHeadersToInclude() {
return fHeadersToInclude;
}
public final void addHeaderToInclude(IPath header) {
fHeadersToInclude.add(header);
}
public final boolean isToBeIncluded(IPath header) {
return fHeadersToInclude.contains(header);
}
public Set<IPath> getHeadersAlreadyIncluded() {
return fHeadersAlreadyIncluded;
}
public final void addHeaderAlreadyIncluded(IPath header) {
fHeadersAlreadyIncluded.add(header);
}
public final boolean isAlreadyIncluded(IPath header) {
return fHeadersAlreadyIncluded.contains(header);
}
public final boolean isIncluded(IPath header) {
return fHeadersAlreadyIncluded.contains(header) || fHeadersToInclude.contains(header);
}
public void addHeadersIncludedPreviously(IASTPreprocessorIncludeStatement[] includes) {
for (IASTPreprocessorIncludeStatement include : includes) {
if (include.isPartOfTranslationUnitFile()) {
String path = include.getPath();
// An empty path means that the include was not resolved.
if (!path.isEmpty())
fHeadersIncludedPreviously.add(Path.fromOSString(path));
}
}
}
public final boolean wasIncludedPreviously(IPath header) {
return fHeadersIncludedPreviously.contains(header);
}
}

View file

@ -15,6 +15,7 @@ import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import org.eclipse.ui.IMemento;
@ -262,4 +263,21 @@ public class IncludeGroupStyle implements Comparable<IncludeGroupStyle> {
return c;
return includeKind.ordinal() - other.includeKind.ordinal();
}
public IncludeGroupStyle getGroupingStyle(Map<IncludeKind, IncludeGroupStyle> stylesMap) {
if (keepTogether)
return this;
IncludeGroupStyle parent = getParentStyle(stylesMap);
if (parent != null && (parent.keepTogether || parent.includeKind == IncludeKind.OTHER))
return parent;
return stylesMap.get(IncludeKind.OTHER);
}
public IncludeGroupStyle getParentStyle(Map<IncludeKind, IncludeGroupStyle> stylesMap) {
IncludeKind kind = includeKind.parent;
if (kind == null)
return null;
return stylesMap.get(kind);
}
}

View file

@ -23,6 +23,8 @@ import java.util.Set;
import org.eclipse.ui.IMemento;
import org.eclipse.cdt.internal.corext.codemanipulation.IncludeInfo;
/**
* A set of header file substitution rules.
*/

View file

@ -16,6 +16,7 @@ import static org.eclipse.cdt.internal.core.dom.parser.ASTTranslationUnit.getEnd
import static org.eclipse.cdt.internal.core.dom.parser.ASTTranslationUnit.getNodeEndOffset;
import static org.eclipse.cdt.internal.core.dom.parser.ASTTranslationUnit.getNodeOffset;
import static org.eclipse.cdt.internal.core.dom.parser.ASTTranslationUnit.getStartingLineNumber;
import static org.eclipse.cdt.internal.ui.refactoring.includes.IncludeUtil.isContainedInRegion;
import java.util.ArrayList;
import java.util.Arrays;
@ -26,8 +27,6 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IPath;
@ -89,9 +88,8 @@ import org.eclipse.cdt.internal.core.parser.scanner.CharArray;
import org.eclipse.cdt.internal.core.parser.scanner.ILocationResolver;
import org.eclipse.cdt.internal.core.parser.scanner.IncludeGuardDetection;
import org.eclipse.cdt.internal.core.parser.scanner.Lexer.LexerOptions;
import org.eclipse.cdt.internal.core.resources.ResourceLookup;
import org.eclipse.cdt.internal.ui.refactoring.includes.IncludeGroupStyle.IncludeKind;
import org.eclipse.cdt.internal.corext.codemanipulation.IncludeInfo;
import org.eclipse.cdt.internal.corext.codemanipulation.StyledInclude;
/**
* Organizes the include directives and forward declarations of a source or header file.
@ -99,88 +97,44 @@ import org.eclipse.cdt.internal.ui.refactoring.includes.IncludeGroupStyle.Includ
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 implements Comparable<IncludePrototype> {
final IPath header; // null for existing unresolved includes
final IncludeInfo includeInfo; // never null
IASTPreprocessorIncludeStatement existingInclude; // null for newly added includes
final boolean required; // true if the header has to be included
final IncludeGroupStyle style;
private static final Collator COLLATOR = Collator.getInstance();
/** Initializes an include prototype for a new include */
/**
* Represents a new or an existing include statement.
*/
private static class IncludePrototype extends StyledInclude {
private final boolean required; // true if the header has to be included
/** Initializes an include prototype object for a new include */
IncludePrototype(IPath header, IncludeInfo includeInfo, IncludeGroupStyle style) {
if (includeInfo == null)
throw new NullPointerException();
this.header = header;
this.includeInfo = includeInfo;
this.style = style;
super(header, includeInfo, style);
this.required = true;
}
/**
* Initializes an include prototype for an existing include. {@code header} may be
* Initializes an include prototype object for an existing include. {@code header} may be
* {@code null} if the include was not resolved.
*/
IncludePrototype(IASTPreprocessorIncludeStatement include, IPath header,
IncludeInfo includeInfo, IncludeGroupStyle style) {
if (includeInfo == null)
throw new NullPointerException();
this.existingInclude = include;
this.header = header;
this.includeInfo = includeInfo;
this.style = style;
IncludePrototype(IPath header, IncludeInfo includeInfo, IncludeGroupStyle style,
IASTPreprocessorIncludeStatement existingInclude) {
super(header, includeInfo, style, existingInclude);
this.required = false;
}
public void updateFrom(IncludePrototype other) {
this.existingInclude = other.existingInclude;
}
@Override
public int hashCode() {
if (header != null)
return header.hashCode(); // includeInfo is ignored if header is not null
return includeInfo.hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
IncludePrototype other = (IncludePrototype) obj;
if (header != null)
return header.equals(other.header); // includeInfo is ignored if header is not null
if (other.header != null)
return false;
return includeInfo.equals(other.includeInfo);
}
/** For debugging only */
@Override
public String toString() {
return header != null ? header.toPortableString() : includeInfo.toString();
}
@Override
public int compareTo(IncludePrototype other) {
return includeInfo.compareTo(other.includeInfo);
public boolean isRequired() {
return required;
}
}
private static final Collator COLLATOR = Collator.getInstance();
private final IHeaderChooser fHeaderChooser;
private final InclusionContext fContext;
private final IncludeCreationContext fContext;
private final String fLineDelimiter;
public IncludeOrganizer(ITranslationUnit tu, IIndex index, String lineDelimiter,
IHeaderChooser headerChooser) {
fLineDelimiter = lineDelimiter;
fHeaderChooser = headerChooser;
fContext = new InclusionContext(tu, index);
fContext = new IncludeCreationContext(tu, index);
}
/**
@ -195,14 +149,7 @@ public class IncludeOrganizer {
Set<IBinding> bindingsToDefine = bindingClassifier.getBindingsToDefine();
IASTPreprocessorIncludeStatement[] existingIncludes = ast.getIncludeDirectives();
for (IASTPreprocessorIncludeStatement include : existingIncludes) {
if (include.isPartOfTranslationUnitFile()) {
String path = include.getPath();
// An empty path means that the include was not resolved.
if (!path.isEmpty())
fContext.addHeaderIncludedPreviously(Path.fromOSString(path));
}
}
fContext.addHeadersIncludedPreviously(existingIncludes);
HeaderSubstitutor headerSubstitutor = new HeaderSubstitutor(fContext);
// Create the list of header files which have to be included by examining the list of
@ -218,12 +165,12 @@ public class IncludeOrganizer {
new HashMap<IncludePrototype, IncludePrototype>();
// Put the new includes into includePrototypes.
for (IPath header : fContext.getHeadersToInclude()) {
IncludeGroupStyle style = getIncludeStyle(header);
IncludeGroupStyle style = fContext.getIncludeStyle(header);
IncludeInfo includeInfo = createIncludeInfo(header, style);
IncludePrototype prototype = new IncludePrototype(header, includeInfo, style);
updateIncludePrototypes(includePrototypes, prototype);
}
// Put the existing includes into includePrototypes.
// Add existing includes to includePrototypes.
for (IASTPreprocessorIncludeStatement include : existingIncludes) {
if (include.isPartOfTranslationUnitFile()) {
String name = new String(include.getName().getSimpleID());
@ -232,14 +179,15 @@ public class IncludeOrganizer {
// An empty path means that the include was not resolved.
IPath header = path.isEmpty() ? null : Path.fromOSString(path);
IncludeGroupStyle style =
header != null ? getIncludeStyle(header) : getIncludeStyle(includeInfo);
IncludePrototype prototype = new IncludePrototype(include, header, includeInfo, style);
header != null ? fContext.getIncludeStyle(header) : fContext.getIncludeStyle(includeInfo);
IncludePrototype prototype = new IncludePrototype(header, includeInfo, style, include);
updateIncludePrototypes(includePrototypes, prototype);
}
}
NodeCommentMap commentedNodeMap = ASTCommenter.getCommentedNodeMap(ast);
IRegion includeReplacementRegion = getSafeIncludeReplacementRegion(ast, commentedNodeMap);
IRegion includeReplacementRegion =
getSafeIncludeReplacementRegion(fContext.getSourceContents(), ast, commentedNodeMap);
IncludePreferences preferences = fContext.getPreferences();
boolean allowReordering = preferences.allowReordering || existingIncludes.length == 0;
@ -250,9 +198,9 @@ public class IncludeOrganizer {
List<IncludePrototype>[] groupedPrototypes =
(List<IncludePrototype>[]) new List<?>[preferences.includeStyles.size()];
for (IncludePrototype prototype : includePrototypes.keySet()) {
if (prototype.existingInclude == null
|| (allowReordering && isContainedInRegion(prototype.existingInclude, includeReplacementRegion))) {
IncludeGroupStyle groupingStyle = getGroupingStyle(prototype.style);
if (prototype.getExistingInclude() == null
|| (allowReordering && isContainedInRegion(prototype.getExistingInclude(), includeReplacementRegion))) {
IncludeGroupStyle groupingStyle = prototype.getStyle().getGroupingStyle(preferences.includeStyles);
// If reordering is not allowed, group everything together.
int position = allowReordering ? groupingStyle.getOrder() : 0;
List<IncludePrototype> prototypes = groupedPrototypes[position];
@ -262,15 +210,15 @@ public class IncludeOrganizer {
}
prototypes.add(prototype);
}
if (!allowReordering && prototype.existingInclude != null
&& !prototype.required && prototype.header != null // Unused and resolved.
&& isContainedInRegion(prototype.existingInclude, includeReplacementRegion)) {
if (!allowReordering && prototype.getExistingInclude() != null
&& !prototype.isRequired() && prototype.getHeader() != null // Unused and resolved.
&& isContainedInRegion(prototype.getExistingInclude(), includeReplacementRegion)) {
switch (preferences.unusedStatementsDisposition) {
case REMOVE:
createDelete(prototype.existingInclude, edits);
createDelete(prototype.getExistingInclude(), edits);
break;
case COMMENT_OUT:
createCommentOut(prototype.existingInclude, edits);
createCommentOut(prototype.getExistingInclude(), edits);
break;
case KEEP:
break;
@ -283,9 +231,9 @@ public class IncludeOrganizer {
for (List<IncludePrototype> prototypes : groupedPrototypes) {
if (prototypes != null && !prototypes.isEmpty()) {
Collections.sort(prototypes);
IncludeGroupStyle style = prototypes.get(0).style;
IncludeGroupStyle groupingStyle = getGroupingStyle(style);
IncludeGroupStyle parentStyle = getParentStyle(groupingStyle);
IncludeGroupStyle style = prototypes.get(0).getStyle();
IncludeGroupStyle groupingStyle = style.getGroupingStyle(preferences.includeStyles);
IncludeGroupStyle parentStyle = groupingStyle.getParentStyle(preferences.includeStyles);
boolean blankLineBefore = groupingStyle.isBlankLineBefore() ||
(parentStyle != null && parentStyle != previousParentStyle &&
parentStyle.isKeepTogether() && parentStyle.isBlankLineBefore());
@ -294,9 +242,9 @@ public class IncludeOrganizer {
includeDirectives.add(""); // Blank line separator //$NON-NLS-1$
for (IncludePrototype prototype : prototypes) {
String trailingComment = ""; //$NON-NLS-1$
IASTPreprocessorIncludeStatement include = prototype.existingInclude;
IASTPreprocessorIncludeStatement include = prototype.getExistingInclude();
if (include == null
|| (allowReordering && isContainedInRegion(include, includeReplacementRegion))) {
|| (allowReordering && IncludeUtil.isContainedInRegion(include, includeReplacementRegion))) {
if (include != null) {
List<IASTComment> comments = commentedNodeMap.getTrailingCommentsForNode(include);
StringBuilder buf = new StringBuilder();
@ -522,7 +470,7 @@ public class IncludeOrganizer {
IASTFileLocation location = include.getFileLocation();
int offset = location.getNodeOffset();
if (fContext.getTranslationUnit().isCXXLanguage()) {
offset = getLineStart(offset);
offset = getLineStart(fContext.getSourceContents(), offset);
edits.add(new InsertEdit(offset, "//")); //$NON-NLS-1$
} else {
edits.add(new InsertEdit(offset, "/*")); //$NON-NLS-1$
@ -535,8 +483,8 @@ public class IncludeOrganizer {
IASTFileLocation location = include.getFileLocation();
int offset = location.getNodeOffset();
int endOffset = offset + location.getNodeLength();
offset = getLineStart(offset);
endOffset = skipToNextLine(endOffset);
offset = getLineStart(fContext.getSourceContents(), offset);
endOffset = skipToNextLine(fContext.getSourceContents(), endOffset);
edits.add(new DeleteEdit(offset, endOffset - offset));
}
@ -546,16 +494,12 @@ public class IncludeOrganizer {
if (existing == null) {
includePrototypes.put(prototype, prototype);
} else {
existing.updateFrom(prototype);
existing.setExistingInclude(prototype.getExistingInclude());
}
}
private boolean isContainedInRegion(IASTNode node, IRegion region) {
return getNodeOffset(node) >= region.getOffset()
&& getNodeEndOffset(node) <= region.getOffset() + region.getLength();
}
private IRegion getSafeIncludeReplacementRegion(IASTTranslationUnit ast, NodeCommentMap commentMap) {
static IRegion getSafeIncludeReplacementRegion(char[] contents, IASTTranslationUnit ast,
NodeCommentMap commentMap) {
int maxSafeOffset = ast.getFileLocation().getNodeLength();
IASTDeclaration[] declarations = ast.getDeclarations(true);
if (declarations.length != 0)
@ -592,22 +536,22 @@ public class IncludeOrganizer {
}
if (includeOffset < 0) {
if (includeGuardEndOffset >= 0) {
includeOffset = skipToNextLine(includeGuardEndOffset);
includeOffset = skipToNextLine(contents, includeGuardEndOffset);
} else {
includeOffset = 0;
}
if (!topCommentSkipped) {
// Skip the first comment block near the top of the file.
includeOffset = skipStandaloneCommentBlock(includeOffset, maxSafeOffset, ast.getComments(), commentMap);
includeOffset = skipStandaloneCommentBlock(contents, includeOffset, maxSafeOffset, ast.getComments(), commentMap);
}
includeEndOffset = includeOffset;
} else {
includeEndOffset = skipToNextLine(includeEndOffset);
includeEndOffset = skipToNextLine(contents, includeEndOffset);
}
return new Region(includeOffset, includeEndOffset - includeOffset);
}
private int getNumberOfIncludeGuardStatementsToSkip(IASTTranslationUnit ast) {
private static int getNumberOfIncludeGuardStatementsToSkip(IASTTranslationUnit ast) {
IASTPreprocessorStatement statement = findFirstPreprocessorStatement(ast);
if (statement == null)
return 0;
@ -620,7 +564,7 @@ public class IncludeOrganizer {
}
char[] contents = ast.getRawSignature().toCharArray();
if (offset != 0)
Arrays.copyOfRange(contents, offset, contents.length);
contents = Arrays.copyOfRange(contents, offset, contents.length);
CharArrayIntMap ppKeywords= new CharArrayIntMap(40, -1);
Keywords.addKeywordsPreprocessor(ppKeywords);
if (IncludeGuardDetection.detectIncludeGuard(new CharArray(contents), new LexerOptions(), ppKeywords) != null) {
@ -629,39 +573,38 @@ public class IncludeOrganizer {
return num;
}
private IASTPreprocessorStatement findFirstPreprocessorStatement(IASTTranslationUnit ast) {
private static IASTPreprocessorStatement findFirstPreprocessorStatement(IASTTranslationUnit ast) {
for (IASTPreprocessorStatement statement : ast.getAllPreprocessorStatements()) {
if (statement.isPartOfTranslationUnitFile())
return statement;
}
return null;
}
private boolean isPragmaOnce(IASTPreprocessorStatement statement) {
private static boolean isPragmaOnce(IASTPreprocessorStatement statement) {
if (!(statement instanceof IASTPreprocessorPragmaStatement))
return false;
return CharArrayUtils.equals(((IASTPreprocessorPragmaStatement) statement).getMessage(), "once"); //$NON-NLS-1$
}
private int skipToNextLine(int offset) {
char[] contents = fContext.getTranslationUnit().getContents();
while (offset < contents.length) {
if (contents[offset++] == '\n')
private static int skipToNextLine(char[] text, int offset) {
while (offset < text.length) {
if (text[offset++] == '\n')
break;
}
return offset;
}
private int getLineStart(int offset) {
char[] contents = fContext.getTranslationUnit().getContents();
private static int getLineStart(char[] text, int offset) {
while (--offset >= 0) {
if (contents[offset] == '\n')
if (text[offset] == '\n')
break;
}
return offset + 1;
}
private int skipToNextLineAfterNode(IASTNode node) {
return skipToNextLine(getNodeEndOffset(node));
private static int skipToNextLineAfterNode(char[] text, IASTNode node) {
return skipToNextLine(text, getNodeEndOffset(node));
}
/**
@ -720,7 +663,8 @@ public class IncludeOrganizer {
return ""; //$NON-NLS-1$
}
private int skipStandaloneCommentBlock(int offset, int endOffset, IASTComment[] comments, NodeCommentMap commentMap) {
private static int skipStandaloneCommentBlock(char[] contents, int offset, int endOffset,
IASTComment[] comments, NodeCommentMap commentMap) {
Map<IASTComment, IASTNode> inverseLeadingMap = new HashMap<IASTComment, IASTNode>();
for (Map.Entry<IASTNode, List<IASTComment>> entry : commentMap.getLeadingMap().entrySet()) {
IASTNode node = entry.getKey();
@ -753,11 +697,11 @@ public class IncludeOrganizer {
for (int j = 1; j < leadingComments.size(); j++) {
comment = leadingComments.get(j);
if (getStartingLineNumber(comment) > getEndingLineNumber(previous) + 1)
return skipToNextLineAfterNode(previous);
return skipToNextLineAfterNode(contents, previous);
previous = comment;
}
if (getStartingLineNumber(node) > getEndingLineNumber(previous) + 1)
return skipToNextLineAfterNode(previous);
return skipToNextLineAfterNode(contents, previous);
}
node = inverseFreestandingMap.get(comment);
if (node != null) {
@ -766,7 +710,7 @@ public class IncludeOrganizer {
for (int j = 1; j < freestandingComments.size(); j++) {
comment = freestandingComments.get(j);
if (getStartingLineNumber(comment) > getEndingLineNumber(previous) + 1)
return skipToNextLineAfterNode(previous);
return skipToNextLineAfterNode(contents, previous);
previous = comment;
}
}
@ -775,77 +719,6 @@ public class IncludeOrganizer {
return offset;
}
private IncludeGroupStyle getGroupingStyle(IncludeGroupStyle style) {
if (style.isKeepTogether())
return style;
IncludeGroupStyle parent = getParentStyle(style);
if (parent != null && (parent.isKeepTogether() || parent.getIncludeKind() == IncludeKind.OTHER))
return parent;
return fContext.getPreferences().includeStyles.get(IncludeKind.OTHER);
}
private IncludeGroupStyle getParentStyle(IncludeGroupStyle style) {
IncludeKind kind = style.getIncludeKind().parent;
if (kind == null)
return null;
return fContext.getPreferences().includeStyles.get(kind);
}
private IncludeGroupStyle getIncludeStyle(IPath headerPath) {
IncludeKind includeKind;
IncludeInfo includeInfo = fContext.getIncludeForHeaderFile(headerPath);
if (includeInfo != null && includeInfo.isSystem()) {
if (headerPath.getFileExtension() == null) {
includeKind = IncludeKind.SYSTEM_WITHOUT_EXTENSION;
} else {
includeKind = IncludeKind.SYSTEM_WITH_EXTENSION;
}
} else if (isPartnerFile(headerPath)) {
includeKind = IncludeKind.PARTNER;
} else {
IPath dir = fContext.getCurrentDirectory();
if (dir.isPrefixOf(headerPath)) {
if (headerPath.segmentCount() == dir.segmentCount() + 1) {
includeKind = IncludeKind.IN_SAME_FOLDER;
} else {
includeKind = IncludeKind.IN_SUBFOLDER;
}
} else {
IFile[] files = ResourceLookup.findFilesForLocation(headerPath);
if (files.length == 0) {
includeKind = IncludeKind.EXTERNAL;
} else {
IProject project = fContext.getProject();
includeKind = IncludeKind.IN_OTHER_PROJECT;
for (IFile file : files) {
if (file.getProject().equals(project)) {
includeKind = IncludeKind.IN_SAME_PROJECT;
break;
}
}
}
}
}
return fContext.getPreferences().includeStyles.get(includeKind);
}
private IncludeGroupStyle getIncludeStyle(IncludeInfo includeInfo) {
IncludeKind includeKind;
IPath path = Path.fromPortableString(includeInfo.getName());
if (includeInfo.isSystem()) {
if (path.getFileExtension() == null) {
includeKind = IncludeKind.SYSTEM_WITHOUT_EXTENSION;
} else {
includeKind = IncludeKind.SYSTEM_WITH_EXTENSION;
}
} else if (isPartnerFile(path)) {
includeKind = IncludeKind.PARTNER;
} else {
includeKind = IncludeKind.EXTERNAL;
}
return fContext.getPreferences().includeStyles.get(includeKind);
}
private Set<IBinding> removeBindingsDefinedInIncludedHeaders(IASTTranslationUnit ast,
Set<IBinding> bindings, IIndexFileSet reachableHeaders) throws CoreException {
Set<IBinding> filteredBindings = new HashSet<IBinding>(bindings);
@ -888,7 +761,7 @@ public class IncludeOrganizer {
List<IPath> candidatePaths = request.getCandidatePaths();
if (candidatePaths.size() == 1) {
IPath path = candidatePaths.iterator().next();
if (isPartnerFile(path)) {
if (fContext.isPartnerFile(path)) {
request.resolve(path);
fContext.addHeaderToInclude(path);
if (includedByPartner != null) {
@ -1080,33 +953,6 @@ public class IncludeOrganizer {
return headerSubstitutor.getExportingHeaders(symbol);
}
/**
* Checks if the given path points to a partner header of the current translation unit.
* A header is considered a partner if its name without extension is the same as the name of
* the translation unit, or the name of the translation unit differs by one of the suffixes
* used for test files.
*/
private boolean isPartnerFile(IPath path) {
String headerName = path.removeFileExtension().lastSegment();
String sourceName = fContext.getTranslationUnit().getLocation().removeFileExtension().lastSegment();
if (headerName.equals(sourceName))
return true;
if (sourceName.startsWith(headerName)) {
int pos = headerName.length();
while (pos < sourceName.length() && !Character.isLetterOrDigit(sourceName.charAt(pos))) {
pos++;
}
if (pos == sourceName.length())
return true;
String suffix = sourceName.substring(pos);
for (String s : fContext.getPreferences().partnerFileSuffixes) {
if (suffix.equalsIgnoreCase(s))
return true;
}
}
return false;
}
private List<InclusionRequest> createInclusionRequests(IASTTranslationUnit ast,
Set<IBinding> bindingsToDefine, boolean allowDeclarations,
IIndexFileSet reachableHeaders) throws CoreException {
@ -1202,7 +1048,7 @@ public class IncludeOrganizer {
private String createIncludeDirective(IncludePrototype include, String lineComment) {
StringBuilder buf = new StringBuilder();
// Unresolved includes are preserved out of caution.
if (!include.required && include.header != null) {
if (!include.isRequired() && include.getHeader() != null) {
switch (fContext.getPreferences().unusedStatementsDisposition) {
case REMOVE:
return null;
@ -1214,7 +1060,7 @@ public class IncludeOrganizer {
}
}
buf.append("#include "); //$NON-NLS-1$
buf.append(include.includeInfo.toString());
buf.append(include.getIncludeInfo().toString());
buf.append(lineComment);
return buf.toString();
}

View file

@ -10,11 +10,16 @@
*******************************************************************************/
package org.eclipse.cdt.internal.ui.refactoring.includes;
import static org.eclipse.cdt.internal.core.dom.parser.ASTTranslationUnit.getNodeEndOffset;
import static org.eclipse.cdt.internal.core.dom.parser.ASTTranslationUnit.getNodeOffset;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.content.IContentType;
import org.eclipse.jface.text.IRegion;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.index.IIndexFile;
import org.eclipse.cdt.core.index.IIndexFileLocation;
import org.eclipse.cdt.core.index.IndexLocationFactory;
@ -65,4 +70,9 @@ public class IncludeUtil {
public static String getPath(IIndexFileLocation fileLocation) {
return IndexLocationFactory.getAbsolutePath(fileLocation).toOSString();
}
public static boolean isContainedInRegion(IASTNode node, IRegion region) {
return getNodeOffset(node) >= region.getOffset()
&& getNodeEndOffset(node) <= region.getOffset() + region.getLength();
}
}

View file

@ -28,6 +28,8 @@ import org.eclipse.ui.XMLMemento;
import org.eclipse.cdt.ui.CUIPlugin;
import org.eclipse.cdt.internal.corext.codemanipulation.IncludeInfo;
/**
* A set of header file substitution rules.
*/

View file

@ -15,6 +15,7 @@
package org.eclipse.cdt.internal.ui.wizards.classwizard;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
@ -63,13 +64,18 @@ import org.eclipse.cdt.ui.CUIPlugin;
import org.eclipse.cdt.ui.CodeGeneration;
import org.eclipse.cdt.utils.PathUtil;
import org.eclipse.cdt.internal.corext.codemanipulation.IncludeInfo;
import org.eclipse.cdt.internal.corext.codemanipulation.InclusionContext;
import org.eclipse.cdt.internal.corext.codemanipulation.StubUtility;
import org.eclipse.cdt.internal.corext.codemanipulation.StyledInclude;
import org.eclipse.cdt.internal.corext.util.CModelUtil;
import org.eclipse.cdt.internal.corext.util.CodeFormatterUtil;
import org.eclipse.cdt.internal.corext.util.Strings;
import org.eclipse.cdt.internal.formatter.scanner.Scanner;
import org.eclipse.cdt.internal.formatter.scanner.Token;
import org.eclipse.cdt.internal.ui.refactoring.includes.IncludeGroupStyle;
import org.eclipse.cdt.internal.ui.refactoring.includes.IncludePreferences;
import org.eclipse.cdt.internal.ui.wizards.filewizard.NewSourceFileGenerator;
public class NewClassCodeGenerator {
@ -703,7 +709,6 @@ public class NewClassCodeGenerator {
ICProject cProject = headerTU.getCProject();
IProject project = cProject.getProject();
IPath projectLocation = new Path(project.getLocationURI().getPath());
IPath headerLocation = new Path(headerTU.getResource().getLocationURI().getPath());
List<IPath> includePaths = getIncludePaths(headerTU);
List<IPath> baseClassPaths = getBaseClassPaths(verifyBaseClasses());
@ -716,52 +721,36 @@ public class NewClassCodeGenerator {
}
}
List<IPath> systemIncludes = new ArrayList<IPath>();
List<IPath> localIncludes = new ArrayList<IPath>();
// Sort the include paths into system and local
InclusionContext inclusionContext = new InclusionContext(headerTU);
List<StyledInclude> includes = new ArrayList<StyledInclude>();
for (IPath baseClassLocation : baseClassPaths) {
boolean isSystemIncludePath = false;
IncludeInfo includeInfo = inclusionContext.getIncludeForHeaderFile(baseClassLocation);
if (includeInfo != null) {
IncludeGroupStyle style = inclusionContext.getIncludeStyle(includeInfo);
includes.add(new StyledInclude(baseClassLocation, includeInfo, style));
}
}
Collections.sort(includes);
IPath includePath = PathUtil.makeRelativePathToProjectIncludes(baseClassLocation, project);
if (includePath != null && !projectLocation.isPrefixOf(baseClassLocation)) {
isSystemIncludePath = true;
} else if (projectLocation.isPrefixOf(baseClassLocation)
&& projectLocation.isPrefixOf(headerLocation)) {
includePath = PathUtil.makeRelativePath(baseClassLocation, headerLocation.removeLastSegments(1));
}
if (includePath == null)
includePath = baseClassLocation;
// Make the new #include path in the source file only point to a relative file
// (i.e. now that the path has been included above in the project)
includePath = includePath.removeFirstSegments(includePath.segmentCount() - 1).setDevice(null);
if (isSystemIncludePath)
systemIncludes.add(includePath);
else
localIncludes.add(includePath);
}
StringBuilder text = new StringBuilder();
// Write the system include paths, e.g. #include <header.h>
for (IPath includePath : systemIncludes) {
if (!(headerTU.getElementName().equals(includePath.toString()))) {
String include = getIncludeString(includePath.toString(), true);
text.append(include);
text.append(lineDelimiter);
IncludePreferences preferences = inclusionContext.getPreferences();
IncludeGroupStyle previousParentStyle = null;
for (StyledInclude include : includes) {
IncludeGroupStyle style = include.getStyle();
IncludeGroupStyle groupingStyle = style.getGroupingStyle(preferences.includeStyles);
IncludeGroupStyle parentStyle = groupingStyle.getParentStyle(preferences.includeStyles);
if (groupingStyle.isBlankLineBefore() ||
(parentStyle != null && previousParentStyle != null &&
parentStyle != previousParentStyle && parentStyle.isKeepTogether() &&
parentStyle.isBlankLineBefore())) {
text.append(lineDelimiter);
}
}
// Write the local include paths, e.g. #include "header.h"
for (IPath includePath : localIncludes) {
if (!(headerTU.getElementName().equals(includePath.toString()))) {
String include = getIncludeString(includePath.toString(), false);
text.append(include);
text.append(lineDelimiter);
}
}
text.append("#include "); //$NON-NLS-1$
text.append(include.getIncludeInfo().toString());
text.append(lineDelimiter);
previousParentStyle = parentStyle;
}
monitor.done();
return text.toString();
}
@ -1089,24 +1078,15 @@ public class NewClassCodeGenerator {
private String getHeaderIncludeString(ITranslationUnit sourceTU, ITranslationUnit headerTU,
IProgressMonitor monitor) {
IProject project = headerTU.getCProject().getProject();
IPath projectLocation = new Path(project.getLocationURI().getPath());
IPath headerLocation = new Path(headerTU.getResource().getLocationURI().getPath());
IPath sourceLocation = new Path(sourceTU.getResource().getLocationURI().getPath());
IPath includePath = PathUtil.makeRelativePathToProjectIncludes(headerLocation, project);
boolean isSystemIncludePath = false;
if (headerTU.getResource() == null && includePath != null
&& !projectLocation.isPrefixOf(headerLocation)) {
isSystemIncludePath = true;
} else if (projectLocation.isPrefixOf(headerLocation)
&& projectLocation.isPrefixOf(sourceLocation)) {
includePath = PathUtil.makeRelativePath(headerLocation, sourceLocation.removeLastSegments(1));
InclusionContext inclusionContext = new InclusionContext(sourceTU);
IncludeInfo includeInfo = inclusionContext.getIncludeForHeaderFile(headerLocation);
if (includeInfo == null) {
includeInfo = new IncludeInfo(headerLocation.toString(), false);
}
if (includePath == null)
includePath = headerLocation;
return getIncludeString(includePath.toString(), isSystemIncludePath);
return "#include " + includeInfo.toString(); //$NON-NLS-1$
}
private boolean hasInclude(String contents, String include) {
@ -1208,21 +1188,6 @@ public class NewClassCodeGenerator {
return text.toString();
}
private String getIncludeString(String fileName, boolean isSystemInclude) {
StringBuilder buf = new StringBuilder();
buf.append("#include "); //$NON-NLS-1$
if (isSystemInclude)
buf.append('<');
else
buf.append('\"');
buf.append(fileName);
if (isSystemInclude)
buf.append('>');
else
buf.append('\"');
return buf.toString();
}
private int findLastLineChar(String contents, int startPos) {
int endPos = contents.length() - 1;
int linePos = startPos;