/******************************************************************************* * Copyright (c) 2005, 2011 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 - Bug 248869 * E.D.Willink - Bug 288040 xor/or/and *******************************************************************************/ package org.eclipse.ocl.uml.tests; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.resource.impl.ResourceImpl; import org.eclipse.ocl.ParserException; import org.eclipse.ocl.SemanticException; import org.eclipse.ocl.expressions.LetExp; import org.eclipse.ocl.expressions.OCLExpression; import org.eclipse.ocl.expressions.OperationCallExp; import org.eclipse.ocl.expressions.PropertyCallExp; import org.eclipse.ocl.internal.l10n.OCLMessages; import org.eclipse.ocl.lpg.ProblemHandler; import org.eclipse.ocl.options.ParsingOptions; import org.eclipse.ocl.options.ProblemOption; import org.eclipse.ocl.util.OCLUtil; import org.eclipse.uml2.uml.Class; import org.eclipse.uml2.uml.Classifier; import org.eclipse.uml2.uml.Operation; import org.eclipse.uml2.uml.Package; import org.eclipse.uml2.uml.UMLFactory; /** * Operator precedence tests. * * @author Christian W. Damus (cwdamus) */ @SuppressWarnings("nls") public class PrecedenceTest extends AbstractTestSuite { Resource res; Package pkg; Class class1; /** * Test that let expressions require parenthesis when embedding them in * other expressions. */ public void test_let() { helper.setContext(class1); try { helper.createInvariant( "self.base_Property.redefinedProperty->isEmpty() = false implies \n" + " self.base_Property.redefinedProperty->size() = 1 and\n" + " let rp : UML::Property =\n" + " self.base_Property.redefinedProperty->asSequence()->at(1) in \n" + " self.base_Property.name = rp.name and\n" + " self.base_Property.type = rp.type"); } catch (Exception e) { fail("Failed to parse: " + e.getLocalizedMessage()); } helper.setContext(getMetaclass("NamedElement")); try { helper.createInvariant( "namespace.getNamesOfMember(\n" + " let ne : NamedElement = self in ne)->exists(n |\n" + " let len : Integer = n.size() in len = 1)"); } catch (Exception e) { fail("Failed to parse: " + e.getLocalizedMessage()); } } /** * Test that the "in" expression part of a let expression consumes as much * of the input as possible, so that * <blockquote><code>let a = ... in a.b</code></blockquote> * parses as * <blockquote><code>let a = ... in (a.b)</code></blockquote> * not as * <blockquote><code>(let a = ... in a).b</code></blockquote> */ @SuppressWarnings("unchecked") public void test_let_inExpression_182201() { helper.setContext(getMetaclass("NamedElement")); OCLExpression<Classifier> expr = null; try { expr = helper.createQuery("let ne : NamedElement = self in ne.name"); } catch (Exception e) { fail("Failed to parse: " + e.getLocalizedMessage()); } assertNotNull(expr); assertTrue(expr instanceof LetExp<?, ?>); // not a PropertyCallExp LetExp<Classifier, ?> letExp = (LetExp<Classifier, ?>) expr; assertTrue(letExp.getIn() instanceof PropertyCallExp<?, ?>); try { expr = helper.createQuery( "let n : String = let ne : NamedElement = self in ne.name in n.size()"); } catch (Exception e) { fail("Failed to parse: " + e.getLocalizedMessage()); } assertNotNull(expr); assertTrue(expr instanceof LetExp<?, ?>); // not a PropertyCallExp letExp = (LetExp<Classifier, ?>) expr; assertTrue(letExp.getIn() instanceof OperationCallExp<?, ?>); OperationCallExp<Classifier, Operation> opCall = (OperationCallExp<Classifier, Operation>) letExp.getIn(); assertEquals("size", opCall.getReferredOperation().getName()); expr = letExp.getVariable().getInitExpression(); assertTrue(expr instanceof LetExp<?, ?>); letExp = (LetExp<Classifier, ?>) letExp.getVariable().getInitExpression(); assertTrue(letExp.getIn() instanceof PropertyCallExp<?, ?>); // a different nesting try { expr = helper.createQuery( "let ne : NamedElement = self in let n : String = ne.name in n.size()"); } catch (Exception e) { fail("Failed to parse: " + e.getLocalizedMessage()); } assertNotNull(expr); assertTrue(expr instanceof LetExp<?, ?>); // not a PropertyCallExp letExp = (LetExp<Classifier, ?>) expr; assertTrue(letExp.getIn() instanceof LetExp<?, ?>); letExp = (LetExp<Classifier, ?>) letExp.getIn(); assertTrue(letExp.getIn() instanceof OperationCallExp<?, ?>); OperationCallExp<Classifier, Operation> opCall2 = (OperationCallExp<Classifier, Operation>) letExp.getIn(); assertEquals("size", opCall2.getReferredOperation().getName()); } public void test_equality_relational_179249() { helper.setContext(class1); try { // this should parse, because "1 < 2" has higher precedence. // If it hadn't, then we would fail to parse because // Integer has no "< Boolean" operation (we would parse this // expression as "1 < (2 = true)" helper.createInvariant("1 < 2 = true"); } catch (Exception e) { fail("Failed to parse: " + e.getLocalizedMessage()); } } public void test_or_and_precedence() throws ParserException { ParsingOptions.setOption(helper.getEnvironment(), ProblemOption.ELEMENT_NAME_QUOTE_ESCAPE, ProblemHandler.Severity.OK); helper.setContext(class1); assertTrueWithoutWarning("(true or (false and false)) = true"); assertTrueWithoutWarning("(true or false.\"and\"(false)) = true"); assertTrueWithoutWarning("((true or false) and false) = false"); assertTrueWithoutWarning("(true or false and false) = true"); ParsingOptions.setOption(helper.getEnvironment(), ParsingOptions.WARN_OF_XOR_OR_AND_PRECEDENCE_CHANGE, true); assertTrueWithoutWarning("(true or (false and false)) = true"); assertTrueWithoutWarning("(true or false.\"and\"(false)) = true"); assertTrueWithoutWarning("((true or false) and false) = false"); //1.x assertTrue(check(helper, class1, "(true or false and false) = false")); assertTrueWithWarning("(true or false and false) = true", OCLMessages.XorOrAndPrecedence_WARNING); } public void test_xor_or_precedence() throws ParserException { ParsingOptions.setOption(helper.getEnvironment(), ProblemOption.ELEMENT_NAME_QUOTE_ESCAPE, ProblemHandler.Severity.OK); helper.setContext(class1); assertTrueWithoutWarning("(true xor (false or false)) = true"); assertTrueWithoutWarning("(true xor false.\"or\"(false)) = true"); assertTrueWithoutWarning("((true xor false) or true) = true"); assertTrueWithoutWarning("(true xor false or true) = false"); ParsingOptions.setOption(helper.getEnvironment(), ParsingOptions.WARN_OF_XOR_OR_AND_PRECEDENCE_CHANGE, true); assertTrueWithoutWarning("(true xor (false or false)) = true"); assertTrueWithoutWarning("(true xor false.\"or\"(false)) = true"); assertTrueWithoutWarning("((true xor false) or true) = true"); //1.x assertTrue(check(helper, class1, "(true xor false or true) = true")); assertTrueWithWarning("(true xor false or true) = false", OCLMessages.XorOrAndPrecedence_WARNING); } protected void assertTrueWithWarning(String expression, String warning)throws ParserException { helper.createInvariant(expression); try { OCLUtil.checkForErrorsOrWarnings(helper.getEnvironment()); fail("Missing warning: " + warning); } catch (SemanticException e) { System.out.println("Got expected warning: " + e.getLocalizedMessage()); assertEquals(warning, e.getMessage()); } } protected void assertTrueWithoutWarning(String expression)throws ParserException { helper.createInvariant(expression); OCLUtil.checkForErrorsOrWarnings(helper.getEnvironment()); } // // Framework methods // @Override protected void setUp() { super.setUp(); // create a little test model for a Smalltalk-like collection class that // defines operations corresponding to OCL iterators res = new ResourceImpl(URI.createURI("foo://uml")); pkg = UMLFactory.eINSTANCE.createPackage(); pkg.setName("testpkg"); res.getContents().add(pkg); class1 = pkg.createOwnedClass("Class1", false); class1.createOwnedAttribute("base_Property", getMetaclass("Property")); } }