org.netbeans.modules.java.j2seproject.J2SEActionProvider Maven / Gradle / Ivy
/*
* 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.java.j2seproject;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Properties;
import java.util.Queue;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.Future;
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 java.util.stream.Stream;
import javax.swing.JButton;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
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.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;
import org.netbeans.api.java.source.ClasspathInfo;
import org.netbeans.api.java.source.JavaSource;
import org.netbeans.api.project.FileOwnerQuery;
import org.netbeans.api.project.Project;
import org.netbeans.modules.java.api.common.SourceRoots;
import org.netbeans.modules.java.api.common.ant.UpdateHelper;
import org.netbeans.modules.java.api.common.project.ProjectProperties;
import org.netbeans.modules.java.api.common.project.BaseActionProvider;
import org.netbeans.modules.java.api.common.project.BaseActionProvider.Callback3;
import org.netbeans.modules.java.api.common.project.ProjectConfigurations;
import org.netbeans.modules.java.j2seproject.api.J2SEBuildPropertiesProvider;
import org.netbeans.modules.java.preprocessorbridge.spi.CompileOnSaveAction;
import org.netbeans.spi.project.ActionProvider;
import org.netbeans.spi.project.LookupProvider;
import org.netbeans.spi.project.ProjectServiceProvider;
import org.netbeans.spi.project.SingleMethod;
import org.netbeans.spi.project.support.ant.PropertyEvaluator;
import org.netbeans.spi.project.support.ant.PropertyUtils;
import org.openide.DialogDescriptor;
import org.openide.DialogDisplayer;
import org.openide.LifecycleManager;
import org.openide.NotifyDescriptor;
import org.openide.execution.ExecutorTask;
import org.openide.filesystems.FileAttributeEvent;
import org.openide.filesystems.FileChangeListener;
import org.openide.filesystems.FileEvent;
import org.openide.filesystems.FileLock;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileRenameEvent;
import org.openide.filesystems.FileUtil;
import org.openide.loaders.DataObject;
import org.openide.loaders.DataObjectNotFoundException;
import org.openide.modules.Places;
import org.openide.util.BaseUtilities;
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;
import org.openide.util.WeakListeners;
import org.openide.util.lookup.ServiceProvider;
import org.openide.xml.XMLUtil;
/** Action provider of the J2SE project. This is the place where to do
* strange things to J2SE actions. E.g. compile-single.
*/
public class J2SEActionProvider extends BaseActionProvider {
private static final Logger LOG = Logger.getLogger(J2SEActionProvider.class.getName());
// Commands available from J2SE project
private static final String[] supportedActions = {
COMMAND_BUILD,
COMMAND_CLEAN,
COMMAND_REBUILD,
COMMAND_COMPILE_SINGLE,
COMMAND_RUN,
COMMAND_RUN_SINGLE,
COMMAND_DEBUG,
COMMAND_DEBUG_SINGLE,
COMMAND_PROFILE,
COMMAND_PROFILE_SINGLE,
JavaProjectConstants.COMMAND_JAVADOC,
COMMAND_TEST,
COMMAND_TEST_SINGLE,
COMMAND_DEBUG_TEST_SINGLE,
COMMAND_PROFILE_TEST_SINGLE,
SingleMethod.COMMAND_RUN_SINGLE_METHOD,
SingleMethod.COMMAND_DEBUG_SINGLE_METHOD,
JavaProjectConstants.COMMAND_DEBUG_FIX,
COMMAND_DEBUG_STEP_INTO,
COMMAND_DELETE,
COMMAND_COPY,
COMMAND_MOVE,
COMMAND_RENAME,
};
private static final String[] platformSensitiveActions = {
COMMAND_BUILD,
COMMAND_REBUILD,
COMMAND_COMPILE_SINGLE,
COMMAND_RUN,
COMMAND_RUN_SINGLE,
COMMAND_DEBUG,
COMMAND_DEBUG_SINGLE,
COMMAND_PROFILE,
COMMAND_PROFILE_SINGLE,
JavaProjectConstants.COMMAND_JAVADOC,
COMMAND_TEST,
COMMAND_TEST_SINGLE,
COMMAND_DEBUG_TEST_SINGLE,
COMMAND_PROFILE_TEST_SINGLE,
SingleMethod.COMMAND_RUN_SINGLE_METHOD,
SingleMethod.COMMAND_DEBUG_SINGLE_METHOD,
JavaProjectConstants.COMMAND_DEBUG_FIX,
COMMAND_DEBUG_STEP_INTO,
};
private static final String[] actionsDisabledForQuickRun = {
COMMAND_COMPILE_SINGLE,
JavaProjectConstants.COMMAND_DEBUG_FIX,
};
//Post compile on save actions
private final CosAction cosAction;
/** Map from commands to ant targets */
private Map commands;
/**Set of commands which are affected by background scanning*/
private Set bkgScanSensitiveActions;
/**Set of commands which need java model up to date*/
private Set needJavaModelActions;
public J2SEActionProvider(J2SEProject project, UpdateHelper updateHelper) {
super(
project,
updateHelper,
project.evaluator(),
project.getSourceRoots(),
project.getTestSourceRoots(),
project.getAntProjectHelper(),
new CallbackImpl(project));
commands = new HashMap();
// treated specially: COMMAND_{,RE}BUILD
commands.put(COMMAND_CLEAN, new String[] {"clean"}); // NOI18N
commands.put(COMMAND_COMPILE_SINGLE, new String[] {"compile-single"}); // NOI18N
commands.put(COMMAND_RUN, new String[] {"run"}); // NOI18N
commands.put(COMMAND_RUN_SINGLE, new String[] {"run-single"}); // NOI18N
commands.put(COMMAND_DEBUG, new String[] {"debug"}); // NOI18N
commands.put(COMMAND_DEBUG_SINGLE, new String[] {"debug-single"}); // NOI18N
commands.put(COMMAND_PROFILE, new String[] {"profile"}); // NOI18N
commands.put(COMMAND_PROFILE_SINGLE, new String[] {"profile-single"}); // NOI18N
commands.put(JavaProjectConstants.COMMAND_JAVADOC, new String[] {"javadoc"}); // NOI18N
commands.put(COMMAND_TEST, new String[] {"test"}); // NOI18N
commands.put(COMMAND_TEST_SINGLE, new String[] {"test-single"}); // NOI18N
commands.put(COMMAND_DEBUG_TEST_SINGLE, new String[] {"debug-test"}); // NOI18N
commands.put(COMMAND_PROFILE_TEST_SINGLE, new String[]{"profile-test"}); // NOI18N
commands.put(JavaProjectConstants.COMMAND_DEBUG_FIX, new String[] {"debug-fix"}); // NOI18N
commands.put(COMMAND_DEBUG_STEP_INTO, new String[] {"debug-stepinto"}); // NOI18N
commands.put(SingleMethod.COMMAND_RUN_SINGLE_METHOD, new String[] {"test-single-method"}); // NOI18N
commands.put(SingleMethod.COMMAND_DEBUG_SINGLE_METHOD, new String[] {"debug-single-method"}); // NOI18N
this.bkgScanSensitiveActions = new HashSet(Arrays.asList(
COMMAND_RUN,
COMMAND_RUN_SINGLE,
COMMAND_DEBUG,
COMMAND_DEBUG_SINGLE,
COMMAND_DEBUG_STEP_INTO,
SingleMethod.COMMAND_RUN_SINGLE_METHOD,
SingleMethod.COMMAND_DEBUG_SINGLE_METHOD
));
this.needJavaModelActions = new HashSet(Arrays.asList(
JavaProjectConstants.COMMAND_DEBUG_FIX
));
this.cosAction = new CosAction(
this,
project.evaluator(),
project.getSourceRoots(),
project.getTestSourceRoots());
}
@Override
protected String[] getPlatformSensitiveActions() {
return platformSensitiveActions;
}
@Override
protected String[] getActionsDisabledForQuickRun() {
return actionsDisabledForQuickRun;
}
@Override
public Map getCommands() {
return commands;
}
@Override
protected Set getScanSensitiveActions() {
return bkgScanSensitiveActions;
}
@Override
protected Set getJavaModelActions() {
return needJavaModelActions;
}
@Override
protected boolean isCompileOnSaveEnabled() {
return isCompileOnSaveUpdate() && cosAction.getTarget() == null;
}
@Override
protected boolean isCompileOnSaveUpdate() {
return J2SEProjectUtil.isCompileOnSaveEnabled((J2SEProject)getProject());
}
@Override
public String[] getSupportedActions() {
return supportedActions;
}
@Override
public String[] getTargetNames(String command, Lookup context, Properties p, boolean doJavaChecks) throws IllegalArgumentException {
String names[] = super.getTargetNames(command, context, p, doJavaChecks);
ProjectConfigurations.Configuration c = context.lookup(ProjectConfigurations.Configuration.class);
if (c != null) {
String config;
if (!c.isDefault()) {
config = c.getName();
} else {
// Invalid but overrides any valid setting in config.properties.
config = "";
}
p.setProperty(ProjectProperties.PROP_PROJECT_CONFIGURATION_CONFIG, config);
}
return names;
}
@Override
public void invokeAction(String command, Lookup context) throws IllegalArgumentException {
final Runnable superCall = () -> super.invokeAction(command, context);
if (isCompileOnSaveUpdate() && cosAction.getTarget() != null && getScanSensitiveActions().contains(command)) {
LifecycleManager.getDefault ().saveAll (); //Need to do saveAll eagerly
final JButton stopButton = new JButton(NbBundle.getMessage(J2SEActionProvider.class, "TXT_StopBuild"));
stopButton.setMnemonic(NbBundle.getMessage(J2SEActionProvider.class, "MNE_StopBuild").charAt(0));
stopButton.getAccessibleContext().setAccessibleName(NbBundle.getMessage(J2SEActionProvider.class, "AN_StopBuild"));
stopButton.getAccessibleContext().setAccessibleDescription(NbBundle.getMessage(J2SEActionProvider.class, "AD_StopBuild"));
final AtomicBoolean showState = new AtomicBoolean(true);
final AtomicBoolean stopState = new AtomicBoolean();
try {
final JavaSource js = createSource();
js.runWhenScanFinished((cc) -> {
cosAction.newSyncTask(() -> SwingUtilities.invokeLater(() -> {
showState.set(false);
final boolean cancelled = stopState.get();
stopButton.doClick();
if (!cancelled) {
superCall.run();
}
}));
}, false);
final Timer timer = new Timer(1_000, (e) -> {
if (showState.get()) {
final NotifyDescriptor nd = new NotifyDescriptor(
NbBundle.getMessage(J2SEActionProvider.class, "TXT_CosUpdateActive"),
command,
NotifyDescriptor.YES_NO_OPTION,
NotifyDescriptor.INFORMATION_MESSAGE,
new Object[] {stopButton},
null);
final Object res = DialogDisplayer.getDefault().notify(nd);
if (res == DialogDescriptor.CLOSED_OPTION || res == stopButton) {
stopState.set(true);
}
}
});
timer.setRepeats(false);
timer.start();
} catch (IOException ioe) {
Exceptions.printStackTrace(ioe);
superCall.run(); //Last resort - try to run it
}
} else {
superCall.run();
}
}
@NonNull
private static JavaSource createSource() {
final ClasspathInfo cpInfo = ClasspathInfo.create(
ClassPath.EMPTY,
ClassPath.EMPTY,
ClassPath.EMPTY);
final JavaSource js = JavaSource.create(cpInfo);
return js;
}
@ProjectServiceProvider(
service=ActionProvider.class,
projectTypes={@LookupProvider.Registration.ProjectType(id="org-netbeans-modules-java-j2seproject",position=100)})
public static J2SEActionProvider create(@NonNull final Lookup lkp) {
Parameters.notNull("lkp", lkp); //NOI18N
final J2SEProject project = lkp.lookup(J2SEProject.class);
final J2SEActionProvider j2seActionProvider = new J2SEActionProvider(project, project.getUpdateHelper());
j2seActionProvider.startFSListener();
return j2seActionProvider;
}
private static final class CallbackImpl implements Callback3 {
private final J2SEProject prj;
CallbackImpl(@NonNull final J2SEProject project) {
Parameters.notNull("project", project); //NOI18N
this.prj = project;
}
@Override
@NonNull
public Map createAdditionalProperties(@NonNull String command, @NonNull Lookup context) {
final Map result = new HashMap<>();
for (J2SEBuildPropertiesProvider bpp : prj.getLookup().lookupAll(J2SEBuildPropertiesProvider.class)) {
final Map contrib = bpp.createAdditionalProperties(command, context);
assert contrib != null;
if (LOG.isLoggable(Level.FINE)) {
LOG.log(
Level.FINE,
"J2SEBuildPropertiesProvider: {0} added following build properties: {1}", //NOI18N
new Object[]{
bpp.getClass(),
contrib
});
}
result.putAll(contrib);
}
return Collections.unmodifiableMap(result);
}
@Override
public Set createConcealedProperties(String command, Lookup context) {
final Set result = new HashSet<>();
for (J2SEBuildPropertiesProvider bpp : prj.getLookup().lookupAll(J2SEBuildPropertiesProvider.class)) {
final Set contrib = bpp.createConcealedProperties(command, context);
assert contrib != null;
if (LOG.isLoggable(Level.FINE)) {
LOG.log(
Level.FINE,
"J2SEBuildPropertiesProvider: {0} added following concealed properties: {1}", //NOI18N
new Object[]{
bpp.getClass(),
contrib
});
}
result.addAll(contrib);
}
return Collections.unmodifiableSet(result);
}
@Override
public void antTargetInvocationStarted(@NonNull String command, @NonNull Lookup context) {
}
@Override
public void antTargetInvocationFinished(@NonNull String command, @NonNull Lookup context, int result) {
}
@Override
public void antTargetInvocationFailed(@NonNull String command, @NonNull Lookup context) {
}
@CheckForNull
@Override
public ClassPath getProjectSourcesClassPath(@NonNull String type) {
return prj.getClassPathProvider().getProjectSourcesClassPath(type);
}
@CheckForNull
@Override
public ClassPath findClassPath(@NonNull FileObject file, @NonNull String type) {
return prj.getClassPathProvider().findClassPath(file, type);
}
}
private static final class CosAction implements BuildArtifactMapper.ArtifactsUpdated,
CompileOnSaveAction, PropertyChangeListener, FileChangeListener {
private static Map> instances = new WeakHashMap<>();
private static final String COS_UPDATED = "$cos.update"; //NOI18N
private static final String COS_CUSTOM = "$cos.update.resources"; //NOI18N
private static final String PROP_TARGET = "cos.update.target.internal"; //NOI18N
private static final String PROP_SCRIPT = "cos.update.script.internal"; //NOI18N
private static final String PROP_SRCDIR = "cos.src.dir.internal"; //NOI18N
private static final String PROP_INCLUDES ="cos.includes.internal"; //NOI18N
private static final String SNIPPETS = "executor-snippets"; //NOI18N
private static final String SCRIPT = "cos-update.xml"; //NOI18N
private static final String TARGET = "cos-update-internal"; //NOI18N
private static final String SCRIPT_TEMPLATE = "/org/netbeans/modules/java/j2seproject/resources/cos-update-snippet.xml"; //NOI18N
private static final Object NONE = new Object();
private static final RequestProcessor RUNNER = new RequestProcessor(CosAction.class);
private final J2SEActionProvider owner;
private final PropertyEvaluator eval;
private final SourceRoots src;
private final SourceRoots tests;
private final BuildArtifactMapper mapper;
private final Map*@GuardedBy("this")*/URL,BuildArtifactMapper.ArtifactsUpdated> currentListeners;
private final ChangeSupport cs;
private final AtomicReference>> importantFilesCache;
//@GuardedBy("this")
private final Queue deferred = new ArrayDeque<>();
//@GuardedBy("this")
private byte deferredGuard; //0 - unset, 1 - pending, 2 - set
private volatile Object targetCache;
private volatile Object updatedFSProp;
private CosAction(
@NonNull final J2SEActionProvider owner,
@NonNull final PropertyEvaluator eval,
@NonNull final SourceRoots src,
@NonNull final SourceRoots tests) {
this.owner = owner;
this.eval = eval;
this.src = src;
this.tests = tests;
this.mapper = new BuildArtifactMapper();
this.currentListeners = new HashMap<>();
this.cs = new ChangeSupport(this);
this.importantFilesCache = new AtomicReference<>(Pair.of(null,null));
this.eval.addPropertyChangeListener(WeakListeners.propertyChange(this, this.eval));
this.src.addPropertyChangeListener(WeakListeners.propertyChange(this, this.src));
this.tests.addPropertyChangeListener(WeakListeners.propertyChange(this, this.tests));
updateRootsListeners();
instances.put(owner.getProject(), new WeakReference<>(this));
}
@Override
public boolean isEnabled() {
return getTarget() != null && isCustomUpdate();
}
@Override
public boolean isUpdateClasses() {
return isEnabled();
}
@Override
public boolean isUpdateResources() {
return isEnabled();
}
@Override
public Boolean performAction(Context ctx) throws IOException {
switch (ctx.getOperation()) {
case UPDATE:
return performUpdate(ctx);
case CLEAN:
return performClean(ctx);
case SYNC:
return performSync(ctx);
default:
throw new IllegalArgumentException(String.valueOf(ctx.getOperation()));
}
}
@Override
public void artifactsUpdated(@NonNull final Iterable artifacts) {
if (!isCustomUpdate()) {
final String target = getTarget();
if (target != null) {
final FileObject buildXml = owner.findBuildXml();
if (buildXml != null) {
if (checkImportantFiles(buildXml)) {
RUNNER.execute(() -> {
try {
final ExecutorTask task = runTargetInDedicatedTab(
NbBundle.getMessage(J2SEActionProvider.class, "LBL_CompileOnSaveUpdate"),
buildXml,
new String[] {target},
null,
null);
task.result();
} catch (IOException | IllegalArgumentException ex) {
LOG.log(
Level.WARNING,
"Cannot execute pos compile on save target: {0} in: {1} due to: {2}", //NOI18N
new Object[]{
target,
FileUtil.getFileDisplayName(buildXml),
ex.getMessage()
});
}
});
}
}
}
}
}
@Override
public void propertyChange(@NonNull final PropertyChangeEvent evt) {
final String name = evt.getPropertyName();
if (name == null) {
targetCache = null;
updatedFSProp = null;
cs.fireChange();
} else if (COS_UPDATED.equals(name)) {
targetCache = null;
cs.fireChange();
} else if (COS_CUSTOM.equals(name)) {
updatedFSProp = null;
cs.fireChange();
}else if (SourceRoots.PROP_ROOTS.equals(name)) {
updateRootsListeners();
}
}
@Override
public void addChangeListener(@NonNull final ChangeListener listener) {
cs.addChangeListener(listener);
}
@Override
public void removeChangeListener(@NonNull final ChangeListener listener) {
cs.removeChangeListener(listener);
}
@Override
public void fileDeleted(FileEvent fe) {
resetImportantFilesCache();
}
@Override
public void fileChanged(FileEvent fe) {
resetImportantFilesCache();
}
@Override
public void fileFolderCreated(FileEvent fe) {
resetImportantFilesCache();
}
@Override
public void fileDataCreated(FileEvent fe) {
resetImportantFilesCache();
}
@Override
public void fileRenamed(FileRenameEvent fe) {
resetImportantFilesCache();
}
@Override
public void fileAttributeChanged(FileAttributeEvent fe) {
//Not important
}
@NonNull
Future> newSyncTask(@NonNull final Runnable callback) {
return RUNNER.submit(() -> {
drainDeferred();
callback.run();
},
null);
}
private void updateRootsListeners() {
final Set newRoots = new HashSet<>();
Collections.addAll(newRoots, this.src.getRootURLs());
Collections.addAll(newRoots, this.tests.getRootURLs());
synchronized (this) {
final Set toRemove = new HashSet<>(currentListeners.keySet());
toRemove.removeAll(newRoots);
newRoots.removeAll(currentListeners.keySet());
for (URL u : toRemove) {
final BuildArtifactMapper.ArtifactsUpdated l = currentListeners.remove(u);
mapper.removeArtifactsUpdatedListener(u, l);
}
for (URL u : newRoots) {
final BuildArtifactMapper.ArtifactsUpdated l = new WeakArtifactUpdated(this, mapper, u);
currentListeners.put(u, l);
mapper.addArtifactsUpdatedListener(u, l);
}
}
}
@CheckForNull
private String getTarget() {
Object target = targetCache;
if (target == null) {
final String val = eval.getProperty(COS_UPDATED);
target = targetCache = val != null && !val.isEmpty() ?
val :
NONE;
}
if (target == NONE) {
return null;
}
return owner.isCompileOnSaveUpdate()?
(String) target :
null;
}
@CheckForNull
private String getUpdatedFileSetProperty() {
Object res = updatedFSProp;
if (res == null) {
final String val = eval.getProperty(COS_CUSTOM);
res = updatedFSProp = val != null && !val.isEmpty() ?
val :
NONE;
}
if (res == NONE) {
res = null;
}
return (String) res;
}
private boolean isCustomUpdate() {
return getUpdatedFileSetProperty() != null;
}
@CheckForNull
private Boolean performUpdate(@NonNull final Context ctx) {
final String target = getTarget();
if (target != null) {
final FileObject buildXml = owner.findBuildXml();
if (buildXml != null) {
if (checkImportantFiles(buildXml)) {
try {
final FileObject cosScript = getCosScript();
final Iterable extends File> updated = ctx.getUpdated();
final Iterable extends File> deleted = ctx.getDeleted();
final File root = ctx.isCopyResources() ?
BaseUtilities.toFile(ctx.getSourceRoot().toURI()) :
ctx.getCacheRoot();
final String includes = createIncludes(root, updated);
if (includes != null) {
final Properties props = new Properties();
props.setProperty(PROP_TARGET, target);
props.setProperty(PROP_SCRIPT, FileUtil.toFile(buildXml).getAbsolutePath());
props.setProperty(PROP_SRCDIR, root.getAbsolutePath());
props.setProperty(PROP_INCLUDES, includes);
props.setProperty(COS_CUSTOM, getUpdatedFileSetProperty());
final Runnable work = () -> {
try {
final ExecutorTask task = runTargetInDedicatedTab(
NbBundle.getMessage(J2SEActionProvider.class, "LBL_CompileOnSaveUpdate"),
cosScript,
new String[] {TARGET},
props,
null);
task.result();
} catch (IOException | IllegalArgumentException ex) {
LOG.log(
Level.WARNING,
"Cannot execute update targer: {0} in: {1} due to: {2}", //NOI18N
new Object[]{
target,
FileUtil.getFileDisplayName(buildXml),
ex.getMessage()
});
}
};
if (ctx.isAllFilesIndexing()) {
enqueueDeferred(work);
} else {
RUNNER.execute(work);
}
} else {
LOG.warning("BuildArtifactMapper artifacts do not provide attributes."); //NOI18N
}
} catch (IOException | URISyntaxException e) {
LOG.log(
Level.WARNING,
"Cannot execute update targer: {0} in: {1} due to: {2}", //NOI18N
new Object[]{
target,
FileUtil.getFileDisplayName(buildXml),
e.getMessage()
});
}
}
}
}
return true;
}
@CheckForNull
private Boolean performClean(@NonNull final Context ctx) {
//Not sure what to do
return null;
}
@CheckForNull
private Boolean performSync(@NonNull final Context ctx) {
//Not sure what to do
return null;
}
@NonNull
private FileObject getCosScript() throws IOException {
final FileObject snippets = FileUtil.createFolder(
Places.getCacheSubdirectory(SNIPPETS));
FileObject cosScript = snippets.getFileObject(SCRIPT);
if (cosScript == null) {
cosScript = FileUtil.createData(snippets, SCRIPT);
final FileLock lock = cosScript.lock();
try (InputStream in = getClass().getResourceAsStream(SCRIPT_TEMPLATE);
OutputStream out = cosScript.getOutputStream(lock)) {
FileUtil.copy(in, out);
} finally {
lock.releaseLock();
}
}
return cosScript;
}
private boolean checkImportantFiles(@NonNull final FileObject buildScript) {
final URI currentURI = buildScript.toURI();
final Pair> cacheLine = importantFilesCache.get();
final URI lastURI = cacheLine.first();
Collection importantFiles = cacheLine.second();
if (!currentURI.equals(lastURI) || importantFiles == null) {
Optional.ofNullable(lastURI)
.map(BaseUtilities::toFile)
.filter((f) -> !currentURI.equals(lastURI))
.ifPresent(this::safeRemoveFileChangeListener);
Optional.ofNullable(FileUtil.toFile(buildScript))
.filter((f) -> !currentURI.equals(lastURI))
.ifPresent(this::safeAddFileChangeListener);
importantFiles = new ArrayList<>();
try {
final File base = FileUtil.toFile(buildScript.getParent());
if (base != null) {
Optional.ofNullable(DataObject.find(buildScript).getLookup().lookup(AntProjectCookie.class))
.map(AntProjectCookie::getProjectElement)
.map(XMLUtil::findSubElements)
.map(Collection::stream)
.orElse(Stream.empty())
.filter((e) -> "import".equals(e.getNodeName())) //NOI18N
.map((e) -> e.getAttribute("file")) //NOI18N
.filter((p) -> !p.isEmpty())
.map((p) -> PropertyUtils.resolveFile(base, p))
.forEach(importantFiles::add);
}
} catch (DataObjectNotFoundException e) {
LOG.log(
Level.WARNING,
"No DataObject for: {0}, reason: {1}", //NOI18N
new Object[]{
FileUtil.getFileDisplayName(buildScript),
e.getMessage()
});
}
importantFilesCache.set(Pair.of(currentURI, importantFiles));
}
for (File importantFile : importantFiles) {
if (!importantFile.isFile()) {
return false;
}
}
return true;
}
private void resetImportantFilesCache() {
while (true) {
final Pair> expected = importantFilesCache.get();
final Pair> update = Pair.of(expected.first(), null);
if (importantFilesCache.compareAndSet(expected, update)) {
break;
}
}
}
private void safeAddFileChangeListener(@NonNull final File f) {
try {
FileUtil.addFileChangeListener(this, f);
} catch (IllegalArgumentException e) {
//not important
}
}
private void safeRemoveFileChangeListener(@NonNull final File f) {
try {
FileUtil.removeFileChangeListener(this, f);
} catch (IllegalArgumentException e) {
//not important
}
}
@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);
execenv.setUserAction(false);
return AntTargetExecutor.createTargetExecutor(execenv)
.execute(apc, targetNames);
}
@CheckForNull
private static String createIncludes(
@NonNull final File root,
@NonNull final Iterable extends File> artifacts) {
final StringBuilder include = new StringBuilder();
for (File f : artifacts) {
if (include.length() > 0) {
include.append(','); //NOI18N
}
include.append(relativize(f,root));
}
return include.length() == 0 ?
null :
include.toString();
}
private static String relativize(
@NonNull final File file,
@NonNull final File folder) {
final String folderPath = folder.getAbsolutePath();
int start = folderPath.length();
if (!folderPath.endsWith(File.separator)) {
start++;
}
return file.getAbsolutePath().substring(start);
}
private void enqueueDeferred(final Runnable work) {
boolean addGuard = false;
synchronized (this) {
this.deferred.offer(work);
if (deferredGuard == 0) {
addGuard = true;
deferredGuard = 1;
}
}
if (addGuard) {
final JavaSource js = createSource();
synchronized (this) {
if (deferredGuard == 1) {
deferredGuard = 2;
try {
js.runWhenScanFinished((cc) -> drainDeferred(), true);
} catch (IOException ioe) {
Exceptions.printStackTrace(ioe);
}
}
}
}
}
private void drainDeferred() {
Runnable[] todo;
synchronized (this) {
todo = deferred.toArray(new Runnable[deferred.size()]);
deferred.clear();
deferredGuard = 0;
}
for (Runnable r : todo) {
r.run();
}
}
@CheckForNull
static CosAction getInstance(@NonNull final Project p) {
final Reference r = instances.get(p);
return r != null ?
r.get() :
null;
}
private static final class WeakArtifactUpdated extends WeakReference
implements BuildArtifactMapper.ArtifactsUpdated, Runnable {
private final BuildArtifactMapper source;
private final URL url;
WeakArtifactUpdated(
@NonNull final BuildArtifactMapper.ArtifactsUpdated delegate,
@NonNull final BuildArtifactMapper source,
@NonNull final URL url) {
super(delegate);
Parameters.notNull("source", source); //NOI18N
Parameters.notNull("url", url); //NOI18N
this.source = source;
this.url = url;
}
@Override
public void artifactsUpdated(
@NonNull final Iterable artifacts) {
final BuildArtifactMapper.ArtifactsUpdated delegate = get();
if (delegate != null) {
delegate.artifactsUpdated(artifacts);
}
}
@Override
public void run() {
source.removeArtifactsUpdatedListener(url, this);
}
}
}
@ServiceProvider(service = CompileOnSaveAction.Provider.class, position = 10_000)
public static final class Provider implements CompileOnSaveAction.Provider {
@Override
public CompileOnSaveAction forRoot(URL root) {
try {
final Project p = FileOwnerQuery.getOwner(root.toURI());
if (p != null) {
ActionProvider prov = p.getLookup().lookup(ActionProvider.class);
if (prov != null) {
prov.getSupportedActions(); //Force initialization
}
final CosAction action = CosAction.getInstance(p);
return action;
}
} catch (URISyntaxException e) {
Exceptions.printStackTrace(e);
}
return null;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy