Beispiel: JavaHelp


Inhalt:

Erstellen der Docbook-Hilfedatei
JavaHelp generieren
Verwendung im Programm
Mehr Docbook-Magie
Komplettes Beispiel

Hier wird der Aufbau einer Online-Hilfe über JavaHelp gezeigt.
JavaHelp-Seite von Sun: https://javahelp.dev.java.net/

Statt alle benötigten Hilfedateien selbst zu erstellen, gehen wir den Umweg über Docbook (http://www.docbook.org). Dies ist ein allgemeiner Standard für die Erstellung von technischen Dokumentationen über XML-Dateien (http://en.wikipedia.org/wiki/Docbook). Aus der Docbook-XML-Datei werden über eine XSLT-Transformation die HTML-Dateien der Hilfe sowie der Navigationsbaum generiert. Das Ergebnis dieser Transformation wird in eine JAR-Datei umgewandelt und mittels JavaHelp ins Programm eingebunden.

Benötigte Komponenten:
Alle Komponenten (außer dem JDK ;-) ) gibt es hier fertig gepackt (15 MB).

Weitere nützliche Links:


Erstellen der Docbook-Hilfedatei

Am besten fügt man die Docbook-XML-Datei (in meinem Beispiel: "JavaHelpTest.xml") ebenfalls dem Projekt (auf der obersten Ebene des Projekts) zu, denn Eclipse bietet einen brauchbaren XML-Editor.

Aktuell ist Version 5.0 des Docbook-Standards. Entsprechend verweisen wir auf die XSD dieser Version. Der Rumpf der XML-Datei sieht so aus:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>

<book xmlns="http://docbook.org/ns/docbook"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://docbook.org/ns/docbook http://www.docbook.org/xml/5.0/xsd/docbook.xsd"
	version="5.0">

	<info>
		<title>Beispiel für JavaHelp</title>
	</info>

	<chapter xml:id="javahelptest">
		<title>Java Help Test</title>
		<para>
			Dieses Progrämmelchen demonstriert die Grundlagen von JavaHelp.
		</para>
	</chapter>
</book>
Über das Element "info" werden allgemeine JavaHelp-Informationen angegeben, der "title" wird später auch der Titel des JavaHelp-Fensters sein.

Damit das Dokument wohlgeformt ist, muss mindestens ein "chapter" angegeben sein, in diesem ein Titel "title" und ein Inhalt-Abschnitt "para". Jedes Chapter ergibt eine Seite in der Hilfedatei und außerdem einen Eintrag auf der obersten Ebene im Table of Content (TOC).
Weitere Details folgen im Abschnitt
Mehr Docbook-Magie


JavaHelp generieren

Jetzt wird mittels XSLT-Transformation und Saxon aus der Docbook-Datei die JavaHelp erstellt.
Das folgende Script liegt in meinem Beispiel im Projektverzeichnis, die generierten JavaHelp-Dateien landen in einem Unterverzeichnis "Generated" im Projektverzeichnis.
Im ersten Schritt werden Dateien aus einem eventuellen vorherigen Lauf erzeugt.
Danach wird die XSLT-Transformation durchgeführt.
Am Schluss wird mittels des Tools "JavaHelp Indexer" aus dem JavaHelp-Paket ein Volltextindex erzeugt.
REM Verzeichnis "Generated" erzeugen, wenn noch nicht vorhanden
if not exist .\Generated mkdir Generated

cd .\Generated
REM Altes generiertes Zeug löschen:
del *.html
del *.xml
del *.jhm
del *.hs
del *.jar
rd /S /Q JavaHelpSearch 

REM XSLT-Transformation laufenlassen:
set DOCBOOK_XSL_HOME=C:\temp\JavaHelp\docbook-xsl-1.75.2
set CLASSPATH=%CLASSPATH%;C:\temp\JavaHelp\Saxon6.5.5\saxon.jar

java com.icl.saxon.StyleSheet ..\JavaHelpTest.xml %DOCBOOK_XSL_HOME%\javahelp\javahelp.xsl

REM HelpIndex:
set JAVAHELP_HOME=C:\temp\JavaHelp\jh2.0
java -jar %JAVAHELP_HOME%\javahelp\bin\jhindexer.jar .

pause

Jetzt liegen im Unterverzeichnis "Generated" die HTML-Dateien der Hilfe, außerdem generierte Descriptoren der Java-Hilfe (die wir sonst hätten händisch erstellen müssen):
Das Unterverzeichnis "JavaHelpSearch" enthält den generierten Index.


Jetzt können wir die Hilfe-Datei anzeigen:

set JAVAHELP_HOME=C:\temp\JavaHelp\jh2.0

java -jar %JAVAHELP_HOME%\demos\bin\hsviewer.jar -helpset .\Generated\jhelpset.hs

pause
Es sollte dieses Fenster erscheinen:
JavaHelp-Fenster
Im dritten Karteireiter haben wir sogar schon eine schicke Volltextsuche.


Am Schluss wird das Ganze in eine JAR-Datei gepackt (folgende Batch in das Projektverzeichnis legen):
cd Generated

set JAR_HOME="C:\Program Files (x86)\Java\jdk1.6.0_20\bin\"
%JAR_HOME%jar cf JavaHelpTestHilfe.jar *.*

pause


Verwendung im Programm

Initialisierung:
Zuerst einmal müssen wir die Datei "jh.jar" aus dem JavaHelp-Paket (Unterverzeichnis "jh2.0\javahelp\lib") in unserem Projekt zum Build Path zufügen.

Anschließend wird die Hilfe-Anbindung initialisiert. Dazu habe ich in meinem Beispiel im Hauptfenster zwei public static (grusel) Variablen deklariert:
  public static javax.help.HelpSet helpSet = null;
  public static javax.help.HelpBroker helpBroker = null;
Im der Main-Methode wird das HelpSet initialisiert:
    java.net.URL hsURL = HelpSet.findHelpSet(null, "jhelpset.hs");
    this.helpSet = new HelpSet(null, hsURL );
    this.helpBroker = this.helpSet.createHelpBroker();
Damit dieser Code läuft, MUSS die oben generierte JAR-Datei der Hilfe dem Classpath des Projekts zugefügt werden!

Hilfe über Button anzeigen:
Im Hauptfenster soll es einen Button geben, der beim Klick die Hilfe aufruft. Der Code dafür sieht so aus:
    JFrameJavaHelp.helpBroker.enableHelpOnButton (buttonHelp, "javahelptest", JFrameJavaHelp.helpSet);
Wichtig ist der zweite Parameter: er definiert eine Kapitel- bzw. Abschnitt-ID innerhalb der Hilfe-Datei, im Beispiel als "javahelptest" (siehe oben). Pro Dialog/Funktion sollte hier natürlich ein kontextbezogenes Kapitel gewählt werden.

F1-Hilfe:
Mit folgendem Codeschnipsel wird F1 im gesamten RootPane des aktuellen Fensters registiert und mit einem Abschnitt mit der ID "javahelptest" verbunden:
    JFrameJavaHelp.helpBroker.enableHelpKey (this.getRootPane(), "javahelptest", JFrameJavaHelp.helpSet);


Mehr Docbook-Magie

Weitere Table-of-Content-Ebenen
Innerhalb eines Chapters landet die erste "sect1" auf der gleichen HTML-Seite wie die "chapter"-Überschrift und die Links zu den weiteren Sektionen, alle weiteren "sect1"-Elemente werden zu eigenen HTML-Dateien/Hilfe-Seiten.

Falls man einen Abschnitt "sect1" innerhalb eines "chapter" tiefer schachteln will, so kann man weitere "sectx"-Elemente schachteln (das geht bis zu fünf Ebenen tief):
<?xml version="1.0" encoding="UTF-8" standalone="no"?>

<book xmlns="http://docbook.org/ns/docbook" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://docbook.org/ns/docbook http://www.docbook.org/xml/5.0/xsd/docbook.xsd" version="5.0">

	<info>
		<title>Beispiel für JavaHelp</title>
	</info>

	<chapter xml:id="schachtelung">
		<title>Beispiel für eine Reihe von Schachtelungen</title>
		<sect1 xml:id="schachtelung1">
			<title>Erster Unterabschnitt</title>
			<sect2>
				<title>Unterabschnitt 1.1</title>
				<sect3>
					<title>Unterabschnitt 1.1.1</title>
					<sect4>
						<title>Unterabschnitt 1.1.1.1</title>
						<sect5>
							<title>Unterabschnitt 1.1.1.1.1</title>
							<simplesect>
								<para>Whow, hier stecken ganz schön tolle Dinge</para>
							</simplesect>
						</sect5>

						<sect5>
							<title>Unterabschnitt 1.1.1.1.2</title>
							<simplesect>
								<para>Und hier kommt noch mehr!</para>
							</simplesect>
						</sect5>
					</sect4>

				</sect3>

				<sect3>
					<title>Unterabschnitt 1.1.2</title>
					<sect4>
						<title>Unterabschnitt 1.1.2.1</title>
						<sect5>
							<title>Unterabschnitt 1.1.2.1.1</title>
							<simplesect>
								<para>Auch hier gibt es lustiges zu entdecken!</para>
							</simplesect>
						</sect5>

						<sect5>
							<title>Unterabschnitt 1.1.2.1.2</title>
							<simplesect>
								<para>Obwohl, lustig ist das nicht!</para>
							</simplesect>
						</sect5>
					</sect4>

				</sect3>
			</sect2>
		</sect1>
	</chapter>
</book>
In der untersten Ebene "sect5" sind dabei Inhalte in "simplesect" (statt in "para") zu platzieren.

Das Ergebnis sieht so aus:
bis zu sect5


Bilder
Bilder werden durch folgenden Ausdruck eingebunden:
	<mediaobject><imageobject><imagedata fileref="images/bild.png"/></imageobject></mediaobject>
Wenn Bilder verwendet werden, ist das Konzept für die Erstellung der Hilfe zu erweitern. Ich würde empfehlen, die Bilder im Projekt zu halten, z.B. in einem Unterverzeichnis "helpimages" auf der obersten Ebene. Erst beim Erzeugen der JAR-Datei werden die Bilder dann eingebunden. Dazu müssen sie aus dem Bilder-Verzeichnis erstmal in ein Unterverzeichnis der generierten Hilfedateien kopiert werden. Auf dieses Verzeichnis verweist der relative Pfad im "imageobject"-Element übrigens. Die modifizierte Datei für das Erstellen der JAR könnte so aussehen:
cd Generated

REM Bilder kopieren:
mkdir images
copy ..\helpimages .\images

set JAR_HOME="C:\Program Files (x86)\Java\jdk1.6.0_20\bin\"
%JAR_HOME%jar cf JavaHelpTestHilfe.jar *.*

pause

Auflistungen
Simple Listen (mit Bullets als Trenner) werden so deklariert:
	<itemizedlist>
		<listitem><para>Element 1</para></listitem>
		<listitem><para>Element 1</para></listitem>
		<listitem><para>Element 1</para></listitem>
	</itemizedlist>
Das Ergebnis sieht so aus:
itemizedlist

Die weiteren Möglichkeiten (Tabellen etc.) seien eurem Forscherdrang überlassen ;-).


Komplettes Beispiel

"JavaHelpTest.xml" aus meinem Beispiel sieht so aus:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>

<book xmlns="http://docbook.org/ns/docbook"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://docbook.org/ns/docbook http://www.docbook.org/xml/5.0/xsd/docbook.xsd"
	version="5.0">

	<info>
		<title>Beispiel für JavaHelp</title>
	</info>

	<chapter xml:id="javahelptest">
		<title>Java Help Test</title>
		<para>
			Dieses Progrämmelchen demonstriert die Grundlagen von
			JavaHelp.
		</para>
	</chapter>

	<chapter xml:id="hilfeimdialog">
		<title>Ein wichtiger Dialog</title>
		<sect1 xml:id="hilfeimdialog1">
			<title>Erster Unterabschnitt</title>
			<para>
				Dieser Dialog ist sowas von wichtig, dass er sogar zwei
				Hilfe-Abschnitte hat!
			</para>
		</sect1>
		<sect1 xml:id="hilfeimdialog2">
			<title>Zweiter Unterabschnitt</title>
			<para>
				Dies hier ist der zweite Hilfe-Abschnitt (natürlich
				genauso sinnlos wie der erste Abschnitt).
			</para>
		</sect1>
	</chapter>
</book>

Das Hauptfenster meiner Anwendung sieht so aus:
package de.hsrm.cs.swtprojekt.knauf.javahelp;

import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.net.URL;

import javax.help.HelpBroker;
import javax.help.HelpSet;
import javax.swing.JButton;
import javax.swing.JFrame;


/**Hauptfenster des JavaHelp-Testprogramms.
 * 
 * @author Wolfgang Knauf
 *
 */
public class JFrameJavaHelp extends JFrame
{
  /**Serial Version (damit Eclipse nicht meckert) */
  private static final long serialVersionUID = 1L;


  /**JavaHelp: Helpset */
  public static HelpSet helpSet = null;

  /**JavaHelp: HelpBroker */
  public static HelpBroker helpBroker = null;

  
  /**Konstruktor, initialisiert das Fenster.
   * 
   */
  public JFrameJavaHelp()
  {
    this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    this.setPreferredSize(new Dimension(400, 200));
    this.setMinimumSize(new Dimension(400, 200));
    
    //GridBayLayout definieren für volle Fenstergröße:
    GridBagLayout layout = new GridBagLayout();
    this.getContentPane().setLayout(layout);
    //Einen Button zufügen:
    JButton buttonShowDialog = new JButton("Dialog anzeigen");
    buttonShowDialog.addActionListener( new ActionListener()
    {

      @Override
      public void actionPerformed(ActionEvent e)
      {
        JDialogMoreHelp dialog = new JDialogMoreHelp(null);
        dialog.setVisible(true);
      }
      
    });
    
    GridBagConstraints gridBagConstraintsButtonDialog = new GridBagConstraints (0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0);
    this.getContentPane().add(buttonShowDialog, gridBagConstraintsButtonDialog);
    
    
    //Noch einen Button, der ein wenig Hilfe anzeigen soll:
    JButton buttonHelp = new JButton("Hilfe");
    //Hier brauchen wir keinen ActionListener, es reicht, den Button mit der Hilfe zu verknüpfen!
    JFrameJavaHelp.helpBroker.enableHelpOnButton (buttonHelp, "javahelptest", JFrameJavaHelp.helpSet );
    
    GridBagConstraints gridBagConstraintsButtonHelp = new GridBagConstraints (0, 1, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0);
    this.getContentPane().add(buttonHelp, gridBagConstraintsButtonHelp);
    
    //Am Schluss "F1" im gesamten Fenster aktivieren:
    JFrameJavaHelp.helpBroker.enableHelpKey (this.getRootPane(), "javahelptest", JFrameJavaHelp.helpSet);
  }
  
