/** Return the map of name to property for a type. */ protected Map<String, P> getName2Property(C type) { Map<String, P> name2property = type2name2property.get(type); if (name2property == null) { name2property = new HashMap<String, P>(); type2name2property.put(type, name2property); List<P> allProperties = getAttributes(type); for (P candidateProperty : allProperties) { String name = uml.getName(candidateProperty); P oldProperty = name2property.get(name); if (oldProperty == null) { name2property.put(name, candidateProperty); } else { C candidateOwner = uml.getOwningClassifier(candidateProperty); Collection<? extends C> candidateSupertypes = uml.getAllSupertypes(candidateOwner); C oldOwner = uml.getOwningClassifier(oldProperty); if (candidateSupertypes.contains(oldOwner)) { name2property.put(name, candidateProperty); } else { Collection<? extends C> oldSupertypes = uml.getAllSupertypes(oldOwner); if (!oldSupertypes.contains(candidateOwner)) {; // This should not happen } } } } } return name2property; }
/** * Return the operation from the name2operationOrOperations cache that matches operation. Returns * null if there is no operation or more than one. * * @param name2operationOrOperations * @param operation */ protected O getExactMatchingOperation( Map<String, Object> name2operationOrOperations, O operation) { String requiredName = uml.getName(operation); List<PM> requiredParameters = uml.getParameters(operation); Object candidateOperationOrOperations = name2operationOrOperations.get(requiredName); if (candidateOperationOrOperations instanceof List<?>) { O matchingOperation = null; @SuppressWarnings("unchecked") List<O> candidateOperations = (List<O>) candidateOperationOrOperations; for (O candidateOperation : candidateOperations) { List<PM> candidateParameters = uml.getParameters(candidateOperation); if (exactlyMatches(requiredParameters, candidateParameters)) { if (matchingOperation == null) { matchingOperation = candidateOperation; } else { return null; // Ambiguity detected } } } return matchingOperation; } else if (candidateOperationOrOperations != null) { @SuppressWarnings("unchecked") O candidateOperation = (O) candidateOperationOrOperations; List<PM> candidateParameters = uml.getParameters(candidateOperation); if (exactlyMatches(requiredParameters, candidateParameters)) { return candidateOperation; } else { return null; } } else { return null; } }
/** Return true if requiredParameters is an exact match for candidateParameters. */ protected boolean exactlyMatches( List<? extends PM> requiredParameters, List<? extends PM> candidateParameters) { int iMax = requiredParameters.size(); if (iMax != candidateParameters.size()) { return false; } for (int i = 0; i < iMax; i++) { PM requiredParameter = requiredParameters.get(i); PM candidateParameter = candidateParameters.get(i); C requiredType = uml.getOCLType(requiredParameter); C candidateType = uml.getOCLType(candidateParameter); if (requiredType != candidateType) { return false; } } return true; }
/** * Tests that the {@link UMLReflection} API provides the correct owner of an additional attribute. */ public void test_defExpression_attribute_owner() { helper.setContext(apple); try { Property p = helper.defineAttribute("fallen : Boolean = " + "tree.oclIsUndefined()"); UMLReflection<?, Classifier, ?, ?, ?, ?, ?, ?, ?, ?> uml = ocl.getEnvironment().getUMLReflection(); // sanity check assertSame(apple, uml.getOwningClassifier(apple_label)); // check the owner of the additional operation assertSame(apple, uml.getOwningClassifier(p)); } catch (Exception e) { fail("Failed to parse or evaluate: " + e.getLocalizedMessage()); } }
@Override protected List<O> getBestMatchingOperations( C owner, String name, List<? extends TypedElement<C>> args) { if (bypass) { return super.getBestMatchingOperations(owner, name, args); } Map<String, Object> name2operationOrOperations = getName2OperationOrOperations(owner); Object candidateOperationOrOperations = name2operationOrOperations.get(name); if (candidateOperationOrOperations instanceof List<?>) { List<O> matches = null; @SuppressWarnings("unchecked") List<O> candidateOperations = (List<O>) candidateOperationOrOperations; int bestExactitude = 0; for (O oper : candidateOperations) { int exactitude = matchArgsWithExactitude(owner, uml.getParameters(oper), args); if (exactitude >= bestExactitude) { if (exactitude > bestExactitude) { if (matches != null) { matches.clear(); } bestExactitude = exactitude; } if (matches == null) { // assume a small number of redefinitions matches = new java.util.ArrayList<O>(3); } matches.add(oper); } } return matches; } else if (candidateOperationOrOperations != null) { @SuppressWarnings("unchecked") O candidateOperation = (O) candidateOperationOrOperations; int exactitude = matchArgsWithExactitude(owner, uml.getParameters(candidateOperation), args); if (exactitude >= 0) { return Collections.singletonList(candidateOperation); } else { return null; } } else { return null; } }
/** * Tests that the {@link UMLReflection} API provides the correct owner of an additional operation. */ public void test_defExpression_operation_owner() { helper.setContext(fruit); try { Operation o = helper.defineOperation( "bestColor(c : Color) : Color = " + "if self.color = Color::black then c else self.color endif"); UMLReflection<?, Classifier, ?, ?, ?, ?, ?, ?, ?, ?> uml = ocl.getEnvironment().getUMLReflection(); // sanity check assertSame(fruit, uml.getOwningClassifier(fruit_ripen)); // check the owner of the additional operation assertSame(fruit, uml.getOwningClassifier(o)); } catch (Exception e) { fail("Failed to parse or evaluate: " + e.getLocalizedMessage()); } }
/** Tests the parsing the def expression for static attributes and operations. */ public void test_defExpression_static() { try { Environment< Package, Classifier, Operation, Property, EnumerationLiteral, Parameter, State, CallOperationAction, SendSignalAction, Constraint, Class, EObject> env = ocl.getEnvironment(); UMLReflection< Package, Classifier, Operation, Property, EnumerationLiteral, Parameter, State, CallOperationAction, SendSignalAction, Constraint> umlReflection = env.getUMLReflection(); ParsingOptions.setOption(ocl.getEnvironment(), ParsingOptions.SUPPORT_STATIC_FEATURES, true); parseDef( "package ocltest context Fruit " + "def: bestColor1() : Color = null " + "static def: bestColor2() : Color = null " + "def: goodColor1 : Color = null " + "static def: goodColor2 : Color = null " + "endpackage"); Operation operation1 = env.lookupOperation(fruit, "bestColor1", null); assertNotNull(operation1); assertEquals(false, umlReflection.isStatic(operation1)); Operation operation2 = env.lookupOperation(fruit, "bestColor2", null); assertNotNull(operation2); assertEquals(true, umlReflection.isStatic(operation2)); Property property1 = env.lookupProperty(fruit, "goodColor1"); assertNotNull(property1); assertEquals(false, umlReflection.isStatic(property1)); Property property2 = env.lookupProperty(fruit, "goodColor2"); assertNotNull(property2); assertEquals(true, umlReflection.isStatic(property2)); ParsingOptions.setOption(ocl.getEnvironment(), ParsingOptions.SUPPORT_STATIC_FEATURES, false); try { ocl.parse( new OCLInput( "package ocltest context Fruit " + "def: bestColor3() : Color = null " + "static def: bestColor4() : Color = null " + "endpackage")); fail("Should have failed to parse the unsupported static"); } catch (ParserException e) { // success! assertEquals(OCLMessages.UnsupportedStatic_ERROR_, e.getMessage()); System.out.println("Got the expected exception: " + e.getLocalizedMessage()); } } catch (Exception e) { fail("Failed to parse or evaluate: " + e.getLocalizedMessage()); } }
/** Return the map of name to operator or list of operations for a type. */ protected Map<String, Object> getName2OperationOrOperations(C type) { Map<String, Object> name2operationOrOperations = type2name2operationOrOperations.get(type); if (name2operationOrOperations == null) { name2operationOrOperations = new HashMap<String, Object>(); type2name2operationOrOperations.put(type, name2operationOrOperations); List<O> allOperations = getOperations(type); for (O candidateOperation : allOperations) { String name = uml.getName(candidateOperation); Object overloadOrOverloads = name2operationOrOperations.get(name); if (overloadOrOverloads == null) { name2operationOrOperations.put(name, candidateOperation); } else { List<O> overloads; if (overloadOrOverloads instanceof List<?>) { @SuppressWarnings("unchecked") List<O> castOperations = (List<O>) overloadOrOverloads; overloads = castOperations; } else { overloads = new ArrayList<O>(); name2operationOrOperations.put(name, overloads); @SuppressWarnings("unchecked") O castOperation = (O) overloadOrOverloads; overloads.add(castOperation); } C candidateOwner = uml.getOwningClassifier(candidateOperation); Collection<? extends C> candidateSupertypes = uml.getAllSupertypes(candidateOwner); List<PM> candidateParameters = uml.getParameters(candidateOperation); int iMax = candidateParameters.size(); int j = overloads.size(); while (--j >= 0) { // Reverse count to allow remove() O oldOperation = overloads.get(j); List<PM> oldParameters = uml.getParameters(oldOperation); if (iMax == oldParameters.size()) { int i = 0; for (; i < iMax; i++) { PM candidateParameter = candidateParameters.get(i); PM oldParameter = oldParameters.get(i); C oldType = uml.getOCLType(oldParameter); C candidateType = uml.getOCLType(candidateParameter); if (oldType != candidateType) { break; } } if (i >= iMax) { C oldOwner = uml.getOwningClassifier(oldOperation); if (candidateSupertypes.contains(oldOwner)) { overloads.remove(j); } else { Collection<? extends C> oldSupertypes = uml.getAllSupertypes(oldOwner); if (oldSupertypes.contains(candidateOwner)) { break; } } } } } if (j < 0) { overloads.add(candidateOperation); } } } } return name2operationOrOperations; }