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}