Commands, Handlers und Parameter…

Hallo zusammen,

nachdem ich mich selbst nun schon eine ganze Weile damit herumgeärgert habe, die Dokumentation wie üblich spärlich ist, und in den diversen Beiträgen hier im Prinzip schon alle „Fragmente“ einmal zur Sprache kamen, soll hier mal ein funktionierendes Beispiel zusammengebaut werden, und zwar soll es darum gehen,wie man Commands + Handler mit einer Parameterübergabe implementieren kann.

Die einzelnen Bausteine, die wir hierfür benötigen sind:

  • Commands
  • Handler
  • ISourceProviders

Wir machen hier 2 Beispiele, zum Einen den einfachen Fall, und zwar, das ein String als Parameter übergeben wird.

Und den etwas komplizierteren Fall, und zwar indem wir einen beliebigen Parameter übergeben, nennen wir ihn Object;-).

Aber eins nach dem anderen, denken wir uns also ein Beispiel aus.. .. .. .. .. ..

Aha! wir implementieren ein OpenViewCommand, das eine ViewID als String-Parameter bekommt und diesen VIew anschliessend auch öffnet.

Was brauchen wir hierzu:

Command +  Handler

Textfeld für die Eingabe der View-ID

Button zum Auslösen der Command

2 Views, zur Unterscheidung.

Ok, frisch ans Werk:

Wir erstellen also ein einfaches Plug-In Project, natürlich als RCP-Anwendung und nehmen folgendes Template her:

rcp1

Anschliessend implementieren wir einen ersten View der als Container für unsere Widgets dienen soll, die Implementierung ist derart trivial, das ich hier nicht darauf eingehen werden, das ganze sieht anschliessend so aus:

view1

Auf die Implementierung des BusinessLogik für den Button kommen wir später noch.

Jetzt definieren wir eine Command mit der ID de.md.example.commandparameter.openview,der wir auch direkt einen DefaultHandler zuordnen.

<extension
point=“org.eclipse.ui.commands“>
<command
id=“de.md.example.commandparameter.openview“
name=“name“>
</command>
</extension>
<extension
point=“org.eclipse.ui.handlers“>
<handler
class=“de.md.example.commandparameter.commands.OpenViewHandler“
commandId=“de.md.example.commandparameter.openview“>
</handler>
</extension>

Jetzt implementieren wir den SelectionListener auf dem Button, wobei das interessante Code-Fragment im folgenden aufgelistet ist:

ICommandService cS = (ICommandService)getSite().getService(ICommandService.class);
IHandlerService hS = (IHandlerService)getSite().getService(IHandlerService.class);

Command openView = cS.getCommand(„de.md.example.commandparameter.openview“);

Map<String,String> params = new HashMap<String,String>();
params.put(„myViewID“, text.getText());
ParameterizedCommand pC = ParameterizedCommand.generateCommand(openView, params);
try {
hS.executeCommand(pC, null);
} catch (Exception e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}

Zuerst holen wir uns den IHandlerService + ICommandService.

Den CommandService brauchen wir, um uns das auszuführende Command zu holen, den HandlerService brauchen wir, um das Command auszuführen.

Ok, der Reihe nach, zuerst holen wir uns das zuvor in der plugin.xml deklarierte openView-Command

Command openView = cS.getCommand(„de.md.example.commandparameter.openview“);

Dann erzeugen wir uns eine Map, die den von uns übergebenen Parameter beinhaltet

Map<String,String> params = new HashMap<String,String>();
params.put(„myViewID“, „de.md.example.views.myView1“);

de.md.example.views.myView1 ist hier die ID eines Views, die geöffnet werden soll, sobald der Button gedrückt wird.

Zur Ausführung einer Command mit einem Parameter gibt es die Abstraktion ParameterizedCommand,was im Prinzip einfach eine Command mit einem Parameter ist.  Seit Eclipse 3.4 gibt es hier den sehr einfache Weg über

ParameterizedCommand pC = ParameterizedCommand.generateCommand(openView, params);

