org.netbeans.modules.maven.nodes.DependencyNode Maven / Gradle / Ivy
The newest version!
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.netbeans.modules.maven.nodes;
import org.netbeans.modules.maven.ModuleInfoSupport;
import java.awt.Component;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.datatransfer.StringSelection;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Supplier;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.prefs.PreferenceChangeEvent;
import java.util.prefs.PreferenceChangeListener;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.Icon;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JMenuItem;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.ArtifactUtils;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
import org.apache.maven.artifact.resolver.ArtifactResolutionException;
import org.apache.maven.model.Dependency;
import org.apache.maven.model.DependencyManagement;
import org.apache.maven.model.DistributionManagement;
import org.apache.maven.model.Profile;
import org.apache.maven.model.Relocation;
import org.apache.maven.model.building.ModelBuildingRequest;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.ProjectBuildingException;
import org.apache.maven.project.ProjectBuildingRequest;
import org.apache.maven.project.ProjectBuildingResult;
import org.codehaus.plexus.util.FileUtils;
import org.netbeans.api.annotations.common.CheckForNull;
import org.netbeans.api.annotations.common.NonNull;
import org.netbeans.api.annotations.common.NullAllowed;
import org.netbeans.api.java.queries.JavadocForBinaryQuery;
import org.netbeans.api.progress.aggregate.AggregateProgressHandle;
import org.netbeans.api.progress.aggregate.BasicAggregateProgressFactory;
import org.netbeans.api.progress.aggregate.ProgressContributor;
import org.netbeans.api.project.FileOwnerQuery;
import org.netbeans.api.project.Project;
import org.netbeans.api.project.ProjectUtils;
import org.netbeans.api.project.SourceGroup;
import org.netbeans.api.project.ui.OpenProjects;
import org.netbeans.modules.maven.NbArtifactFixer;
import org.netbeans.modules.maven.NbMavenProjectImpl;
import org.netbeans.modules.maven.api.CommonArtifactActions;
import org.netbeans.modules.maven.api.NbMavenProject;
import org.netbeans.modules.maven.dependencies.DependencyExcludeNodeVisitor;
import org.netbeans.modules.maven.dependencies.ExcludeDependencyPanel;
import org.netbeans.modules.maven.embedder.DependencyTreeFactory;
import org.netbeans.modules.maven.embedder.EmbedderFactory;
import org.netbeans.modules.maven.embedder.MavenEmbedder;
import org.netbeans.modules.maven.embedder.exec.ProgressTransferListener;
import org.netbeans.modules.maven.model.ModelOperation;
import org.netbeans.modules.maven.model.Utilities;
import org.netbeans.modules.maven.model.pom.Exclusion;
import org.netbeans.modules.maven.model.pom.POMModel;
import org.netbeans.spi.project.ui.PathFinder;
import static org.netbeans.modules.maven.nodes.Bundle.*;
import org.netbeans.modules.maven.queries.MavenFileOwnerQueryImpl;
import org.netbeans.modules.maven.queries.RepositoryForBinaryQueryImpl;
import org.netbeans.modules.maven.queries.RepositoryForBinaryQueryImpl.Coordinates;
import org.netbeans.modules.maven.spi.IconResources;
import org.netbeans.modules.maven.spi.nodes.DependencyTypeIconBadge;
import org.netbeans.modules.maven.spi.queries.ForeignClassBundler;
import org.netbeans.spi.java.project.support.ui.PackageView;
import org.openide.DialogDescriptor;
import org.openide.DialogDisplayer;
import org.openide.actions.PropertiesAction;
import org.openide.awt.HtmlBrowser;
import org.openide.awt.StatusDisplayer;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
import org.openide.filesystems.URLMapper;
import org.openide.loaders.DataObject;
import org.openide.nodes.AbstractNode;
import org.openide.nodes.Children;
import org.openide.nodes.FilterNode;
import org.openide.nodes.Node;
import org.openide.nodes.PropertySupport;
import org.openide.nodes.Sheet;
import org.openide.util.ContextAwareAction;
import org.openide.util.ImageUtilities;
import org.openide.util.Lookup;
import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages;
import org.openide.util.RequestProcessor;
import org.openide.util.WeakListeners;
import org.openide.util.actions.Presenter;
import org.openide.util.lookup.Lookups;
/**
* node representing a dependency
* @author Milos Kleint
*/
public class DependencyNode extends AbstractNode implements PreferenceChangeListener {
private boolean longLiving;
private PropertyChangeListener listener;
private ChangeListener listener2;
final Data data;
private volatile String iconBase = IconResources.DEPENDENCY_ICON;
private static final String toolTipJavadoc = " " //NOI18N
+ NbBundle.getMessage(DependencyNode.class, "ICON_JavadocBadge");//NOI18N
private static final String toolTipSource = " " //NOI18N
+ NbBundle.getMessage(DependencyNode.class, "ICON_SourceBadge");//NOI18N
private static final String toolTipMissing = " " //NOI18N
+ NbBundle.getMessage(DependencyNode.class, "ICON_MissingBadge");//NOI18N
private static final String toolTipManaged = " " //NOI18N
+ NbBundle.getMessage(DependencyNode.class, "ICON_ManagedBadge");//NOI18N
private static final String toolTipForeignBundler = " " //NOI18N
+ NbBundle.getMessage(DependencyNode.class, "ICON_PreferSourcesBadge");//NOI18N
private static final RequestProcessor RP = new RequestProcessor(DependencyNode.class);
private static Children createChildren(@NullAllowed Node nodeDelegate) {
return nodeDelegate == null ? Children.LEAF : new JarContentFilterChildren(nodeDelegate);
}
@NonNull
private static Lookup createLookup(@NonNull final Project project, @NonNull final Artifact art, @NullAllowed final Node nodeDelegate, Supplier canAddToModuleInfo) {
final PathFinder pathFinderDelegate = nodeDelegate == null ? null : nodeDelegate.getLookup().lookup(PathFinder.class);
final FileObject fo = nodeDelegate == null ? null : nodeDelegate.getLookup().lookup(FileObject.class);
if (fo != null) {
return Lookups.fixed(new Data(art, project, fo, canAddToModuleInfo), project, art, PathFinders.createDelegatingPathFinder(pathFinderDelegate), fo);
} else {
return Lookups.fixed(new Data(art, project, null, canAddToModuleInfo), project, art, PathFinders.createDelegatingPathFinder(pathFinderDelegate));
}
}
@CheckForNull
static Node createNodeDelegate(@NonNull final Artifact art, FileObject fo, final boolean longLiving) {
if (!longLiving) {
return null;
}
//artifact.getFile() should be eagerly normalized
if (fo != null && FileUtil.isArchiveFile(fo)) {
return PackageView.createPackageView(new ArtifactSourceGroup(art));
}
return null;
}
public DependencyNode(
NbMavenProjectImpl project,
final Artifact art,
final FileObject fo,
boolean isLongLiving,
Node nodeDelegate,
Supplier canAddToModuleInfo) {
super(createChildren(nodeDelegate), createLookup(project, art, nodeDelegate, canAddToModuleInfo));
this.data = getLookup().lookup(Data.class);
data.fileObject.set(fo);
longLiving = isLongLiving;
if (longLiving) {
listener = new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (NbMavenProject.PROP_PROJECT.equals(evt.getPropertyName())) {
refreshNode();
}
}
};
NbMavenProject.addPropertyChangeListener(project, WeakListeners.propertyChange(listener, project.getProjectWatcher()));
listener2 = new ChangeListener() {
@Override
public void stateChanged(ChangeEvent event) {
if (event instanceof MavenFileOwnerQueryImpl.GAVCHangeEvent) {
MavenFileOwnerQueryImpl.GAVCHangeEvent ev = (MavenFileOwnerQueryImpl.GAVCHangeEvent) event;
if (ev.matches(art)) {
refreshNode();
}
} else {
refreshNode();
}
}
};
//TODO check if this one is a performance bottleneck.
MavenFileOwnerQueryImpl.getInstance().addChangeListener(
WeakListeners.change(listener2,
MavenFileOwnerQueryImpl.getInstance()));
DependenciesNode.prefs().addPreferenceChangeListener(WeakListeners.create(PreferenceChangeListener.class, this, DependenciesNode.prefs()));
}
setDisplayName(createName(false));
setIconBase(false);
if (longLiving) {
RP.post(new Runnable() {
@Override
public void run() {
refreshNode();
}
});
}
data.setNode(this);
}
private void setIconBase(boolean longLiving) {
String base;
if (longLiving && data.isDependencyProjectAvailable()) {
if (data.isTransitive()) {
base = IconResources.TRANSITIVE_MAVEN_ICON;
} else {
base = IconResources.MAVEN_ICON;
}
} else if (data.isTransitive()) {
if (data.isAddedToCP()) {
base = IconResources.TRANSITIVE_DEPENDENCY_ICON;
} else {
base = IconResources.TRANSITIVE_ARTIFACT_ICON;
}
} else if (data.isAddedToCP()) {
base = IconResources.DEPENDENCY_ICON;
} else {
base = IconResources.ARTIFACT_ICON;
}
this.iconBase = base;
setIconBaseWithExtension(base);
}
@Messages({
"DESC_Dep1=GroupId:",
"DESC_Dep2=ArtifactId:",
"DESC_Dep3=Version:",
"DESC_Dep4=Type:",
"DESC_Dep5=Classifier:",
"DESC_scope=Scope:",
"DESC_via=Via:"
})
@Override public String getShortDescription() {
StringBuilder buf = new StringBuilder();
buf.append("").append(DESC_Dep1()).append(" ").append(data.art.getGroupId()).append("
"); //NOI18N
buf.append(DESC_Dep2()).append(" ").append(data.art.getArtifactId()).append("
");//NOI18N
buf.append(DESC_Dep3()).append(" ").append(data.art.getVersion()).append("
");//NOI18N
buf.append(DESC_Dep4()).append(" ").append(data.art.getType()).append("
");//NOI18N
if (data.art.getClassifier() != null) {
buf.append("").append(DESC_Dep5()).append(" ").append(data.art.getClassifier()).append("
");//NOI18N
}
buf.append("").append(DESC_scope()).append(" ").append(data.art.getScope()).append("
");
List trail = data.art.getDependencyTrail();
for (int i = trail.size() - 2; i > 0 && /* just to be safe */ i < trail.size(); i--) {
String[] id = trail.get(i).split(":"); // g:a:t[:c]:v
buf.append("").append(DESC_via()).append(" ").append(id[1]).append("
");
}
// it seems that with ending tag the icon descriptions are not added.
// buf.append("