Česky   |  Deutsch   |  English   |  Español   |  Français   |  Indonesia   |  日本語   |  한글   |  Polski   |  Português (BR)   |  Türkçe   |  中文   |  正體中文   |  Your Language  
PlanetNetbeans
Planet NetBeans ist eine Sammlung aller NetBeans-relevanten Gedanken aus der ganzen Blogosphäre.
JNBB - Joschs NetBeans Blog - June 19, 2009 10:31 AM
Forum: Das deutschsprachige NetBeans-Forum im neuen Gewand

Nur wenige Stunden ist es her, als das deutsche NetBeans Forum, plötzlich und unerwartet in einem komplett neuen Look erschien. "Smurfi" (Michael, auch hier mit einem DZone Interview), hat mal das komplette Forum auf den neuesten (und damit auch Sicherheits-) Stand gebracht.

Was natürlich bleibt, ist die große Vielfalt an Unterforen - nicht nur Java und die NetBeans IDE sondern auch zu PHP, JavaFX und Glassfish. Und die freundliche deutschsprachige Community, die das Vorhaben mit großem Spaß unterstützt!


Beste Grüße,
  Josch.

 

 

JNBB - Joschs NetBeans Blog - June 09, 2009 10:49 AM
BeanDev: Import und Export von Optionen

Mit der IDE Version 6.7 gibt es auch eine neue Platform Version 10. Und mit jedem Update der Platform kommt für die Entwickler auch die Arbeit (oder Freude) die RCP Entwicklungen mit neuen Features anzureichern. 

Eine sehr schöne Funktionailtät ist das neue Importieren und Exportieren von Optionen. Wie immer im deklarativen Ansatz und modulorientiert.

Man kann nun in der layer.xml festlegen, welche Optionen des eigenen Moduls vom Anwender zu exportieren sind. Diese darf er beliebig häufig in andere Programmversionen importieren. Das ist praktisch für Backups und für Programme auf verschiedenen Rechnern.

Nebenbei erhält man noch einen automatischen Import-Dialog für Moduloptionen, wenn man eine neue Version installiert. Die IDE macht es mit Version 6.7 schon vor.

Grundsätzlich ist es notwendig, dass man seine Optionen schon seit je her mit NbPreferences speichert, damit diese im config/* Ordner aufschlagen. Aber auch andere Speicherorte von Dateien wären möglich, soweit es Dateien sind, die als Ablageort von Eigenschaften verwendet werden. Was für das Dateien letztendlich sind, ist auch egal. Der Export zippt nur die Daten und interpretiert nichts. Dem Import ist auch herzlich egal, was in den Dateien stand.

Jeder Modul-Entwickler kann seine eigenen Export-Definitionen festlegen. In der layer.xml muss dazu ein Folder namens OptionsExport angelegt werden. In diesem Ordner muss ein Unterordner eingefügt werden, der die eigenen Export-Definitionen als File deklariert. Tiefere Unterordner (zur Gruppierung) werden ignoriert.

Hier ein kleines Beispiel:

<folder name="OptionsExport">
<!-- Meine Kategorie im Export -->
<folder name="Mapping">
<!-- Lokalisierte Darstellung der Kategorie-->
<attr name="displayName"
bundlevalue="de/sepix/mapping.options.Bundle#Category_Display_Name"/>
<!-- item -->
<file name="mapOpt">
<!-- Lokalisierte Darstellung des Export-Elements-->
<attr name="displayName"
bundlevalue="de/sepix/mapping.options.Bundle#Export_Display_Name"/>
<attr name="include"
stringvalue="config/Preferences/de/sepix/mapping/settings(.*?)\.properties"/>
</file>
</folder>
</folder>

Mit der obigen Konfig wird eine Export-Kategorie mit dem internen Namen Mapping angelegt. Dargestellt wird aber der Text aus dem ResourceBundle mit dem Schlüssel Category_Display_Name. Unter dieser Kategorie wird eine Export-Definition mit einem include deklariert. Es ist ein regulärer Ausdruck für Dateinamen.

Man darf übrigens auch per exclude Dateien ausschließen.

Grundsätzlich zeigt so schon die Platform im Export-Dialog die Kategorie mit der Exportdefinition an. Wenn aber keine Dateien existieren, die mit include/exclude gefunden werden sollten, ist der Bereich in Export-Dialog deaktiviert. Finden sich aber Dateien, kann der Anwender, diese auch zum Exportieren auswählen. 

So sieht das letztendlich aus:

Wählt man eine Datei zum Importieren aus, kann der Anwender nur die Kategorien auswählen, die tatsächlich in der ZIP-Datei liegen.

Weitere Informationen finden sich dazu im NetBeans Wiki.

Beste Grüße,
  Josch.

 

JNBB - Joschs NetBeans Blog - June 05, 2009 07:50 AM
IDE 6.7: Multilingual switch in's Angloamerikanische

Für viele im nichtenglischsprachigen Raum ist es eine Erleichterung, dass NetBeans 6.7 seit dem RC1 mehrsprachig ausgeliefert wird. D.h. für viele Sprachen sind schon die passenden Resource-Bundle-Dateien mitgeliefert. 

Welche Sprachen das sind, kann man mal im platform10/modules/locale Ordner nachprüfen. Französisch, Spanisch, Deutsch, Niederländisch, Polnisch, Russisch... alles dabei.

De Java-VM selbst gibt die Spracheinstellungen vor, die sie sich einfach aus dem Locale des Betriebsystems zieht. Java legt diese Information in den beiden System Properties user.language und user.country an.

Damit kommen wir schon zu einer Problemlösung. Manche wollen nämlich lieber mit der englischen Oberfläche arbeiten - aus Gewohnheitssache, oder weil man im Team mehrsprachig ist und sich auf eine Sprache geeinigt hat.

Man überschreibt einfach die Java-VM Vorgaben, in dem man in der der netbeans.conf den Parameter netbeans_default_options am Ende um folgende Optionen erweitert:

-J-Duser.country=US -J-Duser.language=en

Die netbeans.conf findet sich im etc-Ordner der NetBeans-Installation.

Beim Neustart von NetBeans 6.7 ist dann die komplette Oberfläche in englischer Sprache.

Beste Grüße,
  Josch.

JNBB - Joschs NetBeans Blog - June 04, 2009 09:08 AM
IDE 6.7: In Farbe und Bunt - Das Ausgabefenster wird erwachsen

Das Ausgabe (Output) Fenster der neuen NetBeans IDE 6.7 hat ein paar kleine Überraschungen parat. 

Augenfällig ist die Farbe bei einem Build. Ant-Target-Namen erscheinen nun in Hellgrau, Echo-Ausgaben in Schwarz und auch rote und grüne Zeilen sind zu finden. Wer sich mit der Platform-API beschäftigt und eigene Plugins schreibt kann auch selber farbige Ausgaben in das Output-Fenster bringen.

Allerdings werden ANSI-Konsolen Spiele immer noch nicht möglich sein ;-)

Für den IDE Endanwender gibt es aber noch ein paar zusätzliche Features: Man kann nun die Schriftgröße und die Schriftart verändern. Entweder per Kontextmenü (rechte Maustaste in das Ausgabefenster) oder Tastenkürzel: Strg+- verkleinert und Strg+= vergrößert. Und damit kommen wir zu einem Problem mit deutschem Tastaturlayout. Nur Strg+- funktioniert... (ändern lassen sich hier die Tastenkürzel nicht)

Über die Menüfunktion "Schrift wählen" kann eine gänzlich andere Schriftart (und Größe) gewählt oder auf die Standardschrift zurückgesetzt werden.

Aber richtig genial ist die neue Filter-Funktion (Strg+G). Mit ihr kann man die Ausgabezeilen einfach und schnell filtern. Strg+G -> Text eingeben, Enter und schon werden nur noch die Zeilen mit der beinhalteten Suchzeichenkette angezeigt. Auf Wunsch wird der Suchfilter auch als Regular Expression behandelt.

Man sieht also, dass auch in kleinen Dingen die IDE immer weiter entwickelt wird. Und gerade so kleine Features erleichtern einem das Leben wirklich.

Beste Grüße,
  Josch.

JNBB - Joschs NetBeans Blog - April 30, 2009 07:13 AM
IDE 6.7: Issue Tracker Modul in NetBeans

Die letzten Monate war ich etwas "Busy" und steckte in ein paar Projekten. Damit lag das Blog etwas brach. Aber so habe ich jetzt die Möglichkeit ein paar Dinge nachzuholen und die Schlagzahl etwas zu erhöhen.

Die neue Beta-Version von NetBeans 6.7 ist seit einigen Tagen endlich auf meiner Festplatte gelandet (viel zu spät, ich hätte mich schon mit den M-Kandidaten befassen müssen) und finde lauter schöne neue Dinge.

Aber auch Sachen, da bin ich etwas überrascht - nicht nur im Positiven. Wer versucht, das Issue-Tracker Modul mit NetBeans Issuezilla zu verknüpfen, muss feststellen, dass man - egal welche URL angegeben wird - immer nur mit Fehlermeldungen bei der Testverbindung bestraft wird.

Der Grund liegt an der Inkompatibilität zwischen Bugzilla und Issuezilla. Das Bugzilla-Plugin kann den Fork Issuezilla nicht bedienen. Schade eigentlich. Und m.E. etwas unverständlich, da doch gerade NetBeans Issuezilla verwendet.

 

Beste Grüße,
  Josch.

JNBB - Joschs NetBeans Blog - April 29, 2009 01:25 PM
IDE 6.7: Debuggen in Schönschrift - der Variable Formatter

Einer der richtig schönen neuen Features von NetBeans 6.7 (jetzt als beta zu erhalten) ist, Werte während des Debuggens mit konfigurierten Formatierungs-Befehlen anzuzeigen.

Ich programmiere zur Zeit einen DayPlanner und habe naturgemäß viel mit java.util.Calendar zu tun. Dessen toString()-Methode ist mehr als geschwätzig und hilft mir, für einen schnellen Überblick, wenig.

Also mal schnell: Extras -> Optionen -> Verschiedenes -> Java Debugger geöffnet. Mit [Add...] kann man eine neue Formatierung anlegen. Hier ein einfaches Beispiel für java.util.Calendar:

Wenn man nun den Debugger anwirft, zeigt mir NetBeans an einem Breakpoint alle Calendar-Variablen mit meinem neuen Formatter an:

Übrigens, das Symbol mit dem Schraubenschlüssel öffnet direkt aus dem Debugger-Fenster die Formatter-Optionen.

Beste Grüße,
  Josch.

JNBB - Joschs NetBeans Blog - April 28, 2009 02:07 PM
BeanDev: Softwareentwicklung in 10 Jahren und ein Video

Eine spannende Darstellung, wie die Entwicklung von NetBeans vorangeschritten ist, findet sich unter diesem Link.


Die Berechnung für den Code-Swarm wurde aus den CVS-History Daten von 1998 bis 2008 erstellt. Danach wurde das NetBeans Projekt in das Mecurial Repositiory verschoben.

Viel Spaß beim anschauen und zuhören...

Josch.

JNBB - Joschs NetBeans Blog - February 15, 2009 11:30 AM
Vorträge zum Swing Application Framework, BeansBinding und JPA

Am 12. Februar folgte ich einer Einladung zu der Java User Group Nürnberg. Ich wurde gefragt, ob ich ein wenig über NetBeans, dem Swing GUI Builder, BeansBinding und JPA erzählen könnte.

Nun ja, ein weites Feld könnte man da sagen. Für den Tag habe ich drei aufbauende Präsentationen und einiges an Demoapplikationen vorbereitet. Die Zeit von 18:00 Uhr bis 19:45 reichte natürlich nicht, aber es hat mich sehr gefreut, dass die Hälfte der ca. 40 Zuhöhrer bis halb Zehn durchgehalten haben.


Im ersten Teil zeigte ich das Swing Application Framework, mit dem man deutlich einfacher Swing Anwendungen von Null auf programmieren kann. Viele Standardprobleme von Swing (und Applikationen allgemein) werden von dem Framework abgefangen. Zum Beispiel Lifecycle-Management, Persistens von Applikations-Sessions, Task-Verwaltung, vereinfachte Bindung von Actions und Ressource-Injection.
Insbesondere die NetBeans-Integration wurde gezeigt. 

Der zweite Teil befasste sich mit dem BeansBinding Framework. In kleinen Beispielen (mit und ohne NetBeans-Integration) bekamen die Zuschauer ein Bild davon, wo BeansBinding sinnvoll einzusetzen ist und welche Arbeitserleichterung BeansBinding bietet. Insbesondere in der Problematik der Bindung von Swing-Komponenten mit Beans. Das BeansBinding Framework verinfacht solche Bindungen durch vorgefertigte Adapter, die über Factory-Methoden erzeugt werden können. Besonders mit JTables eine Vereinfachung.

Mit dem dritten Teil wurde zu dem Swing Application Framework und BeansBinding die Java Persistence API hinzugefügt. Ich war erstaunt, dass viele Teilnehmer sich gar nicht bewusst waren, dass JPA auch problemlos in Java Desktop Anwendungen eingesetzt werden kann. Die meisten gingen davon aus, dass man unbedingt einen Server-Container (Glassfish, JBoss, ...) dafür benötigt.
Ich gab noch mal einen winzigen Überblick von JPA und zeigte dann, wie BeansBinding mit JPA und Swing-Komponenten realisiert wird. In Demos wurde eine kleine CRUD-Anwendung aufgebaut.

Ich möchte mich nochmal herzlich für die vielen Fragen und die Aufmerksamkeit bedanken. Es hat mir viel Spaß bereitet den Abend in Nürnberg sein zu dürfen.

Hier noch die Links zu den Präsentationen als PDF:


Beste Grüße,
  Josch.

 

JNBB - Joschs NetBeans Blog - February 07, 2009 02:03 PM
NetBeans Platform Training in Ulm

Die letzten zwei Tage war ich mit Geertjan Wielenga und Toni Epple als Trainer an der Universität Ulm.

Mit dem NetBeans Platform Certified Training unterstützen wir die Studenten an der Universität mit einem Einstieg in die Softwareentwicklung mit der NetBeans Platform. Am Freitag erhielten wir Verstärkung von Lorenz Weber. Er ist Student der Universität Würzburg und hatte vor einiger Zeit das Certified Training erfolgreich absolviert. Er zeigte den Studenten sein Batch Refactoring Projekt für die NetBeans IDE

Ganz besonders freuten wir uns über den Besuch von David Botterill, dem Technology Manager der Sun Campus Ambassadors. Vielleicht bringt er unsere Trainings ja über die Campus Ambassadors in die Universitäten? Das wäre fantastisch :-)

Training Ulm

An dieser Stelle möchte ich mich auch für die hervorragende Führung durch die wunderschön Stadt Ulm bedanken. Der Organisator des Kurses, Ph. D. Guido de Melo, gab uns einen unvergesslichen Einblick in die Geschichte von Ulm mit seinen kuriosen und staunendswerten Gebäuden und Eckchen.

Folgendes Programm haben wir den Teilnehmern geboten:

  • Tag 1
    • Präsentation 1: Einführung in den Kurs (Geertjan)
    • Präsentation 2: Einstieg in die NetBeans Platform (Toni)
    • Präsentation 3 + Workshop: Modular Systems & Lookup API (Geertjan)
    • Präsentation 4: System FileSystem (Toni)
    • Präsentation 5: Window System (Aljoscha)
  • Tag 2
    • Präsentation 1: Nodes und Explorer Views (Aljoscha)
    • Präsentation 2 + Workshop: Visual Library (Toni)
    • Präsentation 3: Portierung einer Anwendung zur NetBeans Platform (Aljoscha)
    • Präsentation 4: Ideen für Studenten Projekte (Alle)
    • Präsentation 5: Beispiel eines Studenten Projektes: Batch Refactoring (Lorenz)
    • Präsentation 6: Zusammenfassung des Kurses (Geertjan)

Von hier aus noch mal viele Grüße an die Uni Ulm und den Teilnehmern des Kurses. Wir Trainer hatten viel Spaß und hoffen, dass es Euch gefallen hat!

 

Beste Grüße,
  Josch.

Bernhard's Weblog - January 27, 2009 11:08 PM
Using unit-testing code using Netbeans' Repository class

This blog is about unit-testing using netbeans Repository. I'm using the solution described below on Netbeans 6.5.

In my netbeans plugin-project I have code like


        Repository r = Repository.getDefault();
        FileSystem fs = r.getDefaultFileSystem();
        FileObject root = fs.getRoot();
        FileObject baseFolder = FileUtil.createFolder(root, "nb-xpath/data");
        final String foName = "history.xml";
        FileObject historyFileObject = baseFolder.getFileObject(foName);
...
        File file = FileUtil.toFile(historyFileObject);

When you try to unit-test this code, the file instance is null.

Thus I read UsingFileSystemsMasterfs, and I installed the masterfs in the UnitTestLibary. By selecting "UnitTestLibraries|Add UnitTest Dependency", selecting "Show Non-API Modules, and searching for "masterfs", and selecting the Module "Master Filesystem".

The nbproject/project.xml results :


...
            </module-dependencies>
            <test-dependencies>
                <test-type>
                    <name>unit</name>
                    <test-dependency>
                        <code-name-base>org.netbeans.modules.masterfs</code-name-base>
                        <compile-dependency/>
                    </test-dependency>
                </test-type>
            </test-dependencies>
            <public-packages/>
...

But this does not help! The file instance is still null!

After examing the Repsitory sources, I came to the conclusion that if there is no Repository registered in META-INF/services, the MemoryFileSystem is used. Thus I need a way to register some repository class.

Thanks to InitializationOfDefaultLookup, MockServices can help.

I wrote a simple Repository implementation, and register it as Repository implementation to use:

The Repository implemenation for my test-case:


    public static final class MyRepositoryIOTempDir extends Repository {

        private static final long serialVersionUID = 20090127L;

        public MyRepositoryIOTempDir() {
            this(new LocalFileSystem());
        }

        private MyRepositoryIOTempDir(LocalFileSystem lfs) {
            super(lfs);
            // make the java.io.tmpdir the root of this lfs
            final String tmpDir = System.getProperty("java.io.tmpdir");
            assertNotNull(tmpDir);
            File rootFile = new File(tmpDir);
            assertTrue(rootFile != null);
            assertTrue(rootFile.canRead());
            assertTrue(rootFile.canWrite());
            assertTrue(rootFile.isDirectory());
            try {
                lfs.setRootDirectory(rootFile);
            } catch (Exception ex) {
                AssertionError ae = new AssertionError("Cannot set root directory");
                ae.initCause(ex);
                throw ae;
            }
        }
    }

My test-case setUp registers this class like:


    @Before
    public void setUp() {
        MockServices.setServices(MyRepositoryIOTempDir.class);
        // assert that for lookup(Repository.class) MyRepositoryIOTempDir is returned
        final Repository repositoryInstance = Lookup.getDefault().lookup(Repository.class);
        assertTrue(repositoryInstance instanceof MyRepositoryIOTempDir);
    }

Now my tests can run the above code, and the file instance is not null!

Now, that I removed the Master Filesystem dependency from the Unit Test Libraries, my test-cases still work, so it seems to me that when you use File instances retrieved via Repository, you don't need the masterfs test dependency, so far.

Have fun!

JNBB - Joschs NetBeans Blog - January 21, 2009 04:30 PM
Termin: BeansBinding, JPA und Matisse - NetBeans@JUG-Nürnberg

Nicht nur Werbung in eigener Sache, sondern ein Hinweis auf ein doch bestimmt sehr spannendes Thema. 

Am 12. Februar bin ich in Nürnberg und bringe, den hoffentlich zahlreich anwesenden Zuhörern, die Technologien Beans Binding, Java Persistence API und Matisse im Zusammenhang mit NetBeans, nahe.

Es gibt ein wenig Theorie über Beans Binding und JPA, und ordentlich Praxis, wie das in NetBeans umgesetzt wird. Insbesondere, die Anwendung des Swing GUI-Designer mit Beans Binding wird demonstriert.

Wer am 12. Februar 2009 von 18:00 bis 19:45 Uhr Zeit hat, melde sich doch bitte kurz formlos über die folgende Mail an: info <at>  jug-ern.de

Der Veranstaltungsort ist:

ING DiBa,
Allgemeine Deutsche Direktbank AG,
Südwestpark 97,
90449 Nürnberg, Deutschland

Weitere Infos zur Java User Group Erlangen/Nürnberg finden sich auf der Homepage source-knights.de


Da der Termin rum ist, hier die Präsentationen.


Beste Grüße,
  Josch.

Seapegasus Blog - January 21, 2009 12:42 PM
Kostenloses Java-EBook (Neue Auflage)

Die achte Auflage des Java-Kurses "Java ist auch eine Insel" ist draußen: Ein praxisorientiertes Buch nicht nur für diejenigen, die beim Brennball in der Schule immer als letztes gewählt wurden! (He... woher weiss der das?)

Das Buch erwähnt (empfiehlt) auch IDEs (z.B. auch den NetBeans GUI Builder), bleibt aber insgesamt Tool-neutral. Das hat den Vorteil, dass man das Gelernte überall einsetzen kann -- und welchem IDE-Menüpunkt das Beschriebene eventuell entspricht, findet man recht schnell raus. Anscheinend ist geplant, die achte Auflage nach und nach für JDK 7 zu aktualisieren.

Für 50 Euro bekommt man das komplette Buch auf toten Bäumen ausgedruckt -- plus eine DVD mit JDK 6, NetBeans, Eclipse, Beispielprogramme, Aufgaben und Lösungen, und sogar (hä?) ;) ein paar C/C++ Openbooks.
Das Javainsel-Ebook ist kostenlos online, und es gibt's es auch als 14MB Download zum Mitnehmen.

JNBB - Joschs NetBeans Blog - January 17, 2009 09:38 AM
Poll: Verwendet Ihr Beans Binding in der NetBeans IDE

Wade Chandler vom NetBeans Dream Team hat eine Umfrage gestartet, um abzufragen wer BeansBinding (JSR-295) in der NetBeans IDE verwendet.

Poll: Do you use Beans Binding in the NetBeans IDE?

In der Umfrage gibt es auch eine Kommentar-Funktion. Wäre schön wenn Ihr schreiben könntet, warum Ihr BeansBinding (oder auch nicht) verwendet.


Unterstützung für BeansBinding gibt es seit Version 6.0 in NetBeans - insbesondere in dem Projekttyp "Java Desktop Application" mit dem Swing Application Framework und der Java Persistence API.


Tutorials und Screencasts zu BeansBinding (wer es noch nicht nutzt):
http://www.netbeans.org/kb/60/java/gui-binding.html
http://www.netbeans.org/kb/60/java/swing-app-beans-binding-screencast.html
http://wiki.netbeans.org/NBDemoTicker
http://www.bestechvideos.com/2008/10/29/netbeans-tv-swing-application-framework-and-beans-binding
http://wiki.netbeans.org/BestPracticesWithJPAAndBeansBinding
http://blogs.sun.com/roumen/entry/swing_application_framework_swing_databinding

Beste Grüße und vielen Dank für Eure Hilfe!
Josch.

JNBB - Joschs NetBeans Blog - January 15, 2009 11:14 AM
IDE: NetBeans räumt ab!

Dieses Jahr geht es richtig ab, bei der Preisvergabe.

NetBeans hat gleich in mehreren Disziplinen gewonnen.

Development Tool

Winner: NetBeans Platform

Development Utilities

Winner: NetBeans Profiler

Wireless/Mobile

Winner: NetBeans Mobility Pack for Connected Device Configuration (CDC) 5.5

Java Tool

Winner: NetBeans IDE

Open Source

Winner: NetBeans

Beste Grüße,
  Josch.

JNBB - Joschs NetBeans Blog - January 13, 2009 01:49 PM
BeanDev: GUI Beans, ein Custom Property-Editor

In den letzten drei Teilen nahm das JImagePanel durchaus schöne Formen an. Nur fehlt mir noch ein Editor für das Hintergrundbild. Die einfachste Möglichkeit per Dateipfad ein Bild auszuwählen möchte ich hier besprechen.

Vorweg auch der größte Nachteil: Der Pfad ist natürlich unabhängig vom Programm. Das ist für eine Anwendung, die bei einem Nutzer installiert wird, nicht praktikabel. Zu einem späteren Zeitpunkt zeige ich aber die Möglichkeit, Bilder aus dem Projekt zu wählen. Hier im letzten Teil geht es aber zunächst nur um den Custom Editor. Eine weitere Besonderheit ist der Typ des Property: ImageIcon. Der PropertyEditor verwaltet aber nur eine Pfadangabe. Bestimmt wäre es einfacher das Property von Anfang an als Pfadangabe zu betrachten, aber im realen Leben ist es leider nicht immer zu vermeiden, dass man solche Sonderfälle zu meistern hat. Deswegen auch hier ein komplizierteres Beispiel.

Unser JavaBean JImagePanel hat das Property backgroundImage und erwartet den Typ ImageIcon. Da man dem GUI-Design-Anwender nicht zumuten kann, das Bild jedes mal neu zu zeichnen, müssen wir eine Möglichkeit bieten, dieses von der Festplatte zu laden. Natürlich könnte der Anwender auch den Pfad als Text eingeben, aber das soll ja bequem gehen.

Zunächst brauchen wir wieder einen PropertyEditor für ImageIcons. Strg+N -> Kategorie "JavaBeans Objects" -> Property Editor. Name: ImageIconPropertyEditor.

Die Java-Klasse wird dann folgendermaßen erweitert: 

package org.sepix.beandemo;

import java.beans.*;
import javax.swing.ImageIcon;

public class ImageIconPropertyEditor extends PropertyEditorSupport {
private String pathToImage;

@Override
public String getJavaInitializationString() {
if ( pathToImage != null ) {
return "new javax.swing.ImageIcon (\"" + pathToImage.replace ('\\', '/') + "\")";
}
return "null";
}

@Override
public String getAsText() {
ImageIcon ii = (ImageIcon) getValue();
if ( ii == null ) return null;
return pathToImage;
}

@Override
public void setAsText(String text) throws IllegalArgumentException {
pathToImage = text;
setValue(new ImageIcon (pathToImage));
}
}

Im Gegensatz zum letzen PropertyEditor wird hier getTags() nicht überschrieben. Damit kann ein Anwender auch direkt einen Pfad in der Wertfeld des Properties eingeben. Die Methode getAtText() und setAsText(String) konvertieren eine Pfadangabe in ein ImageIcon. Eingehende Prüfungen auf korrekte Pfadangaben ignoriere ich erstmal (Nur Backslash wird in Slash gewandelt)

Der Property-Editor wird wieder (per BeanInfo-Editor), in der JImagePanelBeanInfo-Klasse registriert.

Gewünscht ist aber die Auswahl per Custom Editor. Dazu erstelle ich zunächst ein Customizer Panel. Strg+N -> Kategorie "JavaBeans Objects" -> Customizer. Der Name ist: ImageIconCustomizer.

So sollte der minimale Customizer dann aussehen:

package org.sepix.beandemo;

public class ImageIconCustomizer extends javax.swing.JPanel implements java.beans.Customizer {
private Object bean;

public ImageIconCustomizer() {
initComponents();
}
public void setObject(Object bean) {
this.bean = bean;
}
private void initComponents() {
setLayout(new java.awt.BorderLayout());
}
}

Im Design-Modus habe ich den Customzier in dieser Art aufgebaut:

Ansonsten ist noch keine Funktion hinterlegt. Der Customizer muss aber nun im PropertyEditor hinterlegt werden:

public class ImageIconPropertyEditor extends PropertyEditorSupport {
private String pathToImage;
private ImageIconCustomizer customizer;
public ImageIconPropertyEditor () {
customizer = new ImageIconCustomizer();
}

und am Ende folgende Methoden hinzufügen:

    @Override
public Component getCustomEditor() {
return customizer;
}

@Override
public boolean supportsCustomEditor() {
return true;
}

Das war es auch schon erstmal. "Clean & Build" des Projektes. Im Test-JFrame das alte Bean entfernen und aus der Bean-Kategorie erneut hinzufügen. Jetzt kann man im Property backgroundImage den Button [...] anklicken und bekommt den selbst programmierten Customizer angeboten. Zusätzlich kann man natürlich über die ComboBox alle weiteren NetBeans-Editoren auswählen.

Die Kommunikation zwischen PropertyEditor und Customizer muss über PropertyChangeEvents erfolgen. Wenn der Customizer etwas ändert, muss dieser ein Event feuern. Der PropertyEditor setzt ohne zu zögern den Wert in das Bean ein. Zwar kann der Anwender den Customizer-Dialog abbrechen, aber um das Zurücksetzen des alten Wertes kümmert sich dann NetBeans selber.

Also erweitere ich schon mal den ImageIconPropertyEditor, um das Event-Handling:

    public ImageIconPropertyEditor() {
customizer = new ImageIconCustomizer();
customizer.addPropertyChangeListener(new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent evt) {
if ( "backgroundImage".equals(evt.getPropertyName()) ) {
setAsText (customizer.getPath());
}
}
});
}

Die Methode getPath() existiert im Customizer noch nicht, wird aber gleich hinzugefügt. Ich lege dazu das Feld path an, dazu noch setPath und getPath und weitere Hilfsmethoden zum Anpassen der Customizer-GUI-Ausgabe:

public class ImageIconCustomizer extends javax.swing.JPanel implements java.beans.Customizer {

private Object bean;
private String path;

// ... Constructor und setObject bleibt wie es ist

public void setPath (String path) {
this.path = path;
updateEditor();
updateImage();
}

public String getPath () {
return path;
}

private void updateUserChange (String newPath) {
path = newPath;
firePropertyChange("backgroundImage", null, null);
updateEditor();
updateImage();
}

private void updateEditor() {
jTF_Path.setText (path != null ? path : "");
}

public void updateImage() {
jLB_IconViewer.setIcon(path != null ? new ImageIcon(path) : null);
}

Die obige Methode updateUserChange() kümmert sich um das Feuern des Events und um die passende Aktualisierung im Customizer-Dialog. Damit der Dialog überhaupt auf Benutzereingaben reagieren kann, setze ich einfach zwei Action-Listener an das Textfeld und den Button für die Pfadauswahl. In NetBeans ist das sehr einfach gemacht. Im Designer des Customizers das Textfeld markieren -> Property Window -> View Events -> In das Wertfeld von actionPerformed klicken -> jTF_PathActionPerformed mit [Enter] bestätigen. NetBeans wechselt sofort in den Quelltexteditor und ich fülle die neue Methode aus:

    private void jTF_PathActionPerformed(java.awt.event.ActionEvent evt) {                                         
updateUserChange (jTF_Path.getText());
}

Der gleiche Vorgang mit dem Button. Im Designer des Customizers den Button markieren -> Property Window -> View Events -> In das Wertfeld von actionPerformed klicken -> jPB_ChooserActionPerformed mit [Enter] bestätigen. NetBeans wechselt erneut in den Quelltexteditor und ich fülle die zweite Methode so aus:

    private void jPB_ChooserActionPerformed(java.awt.event.ActionEvent evt) {
JFileChooser loadFile = new JFileChooser (path);
loadFile.setFileFilter(new FileFilter() {
@Override
public boolean accept(File f) {
String name = f.getName().toLowerCase();
return name.endsWith(".jpg")
|| name.endsWith(".png")
|| name.endsWith(".gif");

}
@Override
public String getDescription() {
return "Grafikdateien";
}
});
int result = loadFile.showOpenDialog(this);
if ( result == JFileChooser.APPROVE_OPTION ) {
updateUserChange (loadFile.getSelectedFile().getAbsolutePath());
}
}

(ggf. muss man nun die Imports korrigieren. Rechte Maustaste -> Fix imports...).

Das war es auch schon. Der Customizer funktioniert nun und übergibt die Benutzereingaben an den PropertyEditor. Damit kann der Customizer mit dem Editor schon erheblich mehr, als das eigentlich Bean. Denn JImagePanel macht ja nun überhaupt nichts mit den mühevoll übertragenen Daten.

Aber auch das ist schnell erledigt. Einfach folgende Methode in JImagePanel einfügen:

    @Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
ImageIcon background = getBackgroundImage();
if ( background != null ) {
Dimension s = getSize();
int x = 0;
int y = 0;
int width = background.getIconWidth();
int height = background.getIconHeight();
switch ( getAppearance() ) {
case SCALE: {
width = s.width;
height = s.height;
break;
}
case CENTER: {
x = (s.width/2) - (width/2);
y = (s.height/2) - (height/2);
break;
}
}
g.drawImage(background.getImage(), x, y, width, height, this);
}
}

Die Methode berücksichtigt allerdings nicht die Breite eines eingefügten Border-Objektes. Das ist aber bestimmt einfach nachzuprogrammieren.

 

Mit diesem kleinen Vierteiler sollte es nun möglich sein, beliebig komplexe GUI JavaBeans in NetBeans integrieren zu können. Inklusive eigenen Property-Editoren. Über Feedback würde ich mich natürlich freuen.

Beste Grüße,
  Josch.


Teil 1 | Teil 2 | Teil 3 | -> Teil 4


JNBB - Joschs NetBeans Blog - January 13, 2009 01:00 PM
BeanDev: GUI Beans, eigene Property-Editoren

In diesem dritten Teil geht es ans Eingemachte. Das farbenfrohe Klicken ist vorbei, es wird programmiert.

Im vorherigen Teil habe ich den BeanInfo-Editor vorgestellt und zu der JImagePanel-Klasse nur wenige Eigenschaften freigeschaltet.

Die zwei neuen Eigenschaften backgroundImage und appearance bieten aber nur einen Komfort von zweifelhaften Wert an. NetBeans bietet keinen Editor für ImageIcons an und schon gar nicht für die selbst programmierte Enumeration Appearance.

Ich fange mal mit Appearance an, weil ich dafür erstmal nur die Möglichkeiten von NetBeans ausschöpfen will. Da NetBeans (leider noch nicht) selbst enum-Klassen erkennt, muss ich selber ran und einen eigenen Editor dafür programmieren. Einmal geht es dafür noch in den Datei-Assistenten: Strg+N -> Kategorie "JavaBeans Objects" -> Property Editor. Name soll sein: "AppearancePropertyEditor". Die erzeugte Klasse ist klein und bietet nicht viel.

Nun wird folgendes in die Klasse programmiert:

package org.sepix.beandemo;

import java.beans.*;
import org.sepix.beandemo.JImagePanel.Appearance;

/**
* @author Aljoscha Rittner
*/
public class AppearancePropertyEditor extends PropertyEditorSupport {
@Override
public String[] getTags() {
return new String[] {"SCALE", "CENTER"};
}

@Override
public void setAsText(String text) throws IllegalArgumentException {
String input = text != null ? text.toLowerCase() : "";
if ( input.equals("scale")) {
setValue(JImagePanel.Appearance.SCALE);
} else if ( input.equals("center")) {
setValue(JImagePanel.Appearance.CENTER);
} else {
setValue (null);
}
}

@Override
public String getJavaInitializationString() {
JImagePanel.Appearance app = (Appearance) getValue();
if ( app == null ) return "null";
switch (app) {
case SCALE: {
return "org.sepix.beandemo.JImagePanel.Appearance.SCALE";
}
case CENTER: {
return "org.sepix.beandemo.JImagePanel.Appearance.CENTER";
}
}
return "";
}
}

