/*******************************************************************************
* Copyright (c) 2008, 2010 Wind River Systems, 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:
* Markus Schorn - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.internal.ui.actions;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.ui.IObjectActionDelegate;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchPartSite;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.dom.ILinkage;
import org.eclipse.cdt.core.dom.ast.ASTVisitor;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorIncludeStatement;
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorMacroDefinition;
import org.eclipse.cdt.core.dom.ast.IASTProblem;
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.IProblemBinding;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTQualifiedName;
import org.eclipse.cdt.core.index.IIndex;
import org.eclipse.cdt.core.index.IIndexFile;
import org.eclipse.cdt.core.index.IIndexFileLocation;
import org.eclipse.cdt.core.index.IIndexInclude;
import org.eclipse.cdt.core.index.IndexLocationFactory;
import org.eclipse.cdt.core.model.CModelException;
import org.eclipse.cdt.core.model.CoreModelUtil;
import org.eclipse.cdt.core.model.ICProject;
import org.eclipse.cdt.core.model.ILanguage;
import org.eclipse.cdt.core.model.ITranslationUnit;
import org.eclipse.cdt.core.parser.ExtendedScannerInfo;
import org.eclipse.cdt.core.parser.ISignificantMacros;
import org.eclipse.cdt.core.settings.model.ICConfigurationDescription;
import org.eclipse.cdt.core.settings.model.ICProjectDescription;
import org.eclipse.cdt.core.settings.model.ICProjectDescriptionManager;
import org.eclipse.cdt.ui.CUIPlugin;
import org.eclipse.cdt.internal.core.model.ASTCache;
import org.eclipse.cdt.internal.core.model.TranslationUnit;
import org.eclipse.cdt.internal.core.pdom.ASTFilePathResolver;
import org.eclipse.cdt.internal.core.pdom.PDOM;
import org.eclipse.cdt.internal.core.pdom.indexer.ProjectIndexerInputAdapter;
import org.eclipse.cdt.internal.ui.editor.ASTProvider;
@SuppressWarnings("nls")
public class CreateParserLogAction implements IObjectActionDelegate {
private static final String INDENT = " ";
private static final class MyVisitor extends ASTVisitor {
List<IASTProblem> fProblems= new ArrayList<IASTProblem>();
List<IProblemBinding> fProblemBindings= new ArrayList<IProblemBinding>();
List<Exception> fExceptions= new ArrayList<Exception>();
MyVisitor() {
shouldVisitProblems= true;
shouldVisitNames= true;
}
@Override
public int visit(IASTProblem problem) {
fProblems.add(problem);
return PROCESS_SKIP;
}
@Override
public int visit(IASTName name) {
if (name instanceof ICPPASTQualifiedName) {
return PROCESS_CONTINUE;
}
try {
IBinding binding= name.resolveBinding();
if (binding instanceof IProblemBinding) {
fProblemBindings.add((IProblemBinding) binding);
}
} catch (RuntimeException e) {
fExceptions.add(e);
}
return PROCESS_CONTINUE;
}
}
private static final Comparator<String> COMP_INSENSITIVE= new Comparator<String> () {
public int compare(String o1, String o2) {
return o1.toUpperCase().compareTo(o2.toUpperCase());
}
};
private ISelection fSelection;
private IWorkbenchPartSite fSite;
private boolean fWroteUnresolvedTitle;
public void setActivePart(IAction action, IWorkbenchPart targetPart) {
fSite= targetPart.getSite();
}
public void selectionChanged(IAction action, ISelection selection) {
fSelection = selection;
}
public void run(IAction action) {
if (!(fSelection instanceof IStructuredSelection))
return;
final String title= action.getText().replace("&", "");
IStructuredSelection cElements= SelectionConverter.convertSelectionToCElements(fSelection);
Iterator<?> i= cElements.iterator();
ArrayList<ITranslationUnit> tuSelection= new ArrayList<ITranslationUnit>();
while (i.hasNext()) {
Object o= i.next();
if (o instanceof ITranslationUnit) {
tuSelection.add((ITranslationUnit) o);
}
}
ITranslationUnit[] tuArray= tuSelection.toArray(new ITranslationUnit[tuSelection.size()]);
if (tuArray.length == 0) {
return;
}
FileDialog dlg= new FileDialog(fSite.getShell(), SWT.SAVE);
dlg.setText(title);
dlg.setFilterExtensions(new String[]{"*.log"});
String path= null;
while(path == null) {
path= dlg.open();
if (path == null)
return;
File file= new File(path);
if (file.exists()) {
if (!file.canWrite()) {
final String msg= NLS.bind(ActionMessages.CreateParserLogAction_readOnlyFile, path);
MessageDialog.openError(fSite.getShell(), title, msg);
path= null;
}
else {
final String msg = NLS.bind(ActionMessages.CreateParserLogAction_existingFile, path);
if (!MessageDialog.openQuestion(fSite.getShell(), title, msg)) {
path= null;
}
}
}
}
try {
PrintStream out= new PrintStream(path);
try {
boolean needsep= false;
for (ITranslationUnit tu : tuArray) {
if (needsep) {
out.println(); out.println();
}
createLog(out, tu, new NullProgressMonitor());
needsep= true;
}
}
finally {
out.close();
}
} catch (IOException e) {
MessageDialog.openError(fSite.getShell(), action.getText(), e.getMessage());
}
}
private void createLog(final PrintStream out, final ITranslationUnit tu, IProgressMonitor pm) {
try {
tu.open(pm);
} catch (CModelException e) {
CUIPlugin.log(e);
}
ASTProvider.getASTProvider().runOnAST(tu, ASTProvider.WAIT_IF_OPEN, pm, new ASTCache.ASTRunnable() {
public IStatus runOnAST(ILanguage lang, IASTTranslationUnit ast) throws CoreException {
if (ast != null)
return createLog(out, tu, lang, ast);
return Status.CANCEL_STATUS;
}
});
}
protected IStatus createLog(PrintStream out, ITranslationUnit tu, ILanguage lang, IASTTranslationUnit ast) {
IStatus status = Status.OK_STATUS;
final ICProject cproject = tu.getCProject();
final String projectName= cproject == null ? null : cproject.getElementName();
final IIndex index = ast.getIndex();
ITranslationUnit configureWith = tu;
int ctxLinkage= 0;
ISignificantMacros ctxSigMacros= null;
if (tu instanceof TranslationUnit) {
TranslationUnit itu= (TranslationUnit) tu;
IIndexFile[] ctxToHeader = itu.getContextToHeader(index, ITranslationUnit.AST_CONFIGURE_USING_SOURCE_CONTEXT);
if (ctxToHeader != null) {
try {
final IIndexFile ctxFile = ctxToHeader[0];
ctxLinkage= ctxToHeader[0].getLinkageID();
ctxSigMacros= ctxFile.getSignificantMacros();
configureWith = CoreModelUtil.findTranslationUnitForLocation(ctxFile.getLocation(), cproject);
} catch (CoreException e) {
}
if (configureWith == null) {
configureWith= tu;
ctxToHeader= null;
}
}
}
final ExtendedScannerInfo scfg= new ExtendedScannerInfo(configureWith.getScannerInfo(true));
final MyVisitor visitor= new MyVisitor();
ast.accept(visitor);
out.println("Project: " + projectName);
out.println("File: " + tu.getLocationURI());
out.println("Language: " + lang.getName());
out.println("Index Version: " + PDOM.versionString(PDOM.getDefaultVersion()));
out.println("Build Configuration: " + getBuildConfig(cproject));
if (configureWith == tu) {
out.println("Context: none");
} else {
out.println("Context: " + configureWith.getLocationURI());
out.println(INDENT + getLinkageName(ctxLinkage) + ", " + ctxSigMacros);
}
try {
IIndexFile[] versions= index.getFiles(IndexLocationFactory.getIFL(tu));
out.println("Versions in Index: " + versions.length);
for (IIndexFile f : versions) {
out.println(INDENT + getLinkageName(f.getLinkageID()) + ": " + f.getSignificantMacros());
}
} catch (CoreException e) {
status= e.getStatus();
}
out.println();
output(out, "Include Search Path (option -I):", scfg.getIncludePaths());
output(out, "Local Include Search Path (option -iquote):", scfg.getLocalIncludePath());
output(out, "Preincluded files (option -include):", scfg.getIncludeFiles());
output(out, "Preincluded macro files (option -imacros):", scfg.getMacroFiles());
HashSet<String> reported= new HashSet<String>();
output(out, "Macro definitions (option -D):", scfg.getDefinedSymbols(), reported);
output(out, "Macro definitions (from language + headers in index):", ast.getBuiltinMacroDefinitions(), reported);
output(out, "Macro definitions (from files actually parsed):", ast.getMacroDefinitions(), reported);
try {
outputUnresolvedIncludes(cproject, ast.getIndex(), out, ast.getIncludeDirectives(), ast.getLinkage().getLinkageID());
} catch (CoreException e) {
status= e.getStatus();
}
output(out, "Scanner problems:", ast.getPreprocessorProblems());
output(out, "Parser problems:", visitor.fProblems.toArray(new IASTProblem[0]));
output(out, "Unresolved names:", visitor.fProblemBindings.toArray(new IProblemBinding[0]));
output(out, "Exceptions in name resolution:", visitor.fExceptions);
out.println("Written on " + new Date().toString());
return status;
}
private String getLinkageName(int linkageID) {
switch(linkageID) {
case ILinkage.NO_LINKAGE_ID: return ILinkage.NO_LINKAGE_NAME;
case ILinkage.C_LINKAGE_ID: return ILinkage.C_LINKAGE_NAME;
case ILinkage.CPP_LINKAGE_ID: return ILinkage.CPP_LINKAGE_NAME;
case ILinkage.FORTRAN_LINKAGE_ID: return ILinkage.FORTRAN_LINKAGE_NAME;
case ILinkage.OBJC_LINKAGE_ID: return ILinkage.OBJC_LINKAGE_NAME;
}
return String.valueOf(linkageID);
}
private String getBuildConfig(ICProject cproject) {
ICProjectDescriptionManager prjDescMgr= CCorePlugin.getDefault().getProjectDescriptionManager();
ICProjectDescription prefs= prjDescMgr.getProjectDescription(cproject.getProject(), false);
if (prefs != null) {
ICConfigurationDescription cfg= prefs.getDefaultSettingConfiguration();
if (cfg != null)
return cfg.getName();
}
return "unknown";
}
private void outputUnresolvedIncludes(ICProject prj, IIndex index, PrintStream out,
IASTPreprocessorIncludeStatement[] includeDirectives, int linkageID) throws CoreException {
fWroteUnresolvedTitle= false;
ASTFilePathResolver resolver= new ProjectIndexerInputAdapter(prj);
HashSet<IIndexFile> handled= new HashSet<IIndexFile>();
for (IASTPreprocessorIncludeStatement include : includeDirectives) {
if (include.isResolved()) {
IIndexFileLocation ifl = resolver.resolveASTPath(include.getPath());
IIndexFile ifile= index.getFile(linkageID, ifl, include.getSignificantMacros());
outputUnresolvedIncludes(index, out, ifl, ifile, handled);
}
}
if (fWroteUnresolvedTitle)
out.println();
}
private void outputUnresolvedIncludes(IIndex index, PrintStream out,
IIndexFileLocation ifl, IIndexFile ifile, Set<IIndexFile> handled) throws CoreException {
if (ifile == null) {
writeUnresolvedTitle(out);
out.println(INDENT + ifl.getURI() + " is not indexed");
} else if (handled.add(ifile)) {
IIndexInclude[] includes = ifile.getIncludes();
for (IIndexInclude inc : includes) {
if (inc.isActive()) {
if (inc.isResolved()) {
IIndexFile next = index.resolveInclude(inc);
outputUnresolvedIncludes(index, out, inc.getIncludesLocation(), next, handled);
} else {
writeUnresolvedTitle(out);
out.println(INDENT + "Unresolved inclusion: " + inc.getFullName() + " in file " +
inc.getIncludedByLocation().getURI());
}
}
}
}
}
public void writeUnresolvedTitle(PrintStream out) {
if (!fWroteUnresolvedTitle) {
fWroteUnresolvedTitle= true;
out.println("Unresolved includes (from headers in index):");
}
}
private void output(PrintStream out, String label, String[] list) {
if (list.length > 0) {
out.println(label);
for (String line : list) {
out.println(INDENT + line);
}
out.println();
}
}
private void output(PrintStream out, String label, Map<String, String> definedSymbols, HashSet<String> reported) {
if (!definedSymbols.isEmpty()) {
out.println(label);
SortedMap<String, String> sorted= new TreeMap<String, String>(COMP_INSENSITIVE);
sorted.putAll(definedSymbols);
for (Entry<String, String> entry : sorted.entrySet()) {
final String macro = entry.getKey() + '=' + entry.getValue();
if (reported.add(macro)) {
out.println(INDENT + macro);
}
}
out.println();
}
}
private void output(PrintStream out, String label, IASTPreprocessorMacroDefinition[] defs, HashSet<String> reported) {
if (defs.length > 0) {
out.println(label);
SortedSet<String> macros= new TreeSet<String>(COMP_INSENSITIVE);
for (IASTPreprocessorMacroDefinition def : defs) {
macros.add(def.toString());
}
for (String macro : macros) {
if (reported.add(macro)) {
out.println(INDENT + macro);
}
}
out.println();
}
}
private void output(PrintStream out, String label, IASTProblem[] preprocessorProblems) {
if (preprocessorProblems.length > 0) {
out.println(label);
for (IASTProblem problem : preprocessorProblems) {
out.println(INDENT + problem.getMessageWithLocation());
}
out.println();
}
}
private void output(PrintStream out, String label, IProblemBinding[] list) {
if (list.length > 0) {
out.println(label);
for (IProblemBinding problem : list) {
String file= problem.getFileName();
int line = problem.getLineNumber();
out.println(INDENT + problem.getMessage() + " in file " + file + ':' + line);
}
out.println();
}
}
private void output(PrintStream out, String label, List<Exception> list) {
if (!list.isEmpty()) {
out.println(label);
for (Exception problem : list) {
problem.printStackTrace(out);
}
out.println();
}
}
}