eclipse-ocl / org.eclipse.ocl

Eclipse Public License 2.0
0 stars 0 forks source link

[evaluator] OCL discards values from lists returned by eoperations #417

Closed eclipse-ocl-bot closed 1 month ago

eclipse-ocl-bot commented 1 month ago

| --- | --- | | Bugzilla Link | 287052 | | Status | CLOSED FIXED | | Importance | P3 normal | | Reported | Aug 19, 2009 08:57 EDT | | Modified | Nov 21, 2014 11:23 EDT | | Version | 1.3.0 | | Blocks | 156363, 286784 | | Reporter | Laurent Goubet |

Description

An expression such as "e.oclAsType(EObject).eGet(attribute)->size()" will never return anything else than "1".

The EvaluationVisitorImpl delegates this call to its evaluation environment as it is an EOperation defined in the metamodel (here, ecore). Delegation is done at line 192 of EvaluationVisitorImpl#visitOperationCallExp() :\ ----------8<----------\ result = getEvaluationEnvironment().callOperation(oper, opCode, sourceVal, evalArgs);\ ---------->8----------

Be it the EcoreEvaluationEnvironment or the UMLEvaluationEnvironment, both use coerceValue() to determine the appropriate representation of the operation's return type. The offending code is in these implementations of coerceValue :

----------8<----------\ CollectionKind kind = getCollectionKind(element);

if (kind != null) {\ [...]\ } else {\ if (value instanceof Collection) {\ Collection<?> collection = (Collection<?>) value;\ return collection.isEmpty() ? null : collection.iterator().next();\ } else {\ return value;\ }\ }\ ---------->8----------

If we couldn't determine the collection kind ... we decide to only return the first value contained by the collection.

I am not familiar enough with the TypeChecker mechanism introduced by Christian in 1.3 to know if it could help in this case. Attached is a stupid patch using instanceofs to determine the needed collection kind, namely : a Bag if the "collection" value is a bag, a Sequence if it is an instanceof List (there only are ordered lists in Java AFAIU), an OrderedSet if it is an instanceof LinkedHashSet, a Set in all other cases.

Feel free to point me towards more intelligent potential implementations :).

eclipse-ocl-bot commented 1 month ago

By Laurent Goubet on Aug 19, 2009 09:03

Created attachment 144951 (attachment deleted)\ Second attempt at creating the attachment

eclipse-ocl-bot commented 1 month ago

By Adolfo Sanchez-Barbudo Herrera on Aug 19, 2009 11:21

Hi Laurent,

I haven't revised the code or your patch. I'm just recalling to the OCL 2.0 specification. Take a look to the page 81. Rule [B] of OperationCallExpCS:

-- The source is either a collection or a single object used as a collection.\ [B] OperationCallExpCS.ast.arguments = argumentsCS.ast\ -- if the OclExpressionCS is a collectiontype, then the source is this OclExpressionCS.\ -- Otherwise, the source must be build up by defining a singleton set containing\ -- the OclExpressionCS. This is done though inserting a call to the standard\ -- operation "asSet()"

Since the return parameter of the EObject::eGet operation is a single Object, a set is implicitly created as the source of the operationCallExp, which will contain the returned Object. So I would always expect a 1 value, if you call ->size(). You will have always object regardless it's really one object or a list of objects.

I would previously call ->flatten() to obtain the number of elements in the case you are dealing with a multiple value structural feature.

If u agree, please simply resolve as invalid or something like that.

P.S: Please take part in Bug 242153. I need your and Alex votes to go on with the BUG.

Cheers,\ Adolfo.

eclipse-ocl-bot commented 1 month ago

By Laurent Goubet on Aug 19, 2009 11:28

Hi Adolfo,

The problem here doesn't lie in the call to "size" : I simply added it for the sake of having a number; thus the issue isn't with "eGet" being the source of the call to "size()".

the returned value of "eGet" truly is a single element being the first element of the "real" returned value (which was a collection). The additional values are discarded and cannot be accessed in any way.

P.S : I didn't join in Bug 242153 since I am totally unfamiliar with both IMP and LPG ... I'll try and take a look asap but I don't think my opinion is relevant here.

eclipse-ocl-bot commented 1 month ago

By Adolfo Sanchez-Barbudo Herrera on Aug 19, 2009 12:21

Hi Laurent,

I have had a look to the code. I think that is not a problem of the coercingValue algorithm, which seems to be "right". I mean, the decision is good: If the feature expects to deal with multiple values, regardless you have a collection of values or a single value, you coerce the value to a collection. On the other hand, if the feature deals with a single value, regardless your have a single value or a collection of values, you corce the value to a single one.

I guess that the problem could one of the following:

I would exactly need the problematic oclExpression, and the Ecore or UML Environment you are using to go on this.

Cheers,\ Adolfo.

eclipse-ocl-bot commented 1 month ago

By Ed Willink on Aug 19, 2009 14:33

I don't understand the problem:

The code you're examining is what happens when you have a Collection for a non-collection element; a fairly daft scenario that might merit flattening to give the first value.

getCollectionKind should not return null for a greater then 1 upper bound.

eclipse-ocl-bot commented 1 month ago

By Laurent Goubet on Aug 20, 2009 03:21

The problem here comes from the fact "eGet" is an EOperation with its return type defined to "EJavaObject". Obviously the multiplicity here is 1 since we can be dealing with either multiple or single multiplicity features (I think we all know what "eGet" does).

