Property Testers

Heute soll es mal darum gehen, wie man die coolen PropertyTesters verwenden kann. PropertyTesters geben einem Entwickler die Möglichkeit, Inhalte von beispielsweise Instanzvariablen innerhalb von Expressions zu testen.

Ein typisches Anwendungsszenarion könnte beispielsweise sein, das ein bestimmter Handler nur aktiviert werden soll, wenn eine Boolean-Variable in einem selektierten Objekt innerhalb eines Viewers auf true steht.

Das probieren wir doch mal direkt aus:

Wir definieren ein Command wie folgt:

<extension

point=„org.eclipse.ui.commands“>

<command

id=„de.md.bindings.newCommand“

name=„name“>

</command>

</extension>

Zusätzlich einen Handler:

<extension

point=“org.eclipse.ui.handlers“>

<handler

class=“de.md.bindings.handlers.MySimpleHandler“

commandId=“de.md.bindings.newCommand“>

</handler>

</extension>

Zusätzlich ein Menüeintrag für die Toolbar:

<extension

point=„org.eclipse.ui.menus“>

<menuContribution

locationURI=„toolbar:org.eclipse.ui.main.toolbar“>

<toolbar

id=„de.md.bindings.toolbar1“>

<command

commandId=„de.md.bindings.newCommand“

icon=„icons/alt_window_16.gif“

mode=„FORCE_TEXT“

style=„push“>

</command>

</toolbar>

</menuContribution>

</extension>

Weiterhin einen View als Standalone View in der Perspektive:

<extension

point=„org.eclipse.ui.views“>

<view

class=„de.md.bindings.ViewPart“

id=„de.md.bindings.view“

name=„name“

restorable=„true“>

</view>

</extension>

Die CreatePartControl-Methode sieht ungefähr so aus:

@Override

public void createPartControl(Composite parent) {

viewer = new ComboViewer(parent, SWT.NONE);

viewer.setContentProvider(new IStructuredContentProvider() {

@Override

public Object[] getElements(Object inputElement) {

return (Object[]) inputElement;

}

@Override

public void dispose() {

// TODO Auto-generated method stub

}

@Override

public void inputChanged(Viewer viewer, Object oldInput,

Object newInput) {

// TODO Auto-generated method stub

}

});

viewer.setLabelProvider(new LabelProvider());

viewer.setInput(new MyTestObject[] { new MyTestObject(false),

new MyTestObject(true) });

getSite().setSelectionProvider(viewer);

}

Hier wird eine Klasse „MyTestObjekt“ referenziert, die sieht ungefähr so aus:

public class MyTestObject {

private boolean result;

public MyTestObject(boolean value){

this.result = value;

}

public Boolean getResult(){

return result;

}

public String toString(){

return result+„“;

}

}

Hier ist der zuvor beschriebene Boolean-Wert vorhanden, den wir gleich mit einem PropertyTester referenzieren wollen, im View haben wir eine Combobox, die 2 dieser Objekte (eins mit false, eins mit true bereitstellt).

Das ganze zusammengebaut sieht ungefähr so aus:

Was wir jetzt wollen ist, das das Command in der Toolbar aktiviert / deaktiviert wird, je nachdem, was in der Combo ausgewählt wird, mal schauen:

Wir definieren einfach mal einen PropertyTester:

<extension

point=“org.eclipse.core.expressions.propertyTesters“>

<propertyTester

class=„de.md.bindings.PropertyTester1“

id=„de.md.bindings.propertyTester“

namespace=„de.md.bindings.propertyTester1“

properties=„de.md.bindings.propertyTester1“

type=„de.md.bindings.PropertyTester1“>

</propertyTester>

</extension>

Der muss jetzt noch angepasst werden:

Ein PropertyTester besteht aus einer Klasse, die die Klasse org.eclipse.core.expressions.PropertyTester erweitert. Meine einfache Implementierung sieht so aus:

public class PropertyTester extends org.eclipse.core.expressions.PropertyTester {

public PropertyTester() {

// TODO Auto-generated constructor stub

}

@Override

public boolean test(Object receiver, String property, Object[] args,

Object expectedValue) {

if(receiver instanceof MyTestObject){

MyTestObject rec = (MyTestObject)receiver;

if(property.equals(„result“)){

return rec.getResult().booleanValue();

}

}

return false;

}

}

