| /******************************************************************************* |
| * Copyright (c) 2007, 2010 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM - Initial API and implementation |
| * Zeligsoft - Bugs 2488692, 253252, 259630 |
| * E.D.Willink - Bug 295166 |
| *******************************************************************************/ |
| |
| package org.eclipse.ocl.uml.tests; |
| |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.Map; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| |
| import org.eclipse.emf.common.util.URI; |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.emf.ecore.EPackage; |
| import org.eclipse.emf.ecore.resource.Resource; |
| import org.eclipse.emf.ecore.resource.ResourceSet; |
| import org.eclipse.emf.ecore.util.EcoreUtil; |
| import org.eclipse.ocl.Environment; |
| import org.eclipse.ocl.EnvironmentFactory; |
| import org.eclipse.ocl.EvaluationEnvironment; |
| import org.eclipse.ocl.expressions.OCLExpression; |
| import org.eclipse.ocl.lpg.ProblemHandler; |
| import org.eclipse.ocl.options.Customizable; |
| import org.eclipse.ocl.options.EvaluationOptions; |
| import org.eclipse.ocl.options.Option; |
| import org.eclipse.ocl.options.ParsingOptions; |
| import org.eclipse.ocl.options.ProblemOption; |
| import org.eclipse.ocl.uml.OCL; |
| import org.eclipse.ocl.uml.UMLEnvironment; |
| import org.eclipse.ocl.uml.UMLEnvironmentFactory; |
| import org.eclipse.ocl.uml.UMLEvaluationEnvironment; |
| import org.eclipse.ocl.uml.options.EvaluationMode; |
| import org.eclipse.ocl.uml.options.UMLEvaluationOptions; |
| import org.eclipse.ocl.util.OCLUtil; |
| import org.eclipse.uml2.uml.CallOperationAction; |
| import org.eclipse.uml2.uml.Class; |
| import org.eclipse.uml2.uml.Classifier; |
| import org.eclipse.uml2.uml.Comment; |
| import org.eclipse.uml2.uml.Constraint; |
| import org.eclipse.uml2.uml.EnumerationLiteral; |
| import org.eclipse.uml2.uml.InstanceSpecification; |
| 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.SendSignalAction; |
| import org.eclipse.uml2.uml.State; |
| import org.eclipse.uml2.uml.UMLFactory; |
| import org.eclipse.uml2.uml.UMLPackage; |
| |
| /** |
| * Tests the {@link UMLEnvironment} class. |
| * |
| * @author Christian W. Damus (cdamus) |
| */ |
| @SuppressWarnings("nls") |
| public class UMLEnvironmentTest |
| extends AbstractTestSuite { |
| |
| /** |
| * Tests extensibility of the OCL Ecore environment. |
| */ |
| public void test_extensibility_156360() { |
| OCL ocl = OCL.newInstance(new MyEnvironmentFactory()); |
| |
| OCL.Helper helper = ocl.createOCLHelper(); |
| helper.setContext(getUMLString()); |
| |
| Constraint constraint = null; |
| |
| try { // Double \ for Java and Double \ again for OCL String Literal |
| constraint = helper.createInvariant( |
| "self.regexMatch('\\\\d{3}-\\\\d{3}-\\\\d{3}') <> null"); |
| } catch (Exception e) { |
| fail("Failed to parse: " + e.getLocalizedMessage()); |
| } |
| |
| assertTrue(ocl.check("123-456-789", constraint)); |
| assertFalse(ocl.check("123-4567-890", constraint)); |
| assertFalse(ocl.check("123-abc-456", constraint)); |
| } |
| |
| /** |
| * Tests the instance-model evaluation mode. |
| */ |
| public void test_evaluationMode_instanceModel_194390() { |
| expectModified = true; |
| UMLEnvironmentFactory factory = new UMLEnvironmentFactory(resourceSet); |
| |
| OCL ocl = OCL.newInstance(factory); |
| EvaluationOptions.setOption(ocl.getEvaluationEnvironment(), |
| UMLEvaluationOptions.EVALUATION_MODE, |
| EvaluationMode.INSTANCE_MODEL); |
| |
| OCL.Helper helper = ocl.createOCLHelper(); |
| helper.setContext(getMetaclass("Element")); |
| |
| Constraint constraint = null; |
| |
| try { |
| constraint = helper.createInvariant( |
| "self.oclIsKindOf(InstanceSpecification)"); |
| } catch (Exception e) { |
| fail("Failed to parse: " + e.getLocalizedMessage()); |
| } |
| |
| InstanceSpecification instance1 = (InstanceSpecification) fruitPackage.createPackagedElement( |
| null, UMLPackage.Literals.INSTANCE_SPECIFICATION); |
| instance1.getClassifiers().add(apple); |
| |
| try { |
| // if we had treated this as an EObject (not an InstanceSpecification) |
| // then this would have checked true |
| assertFalse(ocl.check(instance1, constraint)); |
| |
| instance1.getClassifiers().add(getMetaclass("InstanceSpecification")); |
| |
| // now we really are an instance of InstanceSpecification |
| assertTrue(ocl.check(instance1, constraint)); |
| } finally { |
| // clean up |
| instance1.destroy(); |
| } |
| } |
| |
| /** |
| * Tests the Java-EObjects evaluation mode. |
| */ |
| public void test_evaluationMode_runtimeObjects_194390() { |
| expectModified = true; |
| UMLEnvironmentFactory factory = new UMLEnvironmentFactory(resourceSet); |
| |
| OCL ocl = OCL.newInstance(factory); |
| EvaluationOptions.setOption(ocl.getEvaluationEnvironment(), |
| UMLEvaluationOptions.EVALUATION_MODE, |
| EvaluationMode.RUNTIME_OBJECTS); |
| |
| OCL.Helper helper = ocl.createOCLHelper(); |
| helper.setContext(getMetaclass("Element")); |
| |
| Constraint constraint1 = null; |
| Constraint constraint2 = null; |
| |
| try { |
| constraint1 = helper.createInvariant( |
| "self.oclIsKindOf(InstanceSpecification)"); |
| constraint2 = helper.createInvariant( |
| "self.oclIsKindOf(ocltest::Apple)"); |
| } catch (Exception e) { |
| fail("Failed to parse: " + e.getLocalizedMessage()); |
| } |
| |
| InstanceSpecification instance1 = (InstanceSpecification) fruitPackage.createPackagedElement( |
| null, UMLPackage.Literals.INSTANCE_SPECIFICATION); |
| instance1.getClassifiers().add(apple); |
| |
| try { |
| // now we really are an instance of InstanceSpecification |
| assertTrue(ocl.check(instance1, constraint1)); |
| |
| // if we had treated this as an InstanceSpecification (not an EObject) |
| // then this would have checked true |
| assertFalse(ocl.check(instance1, constraint2)); |
| } finally { |
| // clean up |
| instance1.destroy(); |
| } |
| } |
| |
| /** |
| * Tests the adaptive evaluation mode. |
| */ |
| public void test_evaluationMode_adaptive_194390() { |
| expectModified = true; |
| UMLEnvironmentFactory factory = new UMLEnvironmentFactory(resourceSet); |
| |
| OCL ocl = OCL.newInstance(factory); |
| EvaluationOptions.setOption(ocl.getEvaluationEnvironment(), |
| UMLEvaluationOptions.EVALUATION_MODE, |
| EvaluationMode.ADAPTIVE); |
| |
| OCL.Helper helper = ocl.createOCLHelper(); |
| helper.setContext(getMetaclass("Element")); |
| |
| Constraint constraint = null; |
| Constraint ownerConstraint = null; |
| |
| try { |
| constraint = helper.createInvariant( |
| "self.oclIsKindOf(InstanceSpecification)"); |
| ownerConstraint = helper.createInvariant( |
| "self.owner.oclIsKindOf(InstanceSpecification)"); |
| } catch (Exception e) { |
| fail("Failed to parse: " + e.getLocalizedMessage()); |
| } |
| |
| InstanceSpecification instance1 = (InstanceSpecification) fruitPackage.createPackagedElement( |
| null, UMLPackage.Literals.INSTANCE_SPECIFICATION); |
| instance1.getClassifiers().add(apple); |
| Comment comment = instance1.createOwnedComment(); |
| InstanceSpecification instance2 = (InstanceSpecification) fruitPackage.createPackagedElement( |
| null, UMLPackage.Literals.INSTANCE_SPECIFICATION); |
| instance2.getClassifiers().add(getMetaclass("InstanceSpecification")); |
| |
| try { |
| // adaptive mode handles instance specification (M1 level) when it |
| // happens to be classified by the "InstanceSpecification" metaclass |
| assertTrue(ocl.check(instance2, constraint)); |
| |
| // adaptive mode handles Java instance (M0 level) based on context |
| // being a non-instance-like element |
| assertTrue(ocl.check(comment, ownerConstraint)); |
| |
| // adaptive mode does not handle Java instance when it is a |
| // value- or instance-specification (M0 level) |
| assertFalse(ocl.check(instance1, constraint)); |
| } finally { |
| // clean up |
| instance2.destroy(); |
| comment.destroy(); |
| instance1.destroy(); |
| } |
| } |
| |
| /** |
| * Tests that nested environments correctly copy their parent options when |
| * inheriting the entire options map ({@link Customizable#getOptions()}). |
| */ |
| public void test_optionInheritance() { |
| Environment<Package, Classifier, Operation, Property, EnumerationLiteral, Parameter, State, CallOperationAction, SendSignalAction, Constraint, Class, EObject> |
| parent = ocl.getEnvironment(); |
| |
| // set option in parent environment |
| ParsingOptions.setOption(parent, ProblemOption.CLOSURE_ITERATOR, |
| ProblemHandler.Severity.INFO); |
| |
| Map<Option<?>, Object> parentOptions = |
| OCLUtil.getAdapter(parent, Customizable.class).getOptions(); |
| |
| // check that the map has the option |
| assertSame(ProblemHandler.Severity.INFO, parentOptions.get(ProblemOption.CLOSURE_ITERATOR)); |
| |
| Environment<Package, Classifier, Operation, Property, EnumerationLiteral, Parameter, State, CallOperationAction, SendSignalAction, Constraint, Class, EObject> |
| child = parent.getFactory().createEnvironment(parent); |
| |
| Map<Option<?>, Object> childOptions = |
| OCLUtil.getAdapter(child, Customizable.class).getOptions(); |
| |
| // check that the child's map has the option |
| assertSame(ProblemHandler.Severity.INFO, parentOptions.get(ProblemOption.CLOSURE_ITERATOR)); |
| |
| // all of the options should be the same |
| assertEquals(parentOptions, childOptions); |
| |
| // but different maps |
| assertNotSame(parentOptions, childOptions); |
| |
| // change option in child |
| ParsingOptions.setOption(child, ProblemOption.CLOSURE_ITERATOR, |
| ProblemHandler.Severity.ERROR); |
| childOptions = OCLUtil.getAdapter(child, Customizable.class).getOptions(); |
| |
| assertFalse(parentOptions.equals(childOptions)); |
| |
| assertSame(ProblemHandler.Severity.ERROR, childOptions.get(ProblemOption.CLOSURE_ITERATOR)); |
| } |
| |
| |
| /** |
| * Tests the evaluation of inverse link navigation in instance models. |
| */ |
| public void test_linkNavigationInReverse_259630() { |
| UMLEnvironmentFactory factory = new UMLEnvironmentFactory(resourceSet); |
| |
| OCL ocl = OCL.newInstance(factory); |
| EvaluationOptions |
| .setOption(ocl.getEvaluationEnvironment(), |
| UMLEvaluationOptions.EVALUATION_MODE, |
| EvaluationMode.INSTANCE_MODEL); |
| URI uri = getTestModelURI("/model/instances.uml"); |
| Resource res = resourceSet.getResource(uri, true); |
| Package instancesPkg = (Package) EcoreUtil.getObjectByType(res |
| .getContents(), UMLPackage.Literals.PACKAGE); |
| |
| Class classA = (Class) instancesPkg.getOwnedType("A"); |
| Class classB = (Class) instancesPkg.getOwnedType("B"); |
| InstanceSpecification anA = (InstanceSpecification) instancesPkg |
| .getPackagedElement("anA"); |
| InstanceSpecification anotherA = (InstanceSpecification) instancesPkg |
| .getPackagedElement("anotherA"); |
| InstanceSpecification aB = (InstanceSpecification) instancesPkg |
| .getPackagedElement("aB"); |
| |
| // test forward navigation to be sure |
| OCL.Helper helper = ocl.createOCLHelper(); |
| helper.setContext(classA); |
| OCLExpression<Classifier> query = null; |
| try { |
| query = helper.createQuery("self.b"); |
| } catch (Exception e) { |
| fail("Failed to parse: " + e.getLocalizedMessage()); |
| } |
| |
| Object result = ocl.evaluate(anA, query); |
| assertEquals( |
| "Wrong result in forward direction", Collections.singleton(aB), result); |
| |
| // now for the interesting test: inverse navigation |
| helper.setContext(classB); |
| try { |
| query = helper.createQuery("self.a"); |
| } catch (Exception e) { |
| fail("Failed to parse: " + e.getLocalizedMessage()); |
| } |
| |
| result = ocl.evaluate(aB, query); |
| |
| assertTrue(result instanceof Collection<?>); |
| |
| Collection<?> collection = (Collection<?>) result; |
| assertEquals("Wrong number of results", 2, collection.size()); |
| assertTrue("anA not in the results", collection.contains(anA)); |
| assertTrue("anotherA not in the results", collection.contains(anotherA)); |
| } |
| |
| // |
| // Framework methods |
| // |
| |
| class MyEnvironment extends UMLEnvironment { |
| Operation regexMatch; |
| |
| // this constructor is used to initialize the root environment |
| MyEnvironment(EPackage.Registry registry, ResourceSet rset) { |
| super(registry, rset); |
| |
| defineCustomOperations(); |
| } |
| |
| // this constructor is used to initialize child environments |
| MyEnvironment(MyEnvironment parent) { |
| super(parent); |
| |
| // get the parent's custom operations |
| regexMatch = parent.regexMatch; |
| } |
| |
| @Override |
| protected void setFactory( |
| EnvironmentFactory<Package, Classifier, Operation, Property, EnumerationLiteral, Parameter, State, CallOperationAction, SendSignalAction, Constraint, Class, EObject> factory) { |
| super.setFactory(factory); |
| } |
| |
| // use the AbstractEnvironment's mechanism for defining "additional operations" |
| // to add our custom operation to OCL's String primitive type |
| private void defineCustomOperations() { |
| // pattern-matching operation |
| regexMatch = UMLFactory.eINSTANCE.createOperation(); |
| regexMatch.setName("regexMatch"); |
| regexMatch.setType(getOCLStandardLibrary().getString()); |
| regexMatch.createOwnedParameter("pattern", getOCLStandardLibrary().getString()); |
| regexMatch.setIsQuery(true); |
| |
| // annotate it so that we will recognize it in the evaluation environment |
| regexMatch.addKeyword("MyEnvironment"); |
| |
| // define it as an additional operation on OCL String |
| addHelperOperation(getOCLStandardLibrary().getString(), regexMatch); |
| } |
| } |
| |
| class MyEvaluationEnvironment extends UMLEvaluationEnvironment { |
| MyEvaluationEnvironment(UMLEnvironmentFactory factory) { |
| super(factory); |
| } |
| |
| MyEvaluationEnvironment( |
| EvaluationEnvironment<Classifier, Operation, Property, Class, EObject> parent) { |
| super(parent); |
| } |
| |
| @Override |
| public Object callOperation(Operation operation, int opcode, Object source, Object[] args) { |
| if (!operation.getKeywords().contains("MyEnvironment")) { |
| // not our custom regex operation |
| return super.callOperation(operation, opcode, source, args); |
| } |
| |
| if ("regexMatch".equals(operation.getName())) { |
| Pattern pattern = Pattern.compile((String) args[0]); |
| Matcher matcher = pattern.matcher((String) source); |
| |
| return matcher.matches()? matcher.group() : null; |
| } |
| |
| throw new UnsupportedOperationException(); // unknown operation |
| } |
| } |
| |
| class MyEnvironmentFactory extends UMLEnvironmentFactory { |
| @Override |
| public MyEnvironment createEnvironment() { |
| MyEnvironment result = new MyEnvironment(getEPackageRegistry(), getResourceSet()); |
| result.setFactory(this); |
| return result; |
| } |
| |
| @Override |
| public Environment<Package, Classifier, Operation, Property, EnumerationLiteral, Parameter, State, CallOperationAction, SendSignalAction, Constraint, Class, EObject> |
| createEnvironment(Environment<Package, Classifier, Operation, Property, EnumerationLiteral, Parameter, State, CallOperationAction, SendSignalAction, Constraint, Class, EObject> parent) { |
| if (!(parent instanceof MyEnvironment)) { |
| throw new IllegalArgumentException( |
| "Parent environment must be my environment: " + parent); |
| } |
| |
| MyEnvironment result = new MyEnvironment((MyEnvironment) parent); |
| result.setFactory(this); |
| return result; |
| } |
| |
| @Override |
| public EvaluationEnvironment<Classifier, Operation, Property, Class, EObject> |
| createEvaluationEnvironment() { |
| return new MyEvaluationEnvironment(this); |
| } |
| |
| @Override |
| public EvaluationEnvironment<Classifier, Operation, Property, Class, EObject> |
| createEvaluationEnvironment( |
| EvaluationEnvironment<Classifier, Operation, Property, Class, EObject> parent) { |
| return new MyEvaluationEnvironment(parent); |
| } |
| |
| |
| } |
| } |