1 /*
2 * $Id: Section.java 3373 2008-05-12 16:21:24Z xlv $
3 *
4 * Copyright 1999, 2000, 2001, 2002 by Bruno Lowagie.
5 *
6 * The contents of this file are subject to the Mozilla Public License Version 1.1
7 * (the "License"); you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at http://www.mozilla.org/MPL/
9 *
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the License.
13 *
14 * The Original Code is 'iText, a free JAVA-PDF library'.
15 *
16 * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
17 * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
18 * All Rights Reserved.
19 * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
20 * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
21 *
22 * Contributor(s): all the names of the contributors are added in the source code
23 * where applicable.
24 *
25 * Alternatively, the contents of this file may be used under the terms of the
26 * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
27 * provisions of LGPL are applicable instead of those above. If you wish to
28 * allow use of your version of this file only under the terms of the LGPL
29 * License and not to allow others to use your version of this file under
30 * the MPL, indicate your decision by deleting the provisions above and
31 * replace them with the notice and other provisions required by the LGPL.
32 * If you do not delete the provisions above, a recipient may use your version
33 * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
34 *
35 * This library is free software; you can redistribute it and/or modify it
36 * under the terms of the MPL as stated above or under the terms of the GNU
37 * Library General Public License as published by the Free Software Foundation;
38 * either version 2 of the License, or any later version.
39 *
40 * This library is distributed in the hope that it will be useful, but WITHOUT
41 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
42 * FOR A PARTICULAR PURPOSE. See the GNU Library general Public License for more
43 * details.
44 *
45 * If you didn't download this code from the following link, you should check if
46 * you aren't using an obsolete version:
47 * http://www.lowagie.com/iText/
48 */
49
50 package com.lowagie.text;
51
52 import java.util.ArrayList;
53 import java.util.Collection;
54 import java.util.Iterator;
55
56 /**
57 * A <CODE>Section</CODE> is a part of a <CODE>Document</CODE> containing
58 * other <CODE>Section</CODE>s, <CODE>Paragraph</CODE>s, <CODE>List</CODE>
59 * and/or <CODE>Table</CODE>s.
60 * <P>
61 * Remark: you can not construct a <CODE>Section</CODE> yourself.
62 * You will have to ask an instance of <CODE>Section</CODE> to the
63 * <CODE>Chapter</CODE> or <CODE>Section</CODE> to which you want to
64 * add the new <CODE>Section</CODE>.
65 * <P>
66 * Example:
67 * <BLOCKQUOTE><PRE>
68 * Paragraph title2 = new Paragraph("This is Chapter 2", FontFactory.getFont(FontFactory.HELVETICA, 18, Font.BOLDITALIC, new Color(0, 0, 255)));
69 * Chapter chapter2 = new Chapter(title2, 2);
70 * Paragraph someText = new Paragraph("This is some text");
71 * chapter2.add(someText);
72 * Paragraph title21 = new Paragraph("This is Section 1 in Chapter 2", FontFactory.getFont(FontFactory.HELVETICA, 16, Font.BOLD, new Color(255, 0, 0)));
73 * <STRONG>Section section1 = chapter2.addSection(title21);</STRONG>
74 * Paragraph someSectionText = new Paragraph("This is some silly paragraph in a chapter and/or section. It contains some text to test the functionality of Chapters and Section.");
75 * <STRONG>section1.add(someSectionText);</STRONG>
76 * Paragraph title211 = new Paragraph("This is SubSection 1 in Section 1 in Chapter 2", FontFactory.getFont(FontFactory.HELVETICA, 14, Font.BOLD, new Color(255, 0, 0)));
77 * <STRONG>Section section11 = section1.addSection(40, title211, 2);</STRONG>
78 * <STRONG>section11.add(someSectionText);</STRONG>
79 * </PRE></BLOCKQUOTE>
80 */
81
82 public class Section extends ArrayList implements TextElementArray, LargeElement {
83 // constant
84 /**
85 * A possible number style. The default number style: "1.2.3."
86 * @since iText 2.0.8
87 */
88 public static final int NUMBERSTYLE_DOTTED = 0;
89 /**
90 * A possible number style. For instance: "1.2.3"
91 * @since iText 2.0.8
92 */
93 public static final int NUMBERSTYLE_DOTTED_WITHOUT_FINAL_DOT = 1;
94
95 /** A serial version uid. */
96 private static final long serialVersionUID = 3324172577544748043L;
97
98 // member variables
99
100 /** The title of this section. */
101 protected Paragraph title;
102
103 /** The bookmark title if different from the content title */
104 protected String bookmarkTitle;
105
106 /** The number of sectionnumbers that has to be shown before the section title. */
107 protected int numberDepth;
108
109 /**
110 * The style for sectionnumbers.
111 * @since iText 2.0.8
112 */
113 protected int numberStyle = NUMBERSTYLE_DOTTED;
114
115 /** The indentation of this section on the left side. */
116 protected float indentationLeft;
117
118 /** The indentation of this section on the right side. */
119 protected float indentationRight;
120
121 /** The additional indentation of the content of this section. */
122 protected float indentation;
123
124 /** false if the bookmark children are not visible */
125 protected boolean bookmarkOpen = true;
126
127 /** true if the section has to trigger a new page */
128 protected boolean triggerNewPage = false;
129
130 /** This is the number of subsections. */
131 protected int subsections = 0;
132
133 /** This is the complete list of sectionnumbers of this section and the parents of this section. */
134 protected ArrayList numbers = null;
135
136 /**
137 * Indicates if the Section will be complete once added to the document.
138 * @since iText 2.0.8
139 */
140 protected boolean complete = true;
141
142 /**
143 * Indicates if the Section was added completely to the document.
144 * @since iText 2.0.8
145 */
146 protected boolean addedCompletely = false;
147
148 /**
149 * Indicates if this is the first time the section was added.
150 * @since iText 2.0.8
151 */
152 protected boolean notAddedYet = true;
153
154 // constructors
155
156 /**
157 * Constructs a new <CODE>Section</CODE>.
158 */
159 protected Section() {
160 title = new Paragraph();
161 numberDepth = 1;
162 }
163
164 /**
165 * Constructs a new <CODE>Section</CODE>.
166 *
167 * @param title a <CODE>Paragraph</CODE>
168 * @param numberDepth the numberDepth
169 */
170 protected Section(Paragraph title, int numberDepth) {
171 this.numberDepth = numberDepth;
172 this.title = title;
173 }
174
175 // implementation of the Element-methods
176
177 /**
178 * Processes the element by adding it (or the different parts) to an
179 * <CODE>ElementListener</CODE>.
180 *
181 * @param listener the <CODE>ElementListener</CODE>
182 * @return <CODE>true</CODE> if the element was processed successfully
183 */
184 public boolean process(ElementListener listener) {
185 try {
186 Element element;
187 for (Iterator i = iterator(); i.hasNext(); ) {
188 element = (Element)i.next();
189 listener.add(element);
190 }
191 return true;
192 }
193 catch(DocumentException de) {
194 return false;
195 }
196 }
197
198 /**
199 * Gets the type of the text element.
200 *
201 * @return a type
202 */
203 public int type() {
204 return Element.SECTION;
205 }
206
207 /**
208 * Checks if this object is a <CODE>Chapter</CODE>.
209 *
210 * @return <CODE>true</CODE> if it is a <CODE>Chapter</CODE>,
211 * <CODE>false</CODE> if it is a <CODE>Section</CODE>.
212 */
213 public boolean isChapter() {
214 return type() == Element.CHAPTER;
215 }
216
217 /**
218 * Checks if this object is a <CODE>Section</CODE>.
219 *
220 * @return <CODE>true</CODE> if it is a <CODE>Section</CODE>,
221 * <CODE>false</CODE> if it is a <CODE>Chapter</CODE>.
222 */
223 public boolean isSection() {
224 return type() == Element.SECTION;
225 }
226
227 /**
228 * Gets all the chunks in this element.
229 *
230 * @return an <CODE>ArrayList</CODE>
231 */
232 public ArrayList getChunks() {
233 ArrayList tmp = new ArrayList();
234 for (Iterator i = iterator(); i.hasNext(); ) {
235 tmp.addAll(((Element) i.next()).getChunks());
236 }
237 return tmp;
238 }
239
240 /**
241 * @see com.lowagie.text.Element#isContent()
242 * @since iText 2.0.8
243 */
244 public boolean isContent() {
245 return true;
246 }
247
248 /**
249 * @see com.lowagie.text.Element#isNestable()
250 * @since iText 2.0.8
251 */
252 public boolean isNestable() {
253 return false;
254 }
255
256 // overriding some of the ArrayList-methods
257
258 /**
259 * Adds a <CODE>Paragraph</CODE>, <CODE>List</CODE> or <CODE>Table</CODE>
260 * to this <CODE>Section</CODE>.
261 *
262 * @param index index at which the specified element is to be inserted
263 * @param o an object of type <CODE>Paragraph</CODE>, <CODE>List</CODE> or <CODE>Table</CODE>=
264 * @throws ClassCastException if the object is not a <CODE>Paragraph</CODE>, <CODE>List</CODE> or <CODE>Table</CODE>
265 */
266 public void add(int index, Object o) {
267 if (isAddedCompletely()) {
268 throw new IllegalStateException("This LargeElement has already been added to the Document.");
269 }
270 try {
271 Element element = (Element) o;
272 if (element.isNestable()) {
273 super.add(index, element);
274 }
275 else {
276 throw new ClassCastException("You can't add a " + element.getClass().getName() + " to a Section.");
277 }
278 }
279 catch(ClassCastException cce) {
280 throw new ClassCastException("Insertion of illegal Element: " + cce.getMessage());
281 }
282 }
283
284 /**
285 * Adds a <CODE>Paragraph</CODE>, <CODE>List</CODE>, <CODE>Table</CODE> or another <CODE>Section</CODE>
286 * to this <CODE>Section</CODE>.
287 *
288 * @param o an object of type <CODE>Paragraph</CODE>, <CODE>List</CODE>, <CODE>Table</CODE> or another <CODE>Section</CODE>
289 * @return a boolean
290 * @throws ClassCastException if the object is not a <CODE>Paragraph</CODE>, <CODE>List</CODE>, <CODE>Table</CODE> or <CODE>Section</CODE>
291 */
292 public boolean add(Object o) {
293 if (isAddedCompletely()) {
294 throw new IllegalStateException("This LargeElement has already been added to the Document.");
295 }
296 try {
297 Element element = (Element) o;
298 if (element.type() == Element.SECTION) {
299 Section section = (Section) o;
300 section.setNumbers(++subsections, numbers);
301 return super.add(section);
302 }
303 else if (o instanceof MarkedSection && ((MarkedObject)o).element.type() == Element.SECTION) {
304 MarkedSection mo = (MarkedSection)o;
305 Section section = (Section)mo.element;
306 section.setNumbers(++subsections, numbers);
307 return super.add(mo);
308 }
309 else if (element.isNestable()) {
310 return super.add(o);
311 }
312 else {
313 throw new ClassCastException("You can't add a " + element.getClass().getName() + " to a Section.");
314 }
315 }
316 catch(ClassCastException cce) {
317 throw new ClassCastException("Insertion of illegal Element: " + cce.getMessage());
318 }
319 }
320
321 /**
322 * Adds a collection of <CODE>Element</CODE>s
323 * to this <CODE>Section</CODE>.
324 *
325 * @param collection a collection of <CODE>Paragraph</CODE>s, <CODE>List</CODE>s and/or <CODE>Table</CODE>s
326 * @return <CODE>true</CODE> if the action succeeded, <CODE>false</CODE> if not.
327 * @throws ClassCastException if one of the objects isn't a <CODE>Paragraph</CODE>, <CODE>List</CODE>, <CODE>Table</CODE>
328 */
329 public boolean addAll(Collection collection) {
330 for (Iterator iterator = collection.iterator(); iterator.hasNext(); ) {
331 this.add(iterator.next());
332 }
333 return true;
334 }
335
336 // methods that return a Section
337
338 /**
339 * Creates a <CODE>Section</CODE>, adds it to this <CODE>Section</CODE> and returns it.
340 *
341 * @param indentation the indentation of the new section
342 * @param title the title of the new section
343 * @param numberDepth the numberDepth of the section
344 * @return a new Section object
345 */
346 public Section addSection(float indentation, Paragraph title, int numberDepth) {
347 if (isAddedCompletely()) {
348 throw new IllegalStateException("This LargeElement has already been added to the Document.");
349 }
350 Section section = new Section(title, numberDepth);
351 section.setIndentation(indentation);
352 add(section);
353 return section;
354 }
355
356 /**
357 * Creates a <CODE>Section</CODE>, adds it to this <CODE>Section</CODE> and returns it.
358 *
359 * @param indentation the indentation of the new section
360 * @param title the title of the new section
361 * @return a new Section object
362 */
363 public Section addSection(float indentation, Paragraph title) {
364 return addSection(indentation, title, numberDepth + 1);
365 }
366
367 /**
368 * Creates a <CODE>Section</CODE>, add it to this <CODE>Section</CODE> and returns it.
369 *
370 * @param title the title of the new section
371 * @param numberDepth the numberDepth of the section
372 * @return a new Section object
373 */
374 public Section addSection(Paragraph title, int numberDepth) {
375 return addSection(0, title, numberDepth);
376 }
377
378 /**
379 * Adds a marked section. For use in class MarkedSection only!
380 */
381 public MarkedSection addMarkedSection() {
382 MarkedSection section = new MarkedSection(new Section(null, numberDepth + 1));
383 add(section);
384 return section;
385 }
386
387 /**
388 * Creates a <CODE>Section</CODE>, adds it to this <CODE>Section</CODE> and returns it.
389 *
390 * @param title the title of the new section
391 * @return a new Section object
392 */
393 public Section addSection(Paragraph title) {
394 return addSection(0, title, numberDepth + 1);
395 }
396
397 /**
398 * Adds a <CODE>Section</CODE> to this <CODE>Section</CODE> and returns it.
399 *
400 * @param indentation the indentation of the new section
401 * @param title the title of the new section
402 * @param numberDepth the numberDepth of the section
403 * @return a new Section object
404 */
405 public Section addSection(float indentation, String title, int numberDepth) {
406 return addSection(indentation, new Paragraph(title), numberDepth);
407 }
408
409 /**
410 * Adds a <CODE>Section</CODE> to this <CODE>Section</CODE> and returns it.
411 *
412 * @param title the title of the new section
413 * @param numberDepth the numberDepth of the section
414 * @return a new Section object
415 */
416 public Section addSection(String title, int numberDepth) {
417 return addSection(new Paragraph(title), numberDepth);
418 }
419
420 /**
421 * Adds a <CODE>Section</CODE> to this <CODE>Section</CODE> and returns it.
422 *
423 * @param indentation the indentation of the new section
424 * @param title the title of the new section
425 * @return a new Section object
426 */
427 public Section addSection(float indentation, String title) {
428 return addSection(indentation, new Paragraph(title));
429 }
430
431 /**
432 * Adds a <CODE>Section</CODE> to this <CODE>Section</CODE> and returns it.
433 *
434 * @param title the title of the new section
435 * @return a new Section object
436 */
437 public Section addSection(String title) {
438 return addSection(new Paragraph(title));
439 }
440
441 // public methods
442
443 /**
444 * Sets the title of this section.
445 *
446 * @param title the new title
447 */
448 public void setTitle(Paragraph title) {
449 this.title = title;
450 }
451
452 /**
453 * Returns the title, preceded by a certain number of sectionnumbers.
454 *
455 * @return a <CODE>Paragraph</CODE>
456 */
457 public Paragraph getTitle() {
458 return constructTitle(title, numbers, numberDepth, numberStyle);
459 }
460
461 /**
462 * Constructs a Paragraph that will be used as title for a Section or Chapter.
463 * @param title the title of the section
464 * @param numbers a list of sectionnumbers
465 * @param numberDepth how many numbers have to be shown
466 * @param numberStyle the numbering style
467 * @return a Paragraph object
468 * @since iText 2.0.8
469 */
470 public static Paragraph constructTitle(Paragraph title, ArrayList numbers, int numberDepth, int numberStyle) {
471 if (title == null) {
472 return null;
473 }
474
475 int depth = Math.min(numbers.size(), numberDepth);
476 if (depth < 1) {
477 return title;
478 }
479 StringBuffer buf = new StringBuffer(" ");
480 for (int i = 0; i < depth; i++) {
481 buf.insert(0, ".");
482 buf.insert(0, ((Integer) numbers.get(i)).intValue());
483 }
484 if (numberStyle == NUMBERSTYLE_DOTTED_WITHOUT_FINAL_DOT) {
485 buf.deleteCharAt(buf.length() - 2);
486 }
487 Paragraph result = new Paragraph(title);
488 result.add(0, new Chunk(buf.toString(), title.getFont()));
489 return result;
490 }
491
492 /**
493 * Sets the depth of the sectionnumbers that will be shown preceding the title.
494 * <P>
495 * If the numberdepth is 0, the sections will not be numbered. If the numberdepth
496 * is 1, the section will be numbered with their own number. If the numberdepth is
497 * higher (for instance x > 1), the numbers of x - 1 parents will be shown.
498 *
499 * @param numberDepth the new numberDepth
500 */
501 public void setNumberDepth(int numberDepth) {
502 this.numberDepth = numberDepth;
503 }
504
505 /**
506 * Returns the numberdepth of this <CODE>Section</CODE>.
507 *
508 * @return the numberdepth
509 */
510 public int getNumberDepth() {
511 return numberDepth;
512 }
513
514 /**
515 * Sets the style for numbering sections.
516 * Possible values are NUMBERSTYLE_DOTTED: 1.2.3. (the default)
517 * or NUMBERSTYLE_DOTTED_WITHOUT_FINAL_DOT: 1.2.3
518 * @since iText 2.0.8
519 */
520 public void setNumberStyle(int numberStyle) {
521 this.numberStyle = numberStyle;
522 }
523
524 /**
525 * Gets the style used for numbering sections.
526 * @since iText 2.0.8
527 * @return a value corresponding with a numbering style
528 */
529 public int getNumberStyle() {
530 return numberStyle;
531 }
532
533 /**
534 * Sets the indentation of this <CODE>Section</CODE> on the left side.
535 *
536 * @param indentation the indentation
537 */
538 public void setIndentationLeft(float indentation) {
539 indentationLeft = indentation;
540 }
541
542 /**
543 * Returns the indentation of this <CODE>Section</CODE> on the left side.
544 *
545 * @return the indentation
546 */
547 public float getIndentationLeft() {
548 return indentationLeft;
549 }
550
551 /**
552 * Sets the indentation of this <CODE>Section</CODE> on the right side.
553 *
554 * @param indentation the indentation
555 */
556 public void setIndentationRight(float indentation) {
557 indentationRight = indentation;
558 }
559
560 /**
561 * Returns the indentation of this <CODE>Section</CODE> on the right side.
562 *
563 * @return the indentation
564 */
565 public float getIndentationRight() {
566 return indentationRight;
567 }
568
569 /**
570 * Sets the indentation of the content of this <CODE>Section</CODE>.
571 *
572 * @param indentation the indentation
573 */
574 public void setIndentation(float indentation) {
575 this.indentation = indentation;
576 }
577
578 /**
579 * Returns the indentation of the content of this <CODE>Section</CODE>.
580 *
581 * @return the indentation
582 */
583 public float getIndentation() {
584 return indentation;
585 }
586
587 /** Setter for property bookmarkOpen.
588 * @param bookmarkOpen false if the bookmark children are not
589 * visible.
590 */
591 public void setBookmarkOpen(boolean bookmarkOpen) {
592 this.bookmarkOpen = bookmarkOpen;
593 }
594
595 /**
596 * Getter for property bookmarkOpen.
597 * @return Value of property bookmarkOpen.
598 */
599 public boolean isBookmarkOpen() {
600 return bookmarkOpen;
601 }
602
603 /**
604 * Setter for property triggerNewPage.
605 * @param triggerNewPage true if a new page has to be triggered.
606 */
607 public void setTriggerNewPage(boolean triggerNewPage) {
608 this.triggerNewPage = triggerNewPage;
609 }
610
611 /**
612 * Getter for property bookmarkOpen.
613 * @return Value of property triggerNewPage.
614 */
615 public boolean isTriggerNewPage() {
616 return triggerNewPage && notAddedYet;
617 }
618
619 /**
620 * Sets the bookmark title. The bookmark title is the same as the section title but
621 * can be changed with this method.
622 * @param bookmarkTitle the bookmark title
623 */
624 public void setBookmarkTitle(String bookmarkTitle) {
625 this.bookmarkTitle = bookmarkTitle;
626 }
627
628 /**
629 * Gets the bookmark title.
630 * @return the bookmark title
631 */
632 public Paragraph getBookmarkTitle() {
633 if (bookmarkTitle == null)
634 return getTitle();
635 else
636 return new Paragraph(bookmarkTitle);
637 }
638
639 /**
640 * Changes the Chapter number.
641 */
642 public void setChapterNumber(int number) {
643 numbers.set(numbers.size() - 1, new Integer(number));
644 Object s;
645 for (Iterator i = iterator(); i.hasNext(); ) {
646 s = i.next();
647 if (s instanceof Section) {
648 ((Section)s).setChapterNumber(number);
649 }
650 }
651 }
652
653 /**
654 * Returns the depth of this section.
655 *
656 * @return the depth
657 */
658 public int getDepth() {
659 return numbers.size();
660 }
661
662 // private methods
663
664 /**
665 * Sets the number of this section.
666 *
667 * @param number the number of this section
668 * @param numbers an <CODE>ArrayList</CODE>, containing the numbers of the Parent
669 */
670 private void setNumbers(int number, ArrayList numbers) {
671 this.numbers = new ArrayList();
672 this.numbers.add(new Integer(number));
673 this.numbers.addAll(numbers);
674 }
675
676 /**
677 * Indicates if this is the first time the section is added.
678 * @since iText2.0.8
679 * @return true if the section wasn't added yet
680 */
681 public boolean isNotAddedYet() {
682 return notAddedYet;
683 }
684
685 /**
686 * Sets the indication if the section was already added to
687 * the document.
688 * @since iText2.0.8
689 * @param notAddedYet
690 */
691 public void setNotAddedYet(boolean notAddedYet) {
692 this.notAddedYet = notAddedYet;
693 }
694
695 /**
696 * @since iText 2.0.8
697 */
698 protected boolean isAddedCompletely() {
699 return addedCompletely;
700 }
701
702 /**
703 * @since iText 2.0.8
704 */
705 protected void setAddedCompletely(boolean addedCompletely) {
706 this.addedCompletely = addedCompletely;
707 }
708
709 /**
710 * @since iText 2.0.8
711 * @see com.lowagie.text.LargeElement#flushContent()
712 */
713 public void flushContent() {
714 setNotAddedYet(false);
715 title = null;
716 Element element;
717 for (Iterator i = iterator(); i.hasNext(); ) {
718 element = (Element)i.next();
719 if (element instanceof Section) {
720 Section s = (Section)element;
721 if (!s.isComplete() && size() == 1) {
722 s.flushContent();
723 return;
724 }
725 else {
726 s.setAddedCompletely(true);
727 }
728 }
729 i.remove();
730 }
731 }
732
733 /**
734 * @since iText 2.0.8
735 * @see com.lowagie.text.LargeElement#isComplete()
736 */
737 public boolean isComplete() {
738 return complete;
739 }
740
741 /**
742 * @since iText 2.0.8
743 * @see com.lowagie.text.LargeElement#setComplete(boolean)
744 */
745 public void setComplete(boolean complete) {
746 this.complete = complete;
747 }
748
749 /**
750 * Adds a new page to the section.
751 * @since 2.1.1
752 */
753 public void newPage() {
754 this.add(Chunk.NEXTPAGE);
755 }
756 }