001package 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. 002 003import com.google.gwt.core.client.Scheduler; 004import com.google.gwt.dom.client.*; 005import com.google.gwt.event.dom.client.*; 006import com.google.gwt.event.dom.client.ChangeHandler; // override namesake in package 007import com.google.gwt.event.logical.shared.*; 008import com.google.web.bindery.event.shared.*; 009import com.google.gwt.i18n.client.*; 010import com.google.gwt.user.client.ui.*; 011import java.util.*; 012import votorola.g.web.gwt.*; 013 014 015/** A scheduler wrapper that coalesces multiple scheduling requests. 016 * 017 * @see <a href='package-summary.html#schedulingOrder'>Scheduler Execution Order</a> 018 */ 019public abstract class CoalescingScheduler 020{ 021 022 023 CoalescingScheduler( Scheduler _baseScheduler ) { baseScheduler = _baseScheduler; } 024 025 026 027 // - C o a l e s c i n g - S c h e d u l e r ------------------------------------------ 028 029 030 /** The base, non-coalescing scheduler that is wrapped by this coalescing scheduler. 031 * It may be used directly for non-coalescing requests, as well. 032 */ 033 public final Scheduler baseScheduler() { return baseScheduler; } 034 035 036 final Scheduler baseScheduler; 037 038 039 040 /** Ensures the command is scheduled for execution. If the command was previously 041 * scheduled and has yet to finish, this method has no effect. 042 */ 043 public abstract void schedule(); 044 045 046 047 // ==================================================================================== 048 049 050 /** A tester of events and scheduled commands. 051 */ 052 public static final class Tester implements Runnable 053 { 054 055 056 private Tester( EventBus _bus ) 057 { 058 bus = _bus; 059 eGHandler = new EGHandler(); 060 } 061 062 063 064 private final RootPanel body = RootPanel.get(); 065 066 067 068 private final EventBus bus; 069 070 071 072 private String context() 073 { 074 final StringBuilder b = GWTX.stringBuilderClear(); 075 for( int s = stampStack.size() - 1; s >= 0; --s ) 076 { 077 final String stamp = stampStack.get( s ); 078 b.append( stamp ); 079 b.append( ' ' ); 080 } 081 return b.toString(); 082 } 083 084 085 086 private final EDHandler eDHandler = new EDHandler(); 087 088 089 090 /** A test handler for a native DOM event. 091 */ 092 private final class EDHandler extends EHandler implements ChangeHandler 093 { 094 EDHandler() 095 { 096 super( "eD" ); 097 // body.addDomHandler( EDHandler.this, ChangeEvent.getType() ); 098 body.addHandler( EDHandler.this, ChangeEvent.getType() ); // no need to unregister, registry does not outlive the handler 099 } 100 101 public void onChange( ChangeEvent _e ) { handle(); } 102 } 103 104 105 106 private final EGHandler eGHandler; 107 108 109 110 /** A test handler for a synthetic GWT event. 111 */ 112 private final class EGHandler extends EHandler implements ValueChangeHandler<String> 113 { 114 EGHandler() 115 { 116 super( "eG" ); 117 bus.addHandlerToSource( ValueChangeEvent.getType(), Tester.this, EGHandler.this ); // no need to unregister, registry does not outlive the handler 118 } 119 120 public void onValueChange( ValueChangeEvent<String> _e ) { handle(); } 121 } 122 123 124 125 private abstract class EHandler extends Handler 126 { 127 EHandler( String type ) { super( type ); } 128 129 String newStamp( final int serial ) 130 { 131 return "- " + serialFormatter.format(serial) + type; 132 } 133 } 134 135 136 137 private void fireED() 138 { 139 final String stamp = eDHandler.newStamp( serial++ ); 140 push( stamp ); 141 try 142 { 143 System.out.println( context() ); 144 DomEvent.fireNativeEvent( Document.get().createChangeEvent(), body ); 145 } 146 finally{ pop( stamp ); } 147 } 148 149 150 151 private void fireEG() 152 { 153 final String stamp = eGHandler.newStamp( serial++ ); 154 push( stamp ); 155 try 156 { 157 System.out.println( context() ); 158 bus.fireEventFromSource( new ValueChange<String>("dummy value"), 159 Tester.this ); 160 } 161 finally{ pop( stamp ); } 162 } 163 164 165 166 private abstract class Handler 167 { 168 Handler( String _type ) { type = _type; } 169 170 final String type; 171 void handle() 172 { 173 System.out.print( " got " ); 174 System.out.println( type ); 175 if( stampStack.size() <= HANDLER_RERUNS ) rerun(); // from within event dispatch 176 } 177 } 178 179 180 private static final int HANDLER_RERUNS = 2; // set 1 or higher for nested reruns 181 182 183 184 /** @param stamp the stamp you previously pushed and now want to pop 185 * @throws IllegalStateException if the stamp is not on top of the stack 186 */ 187 private void pop( final String stamp ) 188 { 189 final String topStamp = stampStack.remove( 0 ); 190 if( !topStamp.equals( stamp )) throw new IllegalStateException(); 191 } 192 193 194 195 private void push( final String stamp ) { stampStack.add( 0, stamp ); } 196 197 198 199 private void rerun() 200 { 201 scheduleFinally(); 202 scheduleDeferred(); 203 scheduleEntry(); 204 205 // fireED(); 206 // fireEG(); 207 208 scheduleEntry(); 209 scheduleDeferred(); 210 scheduleFinally(); 211 212 // fireED(); 213 // fireEG(); 214 215 scheduleEntry(); 216 } 217 218 219 220 private void scheduleDeferred() { Scheduler.get().scheduleDeferred( new SCommand( "sD" )); } 221 222 223 224 private void scheduleEntry() { Scheduler.get().scheduleDeferred( new SCommand( "sE" )); } 225 226 227 228 private void scheduleFinally() { Scheduler.get().scheduleFinally( new SCommand( "sF" )); } 229 230 231 232 private static final int S_COMMAND_RERUNS = 1; // set 1 or higher for nested reruns 233 234 235 236 private final class SCommand implements Scheduler.ScheduledCommand 237 { 238 SCommand( String _type ) 239 { 240 type = _type; 241 stackSizeWhenScheduled = stampStack.size(); 242 stamp = newStamp( serial++ ); 243 push( stamp ); context = context(); pop( stamp ); 244 } 245 private final int stackSizeWhenScheduled; 246 private final String context; 247 private String newStamp( final int serial ) 248 { 249 return "| " + serialFormatter.format(serial) + type; 250 } 251 private final String stamp; 252 private final String type; 253 254 public void execute() 255 { 256 System.out.println( context ); 257 if( stackSizeWhenScheduled < S_COMMAND_RERUNS ) 258 { 259 push( stamp ); 260 try{ rerun(); } // from within executor 261 finally{ pop( stamp ); } 262 } 263 } 264 } 265 266 267 268 private int serial = 1; 269 270 271 272 private static final NumberFormat serialFormatter = NumberFormat.getFormat( "000" ); 273 274 275 276 private final ArrayList<String> stampStack = new ArrayList<String>(); 277 278 279 280 // -------------------------------------------------------------------------------- 281 282 283 /** Returns the single instance of Tester, constructing it if necessary. 284 */ 285 public static Tester i( final EventBus bus ) 286 { 287 if( instance == null ) instance = new Tester( bus ); 288 289 return instance; 290 } 291 292 293 private static Tester instance; 294 295 296 297 // - R u n n a b l e -------------------------------------------------------------- 298 299 300 /** Runs the test, printing the results to standard output. It should therefore 301 * be run in devmode. 302 */ 303 public void run() 304 { 305 System.out.println(); 306 rerun(); 307 } 308 309 310 311 } 312 313 314 315//// P r i v a t e /////////////////////////////////////////////////////////////////////// 316 317 318 boolean isScheduled; 319 320 321 322}