helberg acl-final
TRANSCRIPT
Arbortext Command Language (ACL)
• ACL is a scripting language embedded in Arbortext Editor
• Provides ways to:
– Control the software
– Make dialogs work
– Navigate and manipulate documents
– Interface with other software components (e.g. Publishing Engine, Dynamic Link Manager, Content Manager, etc.)
– Interact with user
Arbortext Command Language (ACL)
• Basic syntax
• Variables and arrays
• Defining and using functions
• Working with files and documents
• Modifying the content of documents
• Using callbacks and hooks to handle events
• Integrating ACL scripts into your application
Basic Syntax
• ACL is sort of Perl-ish, sort of C-ish
– Like Perl, without the “line noise”
– Like C, without the pointer arithmetic and memory management
• Statements are the fundamental steps in a program
– Two types of statements: commands and functions
ACL Commands
• Commands use syntax like this:
commandname argument argument …
• Commands don’t return a value
• BUT May store information in system variables about error status, etc.
• Example:
print composed current
ACL Functions
• Functions use syntax like this:
functionname ( argument, argument, …)
• Functions often, but not always, return a value
– Result of calculation or transformation
– Status of operation (success/failure)
• Example:
if (doc_name(doc)==“foo.xml”) …
ACL Functions
• Gotcha: Return value vs. updated parameter
– Example:
Global $windows[]$return = window_list($windows);
– $return is number indicating status (number of windows), not the list itself
– The actual list (array) of windows is stored in $windows
ACL Comments
• Comments start with a hash #
• Everything after the hash is ignored
# This function is really coolfunction reallyCoolFunction(input) {
# do some really cool stuff here}
• You can stick a comment at the end of a line after some “real” code
if (oid_type($oid) == 4) { # type 4 -> equation
ACL Blocks
• A block is a section of a program that is a cohesive unit
• Delimited with curly braces { }
• Examples:
– Function definitions
– The branches of if/else statements
– The guts of while and for loops
ACL Flow Control
• The usual flow control structures are supported
– for loops
for (i=1; i < count($arr); i++) { doc_close($arr[i]);}
– while loops
find “Leno”while ($status == 0) { delete_mark; insert_string “O’Brien” find “Leno”}
More ACL Flow Control
– if/else branches
if ($size == “legal”) { set stylesheet=“legal.style”}else { set stylesheet=“letter.style”}
Still more flow control
– switch trees:
$resp = response(“Save Changes?”,”Yes”,”No”,”Cancel”);
switch ($resp) {
case 1:
doc_save(current_doc());
break;
case 2:
set modified=off;
doc_close(current_doc());
break;
case 3:
# do nothing
}
ACL Packages
• Analogous to Java or Perl packages
• Similar to namespaces in XML
package log;
global $errlog = “C:\temp\apterr.txt”;function error(msg) {
local $fid = open($log::errlog,a);put($fid, “*** ERROR: $msg\n”);flush($fid);close($fid);
}
Using Packages
• Use require to import a package’s code
• Use package::functionname to call a function in another package
package custom;require log;
function load_doc(docname) {local $docid = doc_open(docname);if ($docid == -1) {
log::error(“Couldn’t open $docname”);}
}
ACL Variables
• Variables in ACL are dynamically typed
– Automatically assumes the required type for the value
• Name is sometimes preceded with a $ like Perl (optional)
– But arrays are NOT preceded with an @
• You can define scope with local and global keywords
– Default is local scope
– Local variables are only accessible in the block in which they are declared (e.g. inside a function)
– Global variables are accessible anywhere
Assigning Values to Variables
• Assign values using the assignment operator =
– $mydoc = “C:\\temp\\mydocs\\mydoc.xml”;
• You can use expressions in assignment
– $mydoc = current_doc();
– $mypath = dirname(doc_path(current_doc()));
Assigning Values to Variables
• You can also use shortcut assignment operators
– $linecount += 1;
– $product *= get_multiplier();
• Use . (dot) operator for string concatenation
– $msg = “Do you want to close “ . doc_name() . “?”;
ACL Variable Evaluation
• Variables are evaluated
– Variable references inside double quotes are evaluated
•$docname = “foo.xml”;
•$msg = “Close $docname?”# returns “Close foo.xml?”
– References inside single quotes are not
•$msg = ‘Close $docname?’# returns “Close $docname?”
Deferred Evaluation of Variables
• Gotcha: In commands, you may need to defer evaluation
– Ex: (wrong way – prints first doc repeatedly)for ($i in $doclist) {
print composed $doclist[$i];}
• Use execute or eval commands to defer
– Ex: (right way – prints each document once)for ($i in $doclist) {
execute(“print composed $doclist[$i]”);}
ACL Arrays
• ACL supports two kinds of arrays
– Normal arrays, indexed by number
$array[1] = “foo”
– Associative arrays, indexed by key
$array[“foo”] = “bar”
• Gotcha: ACL arrays are indexed from one, not zero
Declaring ACL Arrays
• Both types have the same declaration syntax:
– [local | global] $arrayname[];
• Most arrays resize dynamically as necessary
– Unless declared using global and a fixed size
•global $myarray[10];
• Use square brackets [] to access array elements
– $msg = “the first element is $myarray[1]”;
– $msg = “owner of $docname is $owners[$docname]”;
Using ACL Arrays
• You can iterate over items in an array usingfor (… in …) construction
– for ($i in $myarray) {if ($myarray[$i]==doc_name()) {
response(doc_name() . “ is in array”);
break;}
}
• This works for both types of arrays
• Gotcha: Loop index is the array index, not the array value for that index
User-Defined Functions in ACL
• You can define your own functions
– function functionname(param1, param2) {# do some stuff herereturn result of operation;
}
• Example:
– function doc_dir(doc) {local $docname = doc_name(doc);return dirname($docname);
}
Forward Declarations
• In an ACL file, you often have multiple function definitions
• A function can’t use another function if it hasn’t been defined yet
Why You Need Forward Declarations
• This will fail:
function launch_browser(filename) {local $url = file_to_url(filename);system(“start $url”);
}
function file_to_url(filename) {gsub(filename,”\\”,”/”);return("http://". filename);
}
• We try to use file_to_url() before it is defined
How to Write a Forward Declaration
• Forward declaration is an empty function definition at the top of the file
• Lets other functions know that you're going to define the function later
Forward Declaration Example
• This will work because of the forward declaration:
function file_to_url() {}; # forward decl
function launch_browser(filename) {local $url = file_to_url(filename);system(“start $url”);
}
function file_to_url(filename) {gsub(filename,”\\”,”/”);return("http://". filename);
}
A Brief Tour of Built-In Functions
• Arbortext provides a ton of built-in functions for various purposes
• We'll look at some of the most useful categories
– document and file handling
– document content manipulation
– user interaction
• For more information see the online help
– ACL reference in online help is very complete
– If you don't know what it's called, try search
– If you still can't find it, try Adepters email!
Working with Files and Documents
• current_doc() - gets a handle (ID number) for the currently active document
• doc_open() - loads a document
– flags determine whether it's opened as XML or ASCII, whether context checking happens, etc.
• doc_name() - filename of the document
• doc_path() - fully-qualified pathname of the doc
• doc_incomplete() - tells whether doc is valid
• doc_save() - saves the document
• edit_new_window() - opens document in a new edit window
Working with Generic Files
• open() - open an arbitrary file for reading and/or writing (e.g. for logging messages or importing text content into a document)
• read() - read from a file
• getline() - read from a file one line at a time
• put() - write content to a file
• close() - close a file
General File System Info
• glob() - fills an array with names of files that match the globbing expression (e.g. "*.xml")
• file_directory() - tells whether specified filename is really a directory
• file_size() - size of the file
• file_newer() - determines if one file is newer than another
• username() - user name string (from OS)
• system() - execute an arbitrary system command
• file_selector() - starts a standard file selection dialog, returning the user's choice
Modifying Document Contents
• The OID is your friend (Object ID)
– Unique ID for each element in the document
– Can be used to find, access, change, or delete markup
• oid_root() - the root element of the doc
• oid_name() - the name of the specified element
• oid_caret() - the element containing the caret
• oid_attr() - gets the value of the specified attribute on the specified element
• oid_content() - returns the content of the element as a markup string
Moving Around with OIDs
• oid_first() - OID of first element in the doc
• oid_next() - OID of next element
• oid_prev() - OID of previous element
• oid_last() - OID of last element
• oid_parent() - OID of parent element
• oid_child() - OID of nth child element (or null OID if no such child exists)
• goto_oid() - moves the caret to an element
• oid_xpath_nodeset() - nodes that match an XPath expression (OIDs in an array)
Making Changes with OIDs
• oid_select() - selects the element
• oid_modify_attr() - changes an attribute value
• oid_delete_attr() - removes an attribute
• oid_delete - removes the element
• change_tag command - changes the element at the caret (use with goto_oid())
• insert_tag() - inserts a new element at the caret (use with goto_oid())
• insert() - inserts a markup string at the caret
OID example• Return the title of the nearest enclosing element based on
caret location
function oid_title(oid=oid_caret()) { # if this oid has a title, use that oid_find_children(oid,$titles,"title"); if (count($titles) > 0) { return oid_content($titles[1]); } # otherwise walk up the tree till we find one oid = oid_parent(oid); while (oid_valid(oid)) { oid_find_children(oid,$titles,"title"); if (count($titles) > 0) { return oid_content($titles[1]); } oid = oid_parent(oid); }}
Lots More Fun
• There are specific functions for navigating and changing special elements
– Tables
– Graphics
– Equations
– Entities and entity references
– Document objects from repositories
– Change tracking markup
• Too much to cover here--see the online help!
Getting User Input
• There are several ways to get user input in ACL
– response()
– list_response()
– readvar command
– file_selector()
– XUI dialogs
The response() Function
• Simple message with buttons for response
$answer = response(“Continue?”,”Yes”,”No”);
returns 1 returns 2
Multiple Response Buttons
• You can have as many buttons as you like
$ans = response(“Error! What do you want to do?”, \
“&Abort”, \
“&Retry”, \
“&Fail”, \
“Pull out &hair”, \
“&Defenestrate computer”, \
“&Move to MN and open a bait shop”);
The list_response() Function• Similar to response(), but uses an array to
generate a listbox
get_printers($options);
$ans = list_response($options,"Print",\
"Select a printer");
response("You selected $ans");
The readvar Command
• Lets user type a line of text
readvar -title "Notifications" \ -prompt "Enter emails to notify:" \ -value "[email protected]" \ notify;global $emails[];split($notify,$emails,',;');response("You want to notify:\n" . \ join($emails,"\n"));
The file_selector() Function• Use file_selector() to invoke the standard file
chooser for your OS
$fname = file_selector(doc_dir(),"xml",\ "XML files|*.xml|All files|*.*",\ "Select file to open");response("You selected $fname");
Using XUI dialogs
• See Jason & Asli's slides (Tues 1:15, "Using XUI to Manage Cross-References")
• Also dialog item callbacks are handy for this (a few slides from now)
Using Callbacks and Hooks
• Use your own functions to handle events
– document callbacks
– window callbacks
– session callbacks
– dialog item callbacks
• Callbacks vs. Hooks
– Hooks are global, callbacks can have limited scope
Using Callbacks
• Use *_add_callback() to add a callback to the desired item
– window_add_callback(0,"create","setupinfo");
• Use *_remove_callback() to remove a callback
– window_remove_callback(0,"create","setupinfo");
Document Callbacks
• Document callbacks can trigger actions when:
– User copies, cuts, pastes, deletes, inserts, or modifies content
– The document is saved or closed
– A table operation is done
– The document is printed
– Various other things (see online help)
Document Callback Example
• Auto-fill an attribute value when element is inserted
– uses insert_tag_after callback
function remark_add_user(doc,tag,oid,op) { if ((op == 1) && (tag == "remark")) { modify_attr("role",username()); }}
doc_add_callback(current_doc(),'insert_tag_after',\ 'remark_add_user');
Window Callbacks
• Window callbacks can trigger actions when:
– A window is created or destroyed
– A window gains or loses focus
– The caret moves
• Some window callbacks use window_add_callback(), others use window_set()
Window Callback Example
• Reports window information when window is created
function showinfo(win) { response("Window ID=" . win . ", doc ID=" .\ window_doc(win));}
window_add_callback(0,"create","showinfo");
Session Callbacks
• Session callbacks can trigger actions when:
– drag and drop operations happen
– the session ends (quit Arbortext)
Session callback example• Lets user control handling of dropped files using SHIFT
and CTRL keysfunction drop_handler(path,num,count,flags) { local $ext = tolower(substr($path,length($path)-3)); if ($ext == "xml") { if ($flags & 0x0001) { # SHIFT-drag # insert content inline execute("read " . $path); } else if ($flags & 0x0002) { # CTRL-drag # insert Xinclude reference $xi = "<xi:include href='" . $path . "'/>"; insert($xi); } else { execute("edit -newwindow " . path); } }}
session_add_callback("drop_file",drop_handler);
Dialog Item Callbacks
• Used to attach behaviors to dialog items
– Button clicked
– Listbox selection changed
– Checkbox checked or unchecked
– etc.
Dialog Item Callback Example
• Debug message for button
function buttoncb(win,dlgitem,event) { response("You clicked " . \ dlgitem_get($win,$dlgitem,"label"));}
dlgitem_add_callback($win,"MyButton",buttoncb);
Timer Callbacks
• Use a timer callback to make something happen after a specific delay, or repeatedly
Timer Callback Example
• Add autosave every 5 minutes
function autosave(doc) { doc_save(doc);}
timer_add_callback(30000,'autosave',current_doc());
Hooks
• Some overlap with callbacks, but hooks can respond to some things callbacks can’t
– Change tracking events
– Formatting stages (print composition)
– Import/Export
– Preference changes
Hook Example
• Return concurrent Print Composer License to server immediately after printing
function returnlicense() { if (license("PrintPublishing")!=0) { license_release("PrintPublishing"); }}
add_hook(printcompletehook,returnlicense);
Integrating ACL Code with Applications
• ACL code generally goes in the custom directory
– put in $EPIC\custom\...
OR
– set APTCUSTOM environment variable to the location
• e.g. shared configuration on a server
Where to Put ACL Scripts
• Where you put it determines when it runs
– custom\init: runs once when Arbortext starts
– ~/.pubrc (UNIX) or file pointed at by %APTRC% (Windows): User-specific startup script, runs once at startup
– custom\editinit: runs each time you open a document for editing
– custom\doctypes\{yourdoctype}\{yourdoctype}.acl: runs once when the doctype is referenced during a session
– custom\doctypes\{yourdoctype}\instance.acl: runs each time a document of that type is opened
– {document_directory}/{docname}.acl: document-specific, runs when you open {document_dir}/{docname}.xml
Where to Put Non-startup ACL Scripts
• For scripts that don’t need to run at startup, put them in custom\scripts to make them available
– use source {filename} to run from command line or another script
– use require {packagename} to load scripts in a package file from another context
• Gotcha: avoid circular references:
foo.acl:
package foo;require bar;
bar.acl:
package bar;require foo;
Customizing Arbortext Features• Much of Arbortext’s functionality is implemented as
ACL scripts
• By customizing those scripts you can customize how Arbortext works
– Make modified copies of the scripts in custom/scripts. These will override the default scripts.
– DON’T MODIFY ORIGINAL SCRIPTS IN $EPIC/packages or other install subdirectories
• You might want the originals back someday
• You might break something else and have to revert
• Your changes will get blown away next time you upgrade
Using alias Command to Customize
• You can use the alias command to change Arbortext’s definition of certain commands
alias FilePrint { customPrint(); }
Using ACL with Other Custom Code
• You can use ACL to interact with other custom code
– Java
– JavaScript
– VBscript & COM objects
ACL and Java
• Access static methods using java_static()
• Create instances with java_constructor()
• Execute instance methods with java_instance()
• Exchange array data with java_array_to_acl() and java_array_from_acl()
Accessing Java Fields
• No way to access java fields (properties) directly, but you can use reflection
function getJavaField(object,fldname) { class = java_instance(object,"getClass"); field = java_instance($class,"getField",fldname); value = java_instance($field,"get",object); valuestr = java_instance($value,"toString"); java_delete($class); java_delete($field); java_delete($value); return valuestr;}
ACL and JavaScript
• Use js_source() function to execute a JavaScript file
• The source command can also run JavaScript files
• Use javascript() function to execute a string of JavaScript code and return the value to ACL
# make array of day names
split("S,M,T,W,Th,F,S", $daynames, ",");
# use javascript to get today’s day of week
$dow = javascript("d = new Date(); d.getDay();") + 1;
# +1 because JS is zero-indexed but ACL is one-indexed
response("Today is $daynames[$dow]");
ACL and VBScript/COM
• Use source command to execute a VBS file
• com_*() functions provide access to COM objects
# start Excel
$excel = com_attach("Excel.Application");
$ex_wbs = com_prop_get($excel,"Workbooks");
# open our data file
$result = com_call($ex_wbs,"Open","C:\\temp\\data.xls");
# get value of cell A5
$mywb = com_prop_get($excel,"ActiveWorkbook");
$mysheet = com_prop_get($mywb,"Worksheets",1);
$mycell = com_prop_get($mysheet,"Cells",5,1);
$value = com_prop_get($mycell,"value");
# clean up objects with com_release()...
Running ACL Code From the Command Line
• You can run Arbortext in “batch” mode by giving it code on the command line
• Use the –c parameter to give it a string of ACL
• Use the –b parameter to suppress windows (batch mode)
epic –b –c “print composed; quit” foo.xml