I'm using Xtext and GMF and therefor I would like to acces the active GMF compartment node from my xtext project. Because of this, I thought that the selection service of eclipse would be a good way to solve this.
I tried to implement the selection service, but it didn't seems to acces the GMF related node. Rather that I get an xtext class object, because I implemented an IXtextAwareEditPart in GMF. Is there a way to acces the active GMF node?
This code hasn't work for me so far:
ISelectionService selectionService = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getSelectionService();
ISelection selection = selectionService.getSelection();
if (selection instanceof IStructuredSelection) {
IStructuredSelection iStructuredSelection = (IStructuredSelection) selection;
if (iStructuredSelection.getFirstElement() instanceof PartImpl) {
PartImpl partImpl = (PartImpl) iStructuredSelection; // <------ The xtext class?
}
}
Instead of PartImpl, the object returned from iStructuredSelection.getFirstElement() will be a GMF Edit Part (e.g. PartEditPart). Try something like the following:
PartEditPart editPart = (PartEditPart) iStructuredSelection.getFirstElement();
ShapeImpl shapeImpl = (ShapeImpl) editPart.getModel();
PartImpl partImpl = (PartImpl) shapeImpl.getElement();
Related
In my Eclipse e4 RCP application, I'm using annotations to get notified when a Part is activated, using the approach proposed here :
http://www.vogella.com/tutorials/Eclipse4ModelEvents/article.html
#Inject
#Optional
public void subscribeTopicPartActivation(#UIEventTopic(UIEvents.UILifeCycle.ACTIVATE) Event event) {
Object element = event.getProperty(EventTags.ELEMENT);
if (!(element instanceof MPart)) {
return;
}
MPart part = (MPart) element;
System.out.println("Part activated: " + part.getLabel());
}
It works fine, but I've noticed that the activate event is triggered more than once for the same part in cases where we would expect a single activate event (i.e for exemple simple switch to the part...). The event message sent seems to be exactly the same (same target, same topic). Am I missing something ? Is it a normal behavior for the event framework ?
Yes, it appears to be normal. There is a difference in the 'tags' value of the part between the two events. In the second event 'active' has been added to the tags.
This will show the difference:
System.out.println("part " + part.getElementId() + " tags " +
part.getTags().stream().collect(Collectors.joining(", ")));
You can also use the EPartService addPartListener method to listen for just part changes.
I have created a plugin with a JFace Tree similar to this tutorial: tutorial
And I also have defined a menu entry with a command. The handler of the command should change the Model of the tree and update it. But I have no idea how I can get the right tree view (TreeViewer / ViewPart) and how I can access the model out of the command handler. Can someone help?
Solution 1 - Use your own API
You can inject die active MPart into your Handler and then use part.getObject() to get your contribution class. you can then access your own API to get the Model and the TreeViewer.
#Execute
public void executeHandler(#Named(IServiceConstants.ACTIVE_PART) MPart part) {
MyPart myPart = (MyPart)part.getObject();
MyModel myModel = myPart.getMyModel();
TreeViewer viewer = myPart.getMyTreeViewer();
}
Solution 2 - Use DI
When you initialize your TreeViewer, put it into the IEclipseContext of the Part. You can then have the TreeViewer injected into your Handler.
public class MyPart {
#PostConstruct
void postConstruct(Composite parent, IEclipseContext context) {
TreeViewer viewer = new TreeViewer(parent);
context.set(TreeViewer.class, viewer);
}
}
public class MyHandler {
#Execute
public void executeHandler(TreeViewer viewer) {
}
}
You can do the same thing with the model of course. If there is only one instance of the model (if MyModel is a singleton) you can put it in the context of the application rather that that of the part. A good place to do this would be in an AddOn (or in the LifeCycleHandler).
Alternativly you can define the MyModel class to be #Creatable #Singleton, so it will be created by DI on demand. This is handy, when all the stuff your MyModel needs is in the context already.
My favorite Solution in situations like this is to create a ModelService as an OSGi-Service. OSGi Services are always available through the IEclipseContext.
For more info on DI check out this tutorial by Lars Vogel (Chapters 18, 31, 32):
http://www.vogella.com/articles/EclipseRCP/article.html#dependencyinjectione4
http://www.vogella.com/articles/EclipseRCP/article.html#extentcontext
http://www.vogella.com/articles/EclipseRCP/article.html#ownobjects
When attempting to edit a new (proxy) entity using RequestFactoryEditorDriver.edit() I am getting the following error: "Exception caught: Attempting to edit an EntityProxy previously edited by another RequestContext". I am fairly sure that this is a result of my misunderstanding of the request factory/editor framework architecture. Here is the editor code that I think pertains to this problem:
public class OrgMaintenanceWidget extends Composite implements Editor<IOrgProxy> {
... other fields ...
private IOrgEditorDriver _orgEditorDriver;
interface IOrgEditorDriver extends RequestFactoryEditorDriver<IOrgProxy, OrgMaintenanceWidget> {}
public OrgMaintenanceWidget(final IClientFactory clientFactory) {
... widget initialization ...
_orgEditorDriver = GWT.create(IOrgEditorDriver.class);
_orgEditorDriver.initialize(_clientFactory.getRequestFactory().getEventBus(),
_clientFactory.getRequestFactory(), this);
}
#UiHandler("newButton")
public void onNewButtonClick(final ClickEvent clickEvent) {
_org = _clientFactory.getCache().getOrgCache().newOrg();
_orgEditorDriver.edit(_org, _clientFactory.getRequestFactory().orgRequestContext());
}
...
}
It's the "_orgEditorDriver.edit()" line that causes the exception. The "newOrg()" method is:
public IOrgProxy newOrg() {
return _clientFactory.getRequestFactory().orgRequestContext().create(IOrgProxy.class);
}
The RequestFactory is simply:
public interface IRequestFactory extends RequestFactory {
IOrgRequestContext orgRequestContext();
}
I am sure that I'm missing something fundamental about editing a new entity. When I edit an existing entity everything is fine ... the UI components are populated automatically, and flushing the editor back to the entity works very nicely. Here's the code that initiates editing for an existing entity:
#UiHandler("newButton")
public void onNewButtonClick(final ClickEvent clickEvent) {
_org = _clientFactory.getCache().getOrgCache().newOrg();
_orgEditorDriver.edit(_org, _clientFactory.getRequestFactory().orgRequestContext());
}
Any help would be greatly appreciated, and I'll try to publish any lessons learned.
This code:
_clientFactory.getRequestFactory().orgRequestContext().create(IOrgProxy.class);
Means:
Create new orgRequestContext()
Create new IOrgProxy using this context
Edit new IOrgProxy using this context, because as docs say: "Returns a new mutable proxy that this request can carry to the server, perhaps to be persisted.", it means that the proxy is edited by this request.
This code:
_orgEditorDriver.edit(_org, _clientFactory.getRequestFactory().orgRequestContext());
Means:
Again, create new orgRequestContext() (because each invocation of getRequestFactory().orgRequestContext() provides new instance of orgRequestContext()
"Start driving the Editor and its sub-editors with data." as docs say. But as a part of it, use passed orgRequestContext() to edit passed IOrgProxy instance, so that the proxy is editable.
Because the proxy was already edited while created by other RequestContext, you get the exception, because there is fundamental rule in RequestFactory, that proxy can be edited only by one RequestContext.
See also this thread.
I think you can't create an object with one RequestContext and then edit it with another one.
So you can solve this in two ways:
Persist the created object with the RequestContext you used when you created the object. The save method should return the persisted object and this persisted object can be passed to the editor with a fresh new RequestContext
Somewhere save the RequestContext you used for creating the object and pass it to the edit function of your Driver
Solution two could look something like this:
#UiHandler("newButton")
public void onNewButtonClick(final ClickEvent clickEvent) {
IOrgRequestContext ctx = _clientFactory.getRequestFactory().orgRequestContext();
_org = ctx.create(IOrgProxy.class);
_orgEditorDriver.edit(_org,ctx );
}
I use a transformation engine to create an Ecore meta-model at runtime and I wonder how we can register that meta-model with EMF so that it can recognize the meta-model?
If you have the code generated by your metamodel:
resourceSet.getPackageRegistry()
.put(org.eclipse.emf.codegen.ecore.genmodel.GenModelPackage.eINSTANCE.getNsURI()
, org.eclipse.emf.codegen.ecore.genmodel.GenModelPackage.eINSTANCE);
(here for the "genmodel" metamodel)
If you only have the .ecore file:
// register globally the Ecore Resource Factory to the ".ecore" extension
// weird that we need to do this, but well...
Resource.Factory.Registry.INSTANCE.getExtensionToFactoryMap().put(
"ecore", new EcoreResourceFactoryImpl());
ResourceSet rs = new ResourceSetImpl();
// enable extended metadata
final ExtendedMetaData extendedMetaData = new BasicExtendedMetaData(rs.getPackageRegistry());
rs.getLoadOptions().put(XMLResource.OPTION_EXTENDED_META_DATA,
extendedMetaData);
Resource r = rs.getResource(uriOfYourModel, true);
EObject eObject = r.getContents().get(0);
if (eObject instanceof EPackage) {
EPackage p = (EPackage)eObject;
rs.getPackageRegistry().put(p.getNsURI(), p);
}
You can find a bit more about this code here with the method named registerEcorePackages(), used to register .ecore file in the workspace (with their workspace fullpath) in our custom package registry. If you want to register your metamodel in EMF global package registry, replace resourceSet.getPackageRegistry() by EPackage.Registry.INSTANCE.
I had to modify the code from #sbegaudeau a bit for it to work:
Replace
rs.getPackageRegistry().put(p.getNsURI(), p);
with
EPackage.Registry.INSTANCE.put(p.getNsURI(), p);
Also, somehow I cannot register the .ecore type. Had to use "*": Resource.Factory.Registry.INSTANCE.
getExtensionToFactoryMap().put("*", new EcoreResourceFactoryImpl());
Friends,
Now we are developing a eclipse plugin, it contains a action to generated a service interface and it's impl stub.
Now the interface is generated, I want to use eclipse JDT to create a java class which implements sepecified interface, but don't know how.
The info we have:
the interface name, the impl class name, the packagename, the java project contains them.
Thanks in advance for your kindly help.
A quick scan of how the new class wizard does it, it seems that there is no public easy to use API for this. You can have a look at org.eclipse.jdt.ui.wizards.NewTypeWizardPage.createType(IProgressMonitor) method to see how JDT itself creates new classes.
It should be possible to extend the org.eclipse.jdt.ui.wizards.NewTypeWizardPage, so you can leverage the createType() method.
Probably the minimal steps you would have to do is simply generate source content into the correctly placed IFile. ex:
public Object execute(ExecutionEvent event) throws ExecutionException {
final String PACKAGE_PATH = "z.ex/src/z/ex/go";
final String CONTENT = "package z.ex.go;\n"
+ "public class RunAway {\npublic static void main(String[] args) {\n"
+ "System.out.println(\"Run Away\");\n}\n}\n";
final IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
final IResource packageResource = root.findMember(PACKAGE_PATH);
if (packageResource instanceof IFolder) {
IFolder packageFolder = (IFolder) packageResource;
final IFile file = packageFolder.getFile("RunAway.java");
try {
if (!file.exists()) {
file.create(new ByteArrayInputStream(CONTENT.getBytes()),
true, new NullProgressMonitor());
} else {
file.setContents(
new ByteArrayInputStream(CONTENT.getBytes()),
IFile.FORCE | IFile.KEEP_HISTORY,
new NullProgressMonitor());
}
} catch (CoreException e) {
e.printStackTrace();
}
}
return null;
}
See AbstractNewClassWizard for a smaller example that is similar to NewTypeWizardPage and uses some of the JDT APIs.
You can use the new class wizard to create classes.
This will prompt the user for the class name, et cetera. You can initialize the values of the wizard page. Below I am setting the source folder only (and tell the wizard that it cannot be changed, thus the second false parameter). You might want to set the interface and possible the package as well.
OpenNewClassWizardAction wizard = new OpenNewClassWizardAction();
wizard.setOpenEditorOnFinish(false);
NewClassWizardPage page = new NewClassWizardPage();
page.setPackageFragmentRoot(sourceFolder, false);
wizard.setConfiguredWizardPage(page);
wizard.run();
return (IType) wizard.getCreatedElement();
Hope that helps!
Create the entire java file using the JDT - AST. First create the AST and then write it to a java file. It might look as hefty work, but its the best one. You will have complete control.