Sample IFunctionLibrary implementation
Below is a sample implementation of IFunctionLibrary, which you can find under samples\dashboard_studio\tutorial\src:
package com.apama.dashboard.sample;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.StringTokenizer;
import java.util.Vector;
import java.util.regex.Pattern;
import com.apama.dashboard.data.ITabularData;
import com.apama.dashboard.data.IVariableData;
import com.apama.dashboard.data.TabularDataFactory;
import com.apama.dashboard.data.internal.TabularData;
import com.apama.dashboard.function.FunctionDescriptorFactory;
import com.apama.dashboard.function.IFunctionDescriptor;
import com.apama.dashboard.function.IFunctionLibrary;
/**
 * SampleFunctionLibrary is an example of a custom function library for 
 * Dashboard Studio. Custom functions allow you to extend Dashboard Studio 
 * by the additon of custom functions to process data for use as data
 * attachments.
 * <p>
 * SampleFunctionLibrary implements the functions:
 * <ul>
 * <li><b>String to Table</b>: Parses an encoded string to produce tabular
 * data.
 * </ul>
 * 
 * $Copyright(c) 2013 Software AG, Darmstadt, Germany and/or its licensors$
 * @version      $Id$
 */
public class SampleFunctionLibrary implements IFunctionLibrary {
  private final static String FUN_STRINGTOTABLE = "String to Table";
  // Column naming modes
  enum ColMode {
    AUTO, STRING, STATIC;
  };
  /**
   * Get the list of function descriptors for the functions implemented 
   * by this function library. Each command descriptor identifies one 
   * function.
   */
  public Vector<IFunctionDescriptor> getFunctionDescriptors() {
    Vector<IFunctionDescriptor> v = new Vector<IFunctionDescriptor> ();
    IFunctionDescriptor fd = FunctionDescriptorFactory.createFunctionDescriptor(
        FUN_STRINGTOTABLE,
        new String[] { "String", "Row Delimiter", "Column Delimiter", 
                       "Column Name Mode","Column Names", "Allow Empty Rows/Columns"},
        new String[] { "s_arg1", "s_arg2", "s_arg3", "s_arg4", "s_arg5", "s_arg6"},
        IFunctionDescriptor.RETURN_TYPE_TABLE,
        null,
        "This function produces a table from the specified string by using " +
        "the specified row and column delimiters to tokenize the string. If " +
        "the table is to contain only 1 column, do not specify a value for " +
        "Column Delimiter. " +
        "Column names are determined by the \"Column Name Mode\". Specify one of: \n" +
        "  - AUTO : Names generated as col0, col1, col2, ...\n" +
        "  - STATIC : Names defined in \"Column Names\", comma seperated\n" +
        "  - STRING : Names defined in first row of \"String\"\n\n" +
        "Use \"Allow Empty Rows/Columns\" to create empty rows/columns for all delimiters\n"
        + "  - false (default) : empty tokens will be skipped\n" +
        "  - true : empty tokens will result in empty rows/columns\n");
    v.add(fd);
    return v;
  }
  /**
   * Evaluate a function.
   * 
   * @param command Function to evaluate.
   * @param parameters Parameters to function.
   */
  public Object evaluateFunction(String function,  IVariableData parameters) {
    if (function.equals(FUN_STRINGTOTABLE)) {
      return stringToTable(parameters);
    } else {
      return null;
    }
  }
  /**
   * Generate a table from the string parameter passed to function.
   * 
   * @param parameters Parameters to function.
   * @return Tabular data
   */
  private ITabularData stringToTable (IVariableData parameters) {
    // See if "Allow Empty Fields" is used.  If not, then return value of 
    // stringToTableOld() which preserves old behavior
    // This is needed so as not to break any old StringToTable behavior
    String allowEmptyCells = parameters.getStringVar("s_arg6");
    
    if (allowEmptyCells == null ||
      allowEmptyCells.equals("") ||
      allowEmptyCells.equalsIgnoreCase("false") ||
      allowEmptyCells.equals("0") ||
      allowEmptyCells.equalsIgnoreCase("no")) {
      return stringToTableOld(parameters);
    }
    // Function arguments
    String inString = parameters.getStringVar("s_arg1");
    String unquoteRowDelim = parameters.getStringVar("s_arg2");
    String unquoteColDelim = parameters.getStringVar("s_arg3");
    String colModeS = parameters.getStringVar("s_arg4");
    String colNames = parameters.getStringVar("s_arg5");
    String colDelim = unquoteColDelim;
    String rowDelim = unquoteRowDelim;
    // Check required values
    if (inString == null || inString.equals(""))
      return null;
    
    // StringTokenizer will do the right thing
    if ((unquoteColDelim == null) || (unquoteColDelim.length() == 0)) {
      unquoteColDelim = "";
    }
    
    // The delimiters are treated as a list of chars as delimiters
    // Do this by adding a | between chars
    char[] rowDelimChars = unquoteRowDelim.toCharArray();
    char[] colDelimChars = unquoteColDelim.toCharArray();
    StringBuffer rowDelimSB = new StringBuffer();
    StringBuffer colDelimSB = new StringBuffer();
    
    // default any regular expression special chars so we can escape them.
    final String metaChars = "^[^\\[\\]\\^\\+\\|\\?\\\\()\\{\\}\\.<>/;\\*%$]*$";
    
    if (rowDelimChars.length > 0) {
      for (char c : rowDelimChars) {
        // escape any special char
        if (!Pattern.matches(metaChars, String.valueOf(c))) {
          rowDelimSB.append("\\");
        }
        rowDelimSB.append(c);
        rowDelimSB.append("|");
      }
      // remove last '|' 
      rowDelimSB.setLength(rowDelimSB.length()-1);
    }
    if (colDelimChars.length > 0) {
      for (char c : colDelimChars) {
        // escape any special char
        if (!Pattern.matches(metaChars, String.valueOf(c))) {
          colDelimSB.append("\\");
        }
        colDelimSB.append(c);
        colDelimSB.append("|");
      }
      // remove last '|' 
      colDelimSB.setLength(colDelimSB.length()-1);
    }
    // get the actual, escaped, delimiter regular expressions
    rowDelim = rowDelimSB.toString();
    colDelim = colDelimSB.toString();
    
    // How are the columns to be named
    ColMode colMode = ColMode.AUTO;
    if ((colModeS != null) && (colModeS.length() > 0)) {
      try {
        colMode = ColMode.valueOf(colModeS.trim().toUpperCase());
      } catch (IllegalArgumentException e) {
        // bogus column mode is specified, default to AUTO
        colMode = ColMode.AUTO;
      }
    }
    // The number of splitted strings in the first row is the number of columns in table
    int colCount = 0;
    String[] rows;
    
    // if no rowDelim, whole string is treated as a row
    if (rowDelim.equals("")) {
      rows = new String[] {inString};
    } else {
      rows = inString.split(rowDelim, Integer.MAX_VALUE);
    }
    
    // if inString is empty, no row is needed
    if (inString.equals("")) {
      rows = new String[0];
    }
    
    // we do have some rows...
    if (rows.length > 0) {
      // if no column delimiter, whole row is one column
      if (colDelim.equals("")) {
        colCount = 1;
      } else {
        // use col delimiter to split it
        colCount = rows[0].split(colDelim, Integer.MAX_VALUE).length;
      }
    }
    
    // Initialize table and add columns
    ITabularData table = TabularDataFactory.createTabularData();
    String[] columnNames = null;
    switch (colMode) {
    case AUTO:
      for (int i=0; i<colCount; i++) {
        table.addColumn("col" + i,TabularData.COL_TYPE_STRING);
      }
      break;
    case STRING:
      // Make sure this is at least one row
      if (rows.length > 0) {
        // 1st row is the colNames          
        // we do have some rows...
        // if no column delimiter, whole row is one column, which will be the col name
        if (colDelim.equals("")) {
          columnNames = new String[] {rows[0]};
          table.addColumn(columnNames[0], TabularData.COL_TYPE_STRING); // the 1st row IS 
                                                                        // the name
        } else {
              // use col delimiter to split it
          columnNames = rows[0].split(colDelim, Integer.MAX_VALUE);
          int n = 0;
          if (columnNames != null) {
            for (String colName : columnNames) {
              table.addColumn(
                (colName.equals("")) ? "col" + n : colName,TabularData.COL_TYPE_STRING);
              n++;
            }
          }
        }
        
        // since we've used 1st row as column names, remove it from the array
        List<String> rowList = new ArrayList<String>(Arrays.asList(rows));
        rowList = rowList.subList(1, rows.length);
        rows = new String[rows.length-1];
        rows = rowList.toArray(rows);
      }
      
      break;
    case STATIC:
      // get static column from argument
      columnNames = colNames.split(",", Integer.MAX_VALUE);
      
      // Figure out the correct number of columns
      int maxCol = 0;
      
      // if column delimiter is empty, then there is only one column, regardless
      if (unquoteColDelim.equals("")) {
        maxCol = 1;
      } else {
        // If there isn't any row data, just use all columnNames
        maxCol = 
          (rows.length > 0 && !rows[0].equals("")) ? colCount : columnNames.length;
      }
      
      // add column names based on the colNames argument
      int i = 0;
      if (columnNames != null) {
        for (; i < columnNames.length && i < maxCol; i++) {
          String colName = columnNames[i];
          table.addColumn(
            (colName.equals("") ? "col" + i : colName),TabularData.COL_TYPE_STRING);
        }
      }
      
      // if static col names is shorter, fill up with default column names
      for (; i < maxCol; i++) {
        table.addColumn("col" + i,TabularData.COL_TYPE_STRING);
      }
      
      colCount = maxCol;
      break;
    }
    
    // parse string and adding rows to table
    for (int row = 0; row < rows.length; row++) {
      table.addRow("row" + row);
      boolean noColDelimiter = colDelim.equals("");
      if (colCount == 1) {
        table.setCellValue(rows[row], row, 0);
      } else {
        String[] cols;
        
        // if no col delimiter, whole row is one column, no need to split
        if (noColDelimiter) {
          cols = new String[] {rows[row]};
        } else {
          // do need to split it
          cols = rows[row].split(colDelim, Integer.MAX_VALUE);
          
          if (cols != null) {
            for (int col = 0; col < colCount && col < cols.length; col++) {
              table.setCellValue(cols[col], row, col);
            }
          }
        }
      }
    }
    
    return table;
  }
  
