diff --git a/java.j2seproject/nbproject/project.xml b/java.j2seproject/nbproject/project.xml --- a/java.j2seproject/nbproject/project.xml +++ b/java.j2seproject/nbproject/project.xml @@ -55,7 +55,7 @@ 3 - 3.73 + 3.84 diff --git a/java.j2seproject/src/org/netbeans/modules/java/j2seproject/Bundle.properties b/java.j2seproject/src/org/netbeans/modules/java/j2seproject/Bundle.properties --- a/java.j2seproject/src/org/netbeans/modules/java/j2seproject/Bundle.properties +++ b/java.j2seproject/src/org/netbeans/modules/java/j2seproject/Bundle.properties @@ -94,3 +94,5 @@ J2SEProject.too_new=This version of the IDE is too old to read metadata in {0}. +#J2SEActionProvider +LBL_CompileOnSaveUpdate=Compile on Save Update \ No newline at end of file diff --git a/java.j2seproject/src/org/netbeans/modules/java/j2seproject/J2SEActionProvider.java b/java.j2seproject/src/org/netbeans/modules/java/j2seproject/J2SEActionProvider.java --- a/java.j2seproject/src/org/netbeans/modules/java/j2seproject/J2SEActionProvider.java +++ b/java.j2seproject/src/org/netbeans/modules/java/j2seproject/J2SEActionProvider.java @@ -67,14 +67,18 @@ import java.util.Set; import java.util.WeakHashMap; import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Predicate; import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Stream; import javax.swing.event.ChangeListener; import org.apache.tools.ant.module.api.AntProjectCookie; +import org.apache.tools.ant.module.api.AntTargetExecutor; import org.apache.tools.ant.module.api.support.ActionUtils; +import org.apache.tools.ant.module.api.support.AntScriptUtils; 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.classpath.ClassPath; import org.netbeans.api.java.project.JavaProjectConstants; import org.netbeans.api.java.source.BuildArtifactMapper; @@ -109,6 +113,7 @@ import org.openide.util.ChangeSupport; import org.openide.util.Exceptions; import org.openide.util.Lookup; +import org.openide.util.NbBundle; import org.openide.util.Pair; import org.openide.util.Parameters; import org.openide.util.RequestProcessor; @@ -466,11 +471,12 @@ if (buildXml != null) { if (checkImportantFiles(buildXml)) { try { - ActionUtils.runTarget( + runTargetInDedicatedTab( + NbBundle.getMessage(J2SEActionProvider.class, "LBL_CompileOnSaveUpdate"), buildXml, new String[] {target}, null, - null); + null); } catch (IOException ioe) { LOG.log( Level.WARNING, @@ -625,7 +631,8 @@ props.setProperty(COS_CUSTOM, getUpdatedFileSetProperty()); RUNNER.execute(()-> { try { - final ExecutorTask task = ActionUtils.runTarget( + final ExecutorTask task = runTargetInDedicatedTab( + NbBundle.getMessage(J2SEActionProvider.class, "LBL_CompileOnSaveUpdate"), cosScript, new String[] {TARGET}, props, @@ -764,6 +771,37 @@ } } + @NonNull + private static ExecutorTask runTargetInDedicatedTab( + @NullAllowed final String tabName, + @NonNull final FileObject buildXml, + @NullAllowed final String[] targetNames, + @NullAllowed final Properties properties, + @NullAllowed final Set concealedProperties) throws IOException, IllegalArgumentException { + Parameters.notNull("buildXml", buildXml); //NOI18N + if (targetNames != null && targetNames.length == 0) { + throw new IllegalArgumentException("No targets supplied"); // NOI18N + } + final AntProjectCookie apc = AntScriptUtils.antProjectCookieFor(buildXml); + final AntTargetExecutor.Env execenv = new AntTargetExecutor.Env(); + if (properties != null) { + Properties p = execenv.getProperties(); + p.putAll(properties); + execenv.setProperties(p); + } + if (concealedProperties != null) { + execenv.setConcealedProperties(concealedProperties); + } + execenv.setSaveAllDocuments(false); + execenv.setPreferredName(tabName); + final Predicate p = (s) -> tabName == null ? + true : + tabName.equals(s); + execenv.setTabReplaceStrategy(p, p); + return AntTargetExecutor.createTargetExecutor(execenv) + .execute(apc, targetNames); + } + @CheckForNull private static String createIncludes( @NonNull final File root, diff --git a/o.apache.tools.ant.module/apichanges.xml b/o.apache.tools.ant.module/apichanges.xml --- a/o.apache.tools.ant.module/apichanges.xml +++ b/o.apache.tools.ant.module/apichanges.xml @@ -110,6 +110,23 @@ + + + Added methods to override the default tab replacement policy into the AntTargetExecutor.Env + + + + + +

