Eclipse plugin development - e4 part with input: instantiate input - eclipse

Eclipse 4 does not have any notion of an editor, only parts. So how do we receive the file for our "editor" parts? I'm talking about an e4 equivalent of the IEditorPart.init method, and the IEditorInput it receives.
I've stumbled upon this example by Tom Schindl. He is able to inject the input in the part constructor. For the input object to be available for injection, it has to be instantiated in the IEclipseContext somehow. This article refers to Schindl's example, and leads me to consider the following approach: intercept "Open file" commands (if they exist) from the Eclipse platform, create an input instance for the "editor" part and put it in the context (IEcpliseContext.set(class, value)), and open the part with the EPartService.
So the question is: is this the right approach to creating an e4 "editor"? And is it possible to implement?

It is easier to put the input data in the part's transient data since it is tricky to get the data injected at the right point.
For something that behaves like an editor you will probably use a Part Descriptor so that you can create multiple parts from the single descriptor.
You would create the part using something like:
#Inject
EPartService partService;
// Create from part descriptor
MPart part = partService.createPart("part descriptor id");
// Set input in transient data
part.getTransientData().put("inputKey", inputData);
// Add to part stack
MPartStack partStack = ... stack you want to use
partStack.getChildren().add(part);
// Show
partService.showPart(part, PartState.ACTIVATE);
In your part code you get the transient data by injecting the MPart. For example in the constructur:
#Inject
public MyClass(MPart myPart)
{
Object input = part.getTransientData().get("inputKey");
....
}

Related

EMF NotSerializableException

I build application in Java using EMF.
I use Java8 JDK.
All I need is persist instance of my model's class.
I run the code in debug mode on WildFly 10 server, remotely from Eclipse.
private String getXml(Audit audit) throws NeoflexException {
XMLResource res = new XMLResourceImpl();
res.getContents().add(audit);
StringWriter sw = new StringWriter();
try {
res.save(sw, null);
} catch (IOException e) {
throw new NeoflexException(e);
}
return sw.toString();
}
On serialization I get a NotSerializableException.
I see that generated Audit class isn't marked as Serializable.
I can not edit generated code, how ever, I also don't have option to mark this class in diagram as serializable.
As described here: https://www.eclipse.org/forums/index.php/t/261475/
I need to create an interface and derive it from serializable, but I don't have such option. See screenshot attached.
In general, EMF serializability is not bound to the ISerializable interface, but a containment hierarchy formed between the model elements. Furthermore, you should not make EMF interfaces extend the ISerializable inferface at all, as it is misleading (EMF model objects are not supposed to be serializable using the basic Java serialization).
Basically, the instances of your classes should for a containment tree: one object should be the root of the tree, and all other instances should be contained in it. Then you could save this entire containment hierarchy into a file by adding the model root into the resource.
Without knowing the concrete error message in the exception, my first tip would be to check whether the Audit element references some other model element, because if that element is referenced in a non-containment relation, than the serialization will fail.
To set a relation containment, edit your metamodel (Audit.ecore file) and set the property called containment true for the reference. However, you have to make sure that the containment subtree can be set up correctly: there is only a single model root element; all other elements can be reached by exactly one path of containment references from the model root. If an element is referenced by a cross-reference, it must also be included in the containment hierarchy in order for the EMF serialization (Resource.save) to work.
A further issue I see that you create the EMF Resource file manually, without any URI (that determines where you want to save your model) and without any resourceset (that is used to split your models into multiple files/resources with separate containment hierarchies).
In other words, you should create a ResourceSet instance, and use that to create your model Resource instances.
For more details, I suggest to check the basic and serialization specific tutorial at vogella.com.

Eclipse 4 RCP - how to change what is showed in specific area?

I have splitted my application into two main areas.
Part(A)
PartStashContainer(B)
The content of A should be set based on what user wants.
So basically i can have 1..N classes which could be used in Class URI of Part in application model.
I don't know if i should replace the whole Part(A) with new dynamically created Part(C) which has content i want, or i should somehow to modify the existing Part (call setContributionURI, or setObject methods on Part object?).
It does make more sense to me to modify the existing Part, because it is defined in Application model and therefore already describing the location where the content should be.
Possible solutions:
Modify the Part object so it "reload" its content based on new setup (But how? Can setContributionURI or setObject methods help?)
Remove the old Part and add dynamically on same place in Application model the new Part (using EModelService and EPartService).
other solution??
If you want to reuse the Part then do something like:
MPart part = find or inject your part
MyClass myClass = (MyClass)part.getObject();
... call a method of MyClass to change the contents
MyClass is the class you specify for the object in the application model. You should add a method to that to let you change the contents.
Don't try to call setObject, this is really only for use by Eclipse. I don't think setContributionURI would do anything after the part is created (but I am not sure).
If you want to use different classes for the different data then you really should use different Parts.

