package org.netbeans.gradle.project.view; import java.awt.Image; import java.awt.event.ActionEvent; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Objects; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.JOptionPane; import javax.swing.SwingUtilities; import org.jtrim.cancel.Cancellation; import org.jtrim.cancel.CancellationToken; import org.jtrim.concurrent.CancelableTask; import org.jtrim.utils.ExceptionHelper; import org.netbeans.gradle.project.NbGradleProject; import org.netbeans.gradle.project.NbGradleProjectFactory; 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.model.NbGradleModel; import org.netbeans.gradle.project.script.CommonScripts; 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.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.Lookup; import org.openide.util.lookup.Lookups; public final class BuildScriptsNode extends AbstractNode { private final NbGradleProject project; private final BuildScriptChildFactory childFactory; public BuildScriptsNode(NbGradleProject project) { this(project, new BuildScriptChildFactory(project)); } private BuildScriptsNode(NbGradleProject project, BuildScriptChildFactory childFactory) { this(project, childFactory, Children.create(childFactory, true)); } private BuildScriptsNode( NbGradleProject project, BuildScriptChildFactory childFactory, Children children) { super(children, createLookup(childFactory, children)); this.project = project; this.childFactory = childFactory; setName(getClass().getSimpleName()); } private static Lookup createLookup(BuildScriptChildFactory childFactory, Children children) { return Lookups.fixed( NodeUtils.askChildrenNodeFinder(), NodeUtils.defaultNodeRefresher(children, childFactory)); } public static SingleNodeFactory getFactory(NbGradleProject project) { return new NodeFactoryImpl(project); } private static void addOpenFileAction(Path file, List<Action> actions) { if (file != null) { actions.add(new OpenAlwaysFileAction(file)); } } @Override public Action[] getActions(boolean context) { List<Action> actions = new ArrayList<>(); NbGradleModel currentModel = project.currentModel().getValue(); addOpenFileAction(currentModel.getSettingsFile(), actions); addOpenFileAction(currentModel.getBuildFile().toPath(), actions); actions.add(null); actions.add(new OpenOrCreateBuildSrc( NbStrings.getOpenBuildSrcCaption(), project, childFactory)); if (!currentModel.isRootProject()) { actions.add(OpenProjectsAction.createFromProjectDirs( NbStrings.getOpenRootProjectsCaption(), Collections.singleton(currentModel.getSettingsDir().toFile()))); } actions.add(null); actions.add(NodeUtils.getRefreshNodeAction(this)); return actions.toArray(new Action[actions.size()]); } @Override public Image getIcon(int type) { return NbIcons.getFolderIcon(); } @Override public Image getOpenedIcon(int type) { return NbIcons.getOpenFolderIcon(); } @Override public String getDisplayName() { return NbStrings.getBuildScriptsNodeCaption(); } private static Path getBuildSrcDir(NbGradleProject project) { return getBuildSrcDir(project.currentModel().getValue()); } private static Path getBuildSrcDir(NbGradleModel currentModel) { return currentModel.getSettingsDir().resolve(CommonScripts.BUILD_SRC_NAME); } private static class BuildScriptChildFactory extends ChildFactory.Detachable<SingleNodeFactory> implements RefreshableChildren { private final NbGradleProject project; private final ListenerRegistrations listenerRefs; private volatile boolean createdOnce; public BuildScriptChildFactory(NbGradleProject project) { ExceptionHelper.checkNotNullArgument(project, "project"); this.project = project; this.listenerRefs = new ListenerRegistrations(); this.createdOnce = false; } @Override public void refreshChildren() { if (createdOnce) { refresh(false); } } @Override protected void addNotify() { listenerRefs.add(project.currentModel().addChangeListener(new Runnable() { @Override public void run() { refreshChildren(); } })); } @Override protected void removeNotify() { listenerRefs.unregisterAll(); } private static void addProjectScriptsNode( String caption, File projectDir, List<SingleNodeFactory> toPopulate) { NbGradleProject project = NbGradleProjectFactory.tryLoadSafeGradleProject(projectDir); if (project != null) { toPopulate.add(ProjectScriptFilesNode.getFactory(caption, project)); } } private void readKeys(List<SingleNodeFactory> toPopulate) { NbGradleModel currentModel = project.currentModel().getValue(); if (!currentModel.isBuildSrc()) { Path buildSrc = getBuildSrcDir(currentModel); if (Files.isDirectory(buildSrc)) { toPopulate.add(new BuildSrcNodeFactory(buildSrc)); } } File projectDir = currentModel.getProjectDir(); addProjectScriptsNode(NbStrings.getProjectScriptNodeCaption(), projectDir, toPopulate); File rootProjectDir = currentModel.getSettingsDir().toFile(); if (!Objects.equals(projectDir, rootProjectDir)) { addProjectScriptsNode(NbStrings.getRootProjectScriptNodeCaption(), rootProjectDir, toPopulate); } toPopulate.add(GradleHomeNode.getFactory(project.getScriptFileProvider())); } @Override protected boolean createKeys(List<SingleNodeFactory> toPopulate) { createdOnce = true; readKeys(toPopulate); return true; } @Override protected Node createNodeForKey(SingleNodeFactory key) { return key.createNode(); } } @SuppressWarnings("serial") private static class OpenOrCreateBuildSrc extends AbstractAction { private final NbGradleProject project; private final BuildScriptChildFactory childFactory; public OpenOrCreateBuildSrc( String name, NbGradleProject project, BuildScriptChildFactory childFactory) { super(name); this.project = project; this.childFactory = childFactory; } private void openProjectNow(Path buildSrcDir) { OpenProjectsAction.openProject(buildSrcDir); } private static Path resolveAndCreate(Path parent, String name) throws IOException { Path result = parent.resolve(name); Files.createDirectories(result); return result; } private void createDirs(Path buildSrcDir) throws IOException { Files.createDirectories(buildSrcDir); Path srcDir = resolveAndCreate(buildSrcDir, "src"); Path srcMainDir = resolveAndCreate(srcDir, "main"); resolveAndCreate(srcMainDir, "groovy"); resolveAndCreate(srcMainDir, "resources"); Path srcTestDir = resolveAndCreate(srcDir, "test"); resolveAndCreate(srcTestDir, "groovy"); resolveAndCreate(srcTestDir, "resources"); } private void createBuildSrc(Path buildSrcDir) throws IOException { createDirs(buildSrcDir); Path buildGradle = buildSrcDir.resolve(CommonScripts.BUILD_BASE_NAME + CommonScripts.DEFAULT_SCRIPT_EXTENSION); List<String> buildGradleContent = Arrays.asList( "apply plugin: 'groovy'", "", "dependencies {", " compile gradleApi()", " compile localGroovy()", "}"); NbFileUtils.writeLinesToFile(buildGradle, buildGradleContent, StringUtils.UTF8, project); childFactory.refreshChildren(); } private void confirmAndCreateProject(final Path buildSrcDir) { String message = NbStrings.getConfirmCreateBuildSrcMessage(); String title = NbStrings.getConfirmCreateBuildSrcTitle(); boolean confirmed = JOptionPane.showConfirmDialog(null, message, title, JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE) == JOptionPane.YES_OPTION; if (confirmed) { NbTaskExecutors.DEFAULT_EXECUTOR.execute(Cancellation.UNCANCELABLE_TOKEN, new CancelableTask() { @Override public void execute(CancellationToken cancelToken) throws Exception { createBuildSrc(buildSrcDir); openProjectNow(buildSrcDir); } }, null); } } private void doActionNow(final Path buildSrcDir) { if (Files.isDirectory(buildSrcDir)) { openProjectNow(buildSrcDir); } else { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { confirmAndCreateProject(buildSrcDir); } }); } } @Override public void actionPerformed(ActionEvent e) { NbTaskExecutors.DEFAULT_EXECUTOR.execute(Cancellation.UNCANCELABLE_TOKEN, new CancelableTask() { @Override public void execute(CancellationToken cancelToken) throws Exception { doActionNow(getBuildSrcDir(project)); } }, null); } } private static class BuildSrcNodeFactory implements SingleNodeFactory { private final Path buildSrcDir; public BuildSrcNodeFactory(Path buildSrcDir) { this.buildSrcDir = buildSrcDir; } @Override public Node createNode() { return new BuildSrcNode(buildSrcDir); } @Override public int hashCode() { int hash = 7; hash = 73 * hash + Objects.hashCode(this.buildSrcDir); return hash; } @Override public boolean equals(Object obj) { if (obj == null) return false; if (getClass() != obj.getClass()) return false; final BuildSrcNodeFactory other = (BuildSrcNodeFactory)obj; return Objects.equals(this.buildSrcDir, other.buildSrcDir); } } private static class BuildSrcNode extends FilterNode { private final Path buildSrcDir; public BuildSrcNode(Path buildSrcDir) { super(Node.EMPTY.cloneNode(), null, Lookups.fixed(buildSrcDir)); this.buildSrcDir = buildSrcDir; } @Override public Action[] getActions(boolean context) { return new Action[]{ getPreferredAction() }; } @Override public Action getPreferredAction() { return OpenProjectsAction.createFromProjectDirs( NbStrings.getOpenBuildSrcCaption(), Collections.singleton(buildSrcDir.toFile())); } @Override public String getName() { Path parentFile = buildSrcDir.getParent(); return "BuildSrc_" + (parentFile != null ? NbFileUtils.getFileNameStr(parentFile) : "unknown"); } @Override public String getDisplayName() { return NbStrings.getBuildSrcNodeCaption(); } @Override public Image getIcon(int type) { return NbIcons.getGradleIcon(); } @Override public Image getOpenedIcon(int type) { return getIcon(type); } @Override public boolean canRename() { return false; } } private static class NodeFactoryImpl implements SingleNodeFactory { private final NbGradleProject project; private final File projectDir; public NodeFactoryImpl(NbGradleProject project) { ExceptionHelper.checkNotNullArgument(project, "project"); this.project = project; this.projectDir = project.getProjectDirectoryAsFile(); } @Override public Node createNode() { return new BuildScriptsNode(project); } @Override public int hashCode() { int hash = 5; hash = 23 * hash + Objects.hashCode(this.projectDir); return hash; } @Override public boolean equals(Object obj) { if (obj == null) return false; if (getClass() != obj.getClass()) return false; final NodeFactoryImpl other = (NodeFactoryImpl)obj; return Objects.equals(this.projectDir, other.projectDir); } } }