| 1 | package de.uka.ipd.sdq.simucomframework.variables.stoexvisitor; |
| 2 | |
| 3 | import java.util.ArrayList; |
| 4 | import java.util.Map.Entry; |
| 5 | |
| 6 | import org.apache.log4j.Logger; |
| 7 | |
| 8 | import de.uka.ipd.sdq.pcm.parameter.CharacterisedVariable; |
| 9 | import de.uka.ipd.sdq.pcm.stochasticexpressions.PCMStoExPrettyPrintVisitor; |
| 10 | import de.uka.ipd.sdq.pcm.stochasticexpressions.PCMStoExSwitch; |
| 11 | |
| 12 | import de.uka.ipd.sdq.probfunction.math.IProbabilityFunctionFactory; |
| 13 | |
| 14 | import de.uka.ipd.sdq.simucomframework.variables.EvaluationProxy; |
| 15 | import de.uka.ipd.sdq.simucomframework.variables.StackContext; |
| 16 | |
| 17 | import de.uka.ipd.sdq.simucomframework.variables.cache.StoExCacheEntry; |
| 18 | import de.uka.ipd.sdq.simucomframework.variables.exceptions.TypesIncompatibleInComparisionException; |
| 19 | import de.uka.ipd.sdq.simucomframework.variables.exceptions.TypesIncompatibleInProductException; |
| 20 | import de.uka.ipd.sdq.simucomframework.variables.exceptions.TypesIncompatibleInTermException; |
| 21 | import de.uka.ipd.sdq.simucomframework.variables.exceptions.ValueNotInFrameException; |
| 22 | import de.uka.ipd.sdq.simucomframework.variables.functions.FunctionLib; |
| 23 | import de.uka.ipd.sdq.simucomframework.variables.stackframe.SimulatedStackframe; |
| 24 | import de.uka.ipd.sdq.stoex.BoolLiteral; |
| 25 | import de.uka.ipd.sdq.stoex.BooleanOperatorExpression; |
| 26 | import de.uka.ipd.sdq.stoex.CompareExpression; |
| 27 | import de.uka.ipd.sdq.stoex.DoubleLiteral; |
| 28 | import de.uka.ipd.sdq.stoex.Expression; |
| 29 | import de.uka.ipd.sdq.stoex.FunctionLiteral; |
| 30 | import de.uka.ipd.sdq.stoex.IfElseExpression; |
| 31 | import de.uka.ipd.sdq.stoex.IntLiteral; |
| 32 | import de.uka.ipd.sdq.stoex.NegativeExpression; |
| 33 | import de.uka.ipd.sdq.stoex.NotExpression; |
| 34 | import de.uka.ipd.sdq.stoex.Parenthesis; |
| 35 | import de.uka.ipd.sdq.stoex.PowerExpression; |
| 36 | import de.uka.ipd.sdq.stoex.ProbabilityFunctionLiteral; |
| 37 | import de.uka.ipd.sdq.stoex.ProductExpression; |
| 38 | import de.uka.ipd.sdq.stoex.StringLiteral; |
| 39 | import de.uka.ipd.sdq.stoex.TermExpression; |
| 40 | import de.uka.ipd.sdq.stoex.analyser.visitors.ExpressionInferTypeVisitor; |
| 41 | import de.uka.ipd.sdq.stoex.analyser.visitors.TypeEnum; |
| 42 | |
| 43 | |
| 44 | /** |
| 45 | * Visitor to evaluate stoex. It executes the corresponding Java mathematical |
| 46 | * operations at each operator. It partially relies on the types infered |
| 47 | * to do its casts |
| 48 | * @author Steffen Becker |
| 49 | * |
| 50 | */ |
| 51 | public class PCMStoExEvaluationVisitor extends PCMStoExSwitch { |
| 52 | |
| 53 | /** |
| 54 | * This class' logger |
| 55 | */ |
| 56 | private static final Logger logger = |
| 57 | Logger.getLogger(PCMStoExEvaluationVisitor.class.getName()); |
| 58 | |
| 59 | /** |
| 60 | * Pretty printer. This class is stateless hence it is save to have just one object of it |
| 61 | */ |
| 62 | private final static PCMStoExPrettyPrintVisitor printVisitor = new PCMStoExPrettyPrintVisitor(); |
| 63 | |
| 64 | /** |
| 65 | * Subvisitor to evaluate probability functions inside the visited stoex |
| 66 | */ |
| 67 | private final PCMProbfunctionEvaluationVisitor probfunctionVisitor; |
| 68 | |
| 69 | /** |
| 70 | * Stackframe against which variables will be evaluated |
| 71 | */ |
| 72 | private final SimulatedStackframe<Object> myStackFrame; |
| 73 | |
| 74 | /** |
| 75 | * Type infer visitor which will be used to infer the stoex's type |
| 76 | */ |
| 77 | private final ExpressionInferTypeVisitor typeInferer; |
| 78 | |
| 79 | /** |
| 80 | * Function lib contains functions like Exp, etc. This cannot be static as it depends on its respective |
| 81 | * random number generator |
| 82 | */ |
| 83 | private final FunctionLib functionLib; |
| 84 | |
| 85 | /** |
| 86 | * Mode for evaluating variables. Determines when to throw Exceptions if some evaluation fails |
| 87 | */ |
| 88 | private VariableMode mode; |
| 89 | |
| 90 | public PCMStoExEvaluationVisitor(StoExCacheEntry cacheEntry, SimulatedStackframe<Object> frame, VariableMode initialMode, IProbabilityFunctionFactory probFunctionFactory) { |
| 91 | this.typeInferer = cacheEntry.getTypeInferer(); |
| 92 | myStackFrame = frame; |
| 93 | this.mode = initialMode; |
| 94 | probfunctionVisitor = new PCMProbfunctionEvaluationVisitor(cacheEntry); |
| 95 | functionLib = new FunctionLib(probFunctionFactory.getRandomGenerator(), probFunctionFactory.getPDFFactory()); |
| 96 | } |
| 97 | |
| 98 | public void setVariableMode(VariableMode mode) { |
| 99 | this.mode = mode; |
| 100 | } |
| 101 | |
| 102 | public VariableMode getVariableMode() { |
| 103 | return this.mode; |
| 104 | } |
| 105 | |
| 106 | @Override |
| 107 | public Object caseCharacterisedVariable(CharacterisedVariable object) { |
| 108 | String variableID = (String)printVisitor.caseCharacterisedVariable(object); |
| 109 | try { |
| 110 | Object value = this.myStackFrame.getValue(variableID); |
| 111 | if (value instanceof EvaluationProxy) { |
| 112 | EvaluationProxy proxy = (EvaluationProxy)value; |
| 113 | return StackContext.evaluateStatic(proxy.getStoEx(), proxy.getStackFrame()); |
| 114 | } else { |
| 115 | return value; |
| 116 | } |
| 117 | } catch (ValueNotInFrameException e) { |
| 118 | if (mode == VariableMode.EXCEPTION_ON_NOT_FOUND) { |
| 119 | logger.error("Value should be in stackframe, but it is not!",e); |
| 120 | e.printStackTrace(); |
| 121 | } |
| 122 | } |
| 123 | if (mode == VariableMode.EXCEPTION_ON_NOT_FOUND) { |
| 124 | String availableIDs = ""; |
| 125 | for (Entry<String, Object> e : this.myStackFrame.getContents()) { |
| 126 | availableIDs += "<"+e.getKey()+"> "; |
| 127 | } |
| 128 | RuntimeException re = new RuntimeException("Architecture specification incomplete. Stackframe is missing id "+variableID+"\nAvailable IDs are "+availableIDs); |
| 129 | logger.error("Value not found in specification",re); |
| 130 | throw re; |
| 131 | } else if (mode == VariableMode.RETURN_NULL_ON_NOT_FOUND) { |
| 132 | return null; |
| 133 | } else { |
| 134 | if (typeInferer.getType(object) == TypeEnum.INT) |
| 135 | return 0; |
| 136 | if (typeInferer.getType(object) == TypeEnum.DOUBLE) |
| 137 | return 0.0; |
| 138 | if (typeInferer.getType(object) == TypeEnum.ENUM) |
| 139 | return ""; |
| 140 | if (typeInferer.getType(object) == TypeEnum.BOOL) |
| 141 | return false; |
| 142 | RuntimeException re = new RuntimeException("Architecture specification incomplete. Stackframe is missing id "+variableID); |
| 143 | logger.error("Value not found in specification",re); |
| 144 | throw re; |
| 145 | } |
| 146 | } |
| 147 | |
| 148 | @SuppressWarnings("unchecked") |
| 149 | @Override |
| 150 | public Object caseCompareExpression(CompareExpression object) { |
| 151 | TypeEnum leftType = typeInferer.getType(object.getLeft()); |
| 152 | TypeEnum rightType = typeInferer.getType(object.getRight()); |
| 153 | Object leftExpr = doSwitch(object.getLeft()); |
| 154 | Object rightExpr = doSwitch(object.getRight()); |
| 155 | if (leftType == TypeEnum.ANY) leftType = getDynamicType(leftExpr); |
| 156 | if (rightType == TypeEnum.ANY) rightType = getDynamicType(rightExpr); |
| 157 | |
| 158 | if (leftType == TypeEnum.INT && rightType == TypeEnum.DOUBLE) |
| 159 | leftExpr = Double.valueOf( (((Integer)leftExpr).intValue())); |
| 160 | if (rightType == TypeEnum.INT && leftType == TypeEnum.DOUBLE) |
| 161 | rightExpr = Double.valueOf( (((Integer)rightExpr).intValue())); |
| 162 | |
| 163 | // If types still don't comply, give up! |
| 164 | if (leftExpr.getClass() != rightExpr.getClass()) { |
| 165 | throw new TypesIncompatibleInComparisionException("Can not compare "+leftExpr.getClass().getName()+" to "+rightExpr.getClass().getName()); |
| 166 | } |
| 167 | |
| 168 | int result = ((Comparable)leftExpr).compareTo(rightExpr); |
| 169 | switch(object.getOperation()) |
| 170 | { |
| 171 | case EQUALS: |
| 172 | return result == 0; |
| 173 | case LESS: |
| 174 | return result < 0; |
| 175 | case LESSEQUAL: |
| 176 | return result <= 0; |
| 177 | case GREATER: |
| 178 | return result > 0; |
| 179 | case GREATEREQUAL: |
| 180 | return result >= 0; |
| 181 | case NOTEQUAL: |
| 182 | return result != 0; |
| 183 | } |
| 184 | throw new RuntimeException("Unknown Compare Operation found! Should not happen!"); |
| 185 | } |
| 186 | |
| 187 | @Override |
| 188 | public Object caseDoubleLiteral(DoubleLiteral object) { |
| 189 | return object.getValue(); |
| 190 | } |
| 191 | |
| 192 | @Override |
| 193 | public Object caseIntLiteral(IntLiteral object) { |
| 194 | return object.getValue(); |
| 195 | } |
| 196 | |
| 197 | @Override |
| 198 | public Object caseStringLiteral(StringLiteral object) { |
| 199 | return object.getValue(); |
| 200 | } |
| 201 | |
| 202 | @Override |
| 203 | public Object caseParenthesis(Parenthesis object) { |
| 204 | return doSwitch(object.getInnerExpression()); |
| 205 | } |
| 206 | |
| 207 | @Override |
| 208 | public Object caseProbabilityFunctionLiteral(ProbabilityFunctionLiteral object) { |
| 209 | return probfunctionVisitor.doSwitch(object.getFunction_ProbabilityFunctionLiteral()); |
| 210 | } |
| 211 | |
| 212 | @Override |
| 213 | public Object caseProductExpression(ProductExpression object) { |
| 214 | TypeEnum leftType = typeInferer.getType(object.getLeft()); |
| 215 | TypeEnum rightType = typeInferer.getType(object.getRight()); |
| 216 | Object left = doSwitch(object.getLeft()); |
| 217 | Object right = doSwitch(object.getRight()); |
| 218 | if (leftType == TypeEnum.ANY) leftType = getDynamicType(left); |
| 219 | if (rightType == TypeEnum.ANY) rightType = getDynamicType(right); |
| 220 | if (leftType == TypeEnum.INT && rightType == TypeEnum.INT) { |
| 221 | if (!(left instanceof Integer) || !(right instanceof Integer)) { |
| 222 | throw new TypesIncompatibleInProductException("Incompatible types in product expression. Expecting Integer!"); |
| 223 | } |
| 224 | |
| 225 | int leftInt = (Integer)left; |
| 226 | int rightInt = (Integer)right; |
| 227 | switch(object.getOperation()) |
| 228 | { |
| 229 | case DIV: |
| 230 | return leftInt / rightInt; |
| 231 | case MULT: |
| 232 | return leftInt * rightInt; |
| 233 | case MOD: |
| 234 | return leftInt % rightInt; |
| 235 | } |
| 236 | throw new RuntimeException("This should never happen!"); |
| 237 | |
| 238 | } else { |
| 239 | double leftDouble = getDouble(left); |
| 240 | double rightDouble = getDouble(right); |
| 241 | switch(object.getOperation()) |
| 242 | { |
| 243 | case DIV: |
| 244 | return leftDouble / rightDouble; |
| 245 | case MULT: |
| 246 | return leftDouble * rightDouble; |
| 247 | case MOD: |
| 248 | return leftDouble % rightDouble; |
| 249 | } |
| 250 | throw new RuntimeException("This should never happen!"); |
| 251 | } |
| 252 | } |
| 253 | |
| 254 | private double getDouble(Object o) { |
| 255 | if (o instanceof Double) |
| 256 | return (Double)o; |
| 257 | if (o instanceof Integer) |
| 258 | return (Integer)o; |
| 259 | throw new UnsupportedOperationException("Trying to cast a "+o.getClass().getCanonicalName()+" to a Double!"); |
| 260 | } |
| 261 | |
| 262 | private TypeEnum getDynamicType(Object o) { |
| 263 | if (o instanceof Integer) |
| 264 | return TypeEnum.INT; |
| 265 | if (o instanceof Double) |
| 266 | return TypeEnum.DOUBLE; |
| 267 | if (o instanceof String) |
| 268 | return TypeEnum.ENUM; |
| 269 | if (o instanceof Boolean) |
| 270 | return TypeEnum.BOOL; |
| 271 | throw new RuntimeException("Unknown dynamic type found! Should never happen!"); |
| 272 | } |
| 273 | |
| 274 | @Override |
| 275 | public Object caseTermExpression(TermExpression object) { |
| 276 | TypeEnum leftType = typeInferer.getType(object.getLeft()); |
| 277 | TypeEnum rightType = typeInferer.getType(object.getRight()); |
| 278 | Object left = doSwitch(object.getLeft()); |
| 279 | Object right = doSwitch(object.getRight()); |
| 280 | if (leftType == TypeEnum.ANY) leftType = getDynamicType(left); |
| 281 | if (rightType == TypeEnum.ANY) rightType = getDynamicType(right); |
| 282 | if (leftType == TypeEnum.INT && rightType == TypeEnum.INT) { |
| 283 | if (!(left instanceof Integer) || !(right instanceof Integer)) { |
| 284 | throw new TypesIncompatibleInTermException("Incompatible types in term expression. Expecting Integer!"); |
| 285 | } |
| 286 | int leftInt = (Integer)left; |
| 287 | int rightInt = (Integer)right; |
| 288 | switch(object.getOperation()) |
| 289 | { |
| 290 | case ADD: |
| 291 | return leftInt + rightInt; |
| 292 | case SUB: |
| 293 | return leftInt - rightInt; |
| 294 | } |
| 295 | throw new RuntimeException("This should never happen!"); |
| 296 | |
| 297 | } else { |
| 298 | double leftDouble = getDouble(left); |
| 299 | double rightDouble = getDouble(right); |
| 300 | switch(object.getOperation()) |
| 301 | { |
| 302 | case ADD: |
| 303 | return leftDouble + rightDouble; |
| 304 | case SUB: |
| 305 | return leftDouble - rightDouble; |
| 306 | } |
| 307 | throw new RuntimeException("This should never happen!"); |
| 308 | } |
| 309 | } |
| 310 | |
| 311 | @Override |
| 312 | public Object caseBooleanOperatorExpression(BooleanOperatorExpression object) { |
| 313 | boolean b1 = (Boolean) this.doSwitch(object.getLeft()); |
| 314 | boolean b2 = (Boolean) this.doSwitch(object.getRight()); |
| 315 | switch(object.getOperation()) { |
| 316 | case OR: |
| 317 | return b1 || b2; |
| 318 | case AND: |
| 319 | return b1 && b2; |
| 320 | case XOR: |
| 321 | return b1 ^ b2; |
| 322 | } |
| 323 | throw new RuntimeException("This should never happen!"); |
| 324 | } |
| 325 | |
| 326 | @Override |
| 327 | public Object caseNegativeExpression(NegativeExpression object) { |
| 328 | Object value = this.doSwitch(object.getInner()); |
| 329 | if (value instanceof Integer) |
| 330 | return -((Integer)value); |
| 331 | if (value instanceof Double) |
| 332 | return -((Double)value); |
| 333 | throw new RuntimeException("Type mismatch, unary minus only supported for numbers!"); |
| 334 | } |
| 335 | |
| 336 | @Override |
| 337 | public Object caseBoolLiteral(BoolLiteral object) { |
| 338 | return object.isValue(); |
| 339 | } |
| 340 | |
| 341 | @Override |
| 342 | public Object caseNotExpression(NotExpression object) { |
| 343 | Boolean b = (Boolean)this.doSwitch(object.getInner()); |
| 344 | return !b; |
| 345 | } |
| 346 | |
| 347 | @Override |
| 348 | public Object casePowerExpression(PowerExpression object) { |
| 349 | TypeEnum leftType = typeInferer.getType(object.getBase()); |
| 350 | TypeEnum rightType = typeInferer.getType(object.getExponent()); |
| 351 | Object leftExpr = doSwitch(object.getBase()); |
| 352 | Object rightExpr = doSwitch(object.getExponent()); |
| 353 | if (leftType == TypeEnum.ANY) leftType = getDynamicType(leftExpr); |
| 354 | if (rightType == TypeEnum.ANY) rightType = getDynamicType(rightExpr); |
| 355 | |
| 356 | if (leftType == TypeEnum.INT) |
| 357 | leftExpr = Double.valueOf( (((Integer)leftExpr).intValue())); |
| 358 | if (rightType == TypeEnum.INT) |
| 359 | rightExpr = Double.valueOf( (((Integer)rightExpr).intValue())); |
| 360 | return Math.pow((Double)leftExpr, (Double)rightExpr); |
| 361 | } |
| 362 | |
| 363 | @Override |
| 364 | public Object caseFunctionLiteral(FunctionLiteral object) { |
| 365 | String functionID = object.getId(); |
| 366 | ArrayList<Object> parameterValues = new ArrayList<Object>(); |
| 367 | for (Expression e : object.getParameters_FunctionLiteral()) { |
| 368 | parameterValues.add(this.doSwitch(e)); |
| 369 | } |
| 370 | return functionLib.evaluate(functionID,parameterValues); |
| 371 | } |
| 372 | |
| 373 | @Override |
| 374 | public Object caseIfElseExpression(IfElseExpression object) { |
| 375 | boolean cond = (Boolean)this.doSwitch(object.getConditionExpression()); |
| 376 | if (cond) |
| 377 | return this.doSwitch(object.getIfExpression()); |
| 378 | else |
| 379 | return this.doSwitch(object.getElseExpression()); |
| 380 | } |
| 381 | |
| 382 | } |