eclipse-ocl / org.eclipse.ocl

Eclipse Public License 2.0
0 stars 0 forks source link

[language] Support for opposite navigation with an Ecore-binding #339

Closed eclipse-ocl-bot closed 9 hours ago

eclipse-ocl-bot commented 9 hours ago

| --- | --- | | Bugzilla Link | 251621 | | Status | CLOSED FIXED | | Importance | P3 enhancement | | Reported | Oct 21, 2008 17:08 EDT | | Modified | Sep 29, 2013 18:26 EDT | | Blocks | 318248 | | Reporter | Ed Willink |

Description

Bug #245586 started with a discussion of whether OCL should or should not assist QVT in using unnamed opposites in EMOF; the discussion was inconclusive and diverted to refactoring simpleNameCS to allow a derived simplePropertyNameCS which solves the problem within QVTr.

I have just started writing the missing validation constraints for QVTr using my IMP-based MDT OCL text editor. So I'm writing pure MDT OCL expressions referring to the QVT meta-model which is defined in Ecore/EMOF.

I find that I cannot use the implicit toLeadingLower name to navigate a default-named relationship, because AbstractEnvironment.findNonNavigableAssociationEnds is never called.

It is difficult to resolve this in a derived implementation because of a number of private methods.

I'll submit a patch that at least makes derivation possible once bug #242236 has settled. You can choose whether to accept a larger patch that performs the resolution as well.

eclipse-ocl-bot commented 9 hours ago

By Axel Uhl on Nov 13, 2010 20:33

Quick and partial answer on a few of your points.

qualifiers

These seem to be ignored by the EcoreEvaluationEnvironment, although there is a UML-derived FeatureMap idiom that might activate them. Leave it for the pivot model.

I noticed that the qualifiers thing slipped in again after I had already removed it for the standalone hiddenopposites plugin. I'll remove the handling of qualifiers from the patch because they obviously can't occur in an Ecore context.

autogenerated and @since

If you put the @since within the

  • it doesn't get lost when you re-genmodel.

Will do. Helpful hint, thanks.

*'WithHiddenOpposites'

This is rather clumsy and not very informative or accurate. It doesn't scale well. I think SWT gives us a better precedent. e.g ITextHover, then ITextHoverExtension then ITextHoverExtension2... I think we should consider new rather than extending interfaces; VisitorExtension, OCLFactoryExtension. Then, in MDT/OCL 4.0.0, we minimize the API change by just making Visitor extend VisitorExtension. If code steadily migrates to use the derived 'WithHiddenOpposites' we would have to change it all back to once the 'WithHiddenOpposites' is promoted. The SWT approach requires no change at promotion, just an option to delete redundant interfaces.

I think this makes EvaluationVisitorWithHiddenOpposite obsolete since EvaluationVisitorDecorator can just extend EvaluationVisitor and VisitorExtension.

I think it's useful to see for which reason an extended interface is introduced.

OppositeEndFinder

This not what the class does; none of its methods return an opposite end, since the opposite cannot be reified as an EReference. It is a PropertyNavigator. (Also AssociationEnd was discontinued in UML 2.0).

navigateOppositePropertyWithForwardScope, navigateOppositePropertyWithBackwardScope

where is the definition of scope and see? Why is the actual implementation identical?

True scoping is, e.g., introduced by our query2-based implementation of the interface and is needed by the impactAnalyzer. During expression evaluation, forward scope is applied. During backward traversal for instance scope analysis, backward scope needs to be used ("who sees an object?").

should be EReference argument.

Will change that. Thanks for the hint.

OppositePropertyNavigator

This no longer needs to be a separate class. Half of it is copied from EvaluationVisitorImpl and the other half belongs there.

Merged with EvaluationVisitorImpl

