@@ -22,19 +22,20 @@ This file is part of the iText (R) project.
2222 */
2323package com .itextpdf .kernel .pdf ;
2424
25- import com .itextpdf .io .logs .IoLogMessageConstant ;
2625import com .itextpdf .commons .utils .MessageFormatUtil ;
27- import com .itextpdf .kernel . exceptions . PdfException ;
26+ import com .itextpdf .io . logs . IoLogMessageConstant ;
2827import com .itextpdf .kernel .exceptions .KernelExceptionMessageConstant ;
28+ import com .itextpdf .kernel .exceptions .PdfException ;
2929
30+ import java .util .ArrayList ;
31+ import java .util .HashMap ;
3032import java .util .HashSet ;
33+ import java .util .List ;
34+ import java .util .Map ;
3135import java .util .Set ;
3236import org .slf4j .Logger ;
3337import org .slf4j .LoggerFactory ;
3438
35- import java .util .ArrayList ;
36- import java .util .List ;
37-
3839/**
3940 * Algorithm for construction {@link PdfPages} tree
4041 */
@@ -44,9 +45,9 @@ class PdfPagesTree {
4445
4546 private final int leafSize = DEFAULT_LEAF_SIZE ;
4647
47- private List <PdfIndirectReference > pageRefs ;
48+ private NullUnlimitedList <PdfIndirectReference > pageRefs ;
4849 private List <PdfPages > parents ;
49- private List <PdfPage > pages ;
50+ private NullUnlimitedList <PdfPage > pages ;
5051 private PdfDocument document ;
5152 private boolean generated = false ;
5253 private PdfPages root ;
@@ -60,9 +61,9 @@ class PdfPagesTree {
6061 */
6162 public PdfPagesTree (PdfCatalog pdfCatalog ) {
6263 this .document = pdfCatalog .getDocument ();
63- this .pageRefs = new ArrayList <>();
64+ this .pageRefs = new NullUnlimitedList <>();
6465 this .parents = new ArrayList <>();
65- this .pages = new ArrayList <>();
66+ this .pages = new NullUnlimitedList <>();
6667 if (pdfCatalog .getPdfObject ().containsKey (PdfName .Pages )) {
6768 PdfDictionary pages = pdfCatalog .getPdfObject ().getAsDictionary (PdfName .Pages );
6869 if (pages == null ) {
@@ -451,10 +452,9 @@ private void loadPage(int pageNum, Set<PdfIndirectReference> processedParents) {
451452 } else {
452453 int from = parent .getFrom ();
453454
454- // Possible exception in case kids.getSize() < parent.getCount().
455- // In any case parent.getCount() has higher priority.
456455 // 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 ++) {
458458 PdfObject kid = kids .get (i , false );
459459 if (kid instanceof PdfIndirectReference ) {
460460 pageRefs .set (from + i , (PdfIndirectReference ) kid );
@@ -512,4 +512,99 @@ private void correctPdfPagesFromProperty(int index, int correction) {
512512 }
513513 }
514514 }
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+ }
515610}
0 commit comments