Usage of IAdapterFactory with popupMenus in eclipse rcp - eclipse-plugin

My requirement was to add a new menu entry to “Compare with” present in one of the 3rd party views.
As this was using “org.eclipse.ui.popupMenus” to add a menu entry to the above contribution. Even I was forced to use the same extension point even though its deprecated.
I was able to add a menu entry to the contribution with the below code
<extension point="org.eclipse.ui.popupMenus">
<objectContribution
adaptable="true"
id="test.id"
objectClass="local.change">
<action
class="compare.commparetool"
enablesFor="1"
id="id"
label="Compare "
menubarPath="compareWith/group1">
</action>
<visibility>
<objectState
name="local.change"
value=".txt">
</objectState>
</visibility>
</extension>
The above configuration is working fine.
Next requirement was to add property tester to hide the menu entries whenever a file selected is other than .txt file.
As we cannot add property tester to object contribution, I have used IAdapterfactory. Below code is not working.
Observation:
I have added many menu entries (“org.eclipse.ui.menus”) in different views in “Compare with” which is not related to this.
But if user clicks on any of these commands, and then try the view in question, it is working as expected as expected.
Below is the code. Am I missing anything. Do I need to register the adapters in some other place also??
<extension point="org.eclipse.core.runtime.adapters">
<factory
adaptableType="local.change"
class="LocalChangeAdapterFactory">
<adapter
type="org.eclipse.ui.IActionFilter">
</adapter>
</factory>
</extension>
public class LocalChangeAdapterFactory implements IAdapterFactory
{
#SuppressWarnings("unchecked")
#Override
public Object getAdapter(final Object adaptableObject, final Class adapterType)
{
if (adapterType == IActionFilter.class)
{
return LocalChangeActionFilter.getInstance();
}
return null;
}
#SuppressWarnings("unchecked")
#Override
public Class[] getAdapterList()
{
return new Class[] { LocalChangeActionFilter.class };
}
}
public class LocalChangeActionFilter implements IActionFilter
{
private static LocalChangeActionFilter INSTANCE = new LocalChangeActionFilter();
private LocalChangeActionFilter()
{
}
#Override
public boolean testAttribute(final Object target, final String name, final String value)
{
String fileName = "";
if(target.getId==1){
return true;
}else{
return false;
}
public static LocalChangeActionFilter getInstance()
{
return INSTANCE;
}
}

The adaptableType attribute of the adapter factory should specify the type of the existing object that you want to adapt to an IActionFilter. So this is probably a resource of file:
adaptableType="org.eclipse.core.resources.IResource">
The getAdapter method of the IActionFactory should return a class matching the adapter attribute, not your implementing class:
public Class[] getAdapterList()
{
return new Class[] { IActionFilter.class };
}
Your testAttribute method if the action filter must test the name parameter matches the value in the objectState:
#Override
public boolean testAttribute(final Object target, final String name, final String value)
{
if (name.equals("local.change"))
{
.... do test
return true;
}
return false;
}

Related

Eclipse Plugin: How can I tell the plugin to open a new Editor every time instead of switching the focus to an existing Editor?

In my Plugin there is an action to open an Editor (extends EditorPart). When I try to open it a second time, its init method isn't called. Instead the focus is shifted to the editor that is already open.
The Editor is associated with a filetype. Here is the excerpt from the plugin.xml:
<extension point="org.eclipse.ui.editors">
<editor
class="de.blub.tool.ide.editors.GRASPEditor"
default="true"
extensions="grasp"
filenames="*.grasp"
icon="icons/newGraspFile.png"
id="de.blub.tool.ide.editors.GRASPEditor"
name="GRASP File Editor">
</editor>
</extension>
I have an Action to open a new Editor. When I try to click that Action twice it reuses the first Editor. I also tried to use an EditorMatcher that implements IEditorMatchingStrategy and always returns false in its matches() method. Even that doesn't change the behavior.
This seems to be a desired/default behavior in eclipse. How can I change that so that the user can initialize a new Editor each time?
Eclipse looks for the equals method of the IEditorInput instance. The Editor somewhere in its code (in my case in the doSave method) uses a setInput method like this:
#Override
public void init(IEditorSite site, IEditorInput input) throws PartInitException {
// Initialize the editor input
this.input = new MyInputClass(resource);
...
}
#Override
public void doSave(IProgressMonitor monitor) {
...
setInput(input);
}
MyInputClass is the class that extends IEditorInput. The logic for eclipse to reuse an Editor or create a new one is in its equals method. The following example checks the path of an IResource field:
public class MyInputClass implements IEditorInput {
private IResource resource;
public MyInputClass(IResource resource) {
this.resource = resource;
}
public IResource getResource() {
return resource;
}
public void setResource(IResource resource) {
this.resource = resource;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj instanceof MyEditorClass) {
MyEditorClass other = (MyEditorClass) obj;
if (getResource().getFullPath().equals(other.getResource().getFullPath())) {
return true;
}
}
return false;
}
}
Of course one can define another logic inside the equals method. Make sure to not create a chaos, which is very well possible, as greg-449 pointed out in a comment.