Was da passiert ist relativ einfach. Die Methode getTags() liefert alle möglichen "Human readable" Werte für appearance. Die Methode setAtText "übersetzt" diese Zeichenketten in echte Werte vom Typ JImagePanel.Appearance. Und letztendlich bastelt die Methode getJavaInitializationString() aus dem Wert einen Java-Befehl, der direkt in das Programm vom Form-Designer eingefügt wird.

Woher weiß nun NetBeans, dass ich nun einen neuen Editor für Appearance habe? Noch gar nicht. Dafür rufe ich wieder die Klasse JImagePanelBeanInfo auf, wechsele in den Desinger und markiere das Property appearence. Auf der rechten Seite kann ich nun die Eigenschaft "Property Editor Class" setzen. In meinem Fall ist das der Name: org.sepix.beandemo.AppearancePropertyEditor.class - es muss also der vollqualifizierte Name der Editor-Klasse sein (mit Package) und dem Anhängsel .class (das ist übrigens ein Pseudofeld der Java-Programmiersprache für Object-Klassen).

Nun erneut ein "Clean & Build" des Projektes. Das Test-JFrame öffnen und das alte JImagePanel rauslöschen und erneut über die Palette hinzufügen.

Und tatsächlich: appearance hat nun eine Combobox mit zwei Werten.

Wenn ich einen auswähle, wird im Guarded Block des Quelltextes auch der passende Programmbefehl eingefügt:

jImagePanel1.setAppearance(org.sepix.beandemo.JImagePanel.Appearance.CENTER);

Nur noch eine Sache stört mich. Das Property appearance zeigt zunächst immer null an. Wenn man aber JImagePanel anpasst und das Feld appearance initialisiert, dann ist das Problem auch verschwunden:

public class JImagePanel extends JPanel {

public static enum Appearance {SCALE, CENTER};

private ImageIcon backgroundImage;
public static final String PROP_BACKGROUNDIMAGE = "backgroundImage";
private Appearance appearance = Appearance.SCALE; // <- Standard
public static final String PROP_APPEARANCE = "appearance";
...

Super. Eine Eigenschaft haben wir schon auf die Sprünge geholfen. Nun geht es um das Hintergrundbild. Dazu mehr im nächsten Teil!


Teil 1 | Teil 2 | -> Teil 3 | Teil 4


JNBB - Joschs NetBeans Blog - January 13, 2009 12:27 PM
BeanDev: GUI Beans, der BeanInfo Editor

Im vorherigen Teil habe ich eine Bean-Klasse erstellt, die zwei zusätzliche Eigenschaften zu einem JPanel hinzufügt. Zum einem Appearance (Darstellung) und backgroundImage (Hintergrundbild). 

In diesem Teil möchte ich den BeanInfo-Editor verwenden, um diese Eigenschaften in gewünschter Form in NetBeans darzustellen und vor allem viele andere Properties (von JPanel) abschalten.

Zunächst wähle ich die JImagePanel-Klasse im Explorer aus, rechte Maustaste und rufe im Kontextmenü den "BeanInfo Editor..." auf. Da noch keine BeanInfo-Klasse exisitiert, fragt NetBeans noch höflich nach, ob diese Klasse erzeugt werden soll. Das bestätige ich und erhalte damit eine wirklich monströs große Klasse mit dem Namen JImagePanelBeanInfo. Im BeanInfo-Editor habe ich zwei Views zur Verfügung: Source und Designer. Source zeigt mir den Java-Quelltext und Designer eine hierarchische Darstellung von Properties, Event Sources und Methods. Natürlich auch alle Elemente aus JPanel und den Superklassen. Zwischen den ganzen Properties verstecken sich auch die neuen Eigenschaften appearance und backgroundImange.

Um die spätere Darstellung der Eigenschaften im NetBeans-GUI-Designer übersichtlich zu halten, deaktiviere ich einfach erstmal alle Properties und Methods. Also alle Property-Knoten markieren, rechte Maustaste -> "Toggle Include". Das selbe mit den Methoden durchführen. 

Wenn man nun speichert und in die Source-Ansicht wechselt, sieht man, dass der Quelltext doch deutlich kürzer geworden ist. Zurück in der Design-Ansicht markiere ich in der Kategorie Properties die Eigenschaften appearance, backgroundImage und border -> rechte Maustaste -> "Toggle Include".

Damit sollten nur noch die drei Eigenschaften und alle Ereignisse (Event Source) aktiviert sein.

Jetzt kommt die Stunde der Wahrheit. Vom Projekt ein Clean&Build machen. Mit der rechten Maustaste auf JImagePanel klicken -> Tools -> Add to Palette... In dem Dialog nun die Beans-Kategorie auswählen (so finden wir unser Bean einfacher wieder).

Nun erzeuge ich ein neue JFrame zum testen. Strg+N -> Kategorie "Swing GUI Forms" -> JFrame Form.

Der Designer öffnet sich auch sofort und aus der Palette ziehe ich (aus der Kategorie Beans) mein persönliches JImagePanel-Bean. Und wie man am Eigenschaftsfenster sieht, werden nur drei Properties angezeigt. So wie ich das im BeanInfo-Editor festgelegt hatte.

Für das Property "border" gibt es schon einen schönen Editor, den ich über den Button [...] aufrufen kann. Für appearance und backgroundImage sieht die Sache noch etwas Mau aus.

Im dritten Teil zeige ich, wie man für die zwei neuen Eigenschaften eigene Editoren hinterlegen kann.


Teil 1 | -> Teil 2 | Teil 3 | Teil 4


JNBB - Joschs NetBeans Blog - January 13, 2009 10:33 AM
BeanDev: GUI Beans - Zurück zu den Wurzeln

Manchmal kann man schon vergessen, warum NetBeans überhaupt den Teil "Beans" im Namen trägt. Dabei ist es gerade in der NetBeans-IDE recht einfach im Form-Editor eigene Java-Bean-Komponenten einzufügen.

Sicherlich, im Entwicklungsprozess von Version 5.5 zu 6.5 ist ein praktisches Werkzeug, zwischenzeitlich in Version 6.0, unter die Räder gekommen: Der BeanInfo-Editor. Dabei hat gerade dieses Tool in Vergangenheit viel Handarbeit abgenommen, die bei der Pflege von BeanInfo-Klassen auf einem so zukommen.

Auch die aktuelle Version in 6.5 lässt noch einige Wünsche offen, aber es ist wieder schön bequem geworden zu seinen POJO-Klassen BeanInfo-Deskriptoren hinzuzufügen.

NetBeans hat für Java-Bean-Komponenten einiges an Assistenten bereitgestellt. Diese Assistenten stellen i.d.R. nur einfache Java-Templates zur Verfügung. Mit New File... -> Kategorie "JavaBeans Objects" kommt man an die Vorlagen heran.

