001/*
002 * $Id: PdfArray.java 4925 2011-07-08 10:51:25Z redlab_b $
003 *
004 * This file is part of the iText (R) project.
005 * Copyright (c) 1998-2011 1T3XT BVBA
006 * Authors: Bruno Lowagie, Paulo Soares, et al.
007 *
008 * This program is free software; you can redistribute it and/or modify
009 * it under the terms of the GNU Affero General Public License version 3
010 * as published by the Free Software Foundation with the addition of the
011 * following permission added to Section 15 as permitted in Section 7(a):
012 * FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY 1T3XT,
013 * 1T3XT DISCLAIMS THE WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
014 *
015 * This program is distributed in the hope that it will be useful, but
016 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
017 * or FITNESS FOR A PARTICULAR PURPOSE.
018 * See the GNU Affero General Public License for more details.
019 * You should have received a copy of the GNU Affero General Public License
020 * along with this program; if not, see http://www.gnu.org/licenses or write to
021 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
022 * Boston, MA, 02110-1301 USA, or download the license from the following URL:
023 * http://itextpdf.com/terms-of-use/
024 *
025 * The interactive user interfaces in modified source and object code versions
026 * of this program must display Appropriate Legal Notices, as required under
027 * Section 5 of the GNU Affero General Public License.
028 *
029 * In accordance with Section 7(b) of the GNU Affero General Public License,
030 * a covered work must retain the producer line in every PDF that is created
031 * or manipulated using iText.
032 *
033 * You can be released from the requirements of the license by purchasing
034 * a commercial license. Buying such a license is mandatory as soon as you
035 * develop commercial activities involving the iText software without
036 * disclosing the source code of your own applications.
037 * These activities include: offering paid services to customers as an ASP,
038 * serving PDFs on the fly in a web application, shipping iText with a closed
039 * source product.
040 *
041 * For more information, please contact iText Software Corp. at this
042 * address: sales@itextpdf.com
043 */
044package com.itextpdf.text.pdf;
045
046import java.io.IOException;
047import java.io.OutputStream;
048import java.util.ArrayList;
049import java.util.Iterator;
050import java.util.List;
051import java.util.ListIterator;
052
053/**
054 * <CODE>PdfArray</CODE> is the PDF Array object.
055 * <P>
056 * An array is a sequence of PDF objects. An array may contain a mixture of
057 * object types.
058 * An array is written as a left square bracket ([), followed by a sequence of
059 * objects, followed by a right square bracket (]).<BR>
060 * This object is described in the 'Portable Document Format Reference Manual
061 * version 1.7' section 3.2.5 (page 58).
062 *
063 * @see         PdfObject
064 */
065public class PdfArray extends PdfObject implements Iterable<PdfObject> {
066
067        // CLASS VARIABLES
068
069        /** this is the actual array of PdfObjects */
070    protected ArrayList<PdfObject> arrayList;
071
072    // constructors
073
074    /**
075     * Constructs an empty <CODE>PdfArray</CODE>-object.
076     */
077    public PdfArray() {
078        super(ARRAY);
079        arrayList = new ArrayList<PdfObject>();
080    }
081
082    /**
083     * Constructs an <CODE>PdfArray</CODE>-object, containing 1
084     * <CODE>PdfObject</CODE>.
085     *
086     * @param   object          a <CODE>PdfObject</CODE> that has to be added to the array
087     */
088    public PdfArray(final PdfObject object) {
089        super(ARRAY);
090        arrayList = new ArrayList<PdfObject>();
091        arrayList.add(object);
092    }
093
094    /**
095     * Constructs a <CODE>PdfArray</CODE>-object, containing all
096     * <CODE>float</CODE> values in a specified array.
097     *
098     * The <CODE>float</CODE> values are internally converted to
099     * <CODE>PdfNumber</CODE> objects.
100     *
101     * @param values    an array of <CODE>float</CODE> values to be added
102     */
103    public PdfArray(final float values[]) {
104        super(ARRAY);
105        arrayList = new ArrayList<PdfObject>();
106        add(values);
107    }
108
109    /**
110     * Constructs a <CODE>PdfArray</CODE>-object, containing all
111     * <CODE>int</CODE> values in a specified array.
112     *
113     * The <CODE>int</CODE> values are internally converted to
114     * <CODE>PdfNumber</CODE> objects.
115     *
116     * @param values    an array of <CODE>int</CODE> values to be added
117     */
118    public PdfArray(final int values[]) {
119        super(ARRAY);
120        arrayList = new ArrayList<PdfObject>();
121        add(values);
122    }
123
124    /**
125     * Constructs a <CODE>PdfArray</CODE>, containing all elements of a
126     * specified <CODE>ArrayList</CODE>.
127     *
128     * @param l    an <CODE>ArrayList</CODE> with <CODE>PdfObject</CODE>s to be
129     *   added to the array
130     * @throws ClassCastException if the <CODE>ArrayList</CODE> contains
131     *   something that isn't a <CODE>PdfObject</CODE>
132     * @since 2.1.3
133     */
134    public PdfArray(final List<PdfObject> l) {
135        this();
136        for (PdfObject element : l)
137            add(element);
138    }
139
140    /**
141     * Constructs an <CODE>PdfArray</CODE>-object, containing all
142     * <CODE>PdfObject</CODE>s in a specified <CODE>PdfArray</CODE>.
143     *
144     * @param array    a <CODE>PdfArray</CODE> to be added to the array
145     */
146    public PdfArray(final PdfArray array) {
147        super(ARRAY);
148        arrayList = new ArrayList<PdfObject>(array.arrayList);
149    }
150
151    // METHODS OVERRIDING SOME PDFOBJECT METHODS
152
153    /**
154     * Writes the PDF representation of this <CODE>PdfArray</CODE> as an array
155     * of <CODE>byte</CODE> to the specified <CODE>OutputStream</CODE>.
156     *
157     * @param writer for backwards compatibility
158     * @param os the <CODE>OutputStream</CODE> to write the bytes to.
159     */
160    @Override
161    public void toPdf(final PdfWriter writer, final OutputStream os) throws IOException {
162        os.write('[');
163
164        Iterator<PdfObject> i = arrayList.iterator();
165        PdfObject object;
166        int type = 0;
167        if (i.hasNext()) {
168            object = i.next();
169            if (object == null)
170                object = PdfNull.PDFNULL;
171            object.toPdf(writer, os);
172        }
173        while (i.hasNext()) {
174            object = i.next();
175            if (object == null)
176                object = PdfNull.PDFNULL;
177            type = object.type();
178            if (type != PdfObject.ARRAY && type != PdfObject.DICTIONARY && type != PdfObject.NAME && type != PdfObject.STRING)
179                os.write(' ');
180            object.toPdf(writer, os);
181        }
182        os.write(']');
183    }
184
185    /**
186     * Returns a string representation of this <CODE>PdfArray</CODE>.
187     *
188     * The string representation consists of a list of all
189     * <CODE>PdfObject</CODE>s contained in this <CODE>PdfArray</CODE>,
190     * enclosed in square brackets ("[]"). Adjacent elements are separated
191     * by the characters ", " (comma and space).
192     *
193     * @return the string representation of this <CODE>PdfArray</CODE>
194     */
195    @Override
196    public String toString() {
197        return arrayList.toString();
198    }
199
200    // ARRAY CONTENT METHODS
201
202    /**
203     * Overwrites a specified location of the array, returning the previous
204     * value
205     *
206     * @param idx The index of the element to be overwritten
207     * @param obj new value for the specified index
208     * @throws IndexOutOfBoundsException if the specified position doesn't exist
209     * @return the previous value
210     * @since 2.1.5
211     */
212    public PdfObject set(final int idx, final PdfObject obj) {
213        return arrayList.set(idx, obj);
214    }
215
216    /**
217     * Remove the element at the specified position from the array.
218     *
219     * Shifts any subsequent elements to the left (subtracts one from their
220     * indices).
221     *
222     * @param idx The index of the element to be removed.
223     * @throws IndexOutOfBoundsException the specified position doesn't exist
224     * @since 2.1.5
225     */
226    public PdfObject remove(final int idx) {
227        return arrayList.remove(idx);
228    }
229
230    /**
231     * Get the internal arrayList for this PdfArray.  Not Recommended.
232     *
233     * @deprecated
234     * @return the internal ArrayList.  Naughty Naughty.
235     */
236    @Deprecated
237    public ArrayList<PdfObject> getArrayList() {
238        return arrayList;
239    }
240
241    /**
242     * Returns the number of entries in the array.
243     *
244     * @return          the size of the ArrayList
245     */
246    public int size() {
247        return arrayList.size();
248    }
249
250    /**
251     * Returns <CODE>true</CODE> if the array is empty.
252     *
253     * @return <CODE>true</CODE> if the array is empty
254     * @since 2.1.5
255     */
256    public boolean isEmpty() {
257        return arrayList.isEmpty();
258    }
259
260    /**
261     * Adds a <CODE>PdfObject</CODE> to the end of the <CODE>PdfArray</CODE>.
262     *
263     * The <CODE>PdfObject</CODE> will be the last element.
264     *
265     * @param object <CODE>PdfObject</CODE> to add
266     * @return always <CODE>true</CODE>
267     */
268    public boolean add(final PdfObject object) {
269        return arrayList.add(object);
270    }
271
272    /**
273     * Adds an array of <CODE>float</CODE> values to end of the
274     * <CODE>PdfArray</CODE>.
275     *
276     * The values will be the last elements.
277     * The <CODE>float</CODE> values are internally converted to
278     * <CODE>PdfNumber</CODE> objects.
279     *
280     * @param values An array of <CODE>float</CODE> values to add
281     * @return always <CODE>true</CODE>
282     */
283    public boolean add(final float values[]) {
284        for (int k = 0; k < values.length; ++k)
285            arrayList.add(new PdfNumber(values[k]));
286        return true;
287    }
288
289    /**
290     * Adds an array of <CODE>int</CODE> values to end of the <CODE>PdfArray</CODE>.
291     *
292     * The values will be the last elements.
293     * The <CODE>int</CODE> values are internally converted to
294     * <CODE>PdfNumber</CODE> objects.
295     *
296     * @param values An array of <CODE>int</CODE> values to add
297     * @return always <CODE>true</CODE>
298     */
299    public boolean add(final int values[]) {
300        for (int k = 0; k < values.length; ++k)
301            arrayList.add(new PdfNumber(values[k]));
302        return true;
303    }
304
305    /**
306     * Inserts the specified element at the specified position.
307     *
308     * Shifts the element currently at that position (if any) and
309     * any subsequent elements to the right (adds one to their indices).
310     *
311     * @param index The index at which the specified element is to be inserted
312     * @param element The element to be inserted
313     * @throws IndexOutOfBoundsException if the specified index is larger than the
314     *   last position currently set, plus 1.
315     * @since 2.1.5
316     */
317    public void add(final int index, final PdfObject element) {
318        arrayList.add(index, element);
319    }
320
321    /**
322     * Inserts a <CODE>PdfObject</CODE> at the beginning of the
323     * <CODE>PdfArray</CODE>.
324     *
325     * The <CODE>PdfObject</CODE> will be the first element, any other elements
326     * will be shifted to the right (adds one to their indices).
327     *
328     * @param object The <CODE>PdfObject</CODE> to add
329     */
330    public void addFirst(final PdfObject object) {
331        arrayList.add(0, object);
332    }
333
334    /**
335     * Checks if the <CODE>PdfArray</CODE> already contains a certain
336     * <CODE>PdfObject</CODE>.
337     *
338     * @param object The <CODE>PdfObject</CODE> to check
339     * @return <CODE>true</CODE>
340     */
341    public boolean contains(final PdfObject object) {
342        return arrayList.contains(object);
343    }
344
345    /**
346     * Returns the list iterator for the array.
347     *
348     * @return a ListIterator
349     */
350    public ListIterator<PdfObject> listIterator() {
351        return arrayList.listIterator();
352    }
353
354    /**
355     * Returns the <CODE>PdfObject</CODE> with the specified index.
356     *
357     * A possible indirect references is not resolved, so the returned
358     * <CODE>PdfObject</CODE> may be either a direct object or an indirect
359     * reference, depending on how the object is stored in the
360     * <CODE>PdfArray</CODE>.
361     *
362     * @param idx The index of the <CODE>PdfObject</CODE> to be returned
363     * @return A <CODE>PdfObject</CODE>
364     */
365    public PdfObject getPdfObject(final int idx) {
366        return arrayList.get(idx);
367    }
368
369    /**
370     * Returns the <CODE>PdfObject</CODE> with the specified index, resolving
371     * a possible indirect reference to a direct object.
372     *
373     * Thus this method will never return a <CODE>PdfIndirectReference</CODE>
374     * object.
375     *
376     * @param idx The index of the <CODE>PdfObject</CODE> to be returned
377     * @return A direct <CODE>PdfObject</CODE> or <CODE>null</CODE>
378     */
379    public PdfObject getDirectObject(final int idx) {
380        return PdfReader.getPdfObject(getPdfObject(idx));
381    }
382
383    // DOWNCASTING GETTERS
384    // @author Mark A Storer (2/17/06)
385
386    /**
387     * Returns a <CODE>PdfObject</CODE> as a <CODE>PdfDictionary</CODE>,
388     * resolving indirect references.
389     *
390     * The object corresponding to the specified index is retrieved and
391     * resolvedto a direct object.
392     * If it is a <CODE>PdfDictionary</CODE>, it is cast down and returned as such.
393     * Otherwise <CODE>null</CODE> is returned.
394     *
395     * @param idx The index of the <CODE>PdfObject</CODE> to be returned
396     * @return the corresponding <CODE>PdfDictionary</CODE> object,
397     *   or <CODE>null</CODE>
398     */
399    public PdfDictionary getAsDict(final int idx) {
400        PdfDictionary dict = null;
401        PdfObject orig = getDirectObject(idx);
402        if (orig != null && orig.isDictionary())
403            dict = (PdfDictionary) orig;
404        return dict;
405    }
406
407    /**
408     * Returns a <CODE>PdfObject</CODE> as a <CODE>PdfArray</CODE>,
409     * resolving indirect references.
410     *
411     * The object corresponding to the specified index is retrieved and
412     * resolved to a direct object.
413     * If it is a <CODE>PdfArray</CODE>, it is cast down and returned as such.
414     * Otherwise <CODE>null</CODE> is returned.
415     *
416     * @param idx The index of the <CODE>PdfObject</CODE> to be returned
417     * @return the corresponding <CODE>PdfArray</CODE> object,
418     *   or <CODE>null</CODE>
419     */
420    public PdfArray getAsArray(final int idx) {
421        PdfArray array = null;
422        PdfObject orig = getDirectObject(idx);
423        if (orig != null && orig.isArray())
424            array = (PdfArray) orig;
425        return array;
426    }
427
428    /**
429     * Returns a <CODE>PdfObject</CODE> as a <CODE>PdfStream</CODE>,
430     * resolving indirect references.
431     *
432     * The object corresponding to the specified index is retrieved and
433     * resolved to a direct object.
434     * If it is a <CODE>PdfStream</CODE>, it is cast down and returned as such.
435     * Otherwise <CODE>null</CODE> is returned.
436     *
437     * @param idx The index of the <CODE>PdfObject</CODE> to be returned
438     * @return the corresponding <CODE>PdfStream</CODE> object,
439     *   or <CODE>null</CODE>
440     */
441    public PdfStream getAsStream(final int idx) {
442        PdfStream stream = null;
443        PdfObject orig = getDirectObject(idx);
444        if (orig != null && orig.isStream())
445            stream = (PdfStream) orig;
446        return stream;
447    }
448
449    /**
450     * Returns a <CODE>PdfObject</CODE> as a <CODE>PdfString</CODE>,
451     * resolving indirect references.
452     *
453     * The object corresponding to the specified index is retrieved and
454     * resolved to a direct object.
455     * If it is a <CODE>PdfString</CODE>, it is cast down and returned as such.
456     * Otherwise <CODE>null</CODE> is returned.
457     *
458     * @param idx The index of the <CODE>PdfObject</CODE> to be returned
459     * @return the corresponding <CODE>PdfString</CODE> object,
460     *   or <CODE>null</CODE>
461     */
462    public PdfString getAsString(final int idx) {
463        PdfString string = null;
464        PdfObject orig = getDirectObject(idx);
465        if (orig != null && orig.isString())
466            string = (PdfString) orig;
467        return string;
468    }
469
470    /**
471     * Returns a <CODE>PdfObject</CODE> as a <CODE>PdfNumber</CODE>,
472     * resolving indirect references.
473     *
474     * The object corresponding to the specified index is retrieved and
475     * resolved to a direct object.
476     * If it is a <CODE>PdfNumber</CODE>, it is cast down and returned as such.
477     * Otherwise <CODE>null</CODE> is returned.
478     *
479     * @param idx The index of the <CODE>PdfObject</CODE> to be returned
480     * @return the corresponding <CODE>PdfNumber</CODE> object,
481     *   or <CODE>null</CODE>
482     */
483    public PdfNumber getAsNumber(final int idx) {
484        PdfNumber number = null;
485        PdfObject orig = getDirectObject(idx);
486        if (orig != null && orig.isNumber())
487            number = (PdfNumber) orig;
488        return number;
489    }
490
491    /**
492     * Returns a <CODE>PdfObject</CODE> as a <CODE>PdfName</CODE>,
493     * resolving indirect references.
494     *
495     * The object corresponding to the specified index is retrieved and
496     * resolved to a direct object.
497     * If it is a <CODE>PdfName</CODE>, it is cast down and returned as such.
498     * Otherwise <CODE>null</CODE> is returned.
499     *
500     * @param idx The index of the <CODE>PdfObject</CODE> to be returned
501     * @return the corresponding <CODE>PdfName</CODE> object,
502     *   or <CODE>null</CODE>
503     */
504    public PdfName getAsName(final int idx) {
505        PdfName name = null;
506        PdfObject orig = getDirectObject(idx);
507        if (orig != null && orig.isName())
508            name = (PdfName) orig;
509        return name;
510    }
511
512    /**
513     * Returns a <CODE>PdfObject</CODE> as a <CODE>PdfBoolean</CODE>,
514     * resolving indirect references.
515     *
516     * The object corresponding to the specified index is retrieved and
517     * resolved to a direct object.
518     * If it is a <CODE>PdfBoolean</CODE>, it is cast down and returned as
519     * such. Otherwise <CODE>null</CODE> is returned.
520     *
521     * @param idx The index of the <CODE>PdfObject</CODE> to be returned
522     * @return the corresponding <CODE>PdfBoolean</CODE> object,
523     *   or <CODE>null</CODE>
524     */
525    public PdfBoolean getAsBoolean(final int idx) {
526        PdfBoolean bool = null;
527        PdfObject orig = getDirectObject(idx);
528        if (orig != null && orig.isBoolean())
529            bool = (PdfBoolean) orig;
530        return bool;
531    }
532
533    /**
534     * Returns a <CODE>PdfObject</CODE> as a <CODE>PdfIndirectReference</CODE>.
535     *
536     * The object corresponding to the specified index is retrieved.
537     * If it is a <CODE>PdfIndirectReference</CODE>, it is cast down and
538     * returned as such. Otherwise <CODE>null</CODE> is returned.
539     *
540     * @param idx The index of the <CODE>PdfObject</CODE> to be returned
541     * @return the corresponding <CODE>PdfIndirectReference</CODE> object,
542     *   or <CODE>null</CODE>
543     */
544    public PdfIndirectReference getAsIndirectObject(final int idx) {
545        PdfIndirectReference ref = null;
546        PdfObject orig = getPdfObject(idx); // not getDirect this time.
547        if (orig != null && orig.isIndirect())
548            ref = (PdfIndirectReference) orig;
549        return ref;
550    }
551
552        /**
553         * @return an iterator that iterates over the {@link PdfObject}s in this PdfArray.
554         */
555        public Iterator<PdfObject> iterator() {
556                return arrayList.iterator();
557        }
558}