/* ************************************************************************ # # 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 java.util.HashMap; import java.util.Map; import divconq.lang.op.FuncResult; import divconq.lang.op.OperationContext; import divconq.schema.DataType; import divconq.script.inst.CoreInstructionProvider; import divconq.script.mutator.Substring; import divconq.struct.Struct; import divconq.xml.XElement; public class ActivityManager { // operator is type specific - [Type][Name] = mutator protected Map<String, Map<String, IOperator>> operationExtensions = new HashMap<String, Map<String,IOperator>>(); protected Map<String, IInstructionProvider> instProviders = new HashMap<String, IInstructionProvider>(); protected IDebuggerHandler debugger = null; public void addTag(String name, IInstructionProvider cip) { this.instProviders.put(name, cip); } public ActivityManager() { // TODO load from config HashMap<String, IOperator> stringextensions = new HashMap<String, IOperator>(); stringextensions.put("Substring", new Substring()); this.operationExtensions.put("String", stringextensions); CoreInstructionProvider.init(this); } // create anonymous var public Struct createVariable(String type) { DataType mut = OperationContext.get().getSchema().getType(type); if (mut == null) return null; FuncResult<Struct> ret = mut.create(); return ret.getResult(); } public Instruction createInstruction(XElement def) { IInstructionProvider mut = this.instProviders.get(def.getName()); return (mut != null) ? mut.createInstruction(def) : null; } public void operate(StackEntry stack, Struct target, XElement code) { if (target == null) { // TODO log stack.resume(); return; } DataType dt = target.getType(); if (dt != null) { // look for an extension for this mutator first Map<String, IOperator> typeextensions = this.operationExtensions.get(dt.getId()); if (typeextensions != null) { IOperator mut = typeextensions.get(code.getName()); if (mut != null) { mut.operation(stack, code, target); return; } } } // fall back on the default operation handler for the target type target.operation(stack, code); } public void registerDebugger(IDebuggerHandler v) { this.debugger = v; } public IDebuggerHandler getDebugger() { return this.debugger; } /* * goals * x better compiler * x no exceptions * x rewrite stack control * advanced data types * create functions * globals * x improve flexible attributes * x new source handling * more instructions * component management * freeze/thaw * create libraries (call by function) * create libraries (call by script) * * repol * * when a var goes out of scope, call dispose (close streams, sources, dests, remove temp files, etc) * * check user level within instructions - error if user is not authorized * * * value/var attributes * "literal" * "literal string with {$variable} in it" * * ` forces literal, so * "`hello world" resolves to "hello world" * "``hello" resolves to "`hello" * "`$x" resolves to "$x" * * $ forces variable named after value * "$x" reference to x * * $$ forces variable by the name held in value * "$$z" resolves to reference to "x" if z = "x" * <Main Steps="[int][optional]"> ... insts </Main> <With Target="[string]" SetTo="[ref][optional]"> ... mutators </With> <Var Name="[string]" Type="[string]" SetTo="[ref][optional]"> ... mutators </Var> Composite Set/Merge - defaults to Xml if any non-text children or to Json if text only children <Var Name="msg1" Type="ResultMessage"> <Merge Format"Xml,Yaml,Json"> <Record> <Field Name="Kind" Value="Level" /> <Field Name="Code" Value="3" /> <Field Name="Message" Value="Howday partner!" /> </Record> </Merge> </Var> <For Name="[string][optional]" From="[int][optional, default 0]" To="[int][optional, default max long]" Step="[int][optional, default 1]"> ... insts </For> <Debug Code="[int][optional]"> message [string] </Debug> <Info Code="[int][optional]"> message [string] </Info> <Warn Code="[int][optional]"> message [string] </Warn> <Error Code="[int][optional]"> message [string] </Error> **** logic **** // Not may appear on any Logic or Condition <Logic Target="[ref][optional]" Equal|Equals="[reference]" Not="[boolean][optional]"> ... insts </Logic> <Logic Target="[ref][optional]" LessThan="[reference]"> ... insts </Logic> <Logic Target="[ref][optional]" GreaterThan="[reference]"> ... insts </Logic> <Logic Target="[ref][optional]" LessThanOrEqual="[reference]"> ... insts </Logic> <Logic Target="[ref][optional]" GreaterThanOrEqual="[reference]"> ... insts </Logic> // may combine expressions <Logic Target="[ref][optional]" GreaterThanOrEqual="[reference]" LessThan="[reference]"> ... insts </Logic> // condition with Name ignores the top Name, condition without Name uses top Name in compare <Logic Target="[ref][optional]"> <Detail> // automatically an AND expression <Condition ...> <And/Or> <Condition Target="[ref][optional]" ... any of the conditions above ...> <Condition ...> </And/Or> <Condition ...> </Detail> ... insts </Logic> // if no conditions listed then just check to see if this variable - exists, is not null, not false and not empty string <Logic Target="[ref][optional]"> ... insts </Logic> // if no name then it is treated as CommandState <Logic> ... insts </Logic> **** end logic **** <While [any logic]> ... insts </While> <Until [any logic]> ... insts </Until> <If [any logic]> ... insts </If> <Switch Target="[ref][optional]"> ... Case insts </Switch> <Case [any logic]> ... insts </Case> <Break /> <Continue /> <Exit Code="[int]" Message="[string]" /> <IfLastFlagSuccess> ... insts </IfLastFlagSuccess> <IfLastFlagFail> ... insts </IfLastFlagFail> // forces "^_" into Name attribute <IfLastResult [any logic]> ... insts </IfLastResult> <IfExitFlagSuccess> ... insts </IfExitFlagSuccess> <IfExitFlagFail> ... insts </IfExitFlagFail> // we are an async language - don't really wait - instead schedule a call back in N seconds <Sleep Seconds="[int]" Period="ISO Period" Duration="dur" /> <Progress Step="[int]" Name="[string]" Amount="[int]"> message </Progress> - NO <Milestone Name="[string]" Release="[int]" Acquire="[int]" /> // milestone handling <Await Name="[string]" /> <Release Name="[string]" /> // we are an async language - don't really wait - instead service call back triggers our call back <CallService Name="[string]" Feature="[string][optional]" Op="[string][optional]" Hub="[string]" Await="[boolean][optional, def true]" ResultTo="var to set - or just use default[ref][optional]" ResultCodeTo="...[int][optional]"> <Args> <Field ... /> </Args> </CallService> // execute an "program" which only sends back a code and a result // entire execution is one call, we use an async call back // may report to collab for debugging though <CallScript Name="[string]" Hub="[string][optional]" Await="[boolean][optional, def true]" ResultTo="var to set - or just use default[ref][optional]" ResultCodeTo="...[int][optional]"> <Args> <Field ... /> </Args> </CallScript> ** CallScript and CallService can both direct response to a mailbox if Await false this gets aysnc flow, if True just means to look in the mailbox for response in addition to _LastResult // execution is inline and we watch it become part of our stack - heavy but easier to debug <CallFunction Name="[string]" Library="lib.name.here [string][optional]" ResultTo="var to set - or just use default[ref][optional]" ResultCodeTo="...[int][optional]"> <Args> <Field ... /> </Args> </CallFunction> <Args> can be inline: <Args> <Field ... /> </Args> or reference <Args Name="name of var [string]" /> ------------------------------------------------------------------- Examples <Script> <Types> ... special types not in Repo, but useful to local - type validation is ensured (Strict) or warned (Permissive) </Types> // functions (local or lib) present a stack barrier, searches for variable names stop here <Function Name="" ParamType="AnyRecord or specific" ReturnType="AnyRecord or specific"> </Function> ... or can define inline <Function Name=""> <ParamType Inherits=""> ...field defs... </ParamType> <ReturnType Inherits=""> ...field defs... </ReturnType> </Function> <Main> <ParamType Inherits=""> ...field defs... </ParamType> <ReturnType Inherits=""> ...field defs... </ReturnType> <Var Name="x" Type="Integer" SetTo="5"> <Add Value="2" /> <Inc /> <Random From="x" To="y" /> </Var> <List Name="y"> <Scalar Value="2" /> <Scalar Value="^x" /> <Record> <Field Name=""> scalar, record, list </Field> </Record> </List> <With Name="y"> <RemoveItem Index="2" /> <AddItem> <Scalar ... /> </AddItem> <Clear /> <InsertItem Index="0"> <Scalar ... /> </InsertItem> </With> <Record Name="z"> <Field Name=""> scalar, record, list </Field> </Record> // change string at 3rd position of property a (list) <With Name="z.a[2]"> <Concat Value="nuy" /> </With> <With Name="z"> set field, remove field </With> <Component Name="var name" Class="id string - ftcFtp"> <Settings> ... </Settings> </Component> <For Name="bottles" From="4" To="0" Step="-1"> <Switch Name="bottles"> <Case GreaterThan="1"> <Var Name="bottlesleft" Type="Integer" Value="{$bottles}"> <Dec /> </Var> <Info> {$bottles} bottles of beer on the wall, {$bottles} bottles of beer. Take one down and pass it around, {$bottlesleft} bottles of beer on the wall. </Info> </Case> <Case Equal="1"> <Info> {$bottles} bottle of beer on the wall, {$bottles} bottle of beer. Take one down and pass it around, no more bottles of beer on the wall. </Info> </Case> <Case Equal="0"> <Info> No more bottles of beer on the wall, no more bottles of beer. Go to the store and buy some more, 99 bottles of beer on the wall. </Info> </Case> </Switch> </For> </Main> </Script> <Var Name="strm1" Class="divconq.core.StreamController"> <dccTransform> compress encrypt </dccTransform> </Var> <Var Name="disk1" Class="divconq.protos.DiskController"> <dcpChangeDirectory Path="./files/accounts" /> <dcpGet Stream="$strm1" Match="*.xls" /> </Var> <Var Name="sftp1" Class="divconq.protos.SftpController"> <dcpSettings> <Field ... </dcpSettings> <dcpSignOn User="" Password="" Key="" Cert="" /> <dcpChangeDirectory Path="./accountbackup" /> <dcpPut Stream="$strm1" /> <dcpExit /> </Var> <Var Name="disk1"> <dcpExit /> </Var> ----------------------------- upload // Navigator selects/filters based on name, path and attributes <Var Name="disk1" Class="divconq.protos.DiskDriver"> <OpenNavigator Name="snav"> <Root Path="./files/accounts" /> <MatchFolders Pattern="." Recursive="True" /> same as <MatchFolders Recursive="True" /> <MatchFiles Pattern="*.xls" /> <IgnoreFolders Pattern="..." /> <IgnoreFiles Pattern="..." /> <All | Any> <Size LessThan="..." /> <Modified NewerThan="..." /> <Tagged Name="" /> </All | Any> ... each All or Any listed must be true <Decrypt /> // each file is decrypted <Uncompress /> // each file is uncompressed, the contents are </OpenNavigator> </Var> // in addition to FTP, SFTP, Disk, Box, S3, AS2 drivers there is also a Session driver // for transfers to another session * <Var Name="sftp1" Class="divconq.protos.SftpDriver"> <Settings> <Field ... </Settings> <SignIn User="" Password="" Key="" Cert="" /> <OpenNavigator Name="dnav1"> <Root Path="./accountbackup" /> <Compress Name="..." /> // all incoming files are named under this, closed only when navigator is closed <Encrypt /> </OpenNavigator> </Var> // files can have a MarkForDelete call which will delete when navigator is told to Flush or Close // this way we can still iterate files while not yet deleting // file <ForEach Name="sfile" In="$snav"> <!-- like a while loop, for each file given by source --> <Var Name="shash" Type="String" /> <Var Name="dhash" Type="String" /> <Transform ...function local or library /> <With Target="$dnav1"> // Put has Resume="T/F" default to F <Put Source="$sfile" Rename="..." Handle="dfile" /> // use to rename files as they are stored, macros are available </With> ... if errors ... <With Target="$sfile"> <Hash Method="Sha2" Target="$shash" /> </With> <With Target="$dfile"> <Hash Method="Sha2" Name="$dhash" /> </With> ... compare ... <With Target="$dfile"> <Hash Method="Sha2" Name="$dhash" /> </With> </ForEach> - ALT - // all components are automatically disposed at end of script <With Target="$disk1"> <Dispose /> // causes any navigators to dispose also </With> * */ }