Die Methode test wird vom Framework aufgerufen, receiver ist hierbei das getestete Objekt, hierbei wird zuerst überprüft, ob das Receiverobjekt vom konkreten Typ ist und anschliessend auf die entsprechende Property.

Die Expression für die Aktivierung des Handlers ist folgende:

<handler

class=„de.md.bindings.handlers.MySimpleHandler“

commandId=„de.md.bindings.newCommand“>

<activeWhen>

<with

variable=„selection“>

<iterate>

<test

forcePluginActivation=“true“

property=“de.md.bindings.result“>

</test>

</iterate>

</with>

</activeWhen>

</handler>

Die Komplexität des Beispiels lässt sich beliebig erweitern.Was hier im <test/>-Element referenziert wird, ist <namespace des PropertyTesters><Attribut>, also in diesem Fall „de.md.bindings.result“. Da hier eine aktuelle WorkbenchSelection ausgewertet wird, wird der PropertyTester jedesmal aufgerufen, wenn sich eine aktuelle WorkbenchSelektion ändert. Was aber, wenn ein Attribut nicht über einen solchen bereits implementierten MEchanismus gestartet werden soll (Selektion), sondern beispielsweise dann, wenn sich ein bestimmtes Attribut in einem Objekt ändert. Dann bietet Eclipse den Mechanismus der SourceProvider. Das beschreibe ich in einem meiner nächsten Artikel.

Ein wirklich guter Screencast hierzu findet ist http://konigsberg.blogspot.com/2008/06/screencast-using-property-testers-in.html

Advertisements

3 Gedanken zu „Property Testers

  1. Ben

    Hallo,
    danke für diesen Blog-Eintrag. Er hat mir beim ersten Verständnis des Konzepts sehr geholfen.
    Leider funktioniert das Beispiel nicht. Genaugenommen ist es irreführend, da der PropertyTesters Extension Point gar nicht passen kann. Namespace+properties sollten zusammen den „property“ Eintrag ergeben. Im Falle dieses Beispiels wäre also der Namespace Eintrag „de.md.bindings“ und der Properties Eintrag „result“, damit der Test Eintrag des Handlers auf „de.md.bindings.result“ zeigen kann.
    Irgendwo scheint auch noch ein zweiter Bug zu sein, da es auch damit nicht wie erwartet funktioniert. Der type des PropertyTesters erscheint mir z.B. auch komisch. Ich ergänze das hier nochmal, wenn ich da einen Griff dran bekomme.

    Antwort
    1. Ben

      Die IDs weichen ggf. etwas von dem hier gezeigten Projekt ab, da ich von dem Ergebnis des Screencasts aus gearbeitet habe. Aber so geht’s:

      plugin.xml

      PropertyTester:

      public class PropertyTester1 extends PropertyTester {

      @Override
      public boolean test(Object receiver, String property, Object[] args, Object expectedValue) {
      if (receiver instanceof IStructuredSelection && „isTrue“.equals(property) ){
      Object element = ((IStructuredSelection) receiver).getFirstElement();
      if (element instanceof MyTestObject){
      MyTestObject mto = (MyTestObject) element;
      return mto.getResult();
      }
      }
      return false;
      }
      }

      Antwort
      1. Ben

        ah, xml darf ich hier wohl nicht…
        Hier mal ohne xml tags:
        propertyTester
        class=“com.foo.example.propertytester.PropertyTester1″
        id=“com.foo.example.propertytester.propertyTester1″
        namespace=“com.foo.example.testers“
        properties=“isTrue“
        type=“org.eclipse.jface.viewers.ISelection“
        /propertyTester

        handler
        commandId=“com.foo.example.propertytester.commands.sampleCommand“
        class=“com.foo.example.propertytester.handlers.SampleHandler“
        activeWhen
        with
        variable=“selection“
        test
        forcePluginActivation=“true“
        property=“com.foo.example.testers.isTrue“
        /test
        /with
        /activeWhen
        /handler

        Evtl. kommt man noch direkt an das MyTestObject ran. Aber mit dieser Lösung funktioniert schon mal.

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s