This guide describes how to enable software applications for use with the Adept task learning system. In short, an Adept-enabled application must provide instrumentation to log steps that are performed by the user within the application along with corresponding automation for subsequently recreating those steps when learned procedures are executed. The current APIs in Adept support interaction with Java software only.
Adept supports interactions with both individual client applications and suites of complementary client applications, with the latter resulting in the learning of multi-application procedures.
The key requirement for enabling a client application to work with Adept is the creation of an action model. The action model specifies the application's operations in a form suitable for task learning. Each action in the action model has a name and parameters (input and output), and represents a single conceptual operation within the application.
The action model that an application presents to Adept fulfills several important roles. 1) The elements of the action model constitute the building blocks for task learning. As such, the design of the action model significantly affects learning and generalization. 2) Actions in the action model are executed from learned procedures; hence the action model serves as an API between Adept and the application. 3) The action model specifies the level at which the user views and manipulates learned procedures. As such, the action model should be defined in terms to match the users' conceptualization of their interactions with the application.
A sequence of actions is modeled as a dataflow, where the effects of executing an action are characterized by its inputs and outputs. In a strict dataflow model, input and output parameters must be distinct (i.e., a parameter cannot be both an input and an output) and all relevant state changes resulting from executing an action must be captured by the action outputs.
A core assumption in the dataflow model is that data is immutable: it is produced (output) and consumed (input), but it is not changed. This is similar to the notion of pipes in Unix, where the output of one command is passed in as input to the next. Adept is thus best-suited to applications where actions involve data passing between actions—for example, scientific workflows that process and analyze data. It is not designed for applications better captured through a state-based model, where actions primarily involve changes to the state of objects or the environment. For example, a mobile robot whose actions include carrying objects and moving them between rooms would be natural to model in terms of state changes to its current location and to what it is carrying. Modeling such actions using pure dataflow is problematic because there is no way to specify a state change; only input and output of data objects may be expressed.
Adept is a programming by demonstration or learning by demonstration system. In systems of this type, applications provide instrumentation that notifies Adept of executed user actions. Thus, Adept learns from user actions as the user normally operates applications, with the only added requirement being that the user indicate the start and end of the demonstration. The resulting sequence of actions is called the demonstration.
The action model specifies the actions for capture as well as their parameters. These actions are collected and passed to the Adept learning component, which generalizes them into a parameterized procedure that may be subsequently executed, with the same or with different parameters. Capturing and delivering these instrumentation notifications is one principal task involved in enabling Adept for a client.
Adept provides a user interface for executing learned procedures. This invokes Adept's execution component on the procedure, which sends automation requests to the application to execute the individual actions contained within the procedure.
The client application services these requests by executing its normal semantics for that action, and then returning the values for any output parameters of that action. Listening for and responding to automation requests is the other principal task involved in enabling Adept for a client.
LAPDOG is the task learning component of Adept. LAPDOG creates learned procedures from actions demonstrated by the end user and collected by instrumentation in the client applications.
Lumen is the execution component of Adept. It executes learned procedures on demand from the end user.
The Procedure Library is a data store on the client file system for persisting learned procedures. It resides in the platform-specific location that is conventional for storing application data.
The JMS Spine Server is a message-passing service that connects all Adept components. It utilizes Java Messaging Service (JMS), providing a server that conveys all message traffic in the system.
The Bridge is an API layer that provides access to Adept functionality for learning, executing, and visualizing procedures. It also insulates client applications from the details of JMS message passing.
Adept Control is invoked by the UI to start several Adept components and provides some core facilities used by Adept applications.
The Adept UI provides the user interface for Adept. The UI supports control of learning sessions, initiation of the execution of learned procedures, and management of procedures in the Procedure Library.
The Procedure Editor allows Adept users to view, edit and create procedures.
Image Loader and Novo are simple Java applications that are Adept enabled, providing
a sample application illustrating instrumentation and automation for an Adept client,
and operation of the Adept UI as shown in the User Guide.
The specifics of the action model significantly affect the generalizations that are performed. To discuss this, we use a simplified notation for actions in the action model, suppressing some implementation details. In demonstration examples, to highlight dataflow, input arguments are preceded by a "+"; while a "-" precedes output arguments. Similarly, in procedure examples, input and output parameters are designated by "+" and "-", respectively. Variables in procedures, including parameter variables, are designated with a leading dollar sign. Argument types are omitted unless typing is significant to the example, in which case they are notated as ":TypeName" after the action argument. The procedure resulting from learning is notated as
ProcedureName(parameters) { action1(arguments) action2(arguments) ... actionN(arguments) }Action models specify the action name as well as the name and mode (i.e., input or output) of each parameter. For example:
Convert(+Infile, +Format, -Outfile) Delete(+File) GetCreationDate(+File, -Date) List(+Directory, -FileList)
deleteFile(+"MyFile.txt")From this demonstration, Adept can learn a parameterless procedure that always deletes the file "MyFile.txt". However, the true power of Adept is to learn procedures with parameters, such that the procedure can be applied to a range of tasks similar to the demonstrated task, as opposed to only that specific, verbatim task. The process by which parameters are created and associated with demonstrated values is called parameter generalization.
For the action model
Convert(+Infile, +Format, -Outfile) Delete(+File) GetCreationDate(+File, -Date) List(+Directory, -FileList)consider the following execution trace for a demonstration within a file system:
Convert(+manual.pdf, +"HTML", -manual.html) GetCreationDate(+manual.html, -2009-03-23)LAPDOG generalizes a demonstration by converting the arguments in the demonstration to variables in the learned procedure. Further, LAPDOG analyzes the dataflow in the demonstration, looking for support relationships—cases in which an output of an action is used as an input in one or more subsequent actions. In a support relationship, the supporting argument and all supported arguments are replaced or co-designated with a common variable in the learned procedure:
Convert($Infile, "HTML", $File) GetCreationDate($File, $Date)The effect of this co-designation is to force all support-related inputs and outputs to have the same value when the learned procedure is executed.
This example illustrates the basic generalization performed via learning by demonstration. Values in the sequence of demonstrated actions are replaced with variables; this is termed variablization. If no support is found for an argument, then this is made an input parameter to the learned procedure, obliging the user to provide it upon execution. This process of support analysis and variablization is termed parameter generalization.
Limiting variablization by designating action model parameters as ungeneralizable is possible. Such parameters are never variablized but rather remain as constants in the learned procedure. For example, if the Format parameter of the Convert action is made ungeneralizable, learned procedures containing it will always convert to the demonstrated format ("HTML" in this example). Additionally, certain constants—the null value, the empty string, empty list, and empty set—are never generalized. All other values are variablized.
The action model affects the expressive power of learned procedures. For example, an alternative formulation of the action model might model file deletion as
deleteFile(+"MyFile" +"txt")which allows learning a variety of procedures with various parameterizations, controllable from the Adept UI. For example, a two-parameter version looks like
DeleteFile(+$basename +$extension) { deleteFile($basename $extension) }If the underlying system allows wildcards, one could execute
DeleteFile("*" "txt")to delete all text files in some unspecified context.
In some cases, learning will create an unintended input parameter. For example, a demonstration that increments a number by one:
add(+23 +1 -24)might generalize to
AddTwoNumbers(+$number1 +$number2 -$result) { add($number1 $number2 $result) }when the actual intent is to always increment a number by one. As an alternative, the application could make a more specific action available:
addConstant(+$number +$constant -$result)in which
$constant
is designated as an ungeneralizable parameter. This forces the corresponding value
in the demonstration to be retained as a constant:
IncrementNumber(+$number1 -$result) { add($number1 1 $result) }The tradeoff is that the application must make this additional primitive available, and the user must know to use it. Such tradeoffs are typical of action model design considerations.
Support determination depends on the types of the values involved.
For example, given a demonstration of some string-manipulation actions:
concatenate(+"the quick " +"brown fox" -"the quick brown fox") removeBlanks(+"the quick brown fox" -"thequickbrownfox") uppercase(+"the quick brown fox" -"THE QUICK BROWN FOX")the learned procedure is
P(+$string1 +$string2 -$out1 -$out2 -$out3) { concatenate($string1 $string2 $out1) removeBlanks($out1 $out2) uppercase($out1 $out3) }Because the first occurrence of "the quick brown fox" supports values in the second and third actions, they are all co-designated with the same variable. Further, because the first occurrence is computed, it need not, and should not, be an input parameter.
findZipCode(+"alice" -"12345") findZipCode(+"bob" -"12345") printZip(+"12345")the support for the "12345" in printZip is ambiguous. LAPDOG resolves such ambiguities during generalization by using the most recent supporting value:
findZipCode($name1 $zip1) findZipCode($name2 $zip2) printZip($zip2)Using distinct data types can help prevent unwanted generalizations from arising from ambiguous supports. For example, the demonstration (with relevant argument types made explicit):
findZipCode(+"bob" -"12345":Zip) findEmployeeId(+"bob" -"12345":Id) printZip(+"12345":Zip)contains two potential supports for "12345" in the last action. However, it generalizes uniquely to
findZipCode($name $zip) findEmployeeId($name $id) printZip($zip)due to the types of the action parameters.
getAllEmployeeNames(-["alice" "bob" "carl"])Collections may be designated as lists, sets, or bags.
The action model may define arguments as structures, which are arbitrarily structured data similar to records or structures in programming languages. For example, we can define a structure to represent an employee, containing their first name, last name, and employee ID:
findEmployee(+12345 -<firstName="Alice" lastName="Ames" id=12345>)The elements of a structure are named. Each element may be of any type, including lists, sets, and other structures. Supports and variablization are computed both for the structure itself, and for each element. Like any other value, one structure can support another in its entirety:
findEmployee(+12345 -<firstName="Alice" lastName="Ames" id=12345>) printEmployee(+<firstName="Alice" lastName="Ames" id=12345>)generalizes to:
findEmployee($id $employee) printEmployee($employee)Structure elements can support other values. The following demonstration of constructing a full name from first and last names:
findEmployee(+12345 -<firstName="Alice" lastName="Ames" id=12345>) concatenate(+"Ames" +"," -"Ames,") concatenate(+"Ames," +"Alice" -"Ames,Alice")generalizes to:
findEmployee($id $employee) concatenate((mapGet $employee "firstName") "," $string1) concatenate($string1 (mapGet $employee "lastName") "Ames,Alice")Note the generalization of the argument "Ames" to the function
(mapGet $employee "firstName")
. This means that the element named "firstName" of the structure associated with $employee is used as the argument of the action. The functional notation is internal to Adept procedures; in the Adept UI, the mapGet
appears as
'Field firstName in $employee'.
Besides structures, the accessor functions first($List)
and last($List)
are available for lists. Thus, a demonstration
getAllEmployeeNames(-["alice" "bob" "carl"]: list<string>) findZipCode(+"alice" -"12345") printZip(+"12345") findZipCode(+"carl" -"67890") printZip(+"67890")generalizes to:
getAllEmployeeNames($people) findZipCode(first($people) $zip1) printZip($zip1) findZipCode(last($people) $zip2) printZip($zip2)If a list has only one element, both
first($List)
and
last($List)
are valid generalizations for element references.
For example, consider an organization in which an employee typically has one supervisor,
but in unusual cases could have several. The demonstration
findSupervisorNames(+"bob" -["dan"] ) findEmail(+"dan" -"dan@xyz.com")could generalize to either
findSupervisorNames($employee $supervisorList) findEmail(first($supervisorList) $email)or
findSupervisorNames($employee $supervisorList) findEmail(last($supervisorList) $email)with equal validity. Instead of arbitrarily choosing one, in the case of such singleton lists, a third generalization is preferred by default:
findSupervisorNames($employee $supervisorList) findEmail(only($supervisorList) $email)The
only
function means: use the sole element of the list; it causes an error if
the list does not contain exactly one element. List types allow a generalization preference to
be specified as to whether to use only
, first
, or last
in the case of singleton lists. For other collections (sets
and bags), only
is always used.
In addition to accessor functions, constructor functions exist, which support a list, set, or structure by constructing it from individually supported parts. For example, the demonstration
inputEmployeeName(-"Bob" -"Baker") generateNewEmployeeId(-67890) createNewEmployee(+<firstName="Bob" lastName="Baker" id=67890>)generalizes to
inputEmployeeName($firstName $lastName) generateNewEmployeeId($id) createNewEmployee((mapGen "firstName" $firstName "lastName" $lastName "id" $id))with
mapGen
being the Adept-internal name for the structure-constructor function;
in the Adept UI it is shown as a structured value rather than a function invocation.
Structures obey strict typing with regard to support determination. One structure value does not support another unless they are of identical types, even if the names and the values of their components exactly match.
Structures and collections may be partially supported. For example, in the demostration:
inputEmployeeName(-"Bob" -"Baker") createNewEmployee(+<firstName="Bob" lastName="Baker" id=67890>)the fields "Bob" and "Baker" are supported, but the field 67890 is not. LAPDOG by default treats the structure or collection as unsupported; in this example, the structure argument in whole would be generalized to a procedure parameter, ignoring the partial supports:
P(+$employee) { inputEmployeeName($firstName $lastName) createNewEmployee($employee) }However, a structure or collection type may specify a generalization preference permitting generalization by construction from partial supports. This permits the generalization to:
P(+$id) { inputEmployeeName($firstName $lastName) createNewEmployee((mapGen "firstName" $firstName "lastName" $lastName "id" $id)) }The resulting procedure takes only the unsupported field of the structure as an input, rather than the entire structure, and constructs the structure from it and the supported fields. The user of the procedure may now enter one field value instead of three when running the procedure.
Using the generalize-by-construction preference can lead to many input parameters for the procedure. This is particularly true for collections, which may have an unlimited number of elements. For this reason, this preference allows an optional limit on the number of inputs allowed (default is to allow unlimited inputs); if this limit is exceeded, the structure or collection in whole is made a procedure parameter instead.
getAllEmployeeNames(-["alice" "bob" "carl"]:listHere, list values are delimited with [ ], and the list type is made explicit. The demonstration contains a pattern of repetitions of the findZipCode and printZip actions. The first argument to findZipCode refers in turn to each element of the list supported by the first action in the demonstration. Moreover, the support relationships within the repeated actions are consistent--the output of findZipCode is used consistently as the input to the subsequent printZip. This action pattern forms a valid iteration over a list and generalizes to a procedure containing a loop:) findZipCode(+"alice" -"12345") printZip(+"12345") findZipCode(+"bob" -"12345") printZip(+"12345") findZipCode(+"carl" -"67890") printZip(+"67890")
getAllEmployeeNames($people) for $person in $people do findZipCode($person $zip) printZip($zip) odLAPDOG is constrained to finding loops over collections (lists, sets, bags) that are explicit within the demonstration. In the case of lists, the elements of the list be iterated over in order, and the sequence of repeated actions (the loop body) must be identical except for the references to (including functions over) those elements.
LAPDOG can detect parallel iteration over multiple lists if the lists are of the same length and their elements are accessed consistently within the loop body. Loops may also generate list values that support subsequent lists. The outputs of a repeated action that form a loop body form a list that can be used to support list inputs later in the demonstration. Here is a richer example of loop learning, using abstract actions:
A(-[1 2 3]) B(-[a b c]) C(+a +1 -a1) C(+b +2 -b2) C(+c +3 -c3) D(+[a1 b2 c3])This generalizes to
A(-X) B(-Y) for U in X, V in Y building Z do C(+U +V -W) W accumulate Z od D(+Z)
selectFile(-"Report") renameFile(+"Report" +"Report20101215")would be generalized to a procedure requiring both the original and new names to be input:
RenameFile(+$oldfile +$newfile) { selectFile($oldfile) renameFile($oldfile $newfile) }However, given an appropriate library of information-producing actions, LAPDOG can infer the actions necessary to complete the dataflow. For example, given actions to retrieve today's date and to concatenate strings, the demonstration can be extended to:
selectFile(-"Report") todaysDate(-"20101215") appendString(+"Report" +"20101215" -"Report20101215"> renameFile(+"Report" +"Report20101215")and a more general procedure that captures the rationale behind the new name can be learned:
RenameFile(+$oldfile -$newfile) { selectFile($oldfile) todaysDate($date) appendString($oldfile $date $newfile) renameFile($oldfile $newfile) }There are two categories of information-producing actions: completers and supporters. Completer actions are just like regular actions except that they have no instrumentation counterpart: they cannot be demonstrated and can only be inferred. Supporter actions are both demonstrable (and hence must be instrumented) and inferrable for dataflow completion. Information-producing actions are considered by LAPDOG as part of the parameter generalization process as it searches for a maximally general procedure.
There is another category of actions that can be used to infer implicit dataflow: context actions. They are not demonstrated, nor are they added by LAPDOG, but may be proactively inserted by the application into the demonstration for possible use in dataflow completion. Such actions are intended to capture properties of the environment within which the demonstration is performed. For example, context actions might be used to provide information such as the current user, the user's home directory, the operating system version, and the current date and time. If the context information is used to support actions in the demonstration, the learned procedure retains the corresponding context actions. Otherwise, if an inserted context action does not support any dataflow, it may be removed by Adept from the learned procedure (although Adept currently does not do so) or by the user through the Editor, without affecting the procedure's behavior.
Information-producing actions and context actions serve to capture more dataflow connections between the actions of the demonstration, providing greater generalization and resulting in procedures with fewer input parameters. However, dataflow completion can be an expensive search process, so care must be taken to define only information-producing actions and context actions that are useful and to ensure their proper application through strong parameter typing.
The action model for a client application fulfills several important roles:
The action model is expressed as an XML file. For full examples, see the Image Loader action model file and the Novo action model file.
This section discusses the various semantic options for expressing actions and the data types of their arguments, as well as how to define them in the action model XML file. This is followed by design guidelines in formulating an action model for an application.
<?xml version="1.0"?> <?xml-stylesheet type="text/xsl" href="./am-to-html.xslt"?> <actionModel version="2.1.a"> <!-- Types and Actions --> </actionModel>
The action model file contains specifications for types, actions, and action parameters.
The action model contains a version designator on the actionModel
tag.
This assists the application in action model maintenance. It is also used internally by
Adept, as a part of every action and type name. This allows Adept to detect and prevent
execution of procedures using any action model version other than the one currently registered.
In addition to the various element-specific tags (described in the following subsections),
the action model may contain various
metadata
tags of the form:
<metadata key="foo" value="bar"/>Metadata enables markup of the action model to improve user understanding of the actions and types displayed in the user interface. Keys and values for each action model element are described in the following subsections. Any metadata whose key is not listed in this document is silently ignored. Metadata is optional; that is, leaving out metadata will not adversely impact Adept's functionality, only the UI display. The stylesheet am-to-html.xslt is provided to enhance action model readability. The action model version is required. It is used internally by Adept to version each element of the action model, and to detect inconsistencies across action model versions. Also, it is available to the application to help manage revisions.
The action model of an application may be split into several XML files by using
the require
tag. For example:
... <require url="./common-types.xml" /> ...
All action parameters have a data type. The typed approach to data representation is done for a number of reasons: 1) the semantics of actions are clearer, 2) strong typing can greatly enhance learning by constraining generalization and, in some cases, dramatically pruning the space of potential procedures, 3) the API is clearer, and 4) input values are more natural to enter when executing a learned procedure. There are several classes of data types supported in Adept:
Adept provides several primitive data types that can be referenced immediately in an action model:
string
: A string data type built on java.lang.Stringboolean
: A boolean data type built on java.lang.Booleaninteger
: An integer data type built on java.lang.Integerreal
: A real data type built on java.lang.DoubleThe following example, taken from the Novo action model, illustrates referencing a primitive type in an action declaration:
<action id="dispenseShapes"> <inputParam id="numShapes"> <typeRef typeId="integer"/> </inputParam> ... </action>
Note that if a primitive type is used by multiple action parameters that have different semantics, Adept will not know about the semantic differences between the usages of the type and may generalize values that are semantically unrelated. For example, if an action parameter is declared that represents a person's age and uses the primitive type integer
, another parameter intended to represent the number of days in a month also declared as an integer
will result in an incorrect generalization if a value such as "30" is provided for both parameter values, regardless of whether the parameters are declared in the same action. In order to prevent this, it may be necessary to use "application types" where semantic differences exist.
Application types are used to define custom semantic types in a client application. Generally an application type is built on a simple primitive type from Java, but any class fulfilling the following requirements may be used:
java.lang.String
by providing a
toString()
method.
java.lang.String
by providing either
java.lang.String
and returns an object of this class.
equals()
method must be consistent with the support relationships
and generalizations expected. Essentially this means that if two objects are equals()
and of compatible Adept types, designating them with the same
variable in the learned procedure is valid.
com.sri.pal.CustomTypeFactory
and a default implementation ToStringFactory
to manage converting between string and object representations of custom types,
though no examples of their usage are provided.
The following type declarations declare application types built on java.lang.Integer
and java.lang.String
respectively. These types are semantically separate from the built-in integer
and string
primitive types respectively, even though the underlying representation of the data values are identical with the primitive types.
Note that application types cannot be declared with the same name as a built-in primitive type, such as integer
.
<type id="myInteger"> <description>An integer value</description> <custom> <javaType>java.lang.Integer</javaType> </custom> </type>
<type id="myString"> <description>A string value</description> <custom> <javaType>java.lang.String</javaType> </custom> </type>
Several application types that reference the same javaType may be specified. For example, an application using postal codes may not want to confuse those with other strings:
<type id="ZipCode"> <description>A U.S. postal code</description> <custom> <javaType>java.lang.String</javaType> </custom> </type>
Generally, the values of distinct types do not support each other. An exception is that a hierarchy of primitive types may be specified:
<type id="ZipCodeShort"> <description>A 5-digit U.S. postal code</description> <inherit parent="ZipCode"/> <custom> <javaType>java.lang.String</javaType> </custom> </type> <type id="ZipCodeLong"> <description>A 9-digit U.S. postal code</description> <inherit parent="ZipCode"/> <custom> <javaType>java.lang.String</javaType> </custom> </type>The values of child types may support the values of their direct parent type, or those of any ancestor type. Such a hierarchy enables actions that take only short, only long, or either variant of zip code as parameters.
Enumeration types allow for a fixed set of string values. Use of an enumeration type will cause the Adept UI and Editor to display values of that types as drop-down lists. For example:
<type id="SizeEnum"> <description>Enumeration of size types</description> <enum> <value>Small</value> <value>Medium</value> <value>Large</value> </enum> </type>
Data types can be aggregated into sets, lists, or bags, which have an arbitrary number of elements, all of the same type. For example:
<type id="set(string)"> <description>A set of strings</description> <set> <ref typeRef="string"/> </set> </type>This type describes a set with elements of type string. In general, the element type may be any other type.
Data types can also be aggregated into structures. A structure has a fixed number of named elements of varying types. An element type may be any other type. For example:
<type id="file"> <description> A file on a file system, consisting of a directory, a base file name, and an extension indicating file type </description> <struct> <ref name="directory" typeRef="directory"/> <ref name="file name" typeRef="basefilename"/> <ref name="extension" typeRef="extension"/> </struct> </type>This type describes a reference to a file, consisting of a containing directory, the base name of the file, and its file extension.
A structure can be made opaque. For example:
<type id="coordinate" opaque="true"> <description> An x,y coordinate </description> <struct> <ref name="x" typeRef="real"/> <ref name="y" typeRef="real"/> </struct> </type>Making a structure opaque affects parameter generalization as follows:
As noted earlier, certain types permit specifying generalization preferences for their values.
List types allow specifying a preference for generalizing element access of a singleton value
to either only($List)
, first($List)
, or last($List)
,
with only
being the default:
<type id="sortedList"> <description/> <list> <generalizeSingleton method="first"/> <ref typeRef="string"/> </list> </type>Structure and collection types may specify a generalization preference for unsupported and partially supported values:
<type id="aStruct"> <description/> <struct> <generalizeUnsupported preference="construct" maxInputs="2"/> <ref typeRef="string" name="field1"/> <ref typeRef="string" name="field2"/> <ref typeRef="string" name="field3"/> </struct> </type>The default preference is
parameterize
, which means to generalize by making the entire value
a parameter to the procedure rather than constructing it from its parts. If
maxInputs
is not specified, procedure parameters will be added for each element of the collection or structure, regardless of number.
| Key | Value | Applicable to | Description |
|---|---|---|---|
pluralName |
string |
all types |
The plural of the type (e.g. "emails"). |
aName |
string |
all types |
The type preceded by its indefinite article (e.g. "an email"). |
thisName |
string |
all types |
The indefinite form of the type (e.g. "this email"). |
editor_disallowAskUser |
boolean |
all types |
Disables the |
editor_disallowFixedValue |
boolean |
all types |
Disables the |
editor_disallowExistingValue |
boolean |
all types |
Disables the ability to reference other variables on terms of this type (see the section "Rewiring Values in Steps" in the Editor Guide). |
An action consists of
<action id="OpenImageFile"> <description> Opens an image file in the Image Viewer. </description> <metadata key="name" value="Open image in ,#file"/> <metadata key="fancyName" value="Open ,#file"/> <metadata key="icon" value="./fapps/image.jpg"/> <inputParam id="file"> <description> The image file to open </description> <typeRef typeId="file"/> </inputParam> <outputParam id="image"> <description> Identifies the in-memory image currently being manipulated </description> <typeRef typeId="imageID"/> </outputParam> </action>The identifier is used internally to identify the action. If no naming metadata is supplied, the identifier is used in user interfaces to depict the action as well.
The input parameters describe the data needed by the action to execute. The output parameters describe the data that is computed or produced by the action. Put another way, they describe the dataflow of the action. An action with no outputs is permissible. This indicates that no relevant dataflow for the action exists, i.e., there are no significant data side effects. An action may also have no inputs. For example, an action to report the current date might have as output parameters the year, month, and day.
Input parameters may be specified as ungeneralizable.
This means that the value used for that parameter in a learned procedure
will be identical to the corresponding value in the demonstration from which it is learned.
For example, with the class="constant"
attribute on the "Format" parameter,
the following action definition specifies that the format of the file produced by this action
in a learned procedure will always be the same as the format observed
in the demonstration.
<action id="Convert"> <description> Converts a file to the given format, producing a file with that extension. </description> <inputParam id="Infile"> <description> The file being converted </description> <typeRef typeId="string"/> </inputParam> <inputParam id="Format"> <description> The format the file is converted to. </description> <class class="constant"/> <typeRef typeId="string" /> </inputParam> <outputParam id="Outfile"> <description> The converted file. </description> <typeRef typeId="string"/> </outputParam> </action>
Allowable keys and values for action metadata are shown in the following table.
The action model schema expects that all metadata tags be located
after the description
tag.
| Key | Value | Applicable to | Notes |
|---|---|---|---|
name |
string |
all actions |
A short descriptive name for the action, suitable for display for the user (e.g. "Open document") |
fancyName |
string |
all actions |
A descriptive name for the action that may contain references to action parameters of the form ,#paramName, (e.g., "Copy ,#from, to ,#to"). The paramName must either be followed by a comma, or must appear at the end of the string. When displaying the demonstration, each parameter reference is replaced with the corresponding demonstrated value. When displaying the learned procedure, each parameter reference is replaced with the corresponding variable or expression assigned by LAPDOG. |
icon |
resource |
all actions |
A path to a resource that is an icon file that depicts this action (e.g., "./resources/image.jpg"). Note that this file must be available on the classpath. |
Allowable keys and values for action parameter metadata are shown in the following table.
| Key | Value | Applicable to | Notes |
|---|---|---|---|
userDescription |
string |
all actions |
A user-oriented description of the parameter. |
<action id="DefaultFolder" category="completer"> <description> Add this action automatically when the designated default directory is demonstrated. This means the learned procedure has one less parameter, but one more action. </description> <metadata key="name" value="Use default Imageloader folder"/> <metadata key="fancyName" value="Use default Imageloader folder"/> <metadata key="icon" value="./fapps/image.jpg"/> <outputParam id="defaultFolder"> <description> The directory designated as the default folder for Imageloader </description> <typeRef typeId="directory"/> </outputParam> </action>With this action, the system can infer support for the use of this default directory in the demonstration and add an instance of this action to the learned procedure to support that value. The net effect is that the default directory does not become a procedure parameter.
Sharing dataflow between two applications A and B means that the output of an action of A
is used as an input to an action of B. As discussed in the
Support Relationships section, this means that the types of this output
and input must be compatible. This is a problem because by default the types of different action models
are disjoint. However, Adept permits types in different action models to be specified
as equivalent to each other, which makes them compatible for determining support relationships.
For example, if application A's action model defines a type url
:
<type id="url"> <description>A url such as http://www.yahoo.com</description> <!-- Allow URL params from application B to support and be supported by our URLs --> <equivalentTo>B.url</equivalentTo> <custom> <javaType>java.lang.String</javaType> </custom> </type>the
equivalentTo
specification permits the
url
type of application B to support and be supported by the
url
type of application A. The javaType of equivalent types
must be identical.
The system need not necessarily be directly instrumented/automated at a human-friendly level if a well-defined mapping exists between the system's native instrumentation and the higher-level conceptualization of actions used in the action model. For example, an email application might use an internal operation SendMail that handles sending a new message, replying, and forwarding all in one API, by providing the appropriate parameters for to and cc lists, message bodies, etc. An action model designer might prefer separate actions for Send, Reply, and Forward. This is accomplished by capturing SendMail calls for instrumentation, determining which of the three actions apply given the parameters of the SendMail, and appropriately mapping the parameters to the action's parameters. The reverse would be done for automation.
java.lang.String
by providing a
toString()
method.
java.lang.String
by providing either
java.lang.String
and returns an object of this class.
equals()
method must be consistent with the support relationships
and generalizations expected. Essentially this means that if two objects are equals()
and of compatible Adept types, designating them with the same
variable in the learned procedure is valid.
Another alternative is to model the complex object as a structure. This is particularly appropriate if the object has visible data fields, as opposed to being a "black box" for which only the internals of the client application can access its components.
The dataflow model can pose problems in those cases in which an action changes a parameter or an attribute of a parameter, rather than producing a distinct output. If expressing the operations of a client application in terms of dataflow is infeasible, then the generalizations performed by task learning will be of limited value.
In order to learn loops at all, at minimum a list, set, or bag must appear in a demonstration as the output of an action. If iteration is important for an application, then its action model must provide the aggregates to be iterated over.
To learn a loop over a list of values, those values must appear in the demonstration in the same order in which they appear in the list. This may affect decisions on whether to model certain parameters as lists versus sets or bags.
This section explains how to create a module that allows a client application to exchange instrumented events and automated actions with the Adept task learning system. Conceptually, we will be using the Bridge APIs to make a connection between the client application and Adept. The JMS messaging spine used by Adept provides the communication between the two.
The APIs referenced in this section are implemented in the various files in the lib
directory provided in the Adept installation. All files in this directory must be on the classpath
of the client application to compile and execute it.
Numerous excerpts from sample Adept-enabled applications are provided. The complete source code of the Image Loader and Novo sample applications is provided as well.
com.sri.tasklearning.util
in its startup code, before any other Adept API methods are executed:
void LogUtil.configureLogging(String moduleName, Class> baseClass) Configure the log4j system with a config file. A series of config file names are searched for in the current directory, followed by looking for resources on the classpath relative to baseClass. Config files can be named moduleName.ext, where ext is one of xml, properties, or ini. As a fallback, log4j.ext will be searched for. If all else fails, this will try to use org.apache.log4j.BasicConfigurator. Parameters: moduleName - base name of the config file to be searched for baseClass - class to search for resources relative to
For example, the Image Loader configures logging as follows:
// use log4j.xml as logging config file. LogUtil.configureLogging("imageloader_log", Imageloader.class); LogUtil.configureLogging("log4j", AdeptShell.class);
This initialization allows a configuration file imageloader_log.xml
to be included as a resource
in its JAR file, or placed in its current working directory.
First, we first create a class that is responsible for connecting to Adept and loading the action model. A typical class definition might look something like this:
import com.sri.pal.ActionModel; import com.sri.pal.Bridge; import com.sri.pal.PALException; /** * AdeptWrapper acts as an interface to Adept, allowing access to functionality * such as instrumentation, type instantiation, and setting up the initial state * of Adept by loading an action model. */ public class AdeptWrapper { // Objects related to loading the actionmodel and executing // procedures public static final String NAMESPACE = "imageloader"; public static final String APP_NAME = "imageloader"; public static final String XML_PATH = "imageloader_model.xml"; private static ActionModel actionModel; private static AdeptExecutor executor; public static Bridge bridge; private static boolean bridgeStarted = false; public AdeptWrapper() { loadActionModel(); } ... }The
NAMESPACE
constant defines the name space in which the
type and action names for this application's action model reside.
The APP_NAME
constant identifies the application.
The XML_PATH
is used to read the action model, which is done by
the lone constructor.
The loadActionModel()
method is used to load the action model from an XML file.
A simple implemenation might look like this:
private void loadActionModel() { try { Bridge bridge = Bridge.newInstance(APP_NAME); actionModel = bridge.getActionModel(); URL url = SomeClass.class.getResource(XML_PATH); executor = new AdeptExecutor(); do { bridge = Bridge.newInstance(APP_NAME); actionModel = bridge.getActionModel(); Settypes = actionModel.load(url, NAMESPACE); for (ActionModelDef type : types) { if (type instanceof ActionDef && !(type instanceof ProcedureDef)) { actionModel.registerExecutor( (SimpleTypeName)type.getName(), executor); } } } while (!Bridge.isTaskLearningRunning()); } catch (PALException e) { log.error("Failed to load action model: ", e); } }
In this example, the class loader getResource()
method
is used to access the action model file.
This means that the action model should be either in the application's classpath
or contained in the module's jar file.
Another option is to load the model from a common directory location.
Next we call the actionModel.load()
method passing in our XML file URL, the ActionExecutor (more on this later),
and the application's namespace. The net effect is to connect to Adept via its Bridge interface
and to initialize the action model from a file.
The application is responsible for indicating to Adept that it is responsible for
executing each of its primitive actions. This is done by the loop after
actionModel.load()
by calling actionModel.registerExecutor()
for each action in the loaded action model.
One shortcoming of this example is that it fails if Adept is not executing. The application may require a standalone mode, in which it operates normally but without any Adept interactions. The following example illustrates how to do so.
/** * Attempts to establish the Image loader as a client to Adept. If it fails * Image loader can still continue running. */ public void loadActionModel() { if (bridge == null) { Thread t = new Thread() { public void run() { try { Bridge.setConnectAttemptDuration(15); Bridge.setConnectRetryInterval(5); URL url = AdeptWrapper.class.getResource(XML_PATH); executor = new AdeptExecutor(); do { bridge = Bridge.newInstance(APP_NAME); actionModel = bridge.getActionModel(); SetThe technique used is to time out if repeated connections to the Bridge fail. This example attempts a connection every five seconds, giving up after 15 tries.types = actionModel.load(url, NAMESPACE); for (ActionModelDef type : types) { if (type instanceof ActionDef && !(type instanceof ProcedureDef)) { actionModel.registerExecutor( (SimpleTypeName)type.getName(), executor); } } Thread.sleep(5000); } while (!Bridge.isTaskLearningRunning()); bridgeStarted = true; } catch (PALException e) { log.error("Failed to load action model: ", e); } catch (InterruptedException e) { log.error("Thread interrupted: ", e); } } }; t.start(); } }
loadActionModel()
should be executed when the client application starts executing.
Adept must be running at this time in order to connect to it.
One style is to implement an event loop that responds to events generated by the application that correspond to user operations, dispatching on individual events. This is appropriate when a convenient event loop already exists in the client application, or if its interface to Adept resides in another process or thread. For example, an event capturing the opening of a URL by the Mozilla Firefox browser is represented by the following action:
<action id="OpenUrlEvent"> <description>Firefox open url event</description> <metadata key="name" value="Open URL"/> <metadata key="fancyName" value="Open: ,#url"/> <metadata key="icon" value="./fapps/icon_firefox.png"/> <inputParam id="url"> <description>URL</description> <typeRef typeId="url"/> </inputParam> </action>Instrumentation code must extract the relevant data from the event and invoke the correct action with the Adept system. Here is an example of the event handling:
if (event instanceof IOpenUrlEvent) { IOpenUrlEvent openUrlEvent = (IOpenUrlEvent) event; String url = openUrlEvent.getUrl(); TypeName openUrlName = new TypeName("OpenUrlEvent", NAMESPACE); ActionDef openUrlActionDef = (ActionDef) actionModel.getType(openUrlName); try { // Create an action invocation with no parent invocation, // and a single URL parameter. ActionInvocation openUrlAction = openUrlActionDef.invoke(null, url); // Output parameters would be set here, if there were any. // Indicate action has completed. openUrlAction.setStatus(ActionInvocation.Status.ENDED); } catch (PALException e) { log.error("Failed to invoke open url action", e); } }The IOpenUrlEvent is passed from the application to Adept. It has one input parameter, namely the URL that was opened. It does not have any direct relationship with the action model; the instrumentation code must map it to the OpenUrlEvent action.
First, we dispatch on the event's class. Next, we cast our event as the correct type. Then, we extract the URL parameter from the event. Finally, we create an action definition and invoke it. The action definition is obtained from the action model by calling the getType()
method. Note that the type name is a combination of the event name from our XML file and the namespace used above. After we have our action definition, we call the invoke()
method, passing in the URL. Note that this method takes a variable number of arguments and that order is important. The order of the arguments should correspond to the order of the corresponding parameters in the action model. After invoking the action, we need to set the status to ENDED
.
A second alternative is to intersperse code, similar to the above, for each action throughout the client application at the point at which its execution occurs.
A third alternative requires invoking a utility method such as the following at each point in the client application at each point of action execution.
/** * Takes an action name along with its arguments and invokes it so that * its instrumentation is collected by Adept. Input variables should come * before output variables. This function does not accept null arguments or fewer * arguments than what is defined for the given action in the actionmodel. */ public void instrumentAction(String name, Object... argVals) { if(!bridgeStarted) { log.warn("Could not instrument {}: learning not running.", name); return; } ActionDef def = (ActionDef) getType(name); ActionInvocation action = null; // set input arguments Object[] inputs = new Object[def.numInputFields()]; int index = 0; for (; index < def.numInputFields(); index++) { inputs[index] = argVals[index]; } try { action = def.invoke(null, inputs); } catch (PALException e) { log.error("Action invocation failed: ", e); return; } // set output arguments for (; index < argVals.length; index++) { action.setValue(index, argVals[index]); } action.setStatus(ActionInvocation.Status.ENDED); }The client application calls this method with the appropriate arguments at each point that instrumentation is to be captured. A call looks like:
if (instrumentation) { ... someObject.instrumentAction("SelectImageFiles", dirPath, formatSet, fileSet); }
All of these instrumentation techniques rely on the com.sri.pal.ActionDef
class and its invoke()
method:
/** * Creates a new invocation of this action definition. * parent - always null when creating instrumentation * args - input arguments, in the order in which they * appear in the action model */ public ActionInvocation invoke(ActionInvocation parent, List extends Object> args) throws PALException;An
ActionDef
represents all the information pertaining to an action that is
specified in the action model for it. The invoke()
method creates an invocation,
indicating to Adept that the client has executed that action with the given input parameters.
If the action has outputs, those values are filled in one at a time with the setValue()
method. The first output goes after the last input, and must be in the order in which they
appear in the action model. Finally, the status of the action is updated, indicating to Adept
that its execution is complete. This completes instrumentation for this action.
com.sri.pal.TypeDef
class.
To use several API methods, the client application needs to map the type names specified as IDs in the action model to the object representing that type. The following helper method may be used to do so for both data types and action types:
/** * Requests a type from Adept given a type name. */ public TypeDef getType(String typeName) { return actionModel.getType(new TypeName(typeName, NAMESPACE)); }
ActionDef.invoke()
.
If the Adept type is an atomic type, then no conversion is needed. For example, if the
Adept type of an action parameter is
<type id="integer"> <description>An integer value</description> <custom> <javaType>java.lang.Integer</javaType> </custom> </type>then any
java.lang.Integer
object is valid.
To use the various structured types of Adept—sets, lists, bags, and structures—care
must be taken to create values with the proper structure. Sets, lists, and bags
can use any of the Set
or List
implementations
in the java.util.Collection
framework. For example, in this
method that creates a set of strings, a HashSet<String>
is used to represent a set of strings:
/** * Returns a Set of Strings, suitable for use as an action argument, * given the type name and a list of its elements. */ public Set<String> createStringSet(String name, ArrayList<String> args) { if(!bridgeStarted) { log.error("Could not create set {}: learning not running.", name); return null; } Set<String> setValue = new HashSet<String>(); for (String arg : args) { setValue.add(arg); } return setValue; }To create structures, the
com.sri.pal.StructDef
class is used,
which represents the information in the action model about a structure type.
The structure value is represented by com.sri.pal.Struct
.
For example:
import com.sri.pal.Struct; import com.sri.pal.StructDef; /** * Returns a Struct, suitable for use as an action argument, * given a type name and values for each structure field. * Arguments must be in the order the fields appear in * in the action model. */ public Struct createStruct(String name, Object... args) { if(!bridgeStarted) { log.warn("Could create structure {}: learning not running.", name); return null; } StructDef structDef = (StructDef) getType(name); Struct structureValue = new Struct(structureDef); for (int i = 0; i < structureDef.size() && i < args.length; i++) { structureValue.setValue(i, args[i]); } return structureValue; }Creating a set of structures is similar to creating a set of some primitive type:
/** * Returns a Set of Structs, suitable for use as an action argument, * given a type name, and a list of Struct values. */ public Set<Struct> createStructSet(String name, ArrayList<Struct> args) { if(!bridgeStarted) { log.warn("Could not create set {}: learning not running.", name); return null; } Set<Struct> setValue = new HashSet<Struct>(); for (Struct arg : args) { setValue.add(arg); } return setValue; }
To implement automation, a class is created that implements the
com.sri.pal.ActionExecutor
interface.
This interface requires
a method that executes any incoming execution requests delivered by Adept.
Adept delivers these requests in the form of an ActionInvocation
,
which is simply an action with its input parameters filled in. The execute()
method dispatches on the action name, invoking the appropriate client-application
code for each action and computing the values of any output parameters of the action.
These output parameter values are then copied into the ActionInvocation
.
When execute()
completes, Adept passes the completed
ActionInvocation
back to the requestor.
Here is the body of an example executor class. Note the idiosyncratic construction of the action names.
/** * AdeptExecutor implements the ActionExecutor interface in order to act as a * listener for when actions are executed from Adept. It handles five types of * actions from imageloader_model.xml: SaveImageFile, OpenImageFile, * ResizeImage, SelectImageFiles, and OpenNextImageFile. When it receives one of * these actions it extracts the input arguments, if any, sends the arguments to * ImageloaderGUI to execute, and then sets the output arguments generated, if * any. */ public class AdeptExecutor implements ActionExecutor { private static final Logger log = LoggerFactory .getLogger(AdeptExecutor.class); private ImageloaderGUI gui; public static final String NAMESPACE = AdeptWrapper.NAMESPACE; // Action names private static final String SAVE = "TypeName[" + NAMESPACE + ".SaveImageFile]"; private static final String OPEN = "TypeName[" + NAMESPACE + ".OpenImageFile]"; private static final String RESIZE = "TypeName[" + NAMESPACE + ".ResizeImage]"; private static final String SELECT = "TypeName[" + NAMESPACE + ".SelectImageFiles]"; private static final String NEXT = "TypeName[" + NAMESPACE + ".OpenNextImageFile]"; public AdeptExecutor() { gui = ImageloaderGUI.getInstance(); } }
Here is its execute()
method. Note the use of
gui.endInstrumentation()
and gui.startInstrumentation()
methods to disable instrumentation during execution, which simply reset and set a flag in the
client application:
@Override public void execute(ActionInvocation arg0) throws PALException { if (arg0.getParentInvocation() == null) { log.error("Action execution failed: no parent invocation. ", arg0 .getDefinition().getName()); } // make sure action demonstrations are not duplicated gui.endInstrumentation(); int size = arg0.getDefinition().size(); ArrayList<Object> items = new ArrayList<Object>(); for (int i = 0; i < size; i++) { items.add(arg0.getValue(i)); } // SaveImageFile if (SAVE.equals(arg0.getDefinition().getName().toString())) { String imageID = (String) items.get(0); Struct image = (Struct) items.get(1); String str0 = (String) image.getValue(0); String str1 = (String) image.getValue(1); String str2 = (String) image.getValue(2); String path = str0 + "/" + str1 + "." + str2; gui.saveImageEvent(new Integer(imageID), new File(path)); // OpenImageFile } else if (OPEN.equals(arg0.getDefinition().getName().toString())) { Struct image = (Struct) items.get(0); String str0 = (String) image.getValue(0); String str1 = (String) image.getValue(1); String str2 = (String) image.getValue(2); String path = str0 + "/" + str1 + "." + str2; int ID = gui.openImageEvent(new File(path)); arg0.setValue(1, Integer.toString(ID)); // ResizeImage } else if (RESIZE.equals(arg0.getDefinition().getName().toString())) { String imageID = (String) items.get(0); Integer x = (Integer) items.get(1); Integer y = (Integer) items.get(2); int newID = gui.resizeImageEvent(new Integer(imageID), x, y); arg0.setValue(3, Integer.toString(newID)); // SelectImageFiles } else if (SELECT.equals(arg0.getDefinition().getName().toString())) { String directory = (String) items.get(0); Set<String> formats = (Set<String>) items.get(1); ArrayList<String> list = new ArrayList<String>(); list.addAll(formats); ArrayList<Struct> files = gui.selectImagesEvent(directory, list); arg0.setValue(2, files); // OpenNextImageFile } else if (NEXT.equals(arg0.getDefinition().getName().toString())) { Set<Struct> structureSet = (Set<Struct>) items.get(0); ArrayList<File> files = new ArrayList<File>(); for (Struct t : structureSet) { String str0 = (String) t.getValue(0); String str1 = (String) t.getValue(1); String str2 = (String) t.getValue(2); String path = str0 + "/" + str1 + "." + str2; files.add(new File(path)); } Struct image = (Struct) items.get(1); String str0 = (String) image.getValue(0); String str1 = (String) image.getValue(1); String str2 = (String) image.getValue(2); String path = str0 + "/" + str1 + "." + str2; int imageID = gui.nextImageEvent(files, new File(path)); arg0.setValue(1, Integer.toString(imageID)); } else { log.debug("Action {} ignored.", arg0.getDefinition().getName()); } gui.startInstrumentation(); }
The application delivers instrumentation to Adept for all modeled actions that are executed, including actions that are executed from within a learned procedure. The application need not perform special processing to handle this case; Adept recognizes when an action originates from within a learned procedure and excludes that action from the demonstration, but includes instrumentation for all top-level learned procedures.
%APPDATA%
refers to):
Adept handles all details of saving procedures from its user interface. No client programming is needed to enable it.


| < Day Day Up > |
11.4. Building an Automatic Template SystemYou're probably starting to see how all these tools can be put together in really powerful ways. Most of the rest of the chapter consists of examples of building relatively real and useful new features for Emacs. You can use them as learning tools for how to build your own, and you may be able to use them as-is, or with a little tweaking, in your own daily work. The example we're about to look at is something that one of the authors developed over a decade ago to help with the tedium of creating new files in development projects where a certain amount of structure and standard documentation were always needed. Many coding and writing projects have this characteristic; each file needs some boilerplate, but it needs to be adjusted to the details of the file. Emacs turned out to be very much up to the task of automating a lot of the drudge work, and this template system has been heavily used ever since. Most of the code in this example should already make sense to you. A couple of aspects that will be explained more thoroughly in the next section about programming a major mode. In particular, don't worry too much yet about exactly what a "hook" function is, or funcall. For now it's sufficient to know that the file-not-found-hook allows us to run code when the user uses find-file to open a file that doesn't exist yet (exactly the time at which we'd like to offer our template services). Before launching into the code, it's worth looking at an example of it in action. You'd set up your template by creating a file named file-template-java at the top level of a Java project directory hierarchy, containing something like the code shown in Example 11-2. Example 11-2. file-template-java/* %filename% * Created on %date% * * (c) 2004 MyCorp, etc. etc. */ %package% import org.apache.log4j.Logger; /** * [Documentation Here!] * * @author %author% * @version $Id: ch11.xml,v 1.3 2004/12/14 16:55:39 kend Exp kend $ * **/ public class %class% { /** * Provides access to the CVS version of this class. **/ public static final String VERSION = "$Id: ch11.xml,v 1.3 2004/12/14 16:55:39 kend Exp kend $"; /** * Provides hierarchical control and configuration of debugging via * class package structure. **/ private static Logger log = Logger.getLogger(%class%.class); } The template system shown in Example 11-3 causes an attempt to find a nonexistent Java source file within this project hierarchy (for example, via C-x C-f src/com/mycorp/util/FooManager.java) to result in the prompt Start with template file? (y or n) in the minibuffer, and if you answer y, you'll see your FooManager.java buffer start out with contents in the following example. Example 11-3. FooManager.java/* FooManager.java * Created on Sun Nov 9 20:56:12 2003 * * (c) 2004 MyCorp, etc. etc. */ package com.mycorp.util; import org.apache.log4j.Logger; /** * [Documentation Here!] * * @author Jim Elliott * @version $Id: ch11.xml,v 1.3 2004/12/14 16:55:39 kend Exp kend $ * **/ public class FooManager { /** * Provides access to the CVS version of this class. **/ public static final String VERSION = "$Id: ch11.xml,v 1.3 2004/12/14 16:55:39 kend Exp kend $"; /** * Provides hierarchical control and configuration of debugging via * class package structure. **/ private static Logger log = Logger.getLogger(FooManager.class); } The template has been used to populate the buffer with the standard project header comments and a basic Java class skeleton, with proper contextual values filled in (such as the current time, the person creating the file, the file and class name, and so on). Even the Java package statement has been inferred by examining the directory path in which the source file is being created. The Logger declaration will look familiar to anyone who uses the excellent log4j system to add logging and debugging to their Java projects. (The strange version numbers in "$Id" strings are managed by the CVS version control system and will be updated to the proper file and version information when it's checked in. This topic is discussed in Chapter 12.) To make this work, the template system needs to be able to do a couple of things:
Let's look at the source code that makes this all happen! (As always, if you don't want to type the code listed in Example 11-4 yourself, you can download it from this book's web site.[7])
Example 11-4. template.el;;;;;;;;;;;;;;;;;;;;;;;;;;; -*- Mode: Emacs-Lisp -*- ;;;;;;;;;;;;;;;;;;;;;;;; ;; template.el --- Routines for generating smart skeletal templates for files. (defvar template-file-name "file-template" "*The name of the file to look for when a find-file request fails. If a file with the name specified by this variable exists, offer to use it as a template for creating the new file. You can also have mode-specific templates by appending \"-extension\" to this filename, e.g. a Java specific template would be file-template-java.") (defvar template-replacements-alist '(("%filename%" . (lambda ( ) (file-name-nondirectory (buffer-file-name)))) ("%creator%" . user-full-name) ("%author%" . user-full-name) ("%date%" . current-time-string) ("%once%" . (lambda ( ) (template-insert-include-once))) ("%package%" . (lambda ( ) (template-insert-java-package))) ("%class%" . (lambda ( ) (template-insert-class-name))) ) "A list which specifies what substitutions to perform upon loading a template file. Each list element consists of a string, which is the target to be replaced if it is found in the template, paired with a function, which is called to generate the replacement value for the string.") (defun find-template-file ( ) "Searches the current directory and its parents for a file matching the name configured for template files. The name of the first such readable file found is returned, allowing for hierarchical template configuration. A template file with the same extension as the file being loaded (using a \"-\" instead of a \".\" as the template file's delimiter, to avoid confusing other software) will take precedence over an extension-free, generic template." (let ((path (file-name-directory (buffer-file-name))) (ext (file-name-extension (buffer-file-name))) attempt result) (while (and (not result) (> (length path) 0)) (setq attempt (concat path template-file-name "-" ext)) (if (file-readable-p attempt) (setq result attempt) (setq attempt (concat path template-file-name)) (if (file-readable-p attempt) (setq result attempt) (setq path (if (string-equal path "/") "" (file-name-directory (substring path 0 -1))))))) result)) (defun template-file-not-found-hook ( ) "Called when a find-file command has not been able to find the specified file in the current directory. Sees if it makes sense to offer to start it based on a template." (condition-case nil (if (and (find-template-file) (y-or-n-p "Start with template file? ")) (progn (buffer-disable-undo) (insert-file (find-template-file)) (goto-char (point-min)) ;; Magically do the variable substitutions (let ((the-list template-replacements-alist)) (while the-list (goto-char (point-min)) (replace-string (car (car the-list)) (funcall (cdr (car the-list))) nil) (setq the-list (cdr the-list)))) (goto-char (point-min)) (buffer-enable-undo) (set-buffer-modified-p nil))) ;; This is part of the condition-case; it catches the situation where ;; the user has hit C-g to abort the find-file (since they realized ;; that they didn't mean it) and deletes the buffer that has already ;; been created to go with that file, since it will otherwise become ;; mysterious clutter they may not even know about. ('quit (kill-buffer (current-buffer)) (signal 'quit "Quit")))) ; Install the above routine (or (memq 'template-file-not-found-hook find-file-not-found-hooks) (setq find-file-not-found-hooks (append find-file-not-found-hooks '(template-file-not-found-hook))) ) (defun template-insert-include-once ( ) "Returns preprocessor directives such that the file will be included only once during a compilation process which includes it an abitrary number of times." (let ((name (file-name-nondirectory (buffer-file-name))) basename) (if (string-match ".h$" name) (progn (setq basename (upcase (substring name 0 -2))) (concat "#ifndef _H_" basename "\n#define _H_" basename "\n\n\n#endif /* not defined _H_" basename " */\n")) "" ; the "else" clause, returns an empty string. ))) (defun template-insert-java-package ( ) "Inserts an appropriate Java package directive based on the path to the current file name (assuming that it is in the com, org or net subtree). If no recognizable package path is found, inserts nothing." (let ((name (file-name-directory (buffer-file-name))) result) (if (string-match "/\\(com\\|org\\|net\\)/.*/$" name) (progn (setq result (substring name (+ (match-beginning 0) 1) (- (match-end 0) 1))) (while (string-match "/" result) (setq result (concat (substring result 0 (match-beginning 0)) "." (substring result (match-end 0))))) (concat "package " result ";")) ""))) (defun template-insert-class-name ( ) "Inserts the name of the java class being defined in the current file, based on the file name. If not a Java source file, inserts nothing." (let ((name (file-name-nondirectory (buffer-file-name)))) (if (string-match "\\(.*\\)\\.java" name) (substring name (match-beginning 1) (match-end 1)) ""))) (provide 'template) You'll notice that this code makes heavy use of the regular expression facilities, which is no surprise. The first section sets up some variables that configure the operation of the template system. template-file-name determines the file name (or prefix) that is used to search for templates; the default value of file-template is probably fine. template-replacements-alist sets up the standard placeholders, and the mechanism by which they get replaced by appropriate values. Adding entries to this list is one way to extend the system. Each entry consists of the placeholder to be replaced, followed by the Lisp function to be executed to produce its replacement. The way this function can be stored in a list and executed when appropriate later is one of the great things about Lisp and is discussed in more depth in the calculator mode example in the next section. The placeholders supported are:
The first function, find-template-file, is responsible for searching the directory hierarchy above the file being created, looking for a file with the right name to be considered a file template (if template-file-name has been left at its default value, this looks for either a file named file-template or file-template-ext where ext is the extension at the end of the name of the file being created). It just keeps lopping the last directory off the path in which it's looking, starting with the location of the new file, and seeing if it can read a file with one of those names in the current directory, until it runs out of directories. The function template-file-not-found-hook is the "main program" of the template system. It gets "hooked in" to the normal Emacs find-file process, and called whenever find-file doesn't find the file the user asked for (in other words, a new file is being created). It uses condition-case (a mechanism similar to exception handling in C++ and Java) to make sure it gets a chance to clean up after itself if the user cancels the process of filling in the template file. It checks whether the template file can be found, asks users if they want to use it, and (if they do) loads it into the new buffer and performs the placeholder substitutions. For an explanation of the list manipulation and funcall code that makes the substitutions work, read the discussion of Calculator mode in the next section. Finally, it jumps to the beginning of the new buffer and marks it as unchanged (because, as far as users are concerned, it's a brand new buffer on which they've not yet had to expend any effort). Immediately after the function definition is the chunk of code that hooks it into the find-file mechanism. The file-not-found-hooks is a variable that Emacs uses to keep track of things to do when a requested file is not found. (Giving you opportunities to change or enhance normal behavior through "hooks" is a wonderful trait of Emacs that is discussed in more depth following the Calculator mode example later in this chapter.) Our code checks to make sure it's not already hooked up (so you don't end up having it run twice or more if you re-load the library file during an Emacs session), and then installs our hook at the end of the list if it's not there. The rest of the file is helper functions to handle the more complex placeholders. template-insert-java-package figures out the value that should replace %package%, while template-insert-class-name figures out the Java class name that replaces %class%. The last function call in the file, (provide 'template), records the fact that a "feature" named "template" has been loaded successfully. The provide function works with require to allow libraries to be loaded just once. When the function (require 'template) is executed, Emacs checks whether the feature "template" has ever been provided. If it has, it does nothing, otherwise, it calls load-library to load it. It's a good practice to have your libraries support this mechanism, so that they can be gracefully and efficiently used by other libraries through the require mechanism. You'll find this pattern throughout the Emacs library sources. |
| < Day Day Up > |


Strictly speacking, CLPP use following libraries:
All used libraries are header-only, except Boost.Filesystem and Boost.System, so you must build these libraries and link it with your program.
However, if you using C++ professionally, you (in my humble opinion) must have Boost C++ libraries. So just download full package from there, install it and enjoy!
Note: If you use Windows - see http://www.boostpro.com/download.


public static interface MockRestServiceServer.MockRestServiceServerBuilder
MockRestServiceServer
.| 限定符和类型 | 方法和说明 |
|---|---|
MockRestServiceServer.MockRestServiceServerBuilder |
bufferContent()
Use the
BufferingClientHttpRequestFactory wrapper to buffer
the input and output streams, and for example, allow multiple reads
of the response body. |
MockRestServiceServer |
build()
Build the
MockRestServiceServer and set up the underlying
RestTemplate or AsyncRestTemplate with a
ClientHttpRequestFactory that creates mock requests. |
MockRestServiceServer |
build(RequestExpectationManager manager)
An overloaded build alternative that accepts a custom
RequestExpectationManager . |
MockRestServiceServer.MockRestServiceServerBuilder |
ignoreExpectOrder(boolean ignoreExpectOrder)
Whether to allow expected requests to be executed in any order not
necessarily matching the order of declaration.
|
MockRestServiceServer.MockRestServiceServerBuilder ignoreExpectOrder(boolean ignoreExpectOrder)
Effectively a shortcut for:
builder.build(new UnorderedRequestExpectationManager)
.
By default this is set to false
ignoreExpectOrder
- whether to ignore the order of expectationsMockRestServiceServer.MockRestServiceServerBuilder bufferContent()
BufferingClientHttpRequestFactory
wrapper to buffer
the input and output streams, and for example, allow multiple reads
of the response body.MockRestServiceServer build()
MockRestServiceServer
and set up the underlying
RestTemplate
or AsyncRestTemplate
with a
ClientHttpRequestFactory
that creates mock requests.MockRestServiceServer build(RequestExpectationManager manager)
RequestExpectationManager
.


"Sass is the most mature, stable, and powerful professional grade CSS extension language in the world."
sudo gem install sass
sass style.scss:someotherfile.css
sass --watch style.scss:someotherfile.css
--style=[nested|expanded|compact|compressed]
sass app/scss:public/css
sass-convert style.scss style.sass
@import 'file', 'another-file';
@include clearfix;
"The Unix philosophy emphasizes building short, simple, clear, modular, and extensible code that can be easily maintained and repurposed by developers other than its creators."
"CSS Authoring Framework"
Vendor Prefixes
Border Radius
Box Shadows
Gradients
Transform
Transition
to name a few ...
Vertical Rhythm
Clearfix
Intrinsic Ratios
Parallax
Triangles
to name a few ...
Commenting
// Single-line comments
/* Multi-line comments */


I first took gabapentin 2.5 years ago, when I was a senior in high school. I was very interested in drugs, but had never taken anything out of fear of long term brain effects. Experience reports indicated it might improve my talkativeness- something I could definitely use. Nervously, I began with a dose of 600 mg. I feared I might lose control and make a fool of myself at school. On the contrary, I felt nothing at all except a brief dizziness about four hours later, upon standing up at the end of a class.
I was disappointed, but gave 600mg another shot a month later, with similar results. I'm not exactly sure why I continued taking it, perhaps the very idea of taking drugs was a thrill. Over the course of a year, I continued taking gabapentin about once or twice a month, and between familiarization with the effects and slowly upping the dosage, I grew to love it. One year ago, I took 900mg and went to the library. Suddenly all the books seemed interesting, and I wanted to learn about every subject there was.
On another occasion, I walked outside into my apartment building as effects were coming on, and suddenly the building (in which I know nobody else) truly felt like my home. Then I went out into the city, which again felt like my home. On the college campus, everybody I walked by felt like a long-time friend, who I could approach and chat with and hug. I resisted this urge, but I feel as though I have become more social even while sober as a result of gabapentin showing me that I really can be a social person, and enjoy doing so.
In the past year I have come to take gabapentin for just about any occasion. I take it when I am happy to celebrate, I take it when I am sad to cheer me up. I take it to improve dull tasks and interesting ones alike. My intake is now about 1500mg, two to four times a week, and I recently procured a prescription that will allow me to sustain this high intake. I have become a gabapentin addict. I would like to reduce my consumption, but its effects are still too enjoyable for that to happen anytime soon. I have tried other depressants: various benzodiazepines and opioids, Kava, Kratom, and alcohol, but none have won my heart as gabapentin has.
I would describe its effects on me as follows:
-Onset time is 1.5 to 2 hours, may depend on stomach contents but I can't quite tell.
-Lasts for 3 to 6 more hours after effects start.
-Pleasant warm buzzing feeling in my legs
-Drastically increases my sociability, and inspires me to sing aloud in public- highly unusual when sober.
-Produces the desire to talk to people, and have (potentially uncharacteristic) heartfelt conversations with them, especially people I haven't heard from in a long time.
-A tolerance definitely exists; waiting about four days between doses seems to minimize this.
Experience Reports are the writings and opinions of the individual authors who submit them.
Some of the activities described are dangerous and/or illegal and none are recommended by Erowid.


The output is in the console
1.
Click here
2.
Click here
3.
Click here
4.
Click here
5.
Click here
6.
Click here


The problem
Images slow down your website. Why not lazy load all of them by default?

Note: if you need to support users without JavaScript, you can simply add a
<noscript>
element underneath your image with a standard<img>
element with asrc
attribute.
What this plugin is not
A lazy-loading library. This library will prep your images for use with Lozad.js. You'll need to install Lozad.js for your images to load with this plugin.
Table of Contents
Installation
npm install markdown-it-plugin-data-src
Usage
CommonJS plugin example
const md = require('markdown-it')()
.use(require('markdown-it-plugin-data-src'))
ES6 Module plugin example
import MarkdownIt from 'markdown-it'
import dataSrc from 'markdown-it-plugin-data-src'
const md = new MarkdownIt()
.use(dataSrc)
Then simply install Lozad.js as you normally would and run it on all images.


| larv.PriorityList = class PriorityList(builtins.object) | ||
| Implements a list of ordered nodes. Every node will be a 2 item tuple: - First item will be the content we want to store - Second item will be the content used for ordering the list. This item needs to have the __lt__ method. Optimized thinking that users will introduce higher priority items first. Note: Ordered from lower value to higher, that's thought out for easy usage using numbers, where 0 will be max priority (assuming no use of negative numbers) and every other number will have less and less priority as they get higher. | ||
Methods defined here:
Data descriptors defined here:
|


What is Emotional Intelligence?
Emotional intelligence is the ability to understand, manage and evaluate your own emotions; to recognize and understand emotions in others; and to use that skill to maintain strong relationships and manage difficult situations calmly and effectively.
How is it different to IQ?
IQ is called “Intelligence Quotient” and it measures people’s relative intelligence. Merriam-Webster defines IQ as “a number used to express the apparent relative intelligence of a person. Determined by either the ratio of the mental age (as reported on a standardized test) to the chronological age multiplied by 100 - or a score determined by one's performance on a standardized intelligence test relative to the average performance of others of the same age.”
IQ looks at problem-solving, short and working memory, knowledge of the world, spatial and visual processing and logic reasoning.
EQ on the other hand looks at identifying emotions, evaluating other’s feelings, controlling your own emotions, perceiving how others feel, empathizing with others and using emotions to help with social communication.
Both these play an important part in our overall success, happiness and health.
Why is it important?
The ability to honestly express your emotions and control them, and to understand and effectively respond to emotions of others is essential in not only the workplace but in all other avenues of life as well.
Emotionally intelligent people are more self aware, they think before they react and they are able to better empathize with how other people are feeling. People with a higher EQ are also better at managing challenging situations and complex relationships. They are great communicators and have incredible social skills.


Sifry has developed Technorati’s Current Events, a page which puts the thoughts from 150000 blogs from the last 2 hours on one page. This will greatly help those who want to know what is currently being written about the war in Irak. By reading something from this broad base of sources, propaganda is likely to be unsuccessful, or at least less successful. You can find the page here.
Journalist Asked to Stop Blogging
Kevin Site, a CNN journalist in Northern Irak, has been asked to stop posting to his blog. Pretty sad to hear that. They could at least have integrated the blog into CNN if they believe he is not spending that time on work related tasks.
Words of Encouragement
I am copying this from kuro5hin.org || A commander’s words to his men. It’s a speech given by Lieutenant Colonel Tim Collins to the 1st Battalion of the Royal Irish Regiment in relation to the war in Irak.
"We go to liberate not to conquer. We will not fly our flags in their country. We are entering Iraq to free a people and the only flag which will be flown in that ancient land is their own. Show respect for them.
There are some who are alive at this moment who will not be alive shortly. Those who do not wish to go on that journey, we will not send.
As for the others I expect you to rock their world. Wipe them out if that is what they choose. But if you are ferocious in battle remember to be magnanimous in victory.
Iraq is steeped in history. It is the site of the Garden of Eden, of the Great Flood and the birthplace of Abraham. Tread lightly there.
You will see things that no man could pay to see and you will have to go a long way to find a more decent, generous and upright people than the Iraqis.
You will be embarrassed by their hospitality even though they have nothing.
Don’t treat them as refugees for they are in their own country. Their children will be poor, in years to come they will know that the light of liberation in their lives was brought by you.
If there are casualties of war then remember that when they woke up and got dressed in the morning they did not plan to die this day.
Allow them dignity in death. Bury them properly and mark their graves."
Latest From Raed
Where is Raed ? has been updated. One thing I found thought provoking:
"On BBC we are watching scenes of Iraqis surrendering. My youngest cousin was muttering â€œwhat shameâ€? to himself, yes it is better for them to do that but still seeing them carrying that white flag makes something deep inside you cringe."
The History of Windows
Neowin.net has a 16 page piece on the - Microsoft Windows History. Lots of screenshots. And while I am at it, I love the tag line of Neowin.net: "Where unprofessional journalism looks better"
That’s what the Cluetrain Manifesto means with a real voice, really talking to your readers. I would extend that to simply telling the truth.
The New US Hardliners
This here is a pretty critical german piece about the new hardliners in the Bush administration, led by Paul D. Wolfowitz: n-tv.de - Der Krieg aus der Denkfabrik (try babelfish.altavista.com for a translation maybe). It’s very good to get this perspective I think, but it should be remembered that it is an opinion piece.
To get another view on the entire story, I suggest you take a look here. This is a PBS Frontline special "The War behind closed doors". It’s a 6 piece video report on how the two camps within the Bush administration are fighting in relation to what the good thing to do is. And I truely believe that what Wolfowitz has thought up is something that he believes is the good thing to do. Whether anyone agrees or not is a totally different thing.
Bagdad Webcam
We already have somebody blogging in Irak and now we have a Webcam in Iraq too. I have to admit that I am not sure how often it is being updated and at this time it doesn’t show anything of particular interest … but it might ;)
Repairing the World
Repairing the World is an editorial by Thomas L. Friedman.
Why? What does Tony Blair get that George Bush doesn’t? The only way I can explain it is by a concept from the Kabbalah called "tikkun olam." It means, "to repair the world." If you listened to Tony Blair’s speeches in recent weeks they contain something so strikingly absent from Mr. Bush’s. Tony Blair constantly puts the struggle for a better Iraq within a broader context of moral concerns. Tony Blair always leaves you with the impression that for him the Iraq war is just one hammer and one nail in an effort to do tikkun olam, to repair the world.
Also read this: D-Day
More than 50 years ago America won a war against European fascism, which it followed up with a Marshall Plan and nation-building, both a handout and a hand up — in a way that made Americans welcome across the world. Today is a D-Day for our generation. May our leaders have the wisdom of their predecessors from the Greatest Generation.
Disinfopedia
Disinfopedia is currently down a lot. They just can’t handle the traffic they get at this time. The site has been built up to counter propaganda from any source. It seems to be built on Wiki, a tool to have a community build a web page, kind of.
Reading Classics
Tufts University has set up the Perseus Digital Library. They have a huge repository of books in there for everybody’s enjoyment. I might be able to read some classics easily after all ;)


