Apama 10.15.0 | Building and Using Apama Dashboards | Building Dashboard Clients | Using Dashboard Functions | Creating custom functions | Sample IFunctionLibrary implementation
 
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 addition 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$
*/
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;
}
}