001: /**
002: * ===========================================
003: * JFreeReport : a free Java reporting library
004: * ===========================================
005: *
006: * Project Info: http://reporting.pentaho.org/
007: *
008: * (C) Copyright 2001-2007, by Object Refinery Ltd, Pentaho Corporation and Contributors.
009: *
010: * This library is free software; you can redistribute it and/or modify it under the terms
011: * of the GNU Lesser General Public License as published by the Free Software Foundation;
012: * either version 2.1 of the License, or (at your option) any later version.
013: *
014: * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
015: * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
016: * See the GNU Lesser General Public License for more details.
017: *
018: * You should have received a copy of the GNU Lesser General Public License along with this
019: * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
020: * Boston, MA 02111-1307, USA.
021: *
022: * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
023: * in the United States and other countries.]
024: *
025: * ------------
026: * Band.java
027: * ------------
028: * (C) Copyright 2001-2007, by Object Refinery Ltd, Pentaho Corporation and Contributors.
029: */package org.jfree.report;
030:
031: import java.util.ArrayList;
032: import java.util.Collection;
033: import java.util.Collections;
034: import java.util.Iterator;
035: import java.util.List;
036:
037: import org.jfree.report.style.BandDefaultStyleSheet;
038: import org.jfree.report.style.BandStyleKeys;
039: import org.jfree.report.style.ElementDefaultStyleSheet;
040:
041: /**
042: * A report band is a collection of other elements and bands, similiar to an AWT-Container.
043: * <p/>
044: * This implementation is not synchronized, to take care that you externally synchronize it when using multiple threads
045: * to modify instances of this class.
046: * <p/>
047: * Trying to add a parent of an band as child to the band, will result in an exception.
048: *
049: * @author David Gilbert
050: * @author Thomas Morgner
051: */
052: public class Band extends Element {
053: /**
054: * An empty array to prevent object creation.
055: */
056: private static final Element[] EMPTY_ARRAY = new Element[0];
057:
058: /**
059: * The defined content type for the band.
060: *
061: * @deprecated The content-type is no longer used.
062: */
063: public static final String CONTENT_TYPE = "X-container";
064:
065: /**
066: * All the elements for this band.
067: */
068: private ArrayList allElements;
069:
070: /**
071: * Cached elements.
072: */
073: private transient Element[] allElementsCached;
074:
075: /**
076: * The prefix for anonymous bands, bands without an userdefined name.
077: */
078: public static final String ANONYMOUS_BAND_PREFIX = "anonymousBand@";
079:
080: /**
081: * Constructs a new band (initially empty).
082: */
083: public Band() {
084: setName(Band.ANONYMOUS_BAND_PREFIX
085: + System.identityHashCode(this ));
086: allElements = new ArrayList();
087: }
088:
089: /**
090: * Constructs a new band with the given pagebreak attributes. Pagebreak attributes have no effect on subbands.
091: *
092: * @param pagebreakAfter defines, whether a pagebreak should be done after that band was printed.
093: * @param pagebreakBefore defines, whether a pagebreak should be done before that band gets printed.
094: */
095: public Band(final boolean pagebreakBefore,
096: final boolean pagebreakAfter) {
097: this ();
098: if (pagebreakBefore) {
099: setPagebreakBeforePrint(pagebreakBefore);
100: }
101: if (pagebreakAfter) {
102: setPagebreakAfterPrint(pagebreakAfter);
103: }
104: }
105:
106: /**
107: * Returns the global stylesheet for all bands. This stylesheet provides the predefined default values for some of the
108: * stylekeys.
109: *
110: * @return the global default stylesheet.
111: */
112: protected ElementDefaultStyleSheet createGlobalDefaultStyle() {
113: return BandDefaultStyleSheet.getBandDefaultStyle();
114: }
115:
116: /**
117: * Adds a report element to the band.
118: *
119: * @param element the element that should be added
120: * @throws NullPointerException if the given element is null
121: * @throws IllegalArgumentException if the position is invalid, either negative or greater than the number of elements
122: * in this band or if the given element is a parent of this element.
123: */
124: public void addElement(final Element element) {
125: addElement(allElements.size(), element);
126: }
127:
128: /**
129: * Adds a report element to the band. The element will be inserted at the specified position.
130: *
131: * @param position the position where to insert the element
132: * @param element the element that should be added
133: * @throws NullPointerException if the given element is null
134: * @throws IllegalArgumentException if the position is invalid, either negative or greater than the number of elements
135: * in this band or if the given element is a parent of this element.
136: */
137: public void addElement(final int position, final Element element) {
138: if (position < 0) {
139: throw new IllegalArgumentException("Position < 0");
140: }
141: if (position > allElements.size()) {
142: throw new IllegalArgumentException("Position < 0");
143: }
144: if (element == null) {
145: throw new NullPointerException(
146: "Band.addElement(...): element is null.");
147: }
148:
149: // check for component loops ...
150: if (element instanceof Band) {
151: Band band = this ;
152: while (band != null) {
153: if (band == element) {
154: throw new IllegalArgumentException(
155: "adding container's parent to itself");
156: }
157: band = band.getParent();
158: }
159: }
160:
161: // remove the element from its old parent ..
162: // this is the default AWT behaviour when adding Components to Container
163: if (element.getParent() != null) {
164: if (element.getParent() == this ) {
165: // already a child, wont add twice ...
166: return;
167: }
168:
169: element.getParent().removeElement(element);
170: }
171:
172: // add the element, update the childs Parent and the childs stylesheet.
173: allElements.add(position, element);
174: allElementsCached = null;
175:
176: // then add the parents, or the band's parent will be unregistered ..
177: element.setParent(this );
178: }
179:
180: /**
181: * Adds a collection of elements to the band.
182: *
183: * @param elements the element collection.
184: * @throws NullPointerException if one of the given elements is null
185: * @throws IllegalArgumentException if one of the given element is a parent of this element.
186: */
187: public void addElements(final Collection elements) {
188: if (elements == null) {
189: throw new NullPointerException(
190: "Band.addElements(...): collection is null.");
191: }
192:
193: final Iterator iterator = elements.iterator();
194: while (iterator.hasNext()) {
195: final Element element = (Element) iterator.next();
196: addElement(element);
197: }
198: }
199:
200: /**
201: * Returns the first element in the list that is known by the given name. Functions should use
202: * {@link org.jfree.report.function.FunctionUtilities#findAllElements(Band, String)} or
203: * {@link org.jfree.report.function.FunctionUtilities#findElement(Band, String)} instead.
204: *
205: * @param name the element name.
206: * @return the first element with the specified name, or <code>null</code> if there is no such element.
207: * @throws NullPointerException if the given name is null.
208: */
209: public Element getElement(final String name) {
210: if (name == null) {
211: throw new NullPointerException(
212: "Band.getElement(...): name is null.");
213: }
214:
215: final Element[] elements = getElementArray();
216: final int elementsSize = elements.length;
217: for (int i = 0; i < elementsSize; i++) {
218: final Element e = elements[i];
219: final String elementName = e.getName();
220: if (elementName != null) {
221: if (elementName.equals(name)) {
222: return e;
223: }
224: }
225: }
226: return null;
227: }
228:
229: /**
230: * Removes an element from the band.
231: *
232: * @param e the element to be removed.
233: * @throws NullPointerException if the given element is null.
234: */
235: public void removeElement(final Element e) {
236: if (e == null) {
237: throw new NullPointerException();
238: }
239: if (e.getParent() != this ) {
240: // this is none of my childs, ignore the request ...
241: return;
242: }
243:
244: e.setParent(null);
245: allElements.remove(e);
246: allElementsCached = null;
247: }
248:
249: /**
250: * Returns all child-elements of this band as immutable list.
251: *
252: * @return an immutable list of all registered elements for this band.
253: * @deprecated use <code>getElementArray()</code> instead.
254: */
255: public List getElements() {
256: return Collections.unmodifiableList(allElements);
257: }
258:
259: /**
260: * Returns the number of elements in this band.
261: *
262: * @return the number of elements of this band.
263: */
264: public int getElementCount() {
265: return allElements.size();
266: }
267:
268: /**
269: * Returns an array of the elements in the band. If the band is empty, an empty array is returned.
270: * <p/>
271: * For performance reasons, a shared cached instance is returned. Do not modify the returned array or live with the
272: * consquences.
273: *
274: * @return the elements.
275: */
276: public Element[] getElementArray() {
277: if (allElementsCached == null) {
278: if (allElements.isEmpty()) {
279: allElementsCached = Band.EMPTY_ARRAY;
280: } else {
281: Element[] elements = new Element[allElements.size()];
282: elements = (Element[]) allElements.toArray(elements);
283: allElementsCached = elements;
284: }
285: }
286: return allElementsCached;
287: }
288:
289: /**
290: * Returns the element stored add the given index.
291: *
292: * @param index the element position within this band
293: * @return the element
294: * @throws IndexOutOfBoundsException if the index is invalid.
295: */
296: public Element getElement(final int index) {
297: if (allElementsCached == null) {
298: if (allElements.isEmpty()) {
299: allElementsCached = Band.EMPTY_ARRAY;
300: } else {
301: Element[] elements = new Element[allElements.size()];
302: elements = (Element[]) allElements.toArray(elements);
303: allElementsCached = elements;
304: }
305: }
306: return allElementsCached[index];
307: }
308:
309: /**
310: * Returns a string representation of the band, useful mainly for debugging
311: * purposes.
312: *
313: * @return a string representation of this band.
314: */
315: public String toString() {
316: final StringBuffer b = new StringBuffer();
317: b.append(this .getClass().getName());
318: b.append("={name=\"");
319: b.append(getName());
320: b.append("\", size=\"");
321: b.append(allElements.size());
322: b.append("\"}");
323: return b.toString();
324: }
325:
326: /**
327: * Clones this band and all elements contained in this band. After the cloning the band is no longer connected to a
328: * report definition.
329: *
330: * @return the clone of this band.
331: * @throws CloneNotSupportedException if this band or an element contained in this band does not support cloning.
332: */
333: public Object clone() throws CloneNotSupportedException {
334: final Band b = (Band) super .clone();
335:
336: final int elementSize = allElements.size();
337: b.allElements = new ArrayList(elementSize);
338: b.allElementsCached = new Element[elementSize];
339:
340: if (allElementsCached != null) {
341: final int length = allElementsCached.length;
342: for (int i = 0; i < length; i++) {
343: final Element eC = (Element) allElementsCached[i]
344: .clone();
345: b.allElements.add(eC);
346: b.allElementsCached[i] = eC;
347: eC.setParent(b);
348: }
349: } else {
350: for (int i = 0; i < elementSize; i++) {
351: final Element e = (Element) allElements.get(i);
352: final Element eC = (Element) e.clone();
353: b.allElements.add(eC);
354: b.allElementsCached[i] = eC;
355: eC.setParent(b);
356: }
357: }
358: return b;
359: }
360:
361: /**
362: * Returns the content type of the element. For bands, the content type is by default "X-container".
363: *
364: * @return the content type
365: * @deprecated we no longer use the content-type.
366: */
367: public String getContentType() {
368: return Band.CONTENT_TYPE;
369: }
370:
371: /**
372: * Returns, whether the page layout manager should perform a pagebreak before this page is printed. This will have no
373: * effect on empty pages or if the band is no root-level band.
374: *
375: * @return true, if to force a pagebreak before this band is printed, false otherwise
376: */
377: public boolean isPagebreakBeforePrint() {
378: return getStyle().getBooleanStyleProperty(
379: BandStyleKeys.PAGEBREAK_BEFORE);
380: }
381:
382: /**
383: * Defines, whether the page layout manager should perform a pagebreak before this page is printed. This will have no
384: * effect on empty pages or if the band is no root-level band.
385: *
386: * @param pagebreakBeforePrint set to true, if to force a pagebreak before this band is printed, false otherwise
387: */
388: public void setPagebreakBeforePrint(
389: final boolean pagebreakBeforePrint) {
390: getStyle().setBooleanStyleProperty(
391: BandStyleKeys.PAGEBREAK_BEFORE, pagebreakBeforePrint);
392: }
393:
394: /**
395: * Returns, whether the page layout manager should perform a pagebreak before this page is printed. This will have no
396: * effect on empty pages or if the band is no root-level band.
397: *
398: * @return true, if to force a pagebreak before this band is printed, false otherwise
399: */
400: public boolean isPagebreakAfterPrint() {
401: return getStyle().getBooleanStyleProperty(
402: BandStyleKeys.PAGEBREAK_AFTER);
403: }
404:
405: /**
406: * Defines, whether the page layout manager should perform a pagebreak before this page is printed. This will have no
407: * effect on empty pages or if the band is no root-level band.
408: *
409: * @param pagebreakAfterPrint set to true, if to force a pagebreak before this band is printed, false otherwise
410: */
411: public void setPagebreakAfterPrint(final boolean pagebreakAfterPrint) {
412: getStyle().setBooleanStyleProperty(
413: BandStyleKeys.PAGEBREAK_AFTER, pagebreakAfterPrint);
414: }
415:
416: /**
417: * Assigns the report definition to this band.
418: *
419: * @param reportDefinition the report definition or null, if the band is not part of a valid report definition.
420: */
421: protected void setReportDefinition(
422: final ReportDefinition reportDefinition) {
423: super .setReportDefinition(reportDefinition);
424: final Element[] elements = getElementArray();
425: final int length = elements.length;
426: for (int i = 0; i < length; i++) {
427: elements[i].setReportDefinition(reportDefinition);
428: }
429: }
430: }
|