  • JavaBeans Component
    Erzeugt eine einfache serialisierbare Java-Klasse mit PropertyChangeSupport und dazu einem Beispiel-Property.
  • BeanInfo und BeanInfo w/o Icon
    Erstellt eine BeanInfo-Klasse (mit oder ohne Icon-Abfrage), die sich auf eine Java-Klasse bezieht. Die BeanInfo-Klasse arbeitet mit einem MultiView-Editor für den Java-Quelltext und einem Designer.
  • PropertyEditor
    Dieser Assistent erzeugt eine sehr einfache Vorlage eines eigenen Eigenschafts-Editor, der für bestimmte Properties festgelegt werden kann. Die Klasse erweitert nur PropertyEditorSupport.
  • Customizer
    Mit dem Customizer-Assistenten wird ein GUI-Panel erstellt, der als besonderer grafischer Editor über den PropertyEditor zurückgegeben werden kann. NetBeans zeigt dann im Eigenschafts-Fenster für das Property einen zusätzlichen Button an, um diesen Customizer aufzurufen.
    Der Customizer ist ein JPanel und implementiert das Interface java.beans.Customizer.

Als Beispiel (wie man die Klassen verwendet), möchte ich hier ein JPanel-GUI Bean vorstellen,

  • welches ein Hintergrund-Bild darstellt,
  • dieses Bild skaliert oder zentriert darstellt
  • und im GUI-Editor weitere Komponenten aufnehmen darf.

Für das Bean JImagePanel nehme ich nicht den Assistenten, um eine JavaBeans Component zu erzeugen. Einfach nur Strg+N -> Java Class auswählen und die Klasse "JImagePanel" nennen. Nun das Panel noch von JPanel ableiten, Fix Imports..., und schon haben wir ein einfaches Java Bean.

 

import javax.swing.JPanel;
public class JImagePanel extends JPanel {

}

Ein sehr einfaches Java-Bean. Um das Hintergrundbild im Panel anzeigen zu können, muss dieses als Property hinterlegt werden. Den Eingabecursor in den Body der Klasse setzen, Alt+Einf eintippen und im Kontextmenü "Add Property..." auswählen.

Im Dialog des Assistenten einfach den Namen auf "backgroundImage" setzen, für Type nehme ich den Browse-Button und gebe im Filter einfach ImageIcon ein und übernehme die Klasse javax.swing.ImageIcon. Außerdem sollte die Eigenschaft "private" sein und ein Haken bei "Bound" sollte noch gesetzt werden. Damit wird auch automatisch "Generate Property Change Support" aktiviert.

 

Da das Hintergrundbild entweder skaliert oder zentriert werden soll, käme da noch eine zusätzliche Eigenschaft hinzu. Man könnte ein boolean verwenden, nur um das Skalieren einstellbar zu machen. Bei false könnte man das Bild dann zentrieren. Das wäre aber schlecht erweiterbar. Also verwende ich eine Enumeration.

Ich füge der Klasse einfach folgende Zeile hinzu:

public static enum Appearance {SCALE, CENTER};

Dann erzeuge ich das passende Property hinzu: Alt+Einf -> "Add Property...". Den Namen auf "appearance" setzen, für Type den enum-Namen "Appearance", Zugriff wieder "private" und [X] Bound. Dann wieder [Ok] anklicken.

Jetzt ist in der (anfänglich einfachen) Java-Klasse eine Menge los. Zwei Properties sind hinzugekommen und PropertyChangeSupport. Außerdem darf man dem Bean nun Listener hinzufügen und entfernen. Ein richtig schönes Bean also!

Im zweiten Teil geht es an den BeanInfo-Editor, um nun die Darstellung der Eigenschaften in NetBeans zu steuern.


-> Teil 1 | Teil 2 | Teil 3 | Teil 4


JNBB - Joschs NetBeans Blog - January 12, 2009 01:39 PM
Artikel: NetBeans 6.5 in Übersicht

Toni Epple, Mitglied des NetBeans Dream Team, hat einen schönen Beitrag zu den Features von NetBeans 6.5 für das JavaMagazine geschrieben.

Es gibt auch eine gekürzte Online-Version in zwei Teilen im JAXenter:

Teil 1 - Alles rund um das Release 6.5 mit Unterstützung für PHP und JavaFX

Teil 2 - Wenn's mal nicht so läuft – Profiler & Debugger

Beste Grüße,
  Josch.

 

 

JNBB - Joschs NetBeans Blog - January 07, 2009 03:53 PM
Passed: Sun(tm) Certified Specialist for NetBeans(tm) IDE

Am 24. Dezember 2008 bekam ich von Sun schon mein Weihnachtsgeschenk: Eine eMail, dass ich den Sun(tm) Certified Specialist for NetBeans(tm) IDE Test bestanden hatte. Im neuen Jahr erhielt ich das Ergebnis auch noch schriftlich.

Laut Report habe ich 3 Fragen nicht richtig beantworten können und somit 95% als Score erreicht, 59% wären mindestens notwendig gewesen.

Trotz fast 10-jähriger Nutzung von NetBeans brachten mich einige Fragen ins Schwitzen - vielleicht weil ich schon so viele IDEs in den Fingern hatte... Ich bin mit dem Ergebnis aber doch sehr zufrieden.

Wer den Test selbst durchführen will, sollte grundlegende Kenntnisse in der Bedienung der IDE haben - auch im Web-Development- und Englisch können. Außerdem kann man sich mal folgende Ressourcen anschauen:

http://wiki.netbeans.org/CertExamStudyGuide

http://sites.google.com/site/netbeansscsni/


Beste Grüße und Frohes Neues Jahr,
  Josch.

JNBB - Joschs NetBeans Blog - December 25, 2008 09:46 AM
Frohe Weihnachten und ein glückliches Neues Jahr!

 

Ich wünsche allen Mitlesern, Freunden und Bekannten ein frohes Fest, fröhliche Weihnachten und einen guten Rutsch in das Neue Jahr!

Als frisch gebackenes NetBeans Dream Team Mitglied werde ich wohl im nächsten Jahr noch präsenter im Netz sein, um vielen Usern den Einstieg in NetBeans als Plattform und IDE zu erleichtern.

 

An dieser Stelle sende ich allen Menschen gute Wünsch, die ich persönlich und schriftlich anders schwer erreichen kann. Dazu gehört das NetBeans Dream Team, allen Trainer der Certified Trainings for NetBeans Plattform, den Helden des deutschen NetBeans Forums (unermüdliche Kämpfer gegen das Unwissen): Insbesondere den beiden Michaels, Christian, Seapegasus, Stefan usw...

 

Alles Gute und allen, die es genießen können: Schöne Feiertage und einen ruhigen Urlaub.


Beste Grüße,
  Josch.

JNBB - Joschs NetBeans Blog - December 01, 2008 12:43 PM
BeanDev: Code Completion und Java Typen erkennen

Wie ich schon in meinem letzten Blog-Post geschrieben hatte, ist das Ermitteln eines Java Datentypes abhängig zu einer Cursorposition im Quelltext alles andere als trivial. 

Aber nur mit diesem Wissen ist es möglich dem Anwender einem dem Kontext sinnvolle Ergebnisliste an Code Completion Einträgen zurückzugeben.

Nehmen wir wieder meinen (virtuellen) ColorCode Completion Provider, der Farbwerte in der Completion Liste vorschlagen soll. Dem Programmierer ist es wohl kaum zuzumuten, immer die Liste an Farbcodes anzuzeigen, auch wenn an aktueller Cursorposition gar kein Color-Objekt erwartet wird.

Also bei solchen Beispielen soll auf keinen Fall eine Liste an Farbcodes vorgeschlagen werden:

int i = |
JFrame f = new JFrame (|
double d = Math.sin (2.4) + |

Aber hier wäre die Vorschlagsliste durchaus sinnvoll:

Color c = |
((Component) comp).setBackground (|
Color outColor = error ? Color.RED : |

Wie findet man aber heraus, welcher Datentyp an der mit | gekennzeichneten Position im Quelltext erwartet wird? Nicht einfach...

Glücklicherweise ist NetBeans Open Source und zu meiner Erleichterung hat sich natürlich jemand anderes schon Gedanken darum gemacht. Das merkt man ja schnell, wenn man selber in der IDE das Code Completion nutzt. Die eigentliche Methode, die dabei hilft ist getSmartTypes aus der Klasse JavaCompletionProvider. Nur ist diese Methode private. Mit ein wenig Arbeit habe ich aber die relevanten Teile von JavaCompletionProvider hinauskopiert und drei Klassen erzeugt, damit man in eigenen Projekten getSmartTypes verwenden kann. 

Innerhalb eines eigenen Completion Providers kann man nun sehr einfach die Datentypen abfragen:

  public void run(CompilationController ci) throws Exception {
    valid = false;
    CompletionEnvironment env = ParserUtilities.getCompletionEnvironment (ci, component.getCaretPosition(), true);
    if ( hasTypes(ParserUtilities.getSmartTypes(env), "Color", "java.awt.Color") ) {
      valid = true;
    }
  }
private boolean hasTypes (Set<? extends TypeMirror> types, String... typeList) {
if ( types == null || types.size() == 0 ) return false;
for (TypeMirror typeMirror : types) {
for (String type : typeList) {
if ( typeMirror.toString().equals(type)) return true;
}
}
return false;
}

Die drei notwendigen Klassen finden sich hier im ZIP-Archiv. Ich würde mich freuen, wenn noch jemand die Hilfsklasse verwenden kann.

Beste Grüße,
  Josch.

JNBB - Joschs NetBeans Blog - November 28, 2008 01:43 PM
BeanDev: Code Completion abhängig vom Java Context

Wenn man für Java Quelltext Code Completion hinzufügen möchte, steht man schnell vor der Frage, wie man das Swing-Document orientierte Code Completion mit der Java Infrastruktur verknüpft.

Geertjan hat in seinem Blogeintrag gezeigt, dass man das über eine extra Service lösen kann. Es wird eine eigenständiger Service als CaretAwareJavaSourceTaskFactory registriert. Dieser gibt über einen globalen statischen Wert bekannt, ob an der aktuelen Cursor-Position die Code Completion-Liste mit eigenen Werten versehen werden soll. Sein CompletionProvider schnappt sich diesen globalen Wert und füllt dann die CompletionItem-Liste, oder auch nicht.

Die lockere Verbindung mit unterschiedlichen Tasks funktioniert, aber ich habe da so meine Befürchtungen, dass das nicht immer deterministisch ist, wenn zwei nebenläufige Prozesse sich über eine globale Variable unterhalten wollen.

Glücklicherweise spricht nichts dagegen im eigenen CompletionProvider selbst einen ActionTask zu starten, um die Analyse direkt in die Hand zu nehmen.

Ein CompletionProvider muss per createTask einen CompletionTask zurückgeben. Dieser Task wird immer dann aufgerufen, wenn der Anwender die Code Completion manuell aktiviert (oder die Code Completion automatisch ausgelöst wird).

Der CompletionTask ist i.d.R. immer eine AsyncCompletionQuery die mehrere Methoden überschreiben kann. Die Methode

public void query (CompletionResultSet completionResultSet, Document document, int caretOffset);

füllt das CompletionResultSet mit eigenen Einträgen. Es gibt nun verschiedene Eingangsparameter, die eine solche Liste begrenzen. Zum einem die aktuelle Benutzereingabe und zum anderen der Scope. Mit Scope ist gemeint, wo die Eingabe gerade durchgeführt wird.

Es macht nicht immer Sinn die eigenen Completion-Items der Liste hinzuzufügen, weil die Eingabeposition im Programmcode überhaupt nicht zu den möglichen Completion Items passt.

Also muss man den semantischen und syntaktischen Kontext prüfen, bevor man den Anwender mit einer sonst überfüllten Liste von Ersetzungswerten vollpumpt.

Hier dient wieder die Hilfsklasse JavaSource, um aus dem Document einen Zugriff auf den Parser zu bekommen. Für ein eigenes Color-Code Completion-Modul habe ich folgende simple Programmierung:

JavaSource js = JavaSource.forDocument(component.getDocument());
js.runUserActionTask(ccpt = new ColorCompletionPreparerTask(component), false);

Die Klasse ColorCompletionPreparerTask enthält eine kleine run-Methode, die die aktuelle Cursor-Position versucht zu analysieren.

  public void run(CompilationController ci) throws Exception {
    ci.toPhase(Phase.ELEMENTS_RESOLVED);
    TreePath tp = ci.getTreeUtilities().pathFor(component.getCaretPosition());

    Kind kind = tp.getLeaf().getKind();
    if (kind == Kind.METHOD_INVOCATION) {
      valid = true;
    }

    Element parent = getIdentifierElement(ci, tp);
    if (parent != null) {
      if (parent.asType().toString().endsWith("Color")) {
        valid = true;
      }
    }

    Element element = ci.getTrees().getElement(tp);

    if (element != null) {
      switch (element.getKind()) {
        case CLASS: {
          if (kind != Kind.METHOD_INVOCATION) {
            valid = false;
          }
          break;
        }
      }
    }
  }

Dazu gibt es noch eine Hilfsmethode getIdentifierElement(...):

  private Element getIdentifierElement(CompilationInfo ci, TreePath identifier) {
    Kind kind = identifier.getLeaf().getKind();
    if (kind == Kind.IDENTIFIER) {
      TreePath parent = identifier.getParentPath();
      Element e = ci.getTrees().getElement(parent);
      if (e != null && e.getKind() == ElementKind.FIELD) {
        return e;
      }
    }
    if (kind == Kind.VARIABLE) {
      Element e = ci.getTrees().getElement(identifier);
      if (e != null && (e.getKind() == ElementKind.LOCAL_VARIABLE || e.getKind() == ElementKind.FIELD)) {
        return e;
      }
    }
    if (kind == Kind.ASSIGNMENT) {
      // ?
    }
    return null;
  }

Die Klasse gibt per isValid() den Status zurück, der im CompletionProvider abgefragt werden kann. Wenn der Wert true ist, wird die Completion-List mit einigen Color.* Konstanten und weiteren Color-Werten gefüllt. Ein Icon zeigt die Farbe in der Completion List auch an, damit man einen besseren Eindruck davon hat.

Allerdings ist der ColorCompletionPreparerTask recht simpel und erfasst nicht alle Möglichkeiten (zum Beispiel innerhalb von Ausdrücken oder bei Zuweisungen, bei denen der Typ an anderer Stelle deklariert wurde).

Das dies nicht sehr trivial ist, zeigt der Sourcecode von JavaCompletionProvider, den man sich hier mal betrachten kann

Beste Grüße,  Josch.

 

 

JNBB - Joschs NetBeans Blog - November 27, 2008 12:35 PM
BeanDev: Code Completion global im Java Quelltext

Ein Code Completion-Modul zu erstellen ist eigentlich relativ trivial. Dazu gibt es schon Tutorials.

Erster Startpunkt ist dieses Tutorial für NetBeans 6.5

Das Problem bei der API zu Code Completion ist, dass man im Prinzip nur mit Swing-Klassen arbeitet - man hat also nur puren Text und kann erstmal gar nicht semantisch mit dem Quelltext arbeiten.

Da gibt es im Prinzip zwei Seiten, wo man Semantik vermisst. Zum einen bei der passenden Darstellung der Code Completion List (d.h. nur das darstellen, was zur Eingabeposition passt) und zum anderen kann man schwer globale Modifikationen im Quelltext durchführen (zum Beispiel einen passenden Import hinzufügen, wenn der Anwender einen Eintrag aus der Code Completion Liste ausgewählt hat).

Die Problematik des passenden Scopes hat Geertjan in seinem Blog schon angesprochen. Er erweitert das obige Tutorial mit einer CaretAwareJavaSourceTaskFactory. Das ist ein eigenständiger Service, der etwas locker und (uneinsichtig) sehr statisch aber effizient den CodeCompletionProvider steuert. Den Blog-Eintrag findet Ihr hier.

Nun kann man also semantisch steuern, welche Code Completion Einträge in der Liste dargestellt werden sollen. Das erspart dem Anwender unnötige Einträge, die gar nicht an die aktuelle Stelle des Quelltextes eingefügt werden können (weil sonst die Syntax hinüber ist).

Was ist aber mit einer globalen Änderung des Quelltextes?

Die Implementierende Klasse von CompletionItem

  public void defaultAction(JTextComponent jTextComponent);

übergibt nur eine JTextComponent. Daraus holt man sich das Document und kann dann bestenfalls (ohne größeren Aufwand) an der Caret-Position den Text einfügen und ggf. noch ein paar Zeichen ersetzen.

Für Modifikationen außerhalb dieses Bereiches bietet die Completion API keine Hilfe.

Dafür gibt es aber die Java Source Infrastructure die aus mehreren Grundpaketen und Modulen aufgebaut ist. Verwendet wird javax.lang.model.*, dazu gesellt sich org.netbeans.api.java.source.* und wird letztendlich von com.sun.source.* unterstützt. Eine Code-Snippet-Samlung zur Java Infrastructure findet sich auf dieser Wiki-Seite.

Wie bringt man das alles zusammen?

Zentraler Einstiegspunkt ist für diesen Fall die JavaSource-Klasse. Sie bietet eine statische Methode, um aus einem Document eine JavaSource-Instance zu generieren:

public void defaultAction(JTextComponent jTextComponent) {
Document doc = jTextComponent.getDocument();
JavaSource source = JavaSource.forDocument (doc);
fixImport (source, "java.awt.Color");
}

Und mit dem JavaSource-Objekt kann man Tasks starten, um am Quelltext Modifikationen durchzuführen:

  private void fixImport (final JavaSource source, final String importDeclaration) {
new Thread (new Runnable() {
public void run() {
try {
ModificationResult result = source.runModificationTask(new Task<WorkingCopy>() {
public void run(WorkingCopy workingCopy) throws Exception {
workingCopy.toPhase(Phase.ELEMENTS_RESOLVED);

CompilationUnitTree cut = workingCopy.getCompilationUnit();
TreeMaker make = workingCopy.getTreeMaker();
List<? extends ImportTree> imports = cut.getImports();
for (ImportTree it : imports) {
if (it.getQualifiedIdentifier() instanceof MemberSelectTree) {
MemberSelectTree ms = (MemberSelectTree) it.getQualifiedIdentifier();
String pkg = ms.getExpression().toString() + "." + ms.getIdentifier().toString();
if (pkg.equals(importDeclaration)) {
return; // Import existiert schon!
}
}
}
CompilationUnitTree copy = make.addCompUnitImport(
cut,
make.Import(make.Identifier(importDeclaration), false));
workingCopy.rewrite(cut, copy);
}
});
result.commit();
} catch (IOException ex) {
Exceptions.printStackTrace(ex);
}
}
}).start();
}

Im Prinzip ist die Methode aus dem obigen Guide-Tutorial zusammengebaut, nähere Erklärungen finden sich dort. Es sollte auch erwähnt werden, dass der Erzeugen von imports so eigentlich nicht gemacht werden soll (wird auch im Guide beschrieben).

Was aber wichtig ist, dass das Verschmelzen von zwei APIs wieder mal nur ein winzig kleiner Befehl ist: JavaSource source = JavaSource.forDocument (doc);

Die Wirkung ist für den Anwender der Code Completion aber bedeutend.

Beste Grüße, 
  Josch.

JNBB - Joschs NetBeans Blog - November 25, 2008 10:33 AM
BeanDev: Quick Search deaktivieren

Benötigt NetBeans 6.5Fast etwas simpel, aber wird ständig wieder gefragt. Wie kann man in einer Plattform Applikation das Quick Search deaktivieren?

Da es eine Toolbar ist, geht es wie mit allen Toolbars. Die layer.xml öffnen und folgenden Dreizeiler hinzufügen:

<folder name="Toolbars">
    <folder name="QuickSearch_hidden"/>
</folder>

Möchte man Quick Seach behalten, aber nur einzelne Suchdienste deaktivieren, geht es folgendermaßen:

<folder name="QuickSearch">
<file name="Actions_hidden"/>
<file name="GoToOption_hidden"/>
<file name="GoToType_hidden"/>
<file name="Help_hidden"/>
</folder>

Und auch die Liste der letzten Suchergebnisse lässt sich abschalten:

 

<folder name="QuickSearch">
<file name="Recent_hidden"/>
</folder>

Beste Grüße,
  Josch.

JNBB - Joschs NetBeans Blog - November 24, 2008 03:46 PM
BeanDev: Quick Search für Platform Developer

Auf dem NetBeans DemoCamp wurde deutlich, dass einige Internet-Ressourcen zwingend notwendig sind, um sich als Platform-Entwickler zurechtzufinden.

Benötigt NetBeans 6.5Immer wieder wurde auf die Developer FAQ und die Platform-Seite von NetBeans verwiesen. So häufig, dass die Idee aufkam, diese Quellen in die neue Quick Search Funktion von NetBeans 6.5 zu integrieren.

Zwar bietet die Quick Search API schon eine (versteckte) Möglichkeit an Webseiten über "einfache" Konfiguration einer layer.xml Datei zu integrieren, aber die Regulären Ausdrücke sind nicht so mächtig wie ein selbst geschriebenes Programm.

Das von mir programmierte Plugin stellt folgende Ressourcen zur Verfügung:

Fast alle relevanten Links von http://wiki.netbeans.org/NetBeansDeveloperFAQ

Alle Links zu den Community Tutorials unter http://www.netbeans.org/community/commdocs.html

Sowie die Links zu den Tutorials auf der Seite http://platform.netbeans.org/tutorials/ (ausgenommen Screencasts, Blogs usw.).

Die Quick Search Suche verwendet die Beschreibungen zu den Links, keine Wörter von Seiteninhalten oder Teile der URLs.

Für das PlugIn gibt es ein eigenes Update-Center. Die Installationsdatei zu dem Update-Center findet sich hier.

Nach der Installation finden sich im Plugin-Center drei zusätzliche Plugins für die obigen Resourcen.

Zusätzlich gibt es noch ein Optionsdialog, um die PlugIns (ohne sie deinstallieren zu müssen) ein- oder auszuschalten.

Wenn die nächsten Tage keine Bugs gefunden werden, veröffentliche ich noch den Sourcecode dazu.

Als HTML Parser verwende ich übrigens die herausragende Bibliothek Jericho HTML Parser. Es war der einzige Parser, der in der Lage war den grausigen HTML-Quelltext der Community-Tutorial-Tabelle zu lesen. Da schwirren nämlich doppelt und dreifach geschlossene <tr>-Tags, doppelt verschachtelte <tr>-Tags und nicht geschlossene <div>-Tags rum. Die meisten Parser kapitulierten mit Exceptions. JTidy brauchte mehrere Minuten, um ein Pretty Print durchzuführen. Zwei Parser lieferten nur die erste Zeile der Tabelle und nur Jericho war überhaupt in der Lage die Tabelle mit Content auszulesen.

Update:

Ein Bug habe ich schon gefunden. Lange Ergebnis-Texte in der Quick Search Liste werden nicht mit der Sucheingabe fett markiert. Das sieht man übrigens schon auf dem obigen Screenshot. Der Fehler liegt aber im Renderer des core-Moduls. Da habe ich leider keinen Einfluss. Issue habe ich schon dazu geschrieben.

... was sich als Duplicate herausstellt. Wieder mal eine Lehrstunde, dass man doppelt und dreifach nach Issues suchen sollte. Nun gut. Das Problem wird hier unter Issue #147372 beschrieben und im dritten Versuch habe ich eine Lösung programmieren können, die HTML erlaubt und abgeschnittene Ergebnisse kennzeichnet. Ich hoffe die Lösung wird so (oder ähnlich) übernommen. Hauptsache es funktioniert :-)

PlugIn-Update:

Das PlugIn wurde auf Version 1.0.3 gehoben. Augenfällig ist nur, dass im Options-Panel nun ein Link zu dieser Seite erscheint. Als Fehlerkorrektur kam hinzu, dass bei nicht erreichbaren Webseiten das Schnellsucheergbnis darüber informiert und nicht mit einer NullPointerException abschmiert.

Beste Grüße,
  Josch.

Seapegasus Blog - November 19, 2008 05:52 PM
NetBeans6.5_de

Wie Ihr sicher schon gesehen habt, ist NetBeans IDE 6.5 (Download) draußen. :) Aber wo ist die deutsche Community-Lokalisierung? Im Sprachen-Menü sind nur die 100%-fertigen aufgelistet (Chinesisch etc). Um die Community-Lokalisierungen wie Deutsch, Franzöisch, Spanisch, Italienisch, Polnisch, u.a. runterzuladen, klickt Ihr "More Languages..."

Der Direktlink ist: NetBeans Multilingual Download

Welche Module sind übersetzt? Auf der Statusseite seht Ihr, dass die gesamte Basis-IDE (Platform, Profiler, Versionsverwaltung), und auch Java SE, C/C++, PHP, Profiler, und Ruby, fertig sind. Das sind stolze 52%! Wir arbeiten noch an Java EE/Web App, Java ME, Groovy, SOA/XML.

Dank dafür an das ganze Team vor allem an Christian P. und Peter H.! Sobald die Lieferung bei mir ankommt, schick ich Euch das neue TranslatedFiles-T-Shirt. :)

Wie kann ich Übersetzungsfehler melden?

JNBB - Joschs NetBeans Blog - November 07, 2008 11:36 AM
NetBeans Demo Camp München - 18. November 2008

In Zusammenarbeit mit der Münchner NetBeans User Group (NUGM) wird das erste NetBeans Demo Camp in München am 18 November 2008 ausgerichtet. Mit Geertjan Wielenga vom NetBeans Team in Prag, Tanja Drüke von Genomatix in München, den NetBeans Dream Team Mitgliedern Sven Reimers, Anton Epple und meine Wenigkeit gibt es eine Menge spannender Vorträge, Informationen und die Möglichkeit uns auch einmal offline zu treffen.

Mit dem Release von NetBeans 6.5, dem 10ten Geburtstag von NetBeans gibt es auch eine Menge zu feiern.

Das Demo-Camp spezialisiert sich auf die Vorstellung von NetBeans als Rich Client Platform. Unternehmen und Einzelpersonen wurden eingeladen, ihre Produkte und ihre Erfahrungen austauschen. Als besondere Gäste werden Mitglieder des NetBeans Dream Team und des NetBeans-Team in Prag erwartet. Sie werden die neuesten Plattform-Funktionen von NetBeans 6.5 vorstellen.

Themen:

  • "New Features in NetBeans Platform 6.5"
    Geertjan Wielenga, NetBeans Guru, Sun Microsystems Inc., Prag.
  • "Demosession: ChipInspector - NetBeans APIs im Einsatz"
    Tanja Drüke, Genomatix Software GmbH, Munich, Germany.
  • "RCP Entwicklung in der Praxis: Third-party libraries und ClassLoader"
    Anton Epple, NetBeans Dream Team Member, Eppleton, Munich, Germany.
  • "Portierung von Swing Applikationen auf die NetBeans Platform"
    Aljoscha Rittner, CEO, Sepix GmbH, Hann. Münden, Germany.
  • "Grosseinsatz - NetBeans RCP im Grossprojekt"
    Sven Reimers, NetBeans Dream Team Member, ND SatCom Defence GmbH, Immenstadt, Germany.

Hier kann man sich kostenlos registrieren. Bis zum 18. November in München!


Beste Grüße
  Josch.

 

JNBB - Joschs NetBeans Blog - November 03, 2008 12:15 PM
Source Talk Tage 2008 - Aufzeichnungen

Die Veranstaltungsaufzeichnungen zu den Source Talk Tagen sind inzwischen verfügbar. Auch der Vortrag von Toni Epple zu NetBeans als Rich Client Platform ist dort zu sehen. Eine Übersicht des Vortrags in Folien findet sich hier. Der Vortrag ohne Video (24MB) und mit Video (68MB) stehen zum Download (als ZIP-Archive) bereit.

Eine Herausforderung wird es aber sein, die Medien abzuspielen. Hier ein Installationstutorial zum TeleTeachingTool. Beherzte Java-Entwickler werden aber die Grundvorraussetzungen (Java, Java Media Framework) schon installiert haben.

Viel Spaß damit.


Beste Grüße,
  Josch.

Seapegasus Blog - October 20, 2008 02:19 PM
Handelsreisende mit NetBeans im Gepäck

Geertjan hat ein interessantes Interview mit "unserem" Aljoscha Rittner über dessen neue Kundendatenverwaltung gemacht. Die Anwendung stellt die Kundenstandorte auf einer annotierten Landkarte dar (inklusive z.B. Radarfallen), und ein Handelsvertreter oder Geschäftsmensch lässt sich damit die optimale Route anzeigen, um auf seiner Fahrt so viele Kunden wie möglich abzuklappern. Sehr schön mit den NetBeans-Platform-APIs implementiert, inklusive Hilfesystem und Assistenten, davon kann man viel lernen.

Auf Englisch ist das Interview als Porting a Sales System to the NetBeans Platform auf der NetBeans Zone erhältlich. Alle Antworten auf Deutsch gibt es zusätzlich als PDF. Lest auch Joschs neuste Platform-Erkenntnisse in seinem Blog.

PS: Herzlichen Glückwunsch zum 10. Geburtstag, NetBeans!

JNBB - Joschs NetBeans Blog - September 11, 2008 06:21 AM
BeanDev: 3rd-Party Bibliotheken und Module

Trivial ist es nicht, manche Fremdbibliotheken in NetBeans zu integrieren. Insbesondere wenn die Libraries über eigene Classloader oder auch nur den lokalen Kontext-Classloader Klassen nachladen wollen. Hat man nämlich diese Bibliothek in ein Library-Wrapper-Modul gepackt, ist diese gut abgeschottet und könnte nur Klassen aus Modulen laden, zu denen Abhängikeiten definiert wurden.

Nun liegt es aber in der Natur der Sache, dass man ja diese (in ein Modul eingepackte) Bibliothek selber in Abhängigkeit einbinden möchte.

  1. Library Wrapper mit public packages <- nutzt <- Modul XYZ
  2. Modul XYZ mit public packages -> stellt Klassen zur Verfügung -> *
  3. Library Wrapper lädt per Classloader Klassen von Modul XYZ

Spätestens Punkt 3 scheitert, weil der Library-Wrapper sich Modul XYZ nicht in Abhängigkeit setzen kann. Es käme zu einer zyklischen Referenz, die das Build-System von NetBeans nicht auflösen kann.

Solche Probleme bekommt man z.B. mit dem Java Media Framework (Media Registry) oder Xalan (Digester).

Da man diese 3rd-Party-Bibliotheken nicht umprogrammieren kann (damit sie z.B. den System Classloader verwenden), muss man sich etwas anderes einfallen lassen.

Zwar unterscheidet NetBeans in seinem "New Project"-Assistenten zwischen Library-Wrapper Module und Module, tatsächlich gibt es keine technische Unterscheidung. Auch ein NetBeans-Modul mit Sourcecode darf Fremdbibliotheken einbinden. Grundsätzlich gibt es auch keine Einschränkung bei der Menge an Fremdbibliotheken (auch wenn es der Assistent des Library-Wrapper Modules suggeriert).

Wenn man in dem eigenen Modul-Projekt die Fremdbibliotheken importiert hat, werden die eigenen Klassen und die Klassen der JAR-Dateien in dem selben Module-Classloader geladen. Außerdem gibt es keine Notwendigkeit zyklische Abhängigkeiten zu deklarieren, weil mit so einem Aufbau schon die Bindung zueinander definiert wurde. Nach außen (aus der Sicht anderer Module) erscheint so ein hybrides Modul aus eigenen Klassen und Fremdklassen als eine Bibliothek mit (i.d.R.) unterschiedlichen Root-Packages.

Was ist also zu tun, um die JAR-Dateien erstmal in dem eigenen Projekt zu aufzunehmen?

Zunächst wechselt man vom Projects-Fenster zum Files-Fenster, damit man dort Ordner auf Projekt-Ebene anlegen kann. Direkt unter dem Projekt-Hauptordner legt man einen Unterordner release an. Allein dieser Ordner sorgt dafür, dass das Build-Script eine Sonderbehandlung für Fremdbiblioteken startet. Wenn dieser Ordner existiert, muss in diesem mindestens der Unterordner modules und darin der Ordner ext existieren (sonst kommt es zu einem Build-Fehler).

Also so sollte es beispielsweise Aussehen:

  • XYZ Module
    • nbproject
    • release
      • modules
        • ext
    • src
    • test

In den ext-Ordner kommen die JAR-Dateien, die benötigt werden.

Das Build-Script kümmert sich nun darum, dass die JAR-Dateien mit dem Projekt in die NBM-Installationsdatei aufgenommen wird. 

Es wird aber noch in der project.xml noch eine Classpath-Referenz benötigt:

In der "Projects-Ansicht" unter "Important Files" die "Project Metadata" öffnen und dann unter den Abschnitt:

<project xmlns="http://www.netbeans.org/ns/project/1">
    <type>org.netbeans.modules.apisupport.project</type>
    <configuration>
        <data xmlns="http://www.netbeans.org/ns/nb-module-project/3">
            <code-name-base>de.example.xyz</code-name-base>

folgende Classpath-Erweiterung angeben:

            <class-path-extension>
            <runtime-relative-path>ext/3rdparty1.jar</runtime-relative-path>
            <binary-origin>release/modules/ext/3rdparty1
.jar</binary-origin>
            </class-path-extension>
            <class-path-extension>
            <runtime-relative-path>ext/3rdparty2.jar</runtime-relative-path>
            <binary-origin>release/modules/ext/3rdparty2
.jar</binary-origin>
            </class-path-extension>
            [...]
 

Das genügt, damit die JAR-Dateien im NBM-Modul installiert werden können. Außerdem werden damit in den Projekt-Eigenschaften die Pakete der Bibliotheken auswählbar. Das ist auch zwingend notwendig, weil auch die eigenen Sourcen nur so auf die Klassen der JARs zugreifen können.

Also die Projekteingeschaften öffnen, zu "API Versioning" wechseln und nun die "Public Packages" auswählen, die innerhalb des Moduls benötigt werden.

Das war es auch schon. Noch eine Empfehlung zum Schluss: Die so verwendeten JAR-Dateien sollten ggf. immer um die vollständige Versionnummer ergänzt werden. Also nicht nur 3rdpart.jar verwenden, sondern diese Datei in 3rdparty_1-5-6.jar umbenennen. Alle fremden JAR-Dateien einer Module-Suite werden in den selben ext-Ordner kopiert. Das kann zu Überschreibungen und Versionkonflikten führen.

Beste Grüße,
  Josch.

 

 

 

JNBB - Joschs NetBeans Blog - August 28, 2008 04:20 PM
Source Talk Tage 2008 in Göttingen

Dieses Jahr ist es wieder soweit. Die Source Talk Tage im Mathematischen Institut in Göttingen laufen an. Von Dienstag, 23. September bis Donnerstag, 25. September 2008 gibt es wieder spannende Themen wie eLearning, OpenSolaris und Java.

Es werden einige "bekannte Gesichter" rund um die Blog Scene der NetBeans Community anwesend sein. Roman Strobl (er war schon mal mit Geertjan da) will uns zeigen, was OpenSolaris als Betriebssystem so spannend macht und Anton Epple führt in die RCP Programmierung mit NetBeans 6.5 ein.

Ich selbst werde auch am Mittwoch und Donnerstag rein schauen. Würde mich freuen eine volles Haus vorzufinden :-)

Beste Grüße,
  Josch.

 

JNBB - Joschs NetBeans Blog - August 28, 2008 03:48 PM
PHP - welche IDE wird es werden?

Nun ist es raus, PDT for Eclipse wird auf Dezember verschoben. Zu viele Probleme sind da gewachsen und das PDT soll produktiv noch nicht einsetzbar sein.

Vernünftige PHP-Entwicklungsumgebungen gab es ja bisher noch gar nicht (meine persönliche Meinung). Nur Zend bot etwas an. Einige IDEs boten Integrationen an, die meistens über das Syntax Highlighting nicht hinauskamen und als Höhepunkt gerade mal eine Verknüpfung zur Online Doku boten.

Mit NetBeans erwächst langsam vielleicht ein Licht am Streifen des Horizonts nach der Sonnenfinsternis ;-) - Es mag noch nicht so viele Features geben wie in Eclipse PDT, doch eines kristalisiert sich heraus: Das was geht wirkt robust, einheitlich und sauber in die IDE integriert.

PHP ist nicht meine Primärsprache, aber NetBeans ist inzwischen auch für PHP meine Primär-IDE geworden. Dank an die Entwickler, die das Projekt so voran treiben.


Beste Grüße,
  Josch.

Links: Code templates in PHP, Semantic(!) coloring, FTP support, PHP Interpreter frei wählbar

 

Seapegasus Blog - August 27, 2008 03:23 PM
Continuous Integration Im Kampf Gegen Den Golem

Es ist vielleicht eine vergessene Tatsache, aber wenn bei NetBeans ein Engineer es schafft, trotz aller Tests und Checks die Build zu vermurksen, dann wird derjenige zur Strafe von dem furchterregenden Prager Golem heimgesucht! (Nein, ich weiss nicht wer ihn gerade zu Besuch hat, wir harmlosen Tech Writer machen ja nie nie nie was an der Build kaputt... :-D)

Hat Eure Firma auch so nette Traditionen? Falls Ihr Euch für Eure wachsenden Team-Projekte gerade nach Werkzeugen für automatisierte Builds und automatisierte Tests umseht, dann lege ich Euch diesen Vortrag zum Stichwort Permanente Integration ans Herz:

In München findet am 19. September das erste Continuous Integration Camp statt. Veranstalter ist die NetBeans User Gruppe der JUG München. Mit dem Meeting wollen wir die Möglichkeit zum Erfahrungsaustausch und zu Diskussionen darüber bieten, was möglich und was sinnvoll ist. Weitere Informationen findet Ihr im WIKI der JUGM.