+ When the IDE is set to the automatic close tabs mode the tabs created by the previous + run of the AntTargetExecutor are closed by successive run. + Added a support to override this behavior. +

+
+ +
+ Added AntTargetExecutor.Env.setConcealedProperties diff --git a/o.apache.tools.ant.module/nbproject/project.properties b/o.apache.tools.ant.module/nbproject/project.properties --- a/o.apache.tools.ant.module/nbproject/project.properties +++ b/o.apache.tools.ant.module/nbproject/project.properties @@ -41,8 +41,8 @@ # made subject to such option by the copyright holder. javac.compilerargs=-Xlint:unchecked -javac.source=1.7 -spec.version.base=3.83.0 +javac.source=1.8 +spec.version.base=3.84.0 compile.ant.jar=${ant.core.lib} compile.ant-launcher.jar=${ant.core.lib}/../ant-launcher.jar src-bridge.cp.extra=build/classes:${compile.ant.jar}:${compile.ant-launcher.jar} diff --git a/o.apache.tools.ant.module/src/org/apache/tools/ant/module/api/AntTargetExecutor.java b/o.apache.tools.ant.module/src/org/apache/tools/ant/module/api/AntTargetExecutor.java --- a/o.apache.tools.ant.module/src/org/apache/tools/ant/module/api/AntTargetExecutor.java +++ b/o.apache.tools.ant.module/src/org/apache/tools/ant/module/api/AntTargetExecutor.java @@ -50,9 +50,11 @@ import java.util.HashSet; import java.util.Properties; import java.util.Set; +import java.util.function.Predicate; import org.apache.tools.ant.module.AntSettings; import org.apache.tools.ant.module.run.TargetExecutor; import org.netbeans.api.annotations.common.NonNull; +import org.netbeans.api.annotations.common.NullAllowed; import org.openide.execution.ExecutorTask; import org.openide.util.NbCollections; import org.openide.util.Parameters; @@ -108,6 +110,14 @@ te.setVerbosity(env.getVerbosity()); te.setProperties(NbCollections.checkedMapByCopy(env.getProperties(), String.class, String.class, true)); te.setConcealedProperties(env.getConcealedProperties()); + if (env.shouldSaveAllDocs != null) { + te.setSaveAllDocuments(env.shouldSaveAllDocs); + } + te.setDisplayName(env.preferredName); + if (env.canReplace != null) { + assert env.canBeReplaced != null; + te.setTabReplaceStrategy(env.canReplace,env.canBeReplaced); + } if (env.getLogger() == null) { return te.execute(); } else { @@ -125,6 +135,10 @@ private Properties properties; private OutputStream outputStream; private volatile Set concealedProperties; + private Boolean shouldSaveAllDocs; + private String preferredName; + private Predicate canReplace; + private Predicate canBeReplaced; /** Create instance of Env class describing environment for Ant target execution. */ @@ -207,6 +221,43 @@ public Set getConcealedProperties() { return concealedProperties; } + + /** + * Overrides the default save all behavior. + * @param shouldSave if true all modified documents are saved before running Ant. + * @since 3.84 + */ + public void setSaveAllDocuments(final boolean shouldSave) { + this.shouldSaveAllDocs = shouldSave; + } + + /** + * Sets the preferred name for output windows. + * @param name the preferred name in case of null the name is assigned automatically + * @since 3.84 + */ + public void setPreferredName(@NullAllowed final String name) { + this.preferredName = name; + } + + /** + * Sets the output tab replacement strategy. + * When the IDE is set to the automatic close tabs mode the tabs created by the previous + * run of the {@link AntTargetExecutor} are closed by successive run. This behavior can be overridden + * by this method. + * @param canReplace the {@link Predicate} used to decide if this execution + * can replace existing tab. The predicate parameter is a name of tab being replaced. + * @param canBeReplaced the {@link Predicate} used to decide if tab can be + * replaced by a new execution. The predicate parameter is a name of a tab being created. + * @since 3.84 + */ + public void setTabReplaceStrategy( + @NonNull final Predicate canReplace, + @NonNull final Predicate canBeReplaced) { + Parameters.notNull("canReplace", canReplace); //NOI18N + Parameters.notNull("canBeReplaced", canBeReplaced); //NOI18N + this.canReplace = canReplace; + this.canBeReplaced = canBeReplaced; + } } - } diff --git a/o.apache.tools.ant.module/src/org/apache/tools/ant/module/run/TargetExecutor.java b/o.apache.tools.ant.module/src/org/apache/tools/ant/module/run/TargetExecutor.java --- a/o.apache.tools.ant.module/src/org/apache/tools/ant/module/run/TargetExecutor.java +++ b/o.apache.tools.ant.module/src/org/apache/tools/ant/module/run/TargetExecutor.java @@ -61,6 +61,7 @@ import java.util.WeakHashMap; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Predicate; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.AbstractAction; @@ -91,6 +92,7 @@ import org.openide.util.Cancellable; import org.openide.util.Mutex; import org.openide.util.NbBundle; +import org.openide.util.Pair; import org.openide.util.Parameters; import org.openide.util.RequestProcessor; import org.openide.util.io.ReaderInputStream; @@ -112,7 +114,7 @@ * Map from tab to tab display name. * @see "#43001" */ - private static final Map freeTabs = new WeakHashMap(); + private static final Map>> freeTabs = new WeakHashMap<>(); /** * Display names of currently active processes. @@ -129,6 +131,9 @@ /** used for the tab etc. */ private String displayName; private String suggestedDisplayName; + private Boolean shouldSaveAllDocs; + private Predicate canBeReplaced = (s) -> true; + private Predicate canReplace = (s) -> true; private volatile Set concealedProperties; /** targets may be null to indicate default target */ @@ -151,8 +156,21 @@ this.concealedProperties = Collections.unmodifiableSet(new HashSet(concealedProperties)); } - void setDisplayName(String n) { - suggestedDisplayName = n; + public void setSaveAllDocuments(boolean shouldSaveAllDocs) { + this.shouldSaveAllDocs = shouldSaveAllDocs; + } + + public void setDisplayName(String n) { + this.suggestedDisplayName = n; + } + + public void setTabReplaceStrategy( + @NonNull final Predicate canReplace, + @NonNull final Predicate canBeReplaced) { + Parameters.notNull("canReplace", canReplace); //NOI18N + Parameters.notNull("canBeReplaced", canBeReplaced); //NOI18N + this.canReplace = canReplace; + this.canBeReplaced = canBeReplaced; } private static String getProcessDisplayName(AntProjectCookie pcookie, List targetNames) { @@ -364,23 +382,27 @@ // OutputWindow if (AntSettings.getAutoCloseTabs()) { // #47753 synchronized (freeTabs) { - for (Map.Entry entry : freeTabs.entrySet()) { + final Set retained = new HashSet<>(); + for (Map.Entry>> entry : freeTabs.entrySet()) { InputOutput free = entry.getKey(); - String freeName = entry.getValue(); + String freeName = entry.getValue().first(); + Predicate freePredicate = entry.getValue().second(); if (io == null && freeName.equals(displayName)) { // Reuse it. io = free; io.getOut().reset(); // Apparently useless and just prints warning: io.getErr().reset(); // useless: io.flushReader(); - } else { + } else if (canReplace.test(freeName) && freePredicate.test(displayName)) { // Discard it. free.closeInputOutput(); stopActions.remove(free); rerunActions.remove(free); + } else { + retained.add(free); } } - freeTabs.clear(); + freeTabs.keySet().retainAll(retained); } } if (io == null) { @@ -466,7 +488,7 @@ } } - if (AntSettings.getSaveAll()) { + if (shouldSaveAllDocs != null ? shouldSaveAllDocs : AntSettings.getSaveAll()) { LifecycleManager.getDefault ().saveAll (); } @@ -557,7 +579,7 @@ } finally { if (io != null) { synchronized (freeTabs) { - freeTabs.put(io, displayName); + freeTabs.put(io, Pair.of(displayName,canBeReplaced)); } } if (thisExec[0] != null) {