package org.netbeans.gradle.project.view; import java.awt.Image; import java.awt.event.ActionEvent; import java.util.Arrays; import java.util.Comparator; import java.util.List; import java.util.Objects; import java.util.Set; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.SwingUtilities; import org.jtrim.cancel.Cancellation; import org.jtrim.cancel.CancellationToken; import org.jtrim.concurrent.CancelableTask; import org.jtrim.concurrent.TaskExecutor; import org.jtrim.utils.ExceptionHelper; 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.filesupport.GradleTemplateConsts; import org.netbeans.gradle.project.filesupport.GradleTemplateRegistration; import org.netbeans.gradle.project.output.OpenEditorOutputListener; import org.netbeans.gradle.project.script.ScriptFileProvider; import org.netbeans.gradle.project.util.ListenerRegistrations; import org.netbeans.gradle.project.util.NbFileUtils; import org.netbeans.gradle.project.util.NbTaskExecutors; import org.netbeans.gradle.project.util.RefreshableChildren; import org.netbeans.gradle.project.util.StringUtils; import org.openide.filesystems.FileObject; import org.openide.loaders.DataFolder; import org.openide.loaders.DataObject; import org.openide.loaders.TemplateWizard; 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 GradleFolderNode extends AbstractNode { private static final TaskExecutor GRADLE_FOLDER_CREATOR = NbTaskExecutors.newExecutor("Gradle folder creator", 1); private final String caption; private final FileObject dir; public GradleFolderNode(String caption, FileObject dir, ScriptFileProvider scriptProvider) { this(caption, dir, new ChildFactoryImpl(dir, scriptProvider)); } private GradleFolderNode( String caption, FileObject dir, ChildFactoryImpl childFactory) { this(caption, dir, childFactory, Children.create(childFactory, true)); } private GradleFolderNode( String caption, FileObject dir, ChildFactoryImpl childFactory, Children children) { super(children, createLookup(childFactory, children)); ExceptionHelper.checkNotNullArgument(caption, "caption"); this.caption = caption; this.dir = dir; setName(dir.toString()); } public static SingleNodeFactory getFactory(String caption, FileObject dir, ScriptFileProvider scriptProvider) { return new FactoryImpl(caption, dir, scriptProvider); } private static Lookup createLookup(ChildFactoryImpl childFactory, Children children) { return Lookups.fixed( NodeUtils.childrenFileFinder(), NodeUtils.defaultNodeRefresher(children, childFactory)); } @Override public Image getIcon(int type) { return NbIcons.getFolderIcon(); } @Override public Image getOpenedIcon(int type) { return NbIcons.getOpenFolderIcon(); } @Override public String getDisplayName() { return caption; } @Override public Action[] getActions(boolean context) { return new Action[] { new CreateGradleFileTask(dir), null, NodeUtils.getRefreshNodeAction(this) }; } @SuppressWarnings("serial") private static class CreateGradleFileTask extends AbstractAction { private final FileObject dir; public CreateGradleFileTask(FileObject dir) { super(NbStrings.getAddNewInitScriptCaption()); this.dir = dir; } @Override public void actionPerformed(ActionEvent e) { final TemplateWizard template = new TemplateWizard(); DataFolder targetFolder = DataFolder.findFolder(dir); template.setTargetFolder(targetFolder); template.setTemplatesFolder(DataFolder.findFolder(GradleTemplateConsts.getTemplateFolder())); GRADLE_FOLDER_CREATOR.execute(Cancellation.UNCANCELABLE_TOKEN, new CancelableTask() { @Override public void execute(CancellationToken cancelToken) throws Exception { Set<DataObject> dataObjs = template.instantiate(DataFolder.find(GradleTemplateRegistration.getTemplateFileObj())); if (dataObjs == null) { return; } for (DataObject dataObj: dataObjs) { final FileObject fileObj = dataObj.getPrimaryFile(); if (fileObj != null) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { OpenEditorOutputListener.tryOpenFile(fileObj, -1); } }); } } } }, null); } } private static class ChildFactoryImpl extends ChildFactory.Detachable<SingleNodeFactory> implements RefreshableChildren { private final FileObject dir; private final ScriptFileProvider scriptProvider; private final ListenerRegistrations listenerRegistrations; private volatile boolean createdOnce; public ChildFactoryImpl(FileObject dir, ScriptFileProvider scriptProvider) { ExceptionHelper.checkNotNullArgument(dir, "dir"); ExceptionHelper.checkNotNullArgument(scriptProvider, "scriptProvider"); this.dir = dir; this.scriptProvider = scriptProvider; this.listenerRegistrations = new ListenerRegistrations(); this.createdOnce = false; } @Override public void refreshChildren() { if (createdOnce) { refresh(false); } } @Override protected void addNotify() { listenerRegistrations.add(NbFileUtils.addDirectoryContentListener(dir, new Runnable() { @Override public void run() { refreshChildren(); } })); } @Override protected void removeNotify() { listenerRegistrations.unregisterAll(); } private static SingleNodeFactory tryGetGradleNode(FileObject child) { return NodeUtils.tryGetFileNode(child, child.getNameExt(), NbIcons.getGradleIcon()); } private void readKeys(List<SingleNodeFactory> toPopulate) { FileObject[] children = dir.getChildren(); Arrays.sort(children, new Comparator<FileObject>() { @Override public int compare(FileObject o1, FileObject o2) { return StringUtils.STR_CMP.compare(o1.getNameExt(), o2.getNameExt()); } }); for (FileObject child: children) { if (scriptProvider.isScriptFileName(child.getNameExt())) { SingleNodeFactory node = tryGetGradleNode(child); if (node != null) { toPopulate.add(node); } } } } @Override protected boolean createKeys(List<SingleNodeFactory> toPopulate) { createdOnce = true; readKeys(toPopulate); return true; } @Override protected Node createNodeForKey(SingleNodeFactory key) { return key.createNode(); } } private static final class FactoryImpl implements SingleNodeFactory { private final String caption; private final FileObject dir; private final ScriptFileProvider scriptProvider; public FactoryImpl(String caption, FileObject dir, ScriptFileProvider scriptProvider) { ExceptionHelper.checkNotNullArgument(caption, "caption"); ExceptionHelper.checkNotNullArgument(dir, "dir"); ExceptionHelper.checkNotNullArgument(scriptProvider, "scriptProvider"); this.caption = caption; this.dir = dir; this.scriptProvider = scriptProvider; } @Override public Node createNode() { return new GradleFolderNode(caption, dir, scriptProvider); } @Override public int hashCode() { int hash = 7; hash = 83 * hash + Objects.hashCode(this.caption); hash = 83 * hash + Objects.hashCode(this.dir); return hash; } @Override public boolean equals(Object obj) { if (obj == null) return false; if (getClass() != obj.getClass()) return false; final FactoryImpl other = (FactoryImpl)obj; return Objects.equals(this.caption, other.caption) && Objects.equals(this.dir, other.dir); } } }