|
@@ -1,44 +1,31 @@ |
1 |
1 |
|
package org.litesoft.sandbox.infrastructure.client; |
2 |
2 |
|
|
3 |
|
- |
import java.util.*; |
4 |
|
- |
|
5 |
3 |
|
import org.litesoft.logger.*; |
6 |
4 |
|
import org.litesoft.sandbox.anywhere.util.*; |
|
5 |
+ |
import org.litesoft.sandbox.infrastructure.client.internal.*; |
7 |
6 |
|
|
8 |
7 |
|
import com.google.gwt.event.logical.shared.*; |
9 |
|
- |
import com.google.gwt.event.shared.*; |
10 |
8 |
|
import com.google.gwt.place.shared.*; |
11 |
9 |
|
import com.google.gwt.user.client.*; |
12 |
10 |
|
import com.google.gwt.user.client.ui.*; |
13 |
11 |
|
|
14 |
|
- |
public class PlaceActivityManager<CommonActivityParam extends CommonActivityParameter> extends ActivityFactoryRegistry implements PlaceChanger |
|
12 |
+ |
public class PlaceActivityManager<CommonActivityParam extends CommonActivityParameter> implements PlaceChanger |
15 |
13 |
|
{ |
16 |
|
- |
private static Logger LOGGER = LoggerFactory.getLogger( PlaceActivityManager.class ); |
|
14 |
+ |
private static final Logger LOGGER = LoggerFactory.getLogger( PlaceActivityManager.class ); |
17 |
15 |
|
|
18 |
|
- |
private static final Activity NULL_ACTIVITY = new AbstractActivity() |
19 |
|
- |
{ |
20 |
|
- |
@Override |
21 |
|
- |
public void start( AcceptsOneWidget panel ) |
22 |
|
- |
{ |
23 |
|
- |
panel.setWidget( new Label( "!" ) ); |
24 |
|
- |
} |
25 |
|
- |
}; |
|
16 |
+ |
private final PanelPlaceActivityHelper mActivityHelper; |
26 |
17 |
|
|
27 |
|
- |
private final AcceptsOneWidget mPanel; |
28 |
18 |
|
private final CommonActivityParam mCommonActivityParam; |
29 |
19 |
|
|
30 |
20 |
|
private final PlaceHistoryMapper mMapper; |
31 |
21 |
|
private final WindowClose mWindowClose; |
32 |
22 |
|
private final Historian mHistorian; |
33 |
|
- |
private Place mCurrentPlace = Place.NOWHERE; |
34 |
|
- |
private Place mPendingCodePlace, mPendingAsyncPlace; |
35 |
|
- |
private Activity mCurrentActivity = NULL_ACTIVITY; |
36 |
|
- |
private Activity mPendingActivity; |
|
23 |
+ |
private Place mPendingCodePlace; |
37 |
24 |
|
|
38 |
25 |
|
public PlaceActivityManager( AcceptsOneWidget pPanel, PlaceHistoryMapper pMapper, WindowClose pWindowClose, Historian pHistorian, CommonActivityParam pCommonActivityParam ) |
39 |
26 |
|
{ |
40 |
|
- |
mPanel = pPanel; |
41 |
|
- |
mCommonActivityParam = (pCommonActivityParam != null) ? pCommonActivityParam : DefaultCommonActivityParam.<CommonActivityParam>createInstance(); |
|
27 |
+ |
mCommonActivityParam = (pCommonActivityParam != null) ? pCommonActivityParam : createDefaultCommonActivityParam(); |
|
28 |
+ |
mActivityHelper = new PanelPlaceActivityHelper<CommonActivityParam>( pPanel, mCommonActivityParam ); |
42 |
29 |
|
mMapper = (pMapper != null) ? pMapper : new PlaceRegistry( mCommonActivityParam ); |
43 |
30 |
|
mWindowClose = (pWindowClose != null) ? pWindowClose : new WindowCloseImpl(); |
44 |
31 |
|
mWindowClose.addWindowClosingHandler( new Window.ClosingHandler() |
|
@@ -91,7 +78,7 @@ |
91 |
78 |
|
@Override |
92 |
79 |
|
public Place getWhere() |
93 |
80 |
|
{ |
94 |
|
- |
return mCurrentPlace; |
|
81 |
+ |
return mActivityHelper.getCurrentPlace(); |
95 |
82 |
|
} |
96 |
83 |
|
|
97 |
84 |
|
/** |
|
@@ -107,16 +94,17 @@ |
107 |
94 |
|
@Override |
108 |
95 |
|
public GoToPlace goTo( Place pNewPlace ) |
109 |
96 |
|
{ |
110 |
|
- |
pNewPlace = UtilsCommon.deNull( pNewPlace, mCurrentPlace ); |
111 |
|
- |
if ( pNewPlace.equals( mCurrentPlace ) ) |
|
97 |
+ |
Place zCurrentPlace = mActivityHelper.getCurrentPlace(); |
|
98 |
+ |
pNewPlace = UtilsCommon.deNull( pNewPlace, zCurrentPlace ); |
|
99 |
+ |
if ( pNewPlace.equals( zCurrentPlace ) ) |
112 |
100 |
|
{ |
113 |
101 |
|
return GoToPlace.AlreadyThere; |
114 |
102 |
|
} |
115 |
|
- |
if ( pNewPlace.equals( mPendingCodePlace ) || pNewPlace.equals( mPendingAsyncPlace ) ) |
|
103 |
+ |
if ( pNewPlace.equals( mPendingCodePlace ) || mActivityHelper.isPendingAsync( pNewPlace ) ) |
116 |
104 |
|
{ |
117 |
105 |
|
return null; |
118 |
106 |
|
} |
119 |
|
- |
if ( null == get( PlaceIdExtractor.getPlaceId( pNewPlace ) ) ) // Check for Factory |
|
107 |
+ |
if ( null == mActivityHelper.get( pNewPlace ) ) // Check for Factory |
120 |
108 |
|
{ |
121 |
109 |
|
return GoToPlace.NoActivity; |
122 |
110 |
|
} |
|
@@ -124,7 +112,7 @@ |
124 |
112 |
|
{ |
125 |
113 |
|
return GoToPlace.CurrentActivityRejectedLeaving; // From Code, maybe the code shouldn't have allowed it! |
126 |
114 |
|
} |
127 |
|
- |
mPendingAsyncPlace = null; |
|
115 |
+ |
mActivityHelper.clearPendingAsync(); |
128 |
116 |
|
mPendingCodePlace = pNewPlace; |
129 |
117 |
|
mHistorian.newItem( mMapper.getToken( pNewPlace ), true ); |
130 |
118 |
|
return null; |
|
@@ -132,6 +120,8 @@ |
132 |
120 |
|
|
133 |
121 |
|
private void handleHistoryToken( String pToken ) |
134 |
122 |
|
{ |
|
123 |
+ |
Place zWasGoingToCodePlace = mPendingCodePlace; |
|
124 |
+ |
mPendingCodePlace = null; |
135 |
125 |
|
mCommonActivityParam.getMessageUserSink().clearMessage(); |
136 |
126 |
|
|
137 |
127 |
|
Place zNewPlace = null; |
|
@@ -154,186 +144,34 @@ |
154 |
144 |
|
LOGGER.warn.log( e, "Unable to parse history token: ", pToken ); |
155 |
145 |
|
} |
156 |
146 |
|
} |
157 |
|
- |
zNewPlace = UtilsCommon.deNull( zNewPlace, mCurrentPlace ); |
158 |
|
- |
if ( zNewPlace.equals( mCurrentPlace ) ) |
|
147 |
+ |
Place zCurrentPlace = mActivityHelper.getCurrentPlace(); |
|
148 |
+ |
zNewPlace = UtilsCommon.deNull( zNewPlace, zCurrentPlace ); |
|
149 |
+ |
if ( zNewPlace.equals( zCurrentPlace ) ) |
159 |
150 |
|
{ |
160 |
|
- |
mPendingCodePlace = null; |
161 |
|
- |
mPendingAsyncPlace = null; |
|
151 |
+ |
mActivityHelper.clearPendingAsync(); |
162 |
152 |
|
return; |
163 |
153 |
|
} |
164 |
|
- |
if ( (mPendingCodePlace != null) && !zNewPlace.equals( mPendingCodePlace ) && userRejectedLeavingCurrentPlace( zNewPlace ) ) |
|
154 |
+ |
if ( (zWasGoingToCodePlace != null) && !zNewPlace.equals( zWasGoingToCodePlace ) && userWantsToStopUrlChange( zNewPlace ) ) |
165 |
155 |
|
{ |
166 |
|
- |
mPendingCodePlace = null; |
167 |
|
- |
mPendingAsyncPlace = null; |
168 |
|
- |
mHistorian.newItem( mMapper.getToken( mCurrentPlace ), false ); // reset the URL back to the Current Place |
|
156 |
+ |
mActivityHelper.clearPendingAsync(); |
|
157 |
+ |
resetURLbackTo( zCurrentPlace ); |
169 |
158 |
|
return; |
170 |
159 |
|
} |
171 |
|
- |
mPendingCodePlace = null; |
172 |
|
- |
if ( zNewPlace.equals( mPendingAsyncPlace ) ) |
|
160 |
+ |
if ( !mActivityHelper.activate( zNewPlace ) ) |
173 |
161 |
|
{ |
174 |
|
- |
return; |
175 |
|
- |
} |
176 |
|
- |
ActivityFactory zFactory = get( zNewPlace ); |
177 |
|
- |
if ( zFactory == null ) |
178 |
|
- |
{ |
179 |
|
- |
mPendingAsyncPlace = null; |
180 |
162 |
|
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 ); |
191 |
|
- |
} |
192 |
|
- |
|
193 |
|
- |
/** |
194 |
|
- |
* Activate the New Place and it's associated Activity (deactivating the current activity. |
195 |
|
- |
* <p/> |
196 |
|
- |
* Note: |
197 |
|
- |
* The current activity's widget will be hidden immediately, which can cause |
198 |
|
- |
* flicker if the next activity provides its widget asynchronously. That can |
199 |
|
- |
* be minimized by decent caching. Perenially slow activities might mitigate |
200 |
|
- |
* this by providing a widget immediately, with some kind of "loading" |
201 |
|
- |
* treatment. |
202 |
|
- |
*/ |
203 |
|
- |
protected void activate( Place pNewPlace, ActivityFactory.Synchronous pSyncFactory ) |
204 |
|
- |
{ |
205 |
|
- |
Set<Throwable> zIssues = new LinkedHashSet<Throwable>(); |
206 |
|
- |
|
207 |
|
- |
mCurrentPlace = pNewPlace; |
208 |
|
- |
|
209 |
|
- |
Activity zNewActivity = UtilsCommon.deNull( createActivity( zIssues, pSyncFactory, pNewPlace ), NULL_ACTIVITY ); |
210 |
|
- |
|
211 |
|
- |
if ( (mPendingActivity != null) && (mPendingActivity != zNewActivity) ) // The place changed again before the new Activity showed its widget |
212 |
|
- |
{ |
213 |
|
- |
tryCancel( zIssues, mPendingActivity ); |
214 |
|
- |
} |
215 |
|
- |
mPendingActivity = zNewActivity; |
216 |
|
- |
|
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 ) ) |
222 |
|
- |
{ |
223 |
|
- |
NULL_ACTIVITY.start( new ProtectedDisplay( zIssues, mPendingActivity = NULL_ACTIVITY ) ); // Can NOT fail! |
224 |
|
- |
} |
225 |
|
- |
} |
226 |
|
- |
|
227 |
|
- |
@SuppressWarnings({"unchecked"}) |
228 |
|
- |
protected Activity createActivity( Collection<Throwable> pIssues, ActivityFactory.Synchronous pFactory, Place pPlace ) |
229 |
|
- |
{ |
230 |
|
- |
try |
231 |
|
- |
{ |
232 |
|
- |
return pFactory.createActivity( mCommonActivityParam, pFactory.getView(), pPlace ); |
233 |
|
- |
} |
234 |
|
- |
catch ( Exception e ) |
235 |
|
- |
{ |
236 |
|
- |
pIssues.add( e ); |
237 |
|
- |
} |
238 |
|
- |
return null; |
239 |
|
- |
} |
240 |
|
- |
|
241 |
|
- |
private boolean canStart( Set<Throwable> pIssues, Activity pActivity, AcceptsOneWidget pPanel ) |
242 |
|
- |
{ |
243 |
|
- |
try |
244 |
|
- |
{ |
245 |
|
- |
pActivity.start( pPanel ); |
246 |
|
- |
return true; |
247 |
|
- |
} |
248 |
|
- |
catch ( Exception e ) |
249 |
|
- |
{ |
250 |
|
- |
pIssues.add( e ); |
251 |
|
- |
} |
252 |
|
- |
return false; |
253 |
|
- |
} |
254 |
|
- |
|
255 |
|
- |
private void tryCancel( Collection<Throwable> pIssues, Activity pActivity ) |
256 |
|
- |
{ |
257 |
|
- |
try |
258 |
|
- |
{ |
259 |
|
- |
pActivity.onCancel(); |
260 |
|
- |
} |
261 |
|
- |
catch ( Exception e ) |
262 |
|
- |
{ |
263 |
|
- |
pIssues.add( e ); |
264 |
|
- |
} |
265 |
|
- |
} |
266 |
|
- |
|
267 |
|
- |
private void tryStop( Collection<Throwable> pIssues, Activity pActivity ) |
268 |
|
- |
{ |
269 |
|
- |
try |
270 |
|
- |
{ |
271 |
|
- |
pActivity.onStop(); |
272 |
|
- |
} |
273 |
|
- |
catch ( Exception e ) |
274 |
|
- |
{ |
275 |
|
- |
pIssues.add( e ); |
276 |
|
- |
} |
277 |
|
- |
} |
278 |
|
- |
|
279 |
|
- |
private void possiblyUpdateActivityAndShowWidget( Set<Throwable> pIssues, Activity pActivity, IsWidget pWidget ) |
280 |
|
- |
{ |
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! |
290 |
|
- |
{ |
291 |
|
- |
return; |
292 |
|
- |
} |
293 |
|
- |
tryStop( pIssues, mCurrentActivity ); |
294 |
|
- |
mCurrentActivity = mPendingActivity; |
295 |
|
- |
mPendingActivity = null; |
296 |
|
- |
showWidget( pWidget ); |
297 |
|
- |
if ( !pIssues.isEmpty() ) |
298 |
|
- |
{ |
299 |
|
- |
throw new UmbrellaException( pIssues ); |
300 |
|
- |
} |
301 |
|
- |
} |
302 |
|
- |
|
303 |
|
- |
private void showWidget( IsWidget pWidget ) |
304 |
|
- |
{ |
305 |
|
- |
if ( mPanel != null ) |
306 |
|
- |
{ |
307 |
|
- |
mPanel.setWidget( pWidget ); |
|
163 |
+ |
resetURLbackTo( zCurrentPlace ); |
308 |
164 |
|
} |
309 |
165 |
|
} |
310 |
166 |
|
|
311 |
|
- |
@SuppressWarnings({"unchecked"}) |
312 |
|
- |
private void loadSyncFactory( final ActivityFactory.Asynchronous pAsyncFactory, final Place pPlace ) |
|
167 |
+ |
protected boolean userWantsToStopUrlChange( Place pNewPlace ) |
313 |
168 |
|
{ |
314 |
|
- |
pAsyncFactory.load( mCommonActivityParam, new ActivityFactory.Asynchronous.Callback<CommonActivityParam, IsWidget, Place>() |
315 |
|
- |
{ |
316 |
|
- |
@Override |
317 |
|
- |
public void loaded( ActivityFactory.Synchronous<CommonActivityParam, IsWidget, Place> pSynchronousFactory ) |
318 |
|
- |
{ |
319 |
|
- |
switchAsyncToSync( pPlace, pAsyncFactory, pSynchronousFactory ); |
320 |
|
- |
} |
321 |
|
- |
} ); |
|
169 |
+ |
return userRejectedLeavingCurrentPlace( pNewPlace ); |
322 |
170 |
|
} |
323 |
171 |
|
|
324 |
|
- |
private void switchAsyncToSync( Place pPlace, ActivityFactory.Asynchronous pAsyncFactory, ActivityFactory.Synchronous pSyncFactory ) |
|
172 |
+ |
protected void resetURLbackTo( Place pCurrentPlace ) |
325 |
173 |
|
{ |
326 |
|
- |
boolean zStillGoing = pPlace.equals( mPendingAsyncPlace ); |
327 |
|
- |
mPendingAsyncPlace = null; |
328 |
|
- |
if ( replace( pAsyncFactory, pSyncFactory ) && zStillGoing ) |
329 |
|
- |
{ |
330 |
|
- |
activate( pPlace, pSyncFactory ); |
331 |
|
- |
} |
332 |
|
- |
} |
333 |
|
- |
|
334 |
|
- |
protected ActivityFactory get( Place pPlace ) |
335 |
|
- |
{ |
336 |
|
- |
return get( PlaceIdExtractor.getPlaceId( pPlace ) ); |
|
174 |
+ |
mHistorian.newItem( mMapper.getToken( pCurrentPlace ), false ); // reset the URL back to the Current Place |
337 |
175 |
|
} |
338 |
176 |
|
|
339 |
177 |
|
protected boolean userRejectedLeavingCurrentPlace( Place newPlace ) |
|
@@ -344,13 +182,19 @@ |
344 |
182 |
|
|
345 |
183 |
|
protected String getLeavingCurrentPlaceWarningText() |
346 |
184 |
|
{ |
347 |
|
- |
return mCurrentActivity.mayStop(); |
|
185 |
+ |
return mActivityHelper.getCurrentActivityMayStop(); |
|
186 |
+ |
} |
|
187 |
+ |
|
|
188 |
+ |
@SuppressWarnings({"unchecked"}) |
|
189 |
+ |
protected CommonActivityParam createDefaultCommonActivityParam() |
|
190 |
+ |
{ |
|
191 |
+ |
return (CommonActivityParam) new DefaultCommonActivityParam(); |
348 |
192 |
|
} |
349 |
193 |
|
|
350 |
194 |
|
/** |
351 |
|
- |
* Default implementation of CommonActivityParam, based on {@link Window}. |
|
195 |
+ |
* Default implementation of CommonActivityParameter, based on {@link Window}. |
352 |
196 |
|
*/ |
353 |
|
- |
private static class DefaultCommonActivityParam implements MessageUserSinkAccessor |
|
197 |
+ |
private class DefaultCommonActivityParam implements CommonActivityParameter |
354 |
198 |
|
{ |
355 |
199 |
|
@Override |
356 |
200 |
|
public MessageUserSink getMessageUserSink() |
|
@@ -358,32 +202,16 @@ |
358 |
202 |
|
return DialogMessageUserSink.INSTANCE; |
359 |
203 |
|
} |
360 |
204 |
|
|
361 |
|
- |
@SuppressWarnings({"unchecked"}) |
362 |
|
- |
public static <CommonActivityParam extends MessageUserSinkAccessor> CommonActivityParam createInstance() |
363 |
|
- |
{ |
364 |
|
- |
return (CommonActivityParam) new DefaultCommonActivityParam(); |
365 |
|
- |
} |
366 |
|
- |
} |
367 |
|
- |
|
368 |
|
- |
/** |
369 |
|
- |
* Wraps our real display to prevent an Activity from taking it over if it is |
370 |
|
- |
* not the currentActivity. |
371 |
|
- |
*/ |
372 |
|
- |
private class ProtectedDisplay implements AcceptsOneWidget |
373 |
|
- |
{ |
374 |
|
- |
private final Set<Throwable> mIssues; |
375 |
|
- |
private final Activity mExpectedActivity; |
376 |
|
- |
|
377 |
|
- |
ProtectedDisplay( Set<Throwable> pIssues, Activity pExpectedActivity ) |
|
205 |
+ |
@Override |
|
206 |
+ |
public Place getWhere() |
378 |
207 |
|
{ |
379 |
|
- |
mIssues = pIssues; |
380 |
|
- |
mExpectedActivity = pExpectedActivity; |
|
208 |
+ |
return PlaceActivityManager.this.getWhere(); |
381 |
209 |
|
} |
382 |
210 |
|
|
383 |
211 |
|
@Override |
384 |
|
- |
public void setWidget( IsWidget pWidget ) |
|
212 |
+ |
public GoToPlace goTo( Place pNewPlace ) |
385 |
213 |
|
{ |
386 |
|
- |
possiblyUpdateActivityAndShowWidget( mIssues, mExpectedActivity, pWidget ); |
|
214 |
+ |
return PlaceActivityManager.this.goTo( pNewPlace ); |
387 |
215 |
|
} |
388 |
216 |
|
} |
389 |
217 |
|
} |