/*
* Copyright 2000-2014 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.
*/
/*
* Created by IntelliJ IDEA.
* User: Anna.Kozlova
* Date: 31-Jul-2006
* Time: 17:44:39
*/
package com.intellij.profile.codeInspection.ui.header;
import com.intellij.codeInsight.daemon.impl.HighlightInfoType;
import com.intellij.codeInsight.daemon.impl.SeverityRegistrar;
import com.intellij.codeInsight.daemon.impl.SmartHashSet;
import com.intellij.codeInspection.InspectionManager;
import com.intellij.codeInspection.ModifiableModel;
import com.intellij.codeInspection.ex.InspectionManagerEx;
import com.intellij.codeInspection.ex.InspectionProfileImpl;
import com.intellij.codeInspection.ex.InspectionToolRegistrar;
import com.intellij.codeInspection.ex.InspectionToolWrapper;
import com.intellij.icons.AllIcons;
import com.intellij.lang.Language;
import com.intellij.lang.annotation.HighlightSeverity;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.colors.CodeInsightColors;
import com.intellij.openapi.editor.colors.TextAttributesKey;
import com.intellij.openapi.editor.markup.TextAttributes;
import com.intellij.openapi.fileChooser.FileChooser;
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
import com.intellij.openapi.fileTypes.InternalStdFileTypes;
import com.intellij.openapi.options.Configurable;
import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.options.SearchableConfigurable;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.*;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.profile.Profile;
import com.intellij.profile.ProfileManager;
import com.intellij.profile.codeInspection.InspectionProfileManager;
import com.intellij.profile.codeInspection.InspectionProjectProfileManager;
import com.intellij.profile.codeInspection.ui.ErrorsConfigurable;
import com.intellij.profile.codeInspection.ui.SingleInspectionProfilePanel;
import com.intellij.ui.ListCellRendererWrapper;
import com.intellij.util.Alarm;
import com.intellij.util.Consumer;
import com.intellij.util.SystemProperties;
import com.intellij.util.containers.HashMap;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.List;
public abstract class InspectionToolsConfigurable
implements ErrorsConfigurable, SearchableConfigurable, Configurable.HoldPreferredFocusedComponent, Configurable.NoScroll {
public static final String ID = "Errors";
public static final String DISPLAY_NAME = "Inspections";
private static final String HEADER_TITLE = "Profile:";
private static final Logger LOG = Logger.getInstance("#" + InspectionToolsConfigurable.class.getName());
protected final InspectionProfileManager myProfileManager;
protected final InspectionProjectProfileManager myProjectProfileManager;
private final CardLayout myLayout = new CardLayout();
private final AuxiliaryRightPanel myAuxiliaryRightPanel;
private final Map<Profile, SingleInspectionProfilePanel> myPanels = new HashMap<Profile, SingleInspectionProfilePanel>();
private final List<Profile> myDeletedProfiles = new ArrayList<Profile>();
private final Set<Language> myFilterLanguages = new SmartHashSet<Language>();
protected ProfilesConfigurableComboBox myProfiles;
private JPanel myPanel;
private JPanel myWholePanel;
private Alarm mySelectionAlarm;
public InspectionToolsConfigurable(@NotNull final InspectionProjectProfileManager projectProfileManager, InspectionProfileManager profileManager) {
myWholePanel = new JPanel(new BorderLayout());
final JPanel toolbar = new JPanel(new GridBagLayout());
toolbar.setBorder(BorderFactory.createEmptyBorder(0, 0, 7, 0));
myPanel = new JPanel();
myWholePanel.add(toolbar, BorderLayout.PAGE_START);
myWholePanel.add(myPanel, BorderLayout.CENTER);
myProfiles = new ProfilesConfigurableComboBox(new ListCellRendererWrapper<Profile>() {
@Override
public void customize(final JList list, final Profile value, final int index, final boolean selected, final boolean hasFocus) {
final SingleInspectionProfilePanel singleInspectionProfilePanel = myPanels.get(value);
final boolean isShared = singleInspectionProfilePanel.isProfileShared();
setIcon(isShared ? AllIcons.General.ProjectSettings : AllIcons.General.Settings);
setText(singleInspectionProfilePanel.getCurrentProfileName());
}
}) {
@Override
public void onProfileChosen(InspectionProfileImpl inspectionProfile) {
myLayout.show(myPanel, getCardName(inspectionProfile));
myAuxiliaryRightPanel.showDescription(inspectionProfile.getDescription());
}
};
JPanel profilesHolder = new JPanel();
profilesHolder.setLayout(new CardLayout());
JComponent manageButton = new ManageButton(new ManageButtonBuilder() {
@Override
public boolean isSharedToTeamMembers() {
SingleInspectionProfilePanel panel = getSelectedPanel();
return panel != null && panel.isProfileShared();
}
@Override
public void setShareToTeamMembers(boolean shared) {
final SingleInspectionProfilePanel selectedPanel = getSelectedPanel();
LOG.assertTrue(selectedPanel != null, "No settings selectedPanel for: " + getSelectedObject());
final String name = getSelectedPanel().getCurrentProfileName();
for (SingleInspectionProfilePanel p : myPanels.values()) {
if (p != selectedPanel && Comparing.equal(p.getCurrentProfileName(), name)) {
final boolean curShared = p.isProfileShared();
if (curShared == shared) {
Messages.showErrorDialog((shared ? "Shared" : "Application level") + " profile with same name exists.", "Inspections Settings");
return;
}
}
}
selectedPanel.setProfileShared(shared);
myProfiles.repaint();
}
@Override
public void copy() {
final InspectionProfileImpl newProfile = copyToNewProfile(getSelectedObject(), getProject());
if (newProfile != null) {
final InspectionProfileImpl modifiableModel = (InspectionProfileImpl)newProfile.getModifiableModel();
modifiableModel.setModified(true);
modifiableModel.setProjectLevel(false);
addProfile(modifiableModel);
rename(modifiableModel);
}
}
@Override
public boolean canRename() {
final InspectionProfileImpl profile = getSelectedObject();
return !profile.isProfileLocked();
}
@Override
public void rename() {
rename(getSelectedObject());
}
private void rename(@NotNull final InspectionProfileImpl inspectionProfile) {
final String initialName = getSelectedPanel().getCurrentProfileName();
myProfiles.showEditCard(initialName, new SaveInputComponentValidator() {
@Override
public void doSave(@NotNull String text) {
if (!text.equals(initialName)) {
getProfilePanel(inspectionProfile).setCurrentProfileName(text);
}
myProfiles.showComboBoxCard();
}
@Override
public boolean checkValid(@NotNull String text) {
final SingleInspectionProfilePanel singleInspectionProfilePanel = myPanels.get(inspectionProfile);
if (singleInspectionProfilePanel == null) {
return false;
}
final boolean isValid = text.equals(initialName) || !hasName(text, singleInspectionProfilePanel.isProfileShared());
if (isValid) {
myAuxiliaryRightPanel.showDescription(getSelectedObject().getDescription());
}
else {
myAuxiliaryRightPanel.showError("Name is already in use. Please change name to unique.");
}
return isValid;
}
@Override
public void cancel() {
myProfiles.showComboBoxCard();
myAuxiliaryRightPanel.showDescription(getSelectedObject().getDescription());
}
});
}
@Override
public boolean canDelete() {
return isDeleteEnabled(myProfiles.getSelectedProfile());
}
@Override
public void delete() {
final InspectionProfileImpl selectedProfile = myProfiles.getSelectedProfile();
myProfiles.getModel().removeElement(selectedProfile);
myDeletedProfiles.add(selectedProfile);
}
@Override
public boolean canEditDescription() {
return true;
}
@Override
public void editDescription() {
myAuxiliaryRightPanel.editDescription(getSelectedObject().getDescription());
}
@Override
public boolean hasDescription() {
return !StringUtil.isEmpty(getSelectedObject().getDescription());
}
@Override
public void export() {
final FileChooserDescriptor descriptor = FileChooserDescriptorFactory.createSingleFolderDescriptor();
descriptor.setDescription("Choose directory to store profile file");
FileChooser.chooseFile(descriptor, getProject(), myWholePanel, null, new Consumer<VirtualFile>() {
@Override
public void consume(VirtualFile file) {
final Element element = new Element("inspections");
try {
final SingleInspectionProfilePanel panel = getSelectedPanel();
LOG.assertTrue(panel != null);
final InspectionProfileImpl profile = getSelectedObject();
LOG.assertTrue(true);
profile.writeExternal(element);
final String filePath = FileUtil.toSystemDependentName(file.getPath()) + File.separator + FileUtil.sanitizeFileName(profile.getName()) + ".xml";
if (new File(filePath).isFile()) {
if (Messages.showOkCancelDialog(myWholePanel, "File \'" + filePath + "\' already exist. Do you want to overwrite it?", "Warning",
Messages.getQuestionIcon()) != Messages.OK) {
return;
}
}
JDOMUtil.writeDocument(new Document(element), filePath, SystemProperties.getLineSeparator());
}
catch (WriteExternalException e1) {
LOG.error(e1);
}
catch (IOException e1) {
LOG.error(e1);
}
}
});
}
@Override
public void doImport() {
final FileChooserDescriptor descriptor = new FileChooserDescriptor(true, false, false, false, false, false) {
@Override
public boolean isFileSelectable(VirtualFile file) {
return file.getFileType().equals(InternalStdFileTypes.XML);
}
};
descriptor.setDescription("Choose profile file");
FileChooser.chooseFile(descriptor, getProject(), myWholePanel, null, new Consumer<VirtualFile>() {
@Override
public void consume(VirtualFile file) {
if (file == null) return;
InspectionProfileImpl profile = new InspectionProfileImpl("TempProfile", InspectionToolRegistrar.getInstance(), myProfileManager);
try {
Element rootElement = JDOMUtil.loadDocument(VfsUtilCore.virtualToIoFile(file)).getRootElement();
if (Comparing.strEqual(rootElement.getName(), "component")) {//import right from .idea/inspectProfiles/xxx.xml
rootElement = rootElement.getChildren().get(0);
}
final Set<String> levels = new HashSet<String>();
for (Object o : rootElement.getChildren("inspection_tool")) {
final Element inspectElement = (Element)o;
levels.add(inspectElement.getAttributeValue("level"));
for (Object s : inspectElement.getChildren("scope")) {
levels.add(((Element)s).getAttributeValue("level"));
}
}
for (Iterator<String> iterator = levels.iterator(); iterator.hasNext(); ) {
String level = iterator.next();
if (myProfileManager.getOwnSeverityRegistrar().getSeverity(level) != null) {
iterator.remove();
}
}
if (!levels.isEmpty()) {
if (Messages.showYesNoDialog(myWholePanel, "Undefined severities detected: " +
StringUtil.join(levels, ", ") +
". Do you want to create them?", "Warning", Messages.getWarningIcon()) == Messages.YES) {
for (String level : levels) {
final TextAttributes textAttributes = CodeInsightColors.WARNINGS_ATTRIBUTES.getDefaultAttributes();
HighlightInfoType.HighlightInfoTypeImpl info =
new HighlightInfoType.HighlightInfoTypeImpl(new HighlightSeverity(level, 50), TextAttributesKey.createTextAttributesKey(level));
myProfileManager.getOwnSeverityRegistrar().registerSeverity(new SeverityRegistrar.SeverityBasedTextAttributes(textAttributes.clone(), info),
textAttributes.getErrorStripeColor());
}
}
}
profile.readExternal(rootElement);
profile.setProjectLevel(false);
profile.initInspectionTools(getProject());
if (getProfilePanel(profile) != null) {
if (Messages.showOkCancelDialog(myWholePanel, "Profile with name \'" +
profile.getName() +
"\' already exists. Do you want to overwrite it?", "Warning", Messages.getInformationIcon()) !=
Messages.OK) {
return;
}
}
final ModifiableModel model = profile.getModifiableModel();
model.setModified(true);
addProfile((InspectionProfileImpl)model);
//TODO myDeletedProfiles ? really need this
myDeletedProfiles.remove(profile);
}
catch (InvalidDataException e1) {
LOG.error(e1);
}
catch (JDOMException e1) {
LOG.error(e1);
}
catch (IOException e1) {
LOG.error(e1);
}
}
});
}
}).build();
myAuxiliaryRightPanel = new AuxiliaryRightPanel(new AuxiliaryRightPanel.DescriptionSaveListener() {
@Override
public void saveDescription(@NotNull String description) {
final InspectionProfileImpl inspectionProfile = getSelectedObject();
if (!Comparing.strEqual(description, inspectionProfile.getDescription())) {
inspectionProfile.setDescription(description);
inspectionProfile.setModified(true);
}
myAuxiliaryRightPanel.showDescription(description);
}
@Override
public void cancel() {
myAuxiliaryRightPanel.showDescription(getSelectedObject().getDescription());
}
});
toolbar.add(new JLabel(HEADER_TITLE),
new GridBagConstraints(0, 1, 1, 1, 0, 0, GridBagConstraints.WEST, GridBagConstraints.VERTICAL, new Insets(0, 0, 0, 0), 0, 0));
toolbar.add(myProfiles.getHintLabel(),
new GridBagConstraints(1, 0, 1, 1, 0, 0, GridBagConstraints.WEST, GridBagConstraints.VERTICAL, new Insets(0, 6, 6, 0), 0, 0));
toolbar.add(myProfiles, new GridBagConstraints(1, 1, 1, 1, 0, 1.0, GridBagConstraints.WEST, GridBagConstraints.VERTICAL, new Insets(0, 6, 0, 0), 0, 0));
toolbar.add(manageButton, new GridBagConstraints(2, 1, 1, 1, 0, 0, GridBagConstraints.WEST, GridBagConstraints.VERTICAL, new Insets(0, 10, 0, 0), 0, 0));
toolbar.add(myAuxiliaryRightPanel.getHintLabel(),
new GridBagConstraints(3, 0, 1, 1, 0, 0, GridBagConstraints.WEST, GridBagConstraints.VERTICAL, new Insets(0, 15, 6, 0), 0, 0));
toolbar.add(myAuxiliaryRightPanel,
new GridBagConstraints(3, 1, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 15, 0, 0), 0, 0));
((InspectionManagerEx)InspectionManager.getInstance(projectProfileManager.getProject())).buildInspectionSearchIndexIfNecessary();
myProjectProfileManager = projectProfileManager;
myProfileManager = profileManager;
}
private Project getProject() {
return myProjectProfileManager.getProject();
}
@Nullable
private InspectionProfileImpl copyToNewProfile(ModifiableModel selectedProfile, @NotNull Project project) {
String profileDefaultName = selectedProfile.getName();
do {
profileDefaultName += " (copy)";
}
while (hasName(profileDefaultName, myPanels.get(selectedProfile).isProfileShared()));
final ProfileManager profileManager = selectedProfile.getProfileManager();
InspectionProfileImpl inspectionProfile = new InspectionProfileImpl(profileDefaultName, InspectionToolRegistrar.getInstance(), profileManager);
inspectionProfile.copyFrom(selectedProfile);
inspectionProfile.setName(profileDefaultName);
inspectionProfile.initInspectionTools(project);
inspectionProfile.setModified(true);
return inspectionProfile;
}
private void addProfile(InspectionProfileImpl model) {
final String modelName = model.getName();
final SingleInspectionProfilePanel panel = createPanel(model, modelName);
myPanel.add(getCardName(model), panel);
myProfiles.getModel().addElement(model);
putProfile(model, panel);
myProfiles.selectProfile(model);
}
@Override
public String getDisplayName() {
return DISPLAY_NAME;
}
@Override
public String getHelpTopic() {
return "preferences.inspections";
}
@Override
@NotNull
public String getId() {
return ID;
}
@Override
public Runnable enableSearch(final String option) {
return new Runnable() {
@Override
public void run() {
SingleInspectionProfilePanel panel = getSelectedPanel();
if (panel != null) {
panel.setFilter(option);
}
}
};
}
@Override
public JComponent createComponent() {
myPanel.setLayout(myLayout);
return myWholePanel;
}
protected abstract InspectionProfileImpl getCurrentProfile();
@Override
public boolean isModified() {
final InspectionProfileImpl selectedProfile = getSelectedObject();
final InspectionProfileImpl currentProfile = getCurrentProfile();
if (!Comparing.equal(selectedProfile, currentProfile)) {
return true;
}
for (SingleInspectionProfilePanel panel : myPanels.values()) {
if (panel.isModified()) return true;
}
if (getProfiles().size() != myPanels.size()) return true;
return !myDeletedProfiles.isEmpty();
}
@Override
public void apply() throws ConfigurationException {
final SingleInspectionProfilePanel selectedPanel = getSelectedPanel();
for (final Profile inspectionProfile : myPanels.keySet()) {
if (myDeletedProfiles.remove(inspectionProfile)) {
deleteProfile(getProfilePanel(inspectionProfile).getSelectedProfile());
}
else {
final SingleInspectionProfilePanel panel = getProfilePanel(inspectionProfile);
panel.apply();
if (panel == selectedPanel) {
applyRootProfile(panel.getCurrentProfileName(), panel.isProfileShared());
}
}
}
doReset();
}
protected abstract void applyRootProfile(final String name, final boolean isShared);
private SingleInspectionProfilePanel getProfilePanel(Profile inspectionProfile) {
return myPanels.get(inspectionProfile);
}
private void putProfile(Profile profile, SingleInspectionProfilePanel panel) {
myPanels.put(profile, panel);
}
protected void deleteProfile(Profile profile) {
final String name = profile.getName();
if (profile.getProfileManager() == myProfileManager) {
if (myProfileManager.getProfile(name, false) != null) {
myProfileManager.deleteProfile(name);
}
return;
}
if (profile.getProfileManager() == myProjectProfileManager) {
if (myProjectProfileManager.getProfile(name, false) != null) {
myProjectProfileManager.deleteProfile(name);
}
}
}
protected boolean acceptTool(InspectionToolWrapper entry) {
return true;
}
@Override
public void reset() {
doReset();
}
private void doReset() {
myDeletedProfiles.clear();
myPanels.clear();
final Collection<Profile> profiles = getProfiles();
final List<Profile> modifiableProfiles = new ArrayList<Profile>(profiles.size());
for (Profile profile : profiles) {
final String profileName = profile.getName();
final ModifiableModel modifiableProfile = ((InspectionProfileImpl)profile).getModifiableModel();
modifiableProfiles.add(modifiableProfile);
final InspectionProfileImpl inspectionProfile = (InspectionProfileImpl)modifiableProfile;
final SingleInspectionProfilePanel panel = createPanel(inspectionProfile, profileName);
putProfile(modifiableProfile, panel);
myPanel.add(getCardName(inspectionProfile), panel);
}
myProfiles.reset(modifiableProfiles);
myAuxiliaryRightPanel.showDescription(getSelectedObject().getDescription());
final InspectionProfileImpl inspectionProfile = getCurrentProfile();
myProfiles.selectProfile(inspectionProfile);
myLayout.show(myPanel, getCardName(inspectionProfile));
final SingleInspectionProfilePanel panel = getSelectedPanel();
if (panel != null) {
panel.setVisible(true);//make sure that UI was initialized
mySelectionAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD);
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
if (mySelectionAlarm != null) {
mySelectionAlarm.addRequest(new Runnable() {
@Override
public void run() {
panel.updateSelection();
}
}, 200);
}
}
});
}
}
private static String getCardName(final InspectionProfileImpl inspectionProfile) {
return (inspectionProfile.isProjectLevel() ? "s" : "a") + inspectionProfile.getName();
}
@NotNull
private SingleInspectionProfilePanel createPanel(InspectionProfileImpl profile, String profileName) {
SingleInspectionProfilePanel panel = new SingleInspectionProfilePanel(myProjectProfileManager, profileName, profile) {
@Override
protected boolean accept(InspectionToolWrapper entry) {
return super.accept(entry) && acceptTool(entry);
}
};
panel.getInspectionsFilter().addLanguages(myFilterLanguages);
return panel;
}
private boolean isDeleteEnabled(@NotNull InspectionProfileImpl inspectionProfile) {
final ProfileManager profileManager = inspectionProfile.getProfileManager();
boolean projectProfileFound = false;
boolean ideProfileFound = false;
final ComboBoxModel model = myProfiles.getModel();
for (int i = 0; i < model.getSize(); i++) {
Profile profile = (Profile)model.getElementAt(i);
if (inspectionProfile == profile) continue;
final boolean isProjectProfile = profile.getProfileManager() == myProjectProfileManager;
projectProfileFound |= isProjectProfile;
ideProfileFound |= !isProjectProfile;
if (ideProfileFound && projectProfileFound) break;
}
return profileManager == myProjectProfileManager ? projectProfileFound : ideProfileFound;
}
protected Collection<Profile> getProfiles() {
final Collection<Profile> result = new ArrayList<Profile>();
result.addAll(new TreeSet<Profile>(myProfileManager.getProfiles()));
result.addAll(myProjectProfileManager.getProfiles());
return result;
}
@Override
public void disposeUIResources() {
for (SingleInspectionProfilePanel panel : myPanels.values()) {
panel.disposeUI();
}
myPanels.clear();
if (mySelectionAlarm != null) {
Disposer.dispose(mySelectionAlarm);
mySelectionAlarm = null;
}
}
@Override
public void selectProfile(Profile profile) {
myProfiles.selectProfile(profile);
}
@Override
public void setFilterLanguages(@NotNull Collection<Language> languages) {
myFilterLanguages.clear();
myFilterLanguages.addAll(languages);
}
@Override
public void selectInspectionTool(String selectedToolShortName) {
final InspectionProfileImpl inspectionProfile = getSelectedObject();
final SingleInspectionProfilePanel panel = getProfilePanel(inspectionProfile);
LOG.assertTrue(panel != null, "No settings panel for: " + inspectionProfile + "; " + configuredProfiles());
panel.selectInspectionTool(selectedToolShortName);
}
protected SingleInspectionProfilePanel getSelectedPanel() {
final InspectionProfileImpl inspectionProfile = getSelectedObject();
return getProfilePanel(inspectionProfile);
}
private String configuredProfiles() {
return "configured profiles: " + StringUtil.join(myPanels.keySet(), ", ");
}
private boolean hasName(final @NotNull String name, boolean shared) {
for (SingleInspectionProfilePanel p : myPanels.values()) {
if (name.equals(p.getCurrentProfileName()) && shared == p.isProfileShared()) {
return true;
}
}
return false;
}
@NotNull
@Override
public InspectionProfileImpl getSelectedObject() {
return myProfiles.getSelectedProfile();
}
@Override
public JComponent getPreferredFocusedComponent() {
final InspectionProfileImpl inspectionProfile = getSelectedObject();
return getProfilePanel(inspectionProfile).getPreferredFocusedComponent();
}
}