/******************************************************************************* * Copyright (c) 2012, 2015 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.core.parser.util.ArrayUtil; import org.eclipse.cdt.internal.corext.codemanipulation.InclusionContext; /** * Context for managing include statements. */ public final 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<>(); fHeadersAlreadyIncluded = new HashSet<>(); fHeadersIncludedPreviously = new HashSet<>(); } public final 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<>(); for (IIndexFile file : fIndex.getAllFiles()) { IPath path = getPath(file); filesByPath.put(path, file); } removeExportedHeaders(fHeadersAlreadyIncluded, filesByPath); removeExportedHeaders(fHeadersToInclude, filesByPath); } private void removeExportedHeaders(Set<IPath> exportingHeaders, Map<IPath, IIndexFile> filesByPath) throws CoreException { Set<IPath> exportedHeaders = new HashSet<>(); for (IPath path : exportingHeaders) { 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<>(); queue.add(file); while ((file = queue.pollFirst()) != null) { for (IIndexInclude include : file.getIncludes()) { if (getPreferences().allowIndirectInclusion || isIncludedFileExported(include)) { file = fIndex.resolveInclude(include); if (file != null) { if (exportedHeaders.add(getPath(file))) queue.add(file); } } } } } } } fHeadersToInclude.removeAll(exportedHeaders); } private boolean isIncludedFileExported(IIndexInclude include) throws CoreException { if (include.isIncludedFileExported()) return true; String name = include.getName(); int index = name.lastIndexOf('.'); String extension = index >= 0 ? name.substring(index + 1) : ""; //$NON-NLS-1$ return ArrayUtil.containsEqual(getPreferences().extensionsOfAutoExportedFiles, extension); } 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); fHeadersAlreadyIncluded.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); } /** * Returns the path of the partner header included previously, or {@code null} if the partner was not * included. */ public final IPath getPartnerHeaderIncludedPreviously() { for (IPath path : fHeadersIncludedPreviously) { if (isPartnerFile(path)) return path; } return null; } /** * Checks if the given file is suitable for inclusion. A file is suitable for inclusion if it is a header * file, or if it is already included by some other file. */ public final boolean canBeIncluded(IIndexFile indexFile) throws CoreException { return !IncludeUtil.isSource(indexFile, getProject()) || fIndex.findIncludedBy(indexFile, 0).length != 0; } }