/* ************************************************************************
#
# DivConq
#
# http://divconq.com/
#
# Copyright:
# Copyright 2014 eTimeline, LLC. All rights reserved.
#
# License:
# See the license.txt file in the project's top-level directory for details.
#
# Authors:
# * Andy White
#
************************************************************************ */
package divconq.script;
import divconq.hub.Hub;
import divconq.lang.op.OperationContext;
import divconq.lang.op.OperationResult;
import divconq.struct.ListStruct;
import divconq.struct.Struct;
import divconq.struct.RecordStruct;
import divconq.struct.scalar.NullStruct;
import divconq.struct.scalar.StringStruct;
import divconq.util.StringUtil;
import divconq.xml.XElement;
public class StackEntry {
protected ExecuteState state = ExecuteState.Ready;
protected StackEntry parent = null;
protected Activity activity = null;
protected Instruction inst = null;
protected RecordStruct inststore = new RecordStruct();
protected IInstructionCallback callback = null;
public StackEntry(Activity act, StackEntry parent, Instruction inst) {
this.activity = act;
this.parent = parent;
this.inst = inst;
}
public StackEntry getParent() {
return this.parent;
}
public void setParent(StackEntry v) {
this.parent = v;
}
public RecordStruct getStore() {
return this.inststore;
}
public Instruction getInstruction() {
return this.inst;
}
public void setInstruction(Instruction v) {
this.inst = v;
}
public ExecuteState getState() {
return (this.state != null) ? this.state : ExecuteState.Done;
}
public void setState(ExecuteState v) {
this.state = v;
}
public void updateCallback(final IInstructionCallback cb) {
final IInstructionCallback old = this.callback;
//System.out.println("Updating callback on " + this.inst.source.getName() + " - thread " + Thread.currentThread().getName());
this.callback = new IInstructionCallback() {
@Override
public void resume() {
cb.resume();
old.resume();
}
};
}
public void resume() {
if (this.callback != null) {
this.callback.resume();
return;
}
if (this.parent != null)
this.parent.resume();
}
public Struct queryVariable(String name) {
return (this.parent != null) ? this.parent.queryVariable(name) : null;
}
public Activity getActivity() {
return this.activity;
}
public StackBlockEntry queryBlockStack() {
if (this.parent == null)
return null;
return this.parent.queryBlockStack();
}
public StackFunctionEntry queryFunctionStack() {
if (this.parent == null)
return null;
return this.parent.queryFunctionStack();
}
public Struct resolveValue(String val) {
if (val == null)
return NullStruct.instance;
val = this.resolveValueToString(val);
// var flag - return the reference to the variable pointed to
if (val.startsWith("$"))
return this.queryVariable(val.substring(1));
// literal flag - return the string as is
if (val.startsWith("`"))
return new StringStruct(val.substring(1));
// otherwise just treat this as a string
return new StringStruct(val);
}
public void operate(Struct target, XElement source) {
Hub.instance.getActivityManager().operate(this, target, source);
}
public Struct refFromSource(String attr) {
return this.refFromElement(this.inst.source, attr);
}
public Struct refFromElement(XElement el, String attr) {
if ((el == null) || StringUtil.isEmpty(attr))
return NullStruct.instance;
return this.resolveValue(el.getAttribute(attr));
}
public String stringFromSource(String attr) {
return this.stringFromElement(this.inst.source, attr, null);
}
public String stringFromSource(String attr, String def) {
return this.stringFromElement(this.inst.source, attr, def);
}
public String stringFromElement(XElement el, String attr) {
return this.stringFromElement(el, attr, null);
}
public String stringFromElement(XElement el, String attr, String def) {
if ((el == null) || StringUtil.isEmpty(attr))
return def;
Struct dt = this.refFromElement(el, attr);
if (dt == NullStruct.instance)
return def;
String ret = Struct.objectToString(dt);
if (StringUtil.isNotEmpty(ret))
return ret;
return def;
}
public long intFromSource(String attr) {
return this.intFromElement(this.inst.source, attr, 0);
}
public long intFromSource(String attr, int def) {
return this.intFromElement(this.inst.source, attr, def);
}
public long intFromElement(XElement el, String attr) {
return this.intFromElement(el, attr, 0);
}
public long intFromElement(XElement el, String attr, int def) {
if ((el == null) || StringUtil.isEmpty(attr))
return def;
Struct dt = this.refFromElement(el, attr);
Long ret = Struct.objectToInteger(dt);
if (ret != null)
return ret;
return def;
}
public boolean boolFromSource(String attr) {
return this.boolFromElement(this.inst.source, attr, false);
}
public boolean boolFromSource(String attr, boolean def) {
return this.boolFromElement(this.inst.source, attr, def);
}
public boolean boolFromElement(XElement el, String attr) {
return this.boolFromElement(el, attr, false);
}
public boolean boolFromElement(XElement el, String attr, boolean def) {
if ((el == null) || StringUtil.isEmpty(attr))
return def;
Struct dt = this.refFromElement(el, attr);
Boolean ret = Struct.objectToBoolean(dt);
if (ret != null)
return ret;
return def;
}
public String resolveValueToString(String val) {
if (val == null)
return "";
// the expansion of variables is per Attribute Value Templates in XSLT
// http://www.w3.org/TR/xslt#attribute-value-templates
StringBuilder sb = new StringBuilder();
int lpos = 0;
int bpos = val.indexOf("{$");
while (bpos != -1) {
int epos = val.indexOf("}", bpos);
if (epos == -1)
break;
sb.append(val.substring(lpos, bpos));
lpos = epos + 1;
String varname = val.substring(bpos + 2, epos).trim();
String[] vparts = varname.split("\\.");
varname = "";
for (String vpart : vparts) {
if (varname.length() == 0) {
varname = vpart;
}
else {
if (vpart.startsWith("$")) {
Struct qvar = this.queryVariable(vpart.substring(1));
if (qvar != null) {
varname += "." + qvar.toString();
continue;
}
}
varname += "." + vpart;
}
}
Struct qvar2 = this.queryVariable(varname);
if (qvar2 != null)
sb.append(qvar2.toString());
else {
OperationContext.get().warnTr(500, varname);
sb.append(val.substring(bpos, epos + 1));
}
bpos = val.indexOf("{$", epos);
}
sb.append(val.substring(lpos));
return sb.toString();
}
public void addVariable(String type, String name) {
this.addVariable(name, this.activity.createStruct(type));
}
public void addVariable(String name, Struct var) {
if (var == null) {
OperationContext.get().errorTr(512);
return;
}
StackBlockEntry b = this.queryBlockStack();
if (b == null)
OperationContext.get().errorTr(513, name);
else
b.addVariable(name, var);
}
// Result Code and Value
public void setLastCode(Long code) {
StackFunctionEntry func = this.queryFunctionStack();
if (func != null)
func.setLastCode(code);
}
public Long getLastCode() {
StackFunctionEntry func = this.queryFunctionStack();
if (func != null)
return func.getLastCode();
return null;
}
public void setLastResult(Struct val) {
StackFunctionEntry func = this.queryFunctionStack();
if (func != null)
func.setLastResult(val);
}
public void setLastResult(long code, Struct val) {
StackFunctionEntry func = this.queryFunctionStack();
if (func != null) {
func.setLastCode(code);
func.setLastResult(val);
}
}
public void setLastResult(OperationResult v) {
StackFunctionEntry func = this.queryFunctionStack();
if (func != null) {
func.setLastCode(v.getCode());
func.setLastResult(v.toLogMessage()); // TODO really, a message?
}
}
public Struct getLastResult() {
StackFunctionEntry func = this.queryFunctionStack();
if (func != null)
return func.getLastResult();
return null;
}
// Run
public void run(IInstructionCallback cb) {
this.callback = cb;
this.run();
}
public void run() {
if (this.inst != null)
this.inst.run(this);
}
public void cancel() {
if (this.inst != null)
this.inst.cancel(this);
}
public void debugStack(ListStruct dumpList) {
RecordStruct dumpRec = new RecordStruct();
dumpList.addItem(dumpRec);
this.collectDebugRecord(dumpRec);
RecordStruct subRec = this.inst.collectDebugRecord(this, dumpRec);
if (subRec != null)
dumpList.addItem(subRec);
}
public void collectDebugRecord(RecordStruct rec) {
}
public boolean codeHasAttribute(String attr) {
if (this.inst != null)
return this.inst.source.hasAttribute(attr);
return false;
}
public StackEntry getExecutingStack() {
return this;
}
}