Es gibt einen Vortrag von Toni Epple und eine konkrete Fallstudie. Einfach per Email anmelden und dann am 19.9. (Freitag Nachmittag) ab nach Heimstetten. Gebt dem Golem keine Chance! ;-)

Seapegasus Blog - August 22, 2008 01:54 PM
GlassFish-Vortrag in Berlin

Wie Arun schon auf Englisch angekündigt hat, gibt es im September einen Vortrag über den GlassFish Application Server zusammen mit der Berlin-Brandenburg JUG.

Mittwoch, 3. September
17:30 - ca. 19:30
Zimmer FR5516, Technische Universität Berlin
Franklinstrasse 28/29 (Stadtplan)
10587 Berlin

Bitte meldet Euch vorher via XING an! Dort findet Ihr auch die Beschreibung. Arun hält den Vortrag auf Englisch.

JNBB - Joschs NetBeans Blog - August 08, 2008 07:29 AM
BeanDev: AutoUpdateCenter per Ant-Script updaten - Teil II

Ich hatte vergangenen Monat darüber berichtet, wie man ein UpdateCenter automatisch per Ant-Script über ftp auf den Server pushen kann.

Ich hatte da schon Bedenken geäußert das ftp-Kennwort im Build-Script zu hinterlegen und zeigte die Möglichkeit auf das Kennwort in den private.properties einzupflegen. Damit fließt das Kennwort nicht in die Versionierung und bleibt (so lange lokal kein fremder Zugriff besteht) geheim.

Wenn man das Kennwort gar nicht speichern will, gibt es den input-Task, mit dem das Ant-Script stoppt, ein Abfrage-Diaolg startet und damit das Kennwort setzen kann.

Man braucht nur drei Zeilen dem Build-Script hizuzufügen und das Target distribute-dev bei depends anpassen:

    <target name="-distribute-prompt-for-ftppass" unless="dev.ftp.password">
        <input addproperty="dev.ftp.password" message="Kennwort für das Update-Center:"/>
    </target>

<target name="distribute-dev" depends="-distribute-prompt-for-ftppass" description="Distributes to developers and testers">

Das war es auch schon. Wenn das Kennwort nicht in einer Property-Datei hinterlegt wurde, kommt der Dialog. Bei richtig hinterlegtem Kennwort oder bei korrekter Eingabe wird dann alles notwendige auf den Server kopiert.

Man sollte nur beachten, dass die Kennworteingabe nicht durch ein Password-Field erfolgt. D.h. bei der Eingabe sieht man das Kennwort!

Mit einem eigenen InputHandler wäre das lösbar. Hier findet sich eine Quelle, die einen PasswordInputHandler implementiert hat (Download-Datei ist antinput.zip).

Beste Grüße,
  Josch.

JNBB - Joschs NetBeans Blog - August 07, 2008 05:27 AM
BeanDev: Plattform Übersetzung als Plugin

Wie ich schon in einem älteren Blog-Eintrag geschrieben hatte, ist es möglich eine Programm-Suite mit einem Branding so zu versehen, dass man eine Übersetzung der Plattform hat.

Bei dem Verfahren gibt es einen bedeutenden Nachteil: Nur per ZIP-Distribution wird die Übersetzung installiert. Der Anwender kann nicht darüber entscheiden, ob er diese Sprache haben will und kann das auch nicht zurücknehmen (außer er löscht die JAR-Dateien aus dem ./locale-Ordner.

Tonny Kohar zeigt in seinem Blog, wie man für eigene Projekte ein Plugin erstellen kann, um eine Sprachvariante installierbar zu machen.

Das beschriebene Build-Script ist aber sehr auf das Projekt zugeschnitten und skizziert nur den Aufbau. Ich habe mich mal der Sache angenommen und ein Build-Script erstellt, dass generell für alle Projekte verwendet werden kann. Außerdem halte ich mich an die Suite-Branding Konvention die lokalisierten Bundle.properties im branding-Ordner (und nicht unter src) zu halten. Das vereinfacht die Integration in das bestehende Build-System von NetBeans.

Wie Tonny auch schon schreibt, reicht es, erst ein normales Modul zu erstellen. Ich gebe dem Modul immer den Namen der Suite mit der Endung -l10n.Ggf. kann man aber auch das Sprachkennzeichen verwenden (z.B. deDE), damit man später unterschieldiche Übersetzungen in getrennten Installationen anbieten kann.

In der Files Ansicht des Explorers lege ich dann den branding-Ordner an. Im Gegensatz zur Suite wird der branding-Ordner in einem Module nicht automatisch vom Build-Script erkannt. Suite-Projekte starten ein speziellen Build-Abschnitt, wenn der branding-Ordner existiert.

Unter dem branding-Ordner werden nun die Abschnitte der Übersetzungen für die Module abgelegt. Core weicht da ein wenig vom Standard ab, aber die Modul-Übersetzungen der Plattform heißen vom Ordner immer wie die zu erzeugenden Übersetzungs-JAR-Dateien:

Unter core und modules werden nun für die Platform-Module die Bundle_xx_XX.properties-Dateien, ein paar Helpset-Dateien und Bildchen eingefügt. Ich hatte schon mal was vorbereitet (allerdings ohne _de_DE-Anhängsel an den Bundle-Dateien): Ein ZIP-Archiv findet sich hier.

Im Prinzip sollte es so aussehen:

Jetzt geht es ans Eingemachte. Das Build-Script muss erweitert werden.

Dafür nun Build.xml öffnen (findet sich am schnellsten in der Projektansicht unter Important Files).

Direkt unter dem ersten import geht es los. Zunächst benötigen wir die Eigenschaften aus dem Suite-Projekt, damit wir den Application-Namen bekommen:

    <property file="${suite.dir}/nbproject/project.properties" prefix="prjsuite"/>

Dann erzeugen wir ein kleines Makro, damit die Tipparbeit für die Module nicht ausufert:

    <macrodef name="sub-jar">
      <attribute name="dep"/>
      <attribute name="module" default="modules"/>
      <sequential>
        <jar jarfile="${locale.dir}/@{dep}_${app.name}.jar" compress="${build.package.compress}">
            <fileset dir="branding/@{module}/@{dep}.jar/"/>
        </jar>
      </sequential>
    </macrodef>

Nun  hängen wir uns in das Target build-init ein, um zwei Variablen zu füllen:

    <target name="build-init" depends="harness.build-init">
      <property name="locale.dir" value="${basedir}/build/cluster/modules/locale/"/>
      <property name="app.name" value="${prjsuite.app.name}"/>
    </target>

Dabei ist locale.dir das Zielverzeichnis für die Übersetzungen und app.name holt sich einfach den Wert aus den Suite-Properties.

Nun hängen wir uns in das jar-Target, erzeugen das locale.dir und basteln schon für das core.jar die erste Übersetzung. Da kann ich das Makro nicht verwenden, weil der Zielname der Übersetzung anders als die JAR-Datei heißen soll:

   <target name="jar" depends="projectized-common.jar">
        <mkdir dir="${locale.dir}"/>

        <!-- Sonderbehandlung für das core-package -->
        <jar jarfile="${locale.dir}/org-netbeans-core_${app.name}.jar" compress="${build.package.compress}">
            <fileset dir="branding/core/core.jar/"/>
        </jar>

Nun folgen die restlichen Übersetzungen aus dem modules-Ordner. Das funktioniert sehr gut mit dem Makro. Hier ein Auszug:

        <!-- Lokalisierung für die modules packages -->
        <sub-jar dep="org-jdesktop-layout"/>
        <sub-jar dep="org-netbeans-api-progress"/>
        <sub-jar dep="org-netbeans-api-visual"/>
        <sub-jar dep="org-netbeans-core-execution"/>

Als letztes in diesem Target kopiere ich die fertigen Archive in das Suite-Projekt. Das ist nicht unbedingt notwendig, erleichtert aber das Erstellen von ZIP-Distributionen mit fertig übersetzten Archiven:

        <!-- Kopieren der JAR-Dateien in das Suite locale Verzeichnis-->
        <copy todir="${cluster}/modules/locale/" >
            <fileset dir="${locale.dir}">
                <exclude name="${module.jar.basename}"/>
            </fileset>   
        </copy>
    </target>

Somit ist zumindest die Erstellung der übersetzten Dateien automatisiert in ein extra Modul implementiert. Aber installierbar ist das noch nicht. Ich verlasse mich da ganz auf die Fähigkeiten von Tonny und übernehme sein erweitertes Target nbm:

    <target name="nbm" depends="projectized-common.nbm">
<property name="nbm-expand" value="nbm-expand"/>

<!-- 1: NBM Datei extrahieren -->
<unjar src="build/${nbm}" dest="build/${nbm-expand}">
<patternset>
<exclude name="META-INF/**"/>
</patternset>
</unjar>

<!-- 2: Übersetzugen in den passenden Ordner packen -->
<copy todir="build/${nbm-expand}/netbeans/modules/locale/" >
<fileset dir="${cluster}/modules/locale/" />
</copy>

<!-- 3: Nun wieder als JAR zusammensetzen -->
<jar jarfile="build/${nbm}" compress="true">
<fileset dir="build/${nbm-expand}" />
</jar>

<!-- 4: Signieren, wenn gewünscht -->
<antcall target="sign-nbm" />
</target>   

Für das optionale Signieren fehlt noch:

    <target name="sign-nbm" if="keystore">
        <signjar jar="build/${nbm}" keystore="${keystore}" storepass="${storepass}" alias="${nbm_alias}" />
    </target>

Und das war es auch schon!

Das Modul kann nun ganz normal im Update-Center publiziert werden. Es können weitere Sprachen hinzugefügt werden. Nur funktioniert es nicht, dass man mehrere Module mit unterschiedlichen Sprachen installieren kann. Dazu müsste man das Makro umprogrammieren und dort ein Lokalisierungskennzeichen (z.B. _deDE) hinzufügen:

        <jar jarfile="${locale.dir}/@{dep}_${app.name}_deDE.jar" compress="${build.package.compress}">
            <fileset dir="branding/@{module}/@{dep}.jar/"/>
       </jar>

Vielleicht kann man sich dazu auch ein Symbol in den project.properties anlegen. Man darf nur die Sonderbehandlung von core.jar nicht vergessen!

 

Hier noch mal das komplette Build.xml Script, wo alle Module der NetBeans-Plaform übersetzt werden.

Beste Grüße,
  Josch.

JNBB - Joschs NetBeans Blog - August 07, 2008 05:27 AM
BeanDev: Plattform Übersetzung als Plugin

Wie ich schon in einem älteren Blog-Eintrag geschrieben hatte, ist es möglich eine Programm-Suite mit einem Branding so zu versehen, dass man eine Übersetzung der Plattform hat.

Bei dem Verfahren gibt es einen bedeutenden Nachteil: Nur per ZIP-Distribution wird die Übersetzung installiert. Der Anwender kann nicht darüber entscheiden, ob er diese Sprache haben will und kann das auch nicht zurücknehmen (außer er löscht die JAR-Dateien aus dem ./locale-Ordner.

Tonny Kohar zeigt in seinem Blog, wie man für eigene Projekte ein Plugin erstellen kann, um eine Sprachvariante installierbar zu machen.

Das beschriebene Build-Script ist aber sehr auf das Projekt zugeschnitten und skizziert nur den Aufbau. Ich habe mich mal der Sache angenommen und ein Build-Script erstellt, dass generell für alle Projekte verwendet werden kann. Außerdem halte ich mich an die Suite-Branding Konvention die lokalisierten Bundle.properties im branding-Ordner (und nicht unter src) zu halten. Das vereinfacht die Integration in das bestehende Build-System von NetBeans.

Wie Tonny auch schon schreibt, reicht es, erst ein normales Modul zu erstellen. Ich gebe dem Modul immer den Namen der Suite mit der Endung -l10n.Ggf. kann man aber auch das Sprachkennzeichen verwenden (z.B. deDE), damit man später unterschieldiche Übersetzungen in getrennten Installationen anbieten kann.

In der Files Ansicht des Explorers lege ich dann den branding-Ordner an. Im Gegensatz zur Suite wird der branding-Ordner in einem Module nicht automatisch vom Build-Script erkannt. Suite-Projekte starten ein speziellen Build-Abschnitt, wenn der branding-Ordner existiert.

Unter dem branding-Ordner werden nun die Abschnitte der Übersetzungen für die Module abgelegt. Core weicht da ein wenig vom Standard ab, aber die Modul-Übersetzungen der Plattform heißen vom Ordner immer wie die zu erzeugenden Übersetzungs-JAR-Dateien:

Unter core und modules werden nun für die Platform-Module die Bundle_xx_XX.properties-Dateien, ein paar Helpset-Dateien und Bildchen eingefügt. Ich hatte schon mal was vorbereitet (allerdings ohne _de_DE-Anhängsel an den Bundle-Dateien): Ein ZIP-Archiv findet sich hier.

Im Prinzip sollte es so aussehen:

Jetzt geht es ans Eingemachte. Das Build-Script muss erweitert werden.

Dafür nun Build.xml öffnen (findet sich am schnellsten in der Projektansicht unter Important Files).

Direkt unter dem ersten import geht es los. Zunächst benötigen wir die Eigenschaften aus dem Suite-Projekt, damit wir den Application-Namen bekommen:

    <property file="${suite.dir}/nbproject/project.properties" prefix="prjsuite"/>

Dann erzeugen wir ein kleines Makro, damit die Tipparbeit für die Module nicht ausufert:

    <macrodef name="sub-jar">
      <attribute name="dep"/>
      <attribute name="module" default="modules"/>
      <sequential>
        <jar jarfile="${locale.dir}/@{dep}_${app.name}.jar" compress="${build.package.compress}">
            <fileset dir="branding/@{module}/@{dep}.jar/"/>
        </jar>
      </sequential>
    </macrodef>

Nun  hängen wir uns in das Target build-init ein, um zwei Variablen zu füllen:

    <target name="build-init" depends="harness.build-init">
      <property name="locale.dir" value="${basedir}/build/cluster/modules/locale/"/>
      <property name="app.name" value="${prjsuite.app.name}"/>
    </target>

Dabei ist locale.dir das Zielverzeichnis für die Übersetzungen und app.name holt sich einfach den Wert aus den Suite-Properties.

Nun hängen wir uns in das jar-Target, erzeugen das locale.dir und basteln schon für das core.jar die erste Übersetzung. Da kann ich das Makro nicht verwenden, weil der Zielname der Übersetzung anders als die JAR-Datei heißen soll:

   <target name="jar" depends="projectized-common.jar">
        <mkdir dir="${locale.dir}"/>

        <!-- Sonderbehandlung für das core-package -->
        <jar jarfile="${locale.dir}/org-netbeans-core_${app.name}.jar" compress="${build.package.compress}">
            <fileset dir="branding/core/core.jar/"/>
        </jar>

Nun folgen die restlichen Übersetzungen aus dem modules-Ordner. Das funktioniert sehr gut mit dem Makro. Hier ein Auszug:

        <!-- Lokalisierung für die modules packages -->
        <sub-jar dep="org-jdesktop-layout"/>
        <sub-jar dep="org-netbeans-api-progress"/>
        <sub-jar dep="org-netbeans-api-visual"/>
        <sub-jar dep="org-netbeans-core-execution"/>

Als letztes in diesem Target kopiere ich die fertigen Archive in das Suite-Projekt. Das ist nicht unbedingt notwendig, erleichtert aber das Erstellen von ZIP-Distributionen mit fertig übersetzten Archiven:

        <!-- Kopieren der JAR-Dateien in das Suite locale Verzeichnis-->
        <copy todir="${cluster}/modules/locale/" >
            <fileset dir="${locale.dir}">
                <exclude name="${module.jar.basename}"/>
            </fileset>   
        </copy>
    </target>

Somit ist zumindest die Erstellung der übersetzten Dateien automatisiert in ein extra Modul implementiert. Aber installierbar ist das noch nicht. Ich verlasse mich da ganz auf die Fähigkeiten von Tonny und übernehme sein erweitertes Target nbm:

    <target name="nbm" depends="projectized-common.nbm">
<property name="nbm-expand" value="nbm-expand"/>

<!-- 1: NBM Datei extrahieren -->
<unjar src="build/${nbm}" dest="build/${nbm-expand}">
<patternset>
<exclude name="META-INF/**"/>
</patternset>
</unjar>

<!-- 2: Übersetzugen in den passenden Ordner packen -->
<copy todir="build/${nbm-expand}/netbeans/modules/locale/" >
<fileset dir="${cluster}/modules/locale/" />
</copy>

<!-- 3: Nun wieder als JAR zusammensetzen -->
<jar jarfile="build/${nbm}" compress="true">
<fileset dir="build/${nbm-expand}" />
</jar>

<!-- 4: Signieren, wenn gewünscht -->
<antcall target="sign-nbm" />
</target>   

Für das optionale Signieren fehlt noch:

    <target name="sign-nbm" if="keystore">
        <signjar jar="build/${nbm}" keystore="${keystore}" storepass="${storepass}" alias="${nbm_alias}" />
    </target>

Und das war es auch schon!

Das Modul kann nun ganz normal im Update-Center publiziert werden. Es können weitere Sprachen hinzugefügt werden. Nur funktioniert es nicht, dass man mehrere Module mit unterschiedlichen Sprachen installieren kann. Dazu müsste man das Makro umprogrammieren und dort ein Lokalisierungskennzeichen (z.B. _deDE) hinzufügen:

        <jar jarfile="${locale.dir}/@{dep}_${app.name}_deDE.jar" compress="${build.package.compress}">
            <fileset dir="branding/@{module}/@{dep}.jar/"/>
       </jar>

Vielleicht kann man sich dazu auch ein Symbol in den project.properties anlegen. Man darf nur die Sonderbehandlung von core.jar nicht vergessen!

 

Hier noch mal das komplette Build.xml Script, wo alle Module der NetBeans-Plaform übersetzt werden.

Beste Grüße,
  Josch.

JNBB - Joschs NetBeans Blog - August 05, 2008 06:44 AM
BeanDev: Access-Warnungen in Wrapper-Bibliotheken deaktiveren

Eingebundene Wrapper-Bibliotheken nutzen teilweise andere JAR-Bibliotheken, die man selbst im Wrapper nicht nutzt und damit erst gar nicht einbindet. 

Die NetBeans-Plattform erkennt aber diese Abhängigkeiten und die fehlende Zugriffsmöglichkeit der Bibliothek im Wrapper.

Im Build-Log erhält man dann beispielsweise folgende Warnungen:

Warning: com.ibm.media.parser.video.MpegParser cannot access com.ms.security.PermissionID
Warning: com.ibm.media.parser.video.MpegParser cannot access com.ms.security.PolicyEngine
Warning: com.ibm.media.protocol.CloneableSourceStreamAdapter$PushStreamSlave cannot access com.ms.security.PermissionID
Warning: com.ibm.media.protocol.CloneableSourceStreamAdapter$PushStreamSlave cannot access com.ms.security.PolicyEngine

Wenn diese fehlenden Zugriffsmöglichkeiten bewusst akzeptiert werden sollen und man die Warnungen aus dem Log entfernt haben möchte, muss man die project.properties anpassen.

Zuständig für das Deaktivieren der Warnungen ist die Eigenschaft module.jar.verifylinkageignores und erwartet einen Regulären Ausdruck.

Beispiel (z.B. für das Java Media Framework):

module.jar.verifylinkageignores=(com.(ibm|sun).media..*)|(javax.media.rtp.rtcp.SourceDescription)

Diese Eigenschaft wird übrigens vom Ant-Target "verify-class-linkage" im Build-Script verwendet. Der Task "verifyclasslinkage" nutzt den zugewiesenen Wert als ignore-Liste der Klassennamen.

 

 

Beste Grüße,
  Josch.

JNBB - Joschs NetBeans Blog - August 05, 2008 06:44 AM
BeanDev: Access-Warnungen in Wrapper-Bibliotheken deaktiveren

Eingebundene Wrapper-Bibliotheken nutzen teilweise andere JAR-Bibliotheken, die man selbst im Wrapper nicht nutzt und damit erst gar nicht einbindet. 

Die NetBeans-Plattform erkennt aber diese Abhängigkeiten und die fehlende Zugriffsmöglichkeit der Bibliothek im Wrapper.

Im Build-Log erhält man dann beispielsweise folgende Warnungen:

Warning: com.ibm.media.parser.video.MpegParser cannot access com.ms.security.PermissionID
Warning: com.ibm.media.parser.video.MpegParser cannot access com.ms.security.PolicyEngine
Warning: com.ibm.media.protocol.CloneableSourceStreamAdapter$PushStreamSlave cannot access com.ms.security.PermissionID
Warning: com.ibm.media.protocol.CloneableSourceStreamAdapter$PushStreamSlave cannot access com.ms.security.PolicyEngine

Wenn diese fehlenden Zugriffsmöglichkeiten bewusst akzeptiert werden sollen und man die Warnungen aus dem Log entfernt haben möchte, muss man die project.properties anpassen.

Zuständig für das Deaktivieren der Warnungen ist die Eigenschaft module.jar.verifylinkageignores und erwartet einen Regulären Ausdruck.

Beispiel (z.B. für das Java Media Framework):

module.jar.verifylinkageignores=(com.(ibm|sun).media..*)|(javax.media.rtp.rtcp.SourceDescription)

Diese Eigenschaft wird übrigens vom Ant-Target "verify-class-linkage" im Build-Script verwendet. Der Task "verifyclasslinkage" nutzt den zugewiesenen Wert als ignore-Liste der Klassennamen.

 

 

Beste Grüße,
  Josch.

JNBB - Joschs NetBeans Blog - July 31, 2008 09:17 AM
BeanDev: AutoUpdate Dateien automatisch kopieren

Das Build System mit Ant ist so vielfältig zu konfigurieren, da klappt auch ein FTP-Upload der AutoUpdate-Dateien auf den eigenen Server.


Zunächst muss man die Apache Commons Net in den Ant-Classpath hinzufügen. Das geht seit NetBeans 6.0 sehr leicht über den Options-Dialog. Die Binary-Versions von commons-net extrahieren und in den gewünschten Pfad kopieren.

Nun in NetBeans den Options-Dialog öffnen, auf Miscellaneous wechseln und in der Ant-Registerzunge dem Classpath die JAR-Datei hinzufügen.

Von nun an hat man die zusätzlichen Tasks, die in commons-net-x.x.x.jar definiert sind. Ein Task davon ist ftp.

Nun das Build-Script der gewünschten NetBeans Application Platform öffnen (findet sich unter Important Files) und folgendes Target hinzufügen:

    <target name="distribute-dev" description="Distri to developers">
      <tstamp/>
      <ftp
          server="ftp.myserver.de"
          userid="thebuilduser"
          password="theuserpwd"
          remotedir="/updates/dev/myapp"
          verbose="true"
          passive="true">
           
        <fileset dir="build/updates" casesensitive="yes">
          <include name="**/*.nbm"/>
          <include name="**/updates.xml"/>
        </fileset>
     
      </ftp>
    </target>

Da das Build-Script aber auch versioniert wird und es ggf. nicht erwünscht ist, dass irgendwo in einem Repository das Kennwort rumschwirrt, sollte man eine Konstante in den private properties dafür anlegen. Diese Eigenschaften findet man in der Files-Ansicht des Projektes unter nbproject/private/private.properties. Dort einfach folgende Zeile hinzufügen:

dev.ftp.password=myv3rys3cr3tpassw0rd

und das Target des Ant-Scriptes anpassen:

          password="${dev.ftp.password}"

(Übrigens hilft hier das Autovervollständigen der NetBeans IDE)

Jetzt muss man zunächst im Kontext-Menü der Application "Create NBMs" aufrufen, damit alle Dateien im build/updates Ordner erzeugt werden.

Nun kann man mit der rechten Maustaste in der Projects-Ansicht auf das Build-Script klicken und Run Target -> distribute-dev aufrufen.

Wenn alle Daten passen, werden die Dateien auf den ftp-Server kopiert.

Möchte man sich den Schritt mit dem Menüaufruf von "Create NBMs" sparen, trägt man in das Target einfach die Abhängigkeit zu dem nbms-Target ein:

    <target name="distribute-dev" depends="nbms" description="Distri to developers">

Somit erzeugt distribute-dev zunächst immer die aktuellen Updater-Dateien und kopiert sie dann auf den Server.

Das war es auch schon und beste Grüße,
  Josch.

JNBB - Joschs NetBeans Blog - July 31, 2008 09:17 AM
BeanDev: AutoUpdate Dateien automatisch kopieren

Das Build System mit Ant ist so vielfältig zu konfigurieren, da klappt auch ein FTP-Upload der AutoUpdate-Dateien auf den eigenen Server.


Zunächst muss man die Apache Commons Net in den Ant-Classpath hinzufügen. Das geht seit NetBeans 6.0 sehr leicht über den Options-Dialog. Die Binary-Versions von commons-net extrahieren und in den gewünschten Pfad kopieren.

Nun in NetBeans den Options-Dialog öffnen, auf Miscellaneous wechseln und in der Ant-Registerzunge dem Classpath die JAR-Datei hinzufügen.

Von nun an hat man die zusätzlichen Tasks, die in commons-net-x.x.x.jar definiert sind. Ein Task davon ist ftp.

Nun das Build-Script der gewünschten NetBeans Application Platform öffnen (findet sich unter Important Files) und folgendes Target hinzufügen:

    <target name="distribute-dev" description="Distri to developers">
      <tstamp/>
      <ftp
          server="ftp.myserver.de"
          userid="thebuilduser"
          password="theuserpwd"
          remotedir="/updates/dev/myapp"
          verbose="true"
          passive="true">
           
        <fileset dir="build/updates" casesensitive="yes">
          <include name="**/*.nbm"/>
          <include name="**/updates.xml"/>
        </fileset>
     
      </ftp>
    </target>

Da das Build-Script aber auch versioniert wird und es ggf. nicht erwünscht ist, dass irgendwo in einem Repository das Kennwort rumschwirrt, sollte man eine Konstante in den private properties dafür anlegen. Diese Eigenschaften findet man in der Files-Ansicht des Projektes unter nbproject/private/private.properties. Dort einfach folgende Zeile hinzufügen:

dev.ftp.password=myv3rys3cr3tpassw0rd

und das Target des Ant-Scriptes anpassen:

          password="${dev.ftp.password}"

(Übrigens hilft hier das Autovervollständigen der NetBeans IDE)

Jetzt muss man zunächst im Kontext-Menü der Application "Create NBMs" aufrufen, damit alle Dateien im build/updates Ordner erzeugt werden.

Nun kann man mit der rechten Maustaste in der Projects-Ansicht auf das Build-Script klicken und Run Target -> distribute-dev aufrufen.

Wenn alle Daten passen, werden die Dateien auf den ftp-Server kopiert.

Möchte man sich den Schritt mit dem Menüaufruf von "Create NBMs" sparen, trägt man in das Target einfach die Abhängigkeit zu dem nbms-Target ein:

    <target name="distribute-dev" depends="nbms" description="Distri to developers">

Somit erzeugt distribute-dev zunächst immer die aktuellen Updater-Dateien und kopiert sie dann auf den Server.

Das war es auch schon und beste Grüße,
  Josch.

OlliN's » netbeans - July 27, 2008 07:48 PM
Apropos Profiler

Gestern habe ich kurz den Netbeans-Profiler gezeigt. Der ist übrigens einfach so bei der Installation von Netbeans mit dabei und funktioniert tadellos. Vor einer Zeit habe ich mal versucht den Profiler von Eclipse (TPTP) zum Laufen zu bringen. Ich habe es nach einiger Zeit geschafft auf meiner Gentoo-Linux-Büchse eine Applikation nach Memory-Leaks zu untersuchen. Aber es ist schon ein Unterschied zwischen “funktioniert einfach” bei Netbeans und “Lese mal Installationsanleitung und probiere eine Weile bis es läuft” bei Eclipse.

Auch den Subversion-Client von Eclipse wird standardmäßig nicht installiert. Und auf der Website von Eclipse ist derzeit (Version 0.7.1) aus Lizenzgründen nicht mal ein vollständiger Client vorhanden. Man muss noch auf eine andere Seite um sich von dort noch Teile des Clients zu holen. Ist zwar alles dokumentiert, doch auch hier ist es bei Netbeans via Pluginmanager ganz einfach den Client zu installieren.

Gerade für Neueinsteiger ist also Netbeans besser geeignet. Aber auch die “alten” Eclipse-Hasen sollten mal über den Tellerrand in Richtung Netbeans schauen… da läuft Einiges (siehe Scripting) und zum Teil besser (siehe GUI-Builder).

Ach, wenn Netbeans doch nur so viele schöne Einstellungen für die Codeformatierungen hätte wie Eclipse… dann wäre ich überglücklich. Wenn man nämlich wie ich in einem Projekt arbeitet, welches die automatische Formatierung beim Speichern einer Datei vorschreibt und auch noch viele spezifische Formatierungsoptionen von Eclipse benutzt, dann ärgert man sich ab und zu wenn man mit Netbeans die eine oder andere Formatierung noch per Hand machen muss.

Seapegasus Blog - July 23, 2008 04:43 PM
EJB-Artikel im JavaSpektrum

Auf ans Kiosk: NetBeans-Dream-Team-Mitglied Adam Bien hat einen weiteren berühmt-berüchtigten EJB 3-Artikel geschrieben, diesmal bekommt Ihr ihn im JavaSpektrum-Magazin zu Gesicht.

Eine Zusammenfassung findet Ihr hier online; für den vollen Inhalt (inklusive NetBeans-Bildschirmphotos) kauft Ihr das Magazin — oder Ihr ladet für zwoenhalb Euro das PDF runter.

<WÄÄBUNG>Unterstützt einen Entwickler, kauft ein PDF!</WÄÄBUNG> :-D (Adam, kriegst Du da was von?)

Dass ich so lange nicht geblogt habe, lag übrigens daran dass ich "kurz" in Island war. :) Und danach hat sich der D&D-Floh in mein Ohr gesetzt. Also, d.h. konkret, ich habe mich überreden lassen, es demnächst zumindest mal zu probieren. Ich hatte bisher erst einmal eine Uraltversion vom Schwarzen Auge gespielt (mein armer Typie befindet sich immer noch im Limbo in irgendeinem aventurischen Wald, umzingelt von Orks), und hab mir noch keine endgültige Meinung über modernere Editionen gebildet. Mal sehen! Jetzt erst mal eine suuupi-Hintergrundstory erfinden. Es muss etwas besseres geben als "ich bin Vollwaise, alle hassen mich, und ein Ork hat meine Hausaufgaben gefressen"...! ;-)

