Subversion Repository Public Repository

litesoft

Diff Revisions 546 vs 547 for /trunk/GWT_Sandbox/MultiModule/common/src/org/litesoft/sandbox/infrastructure/client/PlaceActivityManager.java

Diff revisions: vs.
  @@ -20,23 +20,24 @@
20 20 @Override
21 21 public void start( AcceptsOneWidget panel )
22 22 {
23 + panel.setWidget( new Label( "!" ) );
23 24 }
24 25 };
25 26
26 - private final AcceptsOneWidget display;
27 + private final AcceptsOneWidget mPanel;
27 28 private final CommonActivityParam mCommonActivityParam;
28 29
29 30 private final PlaceHistoryMapper mMapper;
30 31 private final WindowClose mWindowClose;
31 32 private final Historian mHistorian;
32 33 private Place mCurrentPlace = Place.NOWHERE;
33 - private Place mPendingAsyncPlace;
34 - private Activity currentActivity = NULL_ACTIVITY;
35 - private boolean startingNext = false;
34 + private Place mPendingCodePlace, mPendingAsyncPlace;
35 + private Activity mCurrentActivity = NULL_ACTIVITY;
36 + private Activity mPendingActivity;
36 37
37 38 public PlaceActivityManager( AcceptsOneWidget pPanel, PlaceHistoryMapper pMapper, WindowClose pWindowClose, Historian pHistorian, CommonActivityParam pCommonActivityParam )
38 39 {
39 - display = pPanel;
40 + mPanel = pPanel;
40 41 mCommonActivityParam = (pCommonActivityParam != null) ? pCommonActivityParam : DefaultCommonActivityParam.<CommonActivityParam>createInstance();
41 42 mMapper = (pMapper != null) ? pMapper : new PlaceRegistry( mCommonActivityParam );
42 43 mWindowClose = (pWindowClose != null) ? pWindowClose : new WindowCloseImpl();
  @@ -45,10 +46,10 @@
45 46 @Override
46 47 public void onWindowClosing( Window.ClosingEvent event )
47 48 {
48 - String warning = maybeGoTo( Place.NOWHERE );
49 - if ( warning != null )
49 + String zWarning = getLeavingCurrentPlaceWarningText();
50 + if ( zWarning != null )
50 51 {
51 - event.setMessage( warning );
52 + event.setMessage( zWarning );
52 53 }
53 54 }
54 55 } );
  @@ -58,7 +59,7 @@
58 59 @Override
59 60 public void onValueChange( ValueChangeEvent<String> event )
60 61 {
61 - System.out.println( "PlaceActivityManager.onValueChange: " + event.getValue() ); // todo...
62 + handleHistoryToken( event.getValue() );
62 63 }
63 64 } );
64 65 }
  @@ -82,195 +83,233 @@
