*******************************************************************************/\r
package org.simantics.sysdyn.unitParser.nodes;\r
\r
-import java.io.StringReader;\r
import java.util.ArrayList;\r
import java.util.HashMap;\r
\r
-import org.simantics.sysdyn.unitParser.ParseException;\r
import org.simantics.sysdyn.unitParser.UnitCheckingException;\r
import org.simantics.sysdyn.unitParser.UnitCheckingNode;\r
-import org.simantics.sysdyn.unitParser.UnitParser;\r
import org.simantics.sysdyn.unitParser.nodes.UnitResult.UnitType;\r
import org.simantics.sysdyn.utils.Function;\r
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
\r
@Override\r
public UnitResult getUnits(HashMap<String, String> units, ArrayList<Function> functions, boolean allowEquivalents) throws UnitCheckingException {\r
- UnitResult result = new UnitResult(allowEquivalents);\r
UnitCheckingNode functionLabelNode = ((UnitCheckingNode)jjtGetChild(0));\r
String functionLabel = functionLabelNode.printNode();\r
\r
ArrayList<Input> inputs = f.getInputList();\r
// Get arguments from parser.\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, 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
+ // Get correspondent actual units for unit templates in inputs.\r
+ HashMap<String, String> correspondences = getReplacementUnitCorrespondences(inputs, argumentUnits);\r
+\r
+ try {\r
+ // If the function input parameter units are OK, set the output unit accordingly. \r
+ if (f.areArgumentUnitsValid(argumentUnits, correspondences, functions, allowEquivalents, units)) {\r
+ Output o = f.getOutputList().get(0); // Support only single return value functions.\r
+ return o.getUnitResult(units, f, functions, allowEquivalents, correspondences);\r
+ }\r
+ \r
+ // Create the exception when we first time arrive here, since the functions\r
+ // are sorted so that scalar functions are before vector ones and there are\r
+ // a few duplicates, so this way the exception message is more likely useful.\r
+ if (u == null)\r
+ u = getException(functionLabelNode, inputs, argumentUnits);\r
+ // There may be similarly named functions, so if one doesn't succeed, go through the rest of the list anyway.\r
+ } catch (UnitCheckingException e) {\r
+ u = e; // Override the general exception with a more specific one.\r
}\r
- \r
- // Create the exception when we first time arrive here, since the functions\r
- // are sorted so that scalar functions are before vector ones and there are\r
- // a few duplicates, so this way the exception message is more likely useful.\r
- if (u == null)\r
- u = getException(functionLabelNode, inputs, argumentUnits);\r
- // There may be similarly named functions, so if one doesn't succeed, go through the rest of the list anyway.\r
}\r
}\r
\r
if (u == null) {\r
- result.setUnitType(UnitType.ANY); // The function was not found.\r
+ UnitResult result = new UnitResult(allowEquivalents);\r
+ result.setUnitType(UnitType.ANY); // The function was not found.\r
return result;\r
}\r
\r
throw u;\r
}\r
\r
- private static UnitResult getUnitResult(\r
- HashMap<String, String> units, \r
- UnitResult result, \r
- ArrayList<Input> inputs, \r
- ArrayList<Pair<UnitResult,String>> argumentUnits, \r
- Function f,\r
- ArrayList<Function> functions,\r
- boolean allowEquivalents,\r
- Parameter p) throws UnitCheckingException {\r
- \r
- if (Parameter.ANY.equals(p.unit)) {\r
- result.setUnitType(UnitType.ANY);\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 = 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
- String correspondenciesReplaced = replaceCorrespondencies(f, timeReplaced, correspondencies);\r
-\r
- try {\r
- StringReader outputReader = new StringReader(correspondenciesReplaced);\r
- UnitParser outputParser = new UnitParser(outputReader);\r
- UnitCheckingNode output;\r
- output = (UnitCheckingNode) outputParser.expr();\r
- outputReader.close();\r
- result.appendResult(output.getUnits(null, functions, allowEquivalents));\r
- result.setUnitType(UnitType.NORMAL);\r
- } catch (ParseException e) {\r
- e.printStackTrace();\r
- } catch (UnitCheckingException e) {\r
- e.printStackTrace();\r
- }\r
- \r
- }\r
- \r
- return result;\r
- }\r
-\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
- // Go through the unit as long as there are part of form 'p, 'q, etc. and replace them.\r
- while ((index = ret.indexOf('\'')) >= 0) {\r
- String replaced = ret.substring(index, index + 2); // The replaced units are always of length 2.\r
- try {\r
- ret = ret.replace(replaced, correspondencies.get(replaced));\r
- } catch (NullPointerException npe) {\r
- throw new UnitCheckingException("Function " + f.getName() + " output unit could not be determined. Replacement unit " \r
- + replaced + " not found in input unit definitions.");\r
- }\r
- }\r
- return ret;\r
- }\r
-\r
- private static HashMap<String, String> getReplacementUnitCorrespondencies(\r
+ private static HashMap<String, String> getReplacementUnitCorrespondences(\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
+ HashMap<String, String> correspondences = new HashMap<String, String>();\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
+ addPossibleCorrespondence(correspondences, input, argumentUnits.get(i).first);\r
}\r
}\r
} else if (inputs.size() > i) {\r
- addPossibleCorrespondence(correspondencies, inputs.get(i), argumentUnits.get(i).first);\r
+ addPossibleCorrespondence(correspondences, 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
+ return correspondences;\r
}\r
\r
- private static void addPossibleCorrespondence(HashMap<String, String> correspondencies,\r
+ private static void addPossibleCorrespondence(HashMap<String, String> correspondences,\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
+ correspondences.put(input.unit, fullUnit);\r
}\r
}\r
\r
- private boolean matchInputs(ArrayList<Input> inputs,\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
/**\r
* Get the exception (text) shown to user.\r
* @param functionLabelNode\r
* @param argumentUnits\r
* @return\r
*/\r
- private UnitCheckingException getException(UnitCheckingNode functionLabelNode, ArrayList<Input> inputs, ArrayList<Pair<UnitResult,String>> argumentUnits) {\r
+ private static UnitCheckingException getException(UnitCheckingNode functionLabelNode, \r
+ ArrayList<Input> inputs, \r
+ 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
return new UnitCheckingException(exception);\r
}\r
\r
- private ArrayList<Pair<UnitResult, String>> getArgumentsOf(\r
+ private static ArrayList<Pair<UnitResult, String>> getArgumentsOf(\r
UnitCheckingNode functionArgumentsNode, \r
HashMap<String, String> units, \r
ArrayList<Function> functions, \r
return argumentUnits;\r
}\r
\r
- private ArrayList<Pair<UnitResult, String>> getNamedArgumentsOf(\r
+ private static ArrayList<Pair<UnitResult, String>> getNamedArgumentsOf(\r
UnitCheckingNode namedArgumentsNode, \r
HashMap<String, String> units, \r
ArrayList<Function> functions, \r
package org.simantics.sysdyn.utils;\r
\r
+import java.io.StringReader;\r
import java.util.ArrayList;\r
import java.util.Collection;\r
+import java.util.HashMap;\r
\r
import org.simantics.databoard.Bindings;\r
import org.simantics.db.ReadGraph;\r
import org.simantics.db.request.Read;\r
import org.simantics.layer0.Layer0;\r
import org.simantics.sysdyn.SysdynResource;\r
+import org.simantics.sysdyn.unitParser.ParseException;\r
+import org.simantics.sysdyn.unitParser.UnitCheckingException;\r
+import org.simantics.sysdyn.unitParser.UnitCheckingNode;\r
+import org.simantics.sysdyn.unitParser.UnitParser;\r
+import org.simantics.sysdyn.unitParser.nodes.UnitResult;\r
+import org.simantics.sysdyn.unitParser.nodes.UnitResult.UnitType;\r
import org.simantics.ui.SimanticsUI;\r
+import org.simantics.utils.datastructures.Pair;\r
\r
\r
/**\r
public static final String TIME = "TIME";\r
public String name;\r
public String unit = ANY;\r
+ \r
+ public UnitResult getUnitResult(\r
+ HashMap<String, String> units, \r
+ Function f,\r
+ ArrayList<Function> functions,\r
+ boolean allowEquivalents,\r
+ HashMap<String, String> correspondences) throws UnitCheckingException {\r
+ \r
+ UnitResult result = new UnitResult(allowEquivalents);\r
+ if (Parameter.ANY.equals(this.unit)) {\r
+ result.setUnitType(UnitType.ANY);\r
+ } else if ("1".equals(this.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 = this.unit.replace(Parameter.TIME, timeUnit);\r
+\r
+ // Replace 'p, 'q, etc. in output with units from actual inputs.\r
+ String correspondencesReplaced = replaceCorrespondences(f, timeReplaced, correspondences);\r
+\r
+ try {\r
+ StringReader outputReader = new StringReader(correspondencesReplaced);\r
+ UnitParser outputParser = new UnitParser(outputReader);\r
+ UnitCheckingNode output;\r
+ output = (UnitCheckingNode) outputParser.expr();\r
+ outputReader.close();\r
+ result.appendResult(output.getUnits(null, functions, allowEquivalents));\r
+ result.setUnitType(UnitType.NORMAL);\r
+ } catch (ParseException e) {\r
+ e.printStackTrace();\r
+ } catch (UnitCheckingException e) {\r
+ e.printStackTrace();\r
+ }\r
+ }\r
+ \r
+ return result;\r
+ }\r
+ \r
+ private static String replaceCorrespondences(Function f, String original,\r
+ HashMap<String, String> correspondences) throws UnitCheckingException {\r
+ int index;\r
+ String ret = new String(original);\r
+ // Go through the unit as long as there are part of form 'p, 'q, etc. and replace them.\r
+ while ((index = ret.indexOf('\'')) >= 0) {\r
+ String replaced = ret.substring(index, index + 2); // The replaced units are always of length 2.\r
+ try {\r
+ ret = ret.replace(replaced, correspondences.get(replaced));\r
+ } catch (NullPointerException npe) {\r
+ throw new UnitCheckingException("Function " + f.getName() + " output unit could not be determined. Replacement unit " \r
+ + replaced + " not found in input unit definitions.");\r
+ }\r
+ }\r
+ return ret;\r
+ }\r
}\r
\r
public static class Input extends Parameter {\r
return description.replaceAll("<", "<").replaceAll(">", ">").replaceAll("\n", "<BR>");\r
}\r
\r
+ /**\r
+ * Determine if the units of the given arguments are valid\r
+ * @param argumentUnits validated arguments\r
+ * @param correspondences mapping from templates to actual arguments\r
+ * @param functions all functions available\r
+ * @param allowEquivalents true iff equivalent units are used (e.g. $ == dollar)\r
+ * @param units evm\r
+ * @return true iff argument units are valid\r
+ * @throws UnitCheckingException if \r
+ */\r
+ public boolean areArgumentUnitsValid(ArrayList<Pair<UnitResult, String>> argumentUnits, \r
+ HashMap<String, String> correspondences,\r
+ ArrayList<Function> functions, \r
+ boolean allowEquivalents, \r
+ HashMap<String, \r
+ String> units) throws UnitCheckingException {\r
+ ArrayList<Boolean> inputMatches = new ArrayList<Boolean>(); // Table for matching inputs.\r
+ for (int i = 0; i < inputList.size(); ++i)\r
+ inputMatches.add(Boolean.FALSE);\r
+ \r
+ for (int i = 0; i < argumentUnits.size(); ++i) { // Go through all _arguments_\r
+ if (inputList.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 < inputList.size(); ++j) {\r
+ Input namedInput = inputList.get(j);\r
+ if (namedInput.name.equals(argumentUnits.get(i).second)) {\r
+ // Match input unit to argument unit\r
+ UnitResult inputUnit = namedInput.getUnitResult(units, this, functions, allowEquivalents, correspondences);\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 " + this.getName() + ".");\r
+ }\r
+ } else { // Position argument\r
+ if (i >= inputList.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
+ Input input = inputList.get(inputList.size() - 1);\r
+ if (input.variableLength) {\r
+ // Match input unit to argument unit\r
+ UnitResult inputUnit = input.getUnitResult(units, this, functions, allowEquivalents, correspondences);\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 = inputList.get(i).getUnitResult(units, this, functions, allowEquivalents, correspondences);\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 < inputList.size(); ++i) {\r
+ if (!inputMatches.get(i) && !inputList.get(i).optional) {\r
+ return false;\r
+ }\r
+ }\r
+ \r
+ return true;\r
+ }\r
}\r