mentorsearch.io
Find mentorship opportunities in the upcoming
MLH Hackathons without having to check every
listing on mlh.io!


src/games/dartsBaseGame.js
import Round from '../round';
/**
* Provides all the logic to create multiplier games based on this class
*
* @class DartsBaseGame
*/
export default class DartsBaseGame {
/**
* @static
* @property {Object} defaultOptions
* @property {Boolean} defaultOptions.isComputer
*/
static defaultOptions = {
round: {
throwsPerRound: 3
}
};
_players = new Set();
_playerRounds = new Map();
_currentRoundNumber = 0;
_currentPlayer = null;
/**
* Initializes a new instance of the `DartsBaseGame` class.
*
* @param {Player[]} players Collection of players.
* @param {Object} [options={}] Options.
*/
constructor(players, options = {}) {
if(!players || !players.length) {
throw Error('No players provided');
}
this.addPlayers(players);
this._options = Object.assign({}, DartsBaseGame.defaultOptions, options);
this._playersIterator = this._players[Symbol.iterator]();
let data = this._playersIterator.next();
this._currentPlayer = data.value;
this.nextRound();
}
/**
* Represents single `throw`. Delegates its execution to the `currentRound` object.
*
* @param {number} num Number between 0 and 20 plus 25 (bull)
* @param {number} [multiplier=1] Multiplier is between 1 and 3 for all of the numbers but 25 (which has only x2 multiplier).
* @returns {Throw} `Throw` instance.
*/
throw(num, multiplier = 1) {
return this.currentRound.throw(num, multiplier);
}
/**
* Invalidates specific round.
*
* @param {Round} round `Round` instance.
*/
invalidateRound(round) {
round.invalidate();
}
/**
* Invalidates specific `throw`.
*
* @param {Throw} dartsThrow `Throw` instance.
*/
invalidateThrow(dartsThrow) {
dartsThrow.number = 0;
dartsThrow.multiplier = 1;
}
/**
* Sets `currentPlayer` to the next one.
* @returns {Player} Returns player.
*/
nextPlayer() {
let data = this._playersIterator.next();
if(data.done) {
this._playersIterator = this._players[Symbol.iterator]();
this.nextRound();
return this.nextPlayer();
}
this._currentPlayer = data.value;
return this._currentPlayer;
}
/**
* Creates new instance of the `Round` for each player.
*/
nextRound() {
let round;
for(let player of this._players) {
round = new Round(this._options.round);
this._playerRounds.get(player).get('rounds').add(round);
this._playerRounds.get(player).set('currentRound', round);
}
this._currentRoundNumber++;
}
/**
* Adds a player to the game.
*
* @param {Player} player Instance of `Player` class.
*/
addPlayer(player) {
this._players.add(player);
this._playerRounds.set(player, new Map().set('rounds', new Set()));
}
/**
* Add multiple players to the game.
*
* @param {Player[]} players Array of players.
*/
addPlayers(players) {
for(let player of players) {
this.addPlayer(player);
}
}
/**
* @returns {Player[]} Array of players.
*/
get players() {
return Array.from(this._players);
}
/**
* @returns {number} Throws per round.
*/
get throwsPerRound() {
return this._options.round.throwsPerRound;
}
/**
* @returns {Player} Return `Player`
*/
get currentPlayer() {
return this._currentPlayer;
}
/**
* @returns {Round} Return `Round`.
*/
get currentRound() {
return this._playerRounds.get(this.currentPlayer).get('currentRound');
}
/**
* @returns {number} Round number.
*/
get currentRoundNumber() {
return this._currentRoundNumber;
}
/**
* @param {Player} player Instance of the `Player`.
* @returns {Round[]} Array of rounds.
*/
roundsByPlayer(player) {
return Array.from(this._playerRounds.get(player).get('rounds'));
}
/**
* @param {Player} player Instance of the `Player`.
* @returns {Throw[]} Array of `Throw` instances.
*/
throwsByPlayer(player) {
return this.roundsByPlayer(player).reduce((prev, curr) => {
return prev.concat(Array.from(curr.throws));
}, []);
}
}


