package textbender.a.b.rhinohideDemo._; // Copyright 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. import java.awt.BorderLayout; import java.awt.EventQueue; import java.util.concurrent.atomic.AtomicBoolean; import java.util.regex.*; import javax.swing.*; import javax.swing.border.EmptyBorder; import org.w3c.dom.*; import org.w3c.dom.traversal.*; import textbender.g.lang.*; import textbender.g.util.logging.LoggerX; import textbender.o.Browser; import textbender.o.rhinohide.*; import textbender.o.rhinohide._.*; /** Demo of Rhinohide, Traversal Level 2. * * @see http://reluk.ca/var/cache/rhinohide-demo/textbender/a/b/rhinohideDemo/Traversal_2_Demo.xht */ public @ThreadRestricted("AWT event dispatch") final class Traversal_2_Demo extends JApplet implements Runnable { // - A p p l e t ---------------------------------------------------------------------- private final AtomicBoolean isStartedA = new AtomicBoolean(); public @ThreadSafe void start() { if( isStartedA.getAndSet( true )) return; // start once only LoggerX.i(getClass()).info( "starting" ); EventQueue.invokeLater( Traversal_2_Demo.this ); // in AWT event dispatch thread } private final AtomicBoolean isDestroyedA = new AtomicBoolean(); public @ThreadSafe void destroy() { if( isDestroyedA.getAndSet( true )) { assert false; return; } LoggerX.i(getClass()).info( "destroying" ); if( window != null ) window.release(); } // - R u n n a b l e ------------------------------------------------------------------ /** Runs this test applet. */ public void run() { reportList.addElement( "starting..." ); ThreadSafe.U.disableChecking(); // I lack permission, I'm an unsigned applet { // Construct GUI. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - JList reportListV = new JList( reportList ); reportListV.setBorder( new EmptyBorder( /*top*/2, /*left*/2, /*bottom*/2, /*right*/0 )); JScrollPane scrollPane = new JScrollPane( reportListV ); scrollPane.setBorder( null ); getContentPane().add( scrollPane, BorderLayout.CENTER ); } try { window = RhiWindow.createWindow( Traversal_2_Demo.this ); // Query DOM implementation. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - final Document document = window.getDocument(); reportList.addElement ( "browser supports Traversal Level 2... " + // document.isSupported( "Traversal", "2.0" ) //////// hmmm, false in Firefox 1.5, no matter how you spell it: // document.isSupported( "Traversal", ""/*any version*/ ) //////// these are better, because isSupported() assumes level 2 document.getImplementation().hasFeature( "Traversal", "2.0" ) // document.getImplementation().hasFeature( "Traversal", ""/*any version*/ ) ); // Start a DOM traverser. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - final Thread thread = new Thread( new Traverser(), "traverser" ); thread.setDaemon( true ); thread.setPriority( Thread.NORM_PRIORITY - 1 ); thread.start(); // - - - reportList.addElement( "successfully started" ); } catch( StunnedRhinoException xSR ) { LoggerX.i(getClass()).info( Browser.pageExitSupressionMessage( xSR )); } // in case the page exits, from beneath us catch( Exception x ) // all, checked or not { LoggerX.i(getClass()).log( LoggerX.WARNING, /*message*/"", x ); } } //// P r i v a t e /////////////////////////////////////////////////////////////////////// @ThreadRestricted( "AWT event dispatch" ) private final DefaultListModel reportList = new DefaultListModel(); private volatile RhiWindow window; // final after init // ==================================================================================== private final class Traverser implements Runnable { Traverser() { document = window.getDocument(); // Initialize early, where possible (let caller handle exceptions). // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Element targetIndicationElement = document.getElementById ( "target-indication-element" ); targetIndicationText = (Text)targetIndicationElement.getFirstChild(); walker = ((DocumentTraversal)document).createTreeWalker ( document, NodeFilter.SHOW_ALL, /*filter*/null, /*expand entities*/false ); } private final Document document; private Element highlightedElement; private final Text targetIndicationText; private final TreeWalker walker; public void run() { endless: for( ;; ) { traverse: for( int n = 0;; ++n ) { final int delayMS; if( n < 16 ) delayMS = 250; // speed over the boring parts else delayMS = 1000; ThreadX.trySleep( delayMS ); if( isDestroyedA.get() ) break endless; // if we get this far, probably no page exit is impending, and the following is unlikely to throw a StunnedRhinoException (though it still may on occaision, no problem) Node target = walker.nextNode(); if( target == null ) break traverse; // end of doc targetIndicationText.setData( Integer.toString(n) + ": " + target.toString() ); if( !( target instanceof Element )) continue traverse; Element targetE = (Element)target; synchronized( this ) // sync for atomic act/record { if( highlightedElement != null ) { Events_2_Demo.removeHighlight( highlightedElement ); // highlightedElement = null; } Events_2_Demo.addHighlight( targetE ); highlightedElement = targetE; } } walker.setCurrentNode( document ); // back to the beginning... } } } }