001/*
002 * $Id: List.java 4885 2011-05-29 13:49:53Z blowagie $
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;
045
046import java.util.ArrayList;
047
048import com.itextpdf.text.api.Indentable;
049import com.itextpdf.text.factories.RomanAlphabetFactory;
050
051/**
052 * A <CODE>List</CODE> contains several <CODE>ListItem</CODE>s.
053 * <P>
054 * <B>Example 1:</B>
055 * <BLOCKQUOTE><PRE>
056 * <STRONG>List list = new List(true, 20);</STRONG>
057 * <STRONG>list.add(new ListItem("First line"));</STRONG>
058 * <STRONG>list.add(new ListItem("The second line is longer to see what happens once the end of the line is reached. Will it start on a new line?"));</STRONG>
059 * <STRONG>list.add(new ListItem("Third line"));</STRONG>
060 * </PRE></BLOCKQUOTE>
061 *
062 * The result of this code looks like this:
063 *      <OL>
064 *              <LI>
065 *                      First line
066 *              </LI>
067 *              <LI>
068 *                      The second line is longer to see what happens once the end of the line is reached. Will it start on a new line?
069 *              </LI>
070 *              <LI>
071 *                      Third line
072 *              </LI>
073 *      </OL>
074 *
075 * <B>Example 2:</B>
076 * <BLOCKQUOTE><PRE>
077 * <STRONG>List overview = new List(false, 10);</STRONG>
078 * <STRONG>overview.add(new ListItem("This is an item"));</STRONG>
079 * <STRONG>overview.add("This is another item");</STRONG>
080 * </PRE></BLOCKQUOTE>
081 *
082 * The result of this code looks like this:
083 *      <UL>
084 *              <LI>
085 *                      This is an item
086 *              </LI>
087 *              <LI>
088 *                      This is another item
089 *              </LI>
090 *      </UL>
091 *
092 * @see         Element
093 * @see         ListItem
094 */
095
096public class List implements TextElementArray, Indentable {
097
098    // constants
099
100        /** a possible value for the numbered parameter */
101        public static final boolean ORDERED = true;
102        /** a possible value for the numbered parameter */
103        public static final boolean UNORDERED = false;
104        /** a possible value for the lettered parameter */
105        public static final boolean NUMERICAL = false;
106        /** a possible value for the lettered parameter */
107        public static final boolean ALPHABETICAL = true;
108        /** a possible value for the lettered parameter */
109        public static final boolean UPPERCASE = false;
110        /** a possible value for the lettered parameter */
111        public static final boolean LOWERCASE = true;
112
113    // member variables
114
115        /** This is the <CODE>ArrayList</CODE> containing the different <CODE>ListItem</CODE>s. */
116    protected ArrayList<Element> list = new ArrayList<Element>();
117
118    /** Indicates if the list has to be numbered. */
119    protected boolean numbered = false;
120    /** Indicates if the listsymbols are numerical or alphabetical. */
121    protected boolean lettered = false;
122    /** Indicates if the listsymbols are lowercase or uppercase. */
123    protected boolean lowercase = false;
124    /** Indicates if the indentation has to be set automatically. */
125    protected boolean autoindent = false;
126    /** Indicates if the indentation of all the items has to be aligned. */
127    protected boolean alignindent = false;
128
129    /** This variable indicates the first number of a numbered list. */
130    protected int first = 1;
131    /** This is the listsymbol of a list that is not numbered. */
132    protected Chunk symbol = new Chunk("- ");
133    /**
134     * In case you are using numbered/lettered lists, this String is added before the number/letter.
135     * @since   iText 2.1.1
136     */
137    protected String preSymbol = "";
138    /**
139     * In case you are using numbered/lettered lists, this String is added after the number/letter.
140     * @since   iText 2.1.1
141     */
142    protected String postSymbol = ". ";
143
144    /** The indentation of this list on the left side. */
145    protected float indentationLeft = 0;
146    /** The indentation of this list on the right side. */
147    protected float indentationRight = 0;
148    /** The indentation of the listitems. */
149    protected float symbolIndent = 0;
150
151    // constructors
152
153    /** Constructs a <CODE>List</CODE>. */
154    public List() {
155        this(false, false);
156    }
157
158    /**
159     * Constructs a <CODE>List</CODE> with a specific symbol indentation.
160     * @param   symbolIndent    the symbol indentation
161     * @since   iText 2.0.8
162     */
163    public List(final float symbolIndent) {
164        this.symbolIndent = symbolIndent;
165    }
166
167    /**
168     * Constructs a <CODE>List</CODE>.
169     * @param   numbered                a boolean
170     */
171    public List(final boolean numbered) {
172        this(numbered, false);
173    }
174
175    /**
176     * Constructs a <CODE>List</CODE>.
177     * @param   numbered                a boolean
178     * @param lettered has the list to be 'numbered' with letters
179     */
180    public List(final boolean numbered, final boolean lettered) {
181        this.numbered = numbered;
182        this.lettered = lettered;
183        this.autoindent = true;
184        this.alignindent = true;
185    }
186
187    /**
188     * Constructs a <CODE>List</CODE>.
189     * <P>
190     * Remark: the parameter <VAR>symbolIndent</VAR> is important for instance when
191     * generating PDF-documents; it indicates the indentation of the listsymbol.
192     * It is not important for HTML-documents.
193     *
194     * @param   numbered                a boolean
195     * @param   symbolIndent    the indentation that has to be used for the listsymbol
196     */
197    public List(final boolean numbered, final float symbolIndent) {
198        this(numbered, false, symbolIndent);
199    }
200
201    /**
202     * Creates a list
203     * @param numbered has the list to be numbered?
204     * @param lettered has the list to be 'numbered' with letters
205     * @param symbolIndent the indentation of the symbol
206     */
207    public List(final boolean numbered, final boolean lettered, final float symbolIndent) {
208        this.numbered = numbered;
209        this.lettered = lettered;
210        this.symbolIndent = symbolIndent;
211    }
212
213    // implementation of the Element-methods
214
215    /**
216     * Processes the element by adding it (or the different parts) to an
217     * <CODE>ElementListener</CODE>.
218     *
219     * @param   listener        an <CODE>ElementListener</CODE>
220     * @return  <CODE>true</CODE> if the element was processed successfully
221     */
222    public boolean process(final ElementListener listener) {
223        try {
224            for (Element element : list) {
225                listener.add(element);
226            }
227            return true;
228        }
229        catch(DocumentException de) {
230            return false;
231        }
232    }
233
234    /**
235     * Gets the type of the text element.
236     *
237     * @return  a type
238     */
239    public int type() {
240        return Element.LIST;
241    }
242
243    /**
244     * Gets all the chunks in this element.
245     *
246     * @return  an <CODE>ArrayList</CODE>
247     */
248    public java.util.List<Chunk> getChunks() {
249        java.util.List<Chunk> tmp = new ArrayList<Chunk>();
250        for (Element element : list) {
251            tmp.addAll(element.getChunks());
252        }
253        return tmp;
254    }
255
256    // methods to set the membervariables
257
258    /**
259     * Adds a <CODE>String</CODE> to the <CODE>List</CODE>.
260     *
261     * @param   s               the element to add.
262     * @return true if adding the object succeeded
263     * @since 5.0.1
264     */
265    public boolean add(final String s) {
266        if (s != null) {
267            return this.add(new ListItem(s));
268        }
269        return false;
270    }
271
272    /**
273     * Adds an <CODE>Element</CODE> to the <CODE>List</CODE>.
274     *
275     * @param   o               the element to add.
276     * @return true if adding the object succeeded
277     * @since 5.0.1 (signature changed to use Element)
278     */
279    public boolean add(final Element o) {
280        if (o instanceof ListItem) {
281            ListItem item = (ListItem) o;
282            if (numbered || lettered) {
283                Chunk chunk = new Chunk(preSymbol, symbol.getFont());
284                chunk.setAttributes(symbol.getAttributes());
285                int index = first + list.size();
286                if ( lettered )
287                    chunk.append(RomanAlphabetFactory.getString(index, lowercase));
288                else
289                    chunk.append(String.valueOf(index));
290                chunk.append(postSymbol);
291                item.setListSymbol(chunk);
292            }
293            else {
294                item.setListSymbol(symbol);
295            }
296            item.setIndentationLeft(symbolIndent, autoindent);
297            item.setIndentationRight(0);
298            return list.add(item);
299        }
300        else if (o instanceof List) {
301            List nested = (List) o;
302            nested.setIndentationLeft(nested.getIndentationLeft() + symbolIndent);
303            first--;
304            return list.add(nested);
305        }
306        return false;
307    }
308
309    // extra methods
310
311        /** Makes sure all the items in the list have the same indentation. */
312    public void normalizeIndentation() {
313        float max = 0;
314        for (Element o : list) {
315            if (o instanceof ListItem) {
316                max = Math.max(max, ((ListItem)o).getIndentationLeft());
317            }
318        }
319        for (Element o : list) {
320            if (o instanceof ListItem) {
321                ((ListItem)o).setIndentationLeft(max);
322            }
323        }
324    }
325
326    // setters
327
328        /**
329         * @param numbered the numbered to set
330         */
331        public void setNumbered(final boolean numbered) {
332                this.numbered = numbered;
333        }
334
335        /**
336         * @param lettered the lettered to set
337         */
338        public void setLettered(final boolean lettered) {
339                this.lettered = lettered;
340        }
341
342        /**
343         * @param uppercase the uppercase to set
344         */
345        public void setLowercase(final boolean uppercase) {
346                this.lowercase = uppercase;
347        }
348
349        /**
350         * @param autoindent the autoindent to set
351         */
352        public void setAutoindent(final boolean autoindent) {
353                this.autoindent = autoindent;
354        }
355        /**
356         * @param alignindent the alignindent to set
357         */
358        public void setAlignindent(final boolean alignindent) {
359                this.alignindent = alignindent;
360        }
361
362    /**
363     * Sets the number that has to come first in the list.
364     *
365     * @param   first           a number
366     */
367    public void setFirst(final int first) {
368        this.first = first;
369    }
370
371    /**
372     * Sets the listsymbol.
373     *
374     * @param   symbol          a <CODE>Chunk</CODE>
375     */
376    public void setListSymbol(final Chunk symbol) {
377        this.symbol = symbol;
378    }
379
380    /**
381     * Sets the listsymbol.
382     * <P>
383     * This is a shortcut for <CODE>setListSymbol(Chunk symbol)</CODE>.
384     *
385     * @param   symbol          a <CODE>String</CODE>
386     */
387    public void setListSymbol(final String symbol) {
388        this.symbol = new Chunk(symbol);
389    }
390
391    /**
392     * Sets the indentation of this paragraph on the left side.
393     *
394     * @param   indentation             the new indentation
395     */
396    public void setIndentationLeft(final float indentation) {
397        this.indentationLeft = indentation;
398    }
399
400    /**
401     * Sets the indentation of this paragraph on the right side.
402     *
403     * @param   indentation             the new indentation
404     */
405    public void setIndentationRight(final float indentation) {
406        this.indentationRight = indentation;
407    }
408
409        /**
410         * @param symbolIndent the symbolIndent to set
411         */
412        public void setSymbolIndent(final float symbolIndent) {
413                this.symbolIndent = symbolIndent;
414        }
415
416    // methods to retrieve information
417
418    /**
419     * Gets all the items in the list.
420     *
421     * @return  an <CODE>ArrayList</CODE> containing <CODE>ListItem</CODE>s.
422     */
423    public ArrayList<Element> getItems() {
424        return list;
425    }
426
427    /**
428     * Gets the size of the list.
429     *
430     * @return  a <CODE>size</CODE>
431     */
432    public int size() {
433        return list.size();
434    }
435
436    /**
437     * Returns <CODE>true</CODE> if the list is empty.
438     *
439     * @return <CODE>true</CODE> if the list is empty
440     */
441    public boolean isEmpty() {
442        return list.isEmpty();
443    }
444
445    /**
446     * Gets the leading of the first listitem.
447     *
448     * @return  a <CODE>leading</CODE>
449     */
450    public float getTotalLeading() {
451        if (list.size() < 1) {
452            return -1;
453        }
454        ListItem item = (ListItem) list.get(0);
455        return item.getTotalLeading();
456    }
457
458    // getters
459
460    /**
461     * Checks if the list is numbered.
462     * @return  <CODE>true</CODE> if the list is numbered, <CODE>false</CODE> otherwise.
463     */
464
465    public boolean isNumbered() {
466        return numbered;
467    }
468
469    /**
470     * Checks if the list is lettered.
471     * @return  <CODE>true</CODE> if the list is lettered, <CODE>false</CODE> otherwise.
472     */
473    public boolean isLettered() {
474        return lettered;
475    }
476
477    /**
478     * Checks if the list lettering is lowercase.
479     * @return  <CODE>true</CODE> if it is lowercase, <CODE>false</CODE> otherwise.
480     */
481    public boolean isLowercase() {
482        return lowercase;
483    }
484
485    /**
486     * Checks if the indentation of list items is done automatically.
487         * @return the autoindent
488         */
489        public boolean isAutoindent() {
490                return autoindent;
491        }
492
493        /**
494         * Checks if all the listitems should be aligned.
495         * @return the alignindent
496         */
497        public boolean isAlignindent() {
498                return alignindent;
499        }
500
501        /**
502     * Gets the first number        .
503     * @return a number
504         */
505        public int getFirst() {
506                return first;
507        }
508
509        /**
510     * Gets the Chunk containing the symbol.
511     * @return a Chunk with a symbol
512         */
513        public Chunk getSymbol() {
514                return symbol;
515        }
516
517        /**
518     * Gets the indentation of this paragraph on the left side.
519     * @return  the indentation
520         */
521        public float getIndentationLeft() {
522                return indentationLeft;
523        }
524
525        /**
526     * Gets the indentation of this paragraph on the right side.
527     * @return  the indentation
528         */
529        public float getIndentationRight() {
530                return indentationRight;
531        }
532
533        /**
534     * Gets the symbol indentation.
535     * @return the symbol indentation
536         */
537        public float getSymbolIndent() {
538                return symbolIndent;
539        }
540        /**
541         * @see com.itextpdf.text.Element#isContent()
542         * @since       iText 2.0.8
543         */
544        public boolean isContent() {
545                return true;
546        }
547
548        /**
549         * @see com.itextpdf.text.Element#isNestable()
550         * @since       iText 2.0.8
551         */
552        public boolean isNestable() {
553                return true;
554        }
555
556        /**
557         * Returns the String that is after a number or letter in the list symbol.
558         * @return      the String that is after a number or letter in the list symbol
559         * @since       iText 2.1.1
560         */
561        public String getPostSymbol() {
562                return postSymbol;
563        }
564
565        /**
566         * Sets the String that has to be added after a number or letter in the list symbol.
567         * @since       iText 2.1.1
568         * @param       postSymbol the String that has to be added after a number or letter in the list symbol.
569         */
570        public void setPostSymbol(final String postSymbol) {
571                this.postSymbol = postSymbol;
572        }
573
574        /**
575         * Returns the String that is before a number or letter in the list symbol.
576         * @return      the String that is before a number or letter in the list symbol
577         * @since       iText 2.1.1
578         */
579        public String getPreSymbol() {
580                return preSymbol;
581        }
582
583        /**
584         * Sets the String that has to be added before a number or letter in the list symbol.
585         * @since       iText 2.1.1
586         * @param       preSymbol the String that has to be added before a number or letter in the list symbol.
587         */
588        public void setPreSymbol(final String preSymbol) {
589                this.preSymbol = preSymbol;
590        }
591
592}