2010. szeptember 29., szerda

UMLet saját komponens

Már egy előző post-ban (UML tevékenységdiagram) írtam az UMLet UML diagram rajzoló eszközről, és több diagramot is ezzel készítettem, mint pl. a Fa ábrázolása adatbázisban post-ban lévőt is. Ez utóbbiban látható, hogy olyan komponenst használtam, mely nincs az UMLet-ben (lekerekített sarkú téglalap, árnyékkal és színátmenettel).

Ezen eszköz alapvető előnye két dologban rejlik. Egyrészt, ahogy már írtam, kellően egyszerű, nem kell dialógusablakok garmadán keresztülvergődni egy UML diagram elkészítéséhez, hanem ki kell tenni a komponenst, melyhez egy darab Properties beviteli mező tartozik. Ebben lehet minden leírni, gyakorlatilag egy kötött formátumot használva (script-szerűen). Másik előnye, hogy saját komponenseket nagyon egyszerű benne megvalósítani, Java nyelven, így gyakorlatilag tetszőlegesen testre szabható, bármilyen saját komponens létrehozható.

Az UMLet-ben saját elem létrehozására a Custom Element való, melyről az UMLet honlapján egy tutorial is található.

Custom Element szerkesztéséhez először a Custom Elements/New... menüpontot válasszuk ki. Ekkor megjelenik az alsó szerkesztő ablak, három szövegmezővel. Az első szövegmezőben a komponens tulajdonságait adhatjuk meg. A másodikban a forráskódját, a harmadikban pedig látunk egy előnézeti képet.

A példában a következő forráskód szerepel:

int y=textHeight();

drawRectangle(0,0,width,height);

for(String textline : textlines) {
 printCenter(textline,y);
 y = y + textHeight();
}

Gyakorlatilag a kód kirajzol egy téglalapot, és beleírja a szöveget. A width, height, textlines változók lokális változók, míg a textHeight(), drawRectangle() metódusok.

Ha valami szebb megjelenítést akarunk, akkor írjuk ide következő kódot:

// Változó inicializációk
int dontHideBorder = 1;
int roundedCorner = 5;
int shadowOffset = (int) (5 * zoom);
Color bg = new Color(Integer.decode("#58ACFA"));

// Árnyék 
g2.setColor(new Color(200, 200, 200));
g2.fillRoundRect(shadowOffset, shadowOffset, getWidth() - shadowOffset - dontHideBorder, getHeight() - shadowOffset - dontHideBorder, 10, 10);  

// Színátmenettel feltöltött téglalap
g2.setPaint(new GradientPaint(1, 0, bg, getWidth() + 1, 0, Color.WHITE, false));
g2.fillRoundRect(dontHideBorder, dontHideBorder, getWidth() - shadowOffset, getHeight() - shadowOffset, roundedCorner, roundedCorner);

// Keret
g2.setColor(Color.BLACK);
g2.drawRoundRect(dontHideBorder, dontHideBorder, getWidth() - shadowOffset, getHeight() - shadowOffset, roundedCorner, roundedCorner);

// Szöveg
int y = textHeight();
for (String textline : textlines) {
 if (!textline.contains("=")) {
  printCenter(textline, y);
  y = y + textHeight();
 }
}

Itt a legérdekesebb, hogy a g2 egy Graphics2D objektum, melyre lehet rajzolni, ez egy standard JDK-ban található osztály. Ez gyakorlatilag a komponens vászna. A 0,0 a bal felső sarka. Bizonyos számolásoknál figyelembe kell venni azt is, hogy nemrég jelent meg a zoom-olás lehetősége az UMLet-ben, így a helyes koordináták meghatározásakor a zoom-mal való szorzás elengedhetetlen (zoom = 1 a 100%-nál, zoom = 0.5 az 50%-nál).

Látható, hogy a forráskód írása közben fordítás történik, a hibás sorok pirossal kerülnek kiemelésre, és az egeret felé víve megjelenik a hibaüzenet. A háttérben ugyanis az történik, hogy a custom_elements könyvtárba van egy Default.java, mely a default package-ben van, osztályának nevét () kicseréli a CustomElementImpl-re, valamint a /****CUSTOM_CODE START****/ és /****CUSTOM_CODE END****/ szöveg közötti szöveget kicseréli a szöveges mezőbe beírt értékre. Ezt elmenti a custom_elements/tmp könyvtárba, és futás közben lefordítja az Eclipse Java development tools (JDT) Core komponensével. Ez a com.umlet.custom.CustomElementCompiler csomagban található. A Default.java forráskódja a következő:

import java.awt.*;
import java.util.*;

import com.umlet.constants.Constants;

@SuppressWarnings("serial")
public class <!CLASSNAME!> extends com.umlet.custom.CustomElement {

 public CustomElementImpl()
 {
  
 }
 
