Subversion Repository Public Repository

litesoft

Diff Revisions 949 vs 950 for /trunk/Java/GWT/Client/src/org/litesoft/GWT/client/widgets/nonpublic/InjectionPointSelectorController.java

Diff revisions: vs.
  @@ -1,506 +1,505 @@
1 - // This Source Code is in the Public Domain per: http://unlicense.org
2 - package org.litesoft.GWT.client.widgets.nonpublic;
3 -
4 - import org.litesoft.commonfoundation.base.*;
5 - import org.litesoft.commonfoundation.typeutils.Objects;
6 - import org.litesoft.commonfoundation.typeutils.*;
7 - import org.litesoft.core.simpletypes.*;
8 -
9 - import java.util.*;
10 -
11 - import static org.litesoft.core.simpletypes.nonpublic.EqualSupport.*;
12 -
13 - public class InjectionPointSelectorController<T extends Synopsisable> {
14 - public interface View<U extends Synopsisable> {
15 - public void injectBetween( U pLowerBound, U pUpperBound );
16 -
17 - public void show( DrillDownSet pDrillDownSet );
18 -
19 - public void notEnoughVisibleEntries();
20 - }
21 -
22 - public static abstract class ViewEntry<U extends Synopsisable> {
23 - private String mText;
24 -
25 - protected ViewEntry( String pText ) {
26 - mText = Strings.assertNotNullNotEmpty( "Text", pText );
27 - }
28 -
29 - public U getUpperReference() {
30 - return null;
31 - }
32 -
33 - public U getLowerReference() {
34 - return null;
35 - }
36 -
37 - /**
38 - * If Selectable, then is NOT a reference, but one of: Between, There, Top, or Bottom
39 - */
40 - public boolean isSelectable() {
41 - return false;
42 - }
43 -
44 - public boolean isBetween() {
45 - return false;
46 - }
47 -
48 - public boolean isSelected() {
49 - return false;
50 - }
51 -
52 - public final String getText() {
53 - return mText;
54 - }
55 -
56 - @Override
57 - public boolean equals( Object o ) {
58 - if ( this == o ) {
59 - return true;
60 - }
61 - if ( (o == null) || !this.getClass().getName().equals( o.getClass().getName() ) ) {
62 - return false;
63 - }
64 - ViewEntry them = (ViewEntry) o;
65 - return Objects.areEqual( this.getText(), them.getText() ) && //
66 - Objects.areEqual( this.getUpperReference(), them.getUpperReference() ) && //
67 - Objects.areEqual( this.getLowerReference(), them.getLowerReference() );
68 - }
69 -
70 - @Override
71 - public int hashCode() {
72 - return hashCodeEm( calcHashCode( getText() ), //
73 - calcHashCode( getUpperReference() ), //
74 - calcHashCode( getLowerReference() ) );
75 - }
76 - }
77 -
78 - public static class ViewSet {
79 - private List<ViewEntry> mEntries;
80 -
81 - public ViewSet( List<ViewEntry> pEntries ) {
82 - mEntries = pEntries;
83 - }
84 -
85 - public ViewSet( ViewEntry... pEntries ) {
86 - mEntries = Arrays.asList( pEntries );
87 - }
88 -
89 - public ViewEntry[] getEntries() {
90 - return mEntries.toArray( new ViewEntry[mEntries.size()] );
91 - }
92 -
93 - @Override
94 - public String toString() {
95 - return mEntries.toString();
96 - }
97 -
98 - @Override
99 - public boolean equals( Object o ) {
100 - if ( this == o ) {
101 - return true;
102 - }
103 - if ( (o == null) || !this.getClass().getName().equals( o.getClass().getName() ) ) {
104 - return false;
105 - }
106 - ViewSet them = (ViewSet) o;
107 - return Objects.areEqual( this.mEntries, them.mEntries );
108 - }
109 -
110 - @Override
111 - public int hashCode() {
112 - return calcHashCode( mEntries );
113 - }
114 - }
115 -
116 - public static class DrillDownSet {
117 - private ViewSet mHomeSet;
118 - private boolean mSomethingBetweenHomeAndPreviousSets;
119 - private ViewSet mPreviousSet;
120 - private ViewSet mCurrentSet;
121 -
122 - public DrillDownSet( ViewSet pHomeSet, boolean pSomethingBetweenHomeAndPreviousSets, ViewSet pPreviousSet, ViewSet pCurrentSet ) {
123 - mHomeSet = pHomeSet;
124 - mSomethingBetweenHomeAndPreviousSets = pSomethingBetweenHomeAndPreviousSets;
125 - mPreviousSet = pPreviousSet;
126 - mCurrentSet = pCurrentSet;
127 - }
128 -
129 - public ViewSet getHomeSet() {
130 - return mHomeSet;
131 - }
132 -
133 - public boolean isSomethingBetweenHomeAndPreviousSets() {
134 - return mSomethingBetweenHomeAndPreviousSets;
135 - }
136 -
137 - public ViewSet getPreviousSet() {
138 - return mPreviousSet;
139 - }
140 -
141 - public ViewSet getCurrentSet() {
142 - return mCurrentSet;
143 - }
144 -
145 - @Override
146 - public String toString() {
147 - StringBuilder sb = new StringBuilder( "DrillDownSet:" );
148 - add( sb, "Home", mHomeSet );
149 - if ( mSomethingBetweenHomeAndPreviousSets ) {
150 - sb.append( "\n ..." );
151 - }
152 - add( sb, "Prev", mPreviousSet );
153 - add( sb, "Curr", mCurrentSet );
154 - return sb.toString();
155 - }
156 -
157 - private void add( StringBuilder pSb, String pWhat, ViewSet pSet ) {
158 - if ( pSet != null ) {
159 - pSb.append( '\n' ).append( pWhat ).append( ": " ).append( pSet );
160 - }
161 - }
162 -
163 - @Override
164 - public boolean equals( Object o ) {
165 - if ( this == o ) {
166 - return true;
167 - }
168 - if ( (o == null) || !this.getClass().getName().equals( o.getClass().getName() ) ) {
169 - return false;
170 - }
171 - DrillDownSet them = (DrillDownSet) o;
172 - return (this.mSomethingBetweenHomeAndPreviousSets == them.mSomethingBetweenHomeAndPreviousSets) && //
173 - Objects.areEqual( this.mHomeSet, them.mHomeSet ) && //
174 - Objects.areEqual( this.mPreviousSet, them.mPreviousSet ) && //
175 - Objects.areEqual( this.mCurrentSet, them.mCurrentSet );
176 - }
177 -
178 - @Override
179 - public int hashCode() {
180 - return hashCodeEm( calcHashCode( mHomeSet ), //
181 - calcHashCode( mPreviousSet ), //
182 - calcHashCode( mCurrentSet ) );
183 - }
184 - }
185 -
186 - static abstract class SelectionPointEntry<U extends Synopsisable> extends ViewEntry<U> {
187 - private boolean mSelected;
188 - private U mUpperReference;
189 - private U mLowerReference;
190 -
191 - protected SelectionPointEntry( boolean pSelected, String pText, U pUpperReference, U pLowerReference ) {
192 - super( pText );
193 - mSelected = pSelected;
194 - mUpperReference = pUpperReference;
195 - mLowerReference = pLowerReference;
196 - }
197 -
198 - @Override
199 - public U getUpperReference() {
200 - return mUpperReference;
201 - }
202 -
203 - @Override
204 - public U getLowerReference() {
205 - return mLowerReference;
206 - }
207 -
208 - @Override
209 - public boolean isSelectable() {
210 - return true;
211 - }
212 -
213 - @Override
214 - public boolean isSelected() {
215 - return mSelected;
216 - }
217 -
218 - @Override
219 - public String toString() {
220 - return Strings.replace( getText(), '\n', "\\n" ) + ":" + addXtra();
221 - }
222 -
223 - protected String addXtra() {
224 - return "";
225 - }
226 - }
227 -
228 - @SuppressWarnings("EqualsAndHashcode")
229 - static class Between<U extends Synopsisable> extends SelectionPointEntry<U> {
230 - private Between( boolean pSelected, U pUpperReference, U pLowerReference ) {
231 - super( pSelected, "Between", pUpperReference, pLowerReference );
232 - }
233 -
234 - public static <V extends Synopsisable> Between<V> create( boolean pSelected, V pUpperReference, V pLowerReference ) {
235 - return new Between<V>( pSelected, pUpperReference, pLowerReference );
236 - }
237 -
238 - public Between<U> copyAsSelected() {
239 - return isSelected() ? this : create( true, getUpperReference(), getLowerReference() );
240 - }
241 -
242 - @Override
243 - public boolean isBetween() {
244 - return true;
245 - }
246 -
247 - protected String addXtra() {
248 - return (isSelected() ? "(?Y)" : "(?N)");
249 - }
250 -
251 - @Override
252 - @SuppressWarnings({"EqualsWhichDoesntCheckParameterClass"})
253 - public boolean equals( Object o ) {
254 - return super.equals( o ) && (this.isSelected() == ((Between) o).isSelected());
255 - }
256 - }
257 -
258 - static class There<U extends Synopsisable> extends SelectionPointEntry<U> {
259 - private There( U pUpperReference, U pLowerReference ) {
260 - super( false, "There", pUpperReference, pLowerReference );
261 - }
262 -
263 - public static <V extends Synopsisable> There<V> create( V pUpperReference, V pLowerReference ) {
264 - return new There<V>( pUpperReference, pLowerReference );
265 - }
266 - }
267 -
268 - static class Top<U extends Synopsisable> extends SelectionPointEntry<U> {
269 - private Top( U pLowerReference ) {
270 - super( false, "Top\n(There)", null, pLowerReference );
271 - }
272 -
273 - public static <V extends Synopsisable> Top<V> create( V pLowerReference ) {
274 - return new Top<V>( pLowerReference );
275 - }
276 - }
277 -
278 - static class Bottom<U extends Synopsisable> extends SelectionPointEntry<U> {
279 - private Bottom( U pUpperReference ) {
280 - super( false, "Bottom\n(There)", pUpperReference, null );
281 - }
282 -
283 - public static <V extends Synopsisable> Bottom<V> create( V pUpperReference ) {
284 - return new Bottom<V>( pUpperReference );
285 - }
286 - }
287 -
288 - static class Reference<U extends Synopsisable> extends ViewEntry<U> {
289 - private Reference( int pLines, U pReference ) {
290 - super( pReference.getSynopsis( pLines ) );
291 - }
292 -
293 - public static <V extends Synopsisable> Reference<V> create( int pLines, V pReference ) {
294 - return new Reference<V>( pLines, pReference );
295 - }
296 -
297 - @Override
298 - public String toString() {
299 - return "Ref: " + Strings.replace( getText(), '\n', "\\n" );
300 - }
301 - }
302 -
303 - private int mSynopsisLines;
304 - private List<T> mInjectIns;
305 - private View<T> mView;
306 - private int mCurrentEntriesVisible = -1;
307 - private List<Between<T>> mSelectedBetweens = new ArrayList<Between<T>>();
308 -
309 - public InjectionPointSelectorController( int pSynopsisLines, List<T> pInjectIns, View<T> pView ) {
310 - mSynopsisLines = pSynopsisLines;
311 - mInjectIns = pInjectIns;
312 - mView = pView;
313 - }
314 -
315 - public InjectionPointSelectorController( int pSynopsisLines, View<T> pView, T... pInjectIns ) {
316 - this( pSynopsisLines, Arrays.asList( pInjectIns ), pView );
317 - }
318 -
319 - public void updateForEntriesVisible( int pCount ) {
320 - if ( mCurrentEntriesVisible != pCount ) {
321 - DrillDownSet zDrillDownSet = updateDrillDownSetWhenEntriesVisibleChanges( pCount, mSelectedBetweens );
322 - if ( zDrillDownSet == null ) {
323 - mView.notEnoughVisibleEntries();
324 - return;
325 - }
326 - mCurrentEntriesVisible = pCount;
327 - mView.show( zDrillDownSet );
328 - }
329 - }
330 -
331 - public void homeSelected() {
332 - DrillDownSet zDrillDownSet = updateDrillDownSetWhenEntriesVisibleChanges( mCurrentEntriesVisible, mSelectedBetweens );
333 - mView.show( zDrillDownSet );
334 - }
335 -
336 - public void previousSelected() {
337 - mSelectedBetweens.remove( mSelectedBetweens.size() - 1 );
338 - DrillDownSet zDrillDownSet = updateDrillDownSetWhenSelectedBetweensChange( mCurrentEntriesVisible, mSelectedBetweens );
339 - mView.show( zDrillDownSet );
340 - }
341 -
342 - public void viewSelected( ViewEntry<T> pEntry ) {
343 - Objects.assertNotNull( "Entry", pEntry );
344 - if ( pEntry.isBetween() ) {
345 - mSelectedBetweens.add( ((Between<T>) pEntry).copyAsSelected() );
346 - DrillDownSet zDrillDownSet = updateDrillDownSetWhenSelectedBetweensChange( mCurrentEntriesVisible, mSelectedBetweens );
347 - mView.show( zDrillDownSet );
348 - return;
349 - }
350 - if ( pEntry.isSelectable() ) {
351 - mView.injectBetween( pEntry.getLowerReference(), pEntry.getUpperReference() );
352 - return;
353 - }
354 - throw new IllegalArgumentException( "Can not 'viewSelected' for type '" + ClassName.simple( pEntry ) + "': " + pEntry );
355 - }
356 -
357 - /**
358 - * Update the pLastDrillDownSet based on the new pCount (maximum Entries per ViewSet) (public for Testing)
359 - *
360 - * @return null means that pCount was too small!
361 - */
362 - public DrillDownSet updateDrillDownSetWhenEntriesVisibleChanges( int pCount, List<Between<T>> pSelectedBetweens ) {
363 - if ( -1 == (pCount = checkIfNotEnoughVisibleEntries( makeOdd( pCount ) )) ) {
364 - return null;
365 - }
366 - pSelectedBetweens.clear();
367 -
368 - List<ViewSet> zViewSets = buildViewSets( pCount, pSelectedBetweens );
369 - return new DrillDownSet( null, false, null, zViewSets.get( 0 ) );
370 - }
371 -
372 - /**
373 - * Update the pLastDrillDownSet based on the new pCount (maximum Entries per ViewSet) (public for Testing)
374 - *
375 - * @return null means that pCount was too small!
376 - */
377 - public DrillDownSet updateDrillDownSetWhenSelectedBetweensChange( int pCount, List<Between<T>> pSelectedBetweens ) {
378 - List<ViewSet> zViewSets = buildViewSets( makeOdd( pCount ), pSelectedBetweens );
379 - switch ( zViewSets.size() ) {
380 - case 1:
381 - return new DrillDownSet( null, false, null, zViewSets.get( 0 ) );
382 - case 2:
383 - return new DrillDownSet( zViewSets.get( 0 ), false, null, zViewSets.get( 1 ) );
384 - case 3:
385 - return new DrillDownSet( zViewSets.get( 0 ), false, zViewSets.get( 1 ), zViewSets.get( 2 ) );
386 - default:
387 - return new DrillDownSet( zViewSets.get( 0 ), true, zViewSets.get( zViewSets.size() - 2 ), zViewSets.get( zViewSets.size() - 1 ) );
388 - }
389 - }
390 -
391 - private int checkIfNotEnoughVisibleEntries( int pCount ) {
392 - return ((pCount >= 5) || // 5 is enough for anything (more is better)
393 - ((pCount == 3) && (mInjectIns.size() == 1))) ? // Special Case of just 1 InjectIn?
394 - pCount : // pCount is OK
395 - -1; // indicate pCount too small
396 - }
397 -
398 - private int makeOdd( int pCount ) {
399 - return ((pCount & 1) == 0) ? // Is Even?
400 - pCount - 1 : // Even, make Odd!
401 - pCount; // Already Odd
402 - }
403 -
404 - private Between<T> getCurrentSelectedBetween( List<Between<T>> pSelectedBetweens, int pCurrentIndex ) {
405 - return (pCurrentIndex < pSelectedBetweens.size()) ? pSelectedBetweens.get( pCurrentIndex ) : null;
406 - }
407 -
408 - private boolean isSelected( Between<T> pSelectedBetween, T pLast, T pCurr ) {
409 - return (pSelectedBetween != null) && (pLast == pSelectedBetween.getUpperReference()) && (pCurr == pSelectedBetween.getLowerReference());
410 - }
411 -
412 - private List<ViewSet> buildViewSets( int pCount, List<Between<T>> pSelectedBetweens ) {
413 - List<ViewSet> zViewSets = new ArrayList<ViewSet>();
414 -
415 - buildHomeViewSet( zViewSets, pCount, pSelectedBetweens );
416 -
417 - return zViewSets;
418 - }
419 -
420 - private void buildHomeViewSet( List<ViewSet> pViewSets, int pCount, List<Between<T>> pSelectedBetweens ) {
421 - Between<T> zSelectedBetween = getCurrentSelectedBetween( pSelectedBetweens, 0 );
422 -
423 - int zInjectInsToAdd = mInjectIns.size() - 1;
424 - int zAvailableBetweenBuckets = Math.min( zInjectInsToAdd, (pCount - 3) / 2 );
425 -
426 - // System.out.println( "buildHomeViewSett( " + zSelectedBetween + " ): " + //
427 - // pCount + " ( " + mInjectIns.size() + " ) " + zInjectInsToAdd + " | " + zAvailableBetweenBuckets );
428 -
429 - ArrayList<ViewEntry> zEntries = new ArrayList<ViewEntry>( pCount );
430 - pViewSets.add( new ViewSet( zEntries ) );
431 -
432 - int zCurrNdx = 0;
433 - int zThru = mInjectIns.size() - 1;
434 - T zCurrT = mInjectIns.get( zCurrNdx );
435 - zEntries.add( Top.create( zCurrT ) );
436 -
437 - while ( zCurrNdx < zThru ) {
438 - zEntries.add( Reference.create( mSynopsisLines, zCurrT ) );
439 - int zLastNdx = zCurrNdx;
440 - T zLastT = zCurrT;
441 - int zHowMany = zInjectInsToAdd / zAvailableBetweenBuckets;
442 - // System.out.print( " zInjectInsToAdd / zAvailableBetweenBuckets ==> " + zInjectInsToAdd + " / " + zAvailableBetweenBuckets + " = " + zHowMany );
443 - zInjectInsToAdd -= zHowMany;
444 - zCurrNdx += zHowMany;
445 - zCurrT = mInjectIns.get( zCurrNdx );
446 - if ( zHowMany == 1 ) {
447 - // System.out.println( " Add There 1" );
448 - zEntries.add( There.create( zLastT, zCurrT ) );
449 - } else {
450 - // System.out.println( " Add Between " + zHowMany );
451 - if ( isSelected( zSelectedBetween, zLastT, zCurrT ) ) {
452 - zEntries.add( zSelectedBetween );
453 - buildSubsequentViewSet( pViewSets, pCount, zLastNdx, zCurrNdx, pSelectedBetweens, 1 );
454 - } else {
455 - zEntries.add( Between.create( false, zLastT, zCurrT ) );
456 - }
457 - }
458 - zAvailableBetweenBuckets--;
459 - }
460 - zEntries.add( Reference.create( mSynopsisLines, zCurrT ) );
461 - zEntries.add( Bottom.create( zCurrT ) );
462 - }
463 -
464 - private void buildSubsequentViewSet( List<ViewSet> pViewSets, int pCount, int zUpperNdx, int zLowerNdx, List<Between<T>> pSelectedBetweens,
465 - int pCurrentIndex ) {
466 - Between<T> zSelectedBetween = getCurrentSelectedBetween( pSelectedBetweens, pCurrentIndex );
467 -
468 - int zInjectInsToAdd = zLowerNdx - zUpperNdx;
469 - int zAvailableBetweenBuckets = Math.min( zInjectInsToAdd, pCount / 2 );
470 -
471 - // System.out.println( " buildSubsequentViewSet( " + pCurrentIndex + " => " + zSelectedBetween + " ): " + //
472 - // pCount + " ( " + zUpperNdx + ", " + zLowerNdx + " ) " + zInjectInsToAdd + " | " + zAvailableBetweenBuckets );
473 -
474 - ArrayList<ViewEntry> zEntries = new ArrayList<ViewEntry>( pCount );
475 - pViewSets.add( new ViewSet( zEntries ) );
476 -
477 - int zCurrNdx = zUpperNdx;
478 - int zThru = zLowerNdx;
479 - T zCurrT = mInjectIns.get( zCurrNdx );
480 -
481 - while ( zCurrNdx < zThru ) {
482 - zEntries.add( Reference.create( mSynopsisLines, zCurrT ) );
483 - int zLastNdx = zCurrNdx;
484 - T zLastT = zCurrT;
485 - int zHowMany = zInjectInsToAdd / zAvailableBetweenBuckets;
486 - // System.out.print( " zInjectInsToAdd / zAvailableBetweenBuckets ==> " + zInjectInsToAdd + " / " + zAvailableBetweenBuckets + " = " + zHowMany );
487 - zInjectInsToAdd -= zHowMany;
488 - zCurrNdx += zHowMany;
489 - zCurrT = mInjectIns.get( zCurrNdx );
490 - if ( zHowMany == 1 ) {
491 - // System.out.println( " Add There 1" );
492 - zEntries.add( There.create( zLastT, zCurrT ) );
493 - } else {
494 - // System.out.println( " Add Between " + zHowMany );
495 - if ( isSelected( zSelectedBetween, zLastT, zCurrT ) ) {
496 - zEntries.add( zSelectedBetween );
497 - buildSubsequentViewSet( pViewSets, pCount, zLastNdx, zCurrNdx, pSelectedBetweens, pCurrentIndex + 1 );
498 - } else {
499 - zEntries.add( Between.create( false, zLastT, zCurrT ) );
500 - }
501 - }
502 - zAvailableBetweenBuckets--;
503 - }
504 - zEntries.add( Reference.create( mSynopsisLines, zCurrT ) );
505 - }
506 - }
1 + // This Source Code is in the Public Domain per: http://unlicense.org
2 + package org.litesoft.GWT.client.widgets.nonpublic;
3 +
4 + import org.litesoft.commonfoundation.base.*;
5 + import org.litesoft.commonfoundation.typeutils.*;
6 + import org.litesoft.core.simpletypes.*;
7 +
8 + import java.util.*;
9 +
10 + import static org.litesoft.core.simpletypes.nonpublic.EqualSupport.*;
11 +
12 + public class InjectionPointSelectorController<T extends Synopsisable> {
13 + public interface View<U extends Synopsisable> {
14 + public void injectBetween( U pLowerBound, U pUpperBound );
15 +
16 + public void show( DrillDownSet pDrillDownSet );
17 +
18 + public void notEnoughVisibleEntries();
19 + }
20 +
21 + public static abstract class ViewEntry<U extends Synopsisable> {
22 + private String mText;
23 +
24 + protected ViewEntry( String pText ) {
25 + mText = Confirm.significant( "Text", pText );
26 + }
27 +
28 + public U getUpperReference() {
29 + return null;
30 + }
31 +
32 + public U getLowerReference() {
33 + return null;
34 + }
35 +
36 + /**
37 + * If Selectable, then is NOT a reference, but one of: Between, There, Top, or Bottom
38 + */
39 + public boolean isSelectable() {
40 + return false;
41 + }
42 +
43 + public boolean isBetween() {
44 + return false;
45 + }
46 +
47 + public boolean isSelected() {
48 + return false;
49 + }
50 +
51 + public final String getText() {
52 + return mText;
53 + }
54 +
55 + @Override
56 + public boolean equals( Object o ) {
57 + if ( this == o ) {
58 + return true;
59 + }
60 + if ( (o == null) || !this.getClass().getName().equals( o.getClass().getName() ) ) {
61 + return false;
62 + }
63 + ViewEntry them = (ViewEntry) o;
64 + return Currently.areEqual( this.getText(), them.getText() ) && //
65 + Currently.areEqual( this.getUpperReference(), them.getUpperReference() ) && //
66 + Currently.areEqual( this.getLowerReference(), them.getLowerReference() );
67 + }
68 +
69 + @Override
70 + public int hashCode() {
71 + return hashCodeEm( calcHashCode( getText() ), //
72 + calcHashCode( getUpperReference() ), //
73 + calcHashCode( getLowerReference() ) );
74 + }
75 + }
76 +
77 + public static class ViewSet {
78 + private List<ViewEntry> mEntries;
79 +
80 + public ViewSet( List<ViewEntry> pEntries ) {
81 + mEntries = pEntries;
82 + }
83 +
84 + public ViewSet( ViewEntry... pEntries ) {
85 + mEntries = Arrays.asList( pEntries );
86 + }
87 +
88 + public ViewEntry[] getEntries() {
89 + return mEntries.toArray( new ViewEntry[mEntries.size()] );
90 + }
91 +
92 + @Override
93 + public String toString() {
94 + return mEntries.toString();
95 + }
96 +
97 + @Override
98 + public boolean equals( Object o ) {
99 + if ( this == o ) {
100 + return true;
101 + }
102 + if ( (o == null) || !this.getClass().getName().equals( o.getClass().getName() ) ) {
103 + return false;
104 + }
105 + ViewSet them = (ViewSet) o;
106 + return Currently.areEqual( this.mEntries, them.mEntries );
107 + }
108 +
109 + @Override
110 + public int hashCode() {
111 + return calcHashCode( mEntries );
112 + }
113 + }
114 +
115 + public static class DrillDownSet {
116 + private ViewSet mHomeSet;
117 + private boolean mSomethingBetweenHomeAndPreviousSets;
118 + private ViewSet mPreviousSet;
119 + private ViewSet mCurrentSet;
120 +
121 + public DrillDownSet( ViewSet pHomeSet, boolean pSomethingBetweenHomeAndPreviousSets, ViewSet pPreviousSet, ViewSet pCurrentSet ) {
122 + mHomeSet = pHomeSet;
123 + mSomethingBetweenHomeAndPreviousSets = pSomethingBetweenHomeAndPreviousSets;
124 + mPreviousSet = pPreviousSet;
125 + mCurrentSet = pCurrentSet;
126 + }
127 +
128 + public ViewSet getHomeSet() {
129 + return mHomeSet;
130 + }
131 +
132 + public boolean isSomethingBetweenHomeAndPreviousSets() {
133 + return mSomethingBetweenHomeAndPreviousSets;
134 + }
135 +
136 + public ViewSet getPreviousSet() {
137 + return mPreviousSet;
138 + }
139 +
140 + public ViewSet getCurrentSet() {
141 + return mCurrentSet;
142 + }
143 +
144 + @Override
145 + public String toString() {
146 + StringBuilder sb = new StringBuilder( "DrillDownSet:" );
147 + add( sb, "Home", mHomeSet );
148 + if ( mSomethingBetweenHomeAndPreviousSets ) {
149 + sb.append( "\n ..." );
150 + }
151 + add( sb, "Prev", mPreviousSet );
152 + add( sb, "Curr", mCurrentSet );
153 + return sb.toString();
154 + }
155 +
156 + private void add( StringBuilder pSb, String pWhat, ViewSet pSet ) {
157 + if ( pSet != null ) {
158 + pSb.append( '\n' ).append( pWhat ).append( ": " ).append( pSet );
159 + }
160 + }
161 +
162 + @Override
163 + public boolean equals( Object o ) {
164 + if ( this == o ) {
165 + return true;
166 + }
167 + if ( (o == null) || !this.getClass().getName().equals( o.getClass().getName() ) ) {
168 + return false;
169 + }
170 + DrillDownSet them = (DrillDownSet) o;
171 + return (this.mSomethingBetweenHomeAndPreviousSets == them.mSomethingBetweenHomeAndPreviousSets) && //
172 + Currently.areEqual( this.mHomeSet, them.mHomeSet ) && //
173 + Currently.areEqual( this.mPreviousSet, them.mPreviousSet ) && //
174 + Currently.areEqual( this.mCurrentSet, them.mCurrentSet );
175 + }
176 +
177 + @Override
178 + public int hashCode() {
179 + return hashCodeEm( calcHashCode( mHomeSet ), //
180 + calcHashCode( mPreviousSet ), //
181 + calcHashCode( mCurrentSet ) );
182 + }
183 + }
184 +
185 + static abstract class SelectionPointEntry<U extends Synopsisable> extends ViewEntry<U> {
186 + private boolean mSelected;
187 + private U mUpperReference;
188 + private U mLowerReference;
189 +
190 + protected SelectionPointEntry( boolean pSelected, String pText, U pUpperReference, U pLowerReference ) {
191 + super( pText );
192 + mSelected = pSelected;
193 + mUpperReference = pUpperReference;
194 + mLowerReference = pLowerReference;
195 + }
196 +
197 + @Override
198 + public U getUpperReference() {
199 + return mUpperReference;
200 + }
201 +
202 + @Override
203 + public U getLowerReference() {
204 + return mLowerReference;
205 + }
206 +
207 + @Override
208 + public boolean isSelectable() {
209 + return true;
210 + }
211 +
212 + @Override
213 + public boolean isSelected() {
214 + return mSelected;
215 + }
216 +
217 + @Override
218 + public String toString() {
219 + return Strings.replace( getText(), '\n', "\\n" ) + ":" + addXtra();
220 + }
221 +
222 + protected String addXtra() {
223 + return "";
224 + }
225 + }
226 +
227 + @SuppressWarnings("EqualsAndHashcode")
228 + static class Between<U extends Synopsisable> extends SelectionPointEntry<U> {
229 + private Between( boolean pSelected, U pUpperReference, U pLowerReference ) {
230 + super( pSelected, "Between", pUpperReference, pLowerReference );
231 + }
232 +
233 + public static <V extends Synopsisable> Between<V> create( boolean pSelected, V pUpperReference, V pLowerReference ) {
234 + return new Between<V>( pSelected, pUpperReference, pLowerReference );
235 + }
236 +
237 + public Between<U> copyAsSelected() {
238 + return isSelected() ? this : create( true, getUpperReference(), getLowerReference() );
239 + }
240 +
241 + @Override
242 + public boolean isBetween() {
243 + return true;
244 + }
245 +
246 + protected String addXtra() {
247 + return (isSelected() ? "(?Y)" : "(?N)");
248 + }
249 +
250 + @Override
251 + @SuppressWarnings({"EqualsWhichDoesntCheckParameterClass"})
252 + public boolean equals( Object o ) {
253 + return super.equals( o ) && (this.isSelected() == ((Between) o).isSelected());
254 + }
255 + }
256 +
257 + static class There<U extends Synopsisable> extends SelectionPointEntry<U> {
258 + private There( U pUpperReference, U pLowerReference ) {
259 + super( false, "There", pUpperReference, pLowerReference );
260 + }
261 +
262 + public static <V extends Synopsisable> There<V> create( V pUpperReference, V pLowerReference ) {
263 + return new There<V>( pUpperReference, pLowerReference );
264 + }
265 + }
266 +
267 + static class Top<U extends Synopsisable> extends SelectionPointEntry<U> {
268 + private Top( U pLowerReference ) {
269 + super( false, "Top\n(There)", null, pLowerReference );
270 + }
271 +
272 + public static <V extends Synopsisable> Top<V> create( V pLowerReference ) {
273 + return new Top<V>( pLowerReference );
274 + }
275 + }
276 +
277 + static class Bottom<U extends Synopsisable> extends SelectionPointEntry<U> {
278 + private Bottom( U pUpperReference ) {
279 + super( false, "Bottom\n(There)", pUpperReference, null );
280 + }
281 +
282 + public static <V extends Synopsisable> Bottom<V> create( V pUpperReference ) {
283 + return new Bottom<V>( pUpperReference );
284 + }
285 + }
286 +
287 + static class Reference<U extends Synopsisable> extends ViewEntry<U> {
288 + private Reference( int pLines, U pReference ) {
289 + super( pReference.getSynopsis( pLines ) );
290 + }
291 +
292 + public static <V extends Synopsisable> Reference<V> create( int pLines, V pReference ) {
293 + return new Reference<V>( pLines, pReference );
294 + }
295 +
296 + @Override
297 + public String toString() {
298 + return "Ref: " + Strings.replace( getText(), '\n', "\\n" );
299 + }
300 + }
301 +
302 + private int mSynopsisLines;
303 + private List<T> mInjectIns;
304 + private View<T> mView;
305 + private int mCurrentEntriesVisible = -1;
306 + private List<Between<T>> mSelectedBetweens = new ArrayList<Between<T>>();
307 +
308 + public InjectionPointSelectorController( int pSynopsisLines, List<T> pInjectIns, View<T> pView ) {
309 + mSynopsisLines = pSynopsisLines;
310 + mInjectIns = pInjectIns;
311 + mView = pView;
312 + }
313 +
314 + public InjectionPointSelectorController( int pSynopsisLines, View<T> pView, T... pInjectIns ) {
315 + this( pSynopsisLines, Arrays.asList( pInjectIns ), pView );
316 + }
317 +
318 + public void updateForEntriesVisible( int pCount ) {
319 + if ( mCurrentEntriesVisible != pCount ) {
320 + DrillDownSet zDrillDownSet = updateDrillDownSetWhenEntriesVisibleChanges( pCount, mSelectedBetweens );
321 + if ( zDrillDownSet == null ) {
322 + mView.notEnoughVisibleEntries();
323 + return;
324 + }
325 + mCurrentEntriesVisible = pCount;
326 + mView.show( zDrillDownSet );
327 + }
328 + }
329 +
330 + public void homeSelected() {
331 + DrillDownSet zDrillDownSet = updateDrillDownSetWhenEntriesVisibleChanges( mCurrentEntriesVisible, mSelectedBetweens );
332 + mView.show( zDrillDownSet );
333 + }
334 +
335 + public void previousSelected() {
336 + mSelectedBetweens.remove( mSelectedBetweens.size() - 1 );
337 + DrillDownSet zDrillDownSet = updateDrillDownSetWhenSelectedBetweensChange( mCurrentEntriesVisible, mSelectedBetweens );
338 + mView.show( zDrillDownSet );
339 + }
340 +
341 + public void viewSelected( ViewEntry<T> pEntry ) {
342 + Confirm.isNotNull( "Entry", pEntry );
343 + if ( pEntry.isBetween() ) {
344 + mSelectedBetweens.add( ((Between<T>) pEntry).copyAsSelected() );
345 + DrillDownSet zDrillDownSet = updateDrillDownSetWhenSelectedBetweensChange( mCurrentEntriesVisible, mSelectedBetweens );
346 + mView.show( zDrillDownSet );
347 + return;
348 + }
349 + if ( pEntry.isSelectable() ) {
350 + mView.injectBetween( pEntry.getLowerReference(), pEntry.getUpperReference() );
351 + return;
352 + }
353 + throw new IllegalArgumentException( "Can not 'viewSelected' for type '" + ClassName.simple( pEntry ) + "': " + pEntry );
354 + }
355 +
356 + /**
357 + * Update the pLastDrillDownSet based on the new pCount (maximum Entries per ViewSet) (public for Testing)
358 + *
359 + * @return null means that pCount was too small!
360 + */
361 + public DrillDownSet updateDrillDownSetWhenEntriesVisibleChanges( int pCount, List<Between<T>> pSelectedBetweens ) {
362 + if ( -1 == (pCount = checkIfNotEnoughVisibleEntries( makeOdd( pCount ) )) ) {
363 + return null;
364 + }
365 + pSelectedBetweens.clear();
366 +
367 + List<ViewSet> zViewSets = buildViewSets( pCount, pSelectedBetweens );
368 + return new DrillDownSet( null, false, null, zViewSets.get( 0 ) );
369 + }
370 +
371 + /**
372 + * Update the pLastDrillDownSet based on the new pCount (maximum Entries per ViewSet) (public for Testing)
373 + *
374 + * @return null means that pCount was too small!
375 + */
376 + public DrillDownSet updateDrillDownSetWhenSelectedBetweensChange( int pCount, List<Between<T>> pSelectedBetweens ) {
377 + List<ViewSet> zViewSets = buildViewSets( makeOdd( pCount ), pSelectedBetweens );
378 + switch ( zViewSets.size() ) {
379 + case 1:
380 + return new DrillDownSet( null, false, null, zViewSets.get( 0 ) );
381 + case 2:
382 + return new DrillDownSet( zViewSets.get( 0 ), false, null, zViewSets.get( 1 ) );
383 + case 3:
384 + return new DrillDownSet( zViewSets.get( 0 ), false, zViewSets.get( 1 ), zViewSets.get( 2 ) );
385 + default:
386 + return new DrillDownSet( zViewSets.get( 0 ), true, zViewSets.get( zViewSets.size() - 2 ), zViewSets.get( zViewSets.size() - 1 ) );
387 + }
388 + }
389 +
390 + private int checkIfNotEnoughVisibleEntries( int pCount ) {
391 + return ((pCount >= 5) || // 5 is enough for anything (more is better)
392 + ((pCount == 3) && (mInjectIns.size() == 1))) ? // Special Case of just 1 InjectIn?
393 + pCount : // pCount is OK
394 + -1; // indicate pCount too small
395 + }
396 +
397 + private int makeOdd( int pCount ) {
398 + return ((pCount & 1) == 0) ? // Is Even?
399 + pCount - 1 : // Even, make Odd!
400 + pCount; // Already Odd
401 + }
402 +
403 + private Between<T> getCurrentSelectedBetween( List<Between<T>> pSelectedBetweens, int pCurrentIndex ) {
404 + return (pCurrentIndex < pSelectedBetweens.size()) ? pSelectedBetweens.get( pCurrentIndex ) : null;
405 + }
406 +
407 + private boolean isSelected( Between<T> pSelectedBetween, T pLast, T pCurr ) {
408 + return (pSelectedBetween != null) && (pLast == pSelectedBetween.getUpperReference()) && (pCurr == pSelectedBetween.getLowerReference());
409 + }
410 +
411 + private List<ViewSet> buildViewSets( int pCount, List<Between<T>> pSelectedBetweens ) {
412 + List<ViewSet> zViewSets = new ArrayList<ViewSet>();
413 +
414 + buildHomeViewSet( zViewSets, pCount, pSelectedBetweens );
415 +
416 + return zViewSets;
417 + }
418 +
419 + private void buildHomeViewSet( List<ViewSet> pViewSets, int pCount, List<Between<T>> pSelectedBetweens ) {
420 + Between<T> zSelectedBetween = getCurrentSelectedBetween( pSelectedBetweens, 0 );
421 +
422 + int zInjectInsToAdd = mInjectIns.size() - 1;
423 + int zAvailableBetweenBuckets = Math.min( zInjectInsToAdd, (pCount - 3) / 2 );
424 +
425 + // System.out.println( "buildHomeViewSett( " + zSelectedBetween + " ): " + //
426 + // pCount + " ( " + mInjectIns.size() + " ) " + zInjectInsToAdd + " | " + zAvailableBetweenBuckets );
427 +
428 + ArrayList<ViewEntry> zEntries = new ArrayList<ViewEntry>( pCount );
429 + pViewSets.add( new ViewSet( zEntries ) );
430 +
431 + int zCurrNdx = 0;
432 + int zThru = mInjectIns.size() - 1;
433 + T zCurrT = mInjectIns.get( zCurrNdx );
434 + zEntries.add( Top.create( zCurrT ) );
435 +
436 + while ( zCurrNdx < zThru ) {
437 + zEntries.add( Reference.create( mSynopsisLines, zCurrT ) );
438 + int zLastNdx = zCurrNdx;
439 + T zLastT = zCurrT;
440 + int zHowMany = zInjectInsToAdd / zAvailableBetweenBuckets;
441 + // System.out.print( " zInjectInsToAdd / zAvailableBetweenBuckets ==> " + zInjectInsToAdd + " / " + zAvailableBetweenBuckets + " = " + zHowMany );
442 + zInjectInsToAdd -= zHowMany;
443 + zCurrNdx += zHowMany;
444 + zCurrT = mInjectIns.get( zCurrNdx );
445 + if ( zHowMany == 1 ) {
446 + // System.out.println( " Add There 1" );
447 + zEntries.add( There.create( zLastT, zCurrT ) );
448 + } else {
449 + // System.out.println( " Add Between " + zHowMany );
450 + if ( isSelected( zSelectedBetween, zLastT, zCurrT ) ) {
451 + zEntries.add( zSelectedBetween );
452 + buildSubsequentViewSet( pViewSets, pCount, zLastNdx, zCurrNdx, pSelectedBetweens, 1 );
453 + } else {
454 + zEntries.add( Between.create( false, zLastT, zCurrT ) );
455 + }
456 + }
457 + zAvailableBetweenBuckets--;
458 + }
459 + zEntries.add( Reference.create( mSynopsisLines, zCurrT ) );
460 + zEntries.add( Bottom.create( zCurrT ) );
461 + }
462 +
463 + private void buildSubsequentViewSet( List<ViewSet> pViewSets, int pCount, int zUpperNdx, int zLowerNdx, List<Between<T>> pSelectedBetweens,
464 + int pCurrentIndex ) {
465 + Between<T> zSelectedBetween = getCurrentSelectedBetween( pSelectedBetweens, pCurrentIndex );
466 +
467 + int zInjectInsToAdd = zLowerNdx - zUpperNdx;
468 + int zAvailableBetweenBuckets = Math.min( zInjectInsToAdd, pCount / 2 );
469 +
470 + // System.out.println( " buildSubsequentViewSet( " + pCurrentIndex + " => " + zSelectedBetween + " ): " + //
471 + // pCount + " ( " + zUpperNdx + ", " + zLowerNdx + " ) " + zInjectInsToAdd + " | " + zAvailableBetweenBuckets );
472 +
473 + ArrayList<ViewEntry> zEntries = new ArrayList<ViewEntry>( pCount );
474 + pViewSets.add( new ViewSet( zEntries ) );
475 +
476 + int zCurrNdx = zUpperNdx;
477 + int zThru = zLowerNdx;
478 + T zCurrT = mInjectIns.get( zCurrNdx );
479 +
480 + while ( zCurrNdx < zThru ) {
481 + zEntries.add( Reference.create( mSynopsisLines, zCurrT ) );
482 + int zLastNdx = zCurrNdx;
483 + T zLastT = zCurrT;
484 + int zHowMany = zInjectInsToAdd / zAvailableBetweenBuckets;
485 + // System.out.print( " zInjectInsToAdd / zAvailableBetweenBuckets ==> " + zInjectInsToAdd + " / " + zAvailableBetweenBuckets + " = " + zHowMany );
486 + zInjectInsToAdd -= zHowMany;
487 + zCurrNdx += zHowMany;
488 + zCurrT = mInjectIns.get( zCurrNdx );
489 + if ( zHowMany == 1 ) {
490 + // System.out.println( " Add There 1" );
491 + zEntries.add( There.create( zLastT, zCurrT ) );
492 + } else {
493 + // System.out.println( " Add Between " + zHowMany );
494 + if ( isSelected( zSelectedBetween, zLastT, zCurrT ) ) {
495 + zEntries.add( zSelectedBetween );
496 + buildSubsequentViewSet( pViewSets, pCount, zLastNdx, zCurrNdx, pSelectedBetweens, pCurrentIndex + 1 );
497 + } else {
498 + zEntries.add( Between.create( false, zLastT, zCurrT ) );
499 + }
500 + }
501 + zAvailableBetweenBuckets--;
502 + }
503 + zEntries.add( Reference.create( mSynopsisLines, zCurrT ) );
504 + }
505 + }