Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.

Bug 255786

Summary: Investigate support for Constraints directly annotated to Ecore
Product: [Modeling] EMF Reporter: Tristan Faure <faure.tristan>
Component: CoreAssignee: Kenn Hussey <Kenn.Hussey>
Status: VERIFIED FIXED QA Contact:
Severity: enhancement    
Priority: P3 CC: achim.demelt, adolfosbh, alexander.igdalov, Alfredo.Bencomo, andrew987650000, Andy.Carpenter, arash.aghevli, boris.gruschko, davidms, ed, give.a.damus, greg.fenton, janreimone, Kenn.Hussey, mgil
Version: 2.5.0Keywords: plan
Target Milestone: M2Flags: Kenn.Hussey: helios+
Hardware: PC   
OS: Windows XP   
Whiteboard:
Bug Depends on:    
Bug Blocks: 191689, 286329    
Attachments:
Description Flags
the patch for the feature
none
The GOOD patch for the feature
none
some improvements
none
new version with last EMF version
none
simple plugin for ocl
none
a metamodel and dynamic instance
none
some improvements
none
improvements and new management of constraint
none
a new version of ocl checker for user constraints
none
correct errors with many constraints on one Eclass
none
a new version of the meta model tested
none
problems during merge with remote sources this version is ok
none
updated patch based on HEAD
none
sample delegate implementation for OCL
none
updated test model
none
another test model, which includes an invariant
none
updated patch
none
final (hopefully) patch
none
updated sample OCL validation delegate
none
final patch Kenn.Hussey: review?

Description Tristan Faure CLA 2008-11-19 08:58:25 EST
Build ID: I20081030-1917

new feature to permit plugins to implement their constraints language

More information:

as we discussed in newsgroup 
The feature adds the possibility for a developer to add constraint language if he adds an extension point referencing a class wiwh implements the factory : ConstraintsDelegator.Factory. The factories are loaded 

