import org.simantics.sysdyn.utils.Function.Input;\r
import org.simantics.sysdyn.utils.Function.Output;\r
import org.simantics.sysdyn.utils.Function.Parameter;\r
+import org.simantics.utils.datastructures.Pair;\r
\r
/**\r
* See UnitCheckingNodeFactory for mapping\r
// Get the expected inputs\r
ArrayList<Input> inputs = f.getInputList();\r
// Get arguments from parser.\r
- ArrayList<UnitResult> argumentUnits = getArgumentsOf(functionArgumentsNode, units, functions, allowEquivalents);\r
+ ArrayList<Pair<UnitResult, String>> argumentUnits = getArgumentsOf(functionArgumentsNode, units, functions, allowEquivalents);\r
\r
+ //HashMap<String, String> correspondencies = getReplacementUnitCorrespondencies(inputs, argumentUnits);\r
+\r
// If the function input parameter units are OK, set the output unit accordingly. \r
- if (matchInputs(inputs, argumentUnits)) {\r
- result = setUnitResult(units, result, inputs, argumentUnits, f, functions, allowEquivalents);\r
+ if (matchInputs(inputs, argumentUnits, f, functions, allowEquivalents, units)) {\r
+ Output o = f.getOutputList().get(0); // Support only single return value functions.\r
+ result = getUnitResult(units, result, inputs, argumentUnits, f, functions, allowEquivalents, o);\r
return result;\r
}\r
\r
throw u;\r
}\r
\r
- private UnitResult setUnitResult(\r
+ private static UnitResult getUnitResult(\r
HashMap<String, String> units, \r
UnitResult result, \r
ArrayList<Input> inputs, \r
- ArrayList<UnitResult> argumentUnits, \r
+ ArrayList<Pair<UnitResult,String>> argumentUnits, \r
Function f,\r
ArrayList<Function> functions,\r
- boolean allowEquivalents) throws UnitCheckingException {\r
- Output o = f.getOutputList().get(0); // Support only single return value functions.\r
+ boolean allowEquivalents,\r
+ Parameter p) throws UnitCheckingException {\r
\r
- if (Parameter.ANY.equals(o.unit)) {\r
+ if (Parameter.ANY.equals(p.unit)) {\r
result.setUnitType(UnitType.ANY);\r
- } else if ("1".equals(o.unit)) { // TODO: see if this should be changed to something else\r
+ } else if ("1".equals(p.unit)) { // TODO: see if this should be changed to something else\r
result.setUnitType(UnitType.SCALAR);\r
} else {\r
// Replace TIME with actual time unit.\r
String timeUnit = units.get("time");\r
- String timeReplaced = o.unit.replace(Parameter.TIME, timeUnit);\r
+ String timeReplaced = p.unit.replace(Parameter.TIME, timeUnit);\r
\r
// Replace 'p, 'q, etc. in output with units from actual inputs.\r
HashMap<String, String> correspondencies = getReplacementUnitCorrespondencies(inputs, argumentUnits);\r
return result;\r
}\r
\r
- private String replaceCorrespondencies(Function f, String original,\r
+ private static String replaceCorrespondencies(Function f, String original,\r
HashMap<String, String> correspondencies) throws UnitCheckingException {\r
int index;\r
String ret = new String(original);\r
return ret;\r
}\r
\r
- private HashMap<String, String> getReplacementUnitCorrespondencies(\r
- ArrayList<Input> inputs, ArrayList<UnitResult> argumentUnits) {\r
+ private static HashMap<String, String> getReplacementUnitCorrespondencies(\r
+ ArrayList<Input> inputs, ArrayList<Pair<UnitResult, String>> argumentUnits) {\r
// TODO: General case. Currently each 'p, 'q etc. must exist as an atomic unit.\r
HashMap<String, String> correspondencies = new HashMap<String, String>();\r
- for (int i = 0; i < inputs.size(); ++i) {\r
- if (inputs.get(i).unit.matches("'[a-zA-Z]")) {\r
- if (argumentUnits.size() > i) { // Skip optional\r
- correspondencies.put(inputs.get(i).unit, argumentUnits.get(i).getCleanFullUnit());\r
+ for (int i = 0; i < argumentUnits.size(); ++i) {\r
+ if (argumentUnits.get(i).second != null) { // named arguments\r
+ for (Input input : inputs) {\r
+ if (input.name.equals(argumentUnits.get(i).second)) {\r
+ addPossibleCorrespondence(correspondencies, input, argumentUnits.get(i).first);\r
+ }\r
}\r
+ } else if (inputs.size() > i) {\r
+ addPossibleCorrespondence(correspondencies, inputs.get(i), argumentUnits.get(i).first);\r
}\r
}\r
+ // If there is no mapping between the actual and defined inputs (e.g. 'p refers to both m and s),\r
+ // then return the latest correspondence. The users of this functions currently assume such behavior.\r
return correspondencies;\r
}\r
\r
+ private static void addPossibleCorrespondence(HashMap<String, String> correspondencies,\r
+ Input input, UnitResult argumentUnit) {\r
+ if (input.unit.matches("'[a-zA-Z]")) {\r
+ String fullUnit = argumentUnit.getCleanFullUnit();\r
+ if ("".equals(fullUnit))\r
+ fullUnit = "1";\r
+ correspondencies.put(input.unit, fullUnit);\r
+ }\r
+ }\r
+\r
private boolean matchInputs(ArrayList<Input> inputs,\r
- ArrayList<UnitResult> argumentUnits) {\r
- // TODO Auto-generated method stub\r
+ ArrayList<Pair<UnitResult, String>> argumentUnits, Function f, ArrayList<Function> functions, boolean allowEquivalents, HashMap<String, String> units) throws UnitCheckingException {\r
+ ArrayList<Boolean> inputMatches = new ArrayList<Boolean>();\r
+ for (int i = 0; i < inputs.size(); ++i)\r
+ inputMatches.add(Boolean.FALSE);\r
+ \r
+ for (int i = 0; i < argumentUnits.size(); ++i) { // Go through all _arguments_\r
+ if (inputs.size() == 0)\r
+ return false; // No inputs expected but got at least one\r
+ \r
+ if (argumentUnits.get(i).second != null) { // Named argument\r
+ boolean found = false;\r
+ for (int j = 0; j < inputs.size(); ++j) {\r
+ Input namedInput = inputs.get(j);\r
+ if (namedInput.name.equals(argumentUnits.get(i).second)) {\r
+ // Match input unit to argument unit\r
+ UnitResult inputUnit = new UnitResult(allowEquivalents);\r
+ inputUnit = getUnitResult(units, inputUnit, inputs, argumentUnits, f, functions, allowEquivalents, namedInput);\r
+ if (!inputUnit.equals(argumentUnits.get(i).first))\r
+ return false;\r
+ inputMatches.set(j, Boolean.TRUE);\r
+ found = true;\r
+ break;\r
+ }\r
+ }\r
+ if (!found) {\r
+ throw new UnitCheckingException("Undefined input argument " + argumentUnits.get(i).second \r
+ + " used in function " + f.getName() + ".");\r
+ }\r
+ } else { // Position argument\r
+ if (i >= inputs.size()) { // Test for variable length argument\r
+ // Assume there can be only one variable length input and its in the end.\r
+ // And that there cannot be any optional arguments before.\r
+ if (inputs.get(inputs.size() - 1).variableLength) {\r
+ // Match input unit to argument unit\r
+ UnitResult inputUnit = new UnitResult(allowEquivalents);\r
+ inputUnit = getUnitResult(units, inputUnit, inputs, argumentUnits, f, functions, allowEquivalents, inputs.get(inputs.size() - 1));\r
+ if (!inputUnit.equals(argumentUnits.get(i).first))\r
+ return false;\r
+ // The corresponding _input_ has already been gone through, no need to set true.\r
+ } else {\r
+ return false;\r
+ }\r
+ } else {\r
+ // Match input unit to argument unit\r
+ UnitResult inputUnit = new UnitResult(allowEquivalents);\r
+ inputUnit = getUnitResult(units, inputUnit, inputs, argumentUnits, f, functions, allowEquivalents, inputs.get(i));\r
+ if (!inputUnit.equals(argumentUnits.get(i).first))\r
+ return false;\r
+ inputMatches.set(i, Boolean.TRUE);\r
+ }\r
+ }\r
+ }\r
+ \r
+ // See if some of the required inputs has not been defined. \r
+ for (int i = 0; i < inputs.size(); ++i) {\r
+ if (!inputMatches.get(i) && !inputs.get(i).optional) {\r
+ return false;\r
+ }\r
+ }\r
+ \r
return true;\r
}\r
\r
* @param argumentUnits\r
* @return\r
*/\r
- private UnitCheckingException getException(UnitCheckingNode functionLabelNode, ArrayList<Input> inputs, ArrayList<UnitResult> argumentUnits) {\r
+ private UnitCheckingException getException(UnitCheckingNode functionLabelNode, ArrayList<Input> inputs, ArrayList<Pair<UnitResult,String>> argumentUnits) {\r
String exception = new String("Function arguments do not have correct units. Expected " + functionLabelNode.printNode() + "(");\r
\r
for (int i = 0; i < inputs.size(); ++i) {\r
}\r
exception += " ), got " + functionLabelNode.printNode() + "(";\r
for (int i = 0; i < argumentUnits.size(); ++i) {\r
- UnitResult argumentUnit = argumentUnits.get(i);\r
+ UnitResult argumentUnit = argumentUnits.get(i).first;\r
if (i == 0)\r
exception += " ";\r
exception += "[" + argumentUnit.getCleanFullUnit() + "]";\r
\r
- if (i != inputs.size() - 1)\r
+ if (i != argumentUnits.size() - 1)\r
exception += ", ";\r
\r
}\r
return new UnitCheckingException(exception);\r
}\r
\r
- private ArrayList<UnitResult> getArgumentsOf(\r
+ private ArrayList<Pair<UnitResult, String>> getArgumentsOf(\r
UnitCheckingNode functionArgumentsNode, \r
HashMap<String, String> units, \r
ArrayList<Function> functions, \r
boolean allowEquivalents) throws UnitCheckingException {\r
// Arguments are defined recursively in OpenModelica\r
- ArrayList<UnitResult> argumentUnits = new ArrayList<UnitResult>();\r
+ ArrayList<Pair<UnitResult, String>> argumentUnits = new ArrayList<Pair<UnitResult, String>>();\r
if (functionArgumentsNode instanceof FunctionArguments) {\r
UnitCheckingNode firstArg = ((UnitCheckingNode) functionArgumentsNode.jjtGetChild(0));\r
- argumentUnits.add(firstArg.getUnits(units, functions, allowEquivalents));\r
- if (functionArgumentsNode.jjtGetNumChildren() > 1) {\r
- UnitCheckingNode next = ((UnitCheckingNode) functionArgumentsNode.jjtGetChild(1));\r
- argumentUnits.addAll(getArgumentsOf(next, units, functions, allowEquivalents));\r
+ if (firstArg instanceof NamedArguments) {\r
+ // Found all position arguments, continue with named arguments\r
+ argumentUnits.addAll(getNamedArgumentsOf(firstArg, units, functions, allowEquivalents));\r
+ } else {\r
+ argumentUnits.add(new Pair<UnitResult, String>(firstArg.getUnits(units, functions, allowEquivalents), null));\r
+ if (functionArgumentsNode.jjtGetNumChildren() > 1) {\r
+ // Continue to the next argument (within the following function_arguments)\r
+ UnitCheckingNode next = ((UnitCheckingNode) functionArgumentsNode.jjtGetChild(1));\r
+ argumentUnits.addAll(getArgumentsOf(next, units, functions, allowEquivalents));\r
+ }\r
+ }\r
+ }\r
+ return argumentUnits;\r
+ }\r
+\r
+ private ArrayList<Pair<UnitResult, String>> getNamedArgumentsOf(\r
+ UnitCheckingNode namedArgumentsNode, \r
+ HashMap<String, String> units, \r
+ ArrayList<Function> functions, \r
+ boolean allowEquivalents) throws UnitCheckingException {\r
+ // Named arguments are defined recursively in OpenModelica\r
+ ArrayList<Pair<UnitResult, String>> argumentUnits = new ArrayList<Pair<UnitResult, String>>();\r
+ if (namedArgumentsNode instanceof NamedArguments) {\r
+ UnitCheckingNode firstArg = ((UnitCheckingNode) namedArgumentsNode.jjtGetChild(0));\r
+ String argumentName = firstArg.jjtGetFirstToken().image;\r
+ argumentUnits.add(new Pair<UnitResult, String>(firstArg.getUnits(units, functions, allowEquivalents), argumentName));\r
+ if (namedArgumentsNode.jjtGetNumChildren() > 1) {\r
+ // Continue to the next argument (within the following function_arguments)\r
+ UnitCheckingNode next = ((UnitCheckingNode) namedArgumentsNode.jjtGetChild(1));\r
+ argumentUnits.addAll(getNamedArgumentsOf(next, units, functions, allowEquivalents));\r
}\r
- \r
}\r
return argumentUnits;\r
}\r