From eca8e6004cc51c21df0a4a2f654e3a50e6b76582 Mon Sep 17 00:00:00 2001 From: Sergey Prigogin Date: Sun, 11 Aug 2013 20:51:32 -0700 Subject: [PATCH] Bug 414838 - New class creation doesn't properly handle include path with directories for non-system headers. --- .../includes/BindingClassifierTest.java | 4 +- .../AddIncludesOperation.java | 1 - .../codemanipulation}/IncludeInfo.java | 2 +- .../codemanipulation}/InclusionContext.java | 196 ++++++------ .../codemanipulation/StyledInclude.java | 102 ++++++ .../editor/AddIncludeOnSelectionAction.java | 6 +- .../ui/editor/InteractiveHeaderChooser.java | 8 +- .../ui/editor/OrganizeIncludesAction.java | 5 +- .../HeaderSubstitutionMapEditDialog.java | 3 +- .../includes/BindingClassifier.java | 4 +- .../includes/HeaderSubstitutionMap.java | 2 + .../includes/HeaderSubstitutor.java | 5 +- .../includes/IncludeCreationContext.java | 142 +++++++++ .../includes/IncludeGroupStyle.java | 18 ++ .../ui/refactoring/includes/IncludeMap.java | 2 + .../includes/IncludeOrganizer.java | 292 +++++------------- .../ui/refactoring/includes/IncludeUtil.java | 10 + .../refactoring/includes/SymbolExportMap.java | 2 + .../classwizard/NewClassCodeGenerator.java | 111 +++---- 19 files changed, 497 insertions(+), 418 deletions(-) rename core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/{ui/refactoring/includes => corext/codemanipulation}/IncludeInfo.java (98%) rename core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/{ui/refactoring/includes => corext/codemanipulation}/InclusionContext.java (60%) create mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/codemanipulation/StyledInclude.java create mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeCreationContext.java diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/BindingClassifierTest.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/BindingClassifierTest.java index 41151dd4335..1c1d0d1bee9 100644 --- a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/BindingClassifierTest.java +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/BindingClassifierTest.java @@ -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); } diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/codemanipulation/AddIncludesOperation.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/codemanipulation/AddIncludesOperation.java index 86267ecb2c5..fba5e650ca0 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/codemanipulation/AddIncludesOperation.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/codemanipulation/AddIncludesOperation.java @@ -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. diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeInfo.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/codemanipulation/IncludeInfo.java similarity index 98% rename from core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeInfo.java rename to core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/codemanipulation/IncludeInfo.java index cce9689a05e..7f7c3bbd58f 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeInfo.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/codemanipulation/IncludeInfo.java @@ -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; diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/InclusionContext.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/codemanipulation/InclusionContext.java similarity index 60% rename from core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/InclusionContext.java rename to core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/codemanipulation/InclusionContext.java index 18a7c731784..eca6e46f842 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/InclusionContext.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/codemanipulation/InclusionContext.java @@ -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 fIncludeResolutionCache; private final Map fInverseIncludeResolutionCache; - private final IIndex fIndex; - private final Set fHeadersToInclude; - private final Set fHeadersAlreadyIncluded; - private final Set 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(); fInverseIncludeResolutionCache = new HashMap(); - fHeadersToInclude = new HashSet(); - fHeadersAlreadyIncluded = new HashSet(); - fHeadersIncludedPreviously = new HashSet(); + 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 filesByPath = new HashMap(); - for (IIndexFile file : fIndex.getAllFiles()) { - IPath path = getPath(file); - filesByPath.put(path, file); - } - - Set exportedHeaders = new HashSet(); - 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 queue = new ArrayDeque(); - 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 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 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); - } -} +} \ No newline at end of file diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/codemanipulation/StyledInclude.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/codemanipulation/StyledInclude.java new file mode 100644 index 00000000000..f9012e1a9f6 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/codemanipulation/StyledInclude.java @@ -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 { + 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(); + } +} \ No newline at end of file diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/AddIncludeOnSelectionAction.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/AddIncludeOnSelectionAction.java index b6ffa3337fc..90f87d79e13 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/AddIncludeOnSelectionAction.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/AddIncludeOnSelectionAction.java @@ -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) { diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/InteractiveHeaderChooser.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/InteractiveHeaderChooser.java index 4adcddebc56..7b810769264 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/InteractiveHeaderChooser.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/InteractiveHeaderChooser.java @@ -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, IPath> userChoiceCache; + private final Map, IPath> userChoiceCache; - public InteractiveHeaderChooser(Shell shell) { + public InteractiveHeaderChooser(String title, Shell shell) { + this.title = title; this.shell = shell; userChoiceCache = new HashMap, 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(); diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/OrganizeIncludesAction.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/OrganizeIncludesAction.java index 72dcc7d6c5e..6c8a5161a84 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/OrganizeIncludesAction.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/OrganizeIncludesAction.java @@ -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 edits = new ArrayList(); SharedASTJob job = new SharedASTJob(CEditorMessages.OrganizeIncludes_action, tu) { diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/HeaderSubstitutionMapEditDialog.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/HeaderSubstitutionMapEditDialog.java index 50b4803668d..9abef939693 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/HeaderSubstitutionMapEditDialog.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/HeaderSubstitutionMapEditDialog.java @@ -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; diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/BindingClassifier.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/BindingClassifier.java index 0ad0fa3223e..2fb578b487b 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/BindingClassifier.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/BindingClassifier.java @@ -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 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(); diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/HeaderSubstitutionMap.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/HeaderSubstitutionMap.java index 4982b05b619..3f7b92110eb 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/HeaderSubstitutionMap.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/HeaderSubstitutionMap.java @@ -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. */ diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/HeaderSubstitutor.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/HeaderSubstitutor.java index 30c6b9e9858..bb69d62520d 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/HeaderSubstitutor.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/HeaderSubstitutor.java @@ -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(); diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeCreationContext.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeCreationContext.java new file mode 100644 index 00000000000..24a7bde4e39 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeCreationContext.java @@ -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 fHeadersToInclude; + private final Set fHeadersAlreadyIncluded; + private final Set fHeadersIncludedPreviously; + + public IncludeCreationContext(ITranslationUnit tu, IIndex index) { + super(tu); + fIndex = index; + fHeadersToInclude = new HashSet(); + fHeadersAlreadyIncluded = new HashSet(); + fHeadersIncludedPreviously = new HashSet(); + } + + 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 filesByPath = new HashMap(); + for (IIndexFile file : fIndex.getAllFiles()) { + IPath path = getPath(file); + filesByPath.put(path, file); + } + + Set exportedHeaders = new HashSet(); + 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 queue = new ArrayDeque(); + 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 getHeadersToInclude() { + return fHeadersToInclude; + } + + public final void addHeaderToInclude(IPath header) { + fHeadersToInclude.add(header); + } + + public final boolean isToBeIncluded(IPath header) { + return fHeadersToInclude.contains(header); + } + + public Set 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); + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeGroupStyle.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeGroupStyle.java index e6b2e2a6c6b..6b9c4802eef 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeGroupStyle.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeGroupStyle.java @@ -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 { return c; return includeKind.ordinal() - other.includeKind.ordinal(); } + + + public IncludeGroupStyle getGroupingStyle(Map 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 stylesMap) { + IncludeKind kind = includeKind.parent; + if (kind == null) + return null; + return stylesMap.get(kind); + } } diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeMap.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeMap.java index bd496b28c2c..447fb9c0bce 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeMap.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeMap.java @@ -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. */ diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeOrganizer.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeOrganizer.java index e5decf5150c..aa7612c6330 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeOrganizer.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeOrganizer.java @@ -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 { - 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 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(); // 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[] groupedPrototypes = (List[]) 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 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 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 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 inverseLeadingMap = new HashMap(); for (Map.Entry> 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 removeBindingsDefinedInIncludedHeaders(IASTTranslationUnit ast, Set bindings, IIndexFileSet reachableHeaders) throws CoreException { Set filteredBindings = new HashSet(bindings); @@ -888,7 +761,7 @@ public class IncludeOrganizer { List 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 createInclusionRequests(IASTTranslationUnit ast, Set 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(); } diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeUtil.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeUtil.java index 1f090cc552a..f831c8cec31 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeUtil.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/IncludeUtil.java @@ -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(); + } } diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/SymbolExportMap.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/SymbolExportMap.java index 74cfe9ba92a..b098ee433b0 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/SymbolExportMap.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/includes/SymbolExportMap.java @@ -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. */ diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/wizards/classwizard/NewClassCodeGenerator.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/wizards/classwizard/NewClassCodeGenerator.java index 776524cbc25..e39bb79720e 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/wizards/classwizard/NewClassCodeGenerator.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/wizards/classwizard/NewClassCodeGenerator.java @@ -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 includePaths = getIncludePaths(headerTU); List baseClassPaths = getBaseClassPaths(verifyBaseClasses()); @@ -716,52 +721,36 @@ public class NewClassCodeGenerator { } } - List systemIncludes = new ArrayList(); - List localIncludes = new ArrayList(); - - // Sort the include paths into system and local + InclusionContext inclusionContext = new InclusionContext(headerTU); + List includes = new ArrayList(); 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 - 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;