  /**Main-Methode. Zeigt das Hauptfenster an.
   * @param args
   */
  public static void main(String[] args)
  {
    try
    {
      URL hsURL = HelpSet.findHelpSet(null, "jhelpset.hs");
      JFrameJavaHelp.helpSet = new HelpSet(null, hsURL );
      JFrameJavaHelp.helpBroker = JFrameJavaHelp.helpSet.createHelpBroker();
    }
    catch (Exception ex)
    {
      ex.printStackTrace();
    }
    
    JFrameJavaHelp frame = new JFrameJavaHelp();

    frame.pack();
    frame.setVisible(true);
  }

}

Der Dialog "JDialogMoreHelp" sieht so aus:
package de.hsrm.cs.swtprojekt.knauf.javahelp;

import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;

import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;

/**Dialog für noch mehr Hilfe.
 * Kann nix außer um Hilfe zu rufen.
 * 
 * @author Wolfgang Knauf
 *
 */
public class JDialogMoreHelp extends JDialog
{
  /**Serial Version (damit Eclipse nicht meckert) */
  private static final long serialVersionUID = 1L;

  /**Konstruktor.
   * @param frameParent Parentfenster
   */
  public JDialogMoreHelp(JFrame frameParent)
  {
    super (frameParent);
    
    this.setTitle("Dialog mit mehr Hilfe");
    this.setModal(true);
    this.setPreferredSize(new Dimension(200, 200));
    this.setMinimumSize(new Dimension(200, 200));
    
    //GridBayLayout definieren für volle Fenstergröße:
    GridBagLayout layout = new GridBagLayout();
    this.getContentPane().setLayout(layout);
    
    //Jetzt werden sogar drei Hilfe-Buttons reingehängt:
    JButton buttonHelp = new JButton("Hilfe zum Dialog");
    //Hier brauchen wir keinen ActionListener, es reicht, den Button mit der Hilfe zu verknüpfen!
    JFrameJavaHelp.helpBroker.enableHelpOnButton (buttonHelp, "hilfeimdialog", JFrameJavaHelp.helpSet );
    
    GridBagConstraints gridBagConstraintsHilfe = new GridBagConstraints (0, 0, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0);
    this.getContentPane().add(buttonHelp, gridBagConstraintsHilfe);
    
    
    JButton buttonHelp1 = new JButton("Detail-Hilfe 1");
    JFrameJavaHelp.helpBroker.enableHelpOnButton (buttonHelp1, "hilfeimdialog1", JFrameJavaHelp.helpSet );
    GridBagConstraints gridBagConstraintsHilfe1 = new GridBagConstraints (0, 1, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0);
    this.getContentPane().add(buttonHelp1, gridBagConstraintsHilfe1);
    
    JButton buttonHelp2 = new JButton("Detail-Hilfe 2");
    JFrameJavaHelp.helpBroker.enableHelpOnButton (buttonHelp2, "hilfeimdialog2", JFrameJavaHelp.helpSet );
    
    GridBagConstraints gridBagConstraintsHilfe2 = new GridBagConstraints (0, 2, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0);
    this.getContentPane().add(buttonHelp2, gridBagConstraintsHilfe2);
    
    //Am Schluss "F1" im gesamten Fenster aktivieren:
    JFrameJavaHelp.helpBroker.enableHelpKey (this.getRootPane(), "hilfeimdialog", JFrameJavaHelp.helpSet);
  }

}

Stand 20.06.2010
Historie:
03.06.2008: Erstellt
05.06.2008: Tippfehler korrigiert.
10.06.2009: Update auf "docbook-xsl-1.75.1", docbook.xsd von "http://www.docbook.org/xml/5.0/xsd/docbook.xsd", fehlendes "import"-Statement im Beispielprogramm.
17.06.2009: Rahmen um Batchdatei-Inhalte, GridBagContraints-Variable im Beispielprogramm sinnvoller benannt.
20.06.2010: docbook-xsl-1.75.2, broken Links zu JavaHelp repariert, Namespace des Beispielprogramms angepaßt.