blob: 86f0fea5316e7e2c339d11f01215f9f5fceb6aae [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2006, 2020 Stephan Wahlbrink and others.
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License 2.0 which is available at
# https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
# which is available at https://www.apache.org/licenses/LICENSE-2.0.
#
# SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
#
# Contributors:
# Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
#=============================================================================*/
package org.eclipse.statet.nico.core.runtime;
import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.core.filesystem.IFileStore;
import org.eclipse.core.filesystem.URIUtil;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.variables.IDynamicVariable;
import org.eclipse.debug.core.DebugEvent;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.statet.jcommons.collections.CopyOnWriteIdentityListSet;
import org.eclipse.statet.jcommons.collections.ImCollections;
import org.eclipse.statet.jcommons.collections.ImList;
import org.eclipse.statet.jcommons.status.ErrorStatus;
import org.eclipse.statet.jcommons.status.ProgressMonitor;
import org.eclipse.statet.jcommons.status.StatusException;
import org.eclipse.statet.jcommons.ts.core.SystemRunnable;
import org.eclipse.statet.jcommons.ts.core.Tool;
import org.eclipse.statet.jcommons.ts.core.ToolService;
import org.eclipse.statet.ecommons.io.FileUtil;
import org.eclipse.statet.ecommons.net.resourcemapping.core.ResourceMappingUtils;
import org.eclipse.statet.ecommons.runtime.core.util.PathUtils;
import org.eclipse.statet.ecommons.variables.core.DateVariable;
import org.eclipse.statet.ecommons.variables.core.DynamicVariable;
import org.eclipse.statet.ecommons.variables.core.TimeVariable;
import org.eclipse.statet.internal.nico.core.NicoCorePlugin;
import org.eclipse.statet.nico.core.NicoCore;
import org.eclipse.statet.nico.core.NicoVariables;
import org.eclipse.statet.nico.core.runtime.ToolController.IToolStatusListener;
/**
* It belongs to a ToolProcess and has the same life cycle.
*/
public class ToolWorkspace {
public static interface Listener {
public void propertyChanged(ToolWorkspace workspace, Map<String, Object> properties);
}
private class ControllerListener implements IToolStatusListener {
@Override
public void controllerStatusChanged(final ToolStatus oldStatus, final ToolStatus newStatus, final List<DebugEvent> eventCollection) {
if (newStatus == ToolStatus.TERMINATED) {
dispose();
}
// by definition in tool lifecycle thread
if (!newStatus.isRunning()) {
if (ToolWorkspace.this.currentPrompt == null || ToolWorkspace.this.currentPrompt == ToolWorkspace.this.publishedPrompt) {
return;
}
ToolWorkspace.this.publishedPrompt= ToolWorkspace.this.currentPrompt;
firePrompt(ToolWorkspace.this.currentPrompt, eventCollection);
return;
}
else {
ToolWorkspace.this.publishedPrompt= ToolWorkspace.this.defaultPrompt;
firePrompt(ToolWorkspace.this.defaultPrompt, eventCollection);
}
}
}
private class AutoUpdater implements SystemRunnable {
@Override
public String getTypeId() {
return "common/workspace/update.auto";
}
@Override
public String getLabel() {
return "Auto Update";
}
@Override
public boolean canRunIn(final Tool tool) {
return (tool == ToolWorkspace.this.process);
}
@Override
public boolean changed(final int event, final Tool tool) {
switch (event) {
case REMOVING_FROM:
case MOVING_FROM:
return false;
}
return true;
}
@Override
public void run(final ToolService service, final ProgressMonitor m) throws StatusException {
ToolWorkspace.this.isRefreshing= true;
try {
autoRefreshFromTool((ConsoleService) service, m);
}
finally {
ToolWorkspace.this.isRefreshing= false;
}
firePropertiesChanged();
}
}
public static final int DETAIL_PROMPT= 1;
public static final int DETAIL_LINE_SEPARTOR= 2;
private final ToolProcess process;
private volatile String lineSeparator;
private volatile char fileSeparator;
private volatile Prompt currentPrompt;
private volatile Prompt defaultPrompt;
private Prompt publishedPrompt;
private IFileStore workspaceDir;
private final String remoteHost;
private IPath remoteWorkspaceDirPath;
private final Map<String, Object> properties= new HashMap<>();
private final CopyOnWriteIdentityListSet<Listener> propertyListener= new CopyOnWriteIdentityListSet<>();
private boolean autoRefreshEnabled= true;
private boolean isRefreshing;
private final ImList<IDynamicVariable> stringVariables;
private int changeFlags;
public ToolWorkspace(final ToolController controller,
Prompt prompt, final String lineSeparator, final char fileSeparator,
final String remoteHost) {
this.process= controller.getTool();
if (prompt == null) {
prompt= Prompt.DEFAULT;
}
this.publishedPrompt= this.currentPrompt= this.defaultPrompt= prompt;
this.remoteHost= remoteHost;
controlSetLineSeparator(lineSeparator);
controlSetFileSeparator(fileSeparator);
controller.addToolStatusListener(new ControllerListener());
controller.getQueue().addOnIdle(new AutoUpdater(), 5000);
this.stringVariables= ImCollections.<IDynamicVariable>newList(
new DateVariable(NicoVariables.SESSION_STARTUP_DATE_VARIABLE) {
@Override
protected long getTimestamp() {
return ToolWorkspace.this.process.getStartupTimestamp();
}
},
new TimeVariable(NicoVariables.SESSION_STARTUP_TIME_VARIABLE) {
@Override
protected long getTimestamp() {
return ToolWorkspace.this.process.getStartupTimestamp();
}
},
new DateVariable(NicoVariables.SESSION_CONNECTION_DATE_VARIABLE) {
@Override
protected long getTimestamp() {
return ToolWorkspace.this.process.getConnectionTimestamp();
}
},
new TimeVariable(NicoVariables.SESSION_CONNECTION_TIME_VARIABLE) {
@Override
protected long getTimestamp() {
return ToolWorkspace.this.process.getStartupTimestamp();
}
},
new DynamicVariable.LocationVariable(NicoVariables.SESSION_STARTUP_WD_VARIABLE) {
@Override
public String getValue(final String argument) throws CoreException {
return ToolWorkspace.this.process.getStartupWD();
}
} );
}
public ToolProcess getProcess() {
return this.process;
}
public boolean isWindows() {
return (getFileSeparator() == '\\');
}
public void setAutoRefresh(final boolean enable) {
synchronized (this.process.getQueue()) {
if (this.autoRefreshEnabled != enable) {
this.autoRefreshEnabled= enable;
final ToolStatus status= this.process.getToolStatus();
if (status != ToolStatus.TERMINATED) {
if (enable && status.isWaiting()) {
this.process.getQueue().internal_resetOnIdle();
this.process.getQueue().notifyAll();
}
addPropertyChanged("AutoRefresh.enabled", enable);
firePropertiesChanged();
}
}
}
}
public boolean isAutoRefreshEnabled() {
return this.autoRefreshEnabled;
}
protected void autoRefreshFromTool(final ConsoleService service,
final ProgressMonitor m) throws StatusException {
if (this.autoRefreshEnabled) {
refreshFromTool(0, service, m);
}
}
protected void refreshFromTool(final int options, final ConsoleService service,
final ProgressMonitor m) throws StatusException {
}
protected final int getChangeFlags() {
return this.changeFlags;
}
protected void controlBriefChanged(final Object obj, final int flags) {
this.changeFlags|= flags;
}
protected void clearBriefedChanges() {
this.changeFlags= 0;
}
public final String getLineSeparator() {
return this.lineSeparator;
}
public final char getFileSeparator() {
return this.fileSeparator;
}
public final Prompt getPrompt() {
return this.publishedPrompt;
}
protected final Prompt getCurrentPrompt() {
return this.currentPrompt;
}
public final Prompt getDefaultPrompt() {
return this.defaultPrompt;
}
public final IFileStore getWorkspaceDir() {
return this.workspaceDir;
}
public String getEncoding() {
return "UTF-8"; //$NON-NLS-1$
}
public final boolean isRemote() {
return (this.remoteHost != null);
}
public String getRemoteAddress() {
return this.remoteHost;
}
public IPath getRemoteWorkspaceDirPath() {
return this.remoteWorkspaceDirPath;
}
public IPath createToolPath(String toolPath) {
if (toolPath == null) {
return null;
}
if (isWindows() && File.separatorChar == '/') {
toolPath= toolPath.replace('\\', '/');
}
return PathUtils.check(new Path(toolPath));
}
public IFileStore toFileStore(final IPath toolPath) throws CoreException {
if (this.remoteHost != null) {
return ResourceMappingUtils.getManager()
.mapRemoteResourceToFileStore(this.remoteHost, toolPath,
(this.remoteWorkspaceDirPath != null) ? this.remoteWorkspaceDirPath : null );
}
return FileUtil.getFileStore(toolPath.toString(), this.workspaceDir);
}
public IFileStore toFileStore(final String toolPath) throws CoreException {
if (this.remoteHost != null) {
return toFileStore(createToolPath(toolPath));
}
return FileUtil.getFileStore(toolPath.toString(), this.workspaceDir);
}
public String toToolPath(final IFileStore fileStore) throws StatusException {
if (this.remoteHost != null) {
final IPath path= ResourceMappingUtils.getManager()
.mapFileStoreToRemoteResource(this.remoteHost, fileStore);
if (path != null) {
return path.toString();
}
throw new StatusException(new ErrorStatus(NicoCore.BUNDLE_ID,
"Resolving path for the remote system failed." ));
}
return URIUtil.toPath(fileStore.toURI()).toString();
}
final void controlRefresh(final int options, final ConsoleService adapter,
final ProgressMonitor m) throws StatusException {
this.isRefreshing= true;
try {
refreshFromTool(options, adapter, m);
}
finally {
this.isRefreshing= false;
}
firePropertiesChanged();
}
/**
* Use only in tool main thread.
* @param prompt the new prompt, null doesn't change anything
*/
final void controlSetCurrentPrompt(final Prompt prompt, final ToolStatus status) {
if (prompt == this.currentPrompt || prompt == null) {
return;
}
this.currentPrompt= prompt;
if (!status.isRunning()) {
this.publishedPrompt= prompt;
firePrompt(prompt, null);
}
}
/**
* Use only in tool main thread.
* @param prompt the new prompt, null doesn't change anything
*/
final void controlSetDefaultPrompt(final Prompt prompt) {
if (prompt == this.defaultPrompt || prompt == null) {
return;
}
final Prompt oldDefault= this.defaultPrompt;
this.defaultPrompt= prompt;
if (oldDefault == this.currentPrompt) {
this.currentPrompt= prompt;
}
if (oldDefault == this.publishedPrompt) {
this.publishedPrompt= prompt;
firePrompt(prompt, null);
}
}
/**
* Use only in tool main thread.
*
* The default separator is System.getProperty("line.separator") for local
* workspaces, and '\n' for remote workspaces.
*
* @param newSeparator the new line separator, null sets the default separator
*/
final void controlSetLineSeparator(final String newSeparator) {
final String oldSeparator= this.lineSeparator;
if (newSeparator != null) {
this.lineSeparator= newSeparator;
}
else {
this.lineSeparator= (this.remoteHost == null) ? System.getProperty("line.separator") : "\n"; //$NON-NLS-1$
}
// if (!fLineSeparator.equals(oldSeparator)) {
// DebugEvent event= new DebugEvent(ToolWorkspace.this, DebugEvent.CHANGE, DETAIL_LINE_SEPARTOR);
// event.setData(fLineSeparator);
// fireEvent(event);
// }
}
/**
* Use only in tool main thread.
*
* The default separator is System.getProperty("file.separator") for local
* workspaces, and '/' for remote workspaces.
*
* @param newSeparator the new file separator, null sets the default separator
*/
final void controlSetFileSeparator(final char newSeparator) {
final char oldSeparator= this.fileSeparator;
if (newSeparator != 0) {
this.fileSeparator= newSeparator;
}
else {
this.fileSeparator= (isRemote()) ? '/' : File.separatorChar;
}
}
protected final void controlSetWorkspaceDir(final IFileStore directory) {
if ((this.workspaceDir != null) ? !this.workspaceDir.equals(directory) : directory != null) {
this.workspaceDir= directory;
this.properties.put("wd", directory);
if (!this.isRefreshing) {
firePropertiesChanged();
}
}
}
protected final void controlSetRemoteWorkspaceDir(final IPath toolPath) {
this.remoteWorkspaceDirPath= toolPath;
try {
controlSetWorkspaceDir(toFileStore(toolPath));
}
catch (final CoreException e) {
controlSetWorkspaceDir(null);
}
}
private final void firePrompt(final Prompt prompt, final List<DebugEvent> eventCollection) {
final DebugEvent event= new DebugEvent(ToolWorkspace.this, DebugEvent.CHANGE, DETAIL_PROMPT);
event.setData(prompt);
if (eventCollection != null) {
eventCollection.add(event);
return;
}
else {
fireEvent(event);
}
}
protected final void fireEvent(final DebugEvent event) {
final DebugPlugin manager= DebugPlugin.getDefault();
if (manager != null) {
manager.fireDebugEventSet(new DebugEvent[] { event });
}
}
public final void addPropertyListener(final Listener listener) {
this.propertyListener.add(listener);
}
public final void removePropertyListener(final Listener listener) {
this.propertyListener.remove(listener);
}
protected final void addPropertyChanged(final String property, final Object attr) {
this.properties.put(property, attr);
}
protected final void firePropertiesChanged() {
if (this.properties.isEmpty()) {
return;
}
for (final Listener listener : this.propertyListener.toList()) {
try {
listener.propertyChanged(ToolWorkspace.this, this.properties);
}
catch (final Exception e) {
NicoCorePlugin.logError(0, "An unexpected exception was thrown when notifying a tool workspace listener about changes.", e);
}
}
this.properties.clear();
}
public ImList<IDynamicVariable> getStringVariables() {
return this.stringVariables;
}
protected void dispose() {
}
}