navigateOppositeProperty(EStructuralFeature

This should be navigateOppositeProperty(EReference so that the type check is

Fixed.

done up front. This implies EReference in the Ecore file.

I don't think this is easily possible. The .ecore model comes from the OCL.uml model where we only have Property. This maps to EStructuralFeature eventually, as for PropertyCallExp. Ideas?

(EClass) property.eContainer() is confusing. Use property.getEContainingClass(). .isSuperTypeOf(resultCandidate.eClass() is confusing. Use .isInstance(resultCandidate).

Changed.

                if (propertyValue == target
                        || (propertyValue instanceof Collection<?> &&

((Collection<?>) propertyValue).contains(target))) { can this ever fail? containment is very strict in Ecore and MOF.

The target object could be contained in resultCandidate in some containment property other than "property". In this case the check fails.

                    result = coerceValue(property, resultCandidate, /* copy

*/true);

surely this is coercing to the type at the wrong end?

Good catch. Changed into:

result = CollectionUtil.createNewBag(Collections.singleton(resultCandidate));

    } else if (oppositeEndFinder == null) {

can this ever sensibly happen? perhaps an assert in the constructor.

Done.

        result =

oppositeEndFinder.navigateOppositePropertyWithForwardScope(property, (EObject) target);

there is no coerceValue here. coerceValue should be redundant with the static analysis scheduling explicit conversions, but at present the code occasionally needs dynamic conversion.

navigateOppositeProperty... always returns a collection as expected for opposite navigation (c.g. discussion with Ed Merks about the inability to assert any useful multiplicity for the opposite end; an exception to be discussed later may be traveling across a containment in opposite direction where the multiplicity is known to be 0..1.)

There also seems to be significant logic duplication with navigateOppositePropertyWithSymmetricScope.

The check for containment features is to avoid less clever implementations that redefine DefaultOppositeEndFinder behavior performing badly for containments.

So much for now.

eclipse-ocl-bot commented 9 hours ago

By Axel Uhl on Nov 15, 2010 07:57

qualifiers

These seem to be ignored by the EcoreEvaluationEnvironment, although there is a UML-derived FeatureMap idiom that might activate them. Leave it for the pivot model.

Removed the qualifiers stuff for opposite property call exp.

If you expect more extensions to be added, a single extension may be more appropriate. Changed.

I think this makes EvaluationVisitorWithHiddenOpposite obsolete since EvaluationVisitorDecorator can just extend EvaluationVisitor and VisitorExtension.

Done.

More to come.

eclipse-ocl-bot commented 9 hours ago

By Axel Uhl on Nov 15, 2010 11:21

EnvironmentWithHiddenOpposites.getHiddenOppositeProperties(C classifier)

should be CLS cls.

Here, the problem is that OCLSyntaxHelper.getPropertyChoices(C eClass) calls EnvironmentWithHiddenOpposites.getHiddenOppositeProperties(C classifier) using eClass as argument. Casting eClass to CLS doesn't seem good to me. I suggest to leave it at C unless you have a good idea how to refactor OCLSyntaxHelper then.

DefaultOppositeEndFinder.subclasses

The comment is subclasses, but the implementation is proper subclasses.

DefaultOppositeEndFinder.cacheSubclassRelations

This is a confusing name because it works upwards rather than downwards. I would expect it to gather known derived classes rather than register with all superclasses. You don't seem to exploit transitivity. I think a lazy implementation in which a request for the subclasses of X computes the union of the subclasses of X would be better, but I guess you need the pivot meta-model to have the transient subClasses eOpposite of superClasses. In the meantime ESuperAdapter might help. DefaultOppositeEndFinder.updateOppositeCache -> cachePackage -> cache, cacheSubclassRelations -> cacheSubclassForSuperclass*

These names are not obviously part of the same algorithm which is accumulate all caches rather than update opposite cache; there is no deletion of obsolete packages. I think that 'update' is a major functionality increment that requires a fix to Bug 315034. Until we support mutation by design, I see little point supporting partial meta-model mutation. So there seems no point incurring the repeated cost of the O(Classes*Depth) traversal with full filtered set insertion throughout.

Sorry, that was a remnant of earlier functionality which is no longer required. I removed the entire subclass caching code.

DefaultOppositeEndFinder.getAllOppositeEnds(EClassifier

should take an EClass argument.

Same issue as above. Code such as in OCLSyntaxHelper comes in with a C which is EClassifier in the Ecore specialization. I'm so far unaware of a good solution in such generic clients. Is there anything in UMLReflection or TypeUtil that would allow for type-checking the C object to be of type CLS actually?

oppositesOnEClass is unclear. Perhaps referringEClasses.

That would suggest it holds EClass objects which it doesn't. It holds a map from names (String) to sets of EReference elements.

DefaultOppositeEndFinder.getAllSubclasses

is never used, so subclasses seems redundant. Odd since it should be used.

See above. Removed. Sorry it was included in the first place.

EcoreEvaluationEnvironment(OppositeEndFinder oppositeEndFinder)

I don't like this extra constructor. Currently we have zero OppositeEndFinder algorithms. You add one. If we want more then use dependency injection and/or a set method to allow external binding.

That would not allow making it final which I think is a very useful assertion. The field should not change over the course of an evaluation environment's life cycle. Does one additional constructor really hurt that much? And what exactly are you referring to by "dependency injection?" Wasn't there the issue of adding a dependency to the Google DI framework?

EcoreEvaluationEnvironment.createExtentMap

You have fundamentally changed the implementation class. How do you realize the old functionality. It appears that 'oppsiteEndFinder' must be an arbitrary end finder.

What do you mean by "arbitrary?" All tests pass. The DefaultOppositeEndFinder's getAllInstancesSeenBy(...) method mimics the old behavior of the LazyExtentMap, only that additionally it tracks changes by means of a content adapter (see AllInstancesContentAdapter) so that re-using the same ExtentMap across model changes becomes possible. This could be used to avoid re-building the extent map from scratch upon each OCL evaluation.

I've also changed AllInstancesContentAdapter such that a WeakHashMap is used to avoid strongly referencing elements otherwise eligible for GC.

Other OppositeEndFinder implementations may come with a different definition of scope. IMHO, delegating allInstances() to the OppositeEndFinder ensures consistency between the scope used for allInstances() and the scope used for opposite navigation.

OCLMessages.properties

'Assembles a description of a' describes what happens rather than why it happens. (OCL is internationalized and the translators need to understand the context.)

Changed to "Name of a...".

tryLookupOppositeProperty

The tryLookup alternate API was introduced to allow multiple returns and failures for QVT that the original lookup didn't support. Since opposite is new API there is no need for both and the associated tests and casts. Perhaps eliminate tryLookup and give lookup the tryLookup signature.

Done. EnvironmentWithHiddenOpposites.Lookup is no more.

TypeUtil

This is informally deprecated since it uses static methods. You should find that you don't need to extend it.

Then what's a good location for TypeUtil.getOppositePropertyType which is used by AbstractOCLAnalyzer?

AbstractVisitor.visitOppositePropertyCallExp // source is null when the property call expression is an // association class navigation qualifier Is this comment true? Surely opposites apply only to property navigation? and do opposite properties have qualifiers ? (and even if they did is the OCL specification implementable here?)

No, I've removed the comment as well as the qualifiers remnants.

AbstractVisitor.handleOppositePropertyCallExp

same qualifiers query. @param callExp is wrong.

Are you referring to the missing "opposite" in front of "property call" or to the "if there is a source"? The latter was copied from handlePropertyCallExp, and looking at it, it may have to be removed there as well.

ToStringVisitor

probably worth factoring out a handlePropertyQualifiers into AbstractVisitor so that EvaluationVisitor uses it too.

No more, after qualifier results were removed.

EvaluationVisitorImpl.visitOppositePropertyCallExp

Why no opposite counterpart of getPropertyBody(property)? A Complete OCL document could augment a hidden opposite! OK Complete OCL support is not complete. Another problem fixed by the pivot model.

The challenge: to which element to attach the annotation? If an annotation is attached to the forward EReference, we cannot just use that for the opposite direction. Hidden opposites are not supported in case the forward EReference is derived. Ideally, that would be expressed as a constraint / check somewhere. Ideas?

Logger

You use a non-static instance of java.util.Logger. The messages are not internationalized and do not go via the plugin log. Xtext and so the examples plugins uses the com.apache.log4j logger, but I'm not sure that the integration with the error log is yet right.

I've changed the code to use OCLEcorePlugin.warning instead.

OCLEcore.ecore

Why doesn't OppositePropertyCallExp extend OppositePropertyCallExp<...> in the same pattern as PropertyCallExp?

Because I forgot to add the generalization. Thanks for pointing this out. Added.

AbstractOCLAnalyzer.simpleNameCS

    if (astNode == null) {
        astNode = simplePropertyName(simpleNameCS, env, source,
            sourceElementType, simpleName);
    }
    if (astNode == null) {
        astNode = simpleOppositePropertyName(simpleNameCS, env, source,
            sourceElementType, simpleName);
    }

is not right because for each implicit self you must consider property and opposite for the first self before moving on to the second. You will find the second self.property rather than the first self.opposite.

Not sure I understand. When I use a hidden opposite property with implicit self, as in

package ocltest context Stem\
  inv: stemOfApple->notEmpty()\
endpackage

then simpleOppositePropertyName does a lookup for the implicit source. I missed adding lookupImplicitSourceForOppositeProperty to EnvironmentWithHiddenOpposites which I've now fixed. I also added a test case for opposite property use with implicit self which passes. I still have this hunch that I'm not getting what it is that you mean. Maybe you could post a test that fails with the current implementation?

Javadoc

There are many missing @param clauses which gives warnings during build.

Can you please send the list? Or is there a way to see them in the regular Eclipse build? It doesn't seem that @param tags are generally required because I find many operations that either don't have Javadoc at all or have no @param tags for existing parameters. Are you referring to the use of @param tags without content?

I'll post a revised patch according to the above later today.

eclipse-ocl-bot commented 9 hours ago

By Axel Uhl on Nov 15, 2010 14:47

One more question: re-generation changes a few lines beyond indentation / line length formatting issues. For example, I get the following diff:

--- a/plugins/org.eclipse.ocl/src/org/eclipse/ocl/utilities/ExpressionInOCL.java\ +++ b/plugins/org.eclipse.ocl/src/org/eclipse/ocl/utilities/ExpressionInOCL.java\ @@ -140,7 +140,7 @@ public interface ExpressionInOCL<C, PM>

    /**
     * Returns the value of the '<em><b>Generated Type</b></em>' containment reference list.

Seems that the Ecore templates have changed. Am I on the old or new version...? If new, then I should probably not revert those changes, should I?

eclipse-ocl-bot commented 9 hours ago

By Axel Uhl on Nov 15, 2010 15:01

(In reply to comment #54)

One more question: re-generation changes a few lines beyond indentation / line length formatting issues. For example, I get the following diff: ...

Oh, I just see that this diff was manually produced in the "Fix Javadoc warnings" commit. Never mind.

eclipse-ocl-bot commented 9 hours ago

By Axel Uhl on Nov 15, 2010 16:45

Created attachment 183173 (attachment deleted)\ In-place patch for hidden opposites implementation

Change log as compared to attachment 182939 (attachment deleted):

eclipse-ocl-bot commented 9 hours ago

By Axel Uhl on Nov 16, 2010 08:45

Ed,

you had been asking what about the multiplicities of the OppositePropertyCallExp expressions. I implemented EcoreEnvironment.getOppositePropertyType(...) as follows:

/**

This constructs a BagType. The rationale was given largely by an earlier fruitful discussion with Ed Merks who noticed that other than for a forward EReference it will be expensive to track, validate and enforce a hidden opposite's multiplicity. The safe bet therefore is to assume that there may be multiple elements on the other end (therefore 0..*), and no order can be guaranteed nor preserved (therefore ordered=false).

Uniqueness, now that I think closer about it, may optionally be copied from the forward reference's uniqueness, but using unique=false is at least a safe bet.

eclipse-ocl-bot commented 9 hours ago

By Ed Willink on Nov 20, 2010 14:56

I've tried to identify the show stopper problems. The rest are easily resolved. I've suggested solutions to the show stoppers. The solutions are only suggestions; there may be better alternatives.

Show stoppers\

This is an Ecore-specific change, but 30 generic o.e.o files are affected. In particular o.e.o already has an OppositeProperty API. This patch adds another, so that o.e.o.uml ends up with two Opposite APIs with no interrelationship. Most of the o.e.o changes migrate easily to o.e.o.ecore. Some are less pleasant such as ToStringVisitor that lacks a creation factory API, so each o.e.o Impl.toString() must be overridden in o.e.o.ecore Impl.toString(). This change incidentally fixes the problem that P needs to be EReference rather than EStructuralFeature. Once o.e.o is unchanged, it will then be possible to address the limitations of the prevailing o.e.o OppositeProperty API. Hopefully this can be accommodated easily; but it must be addressed rather than ignored.

The multiplicity of opposites has not been resolved. It is perhaps ok to assume [0..1] for containment, ignoring [1], but the assumption of [0..*] for non-containment has a probabilistic foundation of perhaps 90% correct at best. That leaves 10% buggy. The behaviour of the OCL tooling must not provide different results for partially reconstructed opposites than for fully modelled opposites.\ a.b requires a 'many' boolean flag\ a.b->union(..) requires ordered and unique\ Therefore you might as well have all of lower, upper, unique, ordered. I think Ed's comments relate to convenience/efficiency, not guaranteed accuracy.

The resolution order of implicit source has not been respected. In\ context X inv: let ys : Collection(Y) = ... in ys->exists(y : Y | b)\ the resolution of b is Y::b then X::b, so you must look for\ a) Y::b as a property\ b) Y::b as an opposite property\ c) Y.super::b as a property\ d) Y.super::b as an opposite property ...Y.super.super ...\ e) X::b as a property\ f) X::b as an opposite property\ which is a real pain and one of many discouragements for my attempts.\ Your search is\ a) Y::b as a property\ b) Y.super::b as a property ...\ c) X::b as a property\ d) Y::b as an opposite property\ e) Y.super::b as an opposite property ...\ f) X::b as an opposite property\ because you add an additional opposite property pass after the non-opposite property pass.\ Somehow the property pass must look for both forward and reverse in each scope, before advancing to another scope, which is awkward with the current API that returns a Property. I think that you have to re-use the lookupProperty API and return a temporary EReference for the opposite. As a temporary, the EReference does not need to be valid. e.g. its eContainer is null, its eOpposite points at the opposite opposite property whose eOpposite is null. The missing eContainer can be obtained by eOpposite.eOpposite.eType.

I'm very uncomfortable with replacing LazyExtentMap that implements 95% of the MAP API, with ExtentMap that has 75% UOE. I see no advantage in the change; just a usage surprise for existing applications.

AllInstancesContentAdapter\

'scope': I think you mean 'extent'. Similarly 'seeing' etc are confusingly named.

WeakHashMap: I think you mean WeakHashSet, else (or anyway) please document allInstances.

Why 'Weak'. There seems no mechanism to reconstruct freed up content.

allInstances() does not return derived instances. To avoid constructed union sets, I recommend returning an Iterator so that derived instances are only accessed once base instnaces have been exhausted. Ok this is Bug 329389 on the existing code, but that doesn't mean we need to faithfully reproduce a bug.

Minor\

EcoreEnvironment constructors : we leave out the extra constructor and raise a bugzilla to exploit DI to allow user configuration.

UMLReflectionImpl.getOCLCollectionType : you've looked for alternatives without success. We'll open it up.

Trivial\

@since on UtilitiesPackageImpl.getVisitorExtension()

OCLFactoryWithHiddenOpposite -> OCLFactoryExtension\ EvaluationEnvironmentWithHiddenOpposites -> EvaluationEnvironmentExtension\ EnvironmentWithHiddenOpposites -> ExtendedEnvironment

o.e.o.e.OppositePropertyCallExpImpl.accept -> UOE\ o.e.o.OppositePropertyCallExpImpl.accept

visitOppositePropertyCallExp Javadoc comment

TypeUtil.getOppositePropertyType is not needed\ -- inline it in the sole caller.

Javadoc. The existing code is far from perfect. No @param/@return gives no build diagnostics. Partial @param/@return does. So either be lazy or enthuisiastic(preferred), not in between.

Whitespace. I have not infrequent trouble with CVS reporting massive whitespace only changes, sometimes so dramatic as to appear as a 100% change. I would love to know why this happens. I think my workspaces are all Eclipse Windows default, but I know at one point Adolfo and/or Alex and/or Christian used Linux. I think I've now seen this on my own changes, so blaming others is getting pretty tenuous. Maybe I'm doing something stupid. Maybe CVS tooling has a bug.

eclipse-ocl-bot commented 9 hours ago

By Axel Uhl on Nov 20, 2010 17:44

(In reply to comment #58)

This is an Ecore-specific change, but 30 generic o.e.o files are affected. In particular o.e.o already has an OppositeProperty API. This patch adds another, so that o.e.o.uml ends up with two Opposite APIs with no interrelationship. Most of the o.e.o changes migrate easily to o.e.o.ecore. Some are less pleasant such as ToStringVisitor that lacks a creation factory API, so each o.e.o Impl.toString() must be overridden in o.e.o.ecore Impl.toString(). This change incidentally fixes the problem that P needs to be EReference rather than EStructuralFeature. Once o.e.o is unchanged, it will then be possible to address the limitations of the prevailing o.e.o OppositeProperty API. Hopefully this can be accommodated easily; but it must be addressed rather than ignored.

I can try to do that. It would have been helpful if instead of comments to modify the OCL.uml metamodel you had pointed out early on that modifications of the o.e.o bundle are not ok from your perspective and that you would want to introduce the OppositePropertyCallExp specifically and solely for the OCLEcore.ecore metamodel.

The multiplicity of opposites has not been resolved. It is perhaps ok to assume [0..1] for containment, ignoring [1], but the assumption of [0..*] for non-containment has a probabilistic foundation of perhaps 90% correct at best. That leaves 10% buggy. The behaviour of the OCL tooling must not provide different results for partially reconstructed opposites than for fully modelled opposites.

I'm not sure I understand what you're trying to express. Why do you say that "perhaps" it is ok to assume [0..1] for containment? What's the risk you see? [0..*] is always correct and never buggy because there is no possible query result for the hidden opposite property that violates this multiplicity. Getting back to Ed Merks' point, there doesn't seem to be any point in "documenting" some multiplicity if there is no useful way to validate. It would be much more problematic IMHO if we were to allow users to specify more restrictive multiplicities, particularly such that have 1 as upper bound, and then deal with multiple query results. Therefore, please explain what you mean by "buggy." I neither understand your comment about the "different ressults for partially reconstructed opposites."

Such a hidden opposite is basically a query that is easily accessible through OCL. If we don't have it, we'd have to write complicated calls to operations that have an opaque specification of the same query. This makes in no longer amenable to the OCL Impact Analyzer, in particular. I deem the dependency on scope for the opposite query a tolerable aspect, if not even desirable. This, BTW, goes hand in hand with the scoping of allInstances() which has to determine a useful extent for a class and its subclasses. If you use EMF with resources that don't necessarily reside in an Eclipse workspace, scoping and extent definition becomes a tricky yet important topic. Almost never should an allInstances() call really return all objects of a class that may exist anywhere in some enterprise repository landscape. The same scoping that needs to be applied for reasonable allInstances() results should consistently apply to opposite queries.

a.b requires a 'many' boolean flag

No, see above.

a.b->union(..) requires ordered and unique

When it's always !ordered and !unique then so will be the union. If you want anything more specific, write a.b->asSet()->union(...) or a.b->asSequence()->union(...).

Therefore you might as well have all of lower, upper, unique, ordered. I think Ed's comments relate to convenience/efficiency, not guaranteed accuracy.

It's not about convenience but efficiency to the point where we're rather talking feasibility. And making it a Bag in all cases is not violating any contracts, therefore accurate according to the OCL spec. Please clarify what you mean by "not guaranteed accuracy" above.

The resolution order of implicit source has not been respected. In context X inv: let ys : Collection(Y) = ... in ys->exists(y : Y | b) the resolution of b is Y::b then X::b, so you must look for a) Y::b as a property b) Y::b as an opposite property c) Y.super::b as a property d) Y.super::b as an opposite property ...Y.super.super ... e) X::b as a property f) X::b as an opposite property which is a real pain and one of many discouragements for my attempts. Your search is a) Y::b as a property b) Y.super::b as a property ... c) X::b as a property d) Y::b as an opposite property e) Y.super::b as an opposite property ... f) X::b as an opposite property because you add an additional opposite property pass after the non-opposite property pass. Somehow the property pass must look for both forward and reverse in each scope, before advancing to another scope, which is awkward with the current API that returns a Property. I think that you have to re-use the lookupProperty API and return a temporary EReference for the opposite. As a temporary, the EReference does not need to be valid. e.g. its eContainer is null, its eOpposite points at the opposite opposite property whose eOpposite is null. The missing eContainer can be obtained by eOpposite.eOpposite.eType.

Good point. I'll look into that during the upcoming week and see how this may be solved.

I'm very uncomfortable with replacing LazyExtentMap that implements 95% of the MAP API, with ExtentMap that has 75% UOE. I see no advantage in the change; just a usage surprise for existing applications.

No existing tests have indicated any use other than get(...). Maybe Map is the wrong API here anyway. Please see my comments above about consistency of hidden opposite traversal and allInstances() evaluation. It would be possible to use OCL.setExtentMap() after an OCL.newInstance() all the time to inject an extent map that establishes the aforementioned consistency. However, this to me seems error-prone and inconvenient. Are there any clients of getExtentMap() that use anything but get()? All usages I can find by a quick look-up in Eclipse is EvaluationVisitorImpl.visitOperationCallExp for the allInstances() evaluation. All other uses seem to be pass-through. I would even go as far as suggesting we introduced a specific interface just for the purpose of (scoped) allInstances() evaluation that has nothing to do with java.util.Map (which to me seems more like an accident than a purposeful decision). That should then also dispel your concern regarding the introduction of ExtentMap which you seem to reject mostly due to the UnsupportedOperationExceptions begin thrown.

AllInstancesContentAdapter

'scope': I think you mean 'extent'. Similarly 'seeing' etc are confusingly named.

Extent would probably be all instances across the universe. That's infeasible. We need a scope. If someone chooses an Eclipse workspace storage as scope that's fine. But there are other, more complex scenarios such as backend databases holding EMF resources.

WeakHashMap: I think you mean WeakHashSet, else (or anyway) please document allInstances.

Are you suggesting to introduce a JDT dependency? In java.util there is no WeakHashSet. If there were, I would have used it.

Why 'Weak'. There seems no mechanism to reconstruct freed up content.

If I understand Java GC correctly, weak references are only cleared if the object is no longer strongly referenced. We won't solve the general EMF challenge of ResourceSet/Resource with dynamically evicting and re-loading resources. This AFAIK still causes all other types of trouble including duplicate model elements in memory with the corresponding "split brain" problems.

The default implementations I've provided for DefaultOppositeEndFinder and also for AllInstancesContentAdapter are based on what we get by default from EMF without using add-ons such as CDO or query2. This in particular means that the "scope" for all we do is defined by what has so far been loaded into a ResourceSet. The AllInstancesContentAdapter is consistent with this in that when a Resource gets removed from a ResourceSet and later loaded again, the content adapter will add the re-loaded objects again to its allInstances cache. The "reconstruction" part hence is carried out by AllInstancesContentAdapter in particular being an EContentAdapter.

Again, more sophisticated implementations, e.g., based on query2 or some CDO query facility will work too.

allInstances() does not return derived instances. To avoid constructed union sets, I recommend returning an Iterator so that derived instances are only accessed once base instnaces have been exhausted. Ok this is Bug 329389 on the existing code, but that doesn't mean we need to faithfully reproduce a bug.

According to the OCL spec, as 329389 points out, allInstances() shall return instances of subclasses too. Please regard the implementation in AllInstancesContentAdapter.setTarget:

    for (EClass c : target.eClass().getEAllSuperTypes()) {\
        put(c, target);\
    }\
    put(target.eClass(), target);

This ensures that instances will also be returned for allInstances() queries for abstract superclasses.

More on minor issues later.

Best,\ -- Axel

eclipse-ocl-bot commented 9 hours ago

By Ed Willink on Nov 21, 2010 11:16

(In reply to comment #59)

I can try to do that. It would have been helpful if instead of comments to modify the OCL.uml metamodel you had pointed out early on that modifications of the o.e.o bundle are not ok from your perspective and that you would want to introduce the OppositePropertyCallExp specifically and solely for the OCLEcore.ecore metamodel.

It would be great if I had better foresight or prototyped rather than just reviewed and suggested. In #33 I wrote

"Aargh! ToStringVisitor is not created by a factory method. I guess that you'll\ have to add an OppositePropertyCallExp<...> to o.e.o too. Enjoy editing OCL.uml\ and then reloading the genmodel to update OCL.ecore."

so I didn't like it then but it seemed necessary. On further investigation ToStringVisitor is work-round-able. There may be other problems that make an o.e.o change significantly more pragmatic. At present I know no good reasons to change o.e.o and some not to. In particular no changing o.e.o forces the design to resolve the there-is-only-one-opposite-API issue.

The multiplicity of opposites has not been resolved. It is perhaps ok to assume [0..1] for containment, ignoring [1], but the assumption of [0..*] for non-containment has a probabilistic foundation of perhaps 90% correct at best. That leaves 10% buggy. The behaviour of the OCL tooling must not provide different results for partially reconstructed opposites than for fully modelled opposites.

... I don't understand any of your comments that use the word scope since I have seen no definition of your use of (instances) scope, which clearly differs from my use of (namespace) scope.\

It's not about convenience but efficiency to the point where we're rather talking feasibility. And making it a Bag in all cases is not violating any contracts, therefore accurate according to the OCL spec. Please clarify what you mean by "not guaranteed accuracy" above.

Ed's comments were in regard to validation where it is permissible to omit diagnostics that do not apply to the trimmed meta-model. OCL's requirements are different. Static analysis of "a.b.c->subSequence(1,3)" presents no major problems for the full UML/CMOF meta-model, but if b is a hidden opposite, we first need to know whether it is 'many' in order to synthesize the implicit collect or not, then we need to know whether it is a Sequence in order to locate the subSequence operation. Static analysis cannot build the AST without this information.

[0..1]/[1] containment knowledge is desirable for an optimising compiler, since for [1] the compiler can exploit the non-null-ness of the container.

I'm very uncomfortable with replacing LazyExtentMap that implements 95% of the MAP API, with ExtentMap that has 75% UOE. I see no advantage in the change; just a usage surprise for existing applications.

...

I agree that Map is a very unsatisfactory API, but we're stuck with it until we can deprecate it in 4.0.0. I agree that clients probably just require get(), but probably is not good enough. We currently allow almost arbitrary Map interaction.

One day it would be good to have a thin AllInstances interface, a mandatory default AbstractAllInstances implementation, and a derived AllInstancesWithCachedOpposites. To preserve interim API compatibility, I think we need:

AbstractAllInstances implements AllInstances \ LazyExtentMap extends AbstractAllInstances implements Map\ AllInstancesWithCachedOpposites extends LazyExtentMap

AllInstancesContentAdapter

'scope': I think you mean 'extent'. Similarly 'seeing' etc are confusingly named.

Extent would probably be all instances across the universe. That's infeasible. We need a scope. If someone chooses an Eclipse workspace storage as scope that's fine. But there are other, more complex scenarios such as backend databases holding EMF resources.

The universe is clearly unrealistic.

The OCL specification is wonderfully unhelpful; no mention of extent at all.

MOF defines an extent as exactly what we want, so Christian provided a good name for Extent realised as a map.

In practice an Extent is some Resources, which can be minimally user-specified, or greedily scavenged from every XML capability in the workspace. The latter is a performance nightmare to be triggered only after explicit use of a helper function. Realistically an Extent is the transitive closure of all Resources referenced from one or more explicitly specified seed Resources.

If this 'extent' is what you mean by scope, please use extent, or ... discuss further.\

WeakHashMap: I think you mean WeakHashSet, else (or anyway) please document allInstances.

Are you suggesting to introduce a JDT dependency? In java.util there is no WeakHashSet. If there were, I would have used it.

Oops. Not only JDT, but JDT internal. I didn't look at the package. Ok just document that it is always a Map to null.\

Why 'Weak'. There seems no mechanism to reconstruct freed up content.

If I understand Java GC correctly, weak references are only cleared if the object is no longer strongly referenced. We won't solve the general EMF challenge of ResourceSet/Resource with dynamically evicting and re-loading resources. This AFAIK still causes all other types of trouble including duplicate model elements in memory with the corresponding "split brain" problems.

Agreed. But relying on GC to unload means that there is a time window following unloading in which an allInstances can return the instance. This is non-deterministic.

The ExtentMap should be explicitly loaded from Resources and optionally track instances within those Resources, so that the return from allInstances is always identical to a bespoke creation of a new ExtentMap. Tracking incurs significant costs, which are not appropriate for many usages, so I would recommend some analogue of Resources::setTrackingModification(); possibly just reusing the prevailing setting. If tracking is in place there is no need for GC assistance and consequent indeterminacy.

It would seem legal to ask for Collection.allInstances for which there might be some additional instances in let variables. Suddenly 'visible' and 'scope' make sense if including these let variables. Is this what you meant? Are there real applications that want to include temporaries within the extent? I suspect not, but perhaps the solution one day might be to support a DynamicInstances resource that could be added to the user's extent and so support working values too.

According to the OCL spec, as 329389 points out, allInstances() shall return instances of subclasses too. Please regard the implementation in AllInstancesContentAdapter.setTarget:

    for (EClass c : target.eClass().getEAllSuperTypes()) {
        put(c, target);
    }
    put(target.eClass(), target);

This ensures that instances will also be returned for allInstances() queries for abstract superclasses.

OK, missed that, but it means that an object is cached for each of its superclasses, OclAny will cache everything, and just about noone will do OclAny.allInstances(). I strongly recommend using an Iterable return so that the same lazy policy that works so well for the collection iterators works for allInstances too. With your implementation, all instances must be loaded from CDO before the evaluation can proceed. With an iterable only one instance at a time need be loaded, and if the iteration terminates early some instances never get loaded at all. This can be part of Bugzilla 327386, if you don't want to fix this one now.

This laziness could extend to loading. If the extent includes M.xmi conforming to M.ecore and N.ecore and N.xmi conforming to N.ecore, the an allInstances query for a class from M.ecore can proceed without bothering to load N.xmi.

eclipse-ocl-bot commented 9 hours ago

By Axel Uhl on Nov 21, 2010 16:52

(In reply to comment #60)

The multiplicity of opposites has not been resolved. It is perhaps ok to assume [0..1] for containment, ignoring [1], but the assumption of [0..*] for non-containment has a probabilistic foundation of perhaps 90% correct at best. That leaves 10% buggy. The behaviour of the OCL tooling must not provide different results for partially reconstructed opposites than for fully modelled opposites.

... I don't understand any of your comments that use the word scope since I have seen no definition of your use of (instances) scope, which clearly differs from my use of (namespace) scope.

It's not about convenience but efficiency to the point where we're rather talking feasibility. And making it a Bag in all cases is not violating any contracts, therefore accurate according to the OCL spec. Please clarify what you mean by "not guaranteed accuracy" above.

Ed's comments were in regard to validation where it is permissible to omit diagnostics that do not apply to the trimmed meta-model. OCL's requirements are different. Static analysis of "a.b.c->subSequence(1,3)" presents no major problems for the full UML/CMOF meta-model, but if b is a hidden opposite, we first need to know whether it is 'many' in order to synthesize the implicit collect or not, then we need to know whether it is a Sequence in order to locate the subSequence operation. Static analysis cannot build the AST without this information.

I still can't see the problem. With b being a hidden opposites, a.b has type Bag(T) (except the containment case where we may make it a 0..1). It's easily statically analyzed. We need to know whether it's 'many' and we do know: it's 'many' if it's not a containment reference. If you look at the impl, I always produce a Bag currently and don't even care about the container case. This doesn't give any problems. If you still see an issue, could you please produce a test case against my patch that reproduces the problem?

[0..1]/[1] containment knowledge is desirable for an optimising compiler, since for [1] the compiler can exploit the non-null-ness of the container.

But the [1] case is not provable without further expensive constraint checking, so I recommend to use 0..1 for containment references' hidden opposites if we want to do this improvement at all. Then the compiler can't exploit a [1] constraint, but that's the least bit I currently care about.

I'm very uncomfortable with replacing LazyExtentMap that implements 95% of the MAP API, with ExtentMap that has 75% UOE. I see no advantage in the change; just a usage surprise for existing applications.

...

I agree that Map is a very unsatisfactory API, but we're stuck with it until we can deprecate it in 4.0.0. I agree that clients probably just require get(), but probably is not good enough. We currently allow almost arbitrary Map interaction.

What if a client injects an extent map that is obtained using Collections.unmodifiableMap(...)? It would lead to a very similar situation and is already possible with today's implementation. 4.0.0 should deprecate the use of Map for the allInstances() implementation. For 3.1.0 I don't think we're making anything worse by offering a default implementation that doesn't support modifying Map operations.

One day it would be good to have a thin AllInstances interface, a mandatory default AbstractAllInstances implementation, and a derived AllInstancesWithCachedOpposites. To preserve interim API compatibility, I think we need:

AbstractAllInstances implements AllInstances LazyExtentMap extends AbstractAllInstances implements Map AllInstancesWithCachedOpposites extends LazyExtentMap

AllInstancesContentAdapter

'scope': I think you mean 'extent'. Similarly 'seeing' etc are confusingly named.

Extent would probably be all instances across the universe. That's infeasible. We need a scope. If someone chooses an Eclipse workspace storage as scope that's fine. But there are other, more complex scenarios such as backend databases holding EMF resources.

The universe is clearly unrealistic.

The OCL specification is wonderfully unhelpful; no mention of extent at all.

MOF defines an extent as exactly what we want, so Christian provided a good name for Extent realised as a map.

In practice an Extent is some Resources, which can be minimally user-specified, or greedily scavenged from every XML capability in the workspace. The latter is a performance nightmare to be triggered only after explicit use of a helper function. Realistically an Extent is the transitive closure of all Resources referenced from one or more explicitly specified seed Resources.

If this 'extent' is what you mean by scope, please use extent, or ... discuss further.

The MOF spec on extents: "...it is assumed that all objects are directly contained in one extent." But OCL, as you note, doesn't even allow us to reason about which extent to use. Clearly (as has been the case at least since MOF 1.4), there can be many extents for one class. Not being able to choose an extent, yet requiring the results to reflect an (which) extent's contents seems unpragmatic to me.

Particularly, at least from our use cases, we would like to use allInstances() based on what all the objects of a type and its subtypes are that are reachable / visible given some modularization rules (cf. OSGi or Eclipse project dependencies, or ...). This would make allInstances() at least a configurable, pragmatically useful operation which is---as you have demanded at other occasions---predictable.

I understand that with the default EMF and OCL implementation there is hardly any definition of extent nor scope than a ResourceSet with the resources it has currently loaded into memory. This is pretty random as it depends on the random order in which a tool fetches Resources into the ResourceSet and hence not predictable at all. While the AllInstancesContentAdapter mimics this default behavior, the way I have proposed to cut the interfaces allows for a more predictable implementation, e.g., based on query2 or CDO queries.

Therefore, I think although the MOF spec talks about extents, we should allow ourselves to provide a useful implementation of allInstances(). Configurable scopes help towards this goal, I claim.

WeakHashMap: I think you mean WeakHashSet, else (or anyway) please document allInstances.

Are you suggesting to introduce a JDT dependency? In java.util there is no WeakHashSet. If there were, I would have used it.

Oops. Not only JDT, but JDT internal. I didn't look at the package. Ok just document that it is always a Map to null.

Will do if we've reached consensus on how to move forward.

Why 'Weak'. There seems no mechanism to reconstruct freed up content.

If I understand Java GC correctly, weak references are only cleared if the object is no longer strongly referenced. We won't solve the general EMF challenge of ResourceSet/Resource with dynamically evicting and re-loading resources. This AFAIK still causes all other types of trouble including duplicate model elements in memory with the corresponding "split brain" problems.

Agreed. But relying on GC to unload means that there is a time window following unloading in which an allInstances can return the instance. This is non-deterministic.

Removing a resource from a ResourceSet also removes the content adapters from the objects in the resource removed. This calls unsetTarget which keeps AllInstancesContentAdapter up to date. Using weak references was therefore intended as a safety net in case an entire ResourceSet goes dangling. If you don't think this is required, that's easy to remove and most certainly not a show stopper.

The ExtentMap should be explicitly loaded from Resources and optionally track instances within those Resources, so that the return from allInstances is always identical to a bespoke creation of a new ExtentMap. Tracking incurs significant costs, which are not appropriate for many usages, so I would recommend some analogue of Resources::setTrackingModification(); possibly just reusing the prevailing setting. If tracking is in place there is no need for GC assistance and consequent indeterminacy.

See above. With tracking, can we ensure that a ResourceSet always has their Resources properly removed? What about proxy replacements? How does this work with CDO? I assume there is little overhead with using weak references given the increased certainty of not producing a leak.

Letting clients choose between incremental tracking and "extent" reconstruction for each evaluation shouldn't be too difficult. I'll look into that. For us we use many evaluations on evolving models, some of which use allInstances() here and there. Without having measured it, I suppose we benefit big time from incrementally updating the ExtentMap. Cost/benefit ratio may be different for very sparse allInstances() use over frequently changing models.

It would seem legal to ask for Collection.allInstances for which there might be some additional instances in let variables. Suddenly 'visible' and 'scope' make sense if including these let variables. Is this what you meant? Are there real

No. I was talking about model scope as in Resource, ResourceSet, Project, project dependency, modularization, OSGi bundle requirements and such.

applications that want to include temporaries within the extent? I suspect not, but perhaps the solution one day might be to support a DynamicInstances resource that could be added to the user's extent and so support working values too.

No, I would hope that eventually we achieve consistency between the scoping of queries such as with CDO and query2 and the answers we get from an OCL evaluator. Free-floating EObjects that are not even part of a ResourceSet or some other container relevant to element discovery don't seem to fit into that scheme.

According to the OCL spec, as 329389 points out, allInstances() shall return instances of subclasses too. Please regard the implementation in AllInstancesContentAdapter.setTarget:

    for (EClass c : target.eClass().getEAllSuperTypes()) {
        put(c, target);
    }
    put(target.eClass(), target);

This ensures that instances will also be returned for allInstances() queries for abstract superclasses.

OK, missed that, but it means that an object is cached for each of its superclasses, OclAny will cache everything, and just about noone will do

OclAny is not reached by the transitive closure of getEAllSuperTypes(). What do you mean?

OclAny.allInstances(). I strongly recommend using an Iterable return so that

We should do that in an interface that replaces the use of the java.util.Map interface. It would really be good if we knew how people are using the current extent Map interface. I can hardly imagine that people rely on the default extent map being updatable... Obviously, as long as we're stuck with Map there's no way to change that. If that was a show stopper then all of the current extent map implementation would be.

Best,\ -- Axel

eclipse-ocl-bot commented 9 hours ago

By Axel Uhl on Nov 21, 2010 17:20

The resolution order of implicit source has not been respected. In context X inv: let ys : Collection(Y) = ... in ys->exists(y : Y | b) the resolution of b is Y::b then X::b, so you must look for a) Y::b as a property b) Y::b as an opposite property c) Y.super::b as a property d) Y.super::b as an opposite property ...Y.super.super ... e) X::b as a property f) X::b as an opposite property which is a real pain and one of many discouragements for my attempts. Your search is a) Y::b as a property b) Y.super::b as a property ... c) X::b as a property d) Y::b as an opposite property e) Y.super::b as an opposite property ... f) X::b as an opposite property because you add an additional opposite property pass after the non-opposite property pass. Somehow the property pass must look for both forward and reverse in each scope, before advancing to another scope, which is awkward with the current API that returns a Property. I think that you have to re-use the lookupProperty API and return a temporary EReference for the opposite. As a temporary, the EReference does not need to be valid. e.g. its eContainer is null, its eOpposite points at the opposite opposite property whose eOpposite is null. The missing eContainer can be obtained by eOpposite.eOpposite.eType.

Good point. I'll look into that during the upcoming week and see how this may be solved.

How about performing both lookups (lookupProperty(...) and lookupOppositeProperty(...)) and if both happen to find something check the generalization relation between the "owners" (for the hidden opposite it's not their eContainer(), obviously). If one is more general than the other, toss its result. Otherwise, raise an exception due to the ambiguity.

-- Axel

eclipse-ocl-bot commented 9 hours ago

By Ed Willink on Nov 22, 2010 02:27

(In reply to comment #62)

The resolution order of implicit source has not been respected.

...

How about performing both lookups (lookupProperty(...) and lookupOppositeProperty(...)) and if both happen to find something check the generalization relation between the "owners" (for the hidden opposite it's not their eContainer(), obviously). If one is more general than the other, toss its result. Otherwise, raise an exception due to the ambiguity.

That can solve one of the three aspects of the problem.

You could perhaps solve the resolution of iterator scopes and context scope for implicit source, but that will require some unpleasant duplication of logic.

However you will need to change the generic o.e.o code in order to implement a reanalaysis of an additional opposite analysis that follows the current analysis that is not correctly performed by o.e.o.ecore.

The current API is influenced by the specification of Environment. I'm reluctant to adjust it too dramatically. The relevant API is fairly simple, lookupProperty and lookupImplicitSourceForProperty; both need a 'Property' object.

eclipse-ocl-bot commented 9 hours ago

By Ed Willink on Nov 22, 2010 02:35

(In reply to comment #61)

I still can't see the problem. With b being a hidden opposites, a.b has type Bag(T) (except the containment case where we may make it a 0..1). It's easily statically analyzed. We need to know whether it's 'many' and we do know: it's 'many' if it's not a containment reference. If you look at the impl, I always produce a Bag currently and don't even care about the container case. This doesn't give any problems. If you still see an issue, could you please produce a test case against my patch that reproduces the problem?

I expect the analysis using reconstructed opposites to be identical to that produced with the original opposites. I do not expect to rewrite the OCL in order to accommodate limitations in a meta-modeling persistence.

If b is a Sequence reconstructed as a Bag in "a.b->at(1)" how can you analyze it?

eclipse-ocl-bot commented 9 hours ago

By Ed Willink on Nov 22, 2010 03:12

(In reply to comment #61)

Therefore, I think although the MOF spec talks about extents, we should allow ourselves to provide a useful implementation of allInstances(). Configurable scopes help towards this goal, I claim.

I think that we're probably in complete technical agreement here; it's just terminology that needs resolution. It is your re-use of 'scope' for a domain of objects that confuses me. A scope is already used for a namespace/domain of declarations.

MOG 2.0 also says "An Extent is a context in which an Element in a set of Elements in a set can be identified. An element may be a member\ of zero or more extents. An Extent is not an Element, it is part of a MOF capability." and "The same element may have a different identifier in another extent.", so when the spec "...it is assumed that all objects are directly\ contained in one extent." the wording is not helpful. It means that all objects of interest are reachable via IdentifierEntries contained by a single ExtentImpl. It does not prohibit the existence of another ExtentImpl that contains other IdentifierEntries for some of the same objects.

So we just require an ability for users to manage an Extent with the objects of interest. This is exactly what the old LazyExtentMap and the new ExtentMap do.

At the lowest level 'manage' requires per-object management using put/get/contains.

More usefully, 'manage' requires a Resource/ResourceSet loader. NB. The contents of a ResourceSet are normally very tightly controlled by applications, it is only Modisco and Xtext that try to be helpful and load/build the world.

Beyond that 'manage' may need to be adaptive. unload is important and I recall Eike observing that EMF needs help to make Resource really unload.

Perhaps rather than loading a ResourceSet directly, an Extent has many Contributors, so that I do anExtent.install(new TrackingContributor(aResourceSet), so that the management policy is localized in the Contributor allowing many different contributions and many different policies. Your contribution is therefore perhaps a 'TrackingOppositeContributor' that augments a standard ExtentMap rather than a 'TrackingOppositeExtentMap'.

This Contributor idea is not necessary for the current bugzilla, but seems to represent a interesting direction to go in in order to increase flexibility.

eclipse-ocl-bot commented 9 hours ago

By Ed Willink on Nov 22, 2010 04:29

(In reply to comment #61)

OclAny is not reached by the transitive closure of getEAllSuperTypes(). What do you mean?

A different bug. OclAny is the run-time supertype of all types. It's just that the OCL specification failks to specify how this is modeled.

OclAny.allInstances(). I strongly recommend using an Iterable return so that

We should do that in an interface that replaces the use of the java.util.Map interface. It would really be good if we knew how people are using the current extent Map interface. I can hardly imagine that people rely on the default extent map being updatable... Obviously, as long as we're stuck with Map there's no way to change that. If that was a show stopper then all of the current extent map implementation would be.

Unfortunately the only way to find out how users use MDT/OCL is to change something and wait for complaints.

I suspect that realistic usage is:\ a) configure extent\ b) let allInstances() interrogate extent

However in the same way that you have made the extent adaptive, other users may have done similar things.

So we want to come up with a sensible clean API, then merge it with the current API so that we have a good transition path via deprecation. My AllInstances interface was a suggestion for this. My 'Contributor' suggestion is a way of making extent configuration flexible without requiring a variety of Extent implementations.

eclipse-ocl-bot commented 9 hours ago

By Axel Uhl on Nov 22, 2010 07:35

(In reply to comment #64)

(In reply to comment #61)

I still can't see the problem. With b being a hidden opposites, a.b has type Bag(T) (except the containment case where we may make it a 0..1). It's easily statically analyzed. We need to know whether it's 'many' and we do know: it's 'many' if it's not a containment reference. If you look at the impl, I always produce a Bag currently and don't even care about the container case. This doesn't give any problems. If you still see an issue, could you please produce a test case against my patch that reproduces the problem?

I expect the analysis using reconstructed opposites to be identical to that produced with the original opposites. I do not expect to rewrite the OCL in order to accommodate limitations in a meta-modeling persistence.

If b is a Sequence reconstructed as a Bag in "a.b->at(1)" how can you analyze it?

You asked for the hiddenopposites functionality to be an Ecore-only thing. In Ecore there is nothing to reconstruct a hidden opposite from. If there were, there would be no need for the hidden opposite in the first place. OCL expression portability is particularly impossible once I do as you request and push OppositePropertyCallExp down to OCLEcore.ecore only. I can't see your point, sorry.

eclipse-ocl-bot commented 9 hours ago

By Ed Willink on Nov 23, 2010 02:38

(In reply to comment #67)

If b is a Sequence reconstructed as a Bag in "a.b->at(1)" how can you analyze it?

You asked for the hiddenopposites functionality to be an Ecore-only thing. In Ecore there is nothing to reconstruct a hidden opposite from. If there were, there would be no need for the hidden opposite in the first place. OCL expression portability is particularly impossible once I do as you request and push OppositePropertyCallExp down to OCLEcore.ecore only. I can't see your point, sorry.

I think we are considering different use cases.

I'm considering that a UML model exists, OCL expressions are developed against it, then the UML model is serialized as Ecore and the OCL expressions re-used against the Ecore.

I think that you are starting with an Ecore model then enriching your OCL expressions with hidden opposites and writing your OCL expressions to accommodate the multiplicity/kind assumptions made by the reconstructor.

Since you never had a complete model, you are happy to allow the hidden opposites reconstructor to impose part of it.

I am unhappy about adding functionality that only partially repairs opposites, when a full repair is little extra effort; potentially 5 rather than 1 annotation.

eclipse-ocl-bot commented 9 hours ago

By Axel Uhl on Nov 23, 2010 04:14

(In reply to comment #68)

(In reply to comment #67)

If b is a Sequence reconstructed as a Bag in "a.b->at(1)" how can you analyze it?

You asked for the hiddenopposites functionality to be an Ecore-only thing. In Ecore there is nothing to reconstruct a hidden opposite from. If there were, there would be no need for the hidden opposite in the first place. OCL expression portability is particularly impossible once I do as you request and push OppositePropertyCallExp down to OCLEcore.ecore only. I can't see your point, sorry.

I think we are considering different use cases.

I'm considering that a UML model exists, OCL expressions are developed against it, then the UML model is serialized as Ecore and the OCL expressions re-used against the Ecore.

I think that you are starting with an Ecore model then enriching your OCL expressions with hidden opposites and writing your OCL expressions to accommodate the multiplicity/kind assumptions made by the reconstructor.

Since you never had a complete model, you are happy to allow the hidden opposites reconstructor to impose part of it.

I am unhappy about adding functionality that only partially repairs opposites, when a full repair is little extra effort; potentially 5 rather than 1 annotation.

That would additionally require to ensure that an import of a UML model produces all the right annotations and such. We don't have that use case. It's not a show stopper for our current contribution. If someone has that use case they can base on our contribution and add it. If you take it as an argument not to accept our contribution, let me know, we'll then have to look for other ways to help those who would appreciate it.

eclipse-ocl-bot commented 9 hours ago

By Axel Uhl on Nov 23, 2010 04:42

Created attachment 183652 (attachment deleted)\ In-place patch for hidden opposites implementation

This patch additionally fixes the issue regarding lookup precedences of hidden opposites in combination with implicit sources (self, iterators).\ Additionally, it makes the use of the tracking ExtentMap optional and---based on Ed's request---uses the old LazyExtentMap again by default which re-computes the extent each time a new OCL instance is created. To get the tracking behavior, use OCL.setExtentMap(new ExtentMap(...)).

eclipse-ocl-bot commented 9 hours ago

By Ed Willink on Nov 23, 2010 05:53

(In reply to comment #69)

I think we are considering different use cases.

I'm considering that a UML model exists, OCL expressions are developed against it, then the UML model is serialized as Ecore and the OCL expressions re-used against the Ecore.

I think that you are starting with an Ecore model then enriching your OCL expressions with hidden opposites and writing your OCL expressions to accommodate the multiplicity/kind assumptions made by the reconstructor.

Since you never had a complete model, you are happy to allow the hidden opposites reconstructor to impose part of it.

I am unhappy about adding functionality that only partially repairs opposites, when a full repair is little extra effort; potentially 5 rather than 1 annotation.

That would additionally require to ensure that an import of a UML model produces all the right annotations and such. We don't have that use case. It's not a show stopper for our current contribution. If someone has that use case they can base on our contribution and add it. If you take it as an argument not to accept our contribution, let me know, we'll then have to look for other ways to help those who would appreciate it.

Indeed this isn't a show stopper.

A) Currently there is support for Ecore meta-models provided non-navigable opposites are not used.

B) This patch enhances support to Ecore metamodels for which Bag-like behaviour is exhibited by non-navigable opposites.

C) Providing support for arbitrary non-navigable opposites is a further enhancement. Since non-Bag-like opposites are not supported by B) there is no forward API compatibility when C) is introduced.

This is an enhancement, which is so easy that I'll do myself rather than have another Bugzilla and a minute risk that B) is used by no-Bag-like users.

eclipse-ocl-bot commented 9 hours ago

By Ed Willink on Nov 23, 2010 05:55

(In reply to comment #70)

Created an attachment (id=183652) [details] In-place patch for hidden opposites implementation

Is this the right patch? It has 10 API problems that do not obviously appear to be from progress from the previous patch.

eclipse-ocl-bot commented 9 hours ago

By Axel Uhl on Nov 23, 2010 06:09

Regarding the updated patch, here is what it does differently:

The changes as compared to the previous patch are that I use LazyExtentMap again. I added one test case (EvaluationTest.test_allInstancesWithRemovingResource()) that explicitly tests the unloading of a resource which would give an error with LazyExtentMap but succeeds when explicitly setting ExtentMap before. I hope this eliminates your concern regarding ExtentMap not being modifiable through the java.util.Map API as its use is now optional.

Regarding the lookup precedence I added three test cases with a specific test model: OppositePropertyCallExpTest.

As you observed, they had to fail with the previous version of the patch. I then did as you suggested and extended the AbstractEnvironment.tryLookupProperty method so that it always calls lookupNonNavigableEnd which I now implemented for Ecore by providing a non-empty implementation of EcoreEnvironment.findNonNavigableAssociationEnds which now, as you suggested, synthesizes a temporary EReference object which asymmetrically sets the forward reference as its opposite. It doesn't set a container but sets its eType.

With this the three additional test cases for implicit sources and implicit self pass, as do all other existing tests.

With this, what are the remaining true show stoppers from your end? I exclude UML OCL expression portability to Ecore because there ain't no EReference for the non-existing opposites, so what with OCL-UML is a PropertyCallExp can't be one in Ecore. Therefore, portability is not a supportable use case until the Pivot model arrives. It therefore shouldn't be considered a show stopper for adopting this contribution.

Regarding the integration of OppositePropertyCallExp in the OCL.uml model, you could as well argue that anyone relying on the existing ToStringVisitor or extending it will face problems when presenting the ToStringVisitor with an OppositePropertyCallExp. I wonder what is the worse incompatibility. The way it's implemented now at least smoothly makes ToStringVisitor work. So does AbstractVisitor which I'm sure is extended by others. Wasn't letting AbstractVisitor implement VisitorExtension your original proposal? How would that work if the extension came in only in o.e.o.ecore?

eclipse-ocl-bot commented 9 hours ago

By Axel Uhl on Nov 23, 2010 06:10

(In reply to comment #72)

(In reply to comment #70)

Created an attachment (id=183652) [details] [details] In-place patch for hidden opposites implementation

Is this the right patch? It has 10 API problems that do not obviously appear to be from progress from the previous patch.

Maybe missing @since for methods added. For some reason they didn't show up. I'll do a full rebuild later and check again.

eclipse-ocl-bot commented 9 hours ago

By Axel Uhl on Nov 23, 2010 06:12

(In reply to comment #71)

A) Currently there is support for Ecore meta-models provided non-navigable opposites are not used.

B) This patch enhances support to Ecore metamodels for which Bag-like behaviour is exhibited by non-navigable opposites.

C) Providing support for arbitrary non-navigable opposites is a further enhancement. Since non-Bag-like opposites are not supported by B) there is no forward API compatibility when C) is introduced.

This is an enhancement, which is so easy that I'll do myself rather than have another Bugzilla and a minute risk that B) is used by no-Bag-like users.

Please keep in mind that there is nothing we can do to ensure that the query implementing the opposite traversal really only returns a single element. What do you plan to do if it returns many? Is that a runtime exception?

I don't think there will be an incompatibility by making this a separate Bugzilla. The default, if no further annotations exist, is Bag (!ordered, !unique, 0..*). And that's what the current Analyzer implementation does. If you later add more annotations, you should be able to extend the analyzer to type the OppositePropertyCallExp more restrictively, with the above caveats.

eclipse-ocl-bot commented 9 hours ago

By Ed Willink on Nov 23, 2010 06:57

(In reply to comment #75)

Please keep in mind that there is nothing we can do to ensure that the query implementing the opposite traversal really only returns a single element. What do you plan to do if it returns many? Is that a runtime exception?

The current evaluator has a variety of dynamic coerceValue calls, which all appear to derive from inadequate static analysis, which should explicitly synthesize every implicit collect/implicit collection type conversion. Therefore any value obtained that is wrongly typed is invalid, since the model is not well-formed.

The only exception is the non-MOF extension for XSD indeterminate multiplicity, which is I think a -2 upperbound. We can treat -2 as always a collection for static analysis but may need to dynamically convert a non-collection value.

eclipse-ocl-bot commented 9 hours ago

By Axel Uhl on Nov 24, 2010 06:12

(In reply to comment #76)

(In reply to comment #75)

Please keep in mind that there is nothing we can do to ensure that the query implementing the opposite traversal really only returns a single element. What do you plan to do if it returns many? Is that a runtime exception?

The current evaluator has a variety of dynamic coerceValue calls, which all appear to derive from inadequate static analysis, which should explicitly synthesize every implicit collect/implicit collection type conversion. Therefore any value obtained that is wrongly typed is invalid, since the model is not well-formed.

The only exception is the non-MOF extension for XSD indeterminate multiplicity, which is I think a -2 upperbound. We can treat -2 as always a collection for static analysis but may need to dynamically convert a non-collection value.

I suggest you open a new Bugzilla that asks for introducing multiplicity specifications for hidden opposites.

eclipse-ocl-bot commented 9 hours ago

By Axel Uhl on Nov 24, 2010 07:13

(In reply to comment #74)

(In reply to comment #72)

(In reply to comment #70)

Created an attachment (id=183652) [details] [details] [details] In-place patch for hidden opposites implementation

Is this the right patch? It has 10 API problems that do not obviously appear to be from progress from the previous patch.

Maybe missing @since for methods added. For some reason they didn't show up. I'll do a full rebuild later and check again.

Ah, while moving my dev env to a new machine I forgot to set the API baseline. There were a few "@since" missing which I added now. Two issues remain, one of which may be easy to resolve, the other one may deserve brief discussion.

1) AbstractOCLAnalyzer.simplePropertyName used to return PropertyCallExp. The patch widens its return type from PropertyCallExp to NavigationCallExp to also allow for OppositePropertyCallExp to be returned. This gives a "The method org.eclipse.ocl.parser.AbstractOCLAnalyzer.simplePropertyName(SimpleNameCS, Environment<PK,C,O,P,EL,PM,S,COA,SSA,CT,CLS,E>, OCLExpression, C, String) has been removed" error which I find a bit harsh. The method is "protected". The only caller within the MDT project is AbstractOCLAnalyzer.simpleNameCS. The method should probably have been declared "private" in the first place.

I see two solutions:

1.1) Create a compatibility problem filter and tolerate the widening of the return type which would force any clients in subclasses of AbstractOCLAnalyzer other than OCLAnalyzer who call this method to adapt.

1.2) Introduce a new private method with the widened return type used by simpleNameCS and let simplePropertyName delegate to the new private method and suppress results that are not instanceof PropertyCallExp (particularly OppositePropertyCallExp).

I suggest 1.2. Comments?

2) I introduced UMLReflection.getOpposite(...), again not considering the case that clients would provide their own implementations for it, but there is of course a theoretical chance... I'll look for a fix.

eclipse-ocl-bot commented 9 hours ago

By Axel Uhl on Nov 24, 2010 11:12

Created attachment 183777 (attachment deleted)\ In-place patch for hidden opposites implementation

Updated patch which eliminates the API incompatibilities regarding the UMLReflection interface by adding UMLReflectionWithOpposite. Furthermore, this version fixes the simplePropertyName API incompatibility.

eclipse-ocl-bot commented 9 hours ago

By Ed Willink on Nov 25, 2010 12:22

Fixing the opposite property implicit source is good, but I'd prefer to review in entirety rather than piece meal so I think it is just the shrinking to o.e.o.ecore only that is desirable.

NB. Initially I thought this would be too hard. Then when I looked at ToStringVisitor it seemed work-roundable. So I expect the localisation to make a few things easier, e.g. no EReference/Property confusion, and no UML impact. However it is possible that there is a horrible problem, so if I was doing it myself, I would see how it goes for a couple of hours, by which point it should be clear whether it's relatively simple to complete, or the code has collapsed; if it collapses, I'll review the current patch, and do minor tweaks myself early next week.

eclipse-ocl-bot commented 9 hours ago

By Axel Uhl on Nov 25, 2010 12:57

(In reply to comment #80)

Fixing the opposite property implicit source is good, but I'd prefer to review in entirety rather than piece meal so I think it is just the shrinking to o.e.o.ecore only that is desirable.

NB. Initially I thought this would be too hard. Then when I looked at ToStringVisitor it seemed work-roundable. So I expect the localisation to make a few things easier, e.g. no EReference/Property confusion, and no UML impact. However it is possible that there is a horrible problem, so if I was doing it myself, I would see how it goes for a couple of hours, by which point it should be clear whether it's relatively simple to complete, or the code has collapsed; if it collapses, I'll review the current patch, and do minor tweaks myself early next week.

Of course I'm not opposed to you given it a try yourself :-). Just let me know. Today I didn't get to it; maybe tomorrow.

eclipse-ocl-bot commented 9 hours ago

By Axel Uhl on Nov 29, 2010 06:23

Created attachment 184024 (attachment deleted)\ Patch for hidden opposites not changing o.e.ocl models

I'm currently looking at AbstractOCLAnalyzer where currently the creation of OppositePropertyCallExp is concentrated. To keep o.e.o free of OppositePropertyCallExp, we'd have to have a specialized analyzer class for ecore. Currently, the only concrete subclass of AbstractOCLAnalyzer is OCLAnalyzer, still in o.e.o. Its instances are constructed in OCL (that would be easy to redefine in o.e.o.ecore.OCL), but also in HelperUtil and OCLSyntaxHelper, both of which are in o.e.o without any specializations in o.e.o.ecore.

We'd therefore need to subclass OCLAnalyzer, HelperUtil and OCLSyntaxHelper in o.e.o.ecore. This in turn ripples into OCLHelperImpl which makes reference to static methods of HelperUtil. So OCLHelperImpl would need an ecore-specific subclass as well. Its instances seem to solely be created in HelperUtil.createOCLHelper which is called by o.e.o.OCL.createOCLHelper. This can be redefined in o.e.o.ecore.OCL.

This refactoring would require introducing protected factory methods for OCLAnalyzer in OCL which then get redefined in o.e.o.ecore.OCL.

What currently seems worst to me is that all of HelperUtil uses static methods throughout. This seems to make it impossible to apply the same technique that works for the OCL class and leads to massive code duplication for the parse... methods which are all static and all call the static createAnalyzer method. The static parse... methods are only called from non-static methods in OCLHelperImpl. We could introduce a factory method for HelperUtil in OCLHelperImpl and turn the static methods into non-static instance methods. The introducing a factory method for createAnalyzer makes it redefinable in an ecore-specific HelperUtil.

This in turn requires introducing type arguments for HelperUtil and the respective arguments wheverever it's used. It also requires a protected constructor on HelperUtil instead of the private default constructor it had so far.

The next problem is that subclassing OCLHelperImpl in o.e.o.ecore would require exporting it, at least to o.e.o.ecore. Currently the internal package is not exported at all. Furthermore, it needs to become public. Its constructor needs to become protected (rather than package/default).

Next problem: introducing a factory method for OCLAnalyzer in OCL introduces a method with non-API return type OCLAdapter. Even though the method has "protected" visibility this is reported as an API warning. We could simply suppress it or leave it in as a warning.

OCLSyntaxHelper is non-public final. That would have to change too. Its constructor would have to become protected instead of package/default.

It is then necessary to add another sibling to simplePropertyName in AbstractOCLAnalyzer that allows for a NavigationCallExp return.

OCLSyntaxHelper.getPropertyChoices needs to become protected and is appropriately redefined in the ecore-specialization of OCLSyntaxHelper. An additional protected method getEnvironment() is needed to expose the environment to the subclass instance. getDescription(...) needs to become protected. ChoiceImpl needs to become public, its contructor too.

We would need to subclass EvaluationVisitorImpl. This ripples into AbstractEnvironmentFactory.

The internal.evaluation package needs to be exposed to o.e.o.ecore.

ToStringVisitor.maybeAtPre needs to become protected.

The inner class OCLSyntacHelper.ASTVisitor needs to be public and subclassed by the ecore-specific OCLSyntaxHelper. Again, a factory method is required for ASTVisitor construction in OCLSyntaxHelper. The ASTVisitor constructor needs to become protected. OCLSyntaxHelper.getChoices needs to become protected.

OCLAnalyzer.history would have to become protected (or would need a protected setter) so that an ecore specialization can set it. OCLFactoryWithHistory has to become public. OCLFactoryWithHistory.delegate needs to become protected, and so has its constructor and the record method.

ValidationVisitor has to be subclassed to support VisitorExtension. ValidationVisitor.uml and .env and .visitFeatureCall(...) have to become protected.

A factory method for the validation visitor has to be introduced on HelperUtil and OCL.

The HelperUtil.validate method(s) have to become instance-scope to use the re-definable new getValidationVisitor(env) operation.

With these measures I've managed to create an alternative patch that has all tests green. It would be good if you could indicate in which direction you'd like to carry this forward. The above describes several changes of which I may imagine you find them problematic. It's my best attempt, however, at complying with your suggestion to keep o.e.o free of any hiddenopposites stuff, which I find a valid request.

eclipse-ocl-bot commented 9 hours ago

By Ed Willink on Nov 29, 2010 12:28

(In reply to comment #82)

Created an attachment (id=184024) [details] Patch for hidden opposites not changing o.e.ocl models

I misunderstood your comments. Initially I thought that the problems were practically insuperable, but you succeeded. Unfortunately the patch is not of your success; it seems to only change the o.e.o and UML models. Any chance of the right patch?

eclipse-ocl-bot commented 9 hours ago

By Axel Uhl on Nov 30, 2010 13:24

(In reply to comment #83)

(In reply to comment #82)

Created an attachment (id=184024) [details] [details] Patch for hidden opposites not changing o.e.ocl models

I misunderstood your comments. Initially I thought that the problems were practically insuperable, but you succeeded. Unfortunately the patch is not of your success; it seems to only change the o.e.o and UML models. Any chance of the right patch?

Hi Ed,

sorry, I'm not sure what yo umean. When I download the 184024 patch it doesn't contain any changes for OCL.uml. Am I wrong?

eclipse-ocl-bot commented 9 hours ago

By Axel Uhl on Nov 30, 2010 13:24

(In reply to comment #83)

(In reply to comment #82)

Created an attachment (id=184024) [details] [details] Patch for hidden opposites not changing o.e.ocl models

I misunderstood your comments. Initially I thought that the problems were practically insuperable, but you succeeded. Unfortunately the patch is not of your success; it seems to only change the o.e.o and UML models. Any chance of the right patch?

Hi Ed,

sorry, I'm not sure what you mean. When I download the 184024 patch it doesn't contain any changes for OCL.uml. Am I wrong?

eclipse-ocl-bot commented 9 hours ago

By Axel Uhl on Nov 30, 2010 13:41

Sorry, was only a partial patch for the o.e.ocl parts it seems. Replacement to follow soon.

eclipse-ocl-bot commented 9 hours ago

By Axel Uhl on Nov 30, 2010 13:59

Created attachment 184169 (attachment deleted)\ Patch for hidden opposites not changing o.e.ocl models

This one now should contain all changes for all bundles affected for the changes discussed in https://bugs.eclipse.org/bugs/show_bug.cgi?id=251621#c82. Ed, please check if this works now for you.

Once again: I managed to get it done without changes to OCL.uml, but there were several other changes necessary instead. Personally, I think both ways are possible. This patch's advantage is that it really doesn't touch OCL.uml and therefore at least keeps the metamodel extension local to the ecore part where it belongs. Judge for yourself :-)

eclipse-ocl-bot commented 9 hours ago

By Ed Willink on Dec 03, 2010 17:11

Created attachment 184519 (attachment deleted)\ Simplified 'Ecore-only' patch

The Ecore-only approach looks promising.

Attached trims a lot of redundant changes; some unnecessary edit changes, some by migrating Ecore-only functionality to Ecore. As a result there are no changes to o.e.o.uml and no API filters needed for o.e.o.

Localisation of construction in a new EnvironmentExtension provides considerable benefits for the SyntaxHelper.

Consistent use of EReference and derived Environment saves a few casts, tests, and mistyped overloads.

I'm puzzled that ExtentMap still has numerous UOEs. I thought you were changing it to extend LazyExtentMap.

eclipse-ocl-bot commented 9 hours ago

By Axel Uhl on Dec 04, 2010 11:24

I'm puzzled that ExtentMap still has numerous UOEs. I thought you were changing it to extend LazyExtentMap.

Thanks for your adjustments. Will take a more detailed look beginning of next week. Regarding the ExtentMap, according to your request it's not a default replacement for LazyExtentMap but an optional component clients can set using OCL.setExtentMap(...). I still think it would make for the better default because it's consistent with opposite navigation; although I can't really judge how many folks out there actually use the write capabilities on LazyExtentMap.

In other words: if someone uses ExtentMap instead of LazyExtentMap they accept not being able to arbitrarily write to the extent map. Its contents are solely defined through the OppositeEndFinder user. I can try to make that more clear in the Javadoc.

eclipse-ocl-bot commented 9 hours ago

By Axel Uhl on Dec 06, 2010 05:58

Ed,

taking a look at the simplified patch 184519 I don't understand how with your simplification we could pass a specific OppositeEndFinder implementation to the EcoreEnvironment. You pulled creation into a method createOppositeEndFinder(), but in order to change that, we'd have to come up with yet another specialization layer of EcoreEnvironmentFactory and EcoreEnvironment. Do you really think this is to be preferred over an additional one or two overloaded constructors?

eclipse-ocl-bot commented 9 hours ago

By Axel Uhl on Dec 06, 2010 06:02

(In reply to comment #90)

Adding to my previous comment, not passing the OppositeEndFinder through starting from the EcoreEnvironmentFactory and instead having separate createOppositeEndFinder() implementations in EcoreEvaluationEnvironment and EcoreEnvironment runs at risk of inconsistent redefinitions. In other words, if someone goes through the process (like we would have with your simplification) of subclassing EcoreEnvironment only to redefine createOppositeEndFinder(), we may miss to correspondingly subclass EcoreEvaluationEnvironment and redefine its createOppositeEndFinder consistently. How do you see this? I really think this risk isn't compensated by saving a few constructors.

eclipse-ocl-bot commented 9 hours ago

By Axel Uhl on Dec 06, 2010 06:13

(In reply to comment #91)

There would only have to be one additional public constructor, I think, and that would be in EcoreEnvironmentFactory. The constructors in EcoreEnvironment and EcoreEvaluationEnvironment that take an OppositeEndFinder could as well be package/default scope. I'd really just like to make sure that

1) it's easy for clients to use their own OppositeEndFinder as the default really doesn't get you very far as soon as you use a persistence layer other than the Eclipse filesystem-based workspace. This speaks against requiring subclassing.

2) the OppositeEndFinder specified by the client is used consistenly throughout EcoreEnvironment and EcoreEvaluationEnvironment.

I'll compile a variant of your patch suggesting how this could look using package/default-scoped constructors on EcoreEnvironment and EcoreEvaluationEnvironment with hopefully a single additional public constructor.

eclipse-ocl-bot commented 9 hours ago

By Ed Willink on Dec 06, 2010 11:12

The Environment is the hub for a variety of services such as StandardLibrary, PackageRegistry, TypeChecking, TypeResolving, Parsing, Validation, Evaluation, OppositeEndFinding.

It is not scalable to provide distinct constructors for every customisable permutation, and as a result, some of the existing services are difficult to redefine; probably because in many cases redefinition is limited to Ecore/UML.

This is precisely the problem that DI solves, through some annotations such as

@Inject\ @Implement DefaultOppositeEndFinder;\ protected OppositeEndFinder;

allowing the invocation context an alternative to the DefaultOppositeEndFinder.

When there is a use case for this flexibility, I would like to use this everywhere, so that we have a uniform extensibility policy that does not require ingenuity to work out how to override the 'wrong' code. We can do this throughout for 4.0.0. I'm reluctant to introduce any more of the old style unscalable code; we can do it now for EcoreEnvironment in 3.1.0.

eclipse-ocl-bot commented 9 hours ago

By Axel Uhl on Dec 06, 2010 12:25

Created attachment 184636 (attachment deleted)\ In addition to Ed's simplification allows for consistent passing of OppositeEndFinder

Accumulated patch that contains Ed's simplification of my earlier patch but re-introduces two additional constructors on EcoreEnvironmentFactory as well as package/default-scoped constructors on EcoreEnvironment and EcoreEvaluationEnvironment to ensure consistent passing along of an OppositeEndFinder handed in to the EcoreEnvironmentFactory, thus avoiding the need to subclass EcoreEnvironment and EcoreEvaluationEnvironment consistently to redefine their createOppositeEndFinder() consistently.

I suggest to deprecate the parameterless EcoreEvaluationEnvironment() and EcoreEnvironment(Registry) constructors.

I re-added o.e.o.e.OCL.newInstance(OppositeEndFinder) for easier handling. Furthermore, I added constructors to o.e.o.ecore.AbstractVisitor so as to enable passing on a result to the base class constructor.

eclipse-ocl-bot commented 9 hours ago

By Axel Uhl on Dec 06, 2010 17:54

(In reply to comment #93)

The Environment is the hub for a variety of services such as StandardLibrary, PackageRegistry, TypeChecking, TypeResolving, Parsing, Validation, Evaluation, OppositeEndFinding.

It is not scalable to provide distinct constructors for every customisable permutation, and as a result, some of the existing services are difficult to redefine; probably because in many cases redefinition is limited to Ecore/UML.

This is precisely the problem that DI solves, through some annotations such as

@Inject @Implement DefaultOppositeEndFinder; protected OppositeEndFinder;

allowing the invocation context an alternative to the DefaultOppositeEndFinder.

I'm all for it. This will give us an opportunity to clean things up quite a bit with 4.0.0.

When there is a use case for this flexibility, I would like to use this everywhere, so that we have a uniform extensibility policy that does not require ingenuity to work out how to override the 'wrong' code. We can do this throughout for 4.0.0. I'm reluctant to introduce any more of the old style unscalable code; we can do it now for EcoreEnvironment in 3.1.0.

That's great news, assuming that includes the additional constructor for EcoreEnvironmentFactory and the additional newInstance overload in o.e.o.ecore.OCL. I also ensured that the latest patch (184636) as I derived it with some additional minor changes from yours (184519) runs smoothly with the impact analyzer.

eclipse-ocl-bot commented 9 hours ago

By Axel Uhl on Dec 07, 2010 08:33

Created attachment 184716 (attachment deleted)\ In addition to Ed's simplification allows for consistent passing of OppositeEndFinder

In addition to the previous patch this patch contains a protected constructor and getOppositeEndFinder() on EcoreEvaluationEnvironment and a protected constructor for passing an OppositeEndFinder in EcoreEnvironmentFactory (instead of package/default scope which makes it inaccessible to subclasses).

eclipse-ocl-bot commented 9 hours ago

By Ed Willink on Dec 07, 2010 15:49

Created attachment 184753 (attachment deleted)\ Using Dependency Injection

Attached replaces the EnvironmentExtension in 184519 by Google Guice dependency injection, which is also used to support alternate configuration of the OppositeEndFinder.

The basic DI should be a separate Bugzilla, that I thought already existed. At some point many of the Factory facilities can be made more flexible by exploiting DI for them too.

eclipse-ocl-bot commented 9 hours ago

By Ed Willink on Dec 07, 2010 15:54

NB. The DI does not use @Inject since Environment is liklely to be created by traditional constructiobn and so have no injector available. This is something to try to enhance later with the OCL creating the Injector then using it for all non-obvious downstream object creation.

eclipse-ocl-bot commented 9 hours ago

By Axel Uhl on Dec 07, 2010 17:38

(In reply to comment #97)

Created an attachment (id=184753) [details] Using Dependency Injection

Attached replaces the EnvironmentExtension in 184519 by Google Guice dependency injection, which is also used to support alternate configuration of the OppositeEndFinder.

The basic DI should be a separate Bugzilla, that I thought already existed. At some point many of the Factory facilities can be made more flexible by exploiting DI for them too.

Can't build the patch. What do I have to do with my Helios to make it aware of the com.google stuff?

eclipse-ocl-bot commented 9 hours ago

By Ed Willink on Dec 08, 2010 04:15

The com.google stuff is in Orbit, so you can install Orbit, but it should appear automatically if you install Xtext or MDT/OCL Examples.