As the developer povides a factory (no default factory is provided)
The user can add a constraint in its metamodel (currently only in EClass we can discuss if it's enough)
for example 

EClass : A
EAnnotation : Ecore -> constraint1 constraint2
              factory -> factoryID
EAnnotation : factoryID
              constraint1 -> String interpreted by constraint delegator
EAnnotation : factoryID
              constraint2 -> String interpreted by constraint delegator

if the id is too long and unusable in the first EAnnotation the user can use this syntax :

EClass : A
EAnnotation : Ecore -> constraint1 constraint2
              factory -> factoryID#Key
EAnnotation : Key
              constraint1 -> String interpreted by constraint delegator
EAnnotation : Key
              constraint2 -> String interpreted by constraint delegator

As i test, the solution works with dynamic instance and generated code because the constraints are checked in EObjectValidator
I already test for example in a constraint to put a qualified class name which is instanciated in the factory, this kind of behaviour can be useful. but as the class needs to be integrated to environment i think with this behaviour (class interpretation)dynamic instances will not check the constraint.

I don't provide OCL implementation i know it's possible but I prefer offers the possibility to another developer which will check the feature

If somebody can check my work and discuss with me about what is good or bad it will be a great pleasure
Comment 1 Tristan Faure CLA 2008-11-19 08:59:27 EST
Created attachment 118244 [details]
the patch for the feature
Comment 2 Tristan Faure CLA 2008-11-19 09:03:06 EST
Created attachment 118245 [details]
The GOOD patch for the feature
Comment 3 Tristan Faure CLA 2008-11-19 10:43:30 EST
i want to precise the way to fill constraints

it's not 
EClass : A
EAnnotation : Ecore -> constraint1 constraint2
              factory -> factoryID
but
EClass : A
EAnnotation : Ecore 
              constraints -> constraint1 constraint2
              factory -> factoryID
sorry for the error
Comment 4 Tristan Faure CLA 2008-11-20 04:05:30 EST
Created attachment 118338 [details]
some improvements

improvements constraints inheritance is managed
for each element the software checks constraints annotated to eSuperTypes 
this can be disabled with this option : 
EClass : A
EAnnotation : Ecore 
              constraints -> constraint1 constraint2
              factory -> factoryID
              inheritsConstraints -> false

with this option (by default to true) you can specify if you want to apply constraints inherited
the behaviour is all or nothing you can't specify if you want to inherit from esuperTypes 1 or 2
Comment 5 Tristan Faure CLA 2009-01-27 10:37:10 EST
Created attachment 123880 [details]
new version with last EMF version

this version adds some improvements for inheritance management
Comment 6 Tristan Faure CLA 2009-01-27 10:37:54 EST
Created attachment 123881 [details]
simple plugin for ocl 

this plugin adds a factory for constraints delegator and checks OCL rules
Comment 7 Tristan Faure CLA 2009-01-27 10:38:50 EST
Created attachment 123883 [details]
a metamodel and dynamic instance

this zip contains one ecore file : the metamodel with very simple constraints
and one dynamic instance to check it
Comment 8 Tristan Faure CLA 2009-01-27 10:49:46 EST
Created attachment 123885 [details]
some improvements

permits to user to stop inheritance for an element and to not define constraints :
example with no constraints
Element e1 inherits e
     Ecore
           inheritsConstraints --> false
Comment 9 Tristan Faure CLA 2009-01-30 06:02:31 EST
Hi
i would ask for (maybe) modifying the design
Currently :
for each ECLass
Eclass A
    EAnnotation : Ecore
              constraints -> constraint1 constraint2
              factory -> factoryID
    EAnnotation : factoryID
              constraint1 -> String interpreted by constraint delegator
    EAnnotation : factoryID
              constraint2 -> String interpreted by constraint delegator
Eclass B
    EAnnotation : Ecore
              constraints -> constraint1 constraint2
              factory -> factoryID
    EAnnotation : factoryID
              constraint1 -> String interpreted by constraint delegator
    EAnnotation : factoryID
              constraint2 -> String interpreted by constraint delegator

Do you think filling factory for each ECLass is irritating ? maybe we should consider an EAnnotation in EPackage defining Factory example :

EPackage
    Eannotation : Ecore
    factory -> factoryID#Key
    Eclass A
        EAnnotation : Ecore
                  constraints -> constraint1 constraint2
        EAnnotation : Key
                  constraint1 -> String interpreted by constraint     delegator
        EAnnotation : Key
                  constraint2 -> String interpreted by constraint     delegator
    Eclass B
        EAnnotation : Ecore
                  constraints -> constraint1 constraint2
        EAnnotation : Key
                      constraint1 -> String interpreted by constraint         delegator
        EAnnotation : Key
                      constraint2 -> String interpreted by constraint delegator

In this example I used the notation with the Key but we keep the factory id

what is your opinion ? 
Comment 10 Tristan Faure CLA 2009-02-25 09:46:56 EST
Created attachment 126724 [details]
improvements and new management of constraint
Comment 11 Tristan Faure CLA 2009-02-25 09:47:20 EST
Created attachment 126725 [details]
a new version of ocl checker for user constraints
Comment 12 Tristan Faure CLA 2009-02-25 09:53:21 EST
i changed the management of constraints regarding to newsgroup posts : the new version to enter constraints :

EPackage packageName
     Eannotation
          source : http://www.eclipse.org/emf/2002/Ecore  
               factory : nameOfThefactory#key

...

EClass aClassToPutConstraints
     Eannotation
          source : nameOfTheFactory // or key
               constraint : text interpreted by factory
               name : the name of the constraint
               message : the message to display if problems
               severity : WARNING // or ERROR
               inheritsConstraints : true // default to true or false

the only key neccessary is constraint 
if others are not filled default messages are added

if you don't want to put constraints but stopping inheritance
you can just only pu inheritsConstraints to false

for each constraint add an eannotation with source setted to key or factoryName
Comment 13 Tristan Faure CLA 2009-02-25 09:54:08 EST
Created attachment 126726 [details]
correct errors with many constraints on one Eclass
Comment 14 Tristan Faure CLA 2009-02-25 09:56:33 EST
Created attachment 126727 [details]
a new version of the meta model tested
Comment 15 Tristan Faure CLA 2009-02-25 10:04:31 EST
Created attachment 126729 [details]
problems during merge with remote sources this version is ok
Comment 16 Kenn Hussey CLA 2009-09-09 11:46:00 EDT
Created attachment 146761 [details]
updated patch based on HEAD

Here's a more complete set of patches, based on HEAD, which includes support for both static and dynamic constraint/invariant delegation.
Comment 17 Kenn Hussey CLA 2009-09-09 11:48:56 EDT
Created attachment 146762 [details]
sample delegate implementation for OCL
Comment 18 Kenn Hussey CLA 2009-09-09 12:56:33 EDT
Created attachment 146778 [details]
updated test model
Comment 19 Kenn Hussey CLA 2009-09-09 13:33:01 EDT
After discussing this with Ed, we felt it would be better to take a simplified (but not altogether different) approach. The patch I just attached makes use of the following annotations:

EPackage : ?
  EAnnotation : Ecore
    validationDelegates-><uri1> <uri2>

EClass : ?
  EAnnotation : Ecore
    constraints-><c1> <c2>
  EAnnotation : <uri1>
    <c1>-><body1>
    <c2>-><body2>

EOperation : ?
  EAnnotation : <uri2>
    body-><body>

To keep support for invariants and constraints consistent, we decided not to support specification of severity or message at this time; if modelers want to change those (for static models) they can customize the generated code.

The issue of whether or not constraints are "inherited" is also not addressed, in the interest of simplicity.

Feedback is welcome and appreciated.
Comment 20 Kenn Hussey CLA 2009-09-09 13:40:25 EDT
Created attachment 146787 [details]
another test model, which includes an invariant
Comment 21 Tristan Faure CLA 2009-09-14 04:37:06 EDT
Hi no problem for me with the new approach
and for inheritance i just added this to add options for user. i have no trouble with removing it
Comment 22 Kenn Hussey CLA 2009-09-16 13:08:43 EDT
Created attachment 147341 [details]
updated patch

Updated based on feedback from Ed:

- capitalized loop label names
- added a parameter to validation methods to facilitate caching/lookup of compiled expressions by delegates
Comment 23 Dave Steinberg CLA 2009-09-16 18:50:04 EDT
Hi Kenn,

I have a couple of bigger comments for your consideration. Then, once you've addressed or dismissed them, I have a few little niggles to point out.

1. I notice that ValidationDelegateRegistryReader.readElement() is trying to remove a String from a URI-typed map. So, at the very least, I think that's an error. But it gets me wondering whether it should be a Map<URI, Object> in the first place. I don't see any point at which you're doing any kind of URI computation (e.g. resolve) or component access, so I wonder what the overhead of constructing URIs buys. I suspect a Map<String, Object> would be simpler and more appropriate.

2. It seems to me like we're generating a whole lot of code that doesn't really do much. It was one thing when we're expecting people to replace those methods in order to actually implement the constraint or invariant, but when the idea is to have it all work without touching the validator, it seems a bit much to me.  I wonder if the validate and invariant methods could be generated as a call to a convenience method that invokes the validation delegate?  I'd imagine the only thing that would be tricky to do there would be to create a nice diagnostic in the case where the delegate returns false. After all, a generic message for the non-found and exception cases would probably be suffice.

And that leads me to wonder if the delegate should be creating the diagnostic when it returns false. It does have access to the diagnostic chain, and it might have better knowledge of how or why the expression failed to be satisfied. Perhaps passing it the source and code would be helpful then, too.

I haven't convinced myself of this, but I'm interested in your reaction.

As an alternative, maybe it would be good just to allow the user to control whether to generate the validator in the first place. Then, if they didn't want all the "useless" generated code, they could let the EObjectValidator handle the delegation generically.
Comment 24 Dave Steinberg CLA 2009-09-16 22:03:07 EDT
The more I think about this, the less I like my idea of having the delegate create the diagnostic, and the more I like the idea of having a method that implements the get delegate/delegate validation/create diagnostic logic as necessary. At least then it's only one thing to override.
Comment 25 Dave Steinberg CLA 2009-09-16 22:34:19 EDT
While I'm at it, here are my smaller comments:

Should EcoreUtil have add a setValidationDelegates() for completeness? Tools might want to use this in creating a model.

New public and protected methods should be tagged @since 2.6 in EcoreUtil, EObjectValidator, GenClassifier, and GenOperation.

The warning in Diagnositician.validate() has been reintroduced (the annotation suppressing it has been removed).

How about a method on GenOperation to form the expression variable name used in Class.javajet, line 1331? It strikes me as a really long, error-prone string to assemble in the template (especially with the conditional parameter types), but that's just my opinion.
Comment 26 Kenn Hussey CLA 2009-09-17 11:11:32 EDT
(In reply to comment #23)

Thanks, Dave, for the insightful feedback. I agree with both #1 and #2, and will attach updates patches shortly (after which I'll move on to the "niggles").

> And that leads me to wonder if the delegate should be creating the diagnostic
> when it returns false. It does have access to the diagnostic chain, and it
> might have better knowledge of how or why the expression failed to be
> satisfied. Perhaps passing it the source and code would be helpful then, too.

I considered doing that (as the original patch did), but after looking at the implementation implications and discussing it in detail with Ed, decided against it. It seems better that the validation delegate focus on evaluating the expression (only) and that the validator has control over how failures are reported/handled, including the severity, etc..

> As an alternative, maybe it would be good just to allow the user to control
> whether to generate the validator in the first place. Then, if they didn't want
> all the "useless" generated code, they could let the EObjectValidator handle
> the delegation generically.

I think I prefer the first suggestion, i.e. reduce the amount of generated code.
Comment 27 Kenn Hussey CLA 2009-09-17 11:18:46 EDT
(In reply to comment #24)
> The more I think about this, the less I like my idea of having the delegate
> create the diagnostic, and the more I like the idea of having a method that
> implements the get delegate/delegate validation/create diagnostic logic as
> necessary. At least then it's only one thing to override.

Agreed.
Comment 28 Dave Steinberg CLA 2009-09-17 11:24:37 EDT
Will we need a CQ for Tristan's original contribution?
Comment 29 Kenn Hussey CLA 2009-09-17 11:26:38 EDT
(In reply to comment #25)

> Should EcoreUtil have add a setValidationDelegates() for completeness? Tools
> might want to use this in creating a model.

Yes.

> New public and protected methods should be tagged @since 2.6 in EcoreUtil,
> EObjectValidator, GenClassifier, and GenOperation.

OK.

> The warning in Diagnositician.validate() has been reintroduced (the annotation
> suppressing it has been removed).

Odd. I get a warning in my environment that the suppression is unnecessary. In
any case, I'll add it back for now.

> How about a method on GenOperation to form the expression variable name used in
> Class.javajet, line 1331? It strikes me as a really long, error-prone string to
> assemble in the template (especially with the conditional parameter types), but
> that's just my opinion.

I'd rather leave this until we address bug 255469, since we'll need to add constants for operation metadata anyway.
Comment 30 Kenn Hussey CLA 2009-09-17 11:27:21 EDT
(In reply to comment #28)
> Will we need a CQ for Tristan's original contribution?

I was thinking we wouldn't since I created my changes from scratch.
Comment 31 Kenn Hussey CLA 2009-09-17 11:34:07 EDT
Created attachment 147449 [details]
final (hopefully) patch

Dave, here's an updated patch which attempts to address both your bigger comments and your niggles. Please let me know what you think.
Comment 32 Kenn Hussey CLA 2009-09-17 12:16:54 EDT
Created attachment 147455 [details]
updated sample OCL validation delegate

Here is an updated validation delegate implementation for OCL based on the modified EValidator.ValidationDelegate interface.
Comment 33 Dave Steinberg CLA 2009-09-17 14:41:33 EDT
My final comments on your latest patch...

I hope I didn't lead you astray before: I didn't mean to suggest that you
should stop referring to the string identifying a validation delegate as a URI,
just that the URI class isn't needed to represent it (this would be similar to
how we represent an nsURI everywhere, including EPackage.Registry, as a
string). Is your intent that people will use the more restricted Eclipse-style
IDs, instead of the conventional URI form? My hunch is you still expect people
to use URIs (since they're also used as the source of the other annotations),
so I'd recommend going back to the name uri.

Also, whatever name you do use, I'd suggest using the same (or a related) name
in EcoreUtil.setValidationDelegates(). Right now, the parameter's name is
constraints, but its not a list of constraint names (e.g. "nameIsToto"); it's a
list of validation delegate URIs (e.g. "http://www.eclipse.org/ocl/3.0/OCL").

Finally, could the validate() implementation in EObjectValidator be non-static?
Then it would be overrideable in generated validator classes. It might be nice
to have a single place that the diagnostic creating could be customized for
invariants, too, but I guess that would involve yet another level of
indirection.
Comment 34 Kenn Hussey CLA 2009-09-17 15:46:53 EDT
Created attachment 147479 [details]
final patch

Here is a final set of updates that addresses the remaining concerns. Please confirm that the changes are OK.
Comment 35 Dave Steinberg CLA 2009-09-17 16:41:09 EDT
Did you say final?  ;-)

I noticed just two things:

Class.javajet has a  //$NON-NLS-1$ instead of a <%=genModel.getNonNLS()%> on line 1394.

ValidatorClass.javajet has unimported parameter types on line 115.

Otherwise, it all looks really great.
Comment 36 Kenn Hussey CLA 2009-09-17 16:58:51 EDT
(In reply to comment #35)

Great eyes, Dave! I'll make those small changes and commit the code soon.
Comment 37 Tristan Faure CLA 2009-09-18 02:56:20 EDT
(In reply to comment #28)
> Will we need a CQ for Tristan's original contribution?

Hi I've seen this is not needed according to the discussion but what is CQ ?
Comment 38 Dave Steinberg CLA 2009-09-18 09:01:02 EDT
Hi Tristan,

CQ stands for Contribution Questionnaire, and is part of the Eclipse intellectual property process. It's basically a form that needs to be filled out when a code contribution is accepted from a non-committer into an Eclipse project, to ensure that the contributor has the rights to that code and is willing to release it under the EPL.
Comment 39 Tristan Faure CLA 2009-09-18 09:13:00 EDT
(In reply to comment #38)
> Hi Tristan,
> 
> CQ stands for Contribution Questionnaire, and is part of the Eclipse
> intellectual property process. It's basically a form that needs to be filled
> out when a code contribution is accepted from a non-committer into an Eclipse
> project, to ensure that the contributor has the rights to that code and is
> willing to release it under the EPL.

Thank you !
Comment 40 Kenn Hussey CLA 2009-09-18 14:40:52 EDT
The changes have been committed to CVS.
Comment 41 Ed Willink CLA 2009-09-23 03:18:38 EDT
Which CVS?

This is an EMF core bug, but I see no new EMF core plugin corresponding to your example. I doubt that EMF core should have an OCL dependency.

The attachment seemed to demonstrate a very simple addition that can be merged into the MDT/OCL code. I see no MDT/OCL CVS changes.
Comment 42 Kenn Hussey CLA 2009-09-23 09:05:51 EDT
(In reply to comment #41)
> Which CVS?
> 
> This is an EMF core bug, but I see no new EMF core plugin corresponding to your
> example. I doubt that EMF core should have an OCL dependency.
> 
> The attachment seemed to demonstrate a very simple addition that can be merged
> into the MDT/OCL code. I see no MDT/OCL CVS changes.

No changes have been committed for MDT OCL; this would have to be done against a separate (OCL-specific) bug. Note that, while the example works, a more robust implementation which included caching of previously compiled expressions should probably be provided for OCL.
Comment 43 Dave Steinberg CLA 2009-10-13 13:37:11 EDT
Fix available in HEAD: 2.6.0M2 (S200909211047).