JNBB - Joschs NetBeans Blog - July 18, 2008 07:05 AM
BeanDev: Vorbelegen von Optionseigenschaften des Autoupdater

Es ist nur eine Kleinigkeit, aber mit der Umstellung in NetBeans 6.0 von der Options-API auf die Preferences-API kann man relativ einfach auch Einstellungen fremder Module beeinflussen.

So brauche ich für meine Plattform-Entwicklungen kürzere Prüfungsintervalle für das Autoupdate. Als Standard ist "Wöchentlich" vorgegeben. Sinnvoller für Anwendungen im Kundenbereich ist aber eine tägliche Prüfung auf Updates.

Folgender 5-Zeiler im Module-Installer der restored() - Methode, löst das Problem, ohne ggf. vom Benutzer eingestellte Werte zu überschreiben

    final String AU = "/org/netbeans/modules/autoupdate";
    final Preferences root = NbPreferences.root();
    if ( root.node (AU).get ("period", null) == null)  {
      root.node (AU).put ("period", "1");
    }

Dabei sind folgende Werte im Quelltext des Autoupdater definiert:

    public static final int EVERY_STARTUP = 0;
    public static final int EVERY_DAY = 1;
    public static final int EVERY_WEEK = 2;
    public static final int EVERY_2WEEKS = 3;
    public static final int EVERY_MONTH = 4;
    public static final int NEVER = 5;

Wenn man schon auf NetBeans 6.1 entwickelt, gibt es noch eine zusätzliche Möglichkeit den Standardwert des Autoupdaters vorzugeben. Man kann das Intervall als System-Eigenschaft dem NetBeans-Launcher mitgeben. Entweder direkt auf der Kommandozeile oder in etc/app.conf (im harness-Ordner):

 

Täglich auf Updates testen:

-J-Dplugin.manager.check.interval=EVERY_DAY 

Die Konstanten sind (wie oben) EVERY_STARTUP, EVERY_DAY, EVERY_WEEK, EVERY_2WEEKS, EVERY_MONTH oder NEVER. Alternativ kann man auch die Minutenzahl angeben (was im Optionsdialog nicht funktioniert). Alle halbe Stunde auf Updates testen:

-J-Dplugin.manager.check.interval=30 

Wenn man nicht nur auf Updates, sondern auch auf neue Plugins testen möchte, muss man folgende Option setzen (seit NetBeans 6.0):

-J-Dplugin.manager.check.new.plugins=true 

Dann erscheint der Balloon-Tooltip auch dann, wenn neue Plugins im Update-Center auftauchen, die noch nicht installiert wurden. Das lässt sich natürlich auch im Module-Installer festlegen (was aber nicht zuverlässig funktionieren muss, wenn das Intervall EVERY_STARTUP ist):

 

public void restored() {
  [...]
  System.getProperties().put ("plugin.manager.check.new.plugins", "true");
}

Beste Grüße,
  Josch.

JNBB - Joschs NetBeans Blog - July 18, 2008 07:05 AM
BeanDev: Vorbelegen von Optionseigenschaften des Autoupdater

Es ist nur eine Kleinigkeit, aber mit der Umstellung in NetBeans 6.0 von der Options-API auf die Preferences-API kann man relativ einfach auch Einstellungen fremder Module beeinflussen.

So brauche ich für meine Plattform-Entwicklungen kürzere Prüfungsintervalle für das Autoupdate. Als Standard ist "Wöchentlich" vorgegeben. Sinnvoller für Anwendungen im Kundenbereich ist aber eine tägliche Prüfung auf Updates.

Folgender 5-Zeiler im Module-Installer der restored() - Methode, löst das Problem, ohne ggf. vom Benutzer eingestellte Werte zu überschreiben

    final String AU = "/org/netbeans/modules/autoupdate";
    final Preferences root = NbPreferences.root();
    if ( root.node (AU).get ("period", null) == null)  {
      root.node (AU).put ("period", "1");
    }

Dabei sind folgende Werte im Quelltext des Autoupdater definiert:

    public static final int EVERY_STARTUP = 0;
    public static final int EVERY_DAY = 1;
    public static final int EVERY_WEEK = 2;
    public static final int EVERY_2WEEKS = 3;
    public static final int EVERY_MONTH = 4;
    public static final int NEVER = 5;

Wenn man schon auf NetBeans 6.1 entwickelt, gibt es noch eine zusätzliche Möglichkeit den Standardwert des Autoupdaters vorzugeben. Man kann das Intervall als System-Eigenschaft dem NetBeans-Launcher mitgeben. Entweder direkt auf der Kommandozeile oder in etc/app.conf (im harness-Ordner):

 

Täglich auf Updates testen:

-J-Dplugin.manager.check.interval=EVERY_DAY 

Die Konstanten sind (wie oben) EVERY_STARTUP, EVERY_DAY, EVERY_WEEK, EVERY_2WEEKS, EVERY_MONTH oder NEVER. Alternativ kann man auch die Minutenzahl angeben (was im Optionsdialog nicht funktioniert). Alle halbe Stunde auf Updates testen:

-J-Dplugin.manager.check.interval=30 

Wenn man nicht nur auf Updates, sondern auch auf neue Plugins testen möchte, muss man folgende Option setzen (seit NetBeans 6.0):

-J-Dplugin.manager.check.new.plugins=true 

Dann erscheint der Balloon-Tooltip auch dann, wenn neue Plugins im Update-Center auftauchen, die noch nicht installiert wurden. Das lässt sich natürlich auch im Module-Installer festlegen (was aber nicht zuverlässig funktionieren muss, wenn das Intervall EVERY_STARTUP ist):

 

public void restored() {
  [...]
  System.getProperties().put ("plugin.manager.check.new.plugins", "true");
}

Beste Grüße,
  Josch.

JNBB - Joschs NetBeans Blog - July 07, 2008 02:54 PM
BeanDev: Neustart der NetBeans-Platform

Leider gibt es laut Mailing-Liste keine offizielle Möglichkeit die NetBeans-Plattform aus dem Programm heraus neu zu starten.

Also muss man sich behelfen und die Sache "zu Fuß" durchziehen.

Am besten erzeugt man sich mit dem Assistenten eine CallableSystemAction mit dem Namen "SystemRestartAction", setzt sie unter die Kategorie "System", und fügt sie als Global Menu Item im Menü "File" hinter den Menüeintrag "Exit" ein.

Wenn man noch ein schönes Icon hat, kann man es noch im Assistenten hinterlegen, nur ein Tastenkürzel würde ich nicht vergeben.

Der Assistent erzeugt nun eine Java-Klasse mit dem Namen (wie sollte es anders sein) SystemRestartAction. Nur die performAction-Methode muss noch implementiert werden:

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import org.openide.LifecycleManager;
import org.openide.util.Exceptions;
  [...]
  public void performAction() {
    String systemName = NbBundle.getMessage(SystemRestartAction.class, "CTL_SystemFileName");
    String homePath = System.getProperty("netbeans.home", ".");
    File base = new File(homePath).getParentFile();
    File bin = new File(base, "bin");
    final File execution = new File(bin, systemName);
    if ( execution.exists() ) {
      Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
        public void run() {
          try {
            new ProcessBuilder(execution.getAbsolutePath()).start();
          } catch (IOException ex) {
            Exceptions.printStackTrace(ex);
          }
        }
      }));
      LifecycleManager.getDefault().exit();
    } else {
      Exceptions.printStackTrace(new FileNotFoundException ("Can't find the launcher " + execution.getAbsolutePath()));
    }
  }

Der Bundle.properties-Wert "CTL_SystemFileName" muss den Namen der ausführbaren Launcher-Datei tragen. Unter Windows und für NetBeans wäre das netbeans.exe. Damit sieht man schon die wichtigste Einschränkung dieser Methode: sie ist abhängig von dem installierten Betriebssystem. Das zweite (nicht so gravierende) Problem ist, dass man wissen muss wie der Launcher heißt.

Die OS-Abhängigkeit kann man mildern, wenn man per org.openide.util.Utilities die Betriebssysteme abfragt. Die Methoden wären isMac(), isWindows() und isUnix(). Wobei man isUnix und isMac zusammenfassen kann, da der Launcher für die beiden Systeme identisch ist.

Damit könnte man folgende Abfrage realisieren:

 String systemName = 
NbBundle.getMessage (SystemRestartAction.class,
Utilities.isWindows()
? "CTL_SystemFileName.Windows"
: "CTL_SystemFileName.MacOrUnix");

In der Bundle.properties steht dann für die NetBeans-IDE:

CTL_SystemFileName.Windows=netbeans.exe
CTL_SystemFileName.MacOrUnix=netbeans

Für die eigene Plattform-Anwendung muss man dann natürlich den eigenen Applikationsnamen aus der "Standalone Application" nehmen. Das ist in den Projekt-Eigenschaften der Branding-Name im Abschnitt "Build".

Beste Grüße,
  Josch.

 

JNBB - Joschs NetBeans Blog - July 07, 2008 02:54 PM
BeanDev: Neustart der NetBeans-Platform

Leider gibt es laut Mailing-Liste keine offizielle Möglichkeit die NetBeans-Plattform aus dem Programm heraus neu zu starten.

Also muss man sich behelfen und die Sache "zu Fuß" durchziehen.

Am besten erzeugt man sich mit dem Assistenten eine CallableSystemAction mit dem Namen "SystemRestartAction", setzt sie unter die Kategorie "System", und fügt sie als Global Menu Item im Menü "File" hinter den Menüeintrag "Exit" ein.