Creating a custom run configuration using IntelliJ SDK and adding Build task in Before launch section

I'm following the tutorial in Run Configuration section of IntelliJ IDEA SDK.
Following the tutorial, I will get a new configuration panel with an empty "Before launch" section. That section is added by default.
I would like to specify some targets by default, i.e. at least the Build target as done in several plugins (see next picture)
I'm trying to understand how, but I cannot find any example nor documentation on this.
How can I add default build task?
Your run configuration (DemoRunConfiguration in the example) should implement RunProfileWithCompileBeforeLaunchOption. This interface doesn't provide any methods to implement, so this is a kind of mark. build task will be added automatically, no additional steps are required.
How can I add my own task to before launch section?
Your plugin.xml should contain a line with stepsBeforeRunProvider
<stepsBeforeRunProvider implementation="com.MyBeforeRunProvider" id="myBeforeRun"/>
For that you should create "before run provider" with "before run task".
public class BeforeRunProvider extends BeforeRunTaskProvider<MyBeforeRunTask> {
#Override
public Key<MyBeforeRunTask> getId() {
return Key.create("ThisIsId");
}
#Override
public String getName() {
return "Nice name";
}
#Override
public String getDescription(MyBeforeRunTask task) {
return "Description";
}
#Nullable
#Override
public Icon getIcon() {
return AllIcons.Actions.Compile;
}
#Nullable
#Override
public MyBeforeRunTask createTask(#NotNull RunConfiguration runConfiguration) {
return new MyBeforeRunTask(getId());
}
#Override
public boolean executeTask(#NotNull DataContext dataContext, #NotNull RunConfiguration runConfiguration, #NotNull ExecutionEnvironment executionEnvironment, #NotNull MyBeforeRunTask myBeforeRunTask) {
return true;
}
}
And the task:
public class MyBeforeRunTask extends BeforeRunTask<MyBeforeRunTask> {
protected MyBeforeRunTask(#NotNull Key<MyBeforeRunTask> providerId) {
super(providerId);
setEnabled(true);
}
}

GWTP: Troubles to display a CellTable