Wie man sieht, bekommt die Convenience-Methode das Command mit den zuvor definieren Parametern. Führt man dies jetzt aber so aus, wird diese Methode immer „null“ liefern, warum? Weil wir bisher keinen Parameter für unsere Command definiert haben. Das holen wir gleich mal nach und passen die Command-Deklaration folgendermaßen an:

<extension
point=“org.eclipse.ui.commands“>
<command
id=“de.md.example.commandparameter.openview“
name=“name“>
<commandParameter
id=“myViewID“
name=“name“
optional=“true“>
</commandParameter>
</command>
</extension>

Jeder Parameter, der einer Command übergeben wird, muss zuvor hier deklariert werden.

Führen wir jetzt das ganze aus, und fügen in unserem Handler mal eine einfache Ausgabe ein, sieht man sofort folgendes:

viewcommand

Cool! Scheint zu funktionieren, ok, wir implementieren mal 2 Views.

<view
class=“de.md.example.commandparameter.views.View1″
id=“de.md.example.commandparameter.views.view1″
name=“name“
restorable=“true“>
</view>
<view
class=“de.md.example.commandparameter.views.View2″
id=“de.md.example.commandparameter.views.view2″
name=“name“
restorable=“true“>
</view>

Die richtige Implementierung des Handlers ist jetzt denkbar einfach:

String param = (event.getParameter(„myViewID“));
try {
HandlerUtil.getActiveWorkbenchWindow(event).getActivePage().showView(param);
} catch (PartInitException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;

Führt man das ganze jetzt aus, sieht man das das wunderbar funktioniert.

views

Ok, das war der gemütliche Teil, jetzt wollen wir das ganze Mal mit beliebig komplexen Objekten probieren. Man könnte jetzt auf die Idee kommen, das dies im Prinzip genau gleich funktionieren müsste, wir haben hier nur ein Problem:

Map<String,String> params = new HashMap<String,String>();

Autsch!! Die Commands können von Haus aus erst mal nur mit Strings umgehen:-(

Wollte man hierfür also den gleichen Mechanismus verwenden, dann müsste man folgendermaßen vorgehen:

command

Man sieht in der Definition einer Command die beiden Elemente „values“ und „typeid“.

Was uns hier primär interessiert ist der Parameter „typeid“, hier kann man einen Parametertyp angeben, den man zuvor aber auch wieder deklarieren muss, zuvor aber implementieren wir uns das einfachse DomainObjekt, das man sich vorstellen kann, das wir dann als Parameter übergeben können.

public class MyDomainObject {

private String name;
public MyDomainObject(String name) {
this.name = name;
}

public String toString(){
return name;
}

So, jetzt können wir den CommandParameter deklarieren, und zwar so:

parametertype

Das ganze erwartet folgende Parameter:

typeconverter

Zunächst den type, was eben genau unser Domain-Objekt ist und weiterhin einen Converter vom Typ AbstractParameterValueConverter, und hier liegt genau der Hase im Pfeffer, da nämlich Commands nur mit Strings umgehen können, muss jeder Parameter zwingend in einen String konvertiert werden:-(… Keine Angst, das geht besser, der Vollständigkeithalber machen wir das jetzt mal, denn für unser einfachen DomainObject geht das noch relativ einfach, der Converter könne in etwa so aussehen:

public class MyDomainParameterConverter extends AbstractParameterValueConverter {

public MyDomainParameterConverter() {
// TODO Auto-generated constructor stub
}

@Override
public Object convertToObject(String parameterValue)
throws ParameterValueConversionException {
return new MyDomainObject(parameterValue);
}

@Override
public String convertToString(Object parameterValue)
throws ParameterValueConversionException {
return parameterValue.toString();
}

}

Das mag für einfache Objekte ja noch akzeptabel sein, für komplexe Objekthierarchien eher nicht…

Hat man diesen ParameterTypen deklariert, kann man diesen auch in der Definition des COmmandParameters auswählen:

commandids

Hierfür definieren wir uns mal einen zweiten Button, der folgende Implementierung hat:

button = new Button(container, SWT.PUSH);
button.setText(„PrintMyDomainObject“);
button.addSelectionListener(new SelectionAdapter(){
@Override
public void widgetSelected(SelectionEvent e) {
ICommandService cS = (ICommandService)getSite().getService(ICommandService.class);
IHandlerService hS = (IHandlerService)getSite().getService(IHandlerService.class);

Command openView = cS.getCommand(„de.md.example.commandparameter.openview“);

Map<String,Object> params = new HashMap<String,Object>();
params.put(„myDomain“, new MyDomainObject(text.getText()));

ParameterizedCommand pC = ParameterizedCommand.generateCommand(openView, params);
try {
hS.executeCommand(pC, null);
} catch (Exception e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
});

Um das Domain-Objekt im Handler zu extrahieren müssen wir die Konvertierung von Hand anstossen!!

String param = event.getParameter(„myDomain“);
try {

MyDomainObject mD = (MyDomainObject)event.getCommand().getParameterType(„myDomain“).getValueConverter().convertToObject(param);
System.out.println(mD);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

return null;

Man holt sich also im Prinzip über das Command den CommandParameterType, über diesen den Converter, stößt die Konvertierung an und erhält hierdurch sein Objekt…. brrhhh, grausig.. Die Bundles für diesen aktuellen Stand findet ihr hier

Warum ist das aber so implementiert?? Die traurige Wahrheit ist, dieser Mechanismus ist nicht gedacht, hier beliebig komplexe Parameter zu übergeben, sondern dies ist eher dafür gedacht, in der Benutzeroberfläche für Menüs etc. Auswahlmöglichkeiten bereitzustellen, will man wirklich Parameter an Commands übergeben, gibt es hierfür bessere Mechanismen, und die schauen ich mir im nächsten Blogeintrag etwas genauer an. Bis dahin…

Advertisements

5 Gedanken zu „Commands, Handlers und Parameter…

  1. Ivan

    Hi,

    auf der Suche nach einer vernünftigen Erklärung zu komplexen Command Parametern bin auf deinen Blog gestoßen. War schön das hier zu lesen weil so
    ganz klar geht das aus der Dokumentation ja nicht hervor.
    Ich z.B habe folgendes Problem. Ich habe einen Command der aus einem PopUp Menu mit Parameter aufgerufen werden soll. Sprich mein tableViewer hat ein PopUp Menu mit zwei Einträgen die aber den selben Command bzw. Handler aufrufen sollen jedoch mit einem anderen Parameter. Evtl. hast du eine Idee wie bzw. ob das zu bewersteligen ist 😀

    Ansonsten immer weiter so.. hat mir wirklich einiges klarer vor Augen geführt
    Danke.
    Gruß
    Ivan

    Antwort
    1. splitshade Autor

      Hi Ivan,
      danke für den Kommentar.

      Deine Anforderung klingt etwas seltsam, ich würde das über 2 verschiedene Commands realisieren, denen jedoch ein Handler zugewiesen ist.
      Dann kannst Du den Commands jeweils eigene Parameter zuweisen, wie im Artikel beschrieben und anhand dieses Parameters
      die entsprechende Funktionalität ausführen.

      Gruß

      Antwort
  2. Jens Goldhammer

    Hallo splitshade,

    danke für das tolle Tutorial.
    Du hast geschrieben, dass es einen einfacheren Weg gibt, komplexe Parameter an den Command dranzuhängen.
    Ich habe nämlich genau das Problem, dass ich mein Objekt nicht so einfach aus einem String wiederherstellen kann.

    Viele Grüße
    Jens

    Antwort
  3. Jamal Baydoun

    Hallo,

    Dein Tutorial ist ganz toll. So genaue Informationen über Commands findet man im Internet gar nicht!!!

    Hast Du noch vor, einen Tutorial zu schreiben, wie man auch beliebig komplexe Parameter dem Command übergeben kann?

    Danke & Gruß,
    Jamal

    Antwort

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