  /**
   * This is the old StringToTable implementation which uses StringTokenizer, which will
   * by default ignore consecutive delimiters
   * 
   * @param parameters
   * @return
   */
  private ITabularData stringToTableOld (IVariableData parameters) {
    // Function arguments
    String inString = parameters.getStringVar("s_arg1");
    String rowDelim = parameters.getStringVar("s_arg2");
    String colDelim = parameters.getStringVar("s_arg3");
    String colModeS = parameters.getStringVar("s_arg4");
    String colNames = parameters.getStringVar("s_arg5");
    // Check required values
    if ((inString == null) || (inString.length() == 0) ||
      (rowDelim == null) || (rowDelim.length() == 0)) {
      return null;
    }
    
    // StringTokenizer will do the right thing
    if ((colDelim == null) || (colDelim.length() == 0)) {
      colDelim = "";
    }
    
    // Map special delimiter strings to their internal value
    //      rowDelim = delimValue (rowDelim);
    //      colDelim = delimValue (colDelim);
    
    // How are the columns to be named
    ColMode colMode = ColMode.AUTO;
    if ((colModeS != null) && (colModeS.length() > 0)) {
      try {
        colMode = ColMode.valueOf(colModeS.trim().toUpperCase());
      } catch (IllegalArgumentException e) {
        // bogus column mode is specified, default to AUTO
        colMode = ColMode.AUTO;
      }
    }
    // The number of tokens in the first row is the number of columns in table
    int colCount = 1;
    StringTokenizer st = new StringTokenizer (inString,rowDelim);
    if ((st.hasMoreTokens())) {
      colCount = new StringTokenizer(st.nextToken(),colDelim).countTokens();
    }
    // Tokenizer for iterating through rows in string
    StringTokenizer rowSt = new StringTokenizer (inString,rowDelim);
    
    // Initialize table and add columns
    ITabularData table = TabularDataFactory.createTabularData();
    switch (colMode) {
    case AUTO:
      for (int i=0; i<colCount; i++) {
        table.addColumn("col" + i,TabularData.COL_TYPE_STRING);
      }
      break;
    case STRING:
      st = new StringTokenizer (rowSt.nextToken(),colDelim);
      for (int i=0; i<colCount; i++) {
        table.addColumn(st.nextToken(),TabularData.COL_TYPE_STRING);
      }
      break;
    case STATIC:
      st = new StringTokenizer (colNames,",");
      for (int i=0; i<colCount; i++) {
        if (st.hasMoreTokens()) {
          table.addColumn(st.nextToken(),TabularData.COL_TYPE_STRING);
        } else {
          table.addColumn("col" + i,TabularData.COL_TYPE_STRING);
        }
      }
      break;
    }
    
    // Parse string adding rows to table
    int row = 0;
    while (rowSt.hasMoreTokens()) {
      table.addRow("row" + row);
      if (colCount == 1) {
        table.setCellValue(rowSt.nextToken(), row, 0);
      } else {
        int col = 0;
        StringTokenizer colSt = new StringTokenizer (rowSt.nextToken(), colDelim);
        while (colSt.hasMoreTokens() && (col < colCount)) {
          table.setCellValue(colSt.nextToken(), row, col++);
        }
      }
      row++;
    }
    
    return table;
  }
}