| /******************************************************************************* |
| * Copyright (c) 2005, 2018 IBM Corporation, Zeligsoft Inc., and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v2.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v20.html |
| * |
| * Contributors: |
| * IBM - Initial API and implementation |
| * Zeligsoft - Bug 251808 |
| *******************************************************************************/ |
| |
| package org.eclipse.ocl.uml.tests; |
| |
| import java.util.Collection; |
| import java.util.List; |
| |
| import org.eclipse.emf.common.util.Diagnostic; |
| import org.eclipse.emf.common.util.EList; |
| import org.eclipse.emf.common.util.URI; |
| import org.eclipse.emf.ecore.EClass; |
| import org.eclipse.emf.ecore.EFactory; |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.emf.ecore.EPackage; |
| import org.eclipse.emf.ecore.EStructuralFeature; |
| import org.eclipse.emf.ecore.resource.Resource; |
| import org.eclipse.emf.ecore.resource.impl.ResourceImpl; |
| import org.eclipse.emf.ecore.util.ECrossReferenceAdapter; |
| import org.eclipse.ocl.AmbiguousLookupException; |
| import org.eclipse.ocl.Environment; |
| import org.eclipse.ocl.LookupException; |
| import org.eclipse.ocl.OCLInput; |
| import org.eclipse.ocl.ParserException; |
| import org.eclipse.ocl.expressions.AssociationClassCallExp; |
| import org.eclipse.ocl.expressions.FeatureCallExp; |
| import org.eclipse.ocl.expressions.LoopExp; |
| import org.eclipse.ocl.expressions.OCLExpression; |
| import org.eclipse.ocl.expressions.OperationCallExp; |
| import org.eclipse.ocl.helper.Choice; |
| import org.eclipse.ocl.helper.ChoiceKind; |
| import org.eclipse.ocl.helper.ConstraintKind; |
| import org.eclipse.ocl.lpg.BasicEnvironment; |
| import org.eclipse.ocl.lpg.ProblemHandler; |
| import org.eclipse.ocl.options.ParsingOptions; |
| import org.eclipse.ocl.options.ProblemOption; |
| import org.eclipse.ocl.uml.CollectionType; |
| import org.eclipse.ocl.uml.options.UMLParsingOptions; |
| import org.eclipse.ocl.util.OCLUtil; |
| import org.eclipse.uml2.uml.AggregationKind; |
| import org.eclipse.uml2.uml.Association; |
| import org.eclipse.uml2.uml.AssociationClass; |
| import org.eclipse.uml2.uml.Class; |
| import org.eclipse.uml2.uml.Classifier; |
| import org.eclipse.uml2.uml.InstanceSpecification; |
| import org.eclipse.uml2.uml.LiteralNull; |
| import org.eclipse.uml2.uml.LiteralUnlimitedNatural; |
| import org.eclipse.uml2.uml.Operation; |
| import org.eclipse.uml2.uml.Package; |
| import org.eclipse.uml2.uml.Parameter; |
| import org.eclipse.uml2.uml.Property; |
| import org.eclipse.uml2.uml.UMLFactory; |
| import org.eclipse.uml2.uml.UMLPackage; |
| import org.eclipse.uml2.uml.util.UMLUtil; |
| |
| import junit.framework.AssertionFailedError; |
| |
| |
| /** |
| * Tests for association navigation. |
| * |
| * @author Christian W. Damus (cdamus) |
| */ |
| @SuppressWarnings("nls") |
| public class AssociationTest |
| extends AbstractTestSuite { |
| |
| Resource instanceResource; |
| Package instancePackage; |
| |
| /** |
| * Tests support for association end qualifiers. |
| */ |
| public void test_qualifiers_associationEnd_RATLC00538077() { |
| InstanceSpecification aForest = instantiate(instancePackage, forest); |
| |
| InstanceSpecification aTree = instantiate(instancePackage, tree); |
| InstanceSpecification anotherTree = instantiate(instancePackage, tree); |
| |
| Collection<InstanceSpecification> trees = |
| new java.util.HashSet<InstanceSpecification>(); |
| |
| trees.add(aTree); |
| trees.add(anotherTree); |
| |
| link(instancePackage, |
| aForest, forest_trees, |
| new Object[] {"foo", 3}, |
| aTree, a_forest_tree_forest, |
| a_forest_tree); |
| |
| link(instancePackage, |
| aForest, forest_trees, |
| new Object[] {"bar", 2}, |
| anotherTree, a_forest_tree_forest, |
| a_forest_tree); |
| |
| // allowed to specify no qualifiers |
| OCLExpression<Classifier> expr = parse( |
| "package ocltest context Forest " + |
| "inv: self.trees->asSet()" + |
| " endpackage"); |
| |
| Object result = evaluate(expr, aForest); |
| |
| assertEquals(trees, result); |
| |
| // allowed to specify both qualifiers |
| expr = parse( |
| "package ocltest context Forest " + |
| "inv: self.trees['bar', 2]" + |
| " endpackage"); |
| |
| result = evaluate(expr, aForest); |
| |
| assertSame(anotherTree, result); |
| |
| AssertionFailedError err = null; |
| |
| try { |
| // must specify both qualifiers or none |
| parse( |
| "package ocltest context Forest " + |
| "inv: not self.trees['foo'].apples->isEmpty()" + |
| " endpackage"); |
| } catch (AssertionFailedError e) { |
| // this is expected (success case) |
| err = e; |
| debugPrintln("Got expected error: " + e.getLocalizedMessage()); |
| } |
| assertNotNull("Parse should have failed", err); |
| |
| try { |
| // qualifiers must conform |
| parse( |
| "package ocltest context Forest " + |
| "inv: not self.trees[3, 'foo'].apples->isEmpty()" + |
| " endpackage"); |
| } catch (AssertionFailedError e) { |
| // this is expected (success case) |
| err = e; |
| debugPrintln("Got expected error: " + e.getLocalizedMessage()); |
| } |
| assertNotNull("Parse should have failed", err); |
| |
| try { |
| // too many qualifiers |
| parse( |
| "package ocltest context Forest " + |
| "inv: not self.trees['foo', 3, 'bar'].apples->isEmpty()" + |
| " endpackage"); |
| } catch (AssertionFailedError e) { |
| // this is expected (success case) |
| err = e; |
| debugPrintln("Got expected error: " + e.getLocalizedMessage()); |
| } |
| assertNotNull("Parse should have failed", err); |
| } |
| |
| /** |
| * Tests support for association class navigation. |
| */ |
| public void test_associationClass_RATLC00538077() { |
| InstanceSpecification aTree = instantiate(instancePackage, tree); |
| |
| InstanceSpecification anApple = instantiate(instancePackage, apple); |
| setValue(anApple, fruit_color, color_red); |
| |
| InstanceSpecification anotherApple = instantiate(instancePackage, apple); |
| setValue(anotherApple, fruit_color, color_green); |
| |
| InstanceSpecification aStem = link(instancePackage, |
| aTree, tree_apples, |
| anApple, apple_tree, |
| stem); |
| setValue(aStem, stem_length, 3); |
| |
| InstanceSpecification anotherStem = link(instancePackage, |
| aTree, tree_apples, |
| anotherApple, apple_tree, |
| stem); |
| setValue(anotherStem, stem_length, 2); |
| |
| // navigate the association end as usual |
| OCLExpression<Classifier> expr = parse( |
| "package ocltest context Tree " + |
| "inv: self.apples->forAll(a : Apple | a.color <> Color::black)" + |
| " endpackage"); |
| |
| assertTrue(check(expr, aTree)); |
| |
| setValue(anotherApple, fruit_color, color_black); |
| assertFalse(check(expr, aTree)); |
| |
| // navigate to the association class, itself |
| expr = parse( |
| "package ocltest context Tree " + |
| "inv: self.stem->forAll(s : Stem | s.length > 0)" + |
| " endpackage"); |
| |
| assertTrue(check(expr, aTree)); |
| |
| setValue(anotherStem, stem_length, 0); |
| assertFalse(check(expr, aTree)); |
| } |
| |
| /** |
| * Tests support for association class navigation in a scalar (multiplicity |
| * upper bound is 1) direction. |
| */ |
| public void test_associationClass_scalar_182193() { |
| InstanceSpecification aTree = instantiate(instancePackage, tree); |
| setValue(aTree, tree_height, 30); |
| |
| InstanceSpecification anApple = instantiate(instancePackage, apple); |
| setValue(anApple, fruit_color, color_red); |
| |
| InstanceSpecification anotherApple = instantiate(instancePackage, apple); |
| setValue(anotherApple, fruit_color, color_green); |
| |
| InstanceSpecification aStem = link(instancePackage, |
| aTree, tree_apples, |
| anApple, apple_tree, |
| stem); |
| setValue(aStem, stem_length, 3); |
| |
| InstanceSpecification anotherStem = link(instancePackage, |
| aTree, tree_apples, |
| anotherApple, apple_tree, |
| stem); |
| setValue(anotherStem, stem_length, 2); |
| |
| // navigate the association end as usual |
| OCLExpression<Classifier> expr = parse( |
| "package ocltest context Apple " + |
| "inv: self.tree.height > 20" + |
| " endpackage"); |
| |
| assertTrue(check(expr, anApple)); |
| |
| setValue(aTree, tree_height, 20); |
| assertFalse(check(expr, anApple)); |
| |
| // navigate to the association class, itself |
| expr = parse( |
| "package ocltest context Apple " + |
| "inv: self.stem.length > 0" + |
| " endpackage"); |
| |
| assertTrue(check(expr, anApple)); |
| |
| setValue(anotherStem, stem_length, 0); |
| assertFalse(check(expr, anotherApple)); |
| } |
| |
| /** |
| * Tests support for association class navigation qualifiers. |
| */ |
| public void test_associationClass_qualifiers_RATLC00538077() { |
| InstanceSpecification aTree = instantiate(instancePackage, tree); |
| |
| InstanceSpecification anApple = instantiate(instancePackage, apple); |
| setValue(anApple, fruit_color, color_red); |
| |
| InstanceSpecification anotherApple = instantiate(instancePackage, apple); |
| setValue(anotherApple, fruit_color, color_green); |
| |
| InstanceSpecification aStem = link(instancePackage, |
| aTree, tree_apples, |
| anApple, apple_tree, |
| stem); |
| setValue(aStem, stem_length, 3); |
| |
| InstanceSpecification anotherStem = link(instancePackage, |
| aTree, tree_apples, |
| anotherApple, apple_tree, |
| stem); |
| setValue(anotherStem, stem_length, 2); |
| |
| // navigate to the association class using the optional qualifier |
| OCLExpression<Classifier> expr = parse( |
| "package ocltest context Tree " + |
| "inv: self.stem[apples]->forAll(s : Stem | s.length > 0)" + |
| " endpackage"); |
| |
| assertTrue(check(expr, aTree)); |
| |
| AssertionFailedError err = null; |
| |
| try { |
| // cannot use role name "tree" as a qualifier |
| parse( |
| "package ocltest context Tree " + |
| "inv: self.stem[tree]->forAll(s : Stem | s.length > 0)" + |
| " endpackage"); |
| } catch (AssertionFailedError e) { |
| // this is expected (success case) |
| err = e; |
| debugPrintln("Got expected error: " + e.getLocalizedMessage()); |
| } |
| assertNotNull("Parse should have failed", err); |
| |
| try { |
| // cannot use other expressions as qualifiers |
| parse( |
| "package ocltest context Tree " + |
| "inv: self.stem[3]->forAll(s : Stem | s.length > 0)" + |
| " endpackage"); |
| } catch (AssertionFailedError e) { |
| // this is expected (success case) |
| err = e; |
| debugPrintln("Got expected error: " + e.getLocalizedMessage()); |
| } |
| assertNotNull("Parse should have failed", err); |
| |
| try { |
| // only one qualifier is allowed |
| parse( |
| "package ocltest context Tree " + |
| "inv: self.stem[apples, apples]->forAll(s : Stem | s.length > 0)" + |
| " endpackage"); |
| } catch (AssertionFailedError e) { |
| // this is expected (success case) |
| err = e; |
| debugPrintln("Got expected error: " + e.getLocalizedMessage()); |
| } |
| assertNotNull("Parse should have failed", err); |
| } |
| |
| /** |
| * Tests support for association class navigation in the case of a |
| * reflexive association. |
| */ |
| public void test_associationClass_reflexive_RATLC00538077() { |
| expectModified = true; |
| // set up a reflexive association class for this test case |
| AssociationClass ac = (AssociationClass) fruitPackage.createOwnedType("AC", |
| uml.getAssociationClass()); |
| |
| Property x = ac.createOwnedAttribute("x", |
| getUMLInteger()); |
| |
| Property parent = tree.createOwnedAttribute("parent", |
| tree); |
| parent.setLower(0); |
| parent.setIsUnique(true); |
| parent.setIsOrdered(false); |
| |
| Property children = tree.createOwnedAttribute("children", |
| tree); |
| children.setLower(0); |
| children.setUpper(LiteralUnlimitedNatural.UNLIMITED); |
| children.setIsUnique(true); |
| children.setIsOrdered(false); |
| |
| parent.setAssociation(ac); |
| children.setAssociation(ac); |
| |
| // create some instances |
| InstanceSpecification aTree = instantiate(instancePackage, tree); |
| |
| InstanceSpecification aChild = instantiate(instancePackage, tree); |
| InstanceSpecification anotherChild = instantiate(instancePackage, tree); |
| |
| InstanceSpecification aLink = link(instancePackage, |
| aTree, children, |
| aChild, parent, |
| ac); |
| setValue(aLink, x, 3); |
| |
| InstanceSpecification anotherLink = link(instancePackage, |
| aTree, children, |
| anotherChild, parent, |
| ac); |
| setValue(anotherLink, x, 2); |
| |
| // navigate the association end as usual |
| OCLExpression<Classifier> expr = parse( |
| "package ocltest context Tree " + |
| "inv: self.children->forAll(t : Tree | t.apples->isEmpty())" + |
| " endpackage"); |
| |
| assertTrue(check(expr, aTree)); |
| |
| link(instancePackage, |
| anotherChild, tree_apples, |
| instantiate(instancePackage, apple), apple_tree, |
| null); |
| |
| assertFalse(check(expr, aTree)); |
| |
| // navigate to the association class, itself, using the role qualifier |
| expr = parse( |
| "package ocltest context Tree " + |
| "inv: self.aC[children]->forAll(ac : AC | ac.x > 0)" + |
| " endpackage"); |
| |
| assertTrue(check(expr, aTree)); |
| |
| setValue(anotherLink, x, 0); |
| assertFalse(check(expr, aTree)); |
| |
| // this direction is a scalar reference |
| expr = parse( |
| "package ocltest context Tree " + |
| "inv: self.aC[parent].x > 0" + |
| " endpackage"); |
| |
| assertTrue(check(expr, aChild)); |
| assertFalse(check(expr, anotherChild)); |
| |
| AssertionFailedError err = null; |
| |
| try { |
| // ambiguous navigation to association class |
| parse( |
| "package ocltest context Tree " + |
| "inv: self.aC->forAll(ac : AC | ac.x > 0)" + |
| " endpackage"); |
| } catch (AssertionFailedError e) { |
| // this is expected (success case) |
| err = e; |
| debugPrintln("Got expected error: " + e.getLocalizedMessage()); |
| } |
| assertNotNull("Parse should have failed", err); |
| } |
| |
| /** |
| * Tests support for navigation from association class to its ends. |
| */ |
| public void test_navigateFromAssociationClass_RATLC00538077() { |
| InstanceSpecification aTree = instantiate(instancePackage, tree); |
| setValue(aTree, tree_height, 20); |
| |
| InstanceSpecification anApple = instantiate(instancePackage, apple); |
| setValue(anApple, fruit_color, color_red); |
| |
| InstanceSpecification anotherApple = instantiate(instancePackage, apple); |
| setValue(anotherApple, fruit_color, color_black); |
| |
| InstanceSpecification aStem = link(instancePackage, |
| aTree, tree_apples, |
| anApple, apple_tree, |
| stem); |
| setValue(aStem, stem_length, 3); |
| |
| InstanceSpecification anotherStem = link(instancePackage, |
| aTree, tree_apples, |
| anotherApple, apple_tree, |
| stem); |
| setValue(anotherStem, stem_length, 0); |
| |
| // navigate the association end. This end is multiplicity 1..1 by |
| // definition |
| OCLExpression<Classifier> expr = parse( |
| "package ocltest context Stem " + |
| "inv: self.apples.color <> Color::black" + |
| " endpackage"); |
| |
| assertTrue(check(expr, aStem)); |
| assertFalse(check(expr, anotherStem)); |
| |
| // this end, too, is 1..1 |
| expr = parse( |
| "package ocltest context Stem " + |
| "inv: self.tree.height > 20" + |
| " endpackage"); |
| |
| assertFalse(check(expr, aStem)); |
| assertFalse(check(expr, anotherStem)); |
| |
| setValue(aTree, tree_height, 30); |
| assertTrue(check(expr, aStem)); |
| assertTrue(check(expr, anotherStem)); |
| |
| // I have my own attributes, too |
| expr = parse( |
| "package ocltest context Stem " + |
| "inv: self.length > 0" + |
| " endpackage"); |
| |
| assertTrue(check(expr, aStem)); |
| assertFalse(check(expr, anotherStem)); |
| } |
| |
| /** |
| * Tests that content-assist suggests association class navigation. |
| */ |
| public void test_associationClass_contentAssist_RATLC00538077() { |
| helper.setContext(tree); |
| |
| try { |
| List<Choice> choices = helper.getSyntaxHelp( |
| ConstraintKind.INVARIANT, "self."); |
| assertNotNull(choices); |
| |
| // regular reference feature |
| assertChoice(choices, ChoiceKind.PROPERTY, "apples"); |
| |
| // association class navigation |
| assertChoice(choices, ChoiceKind.ASSOCIATION_CLASS, "stem"); |
| } catch (Exception e) { |
| fail("Parse failed: " + e.getLocalizedMessage()); |
| } |
| } |
| |
| /** |
| * Tests the retention of location information for property names referenced |
| * by association end call expressions for association classes. |
| */ |
| public void test_propertyPositions_associationClassEndCall() { |
| final String exprString = |
| "self.apples->notEmpty()"; |
| OCLExpression<Classifier> constraint = createQuery(tree, exprString); |
| |
| OperationCallExp<Classifier, Operation> notEmptyExp = LocationInformationTest.asOperationCall( |
| constraint); |
| FeatureCallExp<Classifier> mpcExp = LocationInformationTest.asFeatureCall( |
| notEmptyExp.getSource()); |
| |
| LocationInformationTest.assertPropertyLocation(mpcExp, |
| exprString.indexOf("apples"), |
| exprString.indexOf("->")); |
| } |
| |
| /** |
| * Tests the retention of location information for property names referenced |
| * by association class call expressions. |
| */ |
| public void test_propertyPositions_associationClassCall() { |
| final String exprString = |
| "self.stem[apples]->notEmpty()"; |
| OCLExpression<Classifier> constraint = createQuery(tree, exprString); |
| |
| OperationCallExp<Classifier, Operation> notEmptyExp = LocationInformationTest.asOperationCall( |
| constraint); |
| |
| FeatureCallExp<Classifier> mpcExp = LocationInformationTest.asFeatureCall( |
| notEmptyExp.getSource()); |
| |
| LocationInformationTest.assertPropertyLocation(mpcExp, |
| exprString.indexOf("stem"), |
| exprString.indexOf("[")); |
| } |
| |
| /** |
| * Tests the retention of location information for property names referenced |
| * by association class call expressions, where the source is a collection |
| * (so that there is an implicit collect node). |
| */ |
| public void test_propertyPositions_associationClassCall_implicitCollect() { |
| final String exprString = |
| "Tree.allInstances().stem[apples]->notEmpty()"; |
| OCLExpression<Classifier> constraint = createQuery(tree, exprString); |
| |
| OperationCallExp<Classifier, Operation> notEmptyExp = LocationInformationTest.asOperationCall( |
| constraint); |
| |
| // the OCL is implicitly ->collect(stem[apples])->notEmpty() |
| LoopExp<Classifier, Parameter> loopExp = LocationInformationTest.asLoop( |
| notEmptyExp.getSource()); |
| |
| FeatureCallExp<Classifier> mpcExp = LocationInformationTest.asFeatureCall( |
| loopExp.getBody()); |
| |
| LocationInformationTest.assertPropertyLocation(mpcExp, |
| exprString.indexOf("stem"), |
| exprString.indexOf("[")); |
| } |
| |
| /** |
| * Tests the retention of location information for property names referenced |
| * as qualifiers of association class call expressions. |
| */ |
| public void test_propertyPositions_associationClassQualified() { |
| final String exprString = |
| "self.stem[apples]->notEmpty()"; |
| OCLExpression<Classifier> constraint = createQuery(tree, exprString); |
| |
| OperationCallExp<Classifier, Operation> notEmptyExp = LocationInformationTest.asOperationCall( |
| constraint); |
| |
| AssociationClassCallExp<Classifier, Property> accExp = |
| LocationInformationTest.asAssociationClassCall(notEmptyExp.getSource()); |
| |
| List<OCLExpression<Classifier>> qualifiers = accExp.getQualifier(); |
| assertEquals(1, qualifiers.size()); |
| |
| OCLExpression<Classifier> qualifier = qualifiers.get(0); |
| |
| LocationInformationTest.assertLocation(qualifier, |
| exprString.indexOf("apples"), |
| exprString.indexOf("]->")); |
| } |
| |
| /** |
| * Tests the retention of location information for property names referenced |
| * as qualifiers of association class call expressions, where the source |
| * is a collection (so that there is an implicit collect node). |
| */ |
| public void test_propertyPositions_associationClassQualified_implicitCollect() { |
| final String exprString = |
| "Tree.allInstances().stem[apples]->notEmpty()"; |
| OCLExpression<Classifier> constraint = createQuery(tree, exprString); |
| |
| OperationCallExp<Classifier, Operation> notEmptyExp = LocationInformationTest.asOperationCall( |
| constraint); |
| |
| // the OCL is implicitly ->collect(stem[apples])->notEmpty() |
| LoopExp<Classifier, Parameter> loopExp = LocationInformationTest.asLoop( |
| notEmptyExp.getSource()); |
| |
| AssociationClassCallExp<Classifier, Property> accExp = LocationInformationTest.asAssociationClassCall( |
| loopExp.getBody()); |
| |
| List<OCLExpression<Classifier>> qualifiers = accExp.getQualifier(); |
| assertEquals(1, qualifiers.size()); |
| |
| OCLExpression<Classifier> qualifier = qualifiers.get(0); |
| |
| LocationInformationTest.assertLocation(qualifier, |
| exprString.indexOf("apples"), |
| exprString.indexOf("]->")); |
| } |
| |
| /** |
| * Tests that navigation through association ends does not throw NPE |
| * in case that at least one end in the navigation path |
| * (except for the last) is undefined. |
| */ |
| public void test_associationNullContext_bugzilla121614() { |
| // Employee class |
| Class employee = instancePackage.createOwnedClass("Employee", false); |
| |
| // manager reference |
| employee.createOwnedAttribute("manager", employee); |
| |
| // create our test instance |
| InstanceSpecification emp1 = instantiate(instancePackage, employee); |
| |
| // parse & evaluate expression |
| OCLExpression<Classifier> expr = parse( |
| "package instances context Employee " + |
| "inv: self.manager.manager" + |
| " endpackage"); |
| |
| Object result = evaluate(expr, emp1); |
| assertInvalid(result); |
| } |
| |
| /** |
| * Tests that the result of qualifying an association navigation is a |
| * scalar value (not a collection). |
| */ |
| public void test_qualifiedAssociation_scalar_133435() { |
| // unqualified navigation |
| OCLExpression<Classifier> expr = parse( |
| "package ocltest context Forest " + |
| "inv: self.trees" + |
| " endpackage"); |
| |
| assertTrue(expr.getType() instanceof CollectionType); |
| CollectionType collType = (CollectionType) expr.getType(); |
| assertSame(tree, collType.getElementType()); |
| |
| // qualified navigation |
| expr = parse( |
| "package ocltest context Forest " + |
| "inv: self.trees['foo', 3]" + |
| " endpackage"); |
| |
| assertSame(tree, expr.getType()); |
| } |
| |
| public void test_nonNavigableAttribute() { |
| OCLExpression<Classifier> expr = parseConstraint("package ocltest context Tree " + |
| "inv: forest.trees->includes(self)" + |
| " endpackage"); |
| |
| EPackage epackage = UMLUtil.convertToEcore(fruitPackage, null) |
| .iterator().next(); |
| EFactory factory = epackage.getEFactoryInstance(); |
| |
| // create a resource to provide the context of instance searches |
| Resource res = new ResourceImpl(URI.createURI("test://foo")); |
| res.eAdapters().add(new ECrossReferenceAdapter()); |
| |
| resourceSet.getPackageRegistry().put(epackage.getNsURI(), epackage); |
| |
| try { |
| EClass eTree = (EClass) epackage.getEClassifier("Tree"); |
| EClass eForest = (EClass) epackage.getEClassifier("Forest"); |
| EStructuralFeature eTrees = eForest.getEStructuralFeature("trees"); |
| |
| EObject aForest = factory.create(eForest); |
| res.getContents().add(aForest); |
| |
| @SuppressWarnings("unchecked") |
| EList<EObject> trees = (EList<EObject>) aForest.eGet(eTrees); |
| EObject aTree = factory.create(eTree); |
| res.getContents().add(aTree); |
| trees.add(aTree); |
| EObject anotherTree = factory.create(eTree); |
| res.getContents().add(anotherTree); |
| trees.add(anotherTree); |
| aTree = factory.create(eTree); |
| res.getContents().add(aTree); |
| trees.add(aTree); |
| |
| assertTrue(check(expr, anotherTree)); |
| } finally { |
| res.unload(); |
| } |
| } |
| |
| public void test_nonNavigableAttribute_instanceSpecification() { |
| OCLExpression<Classifier> expr = parseConstraint("package ocltest context Tree " + |
| "inv: forest.trees->includes(self)" + |
| " endpackage"); |
| |
| InstanceSpecification aForest = instantiate(instancePackage, forest); |
| |
| InstanceSpecification aTree = instantiate(instancePackage, tree); |
| addValue(aForest, forest_trees, aTree); |
| InstanceSpecification anotherTree = instantiate(instancePackage, tree); |
| addValue(aForest, forest_trees, anotherTree); |
| aTree = instantiate(instancePackage, tree); |
| addValue(aForest, forest_trees, aTree); |
| |
| assertTrue(check(expr, anotherTree)); |
| } |
| |
| public void test_unnamedAttribute_194245() { |
| expectModified = true; |
| Property tree_forest = forest_trees.getOtherEnd(); |
| assertNotNull(tree_forest); |
| assertNotNull(tree_forest.getName()); |
| tree_forest.setName(null); // unnamed end |
| |
| // create a resource to provide the context of instance searches |
| Resource res = new ResourceImpl(URI.createURI("test://foo")); |
| res.eAdapters().add(new ECrossReferenceAdapter()); |
| |
| OCLExpression<Classifier> expr = parseConstraint("package ocltest context Tree " + |
| "inv: forest.trees->includes(self)" + |
| " endpackage"); |
| |
| EPackage epackage = UMLUtil.convertToEcore(fruitPackage, null) |
| .iterator().next(); |
| EFactory factory = epackage.getEFactoryInstance(); |
| |
| resourceSet.getPackageRegistry().put(epackage.getNsURI(), epackage); |
| |
| EClass eTree = (EClass) epackage.getEClassifier("Tree"); |
| EClass eForest = (EClass) epackage.getEClassifier("Forest"); |
| EStructuralFeature eTrees = eForest.getEStructuralFeature("trees"); |
| |
| EObject aForest = factory.create(eForest); |
| res.getContents().add(aForest); |
| |
| @SuppressWarnings("unchecked") |
| EList<EObject> trees = (EList<EObject>) aForest.eGet(eTrees); |
| EObject aTree = factory.create(eTree); |
| res.getContents().add(aTree); |
| trees.add(aTree); |
| EObject anotherTree = factory.create(eTree); |
| res.getContents().add(anotherTree); |
| trees.add(anotherTree); |
| aTree = factory.create(eTree); |
| res.getContents().add(aTree); |
| trees.add(aTree); |
| |
| assertTrue(check(expr, anotherTree)); |
| } |
| |
| public void test_unnamedAttribute_instanceSpecification_194245() { |
| expectModified = true; |
| Property tree_forest = forest_trees.getOtherEnd(); |
| assertNotNull(tree_forest); |
| assertNotNull(tree_forest.getName()); |
| tree_forest.setName(null); // unnamed end |
| |
| OCLExpression<Classifier> expr = parseConstraint("package ocltest context Tree " + |
| "inv: forest.trees->includes(self)" + |
| " endpackage"); |
| |
| InstanceSpecification aForest = instantiate(instancePackage, forest); |
| |
| InstanceSpecification aTree = instantiate(instancePackage, tree); |
| addValue(aForest, forest_trees, aTree); |
| InstanceSpecification anotherTree = instantiate(instancePackage, tree); |
| addValue(aForest, forest_trees, anotherTree); |
| aTree = instantiate(instancePackage, tree); |
| addValue(aForest, forest_trees, aTree); |
| |
| assertTrue(check(expr, anotherTree)); |
| } |
| |
| public void test_associationEndAmbiguity_nonNavigable_194245() { |
| expectModified = true; |
| // create a duplicate end |
| forest.createAssociation( |
| true, AggregationKind.SHARED_LITERAL, "someTrees", 0, -1, tree, |
| false, AggregationKind.SHARED_LITERAL, "forest", 0, 1); |
| |
| BasicEnvironment benv = OCLUtil.getAdapter(ocl.getEnvironment(), |
| BasicEnvironment.class); |
| benv.setOption(ProblemOption.AMBIGUOUS_ASSOCIATION_ENDS, |
| ProblemHandler.Severity.WARNING); |
| |
| parseConstraint("package ocltest context Tree " + |
| "inv: forest.trees->includes(self)" + |
| " endpackage"); |
| |
| Diagnostic diag = ocl.getProblems(); |
| assertNotNull(diag); |
| assertEquals(Diagnostic.WARNING, diag.getSeverity()); |
| debugPrintln("Got expected warning: " + diag.getMessage()); |
| } |
| |
| public void test_associationEndAmbiguity_unnamed_194245() { |
| expectModified = true; |
| Property tree_forest = forest_trees.getOtherEnd(); |
| assertNotNull(tree_forest); |
| assertNotNull(tree_forest.getName()); |
| tree_forest.setName(null); // unnamed end |
| |
| // create a duplicate end |
| forest.createAssociation( |
| true, AggregationKind.SHARED_LITERAL, "someTrees", 0, -1, tree, |
| false, AggregationKind.SHARED_LITERAL, null, 0, 1); // unnamed |
| |
| BasicEnvironment benv = OCLUtil.getAdapter(ocl.getEnvironment(), |
| BasicEnvironment.class); |
| benv.setOption(ProblemOption.AMBIGUOUS_ASSOCIATION_ENDS, |
| ProblemHandler.Severity.WARNING); |
| |
| parseConstraint("package ocltest context Tree " + |
| "inv: forest.trees->includes(self)" + |
| " endpackage"); |
| |
| Diagnostic diag = ocl.getProblems(); |
| assertNotNull(diag); |
| assertEquals(Diagnostic.WARNING, diag.getSeverity()); |
| debugPrintln("Got expected warning: " + diag.getMessage()); |
| } |
| |
| public void test_associationEndAmbiguity_errorSeverity_194245() { |
| expectModified = true; |
| // create a duplicate end |
| forest.createAssociation( |
| true, AggregationKind.SHARED_LITERAL, "someTrees", 0, -1, tree, |
| false, AggregationKind.SHARED_LITERAL, "forest", 0, 1); |
| |
| try { |
| ocl.parse(new OCLInput("package ocltest context Tree " + |
| "inv: forest.trees->includes(self)" + |
| " endpackage")); |
| |
| fail("Should not have parsed"); |
| } catch (ParserException e) { |
| // success |
| debugPrintln("Got expected exception: " + e.getLocalizedMessage()); |
| } |
| } |
| |
| public void test_nonNavigableAttribute_inheritedAssociation_194245() { |
| OCLExpression<Classifier> expr = parseConstraint( |
| "package UML context LiteralNull " + |
| "inv: not owningProperty.type.oclIsUndefined()" + |
| " endpackage"); |
| |
| Package pkg = UMLFactory.eINSTANCE.createPackage(); |
| pkg.setName("pkg"); |
| |
| Class a = pkg.createOwnedClass("A", false); |
| Property foo = a.createOwnedAttribute("foo", null); |
| |
| LiteralNull nullVal = (LiteralNull) foo.createDefaultValue(null, null, |
| UMLPackage.Literals.LITERAL_NULL); |
| |
| assertFalse(check(expr, nullVal)); |
| |
| foo.setType(a); |
| |
| assertTrue(check(expr, nullVal)); |
| } |
| |
| public void test_unnamedAttribute_inheritedAssociation_194245() { |
| OCLExpression<Classifier> expr = parseConstraint( |
| "package UML context Class " + |
| "inv: instanceSpecification.classifier->excluding(self)->isEmpty()" + |
| " endpackage"); |
| |
| Package pkg = UMLFactory.eINSTANCE.createPackage(); |
| pkg.setName("pkg"); |
| |
| Class a = pkg.createOwnedClass("A", false); |
| Class b = pkg.createOwnedClass("B", false); |
| |
| InstanceSpecification instance = (InstanceSpecification) pkg.createPackagedElement( |
| "anA", UMLPackage.Literals.INSTANCE_SPECIFICATION); |
| instance.getClassifiers().add(a); |
| |
| assertTrue(check(expr, a)); |
| |
| instance = (InstanceSpecification) pkg.createPackagedElement( |
| "anotherA", UMLPackage.Literals.INSTANCE_SPECIFICATION); |
| instance.getClassifiers().add(a); |
| |
| assertTrue(check(expr, a)); |
| |
| instance.getClassifiers().add(b); // violate the constraint |
| assertFalse(check(expr, a)); |
| } |
| |
| /** |
| * Tests support for navigation from association to its ends. |
| */ |
| public void test_navigateFromAssociation() { |
| // set the option to consider all Assocations as OCL association-classes |
| ParsingOptions.setOption(ocl.getEnvironment(), UMLParsingOptions.ASSOCIATION_CLASS_TYPE, |
| Association.class); |
| |
| InstanceSpecification aForest = instantiate(instancePackage, forest); |
| setValue(aForest, forest_area, 1000); |
| |
| InstanceSpecification aTree = instantiate(instancePackage, tree); |
| setValue(aTree, tree_height, 2); |
| |
| InstanceSpecification anotherTree = instantiate(instancePackage, tree); |
| setValue(anotherTree, tree_height, 0); |
| |
| InstanceSpecification aLink = link(instancePackage, |
| aForest, forest_trees, |
| aTree, forest_trees.getOtherEnd(), |
| forest_trees.getAssociation()); |
| |
| InstanceSpecification anotherLink = link(instancePackage, |
| aForest, forest_trees, |
| anotherTree, forest_trees.getOtherEnd(), |
| forest_trees.getAssociation()); |
| |
| // navigate the association end. This end is multiplicity 1..1 by |
| // definition |
| OCLExpression<Classifier> expr = parse( |
| "package ocltest context A_Forest_Tree " + |
| "inv: self.trees.height > 0" + |
| " endpackage"); |
| |
| assertTrue(check(expr, aLink)); |
| assertFalse(check(expr, anotherLink)); |
| |
| // this end, too, is 1..1 |
| expr = parse( |
| "package ocltest context A_Forest_Tree " + |
| "inv: self.forest.area > 1000" + |
| " endpackage"); |
| |
| assertFalse(check(expr, aLink)); |
| assertFalse(check(expr, anotherLink)); |
| |
| setValue(aForest, forest_area, 1050); |
| assertTrue(check(expr, aLink)); |
| assertTrue(check(expr, anotherLink)); |
| } |
| |
| /** |
| * Tests that reference to uml::Class::general does not resolve to anything, |
| * not the Classifier::general attribute (because it is redefined as |
| * Class::superType) nor the bogus association end Generalization::general. |
| */ |
| @SuppressWarnings("unchecked") |
| public void test_classGeneralResolvesToNothing_251808() { |
| try { |
| assertNull( |
| "Should not find redefined property Classifier::general", |
| OCLUtil.getAdapter(ocl.getEnvironment(), |
| Environment.Lookup.class).tryLookupProperty( |
| getMetaclass("Class"), "general")); |
| } catch (AmbiguousLookupException e) { |
| fail("Should not be an ambiguous look-up"); |
| } catch (LookupException e) { |
| fail("Should not be a look-up exception"); |
| } |
| |
| // this should parse OK, though |
| parseConstraint("package UML context Class " + |
| "inv: self.oclAsType(Classifier).general->isEmpty()" + |
| " endpackage"); |
| } |
| |
| // |
| // Fixture methods |
| // |
| |
| @Override |
| protected void setUp() { |
| super.setUp(); |
| |
| instanceResource = resourceSet.createResource( |
| URI.createFileURI("/tmp/instances.uml")); |
| |
| instancePackage = umlf.createPackage(); |
| instancePackage.setName("instances"); |
| instanceResource.getContents().add(instancePackage); |
| } |
| } |