circular detail (eye, cheek and shoulder) of rhinoceros sculpture in metal

Rhinohide

Java implementation of the W3C DOM, layered over a Web browser's native JavaScript.

Set-Up  Use  Limitations  Source Code  See Also

Rhinohide provides applets with an interface to the Web document, through a familiar Java binding of the DOM. The code is still in beta and coverage is incomplete, but full support is provided for Core Level 1 and partial support for Core Level 2 and 3, XML, Events, Traversal and Range.

If you have Java 1.6, see the on-line demo/test pages.

Set-Up

Requires Java 1.6.

Requires rhinohide.jar. Download the latest version.

Requires jre/lib/plugin.jar, or equivalent, from your JDK. It contains the JSObject, Java-to-JavaScript bridge. Include it in your compile-time classpath (the Plug-In includes it automatically at runtime).

Use

For complete and detailed usage, see the API docs, the demo pages and demo code. What follows is only a summary:

In your Web page, there are two encoding requirements. One is to disable the browser's in-memory caching; the second is to add a MAYSCRIPT attribute on the applet tag. The necessary JavaScript and HTML will look something like this, in the Web page:

    <script type='text/javascript'>
        if( window.addEventListener )
        {
            window.addEventListener ( 'unload',  // to disable in-memory caching
                function( e ) {}, // do nothing
                /*capture phase*/false );
        }
        </script>

    <applet alt='[applet: unsupported by this browser]'
     archive='rhinohideDemo.jar'
     code='textbender/a/b/rhinohideDemo/_/Core_2_Demo.class'
     MAYSCRIPT='true' width='550' height='90'>
        [applet: unsupported by this browser]
        </applet>
    

In your Java applet, the code is mostly ordinary DOM:

    private volatile RhiWindow window; // final after init

    private final AtomicBoolean isStartedA = new AtomicBoolean();


public void start()
{
    if( isStartedA.getAndSet( true )) return; // start once only

    try
    {
        window = RhiWindow.createWindow( /*applet*/this );
        final Document document = window.getDocument();

      // Core level 2.                                           see Core_2_Demo.java
      // ----------------------------------------------------------------------------
        assert document.getImplementation().hasFeature( "Core", "2.0" );

        Element element = getElementById( document, "parent-of-created" );
        Text text = document.createTextNode( "Hello world, from Java" );
        element.appendChild( text );

      // Events.                                               see Events_2_Demo.java
      // ----------------------------------------------------------------------------
        assert document.isSupported( "Events", "2.0" );

        ((EventTarget)document).addEventListener( "mouseover", new RelaySIP()
        {
            public void handleEvent( Event e )
            {
                // do something
            }
        }, /*use capture*/false );

      // Traversal.                                         see Traversal_2_Demo.java
      // ----------------------------------------------------------------------------
        assert document.isSupported( "Traversal", "2.0" );

        TreeWalker walker = ((DocumentTraversal)document).createTreeWalker
        (
            /*root*/document, NodeFilter.SHOW_ALL,
            /*filter*/null, /*expand entities*/false
        );

      // Range.                                                 see Range_2_Demo.java
      // ----------------------------------------------------------------------------
        assert document.isSupported( "Range", "2.0" );

        Range myRange = ((DocumentRange)document).createRange();
        try // or this non-standard, Mozilla:
        {
            Range userSelectedRange = window.getSelection().getRangeAt( 0 );
        }
        catch( RuntimeException x ) { assert false; }

    }
    catch( StunnedRhinoException x ) // if the page exits in mid-initialization
    {
        Logger.getLogger( getClass().getPackage().getName() )
            .info( "page exit in progress? " + x );
    }
}


public void destroy()
{
    if( window != null ) window.release();
}
    

Limitations

Support at Level: 1 2 3
Core full part part unextended, non-XML
Events part
HTML slim none
MouseEvents part
Range part
Traversal part
UIEvents slim
Views slim
XML part part part

Where the implementation is incomplete (see the table above) the missing parts will throw UnsupportedOperationException at run time. They are marked ‘Not yet coded’ in the API. (But often, they can be coded in-line, as described further below.)

Rhinohide is a DOM interface, as opposed to a full DOM implementation, and therefore inherits the limitations of the browser. For instance, running under Internet Explorer (IE), XML and the higher levels of Core will be unavailable, simply because IE 7 does not implement them.

Test coverage is still limited. The best tested platform is Mozilla (Firefox and such) on Linux. Also tested are IE and Mozilla on Windows. To test a particular platform of your own, please use the on-line demo/test pages. Here is a summary of current, outstanding bugs:

Platform Bug Scope of Affect
IE hierarchy-mutation Insert/append/replace of nodes within a parent
IE flaky-insert Particular orderings of node insert/append
IE null-item NamedNodeMap item removal

Source Code

Source code is included in the download. Or you can browse it on line. Or you can obtain a working copy from the repository. The licence is MIT.

Build instructions have yet to be drafted. (If you need these, please ask.)

As mentioned above, many of the DOM methods are still uncoded. But usually they are trivial to code. Often, too, they can be coded in-line. For example, if this method is needed:

    Node nB = nA.desiredMethod( p1, p2, p3 );    // throws UnsupportedOperationException
    

To code this in-line, try a direct Java-to-JavaScript call:

    Node nB = RhiNode.wrapNode
    (
        ((Rhinohide)nA).window(),
        (JSObject) ((Rhinohide)nA).call( "desiredMethod", p1, p2, p3 )
    );
    

Sometimes that ought to read ‘getMember’ instead of ‘call’. See the source for working examples. (If your in-line solution works, please mail it in.)

See Also

For information on the DOM standard, see the W3C DOM specifications. See also the Wikipedia article on the DOM.

As a Java-to-JavaScript DOM interface, Rhinohide is comparable to the Common DOM API of the Java Plug-In. The Common DOM API has a fuller implementation of HTML Level 1; but Rhinohide extends to higher Core levels, and covers additional features, like XML.

Another comparable implementation is the Java DOM API of the Blackwood Project. But it appears to be inactive.

For details on the communication bridge underlying Rhinohide, see the links to the LiveConnect and JSObject specs, here.

Images (index page and top of this page) of the sculpture Rinoceronte vestido con puntillas by Salvador Dalí, detailed from an original photograph, © Manuel González Olaechea y Franco, with permission as described in § _/.

Copyright 2006-2007, Michael Allan. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Textbender Software"), to deal in the Textbender Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicence, and/or sell copies of the Textbender Software, and to permit persons to whom the Textbender Software is furnished to do so, subject to the following conditions: The preceding copyright notice and this permission notice shall be included in all copies or substantial portions of the Textbender Software. THE TEXTBENDER SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE TEXTBENDER SOFTWARE OR THE USE OR OTHER DEALINGS IN THE TEXTBENDER SOFTWARE.

SourceForge.net textbender