@@ -46,12 +46,14 @@ This file is part of the iText (R) project.
4646import com .itextpdf .io .LogMessageConstant ;
4747import com .itextpdf .io .util .MessageFormatUtil ;
4848import com .itextpdf .kernel .PdfException ;
49- import org .slf4j .Logger ;
50- import org .slf4j .LoggerFactory ;
5149
5250import java .io .Serializable ;
5351import java .util .ArrayList ;
52+ import java .util .HashMap ;
5453import java .util .List ;
54+ import java .util .Map ;
55+ import org .slf4j .Logger ;
56+ import org .slf4j .LoggerFactory ;
5557
5658/**
5759 * Algorithm for construction {@link PdfPages} tree
@@ -62,9 +64,9 @@ class PdfPagesTree implements Serializable {
6264
6365 private final int leafSize = 10 ;
6466
65- private List <PdfIndirectReference > pageRefs ;
67+ private NullUnlimitedList <PdfIndirectReference > pageRefs ;
6668 private List <PdfPages > parents ;
67- private List <PdfPage > pages ;
69+ private NullUnlimitedList <PdfPage > pages ;
6870 private PdfDocument document ;
6971 private boolean generated = false ;
7072 private PdfPages root ;
@@ -78,9 +80,9 @@ class PdfPagesTree implements Serializable {
7880 */
7981 public PdfPagesTree (PdfCatalog pdfCatalog ) {
8082 this .document = pdfCatalog .getDocument ();
81- this .pageRefs = new ArrayList <>();
83+ this .pageRefs = new NullUnlimitedList <>();
8284 this .parents = new ArrayList <>();
83- this .pages = new ArrayList <>();
85+ this .pages = new NullUnlimitedList <>();
8486 if (pdfCatalog .getPdfObject ().containsKey (PdfName .Pages )) {
8587 PdfDictionary pages = pdfCatalog .getPdfObject ().getAsDictionary (PdfName .Pages );
8688 if (pages == null )
@@ -425,10 +427,9 @@ private void loadPage(int pageNum) {
425427 } else {
426428 int from = parent .getFrom ();
427429
428- // Possible exception in case kids.getSize() < parent.getCount().
429- // In any case parent.getCount() has higher priority.
430430 // 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 ++) {
432433 PdfObject kid = kids .get (i , false );
433434 if (kid instanceof PdfIndirectReference ) {
434435 pageRefs .set (from + i , (PdfIndirectReference ) kid );
@@ -486,4 +487,99 @@ private void correctPdfPagesFromProperty(int index, int correction) {
486487 }
487488 }
488489 }
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+ }
489585}
0 commit comments