This is a project that I made for a class, it is a navigational tool that can be used similarly to Google Maps. It was implemented using a graph ADT and was written in Java and Javascript.


This file contains the interface for the XIo component, which encapsulates the Input/Output functions for processors that do not require any special I/O handling.
MODIFICATION HISTORY:
Ver Who Date Changes ----- ---- -------- ------------------------------------------------------- 1.00a rpm 11/07/03 Added InSwap/OutSwap routines for endian conversion 1.00a xd 11/04/04 Improved support for doxygen 1.01a ecm 02/24/06 CR225908 corrected the extra curly braces in macros and bumped version to 1.01.a. 1.11a mta 03/21/07 Updated to new coding style. 1.11b va 04/17/08 Updated Tcl for better CORE_CLOCK_FREQ_HZ definition 1.11a sdm 03/12/09 Updated Tcl to define correct value for CORE_CLOCK_FREQ_HZ (CR #502010) 1.13a sdm 03/12/09 Updated the Tcl to pull appropriate libraries for Little Endian Microblaze 2.0 adk 19/12/13 Updated as per the New Tcl API's 2.1 bss 04/14/14 Updated tcl to copy libgloss.a and libgcc.a libraries 2.1 bss 04/29/14 Updated to copy libgloss.a if exists otherwise libxil.a CR#794205


Your users also don't like waiting...
notes
Yielding
A way of not being selfish and letting others use UI thread.
## Debounce events with rAF