Accessing a composite using E4

In my RCP application, I have a 'Part' which has 2 composites(leftComposite & rightComposite which are tied to parentComposite). Once the user clicks on the menu, i'm trying to access one of the composites(through handler/command) and make it invisible.
How do I access the composite, which belong to a specific Part?
You can get the current MPart by injecting it in the handler. From the MPart you get you class for the part using MPart.getObject(). To actually deal with the Composites in the part you will have to write code in your class to remember the composites and do what you want.
So the handler would look something like:
#Execute
void execute(#Named(IServiceConstants.ACTIVE_PART) MPart part)
{
MyClass myClass = (MyClass)part.getObject();
// Call code in `MyClass` to do what you want
}

In GWT, How to use custom widget tag in an .ui.xml file with and without parameters for the tag in the same file

I am creating a custom widget, say "CustomWid" in UiBinder.
And in CustomWid.java file I am writing two constructors
one with zero args like
CustomWid(){....}
another with some args like
CustomWid(String a,String b){......}
So,Now I am using my custom widget in another .ui.xml file,in that .ui.xml file
it is working fine when we give
<my:CustomWid/> alone,
and also fine when we give like
<my:CustomWid a="srt1" b="str2"/> alone
But "MY PROBLEM" is whenever I am trying to give both the tags in the one .ui.xml as
<my:CustomWid/>
<my:CustomWid a="str1" b="str2"/>
Now it is throwing error when i am using both types of tags in a single .ui.xml
I mean How to use my custom widget tag like a prdefined tag?
I am using #uiConstructor, but it showing error
Please developers... I need answer as early as possible
UiBinder will only ever use a single constructor for a given widget: either its zero-arg constructor, or a #UiConstructor (I'm surprised that you say it works when using either one or the other call but not both: one should fail in every case, and one should succeed in every case; if you haven't annotated a constructor with #UiConstructor, then <my:CustomWid/> should always work and <my:CustomWid a="str1" b="str2"/> should always fail)
There are two solutions here:
use setters for the a and b attributes (void setA(String a) and void setB(String b))), and possibly check later (say, in onLoad or onAttach) that you have either none or both of A and B, but not one without the other (if that's your rule).
use #UiField(provided = true) when you need to use the other constructor (if you choose to have UiBinder use the zero-arg constructor –i.e. no #UiConstructor–, then that means you'll have to move the a="str1" b="str2" from the XML to the Java code: #UiField(provided = true) CustomWid myCustomWid = new CustomWid("str1", "str2")).
The first option has my preference.
It Will not show any errors...'
#UiConstructor
public Component(String displayText,String heading)
{
initWidget(uiBinder.createAndBindUi(this));
this.displayText.setText(displayText);
this.heading.setText(heading);
}`
now use another constructor with default parameters also it will work
public Component()
{
initWidget(uiBinder.createAndBindUi(this));
}
now if you add with xml parameters component and without parameters also works in the same page.

Meshing Acceleo with Xtext

I am in the middle of an Acceleo Transformation aimed at producing code (i.e. Java) from an input UML model.
Some elements of this UML model (i.e. Activities Opaque actions) contain some text which is conform to an Xtext grammar and I'd like to get the equivalent AST Ecore representation in the Acceleo transformation.
To this end I have developed a Java class with a method which takes as input a string, containing the DSL excerpt, and produces an Ecore model conform to it (see http://www.eclipse.org/forums/index.php/m/901947/#msg_901947 for further details). I have tested it in a separate Java application and it seems it works properly.
I have therefore written a simple Acceleo module (i.e. getDSLModel) wrapping that java class and enabling me to get the Ecore model from the DSL textual representation.
Suppose my DSL (and the equivalent Ecore) consist of a root element named DSLModel containing a (0..*) number of DSLStatements (this is a simplification).
When in Acceleo I invoke the wrapper from a string, containing a correct DSL script, I have noticed it correctly returns a ModelImpl.
['statement1;statement2'.getDSLModel()/]
so the Java service and the Xtext parse is working.
However if I try to get the model statements, i.e.:
['statement1;statement2'.getDSLModel().statements/]
it returns an "invalid" string. So I can't use it in a for loop
I have therefore tried to call the eAllContents() OCL service from the model instance i.e.:
['statement1;statement2'.getDSLModel().eAllContents()/]
and it actually returns the list of statements. I do not understand why the features of the Ecore entities returned from the Xtext parser are not working properly.
Here is the Java service which turns a string into a instance of my DSL model (Ecore AST). I have tested it with an independent Java application and it works fine!
public class GetDSLModel {
public DSLModel getDSLModel(String expression){
DSLStandaloneSetupGenerated dslsas = new DSLStandaloneSetupGenerated();
Injector injector = dslsas.createInjectorAndDoEMFRegistration();
XtextResourceSet resourceSet = injector.getInstance(XtextResourceSet.class);
resourceSet.addLoadOption(XtextResource.OPTION_RESOLVE_ALL, Boolean.TRUE);
Resource resource = resourceSet.createResource(URI.createURI("dummy:/example.dsl"));
InputStream in = new ByteArrayInputStream(expression.getBytes());
try {
resource.load(in, resourceSet.getLoadOptions());
DSLModel model = (DSLModel) resource.getContents().get(0);
return model;
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
}
Now I need the AST in the main Acceleo (UML2Text) transformation thus here is the Acceleo wrapper
[query public getDSLModel(str:String): DSLModel = (invoke('sfg.baleno.src.mloaders.GetDSLModel','getDSLModel(java.lang.String)',Sequence{str})).oclAsType(DSLModel)/]
here is what I get if run it
input: ['statement1;statement2'.getDSLModel()/]
output: mypackage.dsl.impl.DSLModelImpl#a1c7a
input: ['statement1;statement2'.getDSLModel().statements/] (Syntactically VALID)
output: invalid
input: ['statement1;statement2'.getDSLModel().eAllContents()/]
output: mypackage.dsl.impl.DSLStatement#e53202 (......
UPDATE
To the Java Class of the main Acceleo module I have added the following lines
#Override
public void initialize(EObject element, File folder, java.util.List<? extends Object> arguments) throws IOException { preInitialize();
super.initialize(element, folder, arguments);
}
#Override
public void initialize(URI modelURI, File folder, java.util.List<?> arguments) throws IOException {
preInitialize();
super.initialize(modelURI, folder, arguments);
}
protected void preInitialize() {
DSLStandaloneSetup.doSetup();
}
and
#Override
public void registerPackages(ResourceSet resourceSet) {
super.registerPackages(resourceSet);
if (!isInWorkspace(org.eclipse.uml2.uml.UMLPackage.class)) {
resourceSet.getPackageRegistry().put(org.eclipse.uml2.uml.UMLPackage.eINSTANCE.getNsURI(), org.eclipse.uml2.uml.UMLPackage.eINSTANCE);
}
if (!isInWorkspace(mypackages.DSLPackage.class)) {
resourceSet.getPackageRegistry().put(mypackages.DSLPackage.eINSTANCE.getNsURI(), mypackages.DSLPackage.eINSTANCE);
}
EcoreUtil.resolveAll(resourceSet);
}
but it still behaves the same.
UPDATE
At this link you temporary find a zipped file of an example EMF workspace containing an Acceleo and a XText project reproducing the issue. The weird thing is that if you run it as a Java application it works but if you run it as an Acceleo application it does not...
https://docs.google.com/open?id=0B2_ovo8IiZaZaXdNdFdPMTI4Yjg
In the top left corner you should find a File menu from which you can download the zip file.
I am unfortunately not that familiar with Xtext ... but I believe your problem lies in the way you load your dsl from within the java service : you are generating in the context of a running Eclipse ("acceleo application"), yet you load your model as if you were in standalone : new DSLStandaloneSetup, createInjector...
I believe that this way of loading your model gives you two instances of the Xtext metamodels and factories, making OCL fails to retrieve the feature "statements" when you try to obtain its values.
One possible way I can think of to bypass this would be to change your service to take an EObject of any sort as parameter and use its resource set to load your dsl (thus using the resource set you've initialized in the launcher, the one Acceleo uses, instead of your own) :
public DSLModel getDSLModel(String expression, EObject eObj){
ResourceSet rSet = eObj.eResource().getResourceSet();
[...]
}
With that, you should not need the second Xtext initialization you do from the service ... If it does not work tough, I don't really have a solution to propose other than to go to the Xtext forums and ask there how to make an application that can load a DSL and work both in standalone and in a pluginized environment.