Wenn man noch ein schönes Icon hat, kann man es noch im Assistenten hinterlegen, nur ein Tastenkürzel würde ich nicht vergeben.

Der Assistent erzeugt nun eine Java-Klasse mit dem Namen (wie sollte es anders sein) SystemRestartAction. Nur die performAction-Methode muss noch implementiert werden:

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import org.openide.LifecycleManager;
import org.openide.util.Exceptions;
  [...]
  public void performAction() {
    String systemName = NbBundle.getMessage(SystemRestartAction.class, "CTL_SystemFileName");
    String homePath = System.getProperty("netbeans.home", ".");
    File base = new File(homePath).getParentFile();
    File bin = new File(base, "bin");
    final File execution = new File(bin, systemName);
    if ( execution.exists() ) {
      Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
        public void run() {
          try {
            new ProcessBuilder(execution.getAbsolutePath()).start();
          } catch (IOException ex) {
            Exceptions.printStackTrace(ex);
          }
        }
      }));
      LifecycleManager.getDefault().exit();
    } else {
      Exceptions.printStackTrace(new FileNotFoundException ("Can't find the launcher " + execution.getAbsolutePath()));
    }
  }

Der Bundle.properties-Wert "CTL_SystemFileName" muss den Namen der ausführbaren Launcher-Datei tragen. Unter Windows und für NetBeans wäre das netbeans.exe. Damit sieht man schon die wichtigste Einschränkung dieser Methode: sie ist abhängig von dem installierten Betriebssystem. Das zweite (nicht so gravierende) Problem ist, dass man wissen muss wie der Launcher heißt.

Die OS-Abhängigkeit kann man mildern, wenn man per org.openide.util.Utilities die Betriebssysteme abfragt. Die Methoden wären isMac(), isWindows() und isUnix(). Wobei man isUnix und isMac zusammenfassen kann, da der Launcher für die beiden Systeme identisch ist.

Damit könnte man folgende Abfrage realisieren:

 String systemName = 
NbBundle.getMessage (SystemRestartAction.class,
Utilities.isWindows()
? "CTL_SystemFileName.Windows"
: "CTL_SystemFileName.MacOrUnix");

In der Bundle.properties steht dann für die NetBeans-IDE:

CTL_SystemFileName.Windows=netbeans.exe
CTL_SystemFileName.MacOrUnix=netbeans

Für die eigene Plattform-Anwendung muss man dann natürlich den eigenen Applikationsnamen aus der "Standalone Application" nehmen. Das ist in den Projekt-Eigenschaften der Branding-Name im Abschnitt "Build".

Beste Grüße,
  Josch.

 

JNBB - Joschs NetBeans Blog - July 02, 2008 07:23 AM
BeanDev: Branding - Shortcuts oder Keymap?

Der deklarative Ansatz, die NetBeans-Plattform zu konfigurieren ist sehr praktisch, hat aber auch so seine Tücken. Der Vorteil ist unbestreitbar. Ohne die compilierten Class-Dateien anfassen zu müssen, kann man in XML- und Bundle-Dateien alle "Branding"-Arbeiten durchführen. Allerdings gibt es inzwischen recht verstreute Einrichtungsmöglichkeiten und sind teilweise auch als historisch zu bezeichnen.

Ein Bereich sind die Tastenkürzel, die man Aktionen der NetBeans-Oberfläche zuweisen kann. Ein populärer Bereich ist, in der layer.xml, der Shortcuts-Folder.

Zunächst benötigt man natürlich eine Action-Klasse, die selbst im Actions-Abschnitt deklariert sein sollte:

<filesystem>
    <folder name="Actions">
        <folder name="Window">
            <file name="de-sepix-demo-QueryAction.instance"/>
        </folder>
    </folder>

Nun kann man im Shortcuts-Folder ein Tastenkürzel zuweisen:

    <folder name="Shortcuts">
        <file name="CS-Q.instance­">
            <attr name="instanceClass­"
                stringvalue="de.sepix.demo.QueryAction"/>
        </file>
    </folder>

Somit ist Strg+Umschalt+Q der Aktion "QueryAction" zugewiesen. Die eingentümliche Syntax für das Tastenkürzel wird in Utilities.stringToKey beschrieben.

Allerdings hat dieses Verfahren einige Nachteile. Das Tastenkürzel ist global einmalig zugewiesen (kann also nicht Tastatur-Profilen zugewiesen werden) und wird für den Anwender nicht sichtbar im Menü angezeigt. 

Wenn man nämlich die QueryAction (hier z.B.) im Window-Menü einfügt:

    <folder name="Menu">
        <folder name="Window">
            <file name="QueryAction.shadow">
                <attr name="originalFile"
                    stringvalue=
                        "Actions/Window/de-sepix-demo-QueryAction.instance"/>
            </file>

erscheint zwar der Menüeintrag, aber ohne die Tastenkürzel-Information, dass der Anwender die Eintrag auch per Strg+Umschalt+Q aufrufen kann.

Eine schönere Methode ist es, dem Anwender die Tastenkürzel über Keymaps zur Verfügung zu stellen. Damit kann der Nutzer die Tastenkürzel bequem im Options-Dialog verwalten und sieht diese auch immer in den Menüs zugewiesen:

    <folder name="Keymaps">
        <folder name="Sepix">
            <file name="CS-Q.shadow">
                <attr name="originalFile"
               stringvalue="Actions/Window/de-sepix-demo-QueryAction.instance"/>
            </file>

          
Nun erscheint automatisch im Options-Dialog zusätzlich zur NetBeans-Keymap auch die Sepix-Keymap.

Aber der Anwender muss erst explizit die Keymap "Sepix" auswählen. Aber auch das kann man vorgeben:

    <folder name="Keymaps">
<attr name="currentKeymap" stringvalue="Sepix"/>
        <folder name="Sepix">
            <file name="CS-Q.shadow">

Mit dem Attribut "currentKeymap" wird die Standard-Keymap vorbelegt.

Will man beim Brandig die NetBeans-Keymap vor dem Anwender verstecken, nutzt man dafür das Standard-Verfahren:

    <folder name="Keymaps">
<folder name="NetBeans_hidden"/>
<attr name="currentKeymap" stringvalue="Sepix"/>
        <folder name="Sepix">
            <file name="CS-Q.shadow">

Mit diesem Verfahren kann man für den Anwender bequem zu verwaltende Tastaturkürzel einrichten.

Beste Grüße,
  Josch.

 

JNBB - Joschs NetBeans Blog - July 02, 2008 07:23 AM
BeanDev: Branding - Shortcuts oder Keymap?

Der deklarative Ansatz, die NetBeans-Plattform zu konfigurieren ist sehr praktisch, hat aber auch so seine Tücken. Der Vorteil ist unbestreitbar. Ohne die compilierten Class-Dateien anfassen zu müssen, kann man in XML- und Bundle-Dateien alle "Branding"-Arbeiten durchführen. Allerdings gibt es inzwischen recht verstreute Einrichtungsmöglichkeiten und sind teilweise auch als historisch zu bezeichnen.

Ein Bereich sind die Tastenkürzel, die man Aktionen der NetBeans-Oberfläche zuweisen kann. Ein populärer Bereich ist, in der layer.xml, der Shortcuts-Folder.

Zunächst benötigt man natürlich eine Action-Klasse, die selbst im Actions-Abschnitt deklariert sein sollte:

<filesystem>
    <folder name="Actions">
        <folder name="Window">
            <file name="de-sepix-demo-QueryAction.instance"/>
        </folder>
    </folder>

Nun kann man im Shortcuts-Folder ein Tastenkürzel zuweisen:

    <folder name="Shortcuts">
        <file name="CS-Q.instance­">
            <attr name="instanceClass­"
                stringvalue="de.sepix.demo.QueryAction"/>
        </file>
    </folder>

Somit ist Strg+Umschalt+Q der Aktion "QueryAction" zugewiesen. Die eingentümliche Syntax für das Tastenkürzel wird in Utilities.stringToKey beschrieben.

Allerdings hat dieses Verfahren einige Nachteile. Das Tastenkürzel ist global einmalig zugewiesen (kann also nicht Tastatur-Profilen zugewiesen werden) und wird für den Anwender nicht sichtbar im Menü angezeigt. 

Wenn man nämlich die QueryAction (hier z.B.) im Window-Menü einfügt:

    <folder name="Menu">
        <folder name="Window">
            <file name="QueryAction.shadow">
                <attr name="originalFile"
                    stringvalue=
                        "Actions/Window/de-sepix-demo-QueryAction.instance"/>
            </file>

erscheint zwar der Menüeintrag, aber ohne die Tastenkürzel-Information, dass der Anwender die Eintrag auch per Strg+Umschalt+Q aufrufen kann.

Eine schönere Methode ist es, dem Anwender die Tastenkürzel über Keymaps zur Verfügung zu stellen. Damit kann der Nutzer die Tastenkürzel bequem im Options-Dialog verwalten und sieht diese auch immer in den Menüs zugewiesen:

    <folder name="Keymaps">
        <folder name="Sepix">
            <file name="CS-Q.shadow">
                <attr name="originalFile"
               stringvalue="Actions/Window/de-sepix-demo-QueryAction.instance"/>
            </file>

          
Nun erscheint automatisch im Options-Dialog zusätzlich zur NetBeans-Keymap auch die Sepix-Keymap.

Aber der Anwender muss erst explizit die Keymap "Sepix" auswählen. Aber auch das kann man vorgeben:

    <folder name="Keymaps">
<attr name="currentKeymap" stringvalue="Sepix"/>
        <folder name="Sepix">
            <file name="CS-Q.shadow">

Mit dem Attribut "currentKeymap" wird die Standard-Keymap vorbelegt.

Will man beim Brandig die NetBeans-Keymap vor dem Anwender verstecken, nutzt man dafür das Standard-Verfahren:

    <folder name="Keymaps">
<folder name="NetBeans_hidden"/>
<attr name="currentKeymap" stringvalue="Sepix"/>
        <folder name="Sepix">
            <file name="CS-Q.shadow">

Mit diesem Verfahren kann man für den Anwender bequem zu verwaltende Tastaturkürzel einrichten.

Beste Grüße,
  Josch.

 

JNBB - Joschs NetBeans Blog - June 27, 2008 06:05 AM
BeanDev: NodeAction und Selection Management

Eine nette Klasse, um Nodes mit Aktionen zu versehen ist die NodeAction-Klasse. Und soweit man sich an die Standard-Tutorials hält, wird man erstmal keine Probleme haben - insbesondere wenn sich alles in einem View innerhalb TopComponent läuft.

NodeAction funktioniert aber nur, wenn das zugeordnete Modell des ExplorerManager mit dem Selection Management der NetBeans Plattform verknüpft ist. Dabei ist es letztendlich irrelevant, ob z.B. ein BeanTreeView-Eintrag visuell markiert ist. Wenn der ExplorerManager von seiner Umwelt abgeschnitten ist, klappt es nicht mit einer NodeAction.

Sehr schnell bekommt man das mit, wenn man ein ExplorerManager und zugehöriges View in einer schlichten Panel-Komponente hat. Zwar funktioniert alles erstmal wie erwartet, sogar NodeActions die per

  @Override
  public Action[] getActions(boolean context) {
    return new Action[] {myNodeAction}
  }

an einen Knoten gebunden werden, zeigen sich im Popup, sind aktiv und werden sogar ausgeführt.

Leider stellt man dann im Debugger fest, dass das übergebene Node-Array in der performAction-Methode immer leer ist.

    @Override
    protected void performAction(Node[] nodes) {
      for (Node node : nodes) {
        if ( node.canDestroy() ) {
          Node parent = node.getParentNode();
          parent.getChildren().remove(new Node[]{node});
        }
      }
    }

Obige performAction-Methode würde nie etwas ausführen.

Wie bekommt man aber nun NodeAction dazu, zu erkennen, welche Knoten im ExplorerManager ausgewählt wurden?

Hier eine Möglichkeit:

Das Panel sollte das Interface Lookup.Provider implementieren und, ähnlich wie in den Tutorials zu TopComponents, ein Lookup (generiert aus einem ExplorerManager) zurückgeben:

public class MyTestPanel extends JPanel 
          implements ExplorerManager.Provider, Loopup.Provider {
  private ExplorerManager myNodeModel = new ExplorerManager();
  private BeanTreeView tree = new BeanTreeView();
  private Lookup lookup;
  public MyTestPanel () {
    initComponents();
    myNodeModel = new ExplorerManager();
    lookup = ExplorerUtils.createLookup(explorerManager, new ActionMap());
    myNodeModel .setRootContext(new MyRootNode());
  }

  public ExplorerManager getExplorerManager() {
    return myNodeModel;
  }

  public Lookup getLookup() {
    return lookup;
  }
  [...]
}

Das BeanTreeView benötigt das Interface ExplorerManager.Provider, ansonsten hat das View kein Model. Die Implementation von Lookup.Provider nutzen wir nun in der TopComponent, in der wir unser TestPanel einfügen:

public class TestTopComponent extends TopComponent {
  TestPanel tp = new TestPanel();
  public TestTopComponent() {
    add (tp);
    associateLookup(tp.getLookup()));
  }
}

Das war es auch schon, der Rest läuft automatisch. Die Methode associateLookup reicht das erzeugte Lookup aus unserem Panel durch die getLookup-Methode von TestTopComponent heraus (getLookup ist schon in TopComponent implementiert). 

Es wird aber die Regel sein, dass TopComponents eigene Lookups benötigen oder gar mehrere Panels mit Lookups nutzen. Dabei hilft aber ProxyLookup:

public class TestTopComponent extends TopComponent {
TestPanel tp = new TestPanel();
  InstanceContent content = new InstanceContent();
  public TestTopComponent() {
    add (tp);
    associateLookup(
      new ProxyLookup (
        new AbstractLookup (instanceContent),
        tp.getLookup())
    );
  }
}

Jetzt kann man selber in content seine Objekte speichern, die man global zur Verfügung stellen will und verliert nicht die Verbindung zu den selektierten Nodes in TestPanel.

Beste Grüße,
  Josch.

JNBB - Joschs NetBeans Blog - June 27, 2008 06:05 AM
BeanDev: NodeAction und Selection Management

Eine nette Klasse, um Nodes mit Aktionen zu versehen ist die NodeAction-Klasse. Und soweit man sich an die Standard-Tutorials hält, wird man erstmal keine Probleme haben - insbesondere wenn sich alles in einem View innerhalb TopComponent läuft.

NodeAction funktioniert aber nur, wenn das zugeordnete Modell des ExplorerManager mit dem Selection Management der NetBeans Plattform verknüpft ist. Dabei ist es letztendlich irrelevant, ob z.B. ein BeanTreeView-Eintrag visuell markiert ist. Wenn der ExplorerManager von seiner Umwelt abgeschnitten ist, klappt es nicht mit einer NodeAction.

Sehr schnell bekommt man das mit, wenn man ein ExplorerManager und zugehöriges View in einer schlichten Panel-Komponente hat. Zwar funktioniert alles erstmal wie erwartet, sogar NodeActions die per

  @Override
  public Action[] getActions(boolean context) {
    return new Action[] {myNodeAction}
  }

an einen Knoten gebunden werden, zeigen sich im Popup, sind aktiv und werden sogar ausgeführt.

Leider stellt man dann im Debugger fest, dass das übergebene Node-Array in der performAction-Methode immer leer ist.

    @Override
    protected void performAction(Node[] nodes) {
      for (Node node : nodes) {
        if ( node.canDestroy() ) {
          Node parent = node.getParentNode();
          parent.getChildren().remove(new Node[]{node});
        }
      }
    }

Obige performAction-Methode würde nie etwas ausführen.

Wie bekommt man aber nun NodeAction dazu, zu erkennen, welche Knoten im ExplorerManager ausgewählt wurden?

Hier eine Möglichkeit:

Das Panel sollte das Interface Lookup.Provider implementieren und, ähnlich wie in den Tutorials zu TopComponents, ein Lookup (generiert aus einem ExplorerManager) zurückgeben:

public class MyTestPanel extends JPanel 
          implements ExplorerManager.Provider, Loopup.Provider {
  private ExplorerManager myNodeModel = new ExplorerManager();
  private BeanTreeView tree = new BeanTreeView();
  private Lookup lookup;
  public MyTestPanel () {
    initComponents();
    myNodeModel = new ExplorerManager();
    lookup = ExplorerUtils.createLookup(explorerManager, new ActionMap());
    myNodeModel .setRootContext(new MyRootNode());
  }

  public ExplorerManager getExplorerManager() {
    return myNodeModel;
  }

  public Lookup getLookup() {
    return lookup;
  }
  [...]
}

Das BeanTreeView benötigt das Interface ExplorerManager.Provider, ansonsten hat das View kein Model. Die Implementation von Lookup.Provider nutzen wir nun in der TopComponent, in der wir unser TestPanel einfügen:

public class TestTopComponent extends TopComponent {
  TestPanel tp = new TestPanel();
  public TestTopComponent() {
    add (tp);
    associateLookup(tp.getLookup()));
  }
}

Das war es auch schon, der Rest läuft automatisch. Die Methode associateLookup reicht das erzeugte Lookup aus unserem Panel durch die getLookup-Methode von TestTopComponent heraus (getLookup ist schon in TopComponent implementiert). 

Es wird aber die Regel sein, dass TopComponents eigene Lookups benötigen oder gar mehrere Panels mit Lookups nutzen. Dabei hilft aber ProxyLookup:

public class TestTopComponent extends TopComponent {
TestPanel tp = new TestPanel();
  InstanceContent content = new InstanceContent();
  public TestTopComponent() {
    add (tp);
    associateLookup(
      new ProxyLookup (
        new AbstractLookup (instanceContent),
        tp.getLookup())
    );
  }
}

Jetzt kann man selber in content seine Objekte speichern, die man global zur Verfügung stellen will und verliert nicht die Verbindung zu den selektierten Nodes in TestPanel.

Beste Grüße,
  Josch.

JNBB - Joschs NetBeans Blog - June 26, 2008 08:45 AM
BeanDev: Minimaler Einsatz für Drag and Drop von Nodes

Es funktioniert fast überall in den aktuellen Versionen der NetBeans IDE: Man kann in den Baumansichten die Elemente anklicken und mit gehaltener linker Maustaste an eine andere Position verschieben.

Wenn man das auch für die eigenen BeanTreeView-Komponenten haben will, muss man ein paar Methoden implementieren bzw. überschreiben, damit das funktioniert.

In diesem "Kochrezept" habe ich ein sehr einfaches Modell gewählt, nur einen Root-Node mit einer einfachen Hierachie. Der Root-Node kann sogar ausgeblendet werden.

Man erzeugt ein JPanel oder TopComponent und implementiert  ExplorerManager.Provider. Die Methode getExplorerManager liefert einen Standard-ExplorerManager:

public class Test extends ToComponent 
implements ExplorerManager.Provider {
  private ExplorerManager em = new ExplorerManager();
  public Test() {
    super();
    initComponenents();
    getExplorerManager().setRootContextNode (new RootNode());
  }

  public ExplorerManager getExplorerManager() {
    return em;
  }

  // ...
}

In initComponents() fügt man dieser Komponente z.B. einem BeanTreeView hinzu, damit man überhaupt etwas von diesem Beispiel hat.

Nun benötigen wir die Klasse RootNode, die von AbstractNode abgeleitet wird und ein spezielles Children-Object an AbstractNode weiterreicht. Dies ist die erste Vorraussetzung, damit die untergeordneten Nodes überhaupt eine benutzerdefinierte Reihenfolge erhalten:

public class RootNode extends AbstractNode {
  public RootNode() {
    super (new RootChildren());
  }
}

Zwar ist RootNode noch nicht fertig, aber werfen wir mal einen Blick auf RootChildren:

public class RootChildren extends Index.ArrayChildren{
  public RootChildren() {
    super();
  }
}

Natürlich hätte oben in RootNode auch ein super (new Index.ArrayChildren()); gereicht (zumal RootChildren schon fertig ist), aber wenn man das für eigene Projekte erweitern will, ist es besser man hat eine größere Kontrolle über das Children-Modell.

