blob: d6ed1d0c87d329ae37de1f80e589d5d229ab129c [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2013 Christian Pontesegger and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* Contributors:
* Christian Pontesegger - initial API and implementation
*******************************************************************************/
package org.eclipse.ease;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.net.URI;
import java.net.URL;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.Platform;
import org.eclipse.ease.tools.ResourceTools;
/**
* Scriptable object. Consists of scriptable data and a result container.
*/
public class Script {
/** command to be executed. */
private final Object fCommand;
/** script result returned from command. */
private final ScriptResult fResult;
/** Internal buffer when delivering code data from streams. */
private String fCodeBuffer = null;
/** Script title (optional). */
private final String fTitle;
/** Interactive/shell originated script piece */
private final boolean fIsShell;
/**
* Constructor.
*
* @param title
* name of script object
* @param command
* command (sequence) to be executed
* @param isShell
* <code>true</code> if the script originated from a shell/interactive session
*/
public Script(final String title, final Object command, final boolean isShell) {
fTitle = title;
fCommand = command;
fResult = new ScriptResult();
fIsShell = isShell;
}
/**
* Constructor.
*
* @param title
* name of script object
* @param command
* command (sequence) to be executed
*/
public Script(final String title, final Object command) {
this(title, command, false);
}
/**
* Constructor. Using no title for this script
*
* @param command
* command (sequence) to be executed
*/
public Script(final Object command) {
this(null, command);
}
/**
* Get the scriptable data as {@link InputStream}. The caller needs to close the stream when it is not used anymore. Calling this method multiple times will
* return different streams with the same text content.
*
* @return scriptable data
* @throws Exception
* when stream cannot be established
*/
public InputStream getCodeStream() throws Exception {
return new ByteArrayInputStream(getCode().getBytes());
}
/**
* Get the scriptable data as {@link String}.
*
* @return scriptable data
* @throws Exception
* when code cannot be read from source
*/
public String getCode() throws Exception {
if (fCodeBuffer != null)
return fCodeBuffer;
if (fCommand instanceof String)
return (String) fCommand;
if (fCommand instanceof StringBuilder)
return fCommand.toString();
if (fCommand instanceof InputStream)
// streams can only be read once, therefore buffer
return bufferStream((InputStream) fCommand);
if (fCommand instanceof Reader)
// readers can only be read once, therefore buffer
return bufferReader((Reader) fCommand);
// if we already have a scriptable
if (fCommand instanceof IScriptable)
return bufferStream(((IScriptable) fCommand).getSourceCode());
// try to adapt to scriptable
final Object scriptable = Platform.getAdapterManager().getAdapter(fCommand, IScriptable.class);
if (scriptable != null)
return bufferStream(((IScriptable) scriptable).getSourceCode());
// last resort, convert to String
if (fCommand != null) {
// better buffer stuff, we do not know if toString() remains constant
fCodeBuffer = fCommand.toString();
return fCodeBuffer;
}
return null;
}
private String bufferReader(final Reader reader) throws IOException {
fCodeBuffer = ResourceTools.toString(reader);
reader.close();
return fCodeBuffer;
}
private String bufferStream(final InputStream stream) throws IOException {
fCodeBuffer = ResourceTools.toString(stream);
stream.close();
return fCodeBuffer;
}
/**
* Returns the command object of this script.
*
* @return command object
*/
public final Object getCommand() {
return fCommand;
}
/**
* Get execution result.
*
* @return execution result.
*/
public final ScriptResult getResult() {
return fResult;
}
/**
* Set the execution result.
*
* @param result
* execution result
*/
public final void setResult(final Object result) {
fResult.setResult(result);
// gracefully close input streams & readers
closeInput();
}
/**
* Set an execution exception.
*
* @param e
* exception
*/
public final void setException(final Throwable e) {
fResult.setException(e);
// gracefully close input streams & readers
closeInput();
}
private void closeInput() {
if (fCommand instanceof InputStream) {
try {
((InputStream) fCommand).close();
} catch (final IOException ex) {
}
} else if (fCommand instanceof Reader) {
try {
((Reader) fCommand).close();
} catch (final IOException ex) {
}
}
}
/**
* Returns the file instance, if the current command is backed by a file.
*
* @return {@link IFile}, {@link File} or <code>null</code>
*/
public Object getFile() {
if ((fCommand instanceof IFile) || (fCommand instanceof File))
return fCommand;
return null;
}
/**
* Check if this script is defined by dynamically generated code. Generated code might be hidden while debugging.
*
* @return <code>true</code> when not a file and not an {@link URL}
*/
public boolean isDynamic() {
return !((fCommand instanceof URL) || (getFile() != null));
}
@Override
public String toString() {
return (getTitle() != null) ? getTitle() : "(unknown script source)";
}
/**
* Get the title of this script. Title has to be set by the caller via the constructor. Typically this is used for dynamic code to indicate its purpose. If
* no title is set we try to extract the name of the executed resource.
*
* @return script title or <code>null</code>
*/
public String getTitle() {
if (fTitle != null)
return fTitle;
if (fCommand instanceof IFile)
return ((IFile) fCommand).getName();
if (fCommand instanceof File)
return ((File) fCommand).getName();
if (fCommand instanceof URI)
return fCommand.toString();
if (fCommand instanceof URL)
return fCommand.toString();
return null;
}
/**
* Script originated from interactive input or shell, as opposed to a source file.
*
* A script engine may decide to process a script originating in the shell differently than one originating in a source file.
*
* @return <code>true</code> origin is interactive input or shell, or <code>false</code> if origin is a source file.
*/
public boolean isShellMode() {
return fIsShell;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = (prime * result) + ((fCommand == null) ? 0 : fCommand.hashCode());
return result;
}
@Override
public boolean equals(final Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final Script other = (Script) obj;
if (fCommand == null) {
if (other.fCommand != null)
return false;
} else if (!fCommand.equals(other.fCommand))
return false;
return true;
}
}