blob: 86cbe6f1d6b7369355dacb2e522d8eb6a4e47a97 [file] [log] [blame]
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.codeInspection;
import com.intellij.analysis.AnalysisScope;
import com.intellij.codeInspection.ex.*;
import com.intellij.conversion.ConversionListener;
import com.intellij.conversion.ConversionService;
import com.intellij.ide.impl.PatchProjectUtil;
import com.intellij.ide.impl.ProjectUtil;
import com.intellij.openapi.application.ApplicationInfo;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ex.ApplicationEx;
import com.intellij.openapi.application.ex.ApplicationInfoEx;
import com.intellij.openapi.application.ex.ApplicationManagerEx;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.util.ProgressIndicatorBase;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileManager;
import com.intellij.profile.Profile;
import com.intellij.profile.codeInspection.InspectionProfileManager;
import com.intellij.profile.codeInspection.InspectionProjectProfileManager;
import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiManager;
import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;
import org.jdom.JDOMException;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.*;
/**
* @author max
*/
@SuppressWarnings({"UseOfSystemOutOrSystemErr"})
public class InspectionApplication {
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.InspectionApplication");
public InspectionToolCmdlineOptionHelpProvider myHelpProvider = null;
public String myProjectPath = null;
public String myOutPath = null;
public String mySourceDirectory = null;
public String myStubProfile = null;
public String myProfileName = null;
public String myProfilePath = null;
public boolean myRunWithEditorSettings = false;
public boolean myRunGlobalToolsOnly = false;
private Project myProject;
private int myVerboseLevel = 0;
public String myOutputFormat = null;
public boolean myErrorCodeRequired = true;
@NonNls public static final String DESCRIPTIONS = ".descriptions";
@NonNls public static final String PROFILE = "profile";
@NonNls public static final String INSPECTIONS_NODE = "inspections";
@NonNls public static final String XML_EXTENSION = ".xml";
public void startup() {
if (myProjectPath == null) {
logError("Project to inspect is not defined");
printHelp();
}
if (myProfileName == null && myProfilePath == null && myStubProfile == null) {
logError("Profile to inspect with is not defined");
printHelp();
}
final ApplicationEx application = ApplicationManagerEx.getApplicationEx();
application.runReadAction(new Runnable() {
@Override
public void run() {
try {
final ApplicationInfoEx applicationInfo = (ApplicationInfoEx)ApplicationInfo.getInstance();
logMessage(1, InspectionsBundle.message("inspection.application.starting.up", applicationInfo.getFullApplicationName()));
application.doNotSave();
logMessageLn(1, InspectionsBundle.message("inspection.done"));
InspectionApplication.this.run();
}
catch (Exception e) {
LOG.error(e);
}
finally {
if (myErrorCodeRequired) application.exit(true, true);
}
}
});
}
private void printHelp() {
assert myHelpProvider != null;
myHelpProvider.printHelpAndExit();
}
private void run() {
File tmpDir = null;
try {
myProjectPath = myProjectPath.replace(File.separatorChar, '/');
VirtualFile vfsProject = LocalFileSystem.getInstance().findFileByPath(myProjectPath);
if (vfsProject == null) {
logError(InspectionsBundle.message("inspection.application.file.cannot.be.found", myProjectPath));
printHelp();
}
logMessage(1, InspectionsBundle.message("inspection.application.opening.project"));
final ConversionService conversionService = ConversionService.getInstance();
if (conversionService != null && conversionService.convertSilently(myProjectPath, createConversionListener()).openingIsCanceled()) {
if (myErrorCodeRequired) System.exit(1);
return;
}
myProject = ProjectUtil.openOrImport(myProjectPath, null, false);
if (myProject == null) {
logError("Unable to open project");
if (myErrorCodeRequired) System.exit(1);
return;
}
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run(){
VirtualFileManager.getInstance().refreshWithoutFileWatcher(false);
}
});
PatchProjectUtil.patchProject(myProject);
logMessageLn(1, InspectionsBundle.message("inspection.done"));
logMessage(1, InspectionsBundle.message("inspection.application.initializing.project"));
Profile inspectionProfile = loadInspectionProfile();
if (inspectionProfile == null) return;
final InspectionManagerEx im = (InspectionManagerEx)InspectionManager.getInstance(myProject);
final GlobalInspectionContextImpl inspectionContext = im.createNewGlobalContext(true);
inspectionContext.setExternalProfile((InspectionProfile)inspectionProfile);
im.setProfile(inspectionProfile.getName());
final AnalysisScope scope;
if (mySourceDirectory == null) {
scope = new AnalysisScope(myProject);
}
else {
mySourceDirectory = mySourceDirectory.replace(File.separatorChar, '/');
VirtualFile vfsDir = LocalFileSystem.getInstance().findFileByPath(mySourceDirectory);
if (vfsDir == null) {
logError(InspectionsBundle.message("inspection.application.directory.cannot.be.found", mySourceDirectory));
printHelp();
}
PsiDirectory psiDirectory = PsiManager.getInstance(myProject).findDirectory(vfsDir);
scope = new AnalysisScope(psiDirectory);
}
logMessageLn(1, InspectionsBundle.message("inspection.done"));
if (!myRunWithEditorSettings) {
logMessageLn(1, InspectionsBundle.message("inspection.application.chosen.profile.log message", inspectionProfile.getName()));
}
InspectionsReportConverter reportConverter = getReportConverter(myOutputFormat);
if (reportConverter == null && myOutputFormat != null && myOutputFormat.endsWith(".xsl")) {
// xslt converter
reportConverter = new XSLTReportConverter(myOutputFormat);
}
final String resultsDataPath;
if ((reportConverter == null || !reportConverter.useTmpDirForRawData()) // use default xml converter(if null( or don't store default xml report in tmp dir
&& myOutPath != null) { // and don't use STDOUT stream
resultsDataPath = myOutPath;
}
else {
try {
tmpDir = FileUtil.createTempDirectory("inspections", "data");
resultsDataPath = tmpDir.getPath();
}
catch (IOException e) {
LOG.error(e);
System.err.println("Cannot create tmp directory.");
System.exit(1);
return;
}
}
final List<File> inspectionsResults = new ArrayList<File>();
ProgressManager.getInstance().runProcess(new Runnable() {
@Override
public void run() {
if (!GlobalInspectionContextUtil.canRunInspections(myProject, false)) {
if (myErrorCodeRequired) System.exit(1);
return;
}
inspectionContext.launchInspectionsOffline(scope, resultsDataPath, myRunGlobalToolsOnly, inspectionsResults);
logMessageLn(1, "\n" +
InspectionsBundle.message("inspection.capitalized.done") +
"\n");
}
}, new ProgressIndicatorBase() {
private String lastPrefix = "";
private int myLastPercent = -1;
@Override
public void setText(String text) {
if (myVerboseLevel == 0) return;
if (myVerboseLevel == 1) {
String prefix = getPrefix(text);
if (prefix == null) return;
if (prefix.equals(lastPrefix)) {
logMessage(1, ".");
return;
}
lastPrefix = prefix;
logMessageLn(1, "");
logMessageLn(1, prefix);
return;
}
if (myVerboseLevel == 3) {
if (!isIndeterminate() && getFraction() > 0) {
final int percent = (int)(getFraction() * 100);
if (myLastPercent == percent) return;
myLastPercent = percent;
String msg = InspectionsBundle.message("inspection.display.name") + " " + percent + "%";
logMessageLn(2, msg);
}
return;
}
logMessageLn(2, text);
}
});
final String descriptionsFile = resultsDataPath + File.separatorChar + DESCRIPTIONS + XML_EXTENSION;
describeInspections(descriptionsFile,
myRunWithEditorSettings ? null : inspectionProfile.getName());
inspectionsResults.add(new File(descriptionsFile));
// convert report
if (reportConverter != null) {
try {
reportConverter.convert(resultsDataPath, myOutPath, inspectionContext.getTools(), inspectionsResults);
}
catch (InspectionsReportConverter.ConversionException e) {
logError("\n" + e.getMessage());
printHelp();
}
}
}
catch (IOException e) {
LOG.error(e);
logError(e.getMessage());
printHelp();
}
catch (Throwable e) {
LOG.error(e);
logError(e.getMessage());
if (myErrorCodeRequired) System.exit(1);
}
finally {
// delete tmp dir
if (tmpDir != null) {
FileUtil.delete(tmpDir);
}
}
}
@Nullable
private Profile loadInspectionProfile() throws IOException, JDOMException {
Profile inspectionProfile = null;
//fetch profile by name from project file (project profiles can be disabled)
if (myProfileName != null) {
inspectionProfile = loadProfileByName(myProfileName);
if (inspectionProfile == null) {
logError("Profile with configured name (" + myProfileName + ") was not found (neither in project nor in config directory)");
if (myErrorCodeRequired) System.exit(1);
return null;
}
return inspectionProfile;
}
if (myProfilePath != null) {
inspectionProfile = loadProfileByPath(myProfilePath);
if (inspectionProfile == null) {
logError("Failed to load profile from \'" + myProfilePath + "\'");
if (myErrorCodeRequired) System.exit(1);
return null;
}
return inspectionProfile;
}
if (myStubProfile != null) {
if (!myRunWithEditorSettings) {
inspectionProfile = loadProfileByName(myStubProfile);
if (inspectionProfile != null) return inspectionProfile;
inspectionProfile = loadProfileByPath(myStubProfile);
if (inspectionProfile != null) return inspectionProfile;
}
inspectionProfile = InspectionProjectProfileManager.getInstance(myProject).getInspectionProfile();
logError("Using default project profile");
}
return inspectionProfile;
}
@Nullable
private Profile loadProfileByPath(final String profilePath) throws IOException, JDOMException {
Profile inspectionProfile = InspectionProfileManager.getInstance().loadProfile(profilePath);
if (inspectionProfile != null) {
logMessageLn(1, "Loaded profile \'" + inspectionProfile.getName() + "\' from file \'" + profilePath + "\'");
}
return inspectionProfile;
}
@Nullable
private Profile loadProfileByName(final String profileName) {
Profile inspectionProfile = InspectionProjectProfileManager.getInstance(myProject).getProfile(profileName, false);
if (inspectionProfile != null) {
logMessageLn(1, "Loaded shared project profile \'" + profileName + "\'");
}
else {
//check if ide profile is used for project
final Collection<Profile> profiles = InspectionProjectProfileManager.getInstance(myProject).getProfiles();
for (Profile profile : profiles) {
if (Comparing.strEqual(profile.getName(), profileName)) {
inspectionProfile = profile;
logMessageLn(1, "Loaded local profile \'" + profileName + "\'");
break;
}
}
}
return inspectionProfile;
}
@Nullable
private InspectionsReportConverter getReportConverter(@Nullable final String outputFormat) {
for (InspectionsReportConverter converter : InspectionsReportConverter.EP_NAME.getExtensions()) {
if (converter.getFormatName().equals(outputFormat)) {
return converter;
}
}
return null;
}
private ConversionListener createConversionListener() {
return new ConversionListener() {
@Override
public void conversionNeeded() {
logMessageLn(1, InspectionsBundle.message("inspection.application.project.has.older.format.and.will.be.converted"));
}
@Override
public void successfullyConverted(final File backupDir) {
logMessageLn(1, InspectionsBundle.message(
"inspection.application.project.was.succesfully.converted.old.project.files.were.saved.to.0",
backupDir.getAbsolutePath()));
}
@Override
public void error(final String message) {
logError(InspectionsBundle.message("inspection.application.cannot.convert.project.0", message));
}
@Override
public void cannotWriteToFiles(final List<File> readonlyFiles) {
StringBuilder files = new StringBuilder();
for (File file : readonlyFiles) {
files.append(file.getAbsolutePath()).append("; ");
}
logError(InspectionsBundle.message("inspection.application.cannot.convert.the.project.the.following.files.are.read.only.0", files.toString()));
}
};
}
@Nullable
private static String getPrefix(final String text) {
//noinspection HardCodedStringLiteral
int idx = text.indexOf(" in ");
if (idx == -1) {
//noinspection HardCodedStringLiteral
idx = text.indexOf(" of ");
}
return idx == -1 ? null : text.substring(0, idx);
}
public void setVerboseLevel(int verboseLevel) {
myVerboseLevel = verboseLevel;
}
private void logMessage(int minVerboseLevel, String message) {
if (myVerboseLevel >= minVerboseLevel) {
System.out.print(message);
}
}
private static void logError(String message) {
System.err.println(message);
}
private void logMessageLn(int minVerboseLevel, String message) {
if (myVerboseLevel >= minVerboseLevel) {
System.out.println(message);
}
}
private static void describeInspections(@NonNls String myOutputPath, final String name) throws IOException {
final InspectionToolWrapper[] toolWrappers = InspectionProfileImpl.getDefaultProfile().getInspectionTools(null);
final Map<String, Set<InspectionToolWrapper>> map = new HashMap<String, Set<InspectionToolWrapper>>();
for (InspectionToolWrapper toolWrapper : toolWrappers) {
final String groupName = toolWrapper.getGroupDisplayName();
Set<InspectionToolWrapper> groupInspections = map.get(groupName);
if (groupInspections == null) {
groupInspections = new HashSet<InspectionToolWrapper>();
map.put(groupName, groupInspections);
}
groupInspections.add(toolWrapper);
}
FileWriter fw = new FileWriter(myOutputPath);
try {
@NonNls final PrettyPrintWriter xmlWriter = new PrettyPrintWriter(fw);
xmlWriter.startNode(INSPECTIONS_NODE);
if (name != null) {
xmlWriter.addAttribute(PROFILE, name);
}
for (String groupName : map.keySet()) {
xmlWriter.startNode("group");
xmlWriter.addAttribute("name", groupName);
final Set<InspectionToolWrapper> entries = map.get(groupName);
for (InspectionToolWrapper toolWrapper : entries) {
xmlWriter.startNode("inspection");
xmlWriter.addAttribute("shortName", toolWrapper.getShortName());
xmlWriter.addAttribute("displayName", toolWrapper.getDisplayName());
final String description = toolWrapper.loadDescription();
if (description != null) {
xmlWriter.setValue(description);
}
else {
LOG.error(toolWrapper.getShortName() + " descriptionUrl==" + toolWrapper);
}
xmlWriter.endNode();
}
xmlWriter.endNode();
}
xmlWriter.endNode();
}
finally {
fw.close();
}
}
}