Hi I'm new to GWT and so, to GWTP too.
I try to play with CellTables and I decided to begin by building a simple one following GWT documentation at developers.google.com/web-toolkit/doc/2.4/DevGuideUiCellWidgets#celltable
I adapted few things to match GWTP MVP design.
First, I created my Celltable on my View.ui.xml file:
xmlns:c="urn:import:com.google.gwt.user.cellview.client">
<g:HTMLPanel>
<c:CellTable pageSize='15' ui:field='cellTable' />
</g:HTMLPanel>
Then, I created a class Contact:
public class Contact {
private final String address;
private final String name;
public Contact(String name, String address) {
this.name = name;
this.address = address;
}
public String getAddress() {
return address;
}
public String getName() {
return name;
}
}
in my View.java file:
#UiField(provided=true) CellTable<Contact> cellTable = new CellTable<Contact>();
public CellTable<Contact> getCellTable() {
return cellTable;
}
Finally in my Presenter.java file:
public interface MyView extends View {
CellTable<Contact> getCellTable();
}
#Override
protected void onReset() {
super.onReset();
// Create name column.
TextColumn<Contact> nameColumn = new TextColumn<Contact>() {
#Override
public String getValue(Contact contact) {
return contact.getName();
}
};
// Create address column.
TextColumn<Contact> addressColumn = new TextColumn<Contact>() {
#Override
public String getValue(Contact contact) {
return contact.getAddress();
}
};
// Add the columns.
getView().getCellTable().addColumn(nameColumn, "Name");
getView().getCellTable().addColumn(addressColumn, "Address");
// Set the total row count.
getView().getCellTable().setRowCount(CONTACTS.size(), true);
// Push the data into the widget.
getView().getCellTable().setRowData(0, CONTACTS);
}
Everything seems good to me but there is no CellTable displayed when I try this code...And I get no errors...
Thanks in advance for your Help!
It looks like you do not use/have DataProvider registered for your CellTable. GWT CellWidgets are based on DataProvider/DIsplay pattern. So CellTable is just a Display for your DataProvider. One DataProvider could have multiple displays.
You do not need to write:
// Set the total row count.
getView().getCellTable().setRowCount(CONTACTS.size(), true);
// Push the data into the widget.
getView().getCellTable().setRowData(0, CONTACTS);
You need to register your CellTable as a Display for your DataProvider (eg ListDataProvider) and than call refresh method when you update DataProvider with new data.

Ninject Cascading Inection with IList

I am trying to use Ninject to implement cascading injection into a class that contains an IList field. It seems that, unless I specifically specify each binding to use in the kernel.Get method, the IList property is always injected with a list of a single default object.
The following VSTest code illustrates the problem. The first test fails because the IList field contains one MyType object with Name=null. The second test passes, but I had to specifically tell Ninject what constructor arguments to use. I am using the latest build from the ninject.web.mvc project for MVC 3.
Does Ninject specifically treat IList different, or is there a better way to handle this? Note that this seems to only be a problem when using an IList. Createing a custom collection object that wraps IList works as expected in the first test.
[TestClass()]
public class NinjectTest
{
[TestMethod()]
public void ListTest_Fails_NameNullAndCountIncorrect()
{
var kernel = new Ninject.StandardKernel(new MyNinjectModule());
var target = kernel.Get<MyModel>();
var actual = target.GetList();
// Fails. Returned value is set to a list of a single object equal to default(MyType)
Assert.AreEqual(2, actual.Count());
// Fails because MyType object is initialized with a null "Name" property
Assert.AreEqual("Fred", actual.First().Name);
}
[TestMethod()]
public void ListTest_Passes_SeemsLikeUnnecessaryConfiguration()
{
var kernel = new Ninject.StandardKernel(new MyNinjectModule());
var target = kernel.Get<MyModel>(new ConstructorArgument("myGenericObject", kernel.Get<IGenericObject<MyType>>(new ConstructorArgument("myList", kernel.Get<IList<MyType>>()))));
var actual = target.GetList();
Assert.AreEqual(2, actual.Count());
Assert.AreEqual("Fred", actual.First().Name);
}
}
public class MyNinjectModule : NinjectModule
{
public override void Load()
{
Bind<IList<MyType>>().ToConstant(new List<MyType> { new MyType { Name = "Fred" }, new MyType { Name = "Bob" } });
Bind<IGenericObject<MyType>>().To<StubObject<MyType>>();
}
}
public class MyModel
{
private IGenericObject<MyType> myGenericObject;
public MyModel(IGenericObject<MyType> myGenericObject)
{
this.myGenericObject = myGenericObject;
}
public IEnumerable<MyType> GetList()
{
return myGenericObject.GetList();
}
}
public interface IGenericObject<T>
{
IList<T> GetList();
}
public class StubObject<T> : IGenericObject<T>
{
private IList<T> _myList;
public StubObject(IList<T> myList)
{
_myList = myList;
}
public IList<T> GetList()
{
return _myList;
}
}
public class MyType
{
public String Name { get; set; }
}
lists, collections and arrays are handled slightly different. For those types ninject will inject a list or array containing an instance of all bindings for the generic type. In your case the implementation type is a class which is aoutobound by default. So the list will contain one instance of that class. If you add an interface to that class and use this one the list will be empty.

Castle Windsor Fluent API: Define Array with Single item as Dependency

