/*
* Copyright (c) 2012, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.wso2.carbon.humantask.core.engine.runtime.xpath;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.h2.util.StringUtils;
import org.w3c.dom.*;
import org.wso2.carbon.humantask.core.HumanTaskConstants;
import org.wso2.carbon.humantask.core.dao.*;
import org.wso2.carbon.humantask.core.engine.PeopleQueryEvaluator;
import org.wso2.carbon.humantask.core.engine.runtime.api.EvaluationContext;
import org.wso2.carbon.humantask.core.engine.runtime.api.HumanTaskRuntimeException;
import org.wso2.carbon.humantask.core.internal.HumanTaskServerHolder;
import org.wso2.carbon.humantask.core.utils.DOMUtils;
import org.wso2.carbon.humantask.core.utils.HTNameSpaces;
import javax.xml.namespace.QName;
import javax.xml.xpath.XPathFunction;
import javax.xml.xpath.XPathFunctionException;
import javax.xml.xpath.XPathFunctionResolver;
import java.util.*;
/**
* The XPath function resolver for Human Task specific evaluation methods.
*/
public class JaxpFunctionResolver implements XPathFunctionResolver {
private static final Log log = LogFactory.getLog(JaxpFunctionResolver.class);
protected static final QName groupQname = HumanTaskConstants.groupQname;
protected static final QName userQname = HumanTaskConstants.userQname;
protected static final QName organizationalEntityQname = HumanTaskConstants.organizationalEntityQname;
private EvaluationContext evalCtx;
private PeopleQueryEvaluator peopleQueryEvaluator;
public XPathFunction resolveFunction(QName functionName, int arity) {
if (log.isDebugEnabled()) {
log.debug("Resolving function: " + functionName);
}
if (functionName.getNamespaceURI() == null) {
log.error(" Error in resolving xpath function name , undeclared namespace " + functionName.getLocalPart()
+ " for function name " + functionName.getNamespaceURI());
throw new NullPointerException("Undeclared namespace for " + functionName);
} else if (HTNameSpaces.HTD_NS.equals(functionName.getNamespaceURI())) {
String localPart = functionName.getLocalPart();
String errMsg = "This operation is not currently supported in this version of WSO2 BPS.";
if (XPath2Constants.FUNCTION_GET_INPUT.equals(localPart)) {
return new GetInput();
} else if (XPath2Constants.FUNCTION_GET_OUTPUT.equals(localPart)) {
return new GetOutput();
} else if (XPath2Constants.FUNCTION_GET_POTENTIAL_OWNERS.equals(localPart)) {
return new GetPotentialOwners();
} else if (XPath2Constants.FUNCTION_GET_ACTUAL_OWNER.equals(localPart)) {
return new GetActualOwner();
} else if (XPath2Constants.FUNCTION_GET_BUSINESS_ADMINISTRATORS.equals(localPart)) {
return new GetBusinessAdministrators();
} else if (XPath2Constants.FUNCTION_GET_EXCLUDED_OWNERS.equals(localPart)) {
return new GetExcludedOwners();
} else if (XPath2Constants.FUNCTION_GET_TASK_INITIATOR.equals(localPart)) {
return new GetTaskInitiator();
} else if (XPath2Constants.FUNCTION_GET_TASK_PRIORITY.equals(localPart)) {
return new GetTaskPriority();
} else if (XPath2Constants.FUNCTION_GET_TASK_STAKEHOLDERS.equals(localPart)) {
return new GetTaskStakeholders();
} else if (XPath2Constants.FUNCTION_GET_LOGICAL_PEOPLE_GROUP.equals(localPart)) {
throw new UnsupportedOperationException(errMsg);
} else if (XPath2Constants.FUNCTION_INTERSECT.equals(localPart)) {
return new Intersect();
} else if (XPath2Constants.FUNCTION_UNION.equals(localPart)) {
return new Union();
} else if (XPath2Constants.FUNCTION_EXCEPT.equals(localPart)) {
return new Except();
} else if (XPath2Constants.FUNCTION_GET_COUNT_OF_FINISHED_SUB_TASKS.equals(localPart)) {
return new GetCountOfFinishedSubTasks();
} else if (XPath2Constants.FUNCTION_GET_COUNT_OF_SUB_TASKS.equals(localPart)) {
return new GetCountOfSubTasks();
} else if (XPath2Constants.FUNCTION_GET_COUNT_OF_SUB_TASKS_IN_STATE.equals(localPart)) {
return new GetCountOfSubTasksInState();
} else if (XPath2Constants.FUNCTION_GET_OUTCOME.equals(localPart)) {
throw new UnsupportedOperationException(errMsg);
} else if (XPath2Constants.FUNCTION_GET_COUNT_OF_SUB_TASKS_WITH_OUTCOME.equals(localPart)) {
throw new UnsupportedOperationException(errMsg);
} else if (XPath2Constants.FUNCTION_GET_SUBTASK_OUTPUT.equals(localPart)) {
throw new UnsupportedOperationException(errMsg);
} else if (XPath2Constants.FUNCTION_GET_SUBTASK_OUTPUTS.equals(localPart)) {
throw new UnsupportedOperationException(errMsg);
} else if (XPath2Constants.FUNCTION_CONCAT.equals(localPart)) {
return new Concat();
} else if (XPath2Constants.FUNCTION_CONCAT_WITH_DELIMITER.equals(localPart)) {
return new ConcatWithDelimiter();
} else if (XPath2Constants.FUNCTION_LEAST_FREQUENT_OCCURENCE.equals(localPart)) {
return new LeastFrequentOccurence();
} else if (XPath2Constants.FUNCTION_MOST_FREQUENT_OCCURENCE.equals(localPart)) {
return new MostFrequentOccurence();
} else if (XPath2Constants.FUNCTION_VOTE_ON_STRING.equals(localPart)) {
return new VoteOnString();
} else if (XPath2Constants.FUNCTION_AND.equals(localPart)) {
return new And();
} else if (XPath2Constants.FUNCTION_OR.equals(localPart)) {
return new Or();
} else if (XPath2Constants.FUNCTION_VOTE.equals(localPart)) {
return new Vote();
} else if (XPath2Constants.FUNCTION_AVG.equals(localPart)) {
return new Avg();
} else if (XPath2Constants.FUNCTION_MAX.equals(localPart)) {
return new Max();
} else if (XPath2Constants.FUNCTION_MIN.equals(localPart)) {
return new Min();
} else if (XPath2Constants.FUNCTION_SUM.equals(localPart)) {
return new Sum();
} else {
throw new IllegalArgumentException("Unknown Human Task Function: " + localPart);
}
}
return null;
}
public JaxpFunctionResolver(EvaluationContext evalCtx) {
this.evalCtx = evalCtx;
peopleQueryEvaluator = HumanTaskServerHolder.getInstance().getHtServer().getTaskEngine()
.getPeopleQueryEvaluator();
}
/**
* Returns the part of the task’s input message.
* In : part name, task name (optional)
* Out : input message part
*/
public class GetInput implements XPathFunction {
public Object evaluate(List args) throws XPathFunctionException {
MessageDAO inputMsg = evalCtx.getInput();
String partName = (String) args.get(0);
Node matchingElement = null;
if (StringUtils.isNullOrEmpty(partName)) {
throw new HumanTaskRuntimeException("The getInput function should be provided with the part name");
}
if (inputMsg.getBodyData().hasChildNodes()) {
NodeList nodeList = inputMsg.getBodyData().getChildNodes();
for (int i = 0; i < nodeList.getLength(); i++) {
if (partName.trim().equals(nodeList.item(i).getNodeName())) {
matchingElement = nodeList.item(i);
}
}
}
if (matchingElement == null || matchingElement.getFirstChild() == null) {
throw new HumanTaskRuntimeException(
"Cannot find a matching Element for expression evaluation: getInput");
}
return matchingElement.getFirstChild();
}
}
/**
* Returns the part of the task's output message.
* Note: When User completes a task, taskoutput is saved with part name "message". But When user calls setOutput,
* user can specify their own part name.
* In : part name,task name (optional)
* Out : output message part
*/
public class GetOutput implements XPathFunction {
public Object evaluate(List args) throws XPathFunctionException {
MessageDAO outputMsg = evalCtx.getOutput();
String partName = (String) args.get(0);
Node matchingElement = null;
if (StringUtils.isNullOrEmpty(partName)) {
throw new HumanTaskRuntimeException(
"The getOutput function should be provided with the part name");
}
if (outputMsg.getBodyData().hasChildNodes()) {
NodeList nodeList = outputMsg.getBodyData().getChildNodes();
for (int i = 0; i < nodeList.getLength(); i++) {
if (partName.trim().equals(nodeList.item(i).getNodeName())) {
matchingElement = nodeList.item(i);
}
}
}
if (matchingElement == null || matchingElement.getFirstChild() == null) {
throw new HumanTaskRuntimeException(
"Cannot find a matching Element for expression evaluation: getOutput");
}
return matchingElement.getFirstChild();
}
}
/**
* Returns the potential owners of the task. It MUST evaluate to an empty htt:organizationalEntity in case of an
* error. If the task name is not present the current task MUST be considered.
* In : task name (optional)
* Out : potential owners (htt:organizationalEntity)
*/
public class GetPotentialOwners implements XPathFunction {
public Object evaluate(List args) throws XPathFunctionException {
GenericHumanRoleDAO potentialOwners = null;
TaskDAO taskDAO = evalCtx.getTask();
if (args.size() == 0) {
// Case 1: consider current Task.
potentialOwners = getPotentialOwnersFromCtx();
} else if (taskDAO != null && (args.size() == 1) && (args.get(0) instanceof String)) {
String taskName = (String) args.get(0);
if (taskDAO.getName().equals(taskName)) {
//Case 2: TaskName equals to current task, consider current task
potentialOwners = getPotentialOwnersFromCtx();
} else if (!StringUtils.isNullOrEmpty(taskName)) {
// Case 3: Getting specific task. Unsupported.
log.warn("HumanTask Xpath: getPotentialOwners(\"" + taskName + "\")"
+ " operation is not currently supported in this version of WSO2 BPS."
+ " Use getPotentialOwners() instead.");
// We can evaluate only role based and literal based people assignments only. expression based
// people eval will not work here.
// Also we can obtain only the HumanTaskBaseConfiguration, but without taskDAO we can't build
// eval context for that task configuration.
}
}
// else is an Error case, so potentialOwners is null. createOrgEntity will generate an empty
// htt:organizationalEntity element in such a scenario..
return createOrgEntity(potentialOwners);
}
private GenericHumanRoleDAO getPotentialOwnersFromCtx() {
GenericHumanRoleDAO potentialOwners;
potentialOwners = evalCtx.getGenericHumanRole(GenericHumanRoleDAO.GenericHumanRoleType.POTENTIAL_OWNERS);
return potentialOwners;
}
}
/**
* Returns the actual owner of the task. It MUST evaluate to an empty htt:user in case there is no actual owner.
* If the task name is not present the current task MUST be considered.
* In : task name (optional)
* Out : the actual owner (user id as htt:user)
*/
public class GetActualOwner implements XPathFunction {
public Object evaluate(List args) throws XPathFunctionException {
String username = "";
if (args.size() == 0) {
// Case 1: consider current Task.
username = getUserNameFromCtx();
} else if ((args.size() == 1) && (args.get(0) instanceof String)) {
String taskName = (String) args.get(0);
if (evalCtx.getTask().getName().equals(taskName)) {
//Case 2: TaskName equals to current task, consider current task
username = getUserNameFromCtx();
} else if (!StringUtils.isNullOrEmpty(taskName)) {
// Case 3: Getting specific task. Unsupported.
log.warn("HumanTask Xpath: getActualOwner(\"" + taskName + "\")"
+ " operation is not currently supported in this version of WSO2 BPS."
+ " Use getActualOwner() instead.");
}
}
return createHttUser(username);
}
private String getUserNameFromCtx() {
String username = "";
GenericHumanRoleDAO actualOwners = null;
actualOwners = evalCtx.getGenericHumanRole(GenericHumanRoleDAO.GenericHumanRoleType.ACTUAL_OWNER);
for (OrganizationalEntityDAO oe : actualOwners.getOrgEntities()) {
if (OrganizationalEntityDAO.OrganizationalEntityType.USER.equals(oe.getOrgEntityType())) {
username = oe.getName();
break;
}
}
return username;
}
}
/**
* Returns the business administrators of the task. It MUST evaluate to an empty htt:organizationalEntity in case of
* an error. If the task name is not present the current task MUST be considered.
* In : task name (optional)
* Out : business administrators (htt:organizationalEntity)
*/
public class GetBusinessAdministrators implements XPathFunction {
public Object evaluate(List args) throws XPathFunctionException {
GenericHumanRoleDAO businessAdmins = null;
if (args.size() == 0) {
// Case 1: consider current Task.
businessAdmins = getBussinessAdminsFromCtx();
} else if ((args.size() == 1) && (args.get(0) instanceof String)) {
String taskName = (String) args.get(0);
if (evalCtx.getTask().getName().equals(taskName)) {
//Case 2 : TaskName equals to current task, consider current task
businessAdmins = getBussinessAdminsFromCtx();
} else if (!StringUtils.isNullOrEmpty(taskName)) {
// Case 3: Getting specific task. Unsupported.
log.warn("HumanTask Xpath: getBusinessAdministrators(\"" + taskName + "\")"
+ " operation is not currently supported in this version of WSO2 BPS."
+ " Use getBusinessAdministrators() instead.");
}
}
return createOrgEntity(businessAdmins);
}
private GenericHumanRoleDAO getBussinessAdminsFromCtx() {
return evalCtx.getGenericHumanRole(GenericHumanRoleDAO.GenericHumanRoleType.BUSINESS_ADMINISTRATORS);
}
}
/**
* Returns the excluded owners. It MUST evaluate to an empty htt:organizationalEntity in case of an error.
* If the task name is not present the current task MUST be considered.
* In : task name (optional)
* Out : the actual owner (user id as htt:user)
*/
public class GetExcludedOwners implements XPathFunction {
public Object evaluate(List args) throws XPathFunctionException {
GenericHumanRoleDAO excludedOwners = null;
if (args.size() == 0) {
// Case 1: consider current Task.
excludedOwners = getExcludedOwnersFromCtx();
} else if ((args.size() == 1) && (args.get(0) instanceof String)) {
String taskName = (String) args.get(0);
if (evalCtx.getTask().getName().equals(taskName)) {
//Case 2 : TaskName equals to current task, consider current task
excludedOwners = getExcludedOwnersFromCtx();
} else if (!StringUtils.isNullOrEmpty(taskName)) {
// Case 3: Getting specific task. Unsupported.
log.warn("HumanTask Xpath: getExcludedOwners(\"" + taskName + "\")"
+ " operation is not currently supported in this version of WSO2 BPS."
+ " Use getExcludedOwners() instead.");
}
}
return createOrgEntity(excludedOwners);
}
private GenericHumanRoleDAO getExcludedOwnersFromCtx() {
return evalCtx.getGenericHumanRole(GenericHumanRoleDAO.GenericHumanRoleType.EXCLUDED_OWNERS);
}
}
/**
* Returns the initiator of the task. It MUST evaluate to an empty htt:user in case there is no initiator.
* <p/>
* If the task name is not present the current task MUST be considered.
* In : task name (optional)
* Out :the task initiator (user id as htt:user)
*/
public class GetTaskInitiator implements XPathFunction {
public Object evaluate(List args) throws XPathFunctionException {
String username = "";
if (args.size() == 0) {
username = getUserNameFromCtx();
} else if ((args.size() == 1) && (args.get(0) instanceof String)) {
String taskName = (String) args.get(0);
if (evalCtx.getTask().getName().equals(taskName)) {
//Case 2 : TaskName equals to current task, consider current task
username = getUserNameFromCtx();
} else if (!StringUtils.isNullOrEmpty(taskName)) {
// Case 3: Getting specific task. Unsupported.
log.warn("HumanTask Xpath: getTaskInitiator(\"" + taskName + "\")"
+ " operation is not currently supported in this version of WSO2 BPS."
+ " Use getTaskInitiator() instead.");
}
}
return createHttUser(username);
}
private String getUserNameFromCtx() {
String username = "";
GenericHumanRoleDAO taskInitiator;
taskInitiator = evalCtx.getGenericHumanRole(GenericHumanRoleDAO.GenericHumanRoleType.TASK_INITIATOR);
for (OrganizationalEntityDAO oe : taskInitiator.getOrgEntities()) {
if (OrganizationalEntityDAO.OrganizationalEntityType.USER.equals(oe.getOrgEntityType())) {
username = oe.getName();
break;
}
}
return username;
}
}
/**
* Returns the priority of the task. It MUST evaluate to “5” in case the priority is not explicitly set.
* If the task name is not present the current task MUST be considered.
* In : task name (optional)
* Out : priority (htt:tPriority)
*/
public class GetTaskPriority implements XPathFunction {
public Object evaluate(List args) throws XPathFunctionException {
if (args.size() == 0) {
Integer priority = evalCtx.getTask().getPriority();
return priority;
} else if ((args.size() == 1) && (args.get(0) instanceof String)) {
String taskName = (String) args.get(0);
if (evalCtx.getTask().getName().equals(taskName)) {
//Case 2 : TaskName equals to current task, consider current task
Integer priority = evalCtx.getTask().getPriority();
return priority;
} else if (!StringUtils.isNullOrEmpty(taskName)) {
// Case 3: Getting specific task. Unsupported.
log.warn("HumanTask Xpath: getTaskPriority(\"" + taskName + "\")"
+ " operation is not currently supported in this version of WSO2 BPS."
+ " Use getTaskPriority() instead.");
}
}
return 5;
}
}
/**
* Returns the stakeholders of the task. It MUST evaluate to an empty htt:organizationalEntity in case of an error.
* If the task name is not present the current task MUST be considered.
* In : task name (optional)
* Out : task stakeholders (htt:organizationalEntity)
*/
public class GetTaskStakeholders implements XPathFunction {
public Object evaluate(List args) throws XPathFunctionException {
GenericHumanRoleDAO stakeholders = null;
if (args.size() == 0) {
// Case 1: consider current Task.
stakeholders = getStakeholdersFromCtx();
} else if ((args.size() == 1) && (args.get(0) instanceof String)) {
String taskName = (String) args.get(0);
if (evalCtx.getTask().getName().equals(taskName)) {
//Case 2 : TaskName equals to current task, consider current task
stakeholders = getStakeholdersFromCtx();
} else if (!StringUtils.isNullOrEmpty(taskName)) {
// Case 3: Getting specific task. Unsupported.
log.warn("HumanTask Xpath: getTaskStakeholders(\"" + taskName + "\")"
+ " operation is not currently supported in this version of WSO2 BPS."
+ " Use getTaskStakeholders() instead.");
}
}
return createOrgEntity(stakeholders);
}
private GenericHumanRoleDAO getStakeholdersFromCtx() {
return evalCtx.getGenericHumanRole(GenericHumanRoleDAO.GenericHumanRoleType.STAKEHOLDERS);
}
}
/**
* Constructs an organizationalEntity containing every user that occurs in both set1 and set2, eliminating duplicate
* users.
* In : set1 (htt:organizationalEntity|htt:user), set2 (htt:organizationalEntity|htt:user)
* Out : result (htt:organizationalEntity)
*/
public class Intersect implements XPathFunction {
@Override
public Object evaluate(List args) throws XPathFunctionException {
if (args.size() != 2) {
throw new HumanTaskRuntimeException("Invalid number of arguments for expression: except");
}
if (!(args.get(0) instanceof Node && args.get(1) instanceof Node)) {
throw new HumanTaskRuntimeException("Invalid arguments for expression: except");
}
Node node1 = (Node) args.get(0);
Node node2 = (Node) args.get(1);
Set<String> set1 = new HashSet<String>();
Set<String> set2 = new HashSet<String>();
parseOrgEntityTypeOrUser(node1, set1);
parseOrgEntityTypeOrUser(node2, set2);
set1.retainAll(set2);
return createOrgEntity(set1);
}
}
/**
* Constructs an organizationalEntity containing every user that occurs in either set1 or set2, eliminating
* duplicate users.
* In : set1 (htt:organizationalEntity|htt:user),set2(htt:organizationalEntity|htt:user)
* Out : result (htt:organizationalEntity)
*/
public class Union implements XPathFunction {
@Override
public Object evaluate(List args) throws XPathFunctionException {
if (args.size() != 2) {
throw new HumanTaskRuntimeException("Invalid number of arguments for expression: except");
}
if (!(args.get(0) instanceof Node && args.get(1) instanceof Node)) {
throw new HumanTaskRuntimeException("Invalid arguments for expression: except");
}
Node node1 = (Node) args.get(0);
Node node2 = (Node) args.get(1);
Set<String> resoledUsers = new HashSet<String>();
parseOrgEntityTypeOrUser(node1, resoledUsers);
parseOrgEntityTypeOrUser(node2, resoledUsers);
return createOrgEntity(resoledUsers);
}
}
/**
* Constructs an organizationalEntity containing every user that occurs in set1 but not in set2.
* Note: TODO: This function is required to allow enforcing the separation of duties (“4-eyes principle”).
* In : set1 (htt:organizationalEntity|htt:user), set2 (htt:organizationalEntity|htt:user)
* Out : result (htt:organizationalEntity)
*/
public class Except implements XPathFunction {
@Override
public Object evaluate(List args) throws XPathFunctionException {
if (args.size() != 2) {
throw new HumanTaskRuntimeException(
"Invalid number of arguments: " + args.size() + ", for expression: except");
}
if (!(args.get(0) instanceof Node && args.get(1) instanceof Node)) {
throw new HumanTaskRuntimeException(
"Invalid arguments :" + args.get(0) + " , " + args.get(1) + " , for expression: except");
}
Node node1 = (Node) args.get(0);
Node node2 = (Node) args.get(1);
Set<String> resoledUsers = new HashSet<String>();
Set<String> excludedUsers = new HashSet<String>();
parseOrgEntityTypeOrUser(node1, resoledUsers);
parseOrgEntityTypeOrUser(node2, excludedUsers);
resoledUsers.removeAll(excludedUsers);
return createOrgEntity(resoledUsers);
}
}
public void parseOrgEntityTypeOrUser(Node node1, Set<String> resoledUsers) {
if (node1.getNodeType() == Node.ELEMENT_NODE) {
if (organizationalEntityQname.getNamespaceURI().equals(node1.getNamespaceURI()) && organizationalEntityQname
.getLocalPart().equals(node1.getLocalName())) {
//Parsing organizationalEntity element
parseOrgEntity(node1, resoledUsers);
} else if (userQname.getNamespaceURI().equals(node1.getNamespaceURI()) && userQname.getLocalPart()
.equals(node1.getLocalName())) {
//Parsing user element
String username = node1.getTextContent();
if (username != null) {
username = username.trim();
if (username.length() > 0) {
resoledUsers.add(username);
}
}
} else if (node1.hasChildNodes()) {
NodeList nodeList = node1.getChildNodes();
Node childNode = null;
for (int j = 0; j < nodeList.getLength(); j++) {
if (Node.ELEMENT_NODE == nodeList.item(j).getNodeType()) {
childNode = nodeList.item(j);
break;
}
}
//Parsing tOrganizationalEntity type also to have consistence the expression logic.
if (childNode != null) {
if (childNode.getNodeType() == Node.ELEMENT_NODE && organizationalEntityQname.getNamespaceURI()
.equals(childNode.getNamespaceURI())) {
if (userQname.getLocalPart().equals(childNode.getLocalName()) || groupQname.getLocalPart()
.equals(childNode.getLocalName())) {
parseOrgEntity(node1, resoledUsers);
}
}
} else { // No element found. this is text content.
String username = node1.getTextContent();
if (username != null) {
username = username.trim();
if (username.length() > 0) {
resoledUsers.add(username);
}
}
}
} else {
throw new HumanTaskRuntimeException(
"This function should be provided with htt:organizationalEntity or htt:user element as an argument.");
}
}
}
public void parseOrgEntity(Node node, Set<String> resoledUsers) {
//Reading users
NodeList userList = ((Element) node)
.getElementsByTagNameNS(userQname.getNamespaceURI(), userQname.getLocalPart());
int userListLength = userList.getLength();
for (int j = 0; j < userListLength; j++) {
Node item = userList.item(j);
String username = item.getTextContent();
if (username != null) {
username = username.trim();
if (username.length() > 0) {
resoledUsers.add(username);
}
}
}
//Reading groups
NodeList groupList = ((Element) node)
.getElementsByTagNameNS(groupQname.getNamespaceURI(), groupQname.getLocalPart());
int groupListLength = groupList.getLength();
for (int j = 0; j < groupListLength; j++) {
Node item = groupList.item(j);
String groupName = item.getTextContent();
if (groupName != null) {
groupName = groupName.trim();
if (groupName.length() > 0) {
resoledUsers.addAll(peopleQueryEvaluator.getUserNameListForRole(groupName));
}
}
}
}
/**
* Create organizationalEntity Node from a given username list
* <p/>
* @param userList : User name set.
* @return : organizationalEntity node
*/
private Node createOrgEntity(Set<String> userList) {
Document doc = DOMUtils.newDocument();
Element orgEntity = doc
.createElementNS(organizationalEntityQname.getNamespaceURI(), organizationalEntityQname.getLocalPart());
if (userList != null) {
Element user = null;
for (String userName : userList) {
user = doc.createElementNS(userQname.getNamespaceURI(), userQname.getLocalPart());
user.setTextContent(userName);
orgEntity.appendChild(user);
}
}
return orgEntity;
}
/**
* Create organizationalEntity Node from GenericHumanRoleDAO.
* <p/>
* @param ghr : GenericHumanRoleDAO
* @return : organizationalEntity node
*/
private Node createOrgEntity(GenericHumanRoleDAO ghr) {
Document doc = DOMUtils.newDocument();
Element orgEntity = doc
.createElementNS(organizationalEntityQname.getNamespaceURI(), organizationalEntityQname.getLocalPart());
if (ghr != null) {
Element userOrGroup;
List<OrganizationalEntityDAO> orgEntities = ghr.getOrgEntities();
for (OrganizationalEntityDAO oe : orgEntities) {
if (OrganizationalEntityDAO.OrganizationalEntityType.GROUP.equals(oe.getOrgEntityType())) {
userOrGroup = doc.createElementNS(groupQname.getNamespaceURI(), groupQname.getLocalPart());
userOrGroup.setTextContent(oe.getName());
orgEntity.appendChild(userOrGroup);
} else if (OrganizationalEntityDAO.OrganizationalEntityType.USER.equals(oe.getOrgEntityType())) {
userOrGroup = doc.createElementNS(userQname.getNamespaceURI(), userQname.getLocalPart());
userOrGroup.setTextContent(oe.getName());
orgEntity.appendChild(userOrGroup);
}
}
}
return orgEntity;
}
/**
* Creates htt:User node for given user name
* @param name : String username
* @return : htt:user node
*/
private Node createHttUser(String name) {
Document doc = DOMUtils.newDocument();
Element httUser = doc.createElementNS(userQname.getNamespaceURI(), userQname.getLocalPart());
httUser.setTextContent(name);
return httUser;
}
/**
* Returns the number of finished sub tasks of a task
* If the task name is not present the current task MUST be considered
* NOTE:finished status are Completed,failed,Error,Exited and Obsolete
* In ; task name (optional)
* Out : Number of the finished task sub-tasks. If the task doesn't have sub tasks then 0 is returned
*/
public class GetCountOfFinishedSubTasks implements XPathFunction {
@Override
public Object evaluate(List args) throws XPathFunctionException {
if (args.size() == 0) {
//Case 1 : Consider current task
List<TaskDAO> subTasksList = evalCtx.getTask().getSubTasks(); //nullable
return getCount(subTasksList);
} else if ((args.size() == 1) && (args.get(0) instanceof String)) {
String taskName = (String) args.get(0);
if (evalCtx.getTask().getName().equals(taskName)) {
//Case 2 : TaskName equals to current task, consider current task
List<TaskDAO> subTasksList = evalCtx.getTask().getSubTasks();
return getCount(subTasksList);
} else {
// Case 3: Getting specific task. Unsupported.
log.warn("HumanTask Xpath: getCountOfFinishedSubTasks(\"" + taskName + "\")"
+ " operation is not currently supported in this version of WSO2 BPS."
+ " Use getCountOfFinishedSubTasks() instead.");
}
}
return 0;
}
private Integer getCount(List<TaskDAO> subTasksList) {
int count = 0;
for (TaskDAO subTask : subTasksList) {
TaskStatus status = subTask.getStatus();
if (status.equals(TaskStatus.COMPLETED) || status.equals(TaskStatus.FAILED) || status
.equals(TaskStatus.ERROR) || status.equals(TaskStatus.EXITED) || status
.equals(TaskStatus.OBSOLETE)) {
count++;
}
}
return count;
}
}
/**
* Returns the number of sub tasks of a task,
* If the task name is not present the current task MUST be considered
* In : task name (optional)
* Out : Number of the task sub-tasks. If the task doesn't have sub tasks then 0 is returned
*/
public class GetCountOfSubTasks implements XPathFunction {
@Override
public Object evaluate(List args) throws XPathFunctionException {
if (args.size() == 0) {
//Case 1 : Consider current task
return evalCtx.getTask().getSubTasks().size();
} else if ((args.size() == 1) && (args.get(0) instanceof String)) {
String taskName = (String) args.get(0);
if (evalCtx.getTask().getName().equals(taskName)) {
//Case 2 : TaskName equals to current task, consider current task
return evalCtx.getTask().getSubTasks().size();
} else {
// Case 3: Getting specific task. Unsupported.
log.warn("HumanTask Xpath: getCountOfSubTasks(\"" + taskName + "\")"
+ " operation is not currently supported in this version of WSO2 BPS."
+ " Use getCountOfSubTasks() instead.");
}
}
return 0;
}
}
/**
* Returns the number of a task sub tasks that are in the specified state
* If the task name is not present the current task MUST be considered
* In : state task name (optional)
* Out : Number of the task sub tasks in the specified state. If the task doesn't have sub tasks then 0 is returned
*/
public class GetCountOfSubTasksInState implements XPathFunction {
@Override
public Object evaluate(List args) throws XPathFunctionException {
{
if (args.size() == 1) {
//Case 1 : Consider current task
return countSubTasks(args.get(0));
} else if ((args.size() == 2) && (args.get(1) instanceof String)) {
String taskName = (String) args.get(1);
if (evalCtx.getTask().getName().equals(taskName)) {
//Case 2 : TaskName equals to current task, consider current task
return countSubTasks(args.get(0));
} else {
// Case 3: Getting specific task. Unsupported.
log.warn("HumanTask Xpath: getCountOfSubTasks(\"" + taskName + "\")"
+ " operation is not currently supported in this version of WSO2 BPS."
+ " Use getCountOfSubTasks() instead.");
}
} else {
throw new HumanTaskRuntimeException("Invalid number of arguments : " + args.size()
+ ", for function getCountOfSubTasksInState()");
}
return 0;
}
}
private Integer countSubTasks(Object arg) {
int count = 0;
if (arg instanceof String && isValidStatus((String) arg)) {
//correct argument
List<TaskDAO> subTaskList = evalCtx.getTask().getSubTasks();
String state = (String) arg;
for (TaskDAO subTask : subTaskList) {
if (state.equalsIgnoreCase(subTask.getStatus().name())) {
count++;
}
}
} else {
//invalid argument
throw new HumanTaskRuntimeException(
"Invalid argument: " + arg + ", for function getCountOfSubTasksInState()");
}
return count;
}
}
/**
* check the validity of a given task status
* @param status
* @return true for valid tasks
*/
private boolean isValidStatus(String status) {
for (TaskStatus taskStatus : TaskStatus.values()) {
if (status.equalsIgnoreCase(taskStatus.name())) {
return true;
}
}
return false;
}
/**
* Returns the concatenation of all string nodes,
* returns an empty string for an empty node-set
* logic: goes through the list and concat textContent to result string,
* return empty string for empty list.
* In : node-set of string nodes
*/
public static class Concat implements XPathFunction {
@Override
public Object evaluate(List args) throws XPathFunctionException {
String result = "";
if (args.size() == 1 && args.get(0) instanceof ArrayList) {
ArrayList nodeList = (ArrayList) args.get(0);
for (int i = 0; i < nodeList.size(); i++) {
try {
String item = (((Element) nodeList.get(i))).getTextContent();
result = result.concat(item);
} catch (DOMException e) {
throw new HumanTaskRuntimeException(
"Invalid arguments:" + args.get(0) + ", for function concat()", e);
} catch (ClassCastException e) {
throw new HumanTaskRuntimeException(
"Invalid arguments:" + args.get(0) + " , for function concat()",e);
}
}
} else {
throw new HumanTaskRuntimeException("Invalid arguments:" + args + " , for function concat()");
}
return result;
}
}
/**
* Returns the concatenation of all string nodes, separated by the specified delimiter string,
* returns an empty string for an empty node-set.
* logic: go through the list and conact textContent and the delimiter to result,
* if the last item of the list, delimiter not added.
* In : node-set of string nodes,delimiter string
*/
public static class ConcatWithDelimiter implements XPathFunction {
@Override
public Object evaluate(List args) throws XPathFunctionException {
String result = "";
if (args.size() == 2 && args.get(0) instanceof ArrayList && args.get(1) instanceof String) {
ArrayList nodeList = (ArrayList) args.get(0);
String delimiter = (String) args.get(1);
int length = nodeList.size();
for (int i = 0; i < length; i++) {
try {
String item = ((Element) nodeList.get(i)).getTextContent();
result = result.concat(item);
if (i != length - 1) {
result = result.concat(delimiter);
}
} catch (DOMException e) {
throw new HumanTaskRuntimeException(
"Invalid arguments:" + args.get(0) + ", for function concatWithDelimiter()", e);
} catch (ClassCastException e) {
throw new HumanTaskRuntimeException(
"Invalid arguments:" + args.get(0) + " , for function concatWithDelimiter()",e);
}
}
} else {
throw new HumanTaskRuntimeException(
"Invalid arguments: " + args + ", for function concatWithDelimiter()");
}
return result;
}
}
/**
* Generates a HashMap containing string items and it's frequency in a given NodeList
* logic : create a HashMap where key:textContent value:frequency of the textContent in the list
* @param list : NodeList
* @return : Map<String,Integer> map of string occurrence frequencies
*/
private Map<String, Integer> generateFrequencyMap(ArrayList list) throws HumanTaskRuntimeException {
Map<String, Integer> frequencyMap = new HashMap<String, Integer>();
for (int i = 0; i < list.size(); i++) {
try {
String item = ((Element) list.get(i)).getTextContent();
if (frequencyMap.containsKey(item)) {
int frequency = frequencyMap.get(item) + 1;
frequencyMap.put(item, frequency);
} else {
frequencyMap.put(item, 1);
}
} catch (DOMException e) {
throw new HumanTaskRuntimeException("Invalid arguments:" + list, e);
} catch (ClassCastException e) {
throw new HumanTaskRuntimeException("Invalid arguments:" + list, e);
}
}
return frequencyMap;
}
/**
* Returns the least frequently occurring string value within all string nodes,
* or an empty string in case of a tie or for an empty node-set
* logic: get the frequency map from generateFrequencyMap() and go through the map
* recording the least value, corresponding result and if the least values are same
* set tie = true. If tie=true returns empty string, else result string.
* In : node-set of string nodes
*/
public class LeastFrequentOccurence implements XPathFunction {
@Override
public Object evaluate(List args) throws XPathFunctionException {
if (args.size() == 1 && args.get(0) instanceof ArrayList) {
try {
Map<String, Integer> frequencyMap = generateFrequencyMap((ArrayList) args.get(0));
boolean tie = false;
String result = "";
int least = Integer.MAX_VALUE;
for (Map.Entry<String, Integer> entry : frequencyMap.entrySet()) {
//check if the minimum
if (entry.getValue() < least) {
//new least occurrence
least = entry.getValue();
result = entry.getKey();
tie = false;
} else if (entry.getValue() == least) {
//a tie
tie = true;
}
}
if (tie) {
return "";
} else {
return result;
}
} catch (HumanTaskRuntimeException e) {
throw new HumanTaskRuntimeException(
"Error in processing arguments" + args.get(0) + " for function leastFrequentOccurence()",
e);
}
} else {
throw new HumanTaskRuntimeException(
"Invalid arguments:" + args + ", for function leastFrequentOccurence()");
}
}
}
/**
* Returns the most frequently occurring string value within all string nodes,
* or an empty string in case of a tie or for an empty node-set.
* logic: get the frequency map from generateFrequencyMap() and go through the map
* recording the maximum value, corresponding result and if the max values are same
* set tie = true. If tie=true returns empty string, else result string.
* In : node-set of string nodes
*/
public class MostFrequentOccurence implements XPathFunction {
@Override
public Object evaluate(List args) throws XPathFunctionException {
if (args.size() == 1 && args.get(0) instanceof ArrayList) {
try {
Map<String, Integer> frequencyMap = generateFrequencyMap((ArrayList) args.get(0));
boolean tie = false;
int max = Integer.MIN_VALUE;
String result = "";
for (Map.Entry<String, Integer> entry : frequencyMap.entrySet()) {
if (entry.getValue() > max) {
//new max
max = entry.getValue();
result = entry.getKey();
tie = false;
} else if (entry.getValue() == max) {
//a tie
tie = true;
}
}
if (tie) {
return "";
} else {
return result;
}
} catch (HumanTaskRuntimeException e) {
throw new HumanTaskRuntimeException(
"Error in processing arguments for function mostFrequentOccurence()", e);
}
} else {
throw new HumanTaskRuntimeException("Invalid arguments for function mostFrequentOccurence()");
}
}
}
/**
* Returns the most frequently occurring string value if its occurrence is above the specified percentage and there is no tie,
* or an empty string otherwise (including an empty node-set)
* logic: get the frequency map from generateFrequencyMap() and go through the map
* recording the max value, corresponding result and if the max values are same
* set tie = true. If tie=true returns empty string, if the frequency percentage
* is higher than input percentage return result siring, else empty string.
* Note: input percentage should be given as parts of 100.
* In : node-set of string nodes, percentage
*/
public class VoteOnString implements XPathFunction {
@Override
public Object evaluate(List args) throws XPathFunctionException {
if (args.size() == 2 && args.get(0) instanceof ArrayList && args.get(1) instanceof Number) {
try {
ArrayList list = (ArrayList) args.get(0);
Number percentage = (Number) args.get(1);
Map<String, Integer> frequencyMap = generateFrequencyMap((ArrayList) args.get(0));
boolean tie = false;
int max = Integer.MIN_VALUE;
String result = "";
for (Map.Entry<String, Integer> entry : frequencyMap.entrySet()) {
if (entry.getValue() > max) {
//new max
max = entry.getValue();
result = entry.getKey();
tie = false;
} else if (entry.getValue() == max) {
//a tie
tie = true;
}
}
if (list.size() > 0 && !tie && ((float)max * 100 / (float)list.size()) > percentage.floatValue()) {
return result;
} else {
return "";
}
} catch (HumanTaskRuntimeException e) {
throw new HumanTaskRuntimeException("Error in processing arguments for function voteOnString()", e);
}
} else {
throw new HumanTaskRuntimeException("Invalid arguments for function voteOnString()");
}
}
}
/**
* Returns the conjunction of all boolean nodes - returns false for an empty node-set
* logic: as long as a true is found keep result as true, when the first false is found
* return false immediately, else return true.
* Note: Assumed the nodes contains, "true","false","1" or "0"
* In : node-set of boolean nodes
*/
public static class And implements XPathFunction {
@Override
public Object evaluate(List args) throws XPathFunctionException {
if (args.size() == 1 && args.get(0) instanceof ArrayList) {
boolean result = false;
ArrayList list = (ArrayList) args.get(0);
for (int i = 0; i < list.size(); i++) {
try {
//iterate through node list
String nodeValue = ((Element) list.get(i)).getTextContent();
if (nodeValue.equalsIgnoreCase(XPath2Constants.NODE_VALUE_TRUE) || nodeValue.equalsIgnoreCase(XPath2Constants.NODE_VALUE_ONE)) {
//true
result = true;
} else if (nodeValue.equalsIgnoreCase(XPath2Constants.NODE_VALUE_FALSE) || nodeValue.equalsIgnoreCase(XPath2Constants.NODE_VALUE_ZERO)) {
//false, no point of continuing
return false;
} else {
throw new HumanTaskRuntimeException(
"Invalid argument: " + nodeValue + " for function and(), only booleans allowed");
}
} catch (DOMException e) {
throw new HumanTaskRuntimeException("Invalid arguments: " + args.get(0) + ", for function and()",
e);
} catch (ClassCastException e) {
throw new HumanTaskRuntimeException("Invalid arguments: " + args.get(0) + ", for function and()",
e);
}
}
return result;
} else {
throw new HumanTaskRuntimeException("Invalid arguments: " + args + " for function and()");
}
}
}
/**
* Returns the disjunction of all boolean nodes - returns false for an empty node-set
* logic: go through the list, if a true found, returns true immediately else return false.
* return false for empty list
* In : node-set of boolean nodes
*/
public static class Or implements XPathFunction {
@Override
public Object evaluate(List args) throws XPathFunctionException {
if (args.size() == 1 && args.get(0) instanceof ArrayList) {
boolean result = false;
ArrayList list = (ArrayList) args.get(0);
for (int i = 0; i < list.size(); i++) {
try {
//iterate through element list
String nodeValue = ((Element) list.get(i)).getTextContent();
if (nodeValue.equalsIgnoreCase(XPath2Constants.NODE_VALUE_TRUE) || nodeValue.equalsIgnoreCase(XPath2Constants.NODE_VALUE_ONE)) {
//true, no point of continuing
return true;
} else if (nodeValue.equalsIgnoreCase(XPath2Constants.NODE_VALUE_FALSE) || nodeValue.equalsIgnoreCase(XPath2Constants.NODE_VALUE_ZERO)) {
//false,
result = false;
} else {
throw new HumanTaskRuntimeException(
"Invalid argument: " + nodeValue + " for function or(), only boolean nodes allowed");
}
} catch (DOMException e) {
throw new HumanTaskRuntimeException("Invalid arguments: " + args.get(0) + ", for function or()",
e);
} catch (ClassCastException e) {
throw new HumanTaskRuntimeException("Invalid arguments: " + args.get(0) + ", for function or()",
e);
}
}
return result;
} else {
throw new HumanTaskRuntimeException("Invalid arguments: " + args + ", for function or()");
}
}
}
/**
* Returns the most frequently occurring boolean value if its occurrence is above the specified percentage,
* or false otherwise (including an empty node-set)
* logic: get the true and false count. if true is higher, check if above the percentage and return true.
* return false for any other case. no need to check percentage for false.
* In : node-set of boolean nodes, percentage
*/
public static class Vote implements XPathFunction {
@Override
public Object evaluate(List args) throws XPathFunctionException {
if (args.size() == 2 && args.get(0) instanceof ArrayList && args.get(1) instanceof Number) {
ArrayList list = (ArrayList) args.get(0);
Number percentage = (Number) args.get(1);
int trueCount = 0;
int falseCount = 0;
for (int i = 0; i < list.size(); i++) {
try {
String nodeValue = ((Element) list.get(i)).getTextContent();
if (nodeValue.equalsIgnoreCase(XPath2Constants.NODE_VALUE_TRUE) || nodeValue.equalsIgnoreCase(XPath2Constants.NODE_VALUE_ONE)) {
//true
trueCount++;
} else if (nodeValue.equalsIgnoreCase(XPath2Constants.NODE_VALUE_FALSE) || nodeValue.equalsIgnoreCase(XPath2Constants.NODE_VALUE_ZERO)) {
//false
falseCount++;
} else {
//invalid
throw new HumanTaskRuntimeException(
"Invalid argument: " + nodeValue + ", only boolean nodes allowed");
}
} catch (DOMException e) {
throw new HumanTaskRuntimeException(
"Invalid arguments: " + args.get(0) + ", for function vote()", e);
} catch (ClassCastException e) {
throw new HumanTaskRuntimeException(
"Invalid arguments: " + args.get(0) + ", for function vote()", e);
}
}
float truePercentage = (float)(trueCount * 100) / (float)(trueCount + falseCount);
if (trueCount > falseCount && truePercentage > percentage.floatValue()) {
//returns true
return true;
}
//no point of evaluating for false, anyway false returned
return false;
} else {
throw new HumanTaskRuntimeException("Invalid arguments: " + args + ", for function vote()");
}
}
}
/**
* Returns the average value of all number nodes - returns NaN for an empty node-set
* logic: take the sum of all nodes, return dividing by list size. return NaN for empty list.
* In : node-set of number nodes
*/
public static class Avg implements XPathFunction {
@Override
public Object evaluate(List args) throws XPathFunctionException {
if (args.size() == 1 && args.get(0) instanceof ArrayList) {
ArrayList list = (ArrayList) args.get(0);
if (list.size() > 0) {
try {
//at least one element exists
float sum = 0;
for (int i = 0; i < list.size(); i++) {
float nodeValue = Float.parseFloat(((Element) list.get(i)).getTextContent());
sum += nodeValue;
}
return sum / list.size();
} catch (DOMException e) {
throw new HumanTaskRuntimeException("Invalid arguments: " + args.get(0) + ", for function avg()",
e);
} catch (NumberFormatException e) {
throw new HumanTaskRuntimeException("Invalid arguments: " + args.get(0) + ", for function avg()",
e);
} catch (ClassCastException e) {
throw new HumanTaskRuntimeException("Invalid arguments: " + args.get(0) + ", for function avg()",
e);
}
}
return Double.NaN;
} else {
throw new HumanTaskRuntimeException("Invalid arguments:" + args + " for function avg()");
}
}
}
/**
* Returns the maximum value of all number nodes - returns NaN for an empty node-set
* logic: go through the list and find the maximum value. return NaN for empty list.
* In : node-set of number nodes
*/
public static class Max implements XPathFunction {
@Override
public Object evaluate(List args) throws XPathFunctionException {
if (args.size() == 1 && args.get(0) instanceof ArrayList) {
ArrayList list = (ArrayList) args.get(0);
if (list.size() > 0) {
//at least one element exists
float max = Float.MIN_VALUE;
for (int i = 0; i < list.size(); i++) {
try {
float nodeValue = Float.parseFloat(((Element) list.get(i)).getTextContent());
if (nodeValue > max) {
//new max
max = nodeValue;
}
} catch (DOMException e) {
throw new HumanTaskRuntimeException(
"Invalid arguments: " + args.get(0) + ", for function max()", e);
} catch (NumberFormatException e) {
throw new HumanTaskRuntimeException(
"Invalid arguments: " + args.get(0) + ", for function max()", e);
} catch (ClassCastException e) {
throw new HumanTaskRuntimeException(
"Invalid arguments: " + args.get(0) + ", for function max()", e);
}
}
return max;
}
//NaN for empty node sets
return Double.NaN;
} else {
throw new HumanTaskRuntimeException("Invalid arguments:" + args + ", for function max()");
}
}
}
/**
* Returns the minimum value of all number nodes - returns NaN for an empty node-set
* logic: go through the list and find the minimum value. return NaN for empty list.
* In : node-set of number nodes
*/
public static class Min implements XPathFunction {
@Override
public Object evaluate(List args) throws XPathFunctionException {
if (args.size() == 1 && args.get(0) instanceof ArrayList) {
ArrayList list = (ArrayList) args.get(0);
if (list.size() > 0) {
float min = Float.MAX_VALUE;
//at least one element exists
for (int i = 0; i < list.size(); i++) {
try {
float nodeValue = Float.parseFloat(((Element) list.get(i)).getTextContent());
if (nodeValue < min) {
//new min
min = nodeValue;
}
} catch (DOMException e) {
throw new HumanTaskRuntimeException(
"Invalid arguments: " + args.get(0) + ", for function min()", e);
} catch (NumberFormatException e) {
throw new HumanTaskRuntimeException(
"Invalid arguments: " + args.get(0) + ", for function min()", e);
} catch (ClassCastException e) {
throw new HumanTaskRuntimeException(
"Invalid arguments: " + args.get(0) + ", for function min()", e);
}
}
return min;
}
//NaN for empty node sets
return Double.NaN;
} else {
throw new HumanTaskRuntimeException("Invalid arguments:" + args + ", for function min()");
}
}
}
/**
* Returns the sum value of all number nodes - returns NaN for an empty node-set
* logic: go through the list and add the values to result. return NaN for empty list.
* In : node-set of number nodes
*/
public static class Sum implements XPathFunction {
@Override
public Object evaluate(List args) throws XPathFunctionException {
if (args.size() == 1 && args.get(0) instanceof ArrayList) {
ArrayList list = (ArrayList) args.get(0);
if (list.size() > 0) {
//at least one element exists
float sum = 0;
for (int i = 0; i < list.size(); i++) {
try {
float nodeValue = Float.parseFloat(((Element) list.get(i)).getTextContent());
sum += nodeValue;
} catch (DOMException e) {
throw new HumanTaskRuntimeException(
"Invalid arguments: " + args.get(0) + ", for function sum()", e);
} catch (NumberFormatException e) {
throw new HumanTaskRuntimeException(
"Invalid arguments: " + args.get(0) + ", for function sum()", e);
} catch (ClassCastException e) {
throw new HumanTaskRuntimeException(
"Invalid arguments: " + args.get(0) + ", for function sum()", e);
}
}
return sum;
}
return Double.NaN;
} else {
throw new HumanTaskRuntimeException("Invalid arguments: " + args + " for function sum()");
}
}
}
}