package votorola.g.web.gwt.event; // Copyright 2011-2012, Michael Allan. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Votorola Software"), to deal in the Votorola Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicence, and/or sell copies of the Votorola Software, and to permit persons to whom the Votorola 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 Votorola Software. THE VOTOROLA 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 VOTOROLA SOFTWARE OR THE USE OR OTHER DEALINGS IN THE VOTOROLA SOFTWARE. import com.google.gwt.core.client.Scheduler; import com.google.gwt.dom.client.*; import com.google.gwt.event.dom.client.*; import com.google.gwt.event.dom.client.ChangeHandler; // override namesake in package import com.google.gwt.event.logical.shared.*; import com.google.web.bindery.event.shared.*; import com.google.gwt.i18n.client.*; import com.google.gwt.user.client.ui.*; import java.util.*; import votorola.g.web.gwt.*; /** A scheduler wrapper that coalesces multiple scheduling requests. * * @see Scheduler Execution Order */ public abstract class CoalescingScheduler { CoalescingScheduler( Scheduler _baseScheduler ) { baseScheduler = _baseScheduler; } // - C o a l e s c i n g - S c h e d u l e r ------------------------------------------ /** The base, non-coalescing scheduler that is wrapped by this coalescing scheduler. * It may be used directly for non-coalescing requests, as well. */ public final Scheduler baseScheduler() { return baseScheduler; } final Scheduler baseScheduler; /** Ensures the command is scheduled for execution. If the command was previously * scheduled and has yet to finish, this method has no effect. */ public abstract void schedule(); // ==================================================================================== /** A tester of events and scheduled commands. */ public static final class Tester implements Runnable { private Tester( EventBus _bus ) { bus = _bus; eGHandler = new EGHandler(); } private final RootPanel body = RootPanel.get(); private final EventBus bus; private String context() { final StringBuilder b = GWTX.stringBuilderClear(); for( int s = stampStack.size() - 1; s >= 0; --s ) { final String stamp = stampStack.get( s ); b.append( stamp ); b.append( ' ' ); } return b.toString(); } private final EDHandler eDHandler = new EDHandler(); /** A test handler for a native DOM event. */ private final class EDHandler extends EHandler implements ChangeHandler { EDHandler() { super( "eD" ); // body.addDomHandler( EDHandler.this, ChangeEvent.getType() ); body.addHandler( EDHandler.this, ChangeEvent.getType() ); // no need to unregister, registry does not outlive the handler } public void onChange( ChangeEvent _e ) { handle(); } } private final EGHandler eGHandler; /** A test handler for a synthetic GWT event. */ private final class EGHandler extends EHandler implements ValueChangeHandler { EGHandler() { super( "eG" ); bus.addHandlerToSource( ValueChangeEvent.getType(), Tester.this, EGHandler.this ); // no need to unregister, registry does not outlive the handler } public void onValueChange( ValueChangeEvent _e ) { handle(); } } private abstract class EHandler extends Handler { EHandler( String type ) { super( type ); } String newStamp( final int serial ) { return "- " + serialFormatter.format(serial) + type; } } private void fireED() { final String stamp = eDHandler.newStamp( serial++ ); push( stamp ); try { System.out.println( context() ); DomEvent.fireNativeEvent( Document.get().createChangeEvent(), body ); } finally{ pop( stamp ); } } private void fireEG() { final String stamp = eGHandler.newStamp( serial++ ); push( stamp ); try { System.out.println( context() ); bus.fireEventFromSource( new ValueChange("dummy value"), Tester.this ); } finally{ pop( stamp ); } } private abstract class Handler { Handler( String _type ) { type = _type; } final String type; void handle() { System.out.print( " got " ); System.out.println( type ); if( stampStack.size() <= HANDLER_RERUNS ) rerun(); // from within event dispatch } } private static final int HANDLER_RERUNS = 2; // set 1 or higher for nested reruns /** @param stamp the stamp you previously pushed and now want to pop * @throws IllegalStateException if the stamp is not on top of the stack */ private void pop( final String stamp ) { final String topStamp = stampStack.remove( 0 ); if( !topStamp.equals( stamp )) throw new IllegalStateException(); } private void push( final String stamp ) { stampStack.add( 0, stamp ); } private void rerun() { scheduleFinally(); scheduleDeferred(); scheduleEntry(); // fireED(); // fireEG(); scheduleEntry(); scheduleDeferred(); scheduleFinally(); // fireED(); // fireEG(); scheduleEntry(); } private void scheduleDeferred() { Scheduler.get().scheduleDeferred( new SCommand( "sD" )); } private void scheduleEntry() { Scheduler.get().scheduleDeferred( new SCommand( "sE" )); } private void scheduleFinally() { Scheduler.get().scheduleFinally( new SCommand( "sF" )); } private static final int S_COMMAND_RERUNS = 1; // set 1 or higher for nested reruns private final class SCommand implements Scheduler.ScheduledCommand { SCommand( String _type ) { type = _type; stackSizeWhenScheduled = stampStack.size(); stamp = newStamp( serial++ ); push( stamp ); context = context(); pop( stamp ); } private final int stackSizeWhenScheduled; private final String context; private String newStamp( final int serial ) { return "| " + serialFormatter.format(serial) + type; } private final String stamp; private final String type; public void execute() { System.out.println( context ); if( stackSizeWhenScheduled < S_COMMAND_RERUNS ) { push( stamp ); try{ rerun(); } // from within executor finally{ pop( stamp ); } } } } private int serial = 1; private static final NumberFormat serialFormatter = NumberFormat.getFormat( "000" ); private final ArrayList stampStack = new ArrayList(); // -------------------------------------------------------------------------------- /** Returns the single instance of Tester, constructing it if necessary. */ public static Tester i( final EventBus bus ) { if( instance == null ) instance = new Tester( bus ); return instance; } private static Tester instance; // - R u n n a b l e -------------------------------------------------------------- /** Runs the test, printing the results to standard output. It should therefore * be run in devmode. */ public void run() { System.out.println(); rerun(); } } //// P r i v a t e /////////////////////////////////////////////////////////////////////// boolean isScheduled; }