@@ -43,19 +43,20 @@ This file is part of the iText (R) project.
4343 */
4444package com .itextpdf .kernel .pdf ;
4545
46- import com .itextpdf .io .logs .IoLogMessageConstant ;
4746import com .itextpdf .commons .utils .MessageFormatUtil ;
48- import com .itextpdf .kernel . exceptions . PdfException ;
47+ import com .itextpdf .io . logs . IoLogMessageConstant ;
4948import com .itextpdf .kernel .exceptions .KernelExceptionMessageConstant ;
49+ import com .itextpdf .kernel .exceptions .PdfException ;
5050
51+ import java .util .ArrayList ;
52+ import java .util .HashMap ;
5153import java .util .HashSet ;
54+ import java .util .List ;
55+ import java .util .Map ;
5256import java .util .Set ;
5357import org .slf4j .Logger ;
5458import org .slf4j .LoggerFactory ;
5559
56- import java .util .ArrayList ;
57- import java .util .List ;
58-
5960/**
6061 * Algorithm for construction {@link PdfPages} tree
6162 */
@@ -65,9 +66,9 @@ class PdfPagesTree {
6566
6667 private final int leafSize = DEFAULT_LEAF_SIZE ;
6768
68- private List <PdfIndirectReference > pageRefs ;
69+ private NullUnlimitedList <PdfIndirectReference > pageRefs ;
6970 private List <PdfPages > parents ;
70- private List <PdfPage > pages ;
71+ private NullUnlimitedList <PdfPage > pages ;
7172 private PdfDocument document ;
7273 private boolean generated = false ;
7374 private PdfPages root ;
@@ -81,9 +82,9 @@ class PdfPagesTree {
8182 */
8283 public PdfPagesTree (PdfCatalog pdfCatalog ) {
8384 this .document = pdfCatalog .getDocument ();
84- this .pageRefs = new ArrayList <>();
85+ this .pageRefs = new NullUnlimitedList <>();
8586 this .parents = new ArrayList <>();
86- this .pages = new ArrayList <>();
87+ this .pages = new NullUnlimitedList <>();
8788 if (pdfCatalog .getPdfObject ().containsKey (PdfName .Pages )) {
8889 PdfDictionary pages = pdfCatalog .getPdfObject ().getAsDictionary (PdfName .Pages );
8990 if (pages == null ) {
@@ -472,10 +473,9 @@ private void loadPage(int pageNum, Set<PdfIndirectReference> processedParents) {
472473 } else {
473474 int from = parent .getFrom ();
474475
475- // Possible exception in case kids.getSize() < parent.getCount().
476- // In any case parent.getCount() has higher priority.
477476 // 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 ++) {
479479 PdfObject kid = kids .get (i , false );
480480 if (kid instanceof PdfIndirectReference ) {
481481 pageRefs .set (from + i , (PdfIndirectReference ) kid );
@@ -533,4 +533,99 @@ private void correctPdfPagesFromProperty(int index, int correction) {
533533 }
534534 }
535535 }
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+ }
536631}
0 commit comments