
org.netbeans.modules.javascript.gulp.exec.GulpExecutable 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.javascript.gulp.exec;
import java.awt.EventQueue;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.netbeans.api.annotations.common.CheckForNull;
import org.netbeans.api.annotations.common.NullAllowed;
import org.netbeans.api.extexecution.ExecutionDescriptor;
import org.netbeans.api.extexecution.base.input.InputProcessor;
import org.netbeans.api.extexecution.base.input.InputProcessors;
import org.netbeans.api.extexecution.base.input.LineProcessor;
import org.netbeans.api.options.OptionsDisplayer;
import org.netbeans.api.project.Project;
import org.netbeans.modules.javascript.gulp.options.GulpOptions;
import org.netbeans.modules.javascript.gulp.options.GulpOptionsValidator;
import org.netbeans.modules.javascript.gulp.ui.options.GulpOptionsPanelController;
import org.netbeans.modules.javascript.gulp.util.GulpUtils;
import org.netbeans.modules.web.clientproject.api.util.StringUtilities;
import org.netbeans.modules.web.common.api.ValidationResult;
import org.netbeans.modules.web.common.ui.api.ExternalExecutable;
import org.openide.awt.StatusDisplayer;
import org.openide.filesystems.FileUtil;
import org.openide.util.NbBundle;
import org.openide.util.RequestProcessor;
import org.openide.util.Utilities;
import org.openide.windows.InputOutput;
public class GulpExecutable {
static final Logger LOGGER = Logger.getLogger(GulpExecutable.class.getName());
public static final String GULP_NAME;
private static final String NO_COLOR_PARAM = "--no-color"; // NOI18N
private static final String COLOR_PARAM = "--color"; // NOI18N
private static final String TASKS_PARAM = "--tasks-simple"; // NOI18N
private static final String SILENT_PARAM = "--silent"; // NOI18N
protected final Project project;
protected final String gulpPath;
@NullAllowed
private final File workDir;
static {
if (Utilities.isWindows()) {
GULP_NAME = "gulp.cmd"; // NOI18N
} else {
GULP_NAME = "gulp"; // NOI18N
}
}
GulpExecutable(String gulpPath, Project project, @NullAllowed File workDir) {
assert gulpPath != null;
assert project != null;
this.gulpPath = gulpPath;
this.project = project;
this.workDir = workDir;
}
@CheckForNull
public static GulpExecutable getDefault(Project project, boolean showOptions) {
return createExecutable(project, null, showOptions);
}
@CheckForNull
public static GulpExecutable getDefault(Project project, File workDir, boolean showOptions) {
assert workDir != null;
assert workDir.exists() : workDir;
return createExecutable(project, workDir, showOptions);
}
@CheckForNull
private static GulpExecutable createExecutable(Project project, @NullAllowed File workDir, boolean showOptions) {
assert project != null;
ValidationResult result = new GulpOptionsValidator()
.validateGulp()
.getResult();
if (validateResult(result) != null) {
if (showOptions) {
OptionsDisplayer.getDefault().open(GulpOptionsPanelController.OPTIONS_PATH);
}
return null;
}
String gulp = GulpOptions.getInstance().getGulp();
if (Utilities.isMac()) {
return new MacGulpExecutable(gulp, project, workDir);
}
return new GulpExecutable(gulp, project, workDir);
}
String getCommand() {
return gulpPath;
}
@NbBundle.Messages({
"# {0} - project name",
"GulpExecutable.run=Gulp ({0})",
})
public Future run(String... args) {
assert !EventQueue.isDispatchThread();
assert project != null;
String projectName = GulpUtils.getProjectDisplayName(project);
Future task = getExecutable(Bundle.GulpExecutable_run(projectName))
.additionalParameters(getRunParams(args))
.run(getDescriptor());
assert task != null : gulpPath;
return task;
}
public Future> listTasks() {
final GulpTasksLineProcessor gulpTasksLineProcessor = new GulpTasksLineProcessor();
Future task = getExecutable("list gulp tasks") // NOI18N
.noInfo(true)
.additionalParameters(getParams(getListTasksParams()))
.redirectErrorStream(false)
.run(getSilentDescriptor(), new ExecutionDescriptor.InputProcessorFactory2() {
@Override
public InputProcessor newInputProcessor(InputProcessor defaultProcessor) {
return InputProcessors.bridge(gulpTasksLineProcessor);
}
});
assert task != null : gulpPath;
return new TaskList(this, task, gulpTasksLineProcessor);
}
private ExternalExecutable getExecutable(String title) {
assert title != null;
return new ExternalExecutable(getCommand())
.workDir(getWorkDir())
.displayName(title)
.optionsPath(GulpOptionsPanelController.OPTIONS_PATH)
.noOutput(false);
}
private ExecutionDescriptor getDescriptor() {
assert project != null;
return ExternalExecutable.DEFAULT_EXECUTION_DESCRIPTOR
.showSuspended(true)
.optionsPath(GulpOptionsPanelController.OPTIONS_PATH)
.outLineBased(true)
.errLineBased(true)
.postExecution(new Runnable() {
@Override
public void run() {
// #246886
FileUtil.refreshFor(getWorkDir());
}
});
}
private static ExecutionDescriptor getSilentDescriptor() {
return new ExecutionDescriptor()
.inputOutput(InputOutput.NULL)
.inputVisible(false)
.frontWindow(false)
.showProgress(false)
.charset(StandardCharsets.UTF_8)
.outLineBased(true);
}
private File getWorkDir() {
if (workDir != null
&& workDir.exists()) {
return workDir;
}
File dir = FileUtil.toFile(project.getProjectDirectory());
assert dir != null : project.getProjectDirectory();
return dir;
}
private List getRunParams(String... args) {
List params = new ArrayList<>(args.length + 1);
params.addAll(Arrays.asList(args));
params.add(COLOR_PARAM);
return getParams(params);
}
List getParams(List params) {
assert params != null;
return params;
}
@CheckForNull
private static String validateResult(ValidationResult result) {
if (result.isFaultless()) {
return null;
}
if (result.hasErrors()) {
return result.getFirstErrorMessage();
}
return result.getFirstWarningMessage();
}
static List getListTasksParams() {
return Arrays.asList(NO_COLOR_PARAM, SILENT_PARAM, TASKS_PARAM);
}
//~ Inner classes
private static final class MacGulpExecutable extends GulpExecutable {
private static final String BASH_COMMAND = "/bin/bash -lc"; // NOI18N
MacGulpExecutable(String gulpPath, Project project, File workDir) {
super(gulpPath, project, workDir);
}
@Override
String getCommand() {
return BASH_COMMAND;
}
@Override
List getParams(List params) {
StringBuilder sb = new StringBuilder(200);
sb.append("\""); // NOI18N
sb.append(gulpPath);
sb.append("\" \""); // NOI18N
sb.append(StringUtilities.implode(super.getParams(params), "\" \"")); // NOI18N
sb.append("\""); // NOI18N
return Collections.singletonList(sb.toString());
}
}
private static final class GulpTasksLineProcessor implements LineProcessor {
// @GuardedBy("tasks")
final List tasks = new ArrayList<>();
@Override
public void processLine(String line) {
if (StringUtilities.hasText(line)) {
synchronized (tasks) {
tasks.add(line);
}
}
}
@Override
public void reset() {
// noop
}
@Override
public void close() {
// noop
}
public List getTasks() {
synchronized (tasks) {
return new ArrayList<>(tasks);
}
}
}
private static final class TaskList implements Future> {
private static final RequestProcessor RP = new RequestProcessor(TaskList.class);
final GulpExecutable gulp;
private final Future task;
private final GulpTasksLineProcessor processor;
private volatile Integer result;
// @GuardedBy("this")
private List gulpTasks = null;
TaskList(GulpExecutable gulp, Future task, GulpTasksLineProcessor processor) {
assert gulp != null;
assert task != null;
assert processor != null;
this.gulp = gulp;
this.task = task;
this.processor = processor;
}
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
return task.cancel(mayInterruptIfRunning);
}
@Override
public boolean isCancelled() {
return task.isCancelled();
}
@Override
public boolean isDone() {
return task.isDone();
}
@Override
public List get() throws InterruptedException, ExecutionException {
try {
result = task.get();
} catch (CancellationException ex) {
// cancelled by user
LOGGER.log(Level.FINE, null, ex);
}
return getGulpTasks();
}
@Override
public List get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
try {
result = task.get(timeout, unit);
} catch (CancellationException ex) {
// cancelled by user
LOGGER.log(Level.FINE, null, ex);
}
return getGulpTasks();
}
@NbBundle.Messages("TaskList.error=Cannot get Gulp tasks.")
private synchronized List getGulpTasks() {
if (gulpTasks != null) {
return Collections.unmodifiableList(gulpTasks);
}
if (result == null
|| result == 1) {
RP.post(new Runnable() {
@Override
public void run() {
gulp.run(getListTasksParams().toArray(new String[0]));
}
});
StatusDisplayer.getDefault().setStatusText(Bundle.TaskList_error());
gulpTasks = Collections.emptyList();
return Collections.unmodifiableList(gulpTasks);
}
List tasks = new ArrayList<>(processor.getTasks());
Collections.sort(tasks);
gulpTasks = new CopyOnWriteArrayList<>(tasks);
return Collections.unmodifiableList(gulpTasks);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy