JXMLPad 

Version 4.0

Site : http://www.jxmlpad.com

Tutorial 1 : Basic usage | Tutorial 2 : Creating a JSP editor

Features :

- JXMLPad is a swing component for editing XML documents.
You can get a registered royalty free version at : http://www.jxmlpad.com/buy.html

- If you need to edit a same XML document with custom editors depending the current node,
look at our open-source framework JXMLAppKit.

I. Usage
II. Actions
III. Helper
IV. Status bar
V. LookAndFeel
VI. Component properties
VII.  XML integrity
VIII. Custom Node Editor
IX. Customize the parser
X. Localized the editor messages
XI. Shared a tree
XII. Bookmarks
XIII. Accessing a schema/dtd by a classpath...

I. Usage

JXMLPad is composed of a main container and a set of elements :
  1. The main container : XMLContainer
  2. A default toolbar inside the XMLContainer
  3. A default popup inside the XMLContainer
  4. A default message location and error panel inside the XMLContainer
  5. One editor or two editors inside the XMLContainer : XMLEditor
  6. One tree view always synchronized with the text changes
  7. One element view synchronized with the tree selection

All default elements of the XMLContainer are externalizable. So user can have for sample its own ToolBar (I.e) or StatusBar.

Several views are available for the MainEditor :

View1

This is the default editor view

View 2

This is a custom view available using SharedProperties.FULL_TEXT_VIEW = false

a. Fast starting

The main class XMLContainer has a set of facilities :
Sample :

XMLContainer container = new XMLContainer();
container.getAccessiblity().setText( "<test></test>" );
...
// After terminating to use the container we dispose it
// This operation is necessary for the garbage collector job
// Except if you initialize the container with the second constructor XMLContainer( true )
container.dispose();

b. Toolkit usage

The toolkit provides a real facility for editing single or multiple XML documents.

com.japisoft.xmlpad.toolkit.SingleDocumentEditor.showEditor( "c:/conf.xml", false );


This sample line will show and edit the 'c:/conf.xml' document file. Note that this function returns a SingleDocumentEditor which is a javax.swing.JFrame frame and you can access to the XML component by calling the getXMLContainer method.

For multiple document usage, you must use the MultipleDocumentEditor.

com.japisoft.xmlpad.toolkit.MultipleDocumentEditor.showEditor( new File( "c:/tmp/xmlpad/xmlpad/samples/xml-data" ), null );

This sample line will show and edit all the files under the xml-data directory. The null argument position is usely for a java.io.FileFilter rejecting file which name doesn't match a criteria.

c. Simple usage

A simple usage of JXMLPad is by including an XMLContainer inside your application as any swing component.

package demo;

import com.japisoft.xmlpad.XMLContainer;

import java.awt.*;
import javax.swing.*;

/** Simple component usage */
public class Demo extends JFrame {
    public Demo() {
        getContentPane().add( new XMLContainer().getView() );
        setSize( new Dimension( 550, 400 ) );
        setTitle( "XMLPad simple demo" );
        setVisible( true );
    }

    public static void main( String[] args ) {
        new Demo();
    }
}


This sample creates a swing Frame with a JXMLPad content.

d. Application usage

Another way to integrate JXMLPad is to see it as a whole application. Most of applications contains a menu.
JXMLPad provides an action model with all available actions. This actions are shown inside the main toolbar.

package demo;

import com.japisoft.xmlpad.*;
import com.japisoft.xmlpad.action.*;

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

/** Simple usage of the <code>XMLContainer</code> toolbar action model
    @version 1.1
*/
public class Demo extends JFrame {

    public Demo() {
        XMLContainer mContainer = new XMLContainer();
        getContentPane().add( mContainer.getView() );
        setSize( new Dimension( 550, 400 ) );
        setTitle( "JXMLPad application demo" );
        resetMenu( mContainer.getToolBarModel() );
        setVisible( true );
    }

    private void resetMenu( ToolBarModel model ) {
        JMenuBar menuBar = new JMenuBar();
        JMenu file = new JMenu( "File" );
        menuBar.add( file );
        setJMenuBar( menuBar );

        // I retreive all Action instances from the
        // current ActionModel

        Action[] fileAction = new Action[] {
            ActionModel.getActionByName( ActionModel.NEW_ACTION ),
            ActionModel.getActionByName( ActionModel.LOAD_ACTION ),
            ActionModel.getActionByName( ActionModel.SAVE_ACTION ),
            ActionModel.getActionByName( ActionModel.SAVEAS_ACTION )
        };
        buildMenu( fileAction, file );
    }

    private void buildMenu( Action[] actions, JMenu menu ) {
        for ( int i = 0; i < actions.length; i++ ) {
            JMenuItem item = ( JMenuItem )menu.add( actions[ i ] );
            item.setText( "" + actions[ i ].getValue( "ACTION.NAME" ) );
        }
    }
}


In this sample, we build a minimal application with a file menu. We retreive all action by the ActionModel class. This
class contains a model with a set of ActionGroup. Each ActionGroup contains a set of XMLAction. A group defines a
collection of action that are similar like cut, copy and paste actions. Visually, a group is seen by a separator inside the
main toolbar.
Each action from the ActionModel has a name, by default the ActionModel contains several actions with some static variables like the NEW_ACTION.