 @Override
 public void paint() {
  Vector<String> textlines = Constants.decomposeStrings(this.getState());
 
  /****CUSTOM_CODE START****/
//Modify the code below to
//define the element's behavior.
//
//Example:  Change the line
//  y = y + textHeight();
//to
//  y = y + 2 * textHeight();
//and observe the element preview.

int y=textHeight();

drawRectangle(0,0,width,height);

for(String textline : textlines) {
 printCenter(textline,y);
 y = y + textHeight();
}
  /****CUSTOM_CODE END****/
 }
}

Ebből a következő dolgok következnek. Az új komponens a com.umlet.custom.CustomElement osztály leszármazottja, mely a com.umlet.element.base.Entity leszármazottja. Ezért ezekből származott (nem private) változóit és metódusait el lehet érni. A java.awt és java.util csomagban lévő osztályok használhatóak közvetlenül, a többit teljes névvel (csomaggal együtt) kell megadni. A textlines változó is elérhető a kódból, ami a Custom Code előtt lett definiálva.

Láthattuk azt is, hogy automatikus kódkiegészítés van a szövegszerkesztő mezőben. Ez úgy lett megvalósítva, hogy a megfelelő metódusok a CustomElement osztályban @CustomFunction annotációval vannak ellátva.

Persze ebben a környezetben kényelmetlen szerkeszteni, megtehetjük azt is, hogy kedvenc IDE-nkbe csináljuk ugyanezt. Ehhez a umlet.jar-t kell a CLASSPATH-ba tenni, és a Default.java-t létrehozni, átmásolni, és a Custom Code közé írni a saját kódot, és ezt másolgatni az UMLet-be.

Ha bonyolultabb dolgokat akarunk megvalósítani, akkor gyakran kaphatunk kivételt, mely a teljes GUI-t lefagyasztja. Ehhez javasolt a Custom Code-ot try-catch-be rakni.

try {
...
} catch (Exception e) {
printLeft(e.getMessage(), textHeight());
}

Ennek leggyakrabban akkor vesszük hasznát, mikor olyan metódust szeretnénk meghívni, melyre nincs jogosultságunk. Ugyanis az UMLet egy saját SecurityManager-t implementált (com.umlet.custom.CustomElementSecurityManager), mely a következőket engedi: java.lang, java.util, com.umlet.custom, com.umlet.constants, java.awt.geom, java.awt.font, java.awt), ebből a legfájóbb pont a com.umlet.control.diagram.DiagramHandler hiánya, ugyanis ebben rengeteg hasznos metódus található, de nem lehet e miatt elérni. Ekkor a következő hibaüzenetet kapjuk: No security critical tasks allowed in customized elements. Sajnos ugyanez igaz a debug-olásra is, a Log4J csomagja sem engedélyezett. (A Log4J bekapcsolásához amúgy csak a főkönyvtárban kell elhelyezni egy log4j.properties állományt.)

Ha szeretnénk meta információkat is elhelyezni a komponens mezői között, akkor nekünk kell azt parse-olnunk. Érdemes kulcs érték párokat használni, és Properties-ként beolvasni. Pl. ha módosítani szeretnénk a háttér színét, akkor a bg=#5F04B4 megadásával lehet. Ezt értelmezni a következő kód tudja:

// Properties mező feldolgozása
Properties props = new Properties();
props.load(new java.io.StringReader(getPanelAttributes()));
bg = new Color(Integer.decode(props.getProperty("bg")));

Hasznos metódus még a textWidth metódus is, nézzük meg pl, ha a szöveget nem középre, hanem lépcsőzetesen akarjuk elhelyezni:

int x = 2 * zoom;
int y = textHeight();
for (String textline : textlines) {
 if (!textline.contains("=")) {
  print(textline, x, y);
  y = y + textHeight();
  x = textWidth(textline);
 }
}

Ha elmentjük a diagramot, akkor a Custom Element kódját is bele menti, tehát hordozható.

Ha gyakran használunk egy bonyolultabb struktúrát (több komponenst), akkor érdemes egy új komponenst létrehozni, mintegy sablonként, saját szöveges konvenciót kitalálni a komponensek leírására, azt parse-olni, és a megjelenítését megvalósítani. Példaképp vegyünk egy csomagdiagramot. Ennek hierarchikus szerkezetét összerakhatjuk komponensekből egyenként, a csomag komponensekből és a közöttük lévő kapcsolatokból, de használhatunk összetett komponenst is. A következő leíráson látható, hogy a struktúrát a behúzás mértéke adja meg:

root
 name
 name
  name
  name
root
 name

Ennek grafikus reprezentációja:

Jó egy kicsit elszakadni a webes alkalmazásoktól, HTML-től, és kézben tartani minden pixelt. A forráskód letölthető, mely tartalmazza azt a bonyolultabb komponenst is, mely lehetővé teszi, hogy a téglalap mellé balra és jobbra is egy számot lehessen kiírni.

0 megjegyzés:

Megjegyzés küldése