Given this XML configuration (which works)
<component type="X.Y.Z.ActivityService, X.Y.Z.Services" id="X.Y.Z.ActivityService" lifestyle="transient">
<parameters>
<Listeners>
<array>
<item>${DefaultActivityListener}</item>
</array>
</Listeners>
</parameters>
</component>
<component type="X.Y.Z.DefaultActivityListener, X.Y.Z.Services" id="DefaultActivityListener" lifestyle="transient" />
I have converted to use the fluent API as below (which doesn't work):
Container.Register(
Component.For<X.Y.Z.ActivityService>()
.ServiceOverrides(
ServiceOverride.ForKey("Listeners").Eq(typeof(X.Y.Z.DefaultActivityListener).Name))
.LifeStyle.Transient
);
Container.Register(
Component.For<X.Y.Z.DefaultActivityListener>()
.Named("DefaultActivityListener")
.LifeStyle.Transient
);
When I now attempt to resolve an instance of X.Y.Z.ActivityService Windsor throws a NotImplementedException in Castle.MicroKernel.SubSystems.Conversion.ArrayConverter.PerformConversion(String, Type).
The implementation of the PerformConversion method is:
public override object PerformConversion(String value, Type targetType)
{
throw new NotImplementedException();
}
I should add that if I remove the ServiceOverrides call, all behaves as expected. So there is specifically something wrong in the way I am wiring up the Listeners parameter. Listeners by the way is a property as opposed to a constructor parameter.
Seeing as the XML config works as expected how do I best use the fluent API (short of implementing the PerformConversion method) in order to achieve the same result?
I am using Release 2.0.
EDIT
I will extend the question to how would you achieve this configuration in code, with or without use of the fluent API.
UPDATE
It appears the problem occurs if you attempt to assign a single element to an array property. Unit tests provided below to illustrate issue.
namespace Components
{
public class A
{
public I[] I { get; set; }
}
public interface I
{
string Name { get; }
}
public class B : I
{
public string Name { get { return "B"; } }
}
public class C : I
{
public string Name { get { return "C"; } }
}
}
[TestMethod]
public void ArrayPropertyTestApi()
{
//PASSES
using (Castle.Windsor.WindsorContainer container = new Castle.Windsor.WindsorContainer())
{
container.Register(Component.For<Components.A>().ServiceOverrides(ServiceOverride.ForKey("I").Eq(typeof(Components.B).FullName, typeof(Components.C).FullName)));
container.Register(Component.For<Components.B>());
container.Register(Component.For<Components.C>());
Components.A svc = container.Resolve<Components.A>();
Assert.IsTrue(svc.I.Length == 2);
Assert.IsTrue(svc.I[0].Name == "B");
Assert.IsTrue(svc.I[1].Name == "C");
}
}
[TestMethod]
public void ArrayPropertyTestApi2()
{
//FAILS
using (Castle.Windsor.WindsorContainer container = new Castle.Windsor.WindsorContainer())
{
container.Register(Component.For<Components.A>().ServiceOverrides(ServiceOverride.ForKey("I").Eq(typeof(Components.B).FullName)));
container.Register(Component.For<Components.B>());
container.Register(Component.For<Components.C>());
Components.A svc = container.Resolve<Components.A>(); //Throws NotImplementedException
Assert.IsTrue(svc.I.Length == 1);
Assert.IsTrue(svc.I[0].Name == "B");
}
}
Question still stands.
Thanks.
[TestFixture]
public class WindsorTests {
[Test]
public void ArrayConfig() {
var container = new WindsorContainer();
container.Register(Component.For<Listener>().Named("listener"));
container.Register(Component.For<ActivityService>()
.ServiceOverrides(ServiceOverride.ForKey("listeners").Eq(new[] {"listener"})));
var service = container.Resolve<ActivityService>();
Assert.AreEqual(1, service.Listeners.Length);
}
}
public class Listener {}
public class ActivityService {
public Listener[] Listeners { get; set; }
}
The key part here is the new[] {"listener"}. The MicroKernel needs to know that the parameter listeners is an array, if you pass just "listener" it assumes that the parameter is scalar and throws because it can't convert a scalar to an array.