82 83 handleHistoryToken( mHistorian.getToken() );
83 84 }
84 85
86 + /**
87 + * Returns the current place.
88 + *
89 + * @return a {@link Place} instance
90 + */
85 91 @Override
86 92 public Place getWhere()
87 93 {
88 94 return mCurrentPlace;
89 95 }
90 96
97 + /**
98 + * Request a change to a new place. It is not a given that we'll actually get
99 + * there. If there is an ActivityFactory for the Place, and the user does not
100 + * reject leaving the current Place, then the URL is adjusted to have the user
101 + * taken to the new place.
102 + *
103 + * @param pNewPlace a {@link Place} instance
104 + *
105 + * @return null if going to pNewPlace, !null means NOT going and why
106 + */
91 107 @Override
92 108 public GoToPlace goTo( Place pNewPlace )
93 109 {
94 - return goTo( pNewPlace, false );
95 - }
96 -
97 - private GoToPlace goTo( Place pNewPlace, boolean pFromHistory )
98 - {
99 - if ( (pNewPlace == null) || pNewPlace.equals( mCurrentPlace ) )
110 + pNewPlace = UtilsCommon.deNull( pNewPlace, mCurrentPlace );
111 + if ( pNewPlace.equals( mCurrentPlace ) )
100 112 {
101 113 return GoToPlace.AlreadyThere;
102 114 }
103 - if ( pNewPlace.equals( mPendingAsyncPlace ) )
115 + if ( pNewPlace.equals( mPendingCodePlace ) || pNewPlace.equals( mPendingAsyncPlace ) )
104 116 {
105 117 return null;
106 118 }
107 -
108 - ActivityFactory zFactory = get( PlaceIdExtractor.getPlaceId( pNewPlace ) );
109 - if ( zFactory == null )
119 + if ( null == get( PlaceIdExtractor.getPlaceId( pNewPlace ) ) ) // Check for Factory
110 120 {
111 121 return GoToPlace.NoActivity;
112 122 }
113 -
114 - String zWarning = maybeGoTo( pNewPlace );
115 - if ( zWarning != null && !mWindowClose.confirm( zWarning ) )
116 - {
117 - return GoToPlace.CurrentActivityRejectedLeaving; // todo: if from Code, then why didn't the code not allow it, if from History, then the URL no longer matches the View!!!
118 - }
119 - if ( zFactory instanceof ActivityFactory.Asynchronous ) // Un-Happy Case!
123 + if ( userRejectedLeavingCurrentPlace( pNewPlace ) )
120 124 {
121 - return LoadSyncFactory( (ActivityFactory.Asynchronous) zFactory, mPendingAsyncPlace = pNewPlace );
125 + return GoToPlace.CurrentActivityRejectedLeaving; // From Code, maybe the code shouldn't have allowed it!
122 126 }
123 127 mPendingAsyncPlace = null;
124 - // todo: Historian?
125 - return justGo( pNewPlace, (ActivityFactory.Synchronous) zFactory );
128 + mPendingCodePlace = pNewPlace;
129 + mHistorian.newItem( mMapper.getToken( pNewPlace ), true );
130 + return null;
126 131 }
127 132
128 - private String maybeGoTo( Place newPlace )
133 + private void handleHistoryToken( String pToken )
129 134 {
130 - return currentActivity.mayStop();
131 - }
135 + mCommonActivityParam.getMessageUserSink().clearMessage();
132 136
133 - private void switchAsyncToSync( Place pPlace, ActivityFactory.Asynchronous pAsyncFactory, ActivityFactory.Synchronous pSyncFactory )
134 - {
135 - boolean zStillGoing = pPlace.equals( mPendingAsyncPlace );
136 - mPendingAsyncPlace = null;
137 - if ( replace( pAsyncFactory, pSyncFactory ) && zStillGoing )
137 + Place zNewPlace = null;
138 +
139 + if ( null == (pToken = UtilsCommon.noEmpty( pToken )) )
138 140 {
139 - justGo( pPlace, pSyncFactory );
141 + zNewPlace = PlaceRegistry.getDefaultPlace();
140 142 }
141 - }
142 -
143 - private GoToPlace justGo( Place pPlace, ActivityFactory.Synchronous pSyncFactory )
144 - {
145 - mCurrentPlace = pPlace;
146 - Activity zActivity = createActivity( pSyncFactory, pPlace );
147 - onPlaceChange( pPlace, zActivity );
148 - return null;
143 + else
144 + {
145 + try
146 + {
147 + if ( null == (zNewPlace = mMapper.getPlace( pToken )) )
148 + {
149 + LOGGER.info.log( "Unrecognized history token: ", pToken );
150 + }
151 + }
152 + catch ( RuntimeException e )
153 + {
154 + LOGGER.warn.log( e, "Unable to parse history token: ", pToken );
155 + }
156 + }
157 + zNewPlace = UtilsCommon.deNull( zNewPlace, mCurrentPlace );
158 + if ( zNewPlace.equals( mCurrentPlace ) )
159 + {
160 + mPendingCodePlace = null;
161 + mPendingAsyncPlace = null;
162 + return;
163 + }
164 + if ( (mPendingCodePlace != null) && !zNewPlace.equals( mPendingCodePlace ) && userRejectedLeavingCurrentPlace( zNewPlace ) )
165 + {
166 + mPendingCodePlace = null;
167 + mPendingAsyncPlace = null;
168 + mHistorian.newItem( mMapper.getToken( mCurrentPlace ), false ); // reset the URL back to the Current Place
169 + return;
170 + }
171 + mPendingCodePlace = null;
172 + if ( zNewPlace.equals( mPendingAsyncPlace ) )
173 + {
174 + return;
175 + }
176 + ActivityFactory zFactory = get( zNewPlace );
177 + if ( zFactory == null )
178 + {
179 + mPendingAsyncPlace = null;
180 + mCommonActivityParam.getMessageUserSink().setErrorMessage( "No Activity Factory for '" + UtilsCommon.justSimpleName( zNewPlace ) + "' from: " + pToken );
181 + mHistorian.newItem( mMapper.getToken( mCurrentPlace ), false ); // reset the URL back to the Current Place
182 + return;
183 + }
184 + if ( zFactory instanceof ActivityFactory.Asynchronous ) // Un-Happy Case!
185 + {
186 + loadSyncFactory( (ActivityFactory.Asynchronous) zFactory, mPendingAsyncPlace = zNewPlace );
187 + return;
188 + }
189 + mPendingAsyncPlace = null;
190 + activate( zNewPlace, (ActivityFactory.Synchronous) zFactory );
149 191 }
150 192
151 193 /**
152 - * Deactivate the current activity, find the next one from our ActivityMapper,
153 - * and start it.
194 + * Activate the New Place and it's associated Activity (deactivating the current activity.
154 195 * <p/>
196 + * Note:
155 197 * The current activity's widget will be hidden immediately, which can cause
156 198 * flicker if the next activity provides its widget asynchronously. That can
157 199 * be minimized by decent caching. Perenially slow activities might mitigate
158 200 * this by providing a widget immediately, with some kind of "loading"
159 201 * treatment.
160 202 */
161 - private void onPlaceChange( Place pPlace, Activity nextActivity )
203 + protected void activate( Place pNewPlace, ActivityFactory.Synchronous pSyncFactory )
162 204 {
163 - Throwable caughtOnStop = null;
164 - Throwable caughtOnCancel = null;
165 - Throwable caughtOnStart = null;
205 + Set<Throwable> zIssues = new LinkedHashSet<Throwable>();
206 +
207 + mCurrentPlace = pNewPlace;
208 +
209 + Activity zNewActivity = UtilsCommon.deNull( createActivity( zIssues, pSyncFactory, pNewPlace ), NULL_ACTIVITY );
166 210
167 - if ( nextActivity == null )
211 + if ( (mPendingActivity != null) && (mPendingActivity != zNewActivity) ) // The place changed again before the new Activity showed its widget
168 212 {
169 - nextActivity = NULL_ACTIVITY;
213 + tryCancel( zIssues, mPendingActivity );
170 214 }
215 + mPendingActivity = zNewActivity;
171 216
172 - if ( currentActivity.equals( nextActivity ) )
217 + // Wrap the actual panel with a per-call instance that protects the actual panel from canceled or stopped activities, and
218 + // which handles the Pending Activity becoming the Current Activity.
219 + AcceptsOneWidget zPanel = new ProtectedDisplay( zIssues, mPendingActivity );
220 +
221 + if ( !canStart( zIssues, mPendingActivity, zPanel ) )
173 222 {
174 - return;
223 + NULL_ACTIVITY.start( new ProtectedDisplay( zIssues, mPendingActivity = NULL_ACTIVITY ) ); // Can NOT fail!
175 224 }
225 + }
176 226
177 - if ( startingNext )
227 + @SuppressWarnings({"unchecked"})
228 + protected Activity createActivity( Collection<Throwable> pIssues, ActivityFactory.Synchronous pFactory, Place pPlace )
229 + {
230 + try
178 231 {
179 - // The place changed again before the new current activity showed its
180 - // widget
181 - caughtOnCancel = tryStopOrCancel( false );
182 - currentActivity = NULL_ACTIVITY;
183 - startingNext = false;
232 + return pFactory.createActivity( mCommonActivityParam, pFactory.getView(), pPlace );
184 233 }
185 - else if ( !currentActivity.equals( NULL_ACTIVITY ) )
234 + catch ( Exception e )
186 235 {
187 - showWidget( null );
188 -
189 - caughtOnStop = tryStopOrCancel( true );
236 + pIssues.add( e );
190 237 }
238 + return null;
239 + }
191 240
192 - currentActivity = nextActivity;
193 -
194 - if ( currentActivity.equals( NULL_ACTIVITY ) )
241 + private boolean canStart( Set<Throwable> pIssues, Activity pActivity, AcceptsOneWidget pPanel )
242 + {
243 + try
195 244 {
196 - showWidget( null );
245 + pActivity.start( pPanel );
246 + return true;
197 247 }
198 - else
248 + catch ( Exception e )
199 249 {
200 - startingNext = true;
201 - caughtOnStart = tryStart();
250 + pIssues.add( e );
202 251 }
252 + return false;
253 + }
203 254
204 - if ( caughtOnStart != null || caughtOnCancel != null || caughtOnStop != null )
255 + private void tryCancel( Collection<Throwable> pIssues, Activity pActivity )
256 + {
257 + try
205 258 {
206 - Set<Throwable> causes = new LinkedHashSet<Throwable>();
207 - if ( caughtOnStop != null )
208 - {
209 - causes.add( caughtOnStop );
210 - }
211 - if ( caughtOnCancel != null )
212 - {
213 - causes.add( caughtOnCancel );
214 - }
215 - if ( caughtOnStart != null )
216 - {
217 - causes.add( caughtOnStart );
218 - }
219 -
220 - throw new UmbrellaException( causes );
259 + pActivity.onCancel();
260 + }
261 + catch ( Exception e )
262 + {
263 + pIssues.add( e );
221 264 }
222 265 }
223 266
224 - private Throwable tryStopOrCancel( boolean stop )
267 + private void tryStop( Collection<Throwable> pIssues, Activity pActivity )
225 268 {
226 - Throwable caughtOnStop = null;
227 269 try
228 270 {
229 - if ( stop )
230 - {
231 - currentActivity.onStop();
232 - }
233 - else
234 - {
235 - currentActivity.onCancel();
236 - }
271 + pActivity.onStop();
237 272 }
238 - catch ( Throwable t )
273 + catch ( Exception e )
239 274 {
240 - caughtOnStop = t;
275 + pIssues.add( e );
241 276 }
242 - return caughtOnStop;
243 277 }
244 278
245 - private Throwable tryStart()
279 + private void possiblyUpdateActivityAndShowWidget( Set<Throwable> pIssues, Activity pActivity, IsWidget pWidget )
246 280 {
247 - Throwable caughtOnStart = null;
248 - try
281 + if ( mPendingActivity == null ) // Normal? Happy case when No Pending Activity and request is from Current Activity
282 + {
283 + if ( pActivity == mCurrentActivity )
284 + {
285 + showWidget( pWidget );
286 + }
287 + return;
288 + }
289 + if ( pActivity != mPendingActivity ) // Is a Pending Activity, but the request is NOT from it!
249 290 {
250 - /*
251 - * Wrap the actual display with a per-call instance that protects the
252 - * display from canceled or stopped activities, and which maintains our
253 - * startingNext state.
254 - */
255 - currentActivity.start( new ProtectedDisplay( currentActivity ) );
291 + return;
256 292 }
257 - catch ( Throwable t )
293 + tryStop( pIssues, mCurrentActivity );
294 + mCurrentActivity = mPendingActivity;
295 + mPendingActivity = null;
296 + showWidget( pWidget );
297 + if ( !pIssues.isEmpty() )
258 298 {
259 - caughtOnStart = t;
299 + throw new UmbrellaException( pIssues );
260 300 }
261 - return caughtOnStart;
262 301 }
263 302
264 - private void showWidget( IsWidget view )
303 + private void showWidget( IsWidget pWidget )
265 304 {
266 - if ( display != null )
305 + if ( mPanel != null )
267 306 {
268 - display.setWidget( view );
307 + mPanel.setWidget( pWidget );
269 308 }
270 309 }
271 310
272 311 @SuppressWarnings({"unchecked"})
273 - private GoToPlace LoadSyncFactory( final ActivityFactory.Asynchronous pAsyncFactory, final Place pPlace )
312 + private void loadSyncFactory( final ActivityFactory.Asynchronous pAsyncFactory, final Place pPlace )
274 313 {
275 314 pAsyncFactory.load( new ActivityFactory.Asynchronous.Callback<CommonActivityParam, IsWidget, Place>()
276 315 {
  @@ -280,75 +319,32 @@
280 319 switchAsyncToSync( pPlace, pAsyncFactory, pSynchronousFactory );
281 320 }
282 321 } );
283 - return null;
284 322 }
285 323
286 - protected String createMessage( String pPlaceId )
324 + private void switchAsyncToSync( Place pPlace, ActivityFactory.Asynchronous pAsyncFactory, ActivityFactory.Synchronous pSyncFactory )
287 325 {
288 - return "Unable to locate 'View' for '" + pPlaceId + "'";
326 + boolean zStillGoing = pPlace.equals( mPendingAsyncPlace );
327 + mPendingAsyncPlace = null;
328 + if ( replace( pAsyncFactory, pSyncFactory ) && zStillGoing )
329 + {
330 + activate( pPlace, pSyncFactory );
331 + }
289 332 }
290 333
291 - @SuppressWarnings({"unchecked"})
292 - protected Activity createActivity( ActivityFactory.Synchronous pFactory, Place pPlace )
334 + protected ActivityFactory get( Place pPlace )
293 335 {
294 - return pFactory.createActivity( mCommonActivityParam, pFactory.getView(), pPlace );
336 + return get( PlaceIdExtractor.getPlaceId( pPlace ) );
295 337 }
296 338
297 - public Activity getActivity( Place pPlace )
339 + protected boolean userRejectedLeavingCurrentPlace( Place newPlace )
298 340 {
299 - String zPlaceId = PlaceIdExtractor.getPlaceId( pPlace );
300 - if ( zPlaceId != null )
301 - {
302 - ActivityFactory zFactory = get( zPlaceId );
303 - if ( zFactory instanceof ActivityFactory.Synchronous )
304 - {
305 - try
306 - {
307 - return createActivity( (ActivityFactory.Synchronous) zFactory, pPlace );
308 - }
309 - catch ( RuntimeException e )
310 - {
311 - String zMessage = createMessage( zPlaceId );
312 - LOGGER.error.log( zMessage, ": ", pPlace.getClass().getName() );
313 - mCommonActivityParam.getMessageUserSink().setErrorMessage( zMessage + "!" );
314 - return null;
315 - }
316 - }
317 - if ( zFactory instanceof ActivityFactory.Asynchronous )
318 - {
319 - String zMessage = createMessage( zPlaceId );
320 - LOGGER.error.log( zMessage, " - ", zFactory.getClass().getName() );
321 - mCommonActivityParam.getMessageUserSink().setErrorMessage( zMessage + "?" );
322 - return null;
323 - }
324 - mCommonActivityParam.getMessageUserSink().setErrorMessage( createMessage( zPlaceId ) );
325 - }
326 - return null;
341 + String zWarning = getLeavingCurrentPlaceWarningText();
342 + return (zWarning != null) && !mWindowClose.confirm( zWarning );
327 343 }
328 344
329 - private void handleHistoryToken( String token )
345 + protected String getLeavingCurrentPlaceWarningText()
330 346 {
331 - Place newPlace = null;
332 -
333 - if ( null != (token = UtilsCommon.noEmpty( token )) )
334 - {
335 - try
336 - {
337 - if ( null == (newPlace = mMapper.getPlace( token )) )
338 - {
339 - LOGGER.info.log( "Unrecognized history token: ", token );
340 - }
341 - }
342 - catch ( RuntimeException e )
343 - {
344 - LOGGER.warn.log( e, "Unable to parse history token: ", token );
345 - }
346 - }
347 - if ( newPlace == null )
348 - {
349 - newPlace = PlaceRegistry.getDefaultPlace();
350 - }
351 - goTo( newPlace, true );
347 + return mCurrentActivity.mayStop();
352 348 }
353 349
354 350 /**
  @@ -375,21 +371,19 @@
375 371 */
376 372 private class ProtectedDisplay implements AcceptsOneWidget
377 373 {
378 - private final Activity activity;
374 + private final Set<Throwable> mIssues;
375 + private final Activity mExpectedActivity;
379 376
380 - ProtectedDisplay( Activity activity )
377 + ProtectedDisplay( Set<Throwable> pIssues, Activity pExpectedActivity )
381 378 {
382 - this.activity = activity;
379 + mIssues = pIssues;
380 + mExpectedActivity = pExpectedActivity;
383 381 }
384 382
385 383 @Override
386 - public void setWidget( IsWidget view )
384 + public void setWidget( IsWidget pWidget )
387 385 {
388 - if ( this.activity == currentActivity )
389 - {
390 - startingNext = false;
391 - showWidget( view );
392 - }
386 + possiblyUpdateActivityAndShowWidget( mIssues, mExpectedActivity, pWidget );
393 387 }
394 388 }
395 389 }