If you’re developing with EMF it is a common task to separate the model into multiple files along with enabling the user to create crosslinks between them. This works out-of box if the files are located inside the same project. However, in some cases for larger models it is simply not enough. It can be useful to be able to define reusable (and versioned) packets of models and enable the user to define a configuration which determines the imported model packages. Sounds familiar? The same functionality exists in PDE. Maybe it can be reused for non-java purposes.
Tag Archives: java
Short story of the day
Never, I mean never write code like this:
[cc_java]
boolean isRel1 = false, isRel2 = false;
if((isRel1 = element1 instanceof IRelation)
|| (isRel2 = element2 instanceof IRelation)){
…
}
[/cc_java]
Especially do not use such code in comparators. If not at first try, than later it will mess up things badly, as you rely on the variable, that will not be set because of the evaluation optimization.
This fact cost me three or four hours today…
A better solution (for those who look for usable code snippets):
[cc_java]
boolean isRel1 = element1 instanceof IRelation;
boolean isRel2 = element2 instanceof IRelation;
if(isRel1 || isRel2) {
…
}
[/cc_java]
Mi a hiba a kódban? #2
A [intlink id="1249" type="post"]múltkori, nagy sikerű írásom[/intlink] hatására Tompika küldött nekem egy hasonlóan izgalmas problémát.
[cc_java]while (true) {
Process p = Runtime.getRuntime().exec(“macska”);
p.waitFor();
}[/cc_java]
A kód eredeti, Tompika kedvenc állatait, a macskákat felemlegetve. A kód elméletben a következőt kellene, hogy csinálja: az első sor a [cci_java]p[/cci_java] referenciával elérhető módon indít egy processzt; míg a második sor vár, amíg a processz fut.
Ha megnézzük a kapcsolódó JavaDoc kommenteket, ez így működőképes is lehet. Ezzel szemben futásidőben problémák léptek fel, amiket feltehetőleg a következő kódrészletre való kicserélés javított:
[cc_java]while (true)
{
Process p = Runtime.getRuntime().exec(“macska”);
p.waitFor();
p.getErrorStream().close();
p.getOutputStream().close();
p.getInputStream().close();
p.destroy();
}
[/cc_java]
A kódrészlet utolsó sora vicces. Idézném a Javadoc kommentet:
[cci_java]public abstract void destroy()[/cci_java]
Kills the subprocess. The subprocess represented by this [cci_java]Process[/cci_java] object is forcibly terminated.
Amit én úgy értelmeznék, hogy a függvényt meghívva explicite lezárom a Processt. Viszont állítólag nem így történik. Valaki tudja a magyarázatot? Kíváncsi lennék rá.
PS.: ha valaki hozzájut hasonló gyöngyszemekhez, és eljuttatja hozzám, szívesen közzéteszem.
Eclipse GEF wtf
Az előző kitérő után most térjünk vissza egy kis kocka témához. A minap érdekes felfedezést tettem, miközben véletlenül a GEF belső kódjába tévedtem debug közben. Alapvetően egyébként meg vagyok elégedve a GEF és általában az eclipse platform minőségével, ritka az az eset, hogy a fejemet fogom egy-egy megoldás láttán.
A következő kódrészlet ugyan működik és mivel a publikus api elrejti, az átlag fejlesztő nem találkozik vele, mégis érdemes rávetni egy pillantást. További szócséplés helyett következzék a kód, szerintem magáért beszél:
//AbstractEditPart.class
private Object[] policies;
//...
/**
* @see EditPart#installEditPolicy(Object, EditPolicy)
*/
public void installEditPolicy(Object key, EditPolicy editPolicy) {
Assert.isNotNull(key, "Edit Policies must be installed with keys");//$NON-NLS-1$
if (policies == null) {
policies = new Object[2];
policies[0] = key;
policies[1] = editPolicy;
} else {
int index = 0;
while (index < policies.length && !key.equals(policies[index]))
index += 2;
if (index < policies.length) {
index++;
EditPolicy old = (EditPolicy)policies[index];
if (old != null && isActive())
old.deactivate();
policies[index] = editPolicy;
} else {
Object newPolicies[] = new Object[policies.length + 2];
System.arraycopy(policies, 0, newPolicies, 0, policies.length);
policies = newPolicies;
policies[index] = key;
policies[index + 1] = editPolicy;
}
}
if (editPolicy != null) {
editPolicy.setHost(this);
if (isActive())
editPolicy.activate();
}
}
Akinek nem világos elsőre, kifejteném a problémát: láthatóan egy tömböt használ a java-ban alapértelmezésként elérhető "Map" funkcionalitásának a kiváltására. A tömb páros (és nulladik) helyén szereplő elem tárolja a kulcsot, az utána lévő páratlan helyen lévő elem az érték.
Minden elem hozzáadásakor dinamikusan növeli a tömb méretét, törléskor meg egyszerűen null-ra állítja a tömb megfelelő elemét. Ehhez még társul egy custom iterátor is, ami a tömb nem null elemeit listázza.
Őszintén nem értem a tervezési döntést, ami a tömb-alapú Map-hez vezethetett. A memóriaigénye a HashMap-nek nem sokkal több, és mivel jellemzően kis elemszámú esetek fordulnak elő, ez nem számottevő. A sebessége a HashMap-nek jobb, a "get" és "put" metódusok általános esetben konstans, de mindenképpen kevesebb a tömb végigjárásánál. Mindennek a tetejébe a fenti kód Map alkalmazásával kb. 3 sorra cserélhető, nem beszélve az osztály egyéb kódjáról, ami a tömböt piszkálja.
Egyetlen érthető mentségként csak arra tudok gondolni, hogy esetleg a kód korábban íródott, minthogy a java collections API-ba belekerült volna a Map. Ez viszont az 1.2-es verzióban történt meg, tehát elég régen. Nem tudom mennyi idős a GEF, így ezt nem tudom eldönetni.. Mindenesetre ez a kód nálam megütötte a WTF szintet.
Mi a hiba a kódban?
Volt ma egy szép debug köröm. Nagyon nem értettem, miért nem működik egy kód – ami ráaásul régebben (július végén) szépen ment, és azóta nem nyúltam hozzá, és elvileg a kapcsolódó libekben sem volt lényegi változás azóta.
Úgy gondolom, bemutatom a kódot, és felteszem a kérdést, látja-e más is a hibát benne.
[ccw_java]private ICoreNotificationObject notificationObject;
public void actionPerformed(ICoreNotificationObject notification) {
this.notificationObject = notification;
Display.getDefault().aSyncExec(new Runnable() {
public void run() {
String action = notificationObject.getActionType();
if (isOneOf(action, new String[] {
ICoreNotificationObject.TA_TRANSACTION_END,
ICoreNotificationObject.TA_UNDO_END,
ICoreNotificationObject.TA_SUBTRANSACTION_END})) {
updateGraph();
transactions.pop();
} else if (isOneOf(action, new String[] {…}){
//…
}
});[/ccw_java]
Még némi információ a kód működéséről: a kód egy eseményfigyelő osztály belsejében van, és a Runnable adatváltozásokat próbál követni, amely tranzakciókba van szervezve, ill. a tranzakciók során visszavonás események is érkezhetnek.
Na, kinek van tippje, mi lehet a hiba? Ha nincs tipp véges időn belül (előre nem specifikálnám), akkor majd megosztom a helyes megfejtést. Annyit mondok előre, hogy fejet falbaverős hiba
.
Update: először is helyesbítettem a kódot, mert sikeresen a javított változatot töltöttem fel.
A problémát az okozta, hogy az asyncExec() hívás indított egy új jobot, amit valamikor majd végrehajt. Csak közben visszaadja a vezérlést, és ezzel lehetővé teszi a rendszer számára, hogy felülírja a run() metóduson belül is használt notificationObject változót.
Az asyncExec() hívás syncExec()-re cserélése megoldotta a problémát, ugyanis az megvárja, hogy visszatérjen a meghívott thread.
Ez a hiba kifejezetten mocskos dolog, mert eredetileg működött, míg a környezet refactoringja előhozta a bugot…