Writing iWay Function Language (IFL)

The server provides a wide range of functions, such as xpath(), sreg(), tpa(), ldap(), file() and so on. It is possible to supply a new function in an extension that will be compiled by the function compiler and executed just as iWay's own functions.

Functions are registered by the addFunction(name,className) function of the extension's register() function, in the same manner as listeners, emitters, console pages, log exits, and so on are registered.

Functions extend com.ibi.funcs.FunctionNode. Your function must offer a null constructor that calls a parental constructor with the "root" name of your function. This becomes the name of the function representation in the Abstract Syntax Tree created during the compile step. In the example, the name of the function is REVERSE, and only the reversal service is offered. If you call addFunction() more than once, adding multiple functions for the same class, then the class name is assigned at construction, and the setFuncName() function can be used to distinguish entries. An example of this is a STRING class that might support substr, length, startswith, and so on.

The name of your function should be clearly distinguishable, and it is suggested that it start with a non-alphabetic character. This assists in avoiding confusion when the compiler is passed over other functional languages for which there may be overlap. The most common example is confusion created by function names that mirror SQL scalar functions. The initial underscore that iWay uses helps to alleviate this issue.

Your function returns a string value when the getXValue method is called. When this is called, the parameters may have been resolved by an upward walk, so that the parameters are themselves strings. In other cases, you must evaluate them. You do this by calling the getXValue() function, which causes a walk down the tree.

For example, if your function is named REVERSE and you want to reverse the characters in a special register named sr1, you configure the call

_reverse(sreg(sr1))

passing one parameter to the reversal function. When the reversal functions's getXValue method is called, it can get the parameter and reverse it.

Parameters are children of the node in the AST. The node itself extends XDNode, enabling you to use the standard tree calls to step through your parameters.

Functions designed to participate in Boolean operations should return strings of "true" or "false". Any value not exactly "true" is considered to be false, but iWay strongly recommends that the two values be returned as specified.

Each function has, in addition to the getXValue() method needed to obtain the function result, an optional optimize() method, receiving the manager and the logger, is called during construction of the abstract syntax tree. A common use of optimize() is to load the parameters, preventing the need for a treewalk during actual execution. This is shown in the example below. Although the example function has one parameter, and because the parameter is required for compilation, use of the array is simplified. Functions with multiple parameters, some of which may be optional, can interrogate the array in the getXValue() method to determine the actual parameters passed in at runtime.

Functions should return metadata describing their purpose and parameters. This is done using the getDesc() function. The getDesc() returns a tree of information. The following is an example of the _inflate() function tree.

<func  name='_inflate' class='encoding'>
  <desc>Inflate a compressed value</desc>
  <parm name='value' type='string' req='yes'>The string to inflate</parm>
  <parm name='type' type='keyword'  req='yes'>Type of input
       <kw name='base64' >The compressed data is in base 64 form</kw>
       <kw name='string' default='yes'>The decompressed data is a UNICODE string</kw>
  </parm>
</func>

The example function is:

package com.ibi.funcs;
import com.ibi.edaqm.*;
import java.util.*;
 
 /**
  * Sample function extension. This is called with
  * one parameter, to reverse its value
  */
 public class REVERSE extends FunctionNode
 {
   // the constructor must call super() with the name of the AST node
   public REVERSE() // for when I make default nodes
   {
      super("REVERSE");
   }
 
   // called by the interpreter with the name of the function for which
   // this instance is created. Useful if multiple functions are
   // implemented.
   public void setFuncName(String funcName)
   {
   }
 
   // returns the name of the function for error handling
   public String getTypeName()
   {
      return "REVERSE";
   }
 
   public XDNode getDesc() throws XDException
   {
      String info = "<func name='_reverse' class='string'>" +
            "<desc>Reverse a string</desc>" +
            "<parm name='value' type='string' req='yes'>" +
            "The string to reverse</parm>" +
            "</func>";
      XDDocument d = new XDDocument(null,info);
      return d.getRoot();
   }
 
   // how many parameters are needed by this instance
   public int getMinParms() // minimum parms is one
   {
      return 1;
   }
 
// what is the maximum number of parameters I need
   public int getMaxParms() // maximum parms is one
   {
      return 1;
   }
 
   private FunctionNode[] parms;
   // this will be an array of parameters
 
   // Optimize is called during ASTgeneration
   public void optimize(XDManager manager, com.ibi.common.IXLogger logger)
   throws XDException
   {
      super.optimize(manager,logger);
      parms = getParms(); // get the array of parameters
   }
 
   // return the value -- parms are operands in order
   public String getXValue(XDDocument doc,
                           com.ibi.common.ISpecRegManager srm,
                           com.ibi.common.IXLogger logger)
   throws XDException
   {
      // get the first parameter
      String value = parms[0].getXValue(doc,srm,logger);
      if (value.length()==0)
      {
         throw new XDFunctionException("_reverse",1,
                                       "length to reverse cannot be zero");
      }
      StringBuffer sb = new StringBuffer(value);
      sb = sb.reverse();
      return sb.toString();
   }
 }

Once written, you must register the function with the system. Do this using the addFunction() registration call. For example,

package com.ibi.example;
 
 public class Register implements com.ibi.common.IComponentRegister
 {
   public Register()
   {
   }
 
   public void register (com.ibi.common.IComponentManager cm)
   {
      cm.addFunction("_REVERSE","com.ibi.funcs.REVERSE");
      cm.addFunction("_reverse","com.ibi.funcs.REVERSE");
      System.out.println("**** registering _REVERSE  ****");
   }
 
   public void unregister (com.ibi.common.IComponentManager cm)
   {
   }
 }

The manifest as described in Installing Components points to the registration method, which uses addFunction() to install a function. The name of the extension jar containing the new function must, like all extensions, begin with the letters “iw”. The name of the function must be defined with an underscore character (_).


iWay Software