@@ -46,12 +46,14 @@ This file is part of the iText (R) project.
46
46
import com .itextpdf .io .LogMessageConstant ;
47
47
import com .itextpdf .io .util .MessageFormatUtil ;
48
48
import com .itextpdf .kernel .PdfException ;
49
- import org .slf4j .Logger ;
50
- import org .slf4j .LoggerFactory ;
51
49
52
50
import java .io .Serializable ;
53
51
import java .util .ArrayList ;
52
+ import java .util .HashMap ;
54
53
import java .util .List ;
54
+ import java .util .Map ;
55
+ import org .slf4j .Logger ;
56
+ import org .slf4j .LoggerFactory ;
55
57
56
58
/**
57
59
* Algorithm for construction {@link PdfPages} tree
@@ -62,9 +64,9 @@ class PdfPagesTree implements Serializable {
62
64
63
65
private final int leafSize = 10 ;
64
66
65
- private List <PdfIndirectReference > pageRefs ;
67
+ private NullUnlimitedList <PdfIndirectReference > pageRefs ;
66
68
private List <PdfPages > parents ;
67
- private List <PdfPage > pages ;
69
+ private NullUnlimitedList <PdfPage > pages ;
68
70
private PdfDocument document ;
69
71
private boolean generated = false ;
70
72
private PdfPages root ;
@@ -78,9 +80,9 @@ class PdfPagesTree implements Serializable {
78
80
*/
79
81
public PdfPagesTree (PdfCatalog pdfCatalog ) {
80
82
this .document = pdfCatalog .getDocument ();
81
- this .pageRefs = new ArrayList <>();
83
+ this .pageRefs = new NullUnlimitedList <>();
82
84
this .parents = new ArrayList <>();
83
- this .pages = new ArrayList <>();
85
+ this .pages = new NullUnlimitedList <>();
84
86
if (pdfCatalog .getPdfObject ().containsKey (PdfName .Pages )) {
85
87
PdfDictionary pages = pdfCatalog .getPdfObject ().getAsDictionary (PdfName .Pages );
86
88
if (pages == null )
@@ -425,10 +427,9 @@ private void loadPage(int pageNum) {
425
427
} else {
426
428
int from = parent .getFrom ();
427
429
428
- // Possible exception in case kids.getSize() < parent.getCount().
429
- // In any case parent.getCount() has higher priority.
430
430
// NOTE optimization? when we already found needed index
431
- for (int i = 0 ; i < parent .getCount (); i ++) {
431
+ final int pageCount = Math .min (parent .getCount (), kids .size ());
432
+ for (int i = 0 ; i < pageCount ; i ++) {
432
433
PdfObject kid = kids .get (i , false );
433
434
if (kid instanceof PdfIndirectReference ) {
434
435
pageRefs .set (from + i , (PdfIndirectReference ) kid );
@@ -486,4 +487,99 @@ private void correctPdfPagesFromProperty(int index, int correction) {
486
487
}
487
488
}
488
489
}
490
+
491
+ /**
492
+ * The class represents a list which allows null elements, but doesn't allocate a memory for them, in the rest of
493
+ * cases it behaves like usual {@link ArrayList} and should have the same complexity (because keys are unique
494
+ * integers, so collisions are impossible). Class doesn't implement {@code List} interface because it provides
495
+ * only methods which are in use in {@link PdfPagesTree} class.
496
+ *
497
+ * @param <T> elements of the list
498
+ */
499
+ static final class NullUnlimitedList <T > implements Serializable {
500
+ private final Map <Integer , T > map = new HashMap <>();
501
+ private int size = 0 ;
502
+
503
+ // O(1)
504
+ public void add (T element ) {
505
+ if (element == null ) {
506
+ size ++;
507
+ return ;
508
+ }
509
+ map .put (size ++, element );
510
+ }
511
+
512
+ // In worth scenario O(n^2) but it is mostly impossible because keys shouldn't have
513
+ // collisions at all (they are integers). So in average should be O(n).
514
+ public void add (int index , T element ) {
515
+ if (index < 0 || index > size ) {
516
+ return ;
517
+ }
518
+ size ++;
519
+ // Shifts the element currently at that position (if any) and any
520
+ // subsequent elements to the right (adds one to their indices).
521
+ T previous = map .get (index );
522
+ for (int i = index + 1 ; i < size ; i ++) {
523
+ T currentToAdd = previous ;
524
+ previous = map .get (i );
525
+ this .set (i , currentToAdd );
526
+ }
527
+
528
+ this .set (index , element );
529
+ }
530
+
531
+ // average O(1), worth O(n) (mostly impossible in case when keys are integers)
532
+ public T get (int index ) {
533
+ return map .get (index );
534
+ }
535
+
536
+ // average O(1), worth O(n) (mostly impossible in case when keys are integers)
537
+ public void set (int index , T element ) {
538
+ if (element == null ) {
539
+ map .remove (index );
540
+ } else {
541
+ map .put (index , element );
542
+ }
543
+ }
544
+
545
+ // O(n)
546
+ public int indexOf (T element ) {
547
+ if (element == null ) {
548
+ for (int i = 0 ; i < size ; i ++) {
549
+ if (!map .containsKey (i )) {
550
+ return i ;
551
+ }
552
+ }
553
+ return -1 ;
554
+ }
555
+ for (Map .Entry <Integer , T > entry : map .entrySet ()) {
556
+ if (element .equals (entry .getValue ())) {
557
+ return entry .getKey ();
558
+ }
559
+ }
560
+ return -1 ;
561
+ }
562
+
563
+ // In worth scenario O(n^2) but it is mostly impossible because keys shouldn't have
564
+ // collisions at all (they are integers). So in average should be O(n).
565
+ public void remove (int index ) {
566
+ if (index < 0 || index >= size ) {
567
+ return ;
568
+ }
569
+ map .remove (index );
570
+ // Shifts any subsequent elements to the left (subtracts one from their indices).
571
+ T previous = map .get (size - 1 );
572
+ for (int i = size - 2 ; i >= index ; i --) {
573
+ T current = previous ;
574
+ previous = map .get (i );
575
+ this .set (i , current );
576
+ }
577
+ map .remove (--size );
578
+ }
579
+
580
+ // O(1)
581
+ public int size () {
582
+ return size ;
583
+ }
584
+ }
489
585
}
0 commit comments