@@ -43,19 +43,20 @@ This file is part of the iText (R) project.
43
43
*/
44
44
package com .itextpdf .kernel .pdf ;
45
45
46
- import com .itextpdf .io .logs .IoLogMessageConstant ;
47
46
import com .itextpdf .commons .utils .MessageFormatUtil ;
48
- import com .itextpdf .kernel . exceptions . PdfException ;
47
+ import com .itextpdf .io . logs . IoLogMessageConstant ;
49
48
import com .itextpdf .kernel .exceptions .KernelExceptionMessageConstant ;
49
+ import com .itextpdf .kernel .exceptions .PdfException ;
50
50
51
+ import java .util .ArrayList ;
52
+ import java .util .HashMap ;
51
53
import java .util .HashSet ;
54
+ import java .util .List ;
55
+ import java .util .Map ;
52
56
import java .util .Set ;
53
57
import org .slf4j .Logger ;
54
58
import org .slf4j .LoggerFactory ;
55
59
56
- import java .util .ArrayList ;
57
- import java .util .List ;
58
-
59
60
/**
60
61
* Algorithm for construction {@link PdfPages} tree
61
62
*/
@@ -65,9 +66,9 @@ class PdfPagesTree {
65
66
66
67
private final int leafSize = DEFAULT_LEAF_SIZE ;
67
68
68
- private List <PdfIndirectReference > pageRefs ;
69
+ private NullUnlimitedList <PdfIndirectReference > pageRefs ;
69
70
private List <PdfPages > parents ;
70
- private List <PdfPage > pages ;
71
+ private NullUnlimitedList <PdfPage > pages ;
71
72
private PdfDocument document ;
72
73
private boolean generated = false ;
73
74
private PdfPages root ;
@@ -81,9 +82,9 @@ class PdfPagesTree {
81
82
*/
82
83
public PdfPagesTree (PdfCatalog pdfCatalog ) {
83
84
this .document = pdfCatalog .getDocument ();
84
- this .pageRefs = new ArrayList <>();
85
+ this .pageRefs = new NullUnlimitedList <>();
85
86
this .parents = new ArrayList <>();
86
- this .pages = new ArrayList <>();
87
+ this .pages = new NullUnlimitedList <>();
87
88
if (pdfCatalog .getPdfObject ().containsKey (PdfName .Pages )) {
88
89
PdfDictionary pages = pdfCatalog .getPdfObject ().getAsDictionary (PdfName .Pages );
89
90
if (pages == null ) {
@@ -472,10 +473,9 @@ private void loadPage(int pageNum, Set<PdfIndirectReference> processedParents) {
472
473
} else {
473
474
int from = parent .getFrom ();
474
475
475
- // Possible exception in case kids.getSize() < parent.getCount().
476
- // In any case parent.getCount() has higher priority.
477
476
// NOTE optimization? when we already found needed index
478
- for (int i = 0 ; i < parent .getCount (); i ++) {
477
+ final int pageCount = Math .min (parent .getCount (), kids .size ());
478
+ for (int i = 0 ; i < pageCount ; i ++) {
479
479
PdfObject kid = kids .get (i , false );
480
480
if (kid instanceof PdfIndirectReference ) {
481
481
pageRefs .set (from + i , (PdfIndirectReference ) kid );
@@ -533,4 +533,99 @@ private void correctPdfPagesFromProperty(int index, int correction) {
533
533
}
534
534
}
535
535
}
536
+
537
+ /**
538
+ * The class represents a list which allows null elements, but doesn't allocate a memory for them, in the rest of
539
+ * cases it behaves like usual {@link ArrayList} and should have the same complexity (because keys are unique
540
+ * integers, so collisions are impossible). Class doesn't implement {@code List} interface because it provides
541
+ * only methods which are in use in {@link PdfPagesTree} class.
542
+ *
543
+ * @param <T> elements of the list
544
+ */
545
+ static final class NullUnlimitedList <T > {
546
+ private final Map <Integer , T > map = new HashMap <>();
547
+ private int size = 0 ;
548
+
549
+ // O(1)
550
+ public void add (T element ) {
551
+ if (element == null ) {
552
+ size ++;
553
+ return ;
554
+ }
555
+ map .put (size ++, element );
556
+ }
557
+
558
+ // In worth scenario O(n^2) but it is mostly impossible because keys shouldn't have
559
+ // collisions at all (they are integers). So in average should be O(n).
560
+ public void add (int index , T element ) {
561
+ if (index < 0 || index > size ) {
562
+ return ;
563
+ }
564
+ size ++;
565
+ // Shifts the element currently at that position (if any) and any
566
+ // subsequent elements to the right (adds one to their indices).
567
+ T previous = map .get (index );
568
+ for (int i = index + 1 ; i < size ; i ++) {
569
+ T currentToAdd = previous ;
570
+ previous = map .get (i );
571
+ this .set (i , currentToAdd );
572
+ }
573
+
574
+ this .set (index , element );
575
+ }
576
+
577
+ // average O(1), worth O(n) (mostly impossible in case when keys are integers)
578
+ public T get (int index ) {
579
+ return map .get (index );
580
+ }
581
+
582
+ // average O(1), worth O(n) (mostly impossible in case when keys are integers)
583
+ public void set (int index , T element ) {
584
+ if (element == null ) {
585
+ map .remove (index );
586
+ } else {
587
+ map .put (index , element );
588
+ }
589
+ }
590
+
591
+ // O(n)
592
+ public int indexOf (T element ) {
593
+ if (element == null ) {
594
+ for (int i = 0 ; i < size ; i ++) {
595
+ if (!map .containsKey (i )) {
596
+ return i ;
597
+ }
598
+ }
599
+ return -1 ;
600
+ }
601
+ for (Map .Entry <Integer , T > entry : map .entrySet ()) {
602
+ if (element .equals (entry .getValue ())) {
603
+ return entry .getKey ();
604
+ }
605
+ }
606
+ return -1 ;
607
+ }
608
+
609
+ // In worth scenario O(n^2) but it is mostly impossible because keys shouldn't have
610
+ // collisions at all (they are integers). So in average should be O(n).
611
+ public void remove (int index ) {
612
+ if (index < 0 || index >= size ) {
613
+ return ;
614
+ }
615
+ map .remove (index );
616
+ // Shifts any subsequent elements to the left (subtracts one from their indices).
617
+ T previous = map .get (size - 1 );
618
+ for (int i = size - 2 ; i >= index ; i --) {
619
+ T current = previous ;
620
+ previous = map .get (i );
621
+ this .set (i , current );
622
+ }
623
+ map .remove (--size );
624
+ }
625
+
626
+ // O(1)
627
+ public int size () {
628
+ return size ;
629
+ }
630
+ }
536
631
}
0 commit comments