e. Applet usage

Another way to use JXMLPad is by using an applet. This applet usage needs a browser compatible with the java plug-in technology like internet explorer or netscape.

Here a sample of applet :

package demo;

import javax.swing.*;

import com.japisoft.xmlpad.XMLContainer;

/** Here an applet demonstration usage with JXMLPAD */
public class AppletDemo extends JApplet {

        public AppletDemo() {
                super();
                initUI();
        }
       
        private void initUI() {
                getContentPane().add( new XMLContainer().getView() );
        }

}


Here an HTML page using JXMLPad

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<!-- @(#)SwingApplet.html       1.1 97/07/02 -->
<html>
<body>

<!-- HTML CONVERTER -->
<OBJECT
    classid="clsid:CAFEEFAC-0014-0000-0001-ABCDEFFEDCBA"
    WIDTH = "600" HEIGHT = "400" 
    codebase="http://java.sun.com/products/plugin/autodl/jinstall-1_4_0_01-win.c
ab#Version=1,4,0,10">
    <PARAM NAME = "CODE" VALUE = "demo.AppletDemo" >
    <PARAM NAME = "ARCHIVE" VALUE = "../lib/xmlpad.
./lib/xmlpad.jar" >
    <PARAM NAME = "JAVA_CODEBASE" VALUE = "classes">
    <PARAM NAME="type" VALUE="application/x-java-applet;jpi-version=1.4.0_01">
    <PARAM NAME="scriptable" VALUE="false">

    <COMMENT>
        <EMBED
            type="application/x-java-applet;jpi-version=1.4.0_01"
            CODE = "demo.AppletDemo"
            ARCHIVE = "../lib/xmlpad.jar"
            JAVA_CODEBASE = "classes"
            WIDTH = "600"
            HEIGHT = "400" 
            scriptable="false"
            pluginspage="http://java.sun.com/products/plugin/index.html#download">
                <NOEMBED>
               
                </NOEMBED>
        </EMBED>
   </COMMENT>
</OBJECT>

<!--
<APPLET CODE = "demo.AppletDemo" CODEBASE="res" ARCHIVE = "
../lib/xmlpad../lib/xmlpad.jar" WIDTH = "600" HEIGHT = "400">
</APPLET>
-->

<!--"END_CONVERTED_APPLET"-->

</body>
</html>


Note that you may have changes on the ARCHIVE, JAVA_CODEBASE section for getting a valid path to the JXMLPad libraries and your applet ressources.

f. Multiple editor usage

In a multiple editors usage like inside a JTabbedPane, it is required to have only one shared toolBar.

Here a sample from the MultipleDocumentEditor:

public class MultipleDocumentEditor extends JFrame implements ChangeListener {
    private JToolBar toolBar;
    private JTabbedPane tabbedPane;

    private MultipleDocumentEditor() {
        super( "Multiple editor" );
        // Connection listeners for adapting the ActionModel to the current XMLContainer
        tabbedPane.addChangeListener( this );
    }

    public void stateChanged(ChangeEvent e) {
        XMLContainer container = ( XMLContainer )tabbedPane.getSelectedComponent();
        ActionModel.resetActionState( container.getEditor(), container );
    }

    public static MultipleDocumentEditor showEditor( String[] files ) throws FileNotFoundException, IOException {
         ...
        ActionModel.buildToolBar( frame.toolBar );
   
        // Build all containers
        for ( int i = 0; i < files.length; i++ ) {
            // Calling this constructor, the container will be automatically disposed if
            // we remove it from the tabbedpane, else we will have to call the dispose method
            XMLContainer container = new XMLContainer( true );
            container.setToolBarAvailable( false );
            ...
        }
...

}


The showEditor method will add several XMLContainer in a JTabbedPane. Here the useful steps :
  1. Add a listener for resetting the ActionModel to the current XMLContainer. It will notify all XMLAction that they must work on another XMLContainer.
  2. On each change event from a tabbedPane selection, reset the Action state calling resetActionState
  3. Build the common toolbar calling buildToolBar
  4. Disable the default XMLContainer toolbar calling setToolBarAvailable( false )

g. Internal frame usage

JXMLPad maintains several inner references that can't be garbaged. The developper will have to call dispose after each XMLContainer usage.

public class InternalFrameSample implements WindowListener {

   public void windowActivated(WindowEvent e) {}
public void windowClosed(WindowEvent e) {}

    public void windowClosing(WindowEvent e) {
// Freeing all inner resources
// We stop using editor1 and editor2
editor1.dispose();
editor2.dispose();
}

    public void windowDeactivated(WindowEvent e) {}
    public void windowDeiconified(WindowEvent e) {}
    public void windowIconified(WindowEvent e) {}
    public void windowOpened(WindowEvent e) {}

    public InternalFrameSample() {
init();
}

    private XMLContainer editor1;
    private XMLContainer editor2;

    public void init() {
        JFrame frame = new JFrame();
        frame.addWindowListener( this );
        JDesktopPane dp = new JDesktopPane();

        // Add two inner frame

        JInternalFrame editorFrameOne = new JInternalFrame();
        editorFrameOne.setTitle( "My editor 1" );
        // This constructor avoids XMLContainer for freeing automatically its inner reference
        editor1 = new XMLContainer();
        editor1.setText( "<?xml version='1.0'?>\n<test></test>" );
        editorFrameOne.getContentPane().add( editor1.getView() );
        editorFrameOne.setResizable( true );

        dp.add( editorFrameOne );
        editorFrameOne.setSize( 200, 200 );        
        editorFrameOne.setVisible( true );
        editorFrameTwo.setVisible( true );      

        frame.getContentPane().add( dp );
        frame.setSize( 400, 400 );
        frame.setVisible( true );
    }

h. Tag View

JXMLpad has several views for tags. Each view is available using the following properties :

SharedProperties.WRAPPED_LINE : Activate the wrapped view
SharedProperries.FULL_TEXT_VIEW : Activate the full text mode.

Note that for using a view, it must be set before creating an XMLContainer.

II. Actions

a. Definition

As we begin to see previously, a toolbar is composed of action inheriting mainly from the XMLAction class. An XMLAction is
stored inside an ActionGroup. An ActionGroup is stored itself inside an ActionModel. Each time an editor has focused from the
XMLContainer, all actions from the ActionModel are adapting for working on the good editor by calling setXMLEditor and setXMLContainer. You don't need to override this methods, but rather notifyXMLContainer and notifyXMLEditor. So if you wish to use actions with a tabbed pane system (I.e), you will have to reset action for each tabbed pane change. If you want to add action for the realtime tree you must inherit from AbstractTreeAction which inherits itself from the XMLAction.

Note that since the 2.4 the user can manage standard swing javax.swing.Action too.

From your the ActionModel you can :
Each action has a default name, tooltip, accelerator, mnemonic, icon and group. Each value can be overrided using a
Property file (look at b.6). A group is a way to create subMenu for popup. Assuming an action would like to be a part
of the Edit popup menu, it will just have to override getPopupGroup and return Edit.

Each container of JXMLPad has a set of model for actions. For sample, the main toolbar has an action model
This action model can be retreived calling getToolBarModel on the XMLContainer. If you remove or
add an XMLAction ( or a javax.swing.Action compatible ) , it will be handled in real time. By default the action
model contains the ActionModel content. This is the same case for the popup on the text and on the tree.
Here a sample changing it :

XMLContainer container = new XMLContainer();
// Remove the NEW_ACTION for the default toolbar only for THIS container
container.getToolBarModel().removeAction( ActionModel.getActionByName( Action.NEW_ACTION ) );
// Add a new one for THIS container
container.getPopupModel().addAction( myNewAction() );
// Add another one on the tree for THIS container
container.getTreePopupModel().addAction( myTreeAction() );

b. Usage

1. Replacing existing default action by your action
ActionModel.replaceActionByName(ActionModel.NEW_ACTION, new NewAction() );

// Here my New action that write a default JSP page
class NewAction extends XMLAction {
        public NewAction() {
            // Get the same icon than the default New action
            super( ActionModel.NEW_ACTION );
        }
       
        public void notifyXMLContainer() {
          // Initializing the action for this container ?
          // setEnabled( ... )
        }

        public void notifyAction() {
            container.setText( "<%@page language=\"java\"%>\n" +
                            "<html>\n" +
                            "<body>\n" +
                            "</body>\n" +
                            "</html>" );
        }

        public String getName() {
            return ActionModel.NEW_ACTION;
        }
}

2. Disabled/Enabled current action
// Disable the parse and format action
ActionModel.setEnabledAction( ActionModel.REFRESH_ACTION, false );
ActionModel.setEnabledAction( ActionModel.FORMAT_ACTION, false );

3. Invoke an action
// run the new action
ActionModel.activeActionByName(ActionModel.NEW_ACTION);

you can also active an action for another component context calling

// run the new action on another component
ActionModel.activeActionByName(ActionModel.NEW_ACTION, myXMLContainer, myXMLEditor );
4. Properties file definition
You can customize your toolbar with a xmlpad.properties file that must be inside your classpath.

factory=com.japisoft.xmlpad.ComponentFactory
look=com.japisoft.xmlpad.look.MozillaLook
tree=true
location=true
fontname=Dialog
fontsize=12
action.file.1=com.japisoft.xmlpad.action.NewAction
action.file.2=com.japisoft.xmlpad.action.LoadAction
action.file.3=com.japisoft.xmlpad.action.SaveAsAction
action.edit.1=com.japisoft.xmlpad.action.CutAction
action.edit.2=com.japisoft.xmlpad.action.CopyAction
action.edit.3=com.japisoft.xmlpad.action.PasteAction
groupOrder=file,edit

The syntax for an action definition is : action.GROUP.ORDER=YOUR_CLASS

Here We define three actions for two groups : file and edit. Each number is for the position, thus the NewAction will be at the first place followed by the LoadAction.  The groupOrder defines an order for each group, in this sample the file group will be added before the edit group.
5. New actions
Rather than using the xmlpad.properties file for adding new actions, you can add it from the ActionModel.

Here a sample :  We add a new action MyNewAction that inherits from XMLAction.

ActionModel.addActionForGroup( ActionModel.EDIT_GROUP, new MyNewAction() );

It will add in the edit group the MyNewAction. Such action will be now visible in the main toolbar automatically but
you can also the toolbar model for changing it. You can define your own group just by choosing any group name.

Notes :

- The icon for your action is always found from the classpath in the same package of your action using your class name adding '16.gif' or '24.gif'. From sample, the com.japisoft.xmlpad.action.file.NewAction has its default images here : com/japisoft/xmlpad/action/file/NewAction16.gif and com/japisoft/xmlpad/action/file/NewAction24.gif.

- You can override the default Icon location with the getDefaultIcon method.

- Calling setActionReferenceIcon you can have the same icon than another known action.
6. Internationalization
Each XMLAction have a property file containing :
You can override any value by writing a property file matching an Action class name and with a precedence inside the
classpath.

Here a sample overriding the default Copy action to a french version :

We create a com/japisoft/xmlpad/action/edit/CopyAction.properties or we update the default one :

LABEL=Copie
TOOLTIP=Copier la sélection courante
ACCELERATOR=ctrl C

Note : The dialog box have also property file for title and help

Here a sample for the comment dialog located at : com/japisoft/xmlpad/action/xml/CommentAction$CommentDialog.properties

 # Comment dialog, you can override these values

TITLE=Comment
COMMENT=Insert an XML comment. This comment can contain several lines
OK=Comment it
7. UIManager for changing the look
Please look at the IV. b section for available properties.

c. Default actions

Here available actions :

Name
ActionModel Default
Role
New
Yes
Build a new XML document. This action uses the XMLTemplate class for building a new page.
Load
Yes Load an XML document
SaveAs
Yes Save an XML document
Copy
Yes Copy a text
Cut
Yes Cut a text
Paste
Yes Paste a text
Undo
Yes Undo the last action but this is a new document action
Redo
Yes Redo the last action but to undo action has been called
Parse Yes Parse the current document and show any error in red
Search
Yes Parse the current document and show a tree for easily navigating
Split
Yes Split the current editor in two ones
Format
Yes Ident the current XML text.
Tree visibility
False
Show/Hide the realtime tree
Copy node
Yes
Copy the current tree node
Cut node
Yes
Cut the current tree node
Comment node
Yes
Comment the current tree node
Select node
Yes
Select the current tree node in text
Edit node
Yes
Use the editor model : VIII

III. Helper

The content assistant are available for three parts :
The Helper API is built around the SyntaxHelper class. The current instance is available by calling getSyntaxHelper on the XMLContainer.

This syntaxHelper works in several ways :
  1. By reading a DTD (relative to the current document or not). This DTD can be provided calling the setDefaultDTD or setDTD of the XMLContainer or directly inside the SyntaxHelper. This DTD will be automatically read in your current XMLDocument each time it is saved, loaded or parsed. The DefaultDTD is used if no DTD has been found into the current document.
  2. By reading a Schema (relative to the current document or not). This Schema can be provided calling the setDefaultSchema or setSchema from the XMLContainer. This schema will be automatically read in your current XMLDocument each time it is saved, loaded or parsed.
  3. By reading a RelaxNG grammar. This grammar is set by calling setRelaxNGValidationLocation on the XMLContainer. Once the grammar (XML form) provided, completion and parsing will use relaxNG but there's a current schema or DTD.
  4. By using a static document descriptor. This descriptor is not available directly. The user has to call the addTagDescriptor method on the SyntaxHelper.
Usage sample for DTD :

We have declared this DTD by building a jsp.dtd file  :

<!-- Here a minimal DTD for JSP page -->

<!ELEMENT html (head,body)>
<!ELEMENT head (title)>
<!ELEMENT title #PCDATA>
<!ELEMENT body (jsp:include|jsp:getProperty|jsp:setProperty|jsp:useBean)>

<!ELEMENT jsp:include EMPTY>
<!ATTLIST jsp:include
  page CDATA #REQUIRED>

<!ELEMENT jsp:getProperty EMPTY>
<!ATTLIST jsp:getProperty
 name CDATA #REQUIRED
 property CDATA #REQUIRED>

<!ELEMENT jsp:setProperty EMPTY>
<!ATTLIST jsp:setProperty
  property CDATA #REQUIRED
  value CDATA #REQUIRED>

<!ELEMENT jsp:useBean EMPTY>
<!ATTLIST jsp:useBean
  id CDATA #REQUIRED
  scope CDATA #REQUIRED>


Here a way to force usage because my document has no DTD header inside our XML Document :

container.setDefaultDTD( "html", "jsp.dtd" );

If your document contains a DOCTYPE part, there's no reason to force a default usage. Morever, the
DTD will be found relativly to the current document location (similar handling for schemas). That's if you
use setText with a relative schema declaration for setting a new document you may have to set the
default document location using the XMLDocumentInfo from the current container.

For remarks :

- A Default DTD has no usage when you declare a valid XML header like :
<!DOCTYPE Module SYSTEM "file:///home/japisoft/test/project/myDTD.dtd">

- The DTD header handling is made while saving, loading or parsing. However you can force it by calling the searchAndParseDTD on the XMLContainer. This is similar for schema by calling searchAndParseSchema on the XMLContainer.

- RelaxNG will require a library isorelax.jar (located in the samples/relaxng directory) To remove RelaxNG grammar jut call setRelaxNGValidation( null ).

Look at the part XIII for using a DTD or a RelaxNG document from the classpath.

IV. Status bar

JXMLPad has its own status bar inside the XMLContainer component. However, user may want an external toolbar showing
the current XPath location.

Here a sample that uses a minimal external status bar :

public class Demo extends JFrame implements LocationListener {
    private JLabel sb;

    public Demo() {
    XMLContainer container;
    getContentPane().add( ( container = new XMLContainer() ).getView() );
    getContentPane().add( sb = new JLabel( "..." ), BorderLayout.SOUTH );

    setSize( new Dimension( 550, 400 ) );
    setTitle( "XMLPad statusbar demo" );

    // Disable the inner statusbar
    container.setStatusBarAvailable( false );
    // Connect the listener for location
    container.addLocationListener( this );
    setVisible( true );
    }

    public void locationChanged( LocationEvent e ) {
        sb.setText( e.getXPathLocation() );
    }
}


With the LocationListener event, user receives each time the caret is changed the XPath location. Note it is necessary to
remove the default status bar by calling setStatusBarAvailable( false ) else two status bars would be visible.

V. LookAndFeel

a. The Look interface

JXMLPad has a lookAndFeel plug-in system for :

- The syntax coloration,
- The default XML content,
- The real tree renderer.

A set of themes are also availabe from the com.japisoft.xmlpad.look.themes package. Here a sample for using it :


GreenTheme.install();
JFrame fr = new JFrame();
XMLContainer container = new XMLContainer();
//We don't want the element view
//container.setElementView( null );
fr.getContentPane().add( container.getView() );
fr.setSize( 300, 300 );
fr.setVisible( true );

For defining or updating a theme, look at the UIManager section for a complete properties listing. Note that the following Look and Feel system will be deprecated. We advise you to use the UIManager for commodity.

Here a sample from the default lookAndFeel :

package com.japisoft.xmleditor.look;

import java.awt.*;
import com.japisoft.xmleditor.bean.XMLEditor;
import com.japisoft.xmleditor.bean.XMLTemplate;

/**
 * Default look for the <code>XMLEditor</code>
 *
 * @author (c) 2002 JAPISoft
 * @version 1.0
 * @see Look
 * @see LookManager */
public class DefaultLook implements Look {

    public DefaultLook() {
        super();
    }

    public void install( XMLContainer container,XMLEditor editor ) {

        editor.setErrorLineColor( new Color( 200, 0, 0 ) );
        // Syntax Colorization
        editor.setCommentColor( new Color( 255, 0, 0 ) );
        // <? and <!
        editor.setDeclarationColor( Color.gray.darker() );
        editor.setDocTypeColor( Color.gray.darker() );
        editor.setTagColor( Color.green.darker().darker() );
        editor.setLiteralColor( Color.blue );

        // Tag delimiter
        editor.setTagDelimiterHighlight( true );
        editor.setTagDelimiterHighlightColor( editor.getTagColor().brighter() );

        // Att delimiter
        editor.setAttDelimiterHighlight( true );
        editor.setAttDelimiterHighlightColor( new Color( 100, 100, 150 ) );

        editor.setCaretColor( Color.black );
         editor.setEntityColor( Color.blue );

        editor.setSelectionLineColor( new Color( 150, 150, 230 ) );

        editor.setBackground( Color.lightGray );
        editor.setForeground( Color.black );

        editor.setDeclarationFont( new Font( null, Font.BOLD, 10 ) );
        editor.setDocTypeFont( new Font( null, Font.BOLD, 10 ) );

        editor.getCaret().setBlinkRate( 500 );

        // Template

        XMLTemplate template = new XMLTemplate();
        template.setComment( " Version : 1.0, Date : " + new java.util.Date() );
        editor.setTemplate( template );
    }

    public void uninstall( XMLEditor editor ) {

    }
}

This default look is installed once by calling (before any "new XMLContainer" !):

LookManager.setCurrentLook( new com.japisoft.xmleditor.look.XMLPadLook() );

This must be done before the new XMLContainer usage, otherwise you can dynamically
change the look by calling the LookManager.install( Editor ).

Note : One more way to create your own LookAndFeel is by overriding an existing one (like the MozillaLook) and updating only what you prefer.

Known looks :

com.japisoft.xmlpad.look.XMLPadLook
The default one
com.japisoft.xmlpad.look.MozillaLook
A very similar look than the default one except on the tree part
com.japisoft.xmlpad.look.IELook
An "Internet explorer" look

b. UIManager

User can control all the JXMLPad appearance using the javax.swing.UIManager with the following properties. Note that
the UIManager must be used before the building of the XMLContainer.

For the editor :

xmlpad.editor.font
xmlpad.editor.dtdElementColor
xmlpad.editor.dtdAttributeColor
xmlpad.editor.dtdEntityColor
xmlpad.editor.dtdNotationColor
xmlpad.editor.tagBorderLineColor
xmlpad.editor.cdataColor
xmlpad.editor.entityColor
xmlpad.editor.commentColor
xmlpad.editor.declarationColor
xmlpad.editor.docTypeColor
xmlpad.editor.literalColor
xmlpad.editor.tagColor
xmlpad.editor.invalidColor
xmlpad.editor.textColor
xmlpad.editor.attributeColor
xmlpad.editor.attributeSeparatorColor
xmlpad.editor.selectionHighlightColor
xmlpad.editor.backgroundColor
xmlpad.editor.tagBackground
xmlpad.editor.declarationBackground
xmlpad.editor.entityBackground
xmlpad.editor.commentBackground
xmlpad.editor.docTypeBackground
xmlpad.editor.cdataBackground

ex : UIManager.put( "xmlpad.editor.tagColor", Color.red );

For the tree :

xmlpad.tree.font
xmlpad.tree.elementIcon
xmlpad.tree.textIcon
xmlpad.tree.errorIcon
xmlpad.tree.textColor
xmlpad.tree.selectionColor

ex : UIManager.put( "xmlpad.tree.font", new Font( null, 0, 10 ) );

For the element view :

xmlpad.tableElementView.font
xmlpad.tableElementView.prefixNameColor
xmlpad.tableElementView.highlightColor
xmlpad.tableElementView.highlightColor
xmlpad.tableElementView.lowlightColor

For the action :

xmlpad.action.[FULL ACTION CLASS NAME].mnemonic (a string)
xmlpad.action.[FULL ACTION CLASS NAME].accelerator(a string)
xmlpad.action.[FULL ACTION CLASS NAME].label
xmlpad.action.[FULL ACTION CLASS NAME].tooltip
xmlpad.action.[FULL ACTION CLASS NAME].icon

ex : UIManager.put( "xmlpad.action.com.japisoft.xmlpad.action.file.LoadAction.icon", new ImageIcon( "/tmp/icon.gif" ) );

For the popup helper:

xmlpad.helper.backgroundColor
xmlpad.helper.foregroundColor
xmlpad.helper.selectionBackgroundColor
xmlpad.helper.selectionForegroundColor
xmlpad.helper.icon

ex : UIManager.put( "xmlpad.helper.backgroundColor", new Color( 240, 250, 230 ) );

For remarks : you must use UIManager.setString for the mnemonic and the accelerator of your action. JXMLPad has a set of color theme available from the com.japisoft.xmlpad.look.themes.

VI. Component properties

The XMContainer is highly customizable. One easy way to change a part if the 'xmlpad.properties' file localized by default
in the '/lib' directory.

Here the default content :

factory=com.japisoft.xmlpad.ComponentFactory
look=com.japisoft.xmlpad.look.MozillaLook
tree=true
location=true
fontname=Dialog
fontsize=12

Property name
Property role
Default value
factory
Components for the XMLContainer like the toolbar, the editor...
com.japisoft.xmlpad.ComponentFactory
look
The Default lookAndFeel
com.japisoft.xmlpad.look.XMLPadLook
tree
Show the real time tree
true
location
Localize the current cursor in the tree
true
fontname
Font name
Dialog
fontsize
Font size
12

Note : Calling setTreeAvailable( false ) you needn't to use this property file for removing the tree. For using an external tree, call  setTreeDelegate.

VII.  XML integrity

Integrity API part a way to maintain a valid XML document.

The integrity occurs for :
  1. Avoiding to corrupt XML tag. So in such mode, user cannot modify current tags but it's always possible to modify other XML parts
  2. Checking if a saved document is valid by parsing it. If the document is not valid, the document cannot be saved. 
You can force control of the XML integrity by using the XMLIntegrity class. This class is available by the XMLContainer/getDocumentIntegrity method.

VIII. Custom Node Editor

In some context, you don't wish for confort reason to edit a part of the document in the text part and you prefer an external editor showing your XML data in a better view. This is possible with the JXMLPad API inside the com.japisoft.xmlpad.nodeeditor package.

This particular editing view is available inside the left tree using the Popup and the "Edit" action.

This package is built with these elements :

- The Editor interface
- The EditorContext
- The EditorModel
- The DefaultEditor

The first one is an interface that your custom node editor must implement. This interface has two main methods : An accept method where the editor decides if it accepts to edit such node or not and an edit method where the real editing action is done when the user clicks the "edit" action. Inside the edit method, an EditorContext is provided with an access to the current node and text. The role of the edit method is to write the new text result inside the EditorContext calling setResult.

When you build a new custom node editor, you must declare it inside the EditorModel by calling addEditor. This model will add your custom editor with a highter priority than the last added.

Here a sample of the default test editor :

public class DefaultEditor implements Editor {

    /** Accept all text node : by checking <code>isText</code> */
    public boolean accept(SimpleNode node) {
        return ( node.isText() );
    }

    /*
     * Edit a text node by showing a Text dialog
     */
    public void edit(EditorContext context) {
        XMLContainer container = context.getXMLContainer();
        ...
        context.setResult( dialog.getText() );
    }
}

For note : It is also possible since the 2.0RC5 to call an editor outside the Edit menu popup by calling editCurrentNode from the XMLContainer class.

IX. Customize the parser

JXMLPad uses JAXP (Java API for Xml Processing) for parsing or formatting a document. Thus, any parser compliant with JAXP can be used.

"Different vendors provide different implementations of this abstract class. Java chooses the one to use based on the following conditions in order of preference:

  1. The value of the javax.xml.parsers.DocumentBuilderFactory Java system property

  2. The value of the javax.xml.parsers.DocumentBuilderFactory property specified in the lib/jaxp.properties properties file in the JRE directory

  3. The first value found in a META-INF/services/javax.xml.parsers.DocumentBuilderFactory file in the JAR files available to the runtime

  4. The platform default (org.apache.crimson.jaxp.DocumentBuilderFactoryImpl in Sun’s JDK 1.4)."

Here a sample for using xerces

System.setProperty( "javax.xml.parsers.SAXParserFactory",
        "org.apache.xerces.jaxp.SAXParserFactoryImpl" );

X. Localized the editor messages

JXMLPad uses the java resource bundle mechanism for translating actions and dialog box messages. All messages and icons
are stored in the xmlpad-res.jar. To update it, you will have to "unjar" it using the jar command from the JDK distribution : "jar xvf xmlpad-res.jar". All messages are stored in files terminating by .properties. The opposite operation rebuilding the jar is "jar cvf xmlpad-res.jar com".

Sample for the copy action :

com/japisoft/xmlpad/action/edit/CopyAction.properties : This file contains the default values

 # Copy action, you can override these values

LABEL=Copy
TOOLTIP=Copy the current selection
#MNEMONIC=C
ACCELERATOR=ctrl C
#GROUP=Edit
#ICON=com/japisoft/xmlpad/ac


CopyAction_fr.properties : This file contains the french values (due to the _fr in the file name)

 # Copy action, you can override these values

LABEL=Copier
TOOLTIP=Copier la sélection
#MNEMONIC=C
ACCELERATOR=ctrl C
#GROUP=Edit
#ICON=com/japisoft/xmlpad/action/edit/CopyAction16.gif

Note about the ACCELERATOR property : you can specify a cross-platform menu activator using '*'. For instance '* C' means
ctrl C for XP by command C for Mac OS X.


XI. Shared a tree

In some cases, the user may prefer having one shared tree between several XMLContainer. This is possible calling
setTreeDelegate from the UIAccessibility of the XMLContainer. But each time an XMLContainer is activated, it is
required to update the unique tree with the good model and state, this is done by calling "focus" inside the XMLContainer.
As in a similar case, each time an XMLContainer is desactivated it is requires to store it current tree state by calling "unfocus". We provide below a complete sample with internal frame usage :

// Here a sub class for the internal frame that contains an XMLContainer
public class DemoInternalFrame2 extends JInternalFrame {

        private XMLContainer container;

// This is the common tree and we specify the first one having the focus
        public DemoInternalFrame2( JTree sharedTree, boolean focus ) {
                super();
                container = new XMLContainer();

                // This is the initial focus
                if ( focus )                       
container.focus();
                else
container.unfocus();
                // We shared this tree
                container.setTreeDelegate( sharedTree );
                getContentPane().add( container );
                // Here a listener for detecting that our container looses the focus
                addInternalFrameListener( new CustomInternalFrameAdapter() );
        }
        // It is important to have only one container having the focus at a time
        class CustomInternalFrameAdapter extends InternalFrameAdapter {

                public void internalFrameClosed( InternalFrameEvent e ) {
                        // We dispose the container
                        container.dispose();
                }

                public void internalFrameActivated( InternalFrameEvent e) {
                        // We unfocus the previous focus container
                        JDesktopPane dp = getDesktopPane();
                        JInternalFrame[] frame = dp.getAllFrames();
                        for ( int i = 0; i < frame.length; i++ ) {
                                if ( frame[ i ] instanceof DemoInternalFrame2 ) {
                                        if ( ( ( DemoInternalFrame2 )frame[ i ] ).container.hasFocus() ) {
                                                ( ( DemoInternalFrame2 )frame[ i ] ).container.unfocus();
                                                break;
                                        }
                                }
                        }
                        // Notify the container that he has the focus
                        container.focus();
                }
        }        

public static void main( String[] args ) {
                JFrame fr = new JFrame();
                final JDesktopPane dp = new JDesktopPane();
                fr.getContentPane().add( dp );
                JInternalFrame treeFrame = new JInternalFrame( "Tree" );
                // We build here the tree shared by several XMLContainer
                final JTree sharedTree = new JTree();
                // We update the "look-and-feel" of the tree
LookManager.install( sharedTree );
                treeFrame.getContentPane().add( sharedTree );
                dp.add( treeFrame );
                treeFrame.setBounds( 10, 10, 100, 300 );
                treeFrame.setResizable( true );
                treeFrame.setVisible( true );

                // Editor 1

                DemoInternalFrame2 innerFrame1 = new DemoInternalFrame2( sharedTree, true );
                dp.add( innerFrame1 );
                innerFrame1.setBounds( 150, 10, 200, 300 );
                innerFrame1.setResizable( true );
                innerFrame1.setClosable( true );
                innerFrame1.setVisible( true );

                // Editor 2
		...

    }

In this sample we have one shared tree between two internal frames. Each time a frame is activated with call the
focus method from the current XMLContainer and the unfocus method for the previous activated XML container.

XII. Bookmark API

The bookmark API is a way to include in the left bar a set of marks. This mark has two views :
When a mark is set, it follows each line change thus it stays bound to the right element. For managing a set of
bookmarks, it is required to specify a bookmark context to the XMLContainer. This context has three parts :

A bookmark position is an offset inside the text. This offset can be changed if the user moves the line where the
bookmark has been put.

The API includes facilities for creating a context like a DefaultBookmarkContext taking two parameters : One icon and
a color for the text line.

Here we provide a sample of usage and an action for listing each line that contains a bookmark :
public class DemoBookmark extends JFrame {

        private BookmarkContext bookmarks;
        private XMLContainer container;       
        public DemoBookmark() {
                container = new XMLContainer();
                container.setBookmarkContext(
                                        bookmarks = new DefaultBookmarkContext(
                                        new ImageIcon( getClass().getResource( "bookmark.gif" ) ),
                                        new Color( 200, 200, 250 ) ) );

               // We add an action inside the container toolbar for listing the bookmarked lines
                container.getToolBarModel().addAction(
                                new ListBookmarks() );              
                getContentPane().add( container.getView() );               
        }
        // Action that lists the "bookmarked" lines
        class ListBookmarks extends AbstractAction {

                public ListBookmarks() {
                        putValue(
Action.SMALL_ICON,
new ImageIcon( getClass().getResource( "bookmark.gif" ) ) );
                }

                public void actionPerformed( ActionEvent e ) {                      
                        XMLDocument doc = container.getDocument(); 
// We get all the current marks                     
                        BookmarkModel model = bookmarks.getModel();
                        for ( int i = 0; i < model.getBookmarkCount(); i++ ) {
                                BookmarkPosition pos = model.getBookmarkPositionAt( i );
                                int offset = pos.getOffset();
// We convert the offset to a line number
                                int line = doc.getDefaultRootElement().getElementIndex( offset );
                                System.out.println( "Bookmark at " + line );
                        }
                }
        }

        public static void main(String[] args) {             
                DemoBookmark frame = new DemoBookmark();
                frame.setBounds( 10, 10, 600, 400 );
                frame.setVisible( true );               
        }
}



XIII. Accessing a schema/dtd by a classpath...

There are various cases where you don't want to use a relative or an absolute schema for validating your XML document. For instance, you would like to load a DTD from your classpath thus the user will ignore it needs this DTD. JXMLPad has multiple systems for this purpose.

a. DTD

We suppose we have in our classpath a "foo.dtd" DTD that must be bound to this URL : http://www.foo.com/foo.dtd. Thus each time the user manages an XML document with a DOCTYPE reference pointing to this URL, our local DTD will be used. We need to create a DTDMapper which maps our DTD location to another one.

package test;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;

import com.japisoft.dtdparser.DTDMapper;
import com.japisoft.dtdparser.node.RootDTDNode;

/**
 * Here a sample for loading and managing a DTD from the
 * classpath
 * @author (c) 2004 JAPISoft / http://www.japisoft.com
 * @version 1.0
 * */
public class ClasspathDTDMapper implements DTDMapper {

        // We override the DTD url for using this local foo.dtd available
        // From the classpath
        public InputStream getStream( String url ) throws IOException {
               
                if ( "http://www.foo.com/foo.dtd".equals( url ) )
                        return getClass().getResourceAsStream( "foo.dtd" );
               
                return null;
        }

        // We have no cache
        public boolean isCachedEnabled() {
                return false;
        }

        // We use this feature available only if the cache is enabled
        public File updateCache(RootDTDNode root, String url) {
                return null;
        }

}


And we reset this DTDMapper as the default one using :

DTDMapperFactory.setDTDMapper( new ClasspathDTDMapper() );

b. RelaxNG

For loading a RelaxNG schema from the classpath, you have to use the SchemaLocator class. This locator is used inside the setRelaxNGValidationLocation method from the SchemaAccessibility class (available from the XMLContainer). Inside this locator, an inputstream or a reader is available.

XMLContainer container = new XMLContainer();
container.getSchemaAccessibility().setRelaxNG( new SchemaLocator( getClass().getResourceAsStream( "foo.rng" ) ) );


Here we load and use our schema "foo.rng" from the classpath.

(c) 2002 - 2005 JAPISOFT