1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.directory.mavibot.btree;
21
22
23 import java.io.IOException;
24 import java.lang.reflect.Array;
25
26 import org.apache.directory.mavibot.btree.exception.DuplicateValueNotAllowedException;
27 import org.apache.directory.mavibot.btree.exception.EndOfFileExceededException;
28 import org.apache.directory.mavibot.btree.exception.KeyNotFoundException;
29
30
31
32
33
34
35
36
37
38
39
40 {
41
42 protected ValueHolder<V>[] values;
43
44
45
46
47
48
49
50 PersistedLeaf( BTree<K, V> btree )
51 {
52 super( btree );
53 }
54
55
56
57
58
59
60
61
62
63 @SuppressWarnings("unchecked")
64 PersistedLeaf( BTree<K, V> btree, long revision, int nbElems )
65 {
66 super( btree, revision, nbElems );
67 values = ( ValueHolder<V>[] ) Array.newInstance( PersistedValueHolder.class, nbElems );
68 }
69
70
71
72
73
74 public InsertResult<K, V> insert( long revision, K key, V value ) throws IOException
75 {
76
77 int pos = findPos( key );
78
79 if ( pos < 0 )
80 {
81
82
83 int index = -( pos + 1 );
84
85
86 InsertResult<K, V> result = replaceElement( revision, key, value, index );
87
88 return result;
89 }
90
91
92 if ( nbElems < btree.getPageSize() )
93 {
94
95
96 Page<K, V> modifiedPage = addElement( revision, key, value, pos );
97
98 InsertResult<K, V> result = new ModifyResult<K, V>( modifiedPage, null );
99 result.addCopiedPage( this );
100
101 return result;
102 }
103 else
104 {
105
106
107 InsertResult<K, V> result = addAndSplit( revision, key, value, pos );
108 result.addCopiedPage( this );
109
110 return result;
111 }
112 }
113
114
115
116
117
118 @SuppressWarnings("unchecked")
119 public DeleteResult<K, V> delete( long revision, K key, V value, Page<K, V> parent, int parentPos )
120 throws IOException
121 {
122
123 if ( nbElems == 0 )
124 {
125
126 return NotPresentResult.NOT_PRESENT;
127 }
128
129
130 int pos = findPos( key );
131
132 if ( pos >= 0 )
133 {
134
135 return NotPresentResult.NOT_PRESENT;
136 }
137
138
139 Tuple<K, V> removedElement = null;
140
141
142 boolean keyRemoved = false;
143
144 int index = -( pos + 1 );
145
146 ValueHolder<V> valueHolder = values[index];
147
148 if ( value == null )
149 {
150
151 removedElement = new Tuple<K, V>( keys[index].getKey(), value );
152 keyRemoved = true;
153 }
154 else
155 {
156 if ( valueHolder.contains( value ) )
157 {
158 keyRemoved = ( valueHolder.size() == 1 );
159
160 removedElement = new Tuple<K, V>( keys[index].getKey(), value );
161 }
162 else
163 {
164 return NotPresentResult.NOT_PRESENT;
165 }
166 }
167
168 PersistedLeaf<K, V> newLeaf = null;
169
170 if ( keyRemoved )
171 {
172
173 newLeaf = new PersistedLeaf<K, V>( btree, revision, nbElems - 1 );
174 }
175 else
176 {
177
178 newLeaf = new PersistedLeaf<K, V>( btree, revision, nbElems );
179 }
180
181
182 DeleteResult<K, V> defaultResult = new RemoveResult<K, V>( newLeaf, removedElement );
183
184
185 if ( parent == null )
186 {
187
188 copyAfterRemovingElement( keyRemoved, value, newLeaf, index );
189
190
191 defaultResult.addCopiedPage( this );
192
193 return defaultResult;
194 }
195 else if ( keyRemoved )
196 {
197
198
199 int halfSize = btree.getPageSize() / 2;
200
201 if ( nbElems == halfSize )
202 {
203
204
205
206
207 int siblingPos = selectSibling( parent, parentPos );
208 PersistedLeaf<K, V> sibling = ( PersistedLeaf<K, V> ) ( ( ( PersistedNode<K, V> ) parent )
209 .getPage( siblingPos ) );
210
211 if ( sibling.getNbElems() == halfSize )
212 {
213
214 DeleteResult<K, V> result = mergeWithSibling( removedElement, revision, sibling,
215 ( siblingPos < parentPos ), index );
216
217 return result;
218 }
219 else
220 {
221
222 if ( siblingPos < parentPos )
223 {
224 DeleteResult<K, V> result = borrowFromLeft( removedElement, revision, sibling, index );
225
226 return result;
227 }
228 else
229 {
230
231 DeleteResult<K, V> result = borrowFromRight( removedElement, revision, sibling, index );
232
233 return result;
234 }
235 }
236 }
237 else
238 {
239
240
241
242
243 copyAfterRemovingElement( true, value, newLeaf, index );
244
245
246 defaultResult.addCopiedPage( this );
247
248 return defaultResult;
249 }
250 }
251 else
252 {
253
254
255 System.arraycopy( keys, 0, newLeaf.keys, 0, nbElems );
256 System.arraycopy( values, 0, newLeaf.values, 0, nbElems );
257
258
259 try
260 {
261 ValueHolder<V> newValueHolder = valueHolder.clone();
262 newValueHolder.remove( value );
263
264 newLeaf.values[pos] = newValueHolder;
265 }
266 catch ( CloneNotSupportedException e )
267 {
268
269 e.printStackTrace();
270 }
271
272
273 defaultResult.addCopiedPage( this );
274
275 return defaultResult;
276 }
277 }
278
279
280
281
282
283
284
285
286
287
288
289
290 private DeleteResult<K, V> mergeWithSibling( Tuple<K, V> removedElement, long revision,
291 PersistedLeaf<K, V> sibling,
292 boolean isLeft, int pos )
293 throws EndOfFileExceededException, IOException
294 {
295
296
297 PersistedLeaf<K, V> newLeaf = new PersistedLeaf<K, V>( btree, revision, btree.getPageSize() - 1 );
298
299 if ( isLeft )
300 {
301
302
303 System.arraycopy( sibling.keys, 0, newLeaf.keys, 0, sibling.nbElems );
304 System.arraycopy( sibling.values, 0, newLeaf.values, 0, sibling.nbElems );
305
306
307 System.arraycopy( keys, 0, newLeaf.keys, sibling.nbElems, pos );
308 System.arraycopy( values, 0, newLeaf.values, sibling.nbElems, pos );
309
310
311 System.arraycopy( keys, pos + 1, newLeaf.keys, sibling.nbElems + pos, nbElems - pos - 1 );
312 System.arraycopy( values, pos + 1, newLeaf.values, sibling.nbElems + pos, nbElems - pos - 1 );
313 }
314 else
315 {
316
317
318 System.arraycopy( keys, 0, newLeaf.keys, 0, pos );
319 System.arraycopy( values, 0, newLeaf.values, 0, pos );
320
321
322 System.arraycopy( keys, pos + 1, newLeaf.keys, pos, nbElems - pos - 1 );
323 System.arraycopy( values, pos + 1, newLeaf.values, pos, nbElems - pos - 1 );
324
325
326 System.arraycopy( sibling.keys, 0, newLeaf.keys, nbElems - 1, sibling.nbElems );
327 System.arraycopy( sibling.values, 0, newLeaf.values, nbElems - 1, sibling.nbElems );
328 }
329
330
331 DeleteResult<K, V> result = new MergedWithSiblingResult<K, V>( newLeaf, removedElement );
332
333 result.addCopiedPage( this );
334 result.addCopiedPage( sibling );
335
336 return result;
337 }
338
339
340
341
342
343
344
345
346
347
348
349
350
351 private DeleteResult<K, V> borrowFromLeft( Tuple<K, V> removedElement, long revision, PersistedLeaf<K, V> sibling,
352 int pos )
353 throws IOException
354 {
355
356 K siblingKey = sibling.keys[sibling.getNbElems() - 1].getKey();
357 ValueHolder<V> siblingValue = sibling.values[sibling.getNbElems() - 1];
358
359
360 PersistedLeaf<K, V> newSibling = ( PersistedLeaf<K, V> ) sibling.copy( revision, sibling.getNbElems() - 1 );
361
362
363
364 PersistedLeaf<K, V> newLeaf = new PersistedLeaf<K, V>( btree, revision, nbElems );
365
366
367 newLeaf.keys[0] = new PersistedKeyHolder<K>( btree.getKeySerializer(), siblingKey );
368 newLeaf.values[0] = siblingValue;
369
370
371 System.arraycopy( keys, 0, newLeaf.keys, 1, pos );
372 System.arraycopy( values, 0, newLeaf.values, 1, pos );
373
374
375 System.arraycopy( keys, pos + 1, newLeaf.keys, pos + 1, keys.length - pos - 1 );
376 System.arraycopy( values, pos + 1, newLeaf.values, pos + 1, values.length - pos - 1 );
377
378 DeleteResult<K, V> result = new BorrowedFromLeftResult<K, V>( newLeaf, newSibling, removedElement );
379
380
381 result.addCopiedPage( this );
382 result.addCopiedPage( sibling );
383
384 return result;
385 }
386
387
388
389
390
391
392
393
394
395
396
397
398
399 private DeleteResult<K, V> borrowFromRight( Tuple<K, V> removedElement, long revision, PersistedLeaf<K, V> sibling,
400 int pos )
401 throws IOException
402 {
403
404 K siblingKey = sibling.keys[0].getKey();
405 ValueHolder<V> siblingHolder = sibling.values[0];
406
407
408 PersistedLeaf<K, V> newSibling = new PersistedLeaf<K, V>( btree, revision, sibling.getNbElems() - 1 );
409
410
411 System.arraycopy( sibling.keys, 1, newSibling.keys, 0, sibling.nbElems - 1 );
412 System.arraycopy( sibling.values, 1, newSibling.values, 0, sibling.nbElems - 1 );
413
414
415
416 PersistedLeaf<K, V> newLeaf = new PersistedLeaf<K, V>( btree, revision, nbElems );
417
418
419 newLeaf.keys[nbElems - 1] = new PersistedKeyHolder<K>( btree.getKeySerializer(), siblingKey );
420 newLeaf.values[nbElems - 1] = siblingHolder;
421
422
423 System.arraycopy( keys, 0, newLeaf.keys, 0, pos );
424 System.arraycopy( values, 0, newLeaf.values, 0, pos );
425
426
427 System.arraycopy( keys, pos + 1, newLeaf.keys, pos, keys.length - pos - 1 );
428 System.arraycopy( values, pos + 1, newLeaf.values, pos, values.length - pos - 1 );
429
430 DeleteResult<K, V> result = new BorrowedFromRightResult<K, V>( newLeaf, newSibling, removedElement );
431
432
433 result.addCopiedPage( this );
434 result.addCopiedPage( sibling );
435
436 return result;
437 }
438
439
440
441
442
443
444
445
446
447
448 private void copyAfterRemovingElement( boolean keyRemoved, V removedValue, PersistedLeaf<K, V> newLeaf, int pos )
449 throws IOException
450 {
451 if ( keyRemoved )
452 {
453
454
455 if ( nbElems == 1 )
456 {
457 return;
458 }
459
460
461 System.arraycopy( keys, 0, newLeaf.keys, 0, pos );
462 System.arraycopy( values, 0, newLeaf.values, 0, pos );
463
464
465 System.arraycopy( keys, pos + 1, newLeaf.keys, pos, keys.length - pos - 1 );
466 System.arraycopy( values, pos + 1, newLeaf.values, pos, values.length - pos - 1 );
467 }
468 else
469
470 {
471 System.arraycopy( keys, 0, newLeaf.keys, 0, nbElems );
472 System.arraycopy( values, 0, newLeaf.values, 0, nbElems );
473
474
475 ValueHolder<V> valueHolder = newLeaf.values[pos];
476
477 try
478 {
479 ValueHolder<V> newValueHolder = valueHolder.clone();
480
481 newValueHolder.remove( removedValue );
482
483 newLeaf.values[pos] = newValueHolder;
484 }
485 catch ( CloneNotSupportedException e )
486 {
487
488 e.printStackTrace();
489 }
490 }
491 }
492
493
494
495
496
497 public V get( K key ) throws KeyNotFoundException, IOException
498 {
499 int pos = findPos( key );
500
501 if ( pos < 0 )
502 {
503 ValueHolder<V> valueHolder = values[-( pos + 1 )];
504
505 ValueCursor<V> cursor = valueHolder.getCursor();
506
507 cursor.beforeFirst();
508
509 if ( cursor.hasNext() )
510 {
511 V value = cursor.next();
512
513 return value;
514 }
515 else
516 {
517 return null;
518 }
519 }
520 else
521 {
522 throw KEY_NOT_FOUND_EXCEPTION;
523 }
524 }
525
526
527
528
529
530
531 {
532 if ( pos < nbElems )
533 {
534 return keys[pos];
535 }
536 else
537 {
538 return null;
539 }
540 }
541
542
543
544
545
546 @Override
547 public ValueCursor<V> getValues( K key ) throws KeyNotFoundException, IOException, IllegalArgumentException
548 {
549 if ( !btree.isAllowDuplicates() )
550 {
551 throw new IllegalArgumentException( "Duplicates are not allowed in this tree" );
552 }
553
554 int pos = findPos( key );
555
556 if ( pos < 0 )
557 {
558 ValueHolder<V> valueHolder = values[-( pos + 1 )];
559
560 return valueHolder.getCursor();
561 }
562 else
563 {
564 throw KEY_NOT_FOUND_EXCEPTION;
565 }
566 }
567
568
569
570
571
572 public boolean hasKey( K key )
573 {
574 int pos = findPos( key );
575
576 if ( pos < 0 )
577 {
578 return true;
579 }
580
581 return false;
582 }
583
584
585 @Override
586 public boolean contains( K key, V value ) throws IOException
587 {
588 int pos = findPos( key );
589
590 if ( pos < 0 )
591 {
592 ValueHolder<V> valueHolder = values[-( pos + 1 )];
593
594 return valueHolder.contains( value );
595 }
596 else
597 {
598 return false;
599 }
600 }
601
602
603
604
605
606
607 {
608 if ( pos < nbElems )
609 {
610 return values[pos];
611 }
612 else
613 {
614 return null;
615 }
616 }
617
618
619
620
621
622
623
624
625 {
626 values[pos] = value;
627 }
628
629
630
631
632
633 public TupleCursor<K, V> browse( K key, ReadTransaction<K, V> transaction, ParentPos<K, V>[] stack, int depth )
634 {
635 int pos = findPos( key );
636 TupleCursor<K, V> cursor = null;
637
638 if ( pos < 0 )
639 {
640 pos = -( pos + 1 );
641
642
643 ParentPos<K, V> parentPos = new ParentPos<K, V>( this, pos );
644
645
646 parentPos.valueCursor = values[pos].getCursor();
647
648 stack[depth] = parentPos;
649
650 cursor = new TupleCursor<K, V>( transaction, stack, depth );
651 }
652 else
653 {
654
655 if ( pos < nbElems )
656 {
657 ParentPos<K, V> parentPos = new ParentPos<K, V>( this, pos );
658
659
660 parentPos.valueCursor = values[pos].getCursor();
661
662 stack[depth] = parentPos;
663
664 cursor = new TupleCursor<K, V>( transaction, stack, depth );
665 }
666 else if ( nbElems > 0 )
667 {
668
669 ParentPos<K, V> parentPos = new ParentPos<K, V>( this, nbElems - 1 );
670
671
672 parentPos.valueCursor = values[nbElems - 1].getCursor();
673
674 stack[depth] = parentPos;
675
676 cursor = new TupleCursor<K, V>( transaction, stack, depth );
677
678 try
679 {
680 cursor.afterLast();
681 }
682 catch ( IOException e )
683 {
684
685 e.printStackTrace();
686 }
687 }
688 else
689 {
690
691 stack[depth] = null;
692
693 cursor = new TupleCursor<K, V>( transaction, null, 0 );
694 }
695 }
696
697 return cursor;
698 }
699
700
701
702
703
704 public TupleCursor<K, V> browse( ReadTransaction<K, V> transaction, ParentPos<K, V>[] stack, int depth )
705 {
706 int pos = 0;
707 TupleCursor<K, V> cursor = null;
708
709 if ( nbElems == 0 )
710 {
711
712 stack[depth] = new ParentPos<K, V>( null, -1 );
713
714 return new TupleCursor<K, V>( transaction, stack, depth );
715 }
716 else
717 {
718
719 ParentPos<K, V> parentPos = new ParentPos<K, V>( this, pos );
720
721
722 parentPos.valueCursor = values[0].getCursor();
723
724 stack[depth] = parentPos;
725
726 cursor = new TupleCursor<K, V>( transaction, stack, depth );
727 }
728
729 return cursor;
730 }
731
732
733
734
735
736
737
738
739
740 private Page<K, V> copy( long revision, int nbElems )
741 {
742 PersistedLeaf<K, V> newLeaf = new PersistedLeaf<K, V>( btree, revision, nbElems );
743
744
745 System.arraycopy( keys, 0, newLeaf.keys, 0, nbElems );
746
747
748
749 int pos = 0;
750
751 for ( ValueHolder<V> valueHolder : values )
752 {
753 try
754 {
755 newLeaf.values[pos++] = valueHolder.clone();
756 }
757 catch ( CloneNotSupportedException e )
758 {
759
760 e.printStackTrace();
761 }
762
763
764 if ( pos == nbElems )
765 {
766 break;
767 }
768 }
769
770 return newLeaf;
771 }
772
773
774
775
776
777
778
779
780
781
782
783
784 private InsertResult<K, V> replaceElement( long revision, K key, V value, int pos )
785 throws IOException
786 {
787 PersistedLeaf<K, V> newLeaf = this;
788
789
790 ValueHolder<V> valueHolder = values[pos];
791
792 boolean valueExists = valueHolder.contains( value );
793
794
795 if ( !valueExists && !btree.isAllowDuplicates() )
796 {
797 throw new DuplicateValueNotAllowedException( "Duplicate values are not allowed" );
798 }
799
800 if ( this.revision != revision )
801 {
802
803 newLeaf = ( PersistedLeaf<K, V> ) copy( revision, nbElems );
804 }
805
806
807 valueHolder = newLeaf.values[pos];
808 V replacedValue = null;
809
810 if ( !valueExists )
811 {
812 valueHolder.add( value );
813 newLeaf.values[pos] = valueHolder;
814 }
815 else
816 {
817
818
819
820 replacedValue = valueHolder.remove( value );
821 valueHolder.add( value );
822 }
823
824
825 InsertResult<K, V> result = new ModifyResult<K, V>( newLeaf, replacedValue );
826 result.addCopiedPage( this );
827
828 return result;
829 }
830
831
832
833
834
835
836
837
838
839
840
841
842 private Page<K, V> addElement( long revision, K key, V value, int pos )
843 {
844
845 PersistedLeaf<K, V> newLeaf = new PersistedLeaf<K, V>( btree, revision, nbElems + 1 );
846
847
848 ValueHolder<V> valueHolder = new PersistedValueHolder<V>( btree, value );
849
850
851 if ( nbElems == 0 )
852 {
853 newLeaf.keys[0] = new PersistedKeyHolder<K>( btree.getKeySerializer(), key );
854
855 newLeaf.values[0] = valueHolder;
856 }
857 else
858 {
859
860 System.arraycopy( keys, 0, newLeaf.keys, 0, pos );
861 System.arraycopy( values, 0, newLeaf.values, 0, pos );
862
863
864 newLeaf.keys[pos] = new PersistedKeyHolder<K>( btree.getKeySerializer(), key );
865 newLeaf.values[pos] = valueHolder;
866
867
868 System.arraycopy( keys, pos, newLeaf.keys, pos + 1, keys.length - pos );
869 System.arraycopy( values, pos, newLeaf.values, pos + 1, values.length - pos );
870 }
871
872 return newLeaf;
873 }
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891 private InsertResult<K, V> addAndSplit( long revision, K key, V value, int pos )
892 {
893 int middle = btree.getPageSize() >> 1;
894 PersistedLeaf<K, V> leftLeaf = null;
895 PersistedLeaf<K, V> rightLeaf = null;
896 ValueHolder<V> valueHolder = new PersistedValueHolder<V>( btree, value );
897
898
899 if ( pos <= middle )
900 {
901
902 leftLeaf = new PersistedLeaf<K, V>( btree, revision, middle + 1 );
903
904
905 System.arraycopy( keys, 0, leftLeaf.keys, 0, pos );
906 System.arraycopy( values, 0, leftLeaf.values, 0, pos );
907
908
909 leftLeaf.keys[pos] = new PersistedKeyHolder<K>( btree.getKeySerializer(), key );
910 leftLeaf.values[pos] = valueHolder;
911
912
913 System.arraycopy( keys, pos, leftLeaf.keys, pos + 1, middle - pos );
914 System.arraycopy( values, pos, leftLeaf.values, pos + 1, middle - pos );
915
916
917 rightLeaf = new PersistedLeaf<K, V>( btree, revision, middle );
918
919
920 System.arraycopy( keys, middle, rightLeaf.keys, 0, middle );
921 System.arraycopy( values, middle, rightLeaf.values, 0, middle );
922 }
923 else
924 {
925
926 leftLeaf = new PersistedLeaf<K, V>( btree, revision, middle );
927
928
929 System.arraycopy( keys, 0, leftLeaf.keys, 0, middle );
930 System.arraycopy( values, 0, leftLeaf.values, 0, middle );
931
932
933 rightLeaf = new PersistedLeaf<K, V>( btree, revision, middle + 1 );
934
935 int rightPos = pos - middle;
936
937
938 System.arraycopy( keys, middle, rightLeaf.keys, 0, rightPos );
939 System.arraycopy( values, middle, rightLeaf.values, 0, rightPos );
940
941
942 rightLeaf.keys[rightPos] = new PersistedKeyHolder<K>( btree.getKeySerializer(), key );
943 rightLeaf.values[rightPos] = valueHolder;
944
945
946 System.arraycopy( keys, pos, rightLeaf.keys, rightPos + 1, nbElems - pos );
947 System.arraycopy( values, pos, rightLeaf.values, rightPos + 1, nbElems - pos );
948 }
949
950
951 K pivot = rightLeaf.keys[0].getKey();
952
953 if ( pivot == null )
954 {
955 pivot = rightLeaf.keys[0].getKey();
956 }
957
958
959 InsertResult<K, V> result = new SplitResult<K, V>( pivot, leftLeaf, rightLeaf );
960
961 return result;
962 }
963
964
965
966
967
968 public K getLeftMostKey()
969 {
970 return keys[0].getKey();
971 }
972
973
974
975
976
977 public K getRightMostKey()
978 {
979 return keys[nbElems - 1].getKey();
980 }
981
982
983
984
985
986 public Tuple<K, V> findLeftMost() throws IOException
987 {
988 ValueCursor<V> cursor = values[0].getCursor();
989
990 try
991 {
992 cursor.beforeFirst();
993 if ( cursor.hasNext() )
994 {
995 return new Tuple<K, V>( keys[0].getKey(), cursor.next() );
996 }
997 else
998 {
999
1000 return new Tuple<K, V>( keys[0].getKey(), null );
1001 }
1002 }
1003 finally
1004 {
1005 cursor.close();
1006 }
1007 }
1008
1009
1010
1011
1012
1013 public Tuple<K, V> findRightMost() throws EndOfFileExceededException, IOException
1014 {
1015 ValueCursor<V> cursor = values[nbElems - 1].getCursor();
1016
1017 try
1018 {
1019 cursor.afterLast();
1020
1021 if ( cursor.hasPrev() )
1022 {
1023 return new Tuple<K, V>( keys[nbElems - 1].getKey(), cursor.prev() );
1024 }
1025 else
1026 {
1027
1028 return new Tuple<K, V>( keys[nbElems - 1].getKey(), null );
1029 }
1030 }
1031 finally
1032 {
1033 cursor.close();
1034 }
1035 }
1036
1037
1038
1039
1040
1041 public boolean isLeaf()
1042 {
1043 return true;
1044 }
1045
1046
1047
1048
1049
1050 public boolean isNode()
1051 {
1052 return false;
1053 }
1054
1055
1056
1057
1058
1059 public String toString()
1060 {
1061 StringBuilder sb = new StringBuilder();
1062
1063 sb.append( "Leaf[" );
1064 sb.append( super.toString() );
1065
1066 sb.append( "] -> {" );
1067
1068 if ( nbElems > 0 )
1069 {
1070 boolean isFirst = true;
1071
1072 for ( int i = 0; i < nbElems; i++ )
1073 {
1074 if ( isFirst )
1075 {
1076 isFirst = false;
1077 }
1078 else
1079 {
1080 sb.append( ", " );
1081 }
1082
1083 sb.append( "<" ).append( keys[i] ).append( "," ).append( values[i] ).append( ">" );
1084 }
1085 }
1086
1087 sb.append( "}" );
1088
1089 return sb.toString();
1090 }
1091
1092
1093
1094
1095
1096 public String dumpPage( String tabs )
1097 {
1098 StringBuilder sb = new StringBuilder();
1099
1100 sb.append( tabs );
1101
1102 if ( nbElems > 0 )
1103 {
1104 boolean isFirst = true;
1105
1106 for ( int i = 0; i < nbElems; i++ )
1107 {
1108 if ( isFirst )
1109 {
1110 isFirst = false;
1111 }
1112 else
1113 {
1114 sb.append( ", " );
1115 }
1116
1117 sb.append( "<" ).append( keys[i] ).append( "," ).append( values[i] ).append( ">" );
1118 }
1119 }
1120
1121 sb.append( "\n" );
1122
1123 return sb.toString();
1124 }
1125 }