In this case, the EOperation can return just about anything except for primitives. This includes every single subclass of "Object" ... and this includes Collections. The issue with the coercing algorithm is that it knows it is dealing with a Collection, but it forcibly deletes every value except for the very first.

I observed this with eGet, it can happen with any EOperation defined by clients on their DSLs. AFAIU, this will be reproduced with any EOperation with a "1" upper bound but a return type like "EJavaObject", "EList" or any custom data type representing a list.

Granted, these probably won't happen often. The bug hadn't even been detected even though I believe it is present since the first versions. I still believe we should try and fix this behavior : writing an exception which return value should be a collection, yet only getting the first value of this collection seems erroneous to me.

eclipse-ocl-bot commented 1 month ago

By Ed Willink on Aug 20, 2009 04:20

Can you attach the JUnit test case that demonstrates the problem?

Yes it needs fixing, but I'm not convinced that this is the correct diagnosis and consequently solution.

eclipse-ocl-bot commented 1 month ago

By Laurent Goubet on Aug 20, 2009 04:29

Ed,

I haven't created a JUnit test for this, I simply reused the sample of bug 286784.

I'll make one reproducing this bug and atach it asap

eclipse-ocl-bot commented 1 month ago

By Laurent Goubet on Oct 13, 2009 09:44

Created attachment 149436 (attachment deleted)\ Test case highlighting the problem

The attached test case highlights this problem. I put a lot of assertions in there, but all of them are needed as Java doesn't check for the ordering when calling "LinkedHashSet.equals')" ...

This test retrieves the superslass of an EClass (created for the occasion with two superclasses) by two differents means :

I think we all aggree that the results of these two calls should be equal in both type (OrderedSet) and content (the two superclasses of "self"). Where this bug strikes is : the results are not equal. "eGet" is an EOperation defined with its return type set to "EJavaObject", thus OCL doesn't manage to find the actual collection type ... and decides to simply return the first value of this collection, ignoring the remainder.

eclipse-ocl-bot commented 1 month ago

By Ed Willink on Oct 16, 2009 09:23

Hm . I responded to this two days ago, but I think I remember what I wrote.

The test case helps. Using the debuggers toString hsows that the parsed AST contains Set{} for a CollectionLiteralExp.

The problem is that eGet does not return a collection so an implicit set is created for it, and since the implicitly created set contains the unflattened results, your resulting size is 1 not 2.

What you want cannot be done in OCL 2.0 or MDT/OCL 1.x since CollectionTyupe cannot be used in a cast.

Your test case should be used in a JUnit for MDT/OCL 3.0.

eclipse-ocl-bot commented 1 month ago

By Laurent Goubet on Oct 16, 2009 10:04

Ed,

The problem is not with "the resulting size" being "1", the problem is that the result we get is not even a Collection. With what we get as is, the second super type of the test EClass is not even accessible : it's been discarded before the result has been returned.

I must admit I don't know what the ast looks like for this example, but I would expect to get a Collection, even if its type is erroneous (Set instead of an OrderedSet). Feel free to affect the target milestone if you think it cannot be done before MDT/OCL 3.0.

eclipse-ocl-bot commented 1 month ago

By Ed Willink on Oct 16, 2009 10:55

(Sorry, my lost reply was longer and better.)

The problem is your

->asType(EClass)

The -> applied to an Object (not statically known to be a Collection) provokes creation of a Set to contain it. You then cast the Set to a non-Collection.

The only bug I see for OCL 2.0 is a failure to generate an error from this cast.

eclipse-ocl-bot commented 1 month ago

By Laurent Goubet on Apr 19, 2010 11:47

As it stands, this problem also arises when trying to retrieve an UML tagged value which contains multiple elements ; that is, all values except the very first are discarded.

so if you have an XMI looking like:

\ Lib 1\ Lib 2\ for a given stereotype application (obviously multi-valued) and try to retrieve its value through the standard UML operation "Element::getValue(Stereotype, String) : EJavaObject" you end up with only "Lib 1". The (convoluted) expression to use here would be :\ umlElement.getValue(umlElement.getAppliedStereotype('Setting'), 'libraries') I'll try to attach an UML model which present such a tagged value for you to try out :p. Note that because of the code I highlighted in comment \#0 there is absolutely no way around this issue ; no user will ever see his "Lib 2" value because of the coercion from "Collection" to "collection.iterator().next()".
eclipse-ocl-bot commented 1 month ago

By Adolfo Sanchez-Barbudo Herrera on Jun 16, 2010 16:05

Laurent, Ed,

Coming again from the newsgroup ;P

I'm recalling (now, better spelled) the different causes of the problems we have here:

"I guess that the problem could be one of the following:\ a) we are erroneously trying to coerce the value.\ b) we are erroneously coercing a collection value, when we have a feature which\ deals with a single value.\ c) we are erroneously considering that the feature deals with a single value,\ when it should deal with a collection of values (UMLEcoreEnvironment::getCollectionKind function shouldn't\ return a null kind)."

This coud be better understood as follows:

On the one hand, we could think that this is not a problem from the OCL point of view. The feature, to be more precise, the Element::getValue(...) Operation, has been defined as mono-valued so that the coercion algorithm () is properly working: A collection value is being coerced to a single one, which it turns into the first value of the collection (This could also be debatable).

On the other hand, as noticed by Laurent, the user would have expected to obtain a collection from the operation above, and the OCL implementation is avoiding to obtain the desired result.