package org.netbeans.gradle.project.java.nodes;
import java.awt.Image;
import java.io.File;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import org.jtrim.event.ListenerRef;
import org.jtrim.property.PropertySource;
import org.jtrim.utils.ExceptionHelper;
import org.netbeans.gradle.model.java.JavaSourceGroup;
import org.netbeans.gradle.model.java.JavaSourceGroupName;
import org.netbeans.gradle.model.java.JavaSourceSet;
import org.netbeans.gradle.project.NbIcons;
import org.netbeans.gradle.project.NbStrings;
import org.netbeans.gradle.project.api.nodes.SingleNodeFactory;
import org.netbeans.gradle.project.java.JavaExtension;
import org.netbeans.gradle.project.java.model.JavaSourceGroupID;
import org.netbeans.gradle.project.java.model.NamedFile;
import org.netbeans.gradle.project.java.model.NamedSourceRoot;
import org.netbeans.gradle.project.java.query.GradleProjectSources;
import org.netbeans.gradle.project.util.ExcludeIncludeRules;
import org.netbeans.gradle.project.util.ListenerRegistrations;
import org.netbeans.gradle.project.util.RefreshableChildren;
import org.netbeans.gradle.project.util.StringUtils;
import org.netbeans.gradle.project.view.BadgeAwareNode;
import org.netbeans.gradle.project.view.NodeUtils;
import org.openide.nodes.AbstractNode;
import org.openide.nodes.ChildFactory;
import org.openide.nodes.Children;
import org.openide.nodes.Node;
import org.openide.util.Lookup;
import org.openide.util.lookup.Lookups;
public final class JavaSourceSetNode extends AbstractNode {
private final String displayName;
private JavaSourceSetNode(JavaExtension javaExt, String sourceSetName) {
this(sourceSetName, new JavaSourceSetNodeChildFactory(javaExt, sourceSetName));
}
private JavaSourceSetNode(String sourceSetName, JavaSourceSetNodeChildFactory childFactory) {
this(sourceSetName, childFactory, createChildren(childFactory));
}
private JavaSourceSetNode(
String sourceSetName,
JavaSourceSetNodeChildFactory childFactory,
Children children) {
super(children, createLookup(childFactory, children));
this.displayName = StringUtils.capitalizeFirstCharacter(sourceSetName);
setName("java.sourceset." + sourceSetName);
}
private static Lookup createLookup(JavaSourceSetNodeChildFactory childFactory, Children children) {
return Lookups.fixed(
NodeUtils.askChildrenPackageViewsFinder(),
NodeUtils.defaultNodeRefresher(children, childFactory));
}
private static Children createChildren(JavaSourceSetNodeChildFactory childFactory) {
return Children.create(childFactory, true);
}
public static SingleNodeFactory createFactory(JavaExtension javaExt, String sourceSetName) {
return new JavaSourceSetNodeFactory(javaExt, sourceSetName);
}
@Override
public Image getIcon(int type) {
return NbIcons.getPackageIcon();
}
@Override
public Image getOpenedIcon(int type) {
return NbIcons.getOpenPackageIcon();
}
@Override
public String getDisplayName() {
return displayName;
}
private static class JavaSourceSetNodeChildFactory
extends
ChildFactory.Detachable<SingleNodeFactory>
implements
RefreshableChildren {
private final JavaExtension javaExt;
private final String sourceSetName;
private final ListenerRegistrations listenerRegs;
public JavaSourceSetNodeChildFactory(JavaExtension javaExt, String sourceSetName) {
ExceptionHelper.checkNotNullArgument(javaExt, "javaExt");
ExceptionHelper.checkNotNullArgument(sourceSetName, "sourceSetName");
this.javaExt = javaExt;
this.sourceSetName = sourceSetName;
this.listenerRegs = new ListenerRegistrations();
}
@Override
public void refreshChildren() {
refresh(false);
}
@Override
protected void addNotify() {
listenerRegs.unregisterAll();
listenerRegs.add(javaExt.addModelChangeListener(new Runnable() {
@Override
public void run() {
refresh(false);
}
}));
}
@Override
protected void removeNotify() {
listenerRegs.unregisterAll();
}
private JavaSourceSet tryGetSourceSet() {
return javaExt.getCurrentModel().getMainModule().tryGetSourceSetByName(sourceSetName);
}
private static int compareGroupNames(JavaSourceGroupName groupName1, JavaSourceGroupName groupName2) {
if (groupName1 == groupName2) {
return 0;
}
if (groupName1 == JavaSourceGroupName.OTHER) {
return 1;
}
if (groupName2 == JavaSourceGroupName.OTHER) {
return -1;
}
if (groupName1 == JavaSourceGroupName.RESOURCES) {
return 1;
}
if (groupName2 == JavaSourceGroupName.RESOURCES) {
return -1;
}
return StringUtils.STR_CMP.compare(groupName1.name(), groupName2.name());
}
private static JavaSourceGroup[] sortGroups(Collection<JavaSourceGroup> sourceGroups) {
JavaSourceGroup[] result = sourceGroups.toArray(new JavaSourceGroup[sourceGroups.size()]);
Arrays.sort(result, new Comparator<JavaSourceGroup>() {
@Override
public int compare(JavaSourceGroup o1, JavaSourceGroup o2) {
return compareGroupNames(o1.getGroupName(), o2.getGroupName());
}
});
return result;
}
private void addSourceRootNode(List<SingleNodeFactory> toPopulate, NamedSourceRoot root) {
SingleNodeFactory nodeFactory = GradleProjectSources.tryCreateSourceGroupNodeFactory(root);
if (nodeFactory != null) {
toPopulate.add(nodeFactory);
}
}
@Override
protected Node createNodeForKey(SingleNodeFactory key) {
return key.createNode();
}
@Override
protected boolean createKeys(List<SingleNodeFactory> toPopulate) {
JavaSourceSet sourceSet = tryGetSourceSet();
if (sourceSet != null) {
for (JavaSourceGroup sourceGroup: sortGroups(sourceSet.getSourceGroups())) {
String groupName = NamedSourceRoot.getSourceGroupDisplayName(sourceGroup);
Set<File> roots = sourceGroup.getSourceRoots();
ExcludeIncludeRules includeRules = ExcludeIncludeRules.create(sourceGroup);
JavaSourceGroupID groupID = new JavaSourceGroupID(sourceSetName, sourceGroup.getGroupName());
if (roots.size() == 1) {
addSourceRootNode(toPopulate, new NamedSourceRoot(
groupID,
groupName,
roots.iterator().next(),
includeRules));
}
else {
for (NamedFile namedRoot: NamedSourceRoot.nameSourceRoots(roots)) {
addSourceRootNode(toPopulate, new NamedSourceRoot(
groupID,
NbStrings.getMultiRootSourceGroups(groupName, namedRoot.getName()),
namedRoot.getPath(),
includeRules));
}
}
}
}
return true;
}
}
private static class SourceSetFilesProperty implements PropertySource<Set<File>> {
private final JavaExtension javaExt;
private final String sourceSetName;
public SourceSetFilesProperty(JavaExtension javaExt, String sourceSetName) {
this.javaExt = javaExt;
this.sourceSetName = sourceSetName;
}
@Override
public Set<File> getValue() {
JavaSourceSet sourceSet = javaExt.getCurrentModel().getMainModule().tryGetSourceSetByName(sourceSetName);
if (sourceSet == null) {
return Collections.emptySet();
}
Set<File> roots = new HashSet<>();
for (JavaSourceGroup group: sourceSet.getSourceGroups()) {
for (File root: group.getSourceRoots()) {
roots.add(root);
}
}
return roots;
}
@Override
public ListenerRef addChangeListener(Runnable listener) {
return javaExt.addModelChangeListener(listener);
}
}
private static class JavaSourceSetNodeFactory implements SingleNodeFactory {
private final JavaExtension javaExt;
private final String sourceSetName;
private final PropertySource<Set<File>> sourceSetFiles;
public JavaSourceSetNodeFactory(JavaExtension javaExt, String sourceSetName) {
ExceptionHelper.checkNotNullArgument(javaExt, "javaExt");
ExceptionHelper.checkNotNullArgument(sourceSetName, "sourceSetName");
this.javaExt = javaExt;
this.sourceSetName = sourceSetName;
this.sourceSetFiles = new SourceSetFilesProperty(javaExt, sourceSetName);
}
@Override
public Node createNode() {
return BadgeAwareNode.makeBadgeAware(new JavaSourceSetNode(javaExt, sourceSetName), sourceSetFiles);
}
@Override
public int hashCode() {
int hash = 5;
hash = 71 * hash + System.identityHashCode(javaExt);
hash = 71 * hash + sourceSetName.hashCode();
return hash;
}
@Override
public boolean equals(Object obj) {
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
final JavaSourceSetNodeFactory other = (JavaSourceSetNodeFactory)obj;
return this.javaExt == other.javaExt
&& Objects.equals(this.sourceSetName, other.sourceSetName);
}
}
}