Eclipse Community Forums Forum Search:

Home » Modeling » Epsilon » Feature not found in undefined object(Property not working in ETL without setter or on nested class)
 Flavio CostaMessages: 23Registered: February 2018 Junior Member
Hello,

I am writing a standalone (i.e. Java runtime) transformation where the source is an EMF model generated by Xtext and the target is plain (non-EMF) Java classes.

The source class, loaded with new InMemoryEmfModel(...), is generated as:

public class MySourceImpl extends MinimalEObjectImpl.Container implements MySource {

protected MySourceData data;

public MySourceData getData()
{
return data;
}
}


The target class is loaded with new JavaModel(...) and defined as:

public class MyTarget extends MySuperTarget<Data> {

public class Data extends MySuperTarget.Data {
// stuff here
}
}

public abstract class MySuperTarget<D extends Data> {

private D data;

public D getData() {
return data;
}
}


This is how I am trying to implement the transformation:

rule TransformTest
transform ms : Origin!MySource
to md : Destination!MyTarget {

md.data ::= ms.data;
md.data.id = ms.data.id;
}


When module.execute() runs, it fails with the following exception:

Property 'data' not found in object org.eclipse.test.MyTarget@7aa85607
at (bundleresource://14.fwk154482552/org/eclipse/test/transformation/TransformTest.etl@5:4-5:27)
at (unknown@0:0-0:0)


The message is clear that it does not find the data property in the target. After some debugging, I found out that OperationContributorRegistry.findContributedMethodForEvaluatedParameters(...) looks for a MyTarget.setData() method, but it does not really exist because the class is normally instantiated by Gson, which uses the fields directly, instead of setter methods. Anyway, I added a setData() method for test purposes with Epsilon, and now the error changes to the following:

Called feature id on undefined object
at (bundleresource://14.fwk342597804/org/eclipse/test/transformation/TransformTest.etl@6:4-6:31)
at (unknown@0:0-0:0)


Two questions:

1. It looks like the script is trying to call setId() on the MyTarget.data, which is null. Why is the object null if I just set it to the equivalent() of the source object by using the special assignment on the previous line?
2. I tried to create another specific rule to transform MySourceData to MyTarget.Data, but the editor complains about the dot - so should I assume that nested classes are simply not supported as explicit targets of transformation rules?

Thanks,

Flavio

[Updated on: Tue, 22 May 2018 03:04]

Report message to a moderator

Re: Feature not found in undefined object [message #1787226 is a reply to message #1787199] Tue, 22 May 2018 10:58
 Dimitris KolovosMessages: 1612Registered: July 2009 Location: York, UK Senior Member
Hi Flavio,

Could you please provide a minimal running example [1] I can use to reproduce this locally?

Cheers,
Dimitris

[1] https://www.eclipse.org/epsilon/doc/articles/minimal-examples/
Re: Feature not found in undefined object [message #1787249 is a reply to message #1787226] Tue, 22 May 2018 17:56
 Flavio CostaMessages: 23Registered: February 2018 Junior Member
Hi Dimitris,

I have created a minimal working example for this situation, which gives the same problem as reported above - project (plugin) files attached here.

I am using Epsilon 1.4.0.201611012202 with Java 8.

After importing the project, you can right click on org.eclipse.epsilon.mwe.EtlTest, then Run As -> Java Application. It will run the main method, which gives out the expected error:

Called feature id on undefined object
at (...\org.eclipse.epsilon.mwe\target\classes\org\eclipse\epsilon\mwe\TransformTest.etl@6:4-6:28)
at (unknown@0:0-0:0)

at org.eclipse.epsilon.eol.dom.PropertyCallExpression.execute(PropertyCallExpression.java:46)
at org.eclipse.epsilon.eol.dom.PropertyCallExpression.execute(PropertyCallExpression.java:40)
at org.eclipse.epsilon.eol.dom.AssignmentStatement.execute(AssignmentStatement.java:52)
at org.eclipse.epsilon.eol.execute.ExecutorFactory.executeAST(ExecutorFactory.java:97)
at org.eclipse.epsilon.eol.dom.StatementBlock.execute(StatementBlock.java:49)
at org.eclipse.epsilon.eol.execute.ExecutorFactory.executeAST(ExecutorFactory.java:97)
at org.eclipse.epsilon.eol.dom.ExecutableBlock.executeBlockOrExpressionAst(ExecutableBlock.java:94)
at org.eclipse.epsilon.eol.dom.ExecutableBlock.execute(ExecutableBlock.java:108)
at org.eclipse.epsilon.eol.dom.ExecutableBlock.execute(ExecutableBlock.java:137)
at org.eclipse.epsilon.eol.dom.ExecutableBlock.execute(ExecutableBlock.java:81)
at org.eclipse.epsilon.etl.dom.TransformationRule.executeSuperRulesAndBody(TransformationRule.java:221)
at org.eclipse.epsilon.etl.dom.TransformationRule.transform(TransformationRule.java:175)
at org.eclipse.epsilon.etl.strategy.FastTransformationStrategy.executeTransformations(FastTransformationStrategy.java:165)
at org.eclipse.epsilon.etl.strategy.FastTransformationStrategy.transformModels(FastTransformationStrategy.java:157)
at org.eclipse.epsilon.etl.EtlModule.execute(EtlModule.java:133)
at org.eclipse.epsilon.mwe.EtlTest.main(EtlTest.java:41)


Maybe it's just a problem with my ETL script, maybe it's on the way I load the models, or due to the nested class in the target... not sure, so any support would be appreciated.

Thanks,

Flavio
Re: Feature not found in undefined object [message #1787254 is a reply to message #1787249] Tue, 22 May 2018 19:27
 Dimitris KolovosMessages: 1612Registered: July 2009 Location: York, UK Senior Member
Hi Flavio,

In your transformation, md.data ::= ms.data; is a shorthand for md.data = ms.data.eqivalent(). As there's no rule in your transformation that transforms instances of MySourceData, the right hand side of the assignment returns null, which is the expected behaviour, hence the NPE in the following line. Does this help?

Cheers,
Dimitris
Re: Feature not found in undefined object [message #1787267 is a reply to message #1787254] Wed, 23 May 2018 02:28
 Flavio CostaMessages: 23Registered: February 2018 Junior Member
Hi Dimitris,

It does help, but I get back to point 2 that I mentioned above:

I tried to create another specific rule to transform MySourceData to MyTarget.Data, but the editor complains about the dot - so should I assume that nested classes are simply not supported as explicit targets of transformation rules?

Can I create a rule where the target is a nested class? What would be the syntax for it?

Thanks,

Flavio
Re: Feature not found in undefined object [message #1787290 is a reply to message #1787267] Wed, 23 May 2018 08:27
 Dimitris KolovosMessages: 1612Registered: July 2009 Location: York, UK Senior Member
Hi Flavio,

You should be able to add your nested class to the classes of your JavaModel i.e. replace

classes = Collections.singletonList(MyTarget.class);

with

classes = Arrays.asList(MyTarget.class, MyTarget.Data.class);

and then refer to your nested class as Destination!Data in the transformation.

Cheers,
Dimitris
Re: Feature not found in undefined object [message #1787339 is a reply to message #1787290] Wed, 23 May 2018 20:06
 Flavio CostaMessages: 23Registered: February 2018 Junior Member
Dimitris,

It seems to work, thanks for the help! I would prefer to be able to reference it in the transformation as Destination!MyTarget.Data instead, as it would avoid ambiguity in case we have MyTarget.Data and MyOtherTarget.Data, but I hope that won't be an issue with my current model. I also had to make the nested class static, else Epsilon was not able to instantiate it, but that would also be a minor functional improvement.

My main concern now is that Epsilon forces me to provide setters for any properties used in the transformation. My target model is basically read-only, as it's derived from the editable source model, so I don't need and don't want to add setters everywhere so the person working on the client application using the model does not get confused by those.

I see that JavaPropertySetter looks specifically for a setX() method, so the transformation does not work without these. As I mentioned on my initial message above, Gson uses reflection to set private fields even if a setter is not exposed, and I would like to have that feature on Epsilon as well. Is there a way for me to write my own AbstractPropertySetter to implement this feature, or can I add a OperationContributor to do that? Else, should I create a new enhancement request in Bugzilla?

Thanks,

Flavio

[Updated on: Wed, 23 May 2018 20:46]

Report message to a moderator

Re: Feature not found in undefined object [message #1787344 is a reply to message #1787339] Wed, 23 May 2018 22:00
 Dimitris KolovosMessages: 1612Registered: July 2009 Location: York, UK Senior Member
Hi Flavio,

I think that the best way forward is to extend JavaModel and override its getPropertySetter() method to provide your own implementation of AbstractPropertySetter. Please let me know if you face any problems doing this.

Cheers,
Dimitris
Re: Feature not found in undefined object [message #1787453 is a reply to message #1787344] Fri, 25 May 2018 15:34
 Flavio CostaMessages: 23Registered: February 2018 Junior Member
Hi Dimitris,

It was actually fairly easy to implement it! A JavaFieldPropertySetter implementation extending JavaPropertySetter did the trick, with 82 lines of code, and it seems to work very well in my initial testing (it would be even less if org.eclipse.epsilon.eol.execute.introspection.java.ObjectField defined a constructor to set its properties.

Since I was already extending JavaModel, I also added some reflection functionality to automatically identify the classes and object instances to be added to the model, starting from its root element. Therefore, this code:

MySource sourceObj = new MySource("My data id");
Collection<Object> objects = Arrays.asList(sourceObj, sourceObj.getData());
Collection<Class<?>> classes = Arrays.asList(MySource.class, MySourceData.class);
JavaModel javaModel = new JavaModel("Origin", sourceObj, sourceObj.getData());


Can be replaced by one single line of code:
JavaModel javaModel = new ReflectiveJavaModel("Origin", new MySource("My data id"));


The same can be done for the target model, I can now load it with one line and reflection does its magic internally:
JavaModel javaModel = new ReflectiveJavaModel("Destination", Collections.singleton(MyTarget.class));


The other two changes I'd like to see, namely the support for non-static inner classes and explicitly specifying the name of the enclosing class in the ETL rule (e.g. Destination!MyTarget$Data), would apparently be more complicated. It might require changes to FastTransformationStrategy, EolModelElementType, and probably other places. I am happy with the results, I am attaching the final code here for your appreciation. It would be good if at least JavaFieldPropertySetter is added to Epsilon's code base, since it's generic enough and could be useful for anyone using read-only target Java models. Thanks for all the assistance, Flavio Re: Feature not found in undefined object [message #1787470 is a reply to message #1787453] Fri, 25 May 2018 20:34  Flavio CostaMessages: 23Registered: February 2018 Junior Member Actually, I was celebrating it too early: when I started testing with my real model data, I realized I do have multiple inner classes named "Data", so the system was picking up the wrong one. After doing some tests, I found a clean way to specify an explicit enclosing class with the existing ETL syntax: rule TranformData transform msd : Origin!MySourceData to mtd : Destination!MyTarget#Data  In order for that to work, I just needed to override JavaModel.classForName(String) as follows:  public Class<?> classForName(String name) { for (Class<?> c : classes) { // the first condition gets "Enclosing#Nested" from "my.package.Enclosing$Nested"
if (c.getName().substring(c.getName().lastIndexOf('.') + 1).replace('$', '#').equals(name) || c.getCanonicalName().replaceAll("::", ".").equals(name)) { return c; } } return null; }  Updated code attached here. Best regards, Flavio Re: Feature not found in undefined object [message #1787486 is a reply to message #1787470] Sat, 26 May 2018 09:51  Dimitris KolovosMessages: 1612Registered: July 2009 Location: York, UK Senior Member Hi Flavio, Alternatively, you should be able to use backticks to escape the actual fully qualified class name without needing to change classForName e.g. my.package.Enclosing$Nested.all.println();


Cheers,
Dimitris
Re: Feature not found in undefined object [message #1787544 is a reply to message #1787486] Mon, 28 May 2018 14:42
 Flavio CostaMessages: 23Registered: February 2018 Junior Member
Hi Dimitris,

I wasn't aware of the backticks syntax (it is only seems to be mentioned in a footnote on 149 of the Epsilon Book).
I will keep the classForName implementation as it looks cleaner on the ETL script, but it is good to have that as an option.

Thanks,

Flavio
 Previous Topic: Executing rules multiple multiple times with a single match Next Topic: Re: Model instance transformation framework
Goto Forum:

Current Time: Fri Nov 16 07:27:08 GMT 2018