blob: 4300195cd715bf46fd3cef354f9d351848e35789 [file] [log] [blame]
/*
* 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.gradle.nodes;
import org.netbeans.modules.gradle.ActionProviderImpl;
import org.netbeans.modules.gradle.GradleArtifactStore;
import org.netbeans.modules.gradle.NbGradleProjectImpl;
import org.netbeans.modules.gradle.api.GradleDependency;
import org.netbeans.modules.gradle.GradleProject;
import org.netbeans.modules.gradle.api.GradleConfiguration;
import org.netbeans.modules.gradle.api.NbGradleProject;
import org.netbeans.modules.gradle.api.execute.RunUtils;
import org.netbeans.modules.gradle.spi.GradleSettings;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.prefs.PreferenceChangeEvent;
import java.util.prefs.PreferenceChangeListener;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import org.netbeans.api.annotations.common.StaticResource;
import org.netbeans.api.project.Project;
import org.netbeans.api.project.ProjectManager;
import org.netbeans.modules.gradle.api.NbGradleProject.Quality;
import org.netbeans.spi.project.ui.LogicalViewProvider;
import org.openide.awt.HtmlBrowser;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
import org.openide.loaders.DataObject;
import org.openide.loaders.DataObjectNotFoundException;
import org.openide.nodes.AbstractNode;
import org.openide.nodes.ChildFactory;
import org.openide.nodes.Children;
import org.openide.nodes.FilterNode;
import org.openide.nodes.Node;
import org.openide.util.ImageUtilities;
import org.openide.util.NbBundle;
import org.openide.util.lookup.Lookups;
/**
*
* @author Laszlo Kishalmi
*/
public class ConfigurationsNode extends AbstractNode {
private final NbGradleProjectImpl project;
private static final String REQUESTED_COMPONENT = "requestedComponent";
@StaticResource
private static final String LIBRARIES_ICON = "org/netbeans/modules/gradle/resources/libraries.png"; //NOI18N
@StaticResource
private static final String WARNING_BADGE = "org/netbeans/modules/gradle/resources/warning-badge.png"; //NOI18N
@StaticResource
private static final String SOURCES_BADGE = "org/netbeans/modules/gradle/resources/sources-badge.png"; //NOI18N
@StaticResource
private static final String JAVADOC_BADGE = "org/netbeans/modules/gradle/resources/javadoc-badge.png"; //NOI18N
@StaticResource
private static final String UNRESOLVED_ICON = "org/netbeans/modules/gradle/resources/empty.png"; //NOI18N
@StaticResource
private static final String ARTIFACT_ICON = "org/netbeans/modules/gradle/resources/module-artifact.png"; //NOI18N
private final Action downloadSourcesAction;
private final Action downloadJavadocAction;
@NbBundle.Messages({
"LBL_ConfigurationsNode=Configurations"
})
@SuppressWarnings("OverridableMethodCallInConstructor")
public ConfigurationsNode(NbGradleProjectImpl project) {
super(Children.create(new ConfigurationsChildren(project), true), Lookups.singleton(project));
this.project = project;
setName("configurations"); //NOI18N
setDisplayName(Bundle.LBL_ConfigurationsNode());
downloadSourcesAction = ActionProviderImpl.createCustomGradleAction(project, "Download Sources",
ActionProviderImpl.COMMAND_DL_SOURCES, Lookups.singleton(RunUtils.simpleReplaceTokenProvider(REQUESTED_COMPONENT, "ALL"))); //NOI18N
downloadJavadocAction = ActionProviderImpl.createCustomGradleAction(project, "Download Javadoc",
ActionProviderImpl.COMMAND_DL_JAVADOC, Lookups.singleton(RunUtils.simpleReplaceTokenProvider(REQUESTED_COMPONENT, "ALL"))); //NOI18N
}
@Override
public Image getIcon(int type) {
GradleProject gp = project.getGradleProject();
Image ret = ImageUtilities.loadImage(LIBRARIES_ICON);
if (gp.getQuality().worseThan(Quality.FULL) || !gp.getBaseProject().isResolved()) {
Image warn = ImageUtilities.loadImage(WARNING_BADGE);
ret = ImageUtilities.mergeImages(ret, warn, 8, 0);
}
return ret;
}
@Override
public Image getOpenedIcon(int type) {
return getIcon(type);
}
@Override
public Action[] getActions(boolean context) {
return new Action[] {
downloadSourcesAction,
downloadJavadocAction
};
}
@NbBundle.Messages({
"HINT_ConfigurationsNode=Project Configurations",
"HINT_ConfigurationsNodeUnresolved=There are unresolved configurations!"
})
@Override
public String getShortDescription() {
GradleProject gp = project.getGradleProject();
return gp.getBaseProject().isResolved() ? Bundle.HINT_ConfigurationsNode() : Bundle.HINT_ConfigurationsNodeUnresolved();
}
private static class ConfigurationsChildren extends ChildFactory.Detachable<GradleConfiguration> implements PreferenceChangeListener, PropertyChangeListener {
private final NbGradleProjectImpl project;
public ConfigurationsChildren(NbGradleProjectImpl project) {
this.project = project;
}
@Override
protected Node createNodeForKey(GradleConfiguration conf) {
AbstractNode ret = new AbstractNode(Children.create(new ConfigurationChildren(project, conf.getName()), true));
ret.setName(conf.getName());
ret.setShortDescription(conf.getDescription());
StringBuilder displayName = new StringBuilder(conf.getName());
if (!conf.getExtendsFrom().isEmpty()) {
displayName.append(" [");
String separator = "";
for (GradleConfiguration ext : conf.getExtendsFrom()) {
displayName.append(separator);
displayName.append(ext.getName());
separator = ", ";
}
displayName.append(']');
}
ret.setDisplayName(displayName.toString());
ret.setIconBaseWithExtension(LIBRARIES_ICON);
return ret;
}
@Override
protected boolean createKeys(List<GradleConfiguration> list) {
boolean hideEmpty = GradleSettings.getDefault().isHideEmptyConfigurations();
ArrayList<GradleConfiguration> ret = new ArrayList<>();
for (GradleConfiguration conf : project.getGradleProject().getBaseProject().getConfigurations().values()) {
if (!hideEmpty || !conf.isEmpty()) {
ret.add(conf);
}
}
Collections.sort(ret);
list.addAll(ret);
return true;
}
@Override
protected void removeNotify() {
NbGradleProject.removePropertyChangeListener(project, this);
GradleSettings.getDefault().getPreferences().removePreferenceChangeListener(this);
}
@Override
protected void addNotify() {
GradleSettings.getDefault().getPreferences().addPreferenceChangeListener(this);
NbGradleProject.addPropertyChangeListener(project, this);
}
@Override
public void preferenceChange(PreferenceChangeEvent evt) {
if (GradleSettings.PROP_HIDE_EMPTY_CONF.equals(evt.getKey())) {
refresh(false);
}
}
@Override
public void propertyChange(PropertyChangeEvent evt) {
refresh(false);
}
}
private static class ConfigurationChildren extends ChildFactory.Detachable<GradleDependency> implements PropertyChangeListener {
private final NbGradleProjectImpl project;
private final String configuration;
public ConfigurationChildren(NbGradleProjectImpl project, String configuration) {
this.project = project;
this.configuration = configuration;
}
@NbBundle.Messages({
"LBL_LocalDependenciesNode=Local Files",
})
@Override
protected Node[] createNodesForKey(GradleDependency key) {
GradleProject gp = project.getGradleProject();
ArrayList<Node> ret = new ArrayList<>(1);
switch (key.getType()) {
case MODULE: {
GradleDependency.ModuleDependency dep = (GradleDependency.ModuleDependency) key;
for (File artifact : dep.getArtifacts()) {
FileObject fo = FileUtil.toFileObject(artifact);
if (fo != null) {
try {
DataObject dataObject = DataObject.find(fo);
ret.add(new ModuleFilterNode(project, dep, artifact, dataObject.getNodeDelegate().cloneNode()));
} catch (DataObjectNotFoundException ex) {
// Should not happen here
}
}
}
break;
}
case PROJECT: {
GradleDependency.ProjectDependency dep = (GradleDependency.ProjectDependency) key;
//TODO: Shouldn't be done here on AWT
File projDir = dep.getPath();
FileObject fo = FileUtil.toFileObject(projDir);
if (fo != null) {
try {
Project prj = ProjectManager.getDefault().findProject(fo);
if (prj != null && prj.getLookup().lookup(NbGradleProjectImpl.class) != null) {
NbGradleProjectImpl proj = (NbGradleProjectImpl) prj;
assert prj.getLookup().lookup(LogicalViewProvider.class) != null;
Node original = proj.getLookup().lookup(LogicalViewProvider.class).createLogicalView();
ret.add(new SubProjectsNode.ProjectFilterNode(proj, original));
}
} catch (IllegalArgumentException | IOException ex) {
ex.printStackTrace();//TODO log ?
}
} else {
//TODO broken module reference.. show as such..
}
break;
}
case FILE: {
GradleDependency.FileCollectionDependency dep = (GradleDependency.FileCollectionDependency) key;
AbstractNode node = new AbstractNode(Children.create(new FileDependencyChildren(dep), true));
node.setDisplayName(Bundle.LBL_LocalDependenciesNode());
node.setIconBaseWithExtension(LIBRARIES_ICON);
ret.add(node);
break;
}
case UNRESOLVED: {
GradleDependency.UnresolvedDependency dep = (GradleDependency.UnresolvedDependency) key;
AbstractNode node = new AbstractNode(Children.LEAF);
node.setName(dep.getId());
node.setIconBaseWithExtension(UNRESOLVED_ICON);
ret.add(node);
break;
}
}
return ret.toArray(new Node[ret.size()]);
}
@Override
protected boolean createKeys(List<GradleDependency> list) {
GradleProject gp = project.getGradleProject();
ArrayList<GradleDependency> ret = new ArrayList<>();
GradleConfiguration conf = gp.getBaseProject().getConfigurations().get(configuration);
// We can get null here in some extreme cases, e.g. when the project is being deleted
if (conf != null) {
ret.addAll(conf.getUnresolved());
ret.addAll(conf.getProjects());
ret.addAll(conf.getModules());
GradleDependency.FileCollectionDependency fileDeps = conf.getFiles();
if ((fileDeps != null) && !fileDeps.getFiles().isEmpty()) {
ret.add(fileDeps);
}
Collections.sort(ret);
list.addAll(ret);
}
return true;
}
@Override
protected void removeNotify() {
NbGradleProject.removePropertyChangeListener(project, this);
}
@Override
protected void addNotify() {
NbGradleProject.addPropertyChangeListener(project, this);
}
@Override
public void propertyChange(PropertyChangeEvent evt) {
refresh(false);
}
}
private static class ModuleFilterNode extends FilterNode implements ChangeListener {
private final NbGradleProjectImpl project;
private final GradleDependency.ModuleDependency module;
private final File mainJar;
public ModuleFilterNode(NbGradleProjectImpl project, GradleDependency.ModuleDependency module, File mainJar, Node original) {
super(original);
this.project = project;
this.module = module;
this.mainJar = mainJar;
GradleArtifactStore.getDefault().addChangeListener(this);
}
@Override
public Action[] getActions(boolean context) {
GradleArtifactStore store = GradleArtifactStore.getDefault();
List<Action> actions = new ArrayList<>(3);
actions.add(new OpenJavadocAction(FileUtil.toFileObject(mainJar)));
if (store.getSources(mainJar) == null) {
Action download = ActionProviderImpl.createCustomGradleAction(project, "Download Sources",
ActionProviderImpl.COMMAND_DL_SOURCES, Lookups.singleton(RunUtils.simpleReplaceTokenProvider(REQUESTED_COMPONENT, module.getId())));
actions.add(download);
}
if (store.getJavadoc(mainJar) == null) {
Action download = ActionProviderImpl.createCustomGradleAction(project, "Download Javadoc",
ActionProviderImpl.COMMAND_DL_JAVADOC, Lookups.singleton(RunUtils.simpleReplaceTokenProvider(REQUESTED_COMPONENT, module.getId())));
actions.add(download);
}
return actions.toArray(new Action[actions.size()]);
}
@Override
public Image getOpenedIcon(int type) {
return getIcon(type);
}
@Override
public String getShortDescription() {
StringBuilder sb = new StringBuilder("<html>");
sb.append("Artifact Id: <b>").append(module.getId()).append("</b><br/>");
sb.append("File: ").append(mainJar.getAbsolutePath());
return sb.toString();
}
@Override
public String getDisplayName() {
return module.getVersion().isEmpty() ? module.getName() : module.getName() + ":" + module.getVersion();
}
@Override
public Image getIcon(int type) {
GradleArtifactStore store = GradleArtifactStore.getDefault();
Image ret = ImageUtilities.loadImage(ARTIFACT_ICON);
if (store.getJavadoc(mainJar) != null) {
Image javadoc = ImageUtilities.loadImage(JAVADOC_BADGE);
ret = ImageUtilities.mergeImages(ret, javadoc, 0, 8);
}
if (store.getSources(mainJar) != null) {
Image sources = ImageUtilities.loadImage(SOURCES_BADGE);
ret = ImageUtilities.mergeImages(ret, sources, 8, 8);
}
return ret;
}
@Override
public void stateChanged(ChangeEvent e) {
fireIconChange();
}
@Override
public void destroy() throws IOException {
GradleArtifactStore.getDefault().removeChangeListener(this);
super.destroy();
}
}
private static class FileDependencyChildren extends ChildFactory<FileObject> {
final GradleDependency.FileCollectionDependency dep;
public FileDependencyChildren(GradleDependency.FileCollectionDependency dep) {
this.dep = dep;
}
@Override
protected boolean createKeys(List<FileObject> keys) {
ArrayList<FileObject> ret = new ArrayList<>(dep.getFiles().size());
for (File file : dep.getFiles()) {
FileObject fo = FileUtil.toFileObject(file);
if (fo != null) {
ret.add(fo);
}
}
ret.sort(new Comparator<FileObject>() {
@Override
public int compare(FileObject o1, FileObject o2) {
return o1.getNameExt().compareTo(o2.getNameExt());
}
});
keys.addAll(ret);
return true;
}
@Override
protected Node createNodeForKey(FileObject key) {
try {
DataObject dataObject = DataObject.find(key);
return new LocalFileFilterNode(key, dataObject.getNodeDelegate().cloneNode());
} catch (DataObjectNotFoundException ex) {
// Should not happen here
}
return null;
}
}
private static class LocalFileFilterNode extends FilterNode {
private final FileObject mainJar;
public LocalFileFilterNode(FileObject mainJar, Node original) {
super(original);
this.mainJar = mainJar;
}
@Override
public Action[] getActions(boolean context) {
List<Action> actions = new ArrayList<>(3);
actions.add(new OpenJavadocAction(mainJar));
return actions.toArray(new Action[actions.size()]);
}
@Override
public Image getOpenedIcon(int type) {
return getIcon(type);
}
@Override
public String getShortDescription() {
return mainJar.getPath();
}
@Override
public Image getIcon(int type) {
GradleArtifactStore store = GradleArtifactStore.getDefault();
Image ret = super.getIcon(type);
if (store.getJavadoc(FileUtil.toFile(mainJar)) != null) {
Image javadoc = ImageUtilities.loadImage(JAVADOC_BADGE);
ret = ImageUtilities.mergeImages(ret, javadoc, 0, 8);
}
if (store.getSources(FileUtil.toFile(mainJar)) != null) {
Image sources = ImageUtilities.loadImage(SOURCES_BADGE);
ret = ImageUtilities.mergeImages(ret, sources, 8, 8);
}
return ret;
}
}
private static class OpenJavadocAction extends AbstractAction {
private final FileObject mainJar;
@NbBundle.Messages({
"LBL_OpenJavadocAction=Show Javadoc"
})
public OpenJavadocAction(FileObject mainJar) {
super(Bundle.LBL_OpenJavadocAction());
this.mainJar = mainJar;
}
@Override
public void actionPerformed(ActionEvent e) {
File javadoc = GradleArtifactStore.getDefault().getJavadoc(FileUtil.toFile(mainJar));
FileObject fo = FileUtil.toFileObject(javadoc);
if (fo != null) {
FileObject arch = FileUtil.getArchiveRoot(fo);
if (arch != null) {
FileObject index = arch.getFileObject("index.html"); //NOI18N
if (index != null) {
URL indexUrl = index.toURL();
HtmlBrowser.URLDisplayer.getDefault().showURL(indexUrl);
}
}
}
}
@Override
public boolean isEnabled() {
File javadoc = GradleArtifactStore.getDefault().getJavadoc(FileUtil.toFile(mainJar));
return javadoc != null;
}
}
}