Index.ArrayChildren implementiert alle Verwaltungsaufgaben, um Positionen von Knoten zu managen. Das Drag and Drop UI der Views benötigen diese Implementation, um die Knoten anhand von Anwenderaktionen anzuordnen.

Bis jetzt wurde also erst die Wurzel des Baumes definiert, und wie die untergeordneten Knoten im Container (Index.ArrayChildren) dieser RootNode gehalten werden.

Jetzt also zu den ChildNodes:

public class ChildNode extends AbstractNode {
  private InstanceContent content;

  public ChildNode (NodeData data) {
    this (data, new InstanceContent());
  }
 
  public ChildNode (NodeData data, InstanceContent content) {
    super (Children.LEAF, new AbstractLookup (content));
    this.content = content;
    content.add(data);
  }

  public NodeData getData() {
    return getLookup().lookup(NodeData.class);
  }
}

Die obigen Konstruktoren entsprechen meinem Lieblingsaufbau von Nodes, die nur im Arbeitsspeicher gehalten werden. Das ist keine Vorraussetzung, um die Knoten per Drag and Drop zu verschieben.

Allerdings muss nun noch folgende Methode ChildNode hinzugefügt werden:

  @Override
  public boolean canCut() {
    return true;
  }

Wenn ChildNode bei canCut immer false zurückgibt (und das ist in AbstractNode so festgelegt), funktioniert schon der erste Drag-Schritt nicht. In NetBeans wird Drag and Drop im Prinzip als Cut and Paste abgebildet.

Damit wir später auch die Nodes unterscheiden können, implementieren wir getName() neu:

  @Override
  public String getName() {
    return getData().getName();
  }

Um die Sache lauffähig zu machen, brauchen wir noch NodeData:

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

  public String getName() {
    return this.name;
  }
}

Jetzt können wir endlich unsere Knoten im Constructor von der Klasse Test anlegen:

  public Test() {
    super();
    initComponenents();
    Node root = new RootNode();
    getExplorerManager().setRootContextNode (root);
  
    root.getChildren().add (new Node[] {
      new ChildNode (new NodeData ("Erster Knoten")),
      new ChildNode (new NodeData ("Zweiter Knoten")),
      new ChildNode (new NodeData ("Dritter Knoten")),
      new ChildNode (new NodeData ("Letzter!!")),
    });
  }

Zwar wird es nicht gern gesehen die add()-Methode zu verwenden, im Falle von Index.Children und diesem Test ist es aber durchaus legitim.

Wenn man nun daraus ein fertiges Beispielprogramm gebastelt hat, wird man feststellen, dass das UI schon grundsätzlich reagiert. Man kann die Knoten per DnD ziehen und dabei zuschauen, wie das BeanTreeView am Zielort eine passende "Einfügemarke" anzeigt. 

Tja, aber das Fallenlassen funktioniert noch nicht. Der Mauszeiger straft uns auch mit einem Verbotszeichen.

Damit das Drag and Drop schlussendlich wirklich funktioniert, muss man dem BeanTreeView über das Cookie-Set des übergeordneten Knotens den Children-Container bekanntgeben. In diesem Fall ist der übergeordnete Knoten natürlich unsere RootNode-Klasse. Dort fügen wir folgende Methode noch ein:

  @Override
  public Cookie getCookie (Class clazz) {
    Children ch = getChildren();

    if (clazz.isInstance(ch)) {
      // hier den Children-Container bekannt machen:
      return (Cookie) ch;
    }
    return super.getCookie(clazz);
  }

Das hätte AbstractNode auch selber machen können...

Jetzt funktioniert es endlich auch mit dem Drop-Teil von Drag and Drop.

Hier nochmal die Zusammenfassung:

Der Children-Container muss ein Index-ArrayChildren sein, damit benutzerdefinierte Positionen festgelegt werden können. Ansonsten wären die Nodes nur nach festen Kriterien sortiert.

Damit der Drag-Teil überhaupt startet, muss in den Nodes canCut überschrieben werden und true zurückgeben.

Und für den Drop-Teil muss das View (hier BeanTreeView) auf den Children-Container zugreifen können, um anhand des Interfaces überhaupt ermitteln zu können, dass Nodes angeordnet werden können.

Das war es auch schon. Viel Spaß mit dem Kochrezept und dem Ergebnis eines modernen UIs.

Beste Grüße,
  Josch.

 

JNBB - Joschs NetBeans Blog - June 26, 2008 08:45 AM
BeanDev: Minimaler Einsatz für Drag and Drop von Nodes

Es funktioniert fast überall in den aktuellen Versionen der NetBeans IDE: Man kann in den Baumansichten die Elemente anklicken und mit gehaltener linker Maustaste an eine andere Position verschieben.

Wenn man das auch für die eigenen BeanTreeView-Komponenten haben will, muss man ein paar Methoden implementieren bzw. überschreiben, damit das funktioniert.

In diesem "Kochrezept" habe ich ein sehr einfaches Modell gewählt, nur einen Root-Node mit einer einfachen Hierachie. Der Root-Node kann sogar ausgeblendet werden.

Man erzeugt ein JPanel oder TopComponent und implementiert  ExplorerManager.Provider. Die Methode getExplorerManager liefert einen Standard-ExplorerManager:

public class Test extends ToComponent 
implements ExplorerManager.Provider {
  private ExplorerManager em = new ExplorerManager();
  public Test() {
    super();
    initComponenents();
    getExplorerManager().setRootContextNode (new RootNode());
  }

  public ExplorerManager getExplorerManager() {
    return em;
  }

  // ...
}

In initComponents() fügt man dieser Komponente z.B. einem BeanTreeView hinzu, damit man überhaupt etwas von diesem Beispiel hat.

Nun benötigen wir die Klasse RootNode, die von AbstractNode abgeleitet wird und ein spezielles Children-Object an AbstractNode weiterreicht. Dies ist die erste Vorraussetzung, damit die untergeordneten Nodes überhaupt eine benutzerdefinierte Reihenfolge erhalten:

public class RootNode extends AbstractNode {
  public RootNode() {
    super (new RootChildren());
  }
}

Zwar ist RootNode noch nicht fertig, aber werfen wir mal einen Blick auf RootChildren:

public class RootChildren extends Index.ArrayChildren{
  public RootChildren() {
    super();
  }
}

Natürlich hätte oben in RootNode auch ein super (new Index.ArrayChildren()); gereicht (zumal RootChildren schon fertig ist), aber wenn man das für eigene Projekte erweitern will, ist es besser man hat eine größere Kontrolle über das Children-Modell.

Index.ArrayChildren implementiert alle Verwaltungsaufgaben, um Positionen von Knoten zu managen. Das Drag and Drop UI der Views benötigen diese Implementation, um die Knoten anhand von Anwenderaktionen anzuordnen.

Bis jetzt wurde also erst die Wurzel des Baumes definiert, und wie die untergeordneten Knoten im Container (Index.ArrayChildren) dieser RootNode gehalten werden.

Jetzt also zu den ChildNodes:

public class ChildNode extends AbstractNode {
  private InstanceContent content;

  public ChildNode (NodeData data) {
    this (data, new InstanceContent());
  }
 
  public ChildNode (NodeData data, InstanceContent content) {
    super (Children.LEAF, new AbstractLookup (content));
    this.content = content;
    content.add(data);
  }

  public NodeData getData() {
    return getLookup().lookup(NodeData.class);
  }
}

Die obigen Konstruktoren entsprechen meinem Lieblingsaufbau von Nodes, die nur im Arbeitsspeicher gehalten werden. Das ist keine Vorraussetzung, um die Knoten per Drag and Drop zu verschieben.

Allerdings muss nun noch folgende Methode ChildNode hinzugefügt werden:

  @Override
  public boolean canCut() {
    return true;
  }

Wenn ChildNode bei canCut immer false zurückgibt (und das ist in AbstractNode so festgelegt), funktioniert schon der erste Drag-Schritt nicht. In NetBeans wird Drag and Drop im Prinzip als Cut and Paste abgebildet.

Damit wir später auch die Nodes unterscheiden können, implementieren wir getName() neu:

  @Override
  public String getName() {
    return getData().getName();
  }

Um die Sache lauffähig zu machen, brauchen wir noch NodeData:

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

  public String getName() {
    return this.name;
  }
}

Jetzt können wir endlich unsere Knoten im Constructor von der Klasse Test anlegen:

  public Test() {
    super();
    initComponenents();
    Node root = new RootNode();
    getExplorerManager().setRootContextNode (root);
  
    root.getChildren().add (new Node[] {
      new ChildNode (new NodeData ("Erster Knoten")),
      new ChildNode (new NodeData ("Zweiter Knoten")),
      new ChildNode (new NodeData ("Dritter Knoten")),
      new ChildNode (new NodeData ("Letzter!!")),
    });
  }

Zwar wird es nicht gern gesehen die add()-Methode zu verwenden, im Falle von Index.Children und diesem Test ist es aber durchaus legitim.

Wenn man nun daraus ein fertiges Beispielprogramm gebastelt hat, wird man feststellen, dass das UI schon grundsätzlich reagiert. Man kann die Knoten per DnD ziehen und dabei zuschauen, wie das BeanTreeView am Zielort eine passende "Einfügemarke" anzeigt. 

Tja, aber das Fallenlassen funktioniert noch nicht. Der Mauszeiger straft uns auch mit einem Verbotszeichen.

Damit das Drag and Drop schlussendlich wirklich funktioniert, muss man dem BeanTreeView über das Cookie-Set des übergeordneten Knotens den Children-Container bekanntgeben. In diesem Fall ist der übergeordnete Knoten natürlich unsere RootNode-Klasse. Dort fügen wir folgende Methode noch ein:

  @Override
  public Cookie getCookie (Class clazz) {
    Children ch = getChildren();

    if (clazz.isInstance(ch)) {
      // hier den Children-Container bekannt machen:
      return (Cookie) ch;
    }
    return super.getCookie(clazz);
  }

Das hätte AbstractNode auch selber machen können...

Jetzt funktioniert es endlich auch mit dem Drop-Teil von Drag and Drop.

Hier nochmal die Zusammenfassung:

Der Children-Container muss ein Index-ArrayChildren sein, damit benutzerdefinierte Positionen festgelegt werden können. Ansonsten wären die Nodes nur nach festen Kriterien sortiert.

Damit der Drag-Teil überhaupt startet, muss in den Nodes canCut überschrieben werden und true zurückgeben.

Und für den Drop-Teil muss das View (hier BeanTreeView) auf den Children-Container zugreifen können, um anhand des Interfaces überhaupt ermitteln zu können, dass Nodes angeordnet werden können.

Das war es auch schon. Viel Spaß mit dem Kochrezept und dem Ergebnis eines modernen UIs.

Beste Grüße,
  Josch.

 

OlliN's » netbeans - June 25, 2008 10:08 AM
Netbeans Fonts

Rohan Ranade (alias gridbag) hat in seinem letzten Blogeintrag nach Kombinationen der Schriften in Netbeans unter Linux gefragt. Ich benutze ProFont von Tobias Jung unter Gentoo Linux. Die Schrift ist wirklich gut zum Programmieren.

Hier also meine Einstellungen:

my netbeans fonts

netbeans fonts config

JNBB - Joschs NetBeans Blog - June 20, 2008 01:47 PM
BeanDev: Notfall-Branding - NB 6.1 Plattform zu 90% auf Deutsch

Wenn man mit NetBeans 6.1 bereits entwickelt, ist es leider recht unschön, dass man keine lokalisierte Version zur Verfügung hat.

Für einen Prototypen benötigte ich aber unbedingt eine, größtenteils, in das Deutsche übersetzte Plattform. 

Was man auf keinen Fall machen darf, ist das Localization Kit für NetBeans 6.0x zu installieren. Es werden dann die Module der plattform7 installiert, was für RichClient Anwendungen der build-Tod ist. Da muss man erstmal wieder kräftig von Hand aufräumen.

Ich habe mir also das platform7-de.nbm geschnappt, entpackt, alle jarFiles in  Ordner gewandelt und alle Bundle_de.properties in Bundle.properties umbenannt.

Wenn man nun diese 41 lokalisierten Bundle-Ordner in das branding/modules - Verzeichnis kopiert hat man zumindest eine leidlich gute Übersetzung 90% aller plattformspezifischen Texte. Was nicht übersetzt ist, sind neue Labels der 6.1'er Version und die paar Texte, die schon bei 6.0.1 und 6.0 durchs Raster gefallen waren.

Die Dateien muss man im File-Explorer (nicht Projects) unter branding einfügen.

 

Hier das Archiv modules.zip mit den Bundle.properties.

Ich muss aber darauf hinweisen, dass das nix für eine produktive Anwendung ist!

Für meinen Prototypen hat es gereicht. :-)

Beste Grüße,
  Josch.

 

 

 

JNBB - Joschs NetBeans Blog - June 20, 2008 01:47 PM
BeanDev: Notfall-Branding - NB 6.1 Plattform zu 90% auf Deutsch

Wenn man mit NetBeans 6.1 bereits entwickelt, ist es leider recht unschön, dass man keine lokalisierte Version zur Verfügung hat.

Für einen Prototypen benötigte ich aber unbedingt eine, größtenteils, in das Deutsche übersetzte Plattform. 

Was man auf keinen Fall machen darf, ist das Localization Kit für NetBeans 6.0x zu installieren. Es werden dann die Module der plattform7 installiert, was für RichClient Anwendungen der build-Tod ist. Da muss man erstmal wieder kräftig von Hand aufräumen.

Ich habe mir also das platform7-de.nbm geschnappt, entpackt, alle jarFiles in  Ordner gewandelt und alle Bundle_de.properties in Bundle.properties umbenannt.

Wenn man nun diese 41 lokalisierten Bundle-Ordner in das branding/modules - Verzeichnis kopiert hat man zumindest eine leidlich gute Übersetzung 90% aller plattformspezifischen Texte. Was nicht übersetzt ist, sind neue Labels der 6.1'er Version und die paar Texte, die schon bei 6.0.1 und 6.0 durchs Raster gefallen waren.

Die Dateien muss man im File-Explorer (nicht Projects) unter branding einfügen.

 

Hier das Archiv modules.zip mit den Bundle.properties.

Ich muss aber darauf hinweisen, dass das nix für eine produktive Anwendung ist!

Für meinen Prototypen hat es gereicht. :-)

Beste Grüße,
  Josch.

 

 

 

JNBB - Joschs NetBeans Blog - June 08, 2008 07:57 AM
BeanDev: TopComponent und Paletten (wie ich sie zusammen brachte)

Seit sechs Monaten beschäftige ich mich damit, meine alten Projekte soweit umzubauen, dass diese relativ einfach in die NetBeans Plattform integriert werden können. Dieses Refactoring hat eigentlich nichts mit NB zu tun, es geht nur darum eine vernünftige Modularisierung zu erreichen.

In den letzten drei Monaten habe ich schon verstärkt neue Projekte ausschließlich auf der NetBeans 6.0 (und nun 6.1) Plattform implementiert. Immer wieder stoße ich dabei auf Probleme, bei denen mir kein Buch, kein Tutorial und kein Wiki helfen will oder kann.

Insbesondere die Tutorials, mit ihrem Anspruch sehr einfach aufgebaut sein zu wollen, schicken immer wieder auf den Irrweg und hinterlassen manchmal einen verstörten Programmierer mit verzweifeltem Gesichtsausdruck, ausgerauften Haaren und der letztendlichen Erkenntnis: So einfach ist das nun auch wieder nicht.

Dazu gehören auch Paletten und ihre (nahezu mystische) Fähigkeit, sich automatisch einzublenden, wenn man ein TopComponent-Fenster öffnet.

Jeder NB-Entwickler kennt die netten Helferlein, die z.B. beim Öffnen des Formeditors sich zusätzlich einblenden. So was wollte ich auch in meiner Anwendung haben. Es geht um eine Palette, die Funktionseinheiten für einen Query-Builder liefern soll. Die Palette hat mehrere Kategorien, aus denen ein Anwender unterschiedlichste Abfrage-Attribute zusammenklicken kann, die letztendlich nur vorprogrammierte SQL-Anweisungen sind.

Diese Abfrage-Attribute kann der Anwender in einem Query-Builder-Fenster (BeanTreeView in einem TopComponent mit ExplorerManger) nach gutdünken zusammenfügen. So weit so gut. Das Ergebnis der Klickorgie kann der geneigte Anwender dann in einem extra TopComponent-Fenster betrachten.

Folgende Aufteilung hatte ich mir vorgestellt. Der Query-Builder mit seinem BeanTreeView ist links angedockt. Die Palette dockt rechts an und spätestens bei der "Abfrage ausführen"-Aktion soll sich das Ergebnis-Fenster im Editor-Bereich öffnen und die ermittelten Datensätze anzeigen.

Wie man so etwas (größtenteils) deklarativ erledigt erklären vielfältige "Window"-Tutorials. Hier z.B. ein Tutorial.

Wie man eine Palette programmiert und wie diese automatisch zu einem TopComponent geöffnet wird, findet man in diesem Tutorial.

Nun ja, einfach zu verstehen, wenn man erstmal dahinter gestiegen ist, wie das mit dem Layer.xml und Lookup-Registrierungen funktioniert. Dann geht das tatsächlich leicht von der Hand.

... nur, dass es bei mir nicht funktioniert hat.

Jedes mal, wenn ich den Query-Builder geöffnet hatte, zeigte sich nicht die zugeordnete Palette. Nach stundenlangem Debuggen konnte ich auch definitiv ausschließen, dass mir versteckte Exceptions oder falsche Lookup-Registrierungen Steine in den Weg werfen. Es war einfach alles so programmiert, wie es die Autoren der Tutorials vorgemacht haben.

Nach einer Mütze voll Schlaf holte ich mir die Beispielprogramme von der netbeans.org Seite (die höhnisch lächelnd meine Inkompetenz deutlich machten in dem sie natürlich funktionierten) und verglich diese mit meinem Quelltext. Nur 10 Minuten (und durch einen glücklichen Zufall oder Eingebung?) später hatte ich einen kleinen Unterschied festgestellt. Die TopComponents der Beispiele, die eine Palette zum Anzeigen animieren sollen wurden immer im Mode "editor" deklariert. D.h. die TopComponents erschienen im Center-Bereich der Anwendung und wurden nicht an der linken Seite angedockt. Und das war das Problem.

Man merke sich folgendes:

Die automatische deklarative Aktivierung einer Palette zu einem TopComponent funktioniert nur dann, wenn das TopComponent in der *.wstcref-Datei als Mode "editor" registriert wurde.

Danke. Das hätte man ja auch mal erwähnen können.

Da die Anwendung erstmal fertig werden musste, habe ich das Design ein wenig geändert und meinen Query-Builder tatsächlich im "editor"-Bereich angezeigt. Und nun klappte es auch mit der Nachbarin "Palette".

Beste Grüße,
  Josch.

OlliN's » netbeans - April 29, 2008 07:32 AM
package org.jdesktop.beansbinding does not exist

Wer in einer Netbeans Platform Application das Beans Binding benutzen will, stösst auf folgenden Fehler wenn man in Matisse binden möchte:

...package org.jdesktop.beansbinding does not exist

Das entsprechende JAR wird nicht gefunden. Und da es derzeit kein Modul für das Beans Binding gibt, muss man das JAR-File in einem Library Wrapper Module selber dem Projekt hinzufügen. Die entsprechende Datei ist unter <netbeanshome>/java2/modules/ext/beansbinding-1.2.1.jar zu finden. Also legt man in seiner Suite ein neues Library Wrapper Module an und nimmt diese Datei als Jar. Alle Module die das Binding benutzen wollen können nun unter den Properties–>Libraries–>Add Dependency diese Library anziehen und schon ist der Fehler weg.

OlliN's » netbeans - April 28, 2008 02:55 PM
Netbeans 6.1 ist da

Zwar ist Netbeans 6.1 noch nicht auf der Frontpage, downloaden kann man es aber schon:

Und installiert:

about_netbeans_61