blob: c779129a60933a7b789a932ee8cf88e13b669ad0 [file] [log] [blame]
/*
* Copyright 2000-2012 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 org.jetbrains.idea.maven.navigator;
import com.intellij.execution.ProgramRunnerUtil;
import com.intellij.execution.RunManager;
import com.intellij.execution.RunnerAndConfigurationSettings;
import com.intellij.execution.executors.DefaultRunExecutor;
import com.intellij.icons.AllIcons;
import com.intellij.ide.util.treeView.NodeDescriptor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.pom.Navigatable;
import com.intellij.pom.NavigatableAdapter;
import com.intellij.psi.xml.XmlElement;
import com.intellij.ui.JBColor;
import com.intellij.ui.SimpleTextAttributes;
import com.intellij.ui.components.JBList;
import com.intellij.ui.treeStructure.*;
import com.intellij.util.PathUtil;
import com.intellij.util.containers.ContainerUtil;
import gnu.trove.THashMap;
import gnu.trove.THashSet;
import icons.MavenIcons;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.TestOnly;
import org.jetbrains.idea.maven.dom.MavenDomUtil;
import org.jetbrains.idea.maven.dom.MavenPluginDomUtil;
import org.jetbrains.idea.maven.dom.model.MavenDomProfile;
import org.jetbrains.idea.maven.dom.model.MavenDomProfiles;
import org.jetbrains.idea.maven.dom.model.MavenDomProjectModel;
import org.jetbrains.idea.maven.dom.model.MavenDomSettingsModel;
import org.jetbrains.idea.maven.dom.plugin.MavenDomMojo;
import org.jetbrains.idea.maven.dom.plugin.MavenDomPluginModel;
import org.jetbrains.idea.maven.execution.MavenRunConfiguration;
import org.jetbrains.idea.maven.execution.MavenRunConfigurationType;
import org.jetbrains.idea.maven.execution.MavenRunner;
import org.jetbrains.idea.maven.model.*;
import org.jetbrains.idea.maven.project.MavenProject;
import org.jetbrains.idea.maven.project.MavenProjectsManager;
import org.jetbrains.idea.maven.tasks.MavenShortcutsManager;
import org.jetbrains.idea.maven.tasks.MavenTasksManager;
import org.jetbrains.idea.maven.utils.*;
import javax.swing.*;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
import java.awt.*;
import java.awt.event.InputEvent;
import java.net.URL;
import java.util.*;
import java.util.List;
import static org.jetbrains.idea.maven.project.ProjectBundle.message;
public class MavenProjectsStructure extends SimpleTreeStructure {
private static final URL ERROR_ICON_URL = MavenProjectsStructure.class.getResource("/general/error.png");
private static final Collection<String> BASIC_PHASES = MavenConstants.BASIC_PHASES;
private static final Collection<String> PHASES = MavenConstants.PHASES;
private static final Comparator<MavenSimpleNode> NODE_COMPARATOR = new Comparator<MavenSimpleNode>() {
@Override
public int compare(MavenSimpleNode o1, MavenSimpleNode o2) {
return StringUtil.compare(o1.getName(), o2.getName(), true);
}
};
private final Project myProject;
private final MavenProjectsManager myProjectsManager;
private final MavenTasksManager myTasksManager;
private final MavenShortcutsManager myShortcutsManager;
private final MavenProjectsNavigator myProjectsNavigator;
private final SimpleTreeBuilder myTreeBuilder;
private final RootNode myRoot = new RootNode();
private final Map<MavenProject, ProjectNode> myProjectToNodeMapping = new THashMap<MavenProject, ProjectNode>();
public MavenProjectsStructure(Project project,
MavenProjectsManager projectsManager,
MavenTasksManager tasksManager,
MavenShortcutsManager shortcutsManager,
MavenProjectsNavigator projectsNavigator,
SimpleTree tree) {
myProject = project;
myProjectsManager = projectsManager;
myTasksManager = tasksManager;
myShortcutsManager = shortcutsManager;
myProjectsNavigator = projectsNavigator;
configureTree(tree);
myTreeBuilder = new SimpleTreeBuilder(tree, (DefaultTreeModel)tree.getModel(), this, null);
Disposer.register(myProject, myTreeBuilder);
myTreeBuilder.initRoot();
myTreeBuilder.expand(myRoot, null);
}
private void configureTree(final SimpleTree tree) {
tree.setRootVisible(false);
tree.setShowsRootHandles(true);
MavenUIUtil.installCheckboxRenderer(tree, new MavenUIUtil.CheckboxHandler() {
@Override
public void toggle(TreePath treePath, InputEvent e) {
SimpleNode node = tree.getNodeFor(treePath);
if (node != null) {
node.handleDoubleClickOrEnter(tree, e);
}
}
@Override
public boolean isVisible(Object userObject) {
return userObject instanceof ProfileNode;
}
@Override
public MavenUIUtil.CheckBoxState getState(Object userObject) {
MavenProfileKind state = ((ProfileNode)userObject).getState();
switch (state) {
case NONE:
return MavenUIUtil.CheckBoxState.UNCHECKED;
case EXPLICIT:
return MavenUIUtil.CheckBoxState.CHECKED;
case IMPLICIT:
return MavenUIUtil.CheckBoxState.PARTIAL;
}
MavenLog.LOG.error("unknown profile state: " + state);
return MavenUIUtil.CheckBoxState.UNCHECKED;
}
});
}
@Override
public RootNode getRootElement() {
return myRoot;
}
public void update() {
List<MavenProject> projects = myProjectsManager.getProjects();
Set<MavenProject> deleted = new HashSet<MavenProject>(myProjectToNodeMapping.keySet());
deleted.removeAll(projects);
updateProjects(projects, deleted);
}
private void updateFrom(SimpleNode node) {
myTreeBuilder.addSubtreeToUpdateByElement(node);
}
private void updateUpTo(SimpleNode node) {
SimpleNode each = node;
while (each != null) {
updateFrom(each);
each = each.getParent();
}
}
public void updateProjects(List<MavenProject> updated, Collection<MavenProject> deleted) {
for (MavenProject each : updated) {
ProjectNode node = findNodeFor(each);
if (node == null) {
node = new ProjectNode(each);
myProjectToNodeMapping.put(each, node);
}
doUpdateProject(node);
}
for (MavenProject each : deleted) {
ProjectNode node = myProjectToNodeMapping.remove(each);
if (node != null) {
ProjectsGroupNode parent = node.getGroup();
parent.remove(node);
}
}
myRoot.updateProfiles();
}
private void doUpdateProject(ProjectNode node) {
MavenProject project = node.getMavenProject();
ProjectsGroupNode newParentNode = myRoot;
if (myProjectsNavigator.getGroupModules()) {
MavenProject aggregator = myProjectsManager.findAggregator(project);
if (aggregator != null) {
ProjectNode aggregatorNode = findNodeFor(aggregator);
if (aggregatorNode != null && aggregatorNode.isVisible()) {
newParentNode = aggregatorNode.getModulesNode();
}
}
}
node.updateProject();
reconnectNode(node, newParentNode);
ProjectsGroupNode newModulesParentNode = myProjectsNavigator.getGroupModules() && node.isVisible() ? node.getModulesNode() : myRoot;
for (MavenProject each : myProjectsManager.getModules(project)) {
ProjectNode moduleNode = findNodeFor(each);
if (moduleNode != null && !moduleNode.getParent().equals(newModulesParentNode)) {
reconnectNode(moduleNode, newModulesParentNode);
}
}
}
private void reconnectNode(ProjectNode node, ProjectsGroupNode newParentNode) {
ProjectsGroupNode oldParentNode = node.getGroup();
if (oldParentNode == null || !oldParentNode.equals(newParentNode)) {
if (oldParentNode != null) {
oldParentNode.remove(node);
}
newParentNode.add(node);
}
else {
newParentNode.sortProjects();
}
}
public void updateProfiles() {
myRoot.updateProfiles();
}
public void updateIgnored(List<MavenProject> projects) {
for (MavenProject each : projects) {
ProjectNode node = findNodeFor(each);
if (node == null) continue;
node.updateIgnored();
}
}
public void accept(SimpleNodeVisitor visitor) {
((SimpleTree)myTreeBuilder.getTree()).accept(myTreeBuilder, visitor);
}
public void updateGoals() {
for (ProjectNode each : myProjectToNodeMapping.values()) {
each.updateGoals();
}
}
public void updateRunConfigurations() {
for (ProjectNode each : myProjectToNodeMapping.values()) {
each.updateRunConfigurations();
}
}
public void select(MavenProject project) {
ProjectNode node = findNodeFor(project);
if (node != null) select(node);
}
public void select(SimpleNode node) {
myTreeBuilder.select(node, null);
}
private ProjectNode findNodeFor(MavenProject project) {
return myProjectToNodeMapping.get(project);
}
private boolean isShown(Class aClass) {
Class<? extends MavenSimpleNode>[] classes = getVisibleNodesClasses();
if (classes == null) return true;
for (Class<? extends MavenSimpleNode> c : classes) {
if (c == aClass) {
return true;
}
}
return false;
}
enum DisplayKind {
ALWAYS, NEVER, NORMAL
}
protected Class<? extends MavenSimpleNode>[] getVisibleNodesClasses() {
return null;
}
protected boolean showDescriptions() {
return true;
}
protected boolean showOnlyBasicPhases() {
return myProjectsNavigator.getShowBasicPhasesOnly();
}
public static <T extends MavenSimpleNode> List<T> getSelectedNodes(SimpleTree tree, Class<T> nodeClass) {
final List<T> filtered = new ArrayList<T>();
for (SimpleNode node : getSelectedNodes(tree)) {
if ((nodeClass != null) && (!nodeClass.isInstance(node))) {
filtered.clear();
break;
}
//noinspection unchecked
filtered.add((T)node);
}
return filtered;
}
private static List<SimpleNode> getSelectedNodes(SimpleTree tree) {
List<SimpleNode> nodes = new ArrayList<SimpleNode>();
TreePath[] treePaths = tree.getSelectionPaths();
if (treePaths != null) {
for (TreePath treePath : treePaths) {
nodes.add(tree.getNodeFor(treePath));
}
}
return nodes;
}
@Nullable
public static ProjectNode getCommonProjectNode(Collection<? extends MavenSimpleNode> nodes) {
ProjectNode parent = null;
for (MavenSimpleNode node : nodes) {
ProjectNode nextParent = node.findParent(ProjectNode.class);
if (parent == null) {
parent = nextParent;
}
else if (parent != nextParent) {
return null;
}
}
return parent;
}
public enum ErrorLevel {
NONE, ERROR
}
public abstract class MavenSimpleNode extends CachingSimpleNode {
private MavenSimpleNode myParent;
private ErrorLevel myErrorLevel = ErrorLevel.NONE;
private ErrorLevel myTotalErrorLevel = null;
public MavenSimpleNode(MavenSimpleNode parent) {
super(MavenProjectsStructure.this.myProject, null);
setParent(parent);
}
public void setParent(MavenSimpleNode parent) {
myParent = parent;
}
@Override
public NodeDescriptor getParentDescriptor() {
return myParent;
}
public <T extends MavenSimpleNode> T findParent(Class<T> parentClass) {
MavenSimpleNode node = this;
while (true) {
node = node.myParent;
if (node == null || parentClass.isInstance(node)) {
//noinspection unchecked
return (T)node;
}
}
}
public boolean isVisible() {
return getDisplayKind() != DisplayKind.NEVER;
}
public DisplayKind getDisplayKind() {
Class[] visibles = getVisibleNodesClasses();
if (visibles == null) return DisplayKind.NORMAL;
for (Class each : visibles) {
if (each.isInstance(this)) return DisplayKind.ALWAYS;
}
return DisplayKind.NEVER;
}
@Override
protected SimpleNode[] buildChildren() {
List<? extends MavenSimpleNode> children = doGetChildren();
if (children.isEmpty()) return NO_CHILDREN;
List<MavenSimpleNode> result = new ArrayList<MavenSimpleNode>();
for (MavenSimpleNode each : children) {
if (each.isVisible()) result.add(each);
}
return result.toArray(new MavenSimpleNode[result.size()]);
}
protected List<? extends MavenSimpleNode> doGetChildren() {
return Collections.emptyList();
}
@Override
public void cleanUpCache() {
super.cleanUpCache();
myTotalErrorLevel = null;
}
protected void childrenChanged() {
MavenSimpleNode each = this;
while (each != null) {
each.cleanUpCache();
each = (MavenSimpleNode)each.getParent();
}
updateUpTo(this);
}
public ErrorLevel getTotalErrorLevel() {
if (myTotalErrorLevel == null) {
myTotalErrorLevel = calcTotalErrorLevel();
}
return myTotalErrorLevel;
}
private ErrorLevel calcTotalErrorLevel() {
ErrorLevel childrenErrorLevel = getChildrenErrorLevel();
return childrenErrorLevel.compareTo(myErrorLevel) > 0 ? childrenErrorLevel : myErrorLevel;
}
public ErrorLevel getChildrenErrorLevel() {
ErrorLevel result = ErrorLevel.NONE;
for (SimpleNode each : getChildren()) {
ErrorLevel eachLevel = ((MavenSimpleNode)each).getTotalErrorLevel();
if (eachLevel.compareTo(result) > 0) result = eachLevel;
}
return result;
}
public void setErrorLevel(ErrorLevel level) {
if (myErrorLevel == level) return;
myErrorLevel = level;
updateUpTo(this);
}
@Override
protected void doUpdate() {
setNameAndTooltip(getName(), null);
}
protected void setNameAndTooltip(String name, @Nullable String tooltip) {
setNameAndTooltip(name, tooltip, (String)null);
}
protected void setNameAndTooltip(String name, @Nullable String tooltip, @Nullable String hint) {
setNameAndTooltip(name, tooltip, getPlainAttributes());
if (showDescriptions() && !StringUtil.isEmptyOrSpaces(hint)) {
addColoredFragment(" (" + hint + ")", SimpleTextAttributes.GRAY_ATTRIBUTES);
}
}
protected void setNameAndTooltip(String name, @Nullable String tooltip, SimpleTextAttributes attributes) {
clearColoredText();
addColoredFragment(name, prepareAttributes(attributes));
getTemplatePresentation().setTooltip(tooltip);
}
private SimpleTextAttributes prepareAttributes(SimpleTextAttributes from) {
ErrorLevel level = getTotalErrorLevel();
Color waveColor = level == ErrorLevel.NONE ? null : JBColor.RED;
int style = from.getStyle();
if (waveColor != null) style |= SimpleTextAttributes.STYLE_WAVED;
return new SimpleTextAttributes(from.getBgColor(), from.getFgColor(), waveColor, style);
}
@Nullable
@NonNls
String getActionId() {
return null;
}
@Nullable
@NonNls
String getMenuId() {
return null;
}
@Nullable
public VirtualFile getVirtualFile() {
return null;
}
@Nullable
public Navigatable getNavigatable() {
return MavenNavigationUtil.createNavigatableForPom(getProject(), getVirtualFile());
}
@Override
public void handleDoubleClickOrEnter(SimpleTree tree, InputEvent inputEvent) {
String actionId = getActionId();
if (actionId != null) {
MavenUIUtil.executeAction(actionId, inputEvent);
}
}
}
public abstract class GroupNode extends MavenSimpleNode {
public GroupNode(MavenSimpleNode parent) {
super(parent);
}
@Override
public boolean isVisible() {
if (getDisplayKind() == DisplayKind.ALWAYS) return true;
for (SimpleNode each : getChildren()) {
if (((MavenSimpleNode)each).isVisible()) return true;
}
return false;
}
protected <T extends MavenSimpleNode> void insertSorted(List<T> list, T newObject) {
int pos = Collections.binarySearch(list, newObject, NODE_COMPARATOR);
list.add(pos >= 0 ? pos : -pos - 1, newObject);
}
protected void sort(List<? extends MavenSimpleNode> list) {
Collections.sort(list, NODE_COMPARATOR);
}
}
public abstract class ProjectsGroupNode extends GroupNode {
private final List<ProjectNode> myProjectNodes = new ArrayList<ProjectNode>();
public ProjectsGroupNode(MavenSimpleNode parent) {
super(parent);
setUniformIcon(MavenIcons.ModulesClosed);
}
@Override
protected List<? extends MavenSimpleNode> doGetChildren() {
return myProjectNodes;
}
@TestOnly
public List<ProjectNode> getProjectNodesInTests() {
return myProjectNodes;
}
protected void add(ProjectNode projectNode) {
projectNode.setParent(this);
insertSorted(myProjectNodes, projectNode);
childrenChanged();
}
public void remove(ProjectNode projectNode) {
projectNode.setParent(null);
myProjectNodes.remove(projectNode);
childrenChanged();
}
public void sortProjects() {
sort(myProjectNodes);
childrenChanged();
}
}
public class RootNode extends ProjectsGroupNode {
private final ProfilesNode myProfilesNode;
public RootNode() {
super(null);
myProfilesNode = new ProfilesNode(this);
}
@Override
public boolean isVisible() {
return true;
}
@Override
protected List<? extends MavenSimpleNode> doGetChildren() {
return ContainerUtil.concat(Collections.singletonList(myProfilesNode), super.doGetChildren());
}
public void updateProfiles() {
myProfilesNode.updateProfiles();
}
}
public class ProfilesNode extends GroupNode {
private List<ProfileNode> myProfileNodes = new ArrayList<ProfileNode>();
public ProfilesNode(MavenSimpleNode parent) {
super(parent);
setUniformIcon(MavenIcons.ProfilesClosed);
}
@Override
protected List<? extends MavenSimpleNode> doGetChildren() {
return myProfileNodes;
}
@Override
public String getName() {
return message("view.node.profiles");
}
public void updateProfiles() {
Collection<Pair<String, MavenProfileKind>> profiles = myProjectsManager.getProfilesWithStates();
List<ProfileNode> newNodes = new ArrayList<ProfileNode>(profiles.size());
for (Pair<String, MavenProfileKind> each : profiles) {
ProfileNode node = findOrCreateNodeFor(each.first);
node.setState(each.second);
newNodes.add(node);
}
myProfileNodes = newNodes;
sort(myProfileNodes);
childrenChanged();
}
private ProfileNode findOrCreateNodeFor(String profileName) {
for (ProfileNode each : myProfileNodes) {
if (each.getProfileName().equals(profileName)) return each;
}
return new ProfileNode(this, profileName);
}
}
public class ProfileNode extends MavenSimpleNode {
private final String myProfileName;
private MavenProfileKind myState;
public ProfileNode(ProfilesNode parent, String profileName) {
super(parent);
myProfileName = profileName;
}
@Override
public String getName() {
return myProfileName;
}
public String getProfileName() {
return myProfileName;
}
public MavenProfileKind getState() {
return myState;
}
private void setState(MavenProfileKind state) {
myState = state;
}
@Override
@Nullable
@NonNls
protected String getActionId() {
return "Maven.ToggleProfile";
}
@Nullable
@Override
public Navigatable getNavigatable() {
if (myProject == null) return null;
final List<MavenDomProfile> profiles = ContainerUtil.newArrayList();
// search in "Per User Maven Settings" - %USER_HOME%/.m2/settings.xml
// and in "Global Maven Settings" - %M2_HOME%/conf/settings.xml
for (VirtualFile virtualFile : myProjectsManager.getGeneralSettings().getEffectiveSettingsFiles()) {
if (virtualFile != null) {
final MavenDomSettingsModel model = MavenDomUtil.getMavenDomModel(myProject, virtualFile, MavenDomSettingsModel.class);
if (model != null) {
addProfiles(profiles, model.getProfiles().getProfiles());
}
}
}
for (MavenProject mavenProject : myProjectsManager.getProjects()) {
// search in "Profile descriptors" - located in project basedir (profiles.xml)
final VirtualFile mavenProjectFile = mavenProject.getFile();
final VirtualFile profilesXmlFile = MavenUtil.findProfilesXmlFile(mavenProjectFile);
if (profilesXmlFile != null) {
final MavenDomProfiles profilesModel = MavenDomUtil.getMavenDomProfilesModel(myProject, profilesXmlFile);
if (profilesModel != null) {
addProfiles(profiles, profilesModel.getProfiles());
}
}
// search in "Per Project" - Defined in the POM itself (pom.xml)
final MavenDomProjectModel projectModel = MavenDomUtil.getMavenDomProjectModel(myProject, mavenProjectFile);
if (projectModel != null) {
addProfiles(profiles, projectModel.getProfiles().getProfiles());
}
}
return getNavigatable(profiles);
}
private Navigatable getNavigatable(@NotNull final List<MavenDomProfile> profiles) {
if (profiles.size() > 1) {
return new NavigatableAdapter() {
@Override
public void navigate(final boolean requestFocus) {
final JBList list = new JBList(profiles);
list.setCellRenderer(new DefaultListCellRenderer() {
@Override
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
Component result = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
@SuppressWarnings("unchecked") MavenDomProfile mavenDomProfile = (MavenDomProfile)value;
XmlElement xmlElement = mavenDomProfile.getXmlElement();
if (xmlElement != null) {
setText(xmlElement.getContainingFile().getVirtualFile().getPath());
}
return result;
}
});
JBPopupFactory.getInstance().createListPopupBuilder(list)
.setTitle("Choose file to open ")
.setItemChoosenCallback(new Runnable() {
public void run() {
final Object value = list.getSelectedValue();
if (value instanceof MavenDomProfile) {
final Navigatable navigatable = getNavigatable((MavenDomProfile)value);
if (navigatable != null) navigatable.navigate(requestFocus);
}
}
}).createPopup().showInFocusCenter();
}
};
}
else {
return getNavigatable(ContainerUtil.getFirstItem(profiles));
}
}
@Nullable
private Navigatable getNavigatable(@Nullable final MavenDomProfile profile) {
if (profile == null) return null;
XmlElement xmlElement = profile.getId().getXmlElement();
return xmlElement instanceof Navigatable ? (Navigatable)xmlElement : null;
}
private void addProfiles(@NotNull List<MavenDomProfile> result, @Nullable List<MavenDomProfile> profilesToAdd) {
if (profilesToAdd == null) return;
for (MavenDomProfile profile : profilesToAdd) {
if (StringUtil.equals(profile.getId().getValue(), myProfileName)) {
result.add(profile);
}
}
}
}
public class ProjectNode extends GroupNode {
private final MavenProject myMavenProject;
private final LifecycleNode myLifecycleNode;
private final PluginsNode myPluginsNode;
private final DependenciesNode myDependenciesNode;
private final ModulesNode myModulesNode;
private final RunConfigurationsNode myRunConfigurationsNode;
private String myTooltipCache;
public ProjectNode(@NotNull MavenProject mavenProject) {
super(null);
myMavenProject = mavenProject;
myLifecycleNode = new LifecycleNode(this);
myPluginsNode = new PluginsNode(this);
myDependenciesNode = new DependenciesNode(this, mavenProject);
myModulesNode = new ModulesNode(this);
myRunConfigurationsNode = new RunConfigurationsNode(this);
setUniformIcon(MavenIcons.MavenProject);
updateProject();
}
public MavenProject getMavenProject() {
return myMavenProject;
}
public ProjectsGroupNode getGroup() {
return (ProjectsGroupNode)super.getParent();
}
@Override
public boolean isVisible() {
if (!myProjectsNavigator.getShowIgnored() && myProjectsManager.isIgnored(myMavenProject)) return false;
return super.isVisible();
}
@Override
protected List<? extends MavenSimpleNode> doGetChildren() {
return Arrays.asList(myLifecycleNode, myPluginsNode, myRunConfigurationsNode, myDependenciesNode, myModulesNode);
}
public ModulesNode getModulesNode() {
return myModulesNode;
}
private void updateProject() {
setErrorLevel(myMavenProject.getProblems().isEmpty() ? ErrorLevel.NONE : ErrorLevel.ERROR);
myLifecycleNode.updateGoalsList();
myPluginsNode.updatePlugins(myMavenProject);
if (isShown(DependencyNode.class)) {
myDependenciesNode.updateDependencies();
}
myRunConfigurationsNode.updateRunConfigurations(myMavenProject);
myTooltipCache = makeDescription();
updateFrom(getParent());
}
public void updateIgnored() {
getGroup().childrenChanged();
}
public void updateGoals() {
updateFrom(myLifecycleNode);
updateFrom(myPluginsNode);
}
public void updateRunConfigurations() {
myRunConfigurationsNode.updateRunConfigurations(myMavenProject);
updateFrom(myRunConfigurationsNode);
}
@Override
public String getName() {
if (myProjectsNavigator.getAlwaysShowArtifactId()) {
return myMavenProject.getMavenId().getArtifactId();
}
else {
return myMavenProject.getDisplayName();
}
}
@Override
protected void doUpdate() {
String hint = null;
if (!myProjectsNavigator.getGroupModules()
&& myProjectsManager.findAggregator(myMavenProject) == null
&& myProjectsManager.getProjects().size() > myProjectsManager.getRootProjects().size()) {
hint = "root";
}
setNameAndTooltip(getName(), myTooltipCache, hint);
}
@Override
protected SimpleTextAttributes getPlainAttributes() {
if (myProjectsManager.isIgnored(myMavenProject)) {
return new SimpleTextAttributes(SimpleTextAttributes.STYLE_PLAIN, JBColor.GRAY);
}
return super.getPlainAttributes();
}
private String makeDescription() {
StringBuilder desc = new StringBuilder();
desc.append("<html>" +
"<table>" +
"<tr>" +
"<td nowrap>" +
"<table>" +
"<tr>" +
"<td nowrap>Project:</td>" +
"<td nowrap>").append(myMavenProject.getMavenId())
.append("</td>" +
"</tr>" +
"<tr>" +
"<td nowrap>Location:</td>" +
"<td nowrap>").append(myMavenProject.getPath())
.append("</td>" +
"</tr>" +
"</table>" +
"</td>" +
"</tr>");
appendProblems(desc);
desc.append("</table></html>");
return desc.toString();
}
private void appendProblems(StringBuilder desc) {
List<MavenProjectProblem> problems = myMavenProject.getProblems();
if (problems.isEmpty()) return;
desc.append("<tr>" +
"<td nowrap>" +
"<table>");
boolean first = true;
for (MavenProjectProblem each : problems) {
desc.append("<tr>");
if (first) {
desc.append("<td nowrap valign=top>").append(MavenUtil.formatHtmlImage(ERROR_ICON_URL)).append("</td>");
desc.append("<td nowrap valign=top>Problems:</td>");
first = false;
}
else {
desc.append("<td nowrap colspan=2></td>");
}
desc.append("<td nowrap valign=top>").append(wrappedText(each)).append("</td>");
desc.append("</tr>");
}
desc.append("</table>" +
"</td>" +
"</tr>");
}
private String wrappedText(MavenProjectProblem each) {
String text = StringUtil.replace(each.getDescription(), new String[]{"<", ">"}, new String[]{"&lt;", "&gt;"});
StringBuilder result = new StringBuilder();
int count = 0;
for (int i = 0; i < text.length(); i++) {
char ch = text.charAt(i);
result.append(ch);
if (count++ > 80) {
if (ch == ' ') {
count = 0;
result.append("<br>");
}
}
}
return result.toString();
}
@Override
public VirtualFile getVirtualFile() {
return myMavenProject.getFile();
}
@Override
protected void setNameAndTooltip(String name, @Nullable String tooltip, SimpleTextAttributes attributes) {
super.setNameAndTooltip(name, tooltip, attributes);
if (myProjectsNavigator.getShowVersions()) {
addColoredFragment(":" + myMavenProject.getMavenId().getVersion(), new SimpleTextAttributes(SimpleTextAttributes.STYLE_PLAIN, JBColor.GRAY));
}
}
@Override
@Nullable
@NonNls
protected String getMenuId() {
return "Maven.NavigatorProjectMenu";
}
}
public class ModulesNode extends ProjectsGroupNode {
public ModulesNode(ProjectNode parent) {
super(parent);
setUniformIcon(MavenIcons.ModulesClosed);
}
@Override
public String getName() {
return message("view.node.modules");
}
}
public abstract class GoalsGroupNode extends GroupNode {
protected final List<GoalNode> myGoalNodes = new ArrayList<GoalNode>();
public GoalsGroupNode(MavenSimpleNode parent) {
super(parent);
}
@Override
protected List<? extends MavenSimpleNode> doGetChildren() {
return myGoalNodes;
}
}
public abstract class GoalNode extends MavenSimpleNode {
private final MavenProject myMavenProject;
private final String myGoal;
private final String myDisplayName;
public GoalNode(GoalsGroupNode parent, String goal, String displayName) {
super(parent);
myMavenProject = findParent(ProjectNode.class).getMavenProject();
myGoal = goal;
myDisplayName = displayName;
setUniformIcon(MavenIcons.Phase);
}
public String getProjectPath() {
return myMavenProject.getPath();
}
public String getGoal() {
return myGoal;
}
@Override
public String getName() {
return myDisplayName;
}
@Override
protected void doUpdate() {
String s1 = StringUtil.nullize(myShortcutsManager.getDescription(myMavenProject, myGoal));
String s2 = StringUtil.nullize(myTasksManager.getDescription(myMavenProject, myGoal));
String hint;
if (s1 == null) {
hint = s2;
}
else if (s2 == null) {
hint = s1;
}
else {
hint = s1 + ", " + s2;
}
setNameAndTooltip(getName(), null, hint);
}
@Override
protected SimpleTextAttributes getPlainAttributes() {
SimpleTextAttributes original = super.getPlainAttributes();
int style = original.getStyle();
Color color = original.getFgColor();
boolean custom = false;
if ("test".equals(myGoal) && MavenRunner.getInstance(myProject).getSettings().isSkipTests()) {
color = SimpleTextAttributes.GRAYED_ATTRIBUTES.getFgColor();
style |= SimpleTextAttributes.STYLE_STRIKEOUT;
custom = true;
}
if (myGoal.equals(myMavenProject.getDefaultGoal())) {
style |= SimpleTextAttributes.STYLE_BOLD;
custom = true;
}
if (custom) return original.derive(style, color, null, null);
return original;
}
@Override
@Nullable
@NonNls
protected String getActionId() {
return "Maven.RunBuild";
}
@Override
@Nullable
@NonNls
protected String getMenuId() {
return "Maven.BuildMenu";
}
}
public class LifecycleNode extends GoalsGroupNode {
public LifecycleNode(ProjectNode parent) {
super(parent);
for (String goal : PHASES) {
myGoalNodes.add(new StandardGoalNode(this, goal));
}
setUniformIcon(MavenIcons.PhasesClosed);
}
@Override
public String getName() {
return message("view.node.lifecycle");
}
public void updateGoalsList() {
childrenChanged();
}
}
public class StandardGoalNode extends GoalNode {
public StandardGoalNode(GoalsGroupNode parent, String goal) {
super(parent, goal, goal);
}
@Override
public boolean isVisible() {
if (showOnlyBasicPhases() && !BASIC_PHASES.contains(getGoal())) return false;
return super.isVisible();
}
}
public class PluginsNode extends GroupNode {
private final List<PluginNode> myPluginNodes = new ArrayList<PluginNode>();
public PluginsNode(ProjectNode parent) {
super(parent);
setUniformIcon(MavenIcons.PhasesClosed);
}
@Override
public String getName() {
return message("view.node.plugins");
}
@Override
protected List<? extends MavenSimpleNode> doGetChildren() {
return myPluginNodes;
}
public void updatePlugins(MavenProject mavenProject) {
List<MavenPlugin> plugins = mavenProject.getDeclaredPlugins();
for (Iterator<PluginNode> itr = myPluginNodes.iterator(); itr.hasNext(); ) {
PluginNode each = itr.next();
if (plugins.contains(each.getPlugin())) {
each.updatePlugin();
}
else {
itr.remove();
}
}
for (MavenPlugin each : plugins) {
if (!hasNodeFor(each)) {
myPluginNodes.add(new PluginNode(this, each));
}
}
sort(myPluginNodes);
childrenChanged();
}
private boolean hasNodeFor(MavenPlugin plugin) {
for (PluginNode each : myPluginNodes) {
if (each.getPlugin().getMavenId().equals(plugin.getMavenId())) {
return true;
}
}
return false;
}
}
public class PluginNode extends GoalsGroupNode {
private final MavenPlugin myPlugin;
private MavenPluginInfo myPluginInfo;
public PluginNode(PluginsNode parent, MavenPlugin plugin) {
super(parent);
myPlugin = plugin;
setUniformIcon(MavenIcons.MavenPlugin);
updatePlugin();
}
public MavenPlugin getPlugin() {
return myPlugin;
}
@Override
public String getName() {
return myPluginInfo == null ? myPlugin.getDisplayString() : myPluginInfo.getGoalPrefix();
}
@Override
protected void doUpdate() {
setNameAndTooltip(getName(), null, myPluginInfo != null ? myPlugin.getDisplayString() : null);
}
public void updatePlugin() {
boolean hadPluginInfo = myPluginInfo != null;
myPluginInfo = MavenArtifactUtil.readPluginInfo(myProjectsManager.getLocalRepository(), myPlugin.getMavenId());
boolean hasPluginInfo = myPluginInfo != null;
setErrorLevel(myPluginInfo == null ? ErrorLevel.ERROR : ErrorLevel.NONE);
if (hadPluginInfo == hasPluginInfo) return;
myGoalNodes.clear();
if (myPluginInfo != null) {
for (MavenPluginInfo.Mojo mojo : myPluginInfo.getMojos()) {
myGoalNodes.add(new PluginGoalNode(this, mojo.getQualifiedGoal(), mojo.getGoal(), mojo.getDisplayName()));
}
}
sort(myGoalNodes);
updateFrom(this);
childrenChanged();
}
@Override
public boolean isVisible() {
// show regardless absence of children
return super.isVisible() || getDisplayKind() != DisplayKind.NEVER;
}
}
public class PluginGoalNode extends GoalNode {
private final String myUnqualifiedGoal;
public PluginGoalNode(PluginNode parent, String goal, String unqualifiedGoal, String displayName) {
super(parent, goal, displayName);
setUniformIcon(MavenIcons.PluginGoal);
myUnqualifiedGoal = unqualifiedGoal;
}
@Nullable
@Override
public Navigatable getNavigatable() {
PluginNode pluginNode = (PluginNode)getParent();
MavenDomPluginModel pluginModel = MavenPluginDomUtil.getMavenPluginModel(myProject,
pluginNode.getPlugin().getGroupId(),
pluginNode.getPlugin().getArtifactId(),
pluginNode.getPlugin().getVersion());
if (pluginModel == null) return null;
for (MavenDomMojo mojo : pluginModel.getMojos().getMojos()) {
final XmlElement xmlElement = mojo.getGoal().getXmlElement();
if (xmlElement instanceof Navigatable && Comparing.equal(myUnqualifiedGoal, mojo.getGoal().getStringValue())) {
return new NavigatableAdapter() {
@Override
public void navigate(boolean requestFocus) {
((Navigatable)xmlElement).navigate(requestFocus);
}
};
}
}
return null;
}
}
public abstract class BaseDependenciesNode extends GroupNode {
protected final MavenProject myMavenProject;
private List<DependencyNode> myChildren = new ArrayList<DependencyNode>();
protected BaseDependenciesNode(MavenSimpleNode parent, MavenProject mavenProject) {
super(parent);
myMavenProject = mavenProject;
}
public MavenProject getMavenProject() {
return myMavenProject;
}
@Override
protected List<? extends MavenSimpleNode> doGetChildren() {
return myChildren;
}
protected void updateChildren(List<MavenArtifactNode> children, MavenProject mavenProject) {
List<DependencyNode> newNodes = null;
int validChildCount = 0;
for (MavenArtifactNode each : children) {
if (each.getState() != MavenArtifactState.ADDED) continue;
if (newNodes == null) {
if (validChildCount < myChildren.size()) {
DependencyNode currentValidNode = myChildren.get(validChildCount);
if (currentValidNode.myArtifact.equals(each.getArtifact())) {
currentValidNode.updateChildren(each.getDependencies(), mavenProject);
currentValidNode.updateDependency();
validChildCount++;
continue;
}
}
newNodes = new ArrayList<DependencyNode>(children.size());
newNodes.addAll(myChildren.subList(0, validChildCount));
}
DependencyNode newNode = findOrCreateNodeFor(each, mavenProject, validChildCount);
newNodes.add(newNode);
newNode.updateChildren(each.getDependencies(), mavenProject);
newNode.updateDependency();
}
if (newNodes == null) {
if (validChildCount == myChildren.size()) {
return; // All nodes are valid, child did not changed.
}
assert validChildCount < myChildren.size();
newNodes = new ArrayList<DependencyNode>(myChildren.subList(0, validChildCount));
}
myChildren = newNodes;
childrenChanged();
}
private DependencyNode findOrCreateNodeFor(MavenArtifactNode artifact, MavenProject mavenProject, int from) {
for (int i = from; i < myChildren.size(); i++) {
DependencyNode node = myChildren.get(i);
if (node.myArtifact.equals(artifact.getArtifact())) {
return node;
}
}
return new DependencyNode(this, artifact, mavenProject);
}
@Override
String getMenuId() {
return "Maven.DependencyMenu";
}
}
public class DependenciesNode extends BaseDependenciesNode {
public DependenciesNode(ProjectNode parent, MavenProject mavenProject) {
super(parent, mavenProject);
setUniformIcon(AllIcons.Nodes.PpLibFolder);
}
@Override
public String getName() {
return message("view.node.dependencies");
}
public void updateDependencies() {
updateChildren(myMavenProject.getDependencyTree(), myMavenProject);
}
}
public class DependencyNode extends BaseDependenciesNode {
private final MavenArtifact myArtifact;
private final MavenArtifactNode myArtifactNode;
public DependencyNode(MavenSimpleNode parent, MavenArtifactNode artifactNode, MavenProject mavenProject) {
super(parent, mavenProject);
myArtifactNode = artifactNode;
myArtifact = artifactNode.getArtifact();
setUniformIcon(AllIcons.Nodes.PpLib);
}
public MavenArtifact getArtifact() {
return myArtifact;
}
@Override
public String getName() {
return myArtifact.getDisplayStringForLibraryName();
}
@Override
protected void doUpdate() {
String scope = myArtifact.getScope();
setNameAndTooltip(getName(), null, MavenConstants.SCOPE_COMPILE.equals(scope) ? null : scope);
}
private void updateDependency() {
setErrorLevel(myArtifact.isResolved() ? ErrorLevel.NONE : ErrorLevel.ERROR);
}
@Override
public Navigatable getNavigatable() {
final MavenArtifactNode parent = myArtifactNode.getParent();
final VirtualFile file;
if (parent == null) {
file = getMavenProject().getFile();
}
else {
final MavenId id = parent.getArtifact().getMavenId();
final MavenProject pr = myProjectsManager.findProject(id);
file = pr == null ? MavenNavigationUtil.getArtifactFile(getProject(), id) : pr.getFile();
}
return file == null ? null : MavenNavigationUtil.createNavigatableForDependency(getProject(), file, getArtifact());
}
@Override
public boolean isVisible() {
// show regardless absence of children
return getDisplayKind() != DisplayKind.NEVER;
}
}
public class RunConfigurationsNode extends GroupNode {
private final List<RunConfigurationNode> myChildren = new ArrayList<RunConfigurationNode>();
public RunConfigurationsNode(ProjectNode parent) {
super(parent);
setUniformIcon(MavenIcons.Phase);
}
@Override
public String getName() {
return message("view.node.run.configurations");
}
@Override
protected List<? extends MavenSimpleNode> doGetChildren() {
return myChildren;
}
public void updateRunConfigurations(MavenProject mavenProject) {
boolean childChanged = false;
Set<RunnerAndConfigurationSettings> settings = new THashSet<RunnerAndConfigurationSettings>(
RunManager.getInstance(myProject).getConfigurationSettingsList(MavenRunConfigurationType.getInstance()));
for (Iterator<RunConfigurationNode> itr = myChildren.iterator(); itr.hasNext(); ) {
RunConfigurationNode node = itr.next();
if (settings.remove(node.getSettings())) {
node.updateRunConfiguration();
}
else {
itr.remove();
childChanged = true;
}
}
String directory = PathUtil.getCanonicalPath(mavenProject.getDirectory());
int oldSize = myChildren.size();
for (RunnerAndConfigurationSettings cfg : settings) {
MavenRunConfiguration mavenRunConfiguration = (MavenRunConfiguration)cfg.getConfiguration();
if (directory.equals(PathUtil.getCanonicalPath(mavenRunConfiguration.getRunnerParameters().getWorkingDirPath()))) {
myChildren.add(new RunConfigurationNode(this, cfg));
}
}
if (oldSize != myChildren.size()) {
childChanged = true;
sort(myChildren);
}
if (childChanged) {
childrenChanged();
}
}
}
public class RunConfigurationNode extends MavenSimpleNode {
private final RunnerAndConfigurationSettings mySettings;
public RunConfigurationNode(RunConfigurationsNode parent, RunnerAndConfigurationSettings settings) {
super(parent);
mySettings = settings;
setUniformIcon(ProgramRunnerUtil.getConfigurationIcon(settings, false));
}
public RunnerAndConfigurationSettings getSettings() {
return mySettings;
}
@Override
public String getName() {
return mySettings.getName();
}
@Override
protected void doUpdate() {
setNameAndTooltip(getName(),
null,
StringUtil.join(((MavenRunConfiguration)mySettings.getConfiguration()).getRunnerParameters().getGoals(), " "));
}
@Nullable
@Override
String getMenuId() {
return "Maven.RunConfigurationMenu";
}
public void updateRunConfiguration() {
}
@Override
public void handleDoubleClickOrEnter(SimpleTree tree, InputEvent inputEvent) {
ProgramRunnerUtil.executeConfiguration(myProject, mySettings, DefaultRunExecutor.getRunExecutorInstance());
}
}
}