@@ -22,19 +22,20 @@ This file is part of the iText (R) project.
22
22
*/
23
23
package com .itextpdf .kernel .pdf ;
24
24
25
- import com .itextpdf .io .logs .IoLogMessageConstant ;
26
25
import com .itextpdf .commons .utils .MessageFormatUtil ;
27
- import com .itextpdf .kernel . exceptions . PdfException ;
26
+ import com .itextpdf .io . logs . IoLogMessageConstant ;
28
27
import com .itextpdf .kernel .exceptions .KernelExceptionMessageConstant ;
28
+ import com .itextpdf .kernel .exceptions .PdfException ;
29
29
30
+ import java .util .ArrayList ;
31
+ import java .util .HashMap ;
30
32
import java .util .HashSet ;
33
+ import java .util .List ;
34
+ import java .util .Map ;
31
35
import java .util .Set ;
32
36
import org .slf4j .Logger ;
33
37
import org .slf4j .LoggerFactory ;
34
38
35
- import java .util .ArrayList ;
36
- import java .util .List ;
37
-
38
39
/**
39
40
* Algorithm for construction {@link PdfPages} tree
40
41
*/
@@ -44,9 +45,9 @@ class PdfPagesTree {
44
45
45
46
private final int leafSize = DEFAULT_LEAF_SIZE ;
46
47
47
- private List <PdfIndirectReference > pageRefs ;
48
+ private NullUnlimitedList <PdfIndirectReference > pageRefs ;
48
49
private List <PdfPages > parents ;
49
- private List <PdfPage > pages ;
50
+ private NullUnlimitedList <PdfPage > pages ;
50
51
private PdfDocument document ;
51
52
private boolean generated = false ;
52
53
private PdfPages root ;
@@ -60,9 +61,9 @@ class PdfPagesTree {
60
61
*/
61
62
public PdfPagesTree (PdfCatalog pdfCatalog ) {
62
63
this .document = pdfCatalog .getDocument ();
63
- this .pageRefs = new ArrayList <>();
64
+ this .pageRefs = new NullUnlimitedList <>();
64
65
this .parents = new ArrayList <>();
65
- this .pages = new ArrayList <>();
66
+ this .pages = new NullUnlimitedList <>();
66
67
if (pdfCatalog .getPdfObject ().containsKey (PdfName .Pages )) {
67
68
PdfDictionary pages = pdfCatalog .getPdfObject ().getAsDictionary (PdfName .Pages );
68
69
if (pages == null ) {
@@ -451,10 +452,9 @@ private void loadPage(int pageNum, Set<PdfIndirectReference> processedParents) {
451
452
} else {
452
453
int from = parent .getFrom ();
453
454
454
- // Possible exception in case kids.getSize() < parent.getCount().
455
- // In any case parent.getCount() has higher priority.
456
455
// NOTE optimization? when we already found needed index
457
- for (int i = 0 ; i < parent .getCount (); i ++) {
456
+ final int pageCount = Math .min (parent .getCount (), kids .size ());
457
+ for (int i = 0 ; i < pageCount ; i ++) {
458
458
PdfObject kid = kids .get (i , false );
459
459
if (kid instanceof PdfIndirectReference ) {
460
460
pageRefs .set (from + i , (PdfIndirectReference ) kid );
@@ -512,4 +512,99 @@ private void correctPdfPagesFromProperty(int index, int correction) {
512
512
}
513
513
}
514
514
}
515
+
516
+ /**
517
+ * The class represents a list which allows null elements, but doesn't allocate a memory for them, in the rest of
518
+ * cases it behaves like usual {@link ArrayList} and should have the same complexity (because keys are unique
519
+ * integers, so collisions are impossible). Class doesn't implement {@code List} interface because it provides
520
+ * only methods which are in use in {@link PdfPagesTree} class.
521
+ *
522
+ * @param <T> elements of the list
523
+ */
524
+ static final class NullUnlimitedList <T > {
525
+ private final Map <Integer , T > map = new HashMap <>();
526
+ private int size = 0 ;
527
+
528
+ // O(1)
529
+ public void add (T element ) {
530
+ if (element == null ) {
531
+ size ++;
532
+ return ;
533
+ }
534
+ map .put (size ++, element );
535
+ }
536
+
537
+ // In worth scenario O(n^2) but it is mostly impossible because keys shouldn't have
538
+ // collisions at all (they are integers). So in average should be O(n).
539
+ public void add (int index , T element ) {
540
+ if (index < 0 || index > size ) {
541
+ return ;
542
+ }
543
+ size ++;
544
+ // Shifts the element currently at that position (if any) and any
545
+ // subsequent elements to the right (adds one to their indices).
546
+ T previous = map .get (index );
547
+ for (int i = index + 1 ; i < size ; i ++) {
548
+ T currentToAdd = previous ;
549
+ previous = map .get (i );
550
+ this .set (i , currentToAdd );
551
+ }
552
+
553
+ this .set (index , element );
554
+ }
555
+
556
+ // average O(1), worth O(n) (mostly impossible in case when keys are integers)
557
+ public T get (int index ) {
558
+ return map .get (index );
559
+ }
560
+
561
+ // average O(1), worth O(n) (mostly impossible in case when keys are integers)
562
+ public void set (int index , T element ) {
563
+ if (element == null ) {
564
+ map .remove (index );
565
+ } else {
566
+ map .put (index , element );
567
+ }
568
+ }
569
+
570
+ // O(n)
571
+ public int indexOf (T element ) {
572
+ if (element == null ) {
573
+ for (int i = 0 ; i < size ; i ++) {
574
+ if (!map .containsKey (i )) {
575
+ return i ;
576
+ }
577
+ }
578
+ return -1 ;
579
+ }
580
+ for (Map .Entry <Integer , T > entry : map .entrySet ()) {
581
+ if (element .equals (entry .getValue ())) {
582
+ return entry .getKey ();
583
+ }
584
+ }
585
+ return -1 ;
586
+ }
587
+
588
+ // In worth scenario O(n^2) but it is mostly impossible because keys shouldn't have
589
+ // collisions at all (they are integers). So in average should be O(n).
590
+ public void remove (int index ) {
591
+ if (index < 0 || index >= size ) {
592
+ return ;
593
+ }
594
+ map .remove (index );
595
+ // Shifts any subsequent elements to the left (subtracts one from their indices).
596
+ T previous = map .get (size - 1 );
597
+ for (int i = size - 2 ; i >= index ; i --) {
598
+ T current = previous ;
599
+ previous = map .get (i );
600
+ this .set (i , current );
601
+ }
602
+ map .remove (--size );
603
+ }
604
+
605
+ // O(1)
606
+ public int size () {
607
+ return size ;
608
+ }
609
+ }
515
610
}
0 commit comments