Das Modeling-Framework Eclipse Graphiti

Zum jetzigen Zeitpunkt wären wir bereits in der Lage, ein Diagramm anzulegen und im Editor zu öffnen. Die ersten drei Schritte gingen uns geschwind von der Hand, für das verbleibende Add Feature müssen wir etwas mehr Aufwand betreiben. Abbildung 3 veranschaulicht, wie die EClass im Editor aussehen wird (Result). Der obere Teil der Abbildung zeigt, wie die für die grafische Darstellung nötigen Pictogram Elements von dem Add Feature mit dem Domain-Objekt verdrahtet werden.

Abb. 3: Verknüpfung der beteiligten Objekte

In der linken Spalte des Pictogram Model aggregiert ein Container Shape zwei enthaltene Shapes, die für das Textlabel und die Abgrenzungslinie zuständig sind. Alle drei Shapes aggregieren wiederum Informationen der Graphics Algorithms, mit denen die EClass im Editor vollständig dargestellt werden kann. Solche Informationen sind Positionen, Vorder- und Hintergrundfarbe oder ein Farbverlauf, Sichtbarkeit und Linienstärke sowie Linienstil. In unserem Beispiel haben wir ein RoundedRectangle, versehen mit einem Radius für die Ecken, eine Polyline mit Anfangs- und Endpunkt, sowie ein Label mit einem Text als Wert. Das Container Shape und das enthaltene Shape, das das Label enthält, werden mit der korrespondierenden EClass aus dem Domain Model in Verbindung gebracht, dazu dient jeweils ein Link-Objekt, das beide Enden enthält.

In Listing 2 findet sich die vollständige Implementierung des Add Features, die leicht nachvollzogen werden kann. Grundlage ist die Standardimplementierung AbstractAddShapeFeature, von der die beiden Methoden canAdd und add überschrieben werden. Aufgrund des Contexts hat canAdd zu entscheiden, ob das enthaltene Business Object verwendet werden kann. Die Methode add erzeugt die oben beschriebenen Pictogram Elements, Graphics Algorithms, Links und deren Verknüpfungen. Mithilfe der Services IPeCreateService und IGaService können die Pictogram Elements und Graphics Algorithms leicht erzeugt werden, die Positionen dafür erhalten wir vom Context.

Listing 2: Das Add Feature
public class TutorialAddEClassFeature extends AbstractAddShapeFeature {
    private static final IColorConstant CLASS_TEXT_FOREGROUND =
        new ColorConstant(51, 51, 153);
    private static final IColorConstant CLASS_FOREGROUND =
        new ColorConstant(255, 102, 0);
    public TutorialAddEClassFeature(IFeatureProvider fp) {
        super(fp);
    }
    public boolean canAdd(IAddContext context) {
        // check if user wants to add a EClass
        if (context.getNewObject() instanceof EClass) {
                       // check if user wants to add to a diagram
            if (context.getTargetContainer() instanceof Diagram) {
                return true;
            }
        }
        return false;
    }
    public PictogramElement add(IAddContext context) {
        EClass addedClass = (EClass) context.getNewObject();
        Diagram targetDiagram = (Diagram) context.getTargetContainer();
        // CONTAINER SHAPE WITH ROUNDED RECTANGLE
        IPeCreateService peCreateService = Graphiti.getPeCreateService();
        ContainerShape containerShape =
             peCreateService.createContainerShape(targetDiagram, true);
        // define a default size for the shape
        int width = 100;
        int height = 50; 
        IGaService gaService = Graphiti.getGaService();
        {
            // create and set graphics algorithm
            RoundedRectangle roundedRectangle =
                gaService.createRoundedRectangle(containerShape, 5, 5);
            roundedRectangle.setForeground(manageColor(CLASS_FOREGROUND));
            //set color gradient
            roundedRectangle.setRenderingStyle(
                    PredefinedColoredAreas.getBlueWhiteGlossAdaptions());
            roundedRectangle.setLineWidth(2);
            gaService.setLocationAndSize(roundedRectangle,
                context.getX(), context.getY(), width, height);
            // if added Class has no resource we add it to the resource 
            // of the diagram
            // in a real scenario the business model would have its own resource
            if (addedClass.eResource() == null) {
                     getDiagram().eResource().getContents().add(addedClass);
            }
            // create link and wire it
            link(containerShape, addedClass);
        }
        // SHAPE WITH LINE
        {
            // create shape for line
            Shape shape = peCreateService.createShape(containerShape, false);
            // create and set graphics algorithm
            Polyline polyline =
                gaService.createPolyline(shape, new int[] { 0, 20, width, 20 });
            polyline.setForeground(manageColor(CLASS_FOREGROUND));
            polyline.setLineWidth(2);
        }
        // SHAPE WITH TEXT
        {
            // create shape for text
            Shape shape = peCreateService.createShape(containerShape, false);
            // create and set text graphics algorithm
            Text text = gaService.createDefaultText(shape,addedClass.getName());
            text.setForeground(manageColor(CLASS_TEXT_FOREGROUND));
            text.setHorizontalAlignment(Orientation.ALIGNMENT_CENTER);
            text.setVerticalAlignment(Orientation.ALIGNMENT_CENTER);
            text.getFont().setBold(true);
            gaService.setLocationAndSize(text, 0, 0, width, 20);
            // create link and wire it
            link(shape, addedClass);
        }
        return containerShape;
    }
}

Damit der Feature Provider unser neues Feature liefern kann, müssen wir am Ende dieses kleinen Beispiels nur noch getAddFeature überschreiben:

@Override
public IAddFeature getAddFeature(IAddContext context) {
       //is object for add request a EClass?
       if (context.getNewObject() instanceof EClass) {
           return new TutorialAddEClassFeature(this);
       }
       return super.getAddFeature(context);
}

Abb. 4: Der EClass Editor

Jetzt können wir mit einem Wizard (NEW . -> GRAPHITI DIAGRAM) ein Diagramm anlegen und im Editor öffnen. Per Drag-and-Drop gelangt die PredefinedEClass auf das Diagramm, wie in Abbildung 4 zu sehen ist. Ohne zusätzliche Implementierung kann die EClass Shape jetzt auch noch bewegt, vergrößert und gelöscht werden.

Fazit und Ausblick

Der vorliegende Artikel hat einen ersten Einblick in die Programmierung von grafischen Tools mit Graphiti gegeben. Nach einer Einführung in das Programmierkonzept und das Framework wurde in wenigen Schritten eine erste Version eines bereits lauffähigen grafischen Editors für Ecore-Metamodelle entwickelt. Die dafür benötigte Menge an Programmzeilen war dank der bereits im Framework enthaltenen Standard-Implementierungen sehr gering. Das Graphiti Framework ist Teil des Eclipse Modeling GMP. Für das nächste Release plant das Entwicklerteam eine Reihe von Detailverbesserungen sowie weitere Vereinfachungen am Programmiermodell. Das nächste Release wird im Juni 2011 zusammen mit Eclipse Indigo veröffentlicht werden.

Christian Brand, Matthias Gorning, Tim Kaiser, Jürgen Pasch und Michael Wenz sind in der Entwicklung bei der SAP AG im Tool-Development- und Modeling-Umfeld tätig und konnten in den letzten Jahren reichlich Erfahrung mit Eclipse-Technologien sammeln.
Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.