001: /**
002: * ========================================
003: * JFreeReport : a free Java report library
004: * ========================================
005: *
006: * Project Info: http://reporting.pentaho.org/
007: *
008: * (C) Copyright 2000-2007, by Object Refinery Limited, 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: * $Id: Section.java 3048 2007-07-28 18:02:42Z tmorgner $
027: * ------------
028: * (C) Copyright 2000-2005, by Object Refinery Limited.
029: * (C) Copyright 2005-2007, by Pentaho Corporation.
030: */package org.jfree.report.structure;
031:
032: import java.util.ArrayList;
033: import java.util.Collection;
034: import java.util.Collections;
035: import java.util.Iterator;
036: import java.util.List;
037:
038: import org.jfree.report.flow.FlowControlOperation;
039: import org.jfree.util.ObjectUtilities;
040:
041: /**
042: * A report section is a collection of other elements and sections.
043: * <p/>
044: * This implementation is not synchronized, to take care that you externally
045: * synchronize it when using multiple threads to modify instances of this
046: * class.
047: * <p/>
048: * Trying to add a parent of an band as child to the band, will result in an
049: * exception.
050: * <p/>
051: * The attribute and style expressions added to the element are considered
052: * unnamed and stateless. To define a named, statefull state expression, one
053: * would create an ordinary named expression or function and would then
054: * reference that expression from within a style or attribute expression.
055: *
056: * @author Thomas Morgner
057: */
058: public class Section extends Element {
059: /**
060: * An empty array to prevent object creation.
061: */
062: private static final Node[] EMPTY_ARRAY = new Node[0];
063: private static final FlowControlOperation[] EMPTY_FLOWCONTROL = new FlowControlOperation[0];
064: /**
065: * All the elements for this band, stored by name.
066: */
067: private ArrayList allElements;
068:
069: /**
070: * Cached elements.
071: */
072: private transient Node[] allElementsCached;
073:
074: private ArrayList operationsBefore;
075: private ArrayList operationsAfter;
076: private transient FlowControlOperation[] operationsBeforeCached;
077: private transient FlowControlOperation[] operationsAfterCached;
078: private boolean repeat;
079:
080: /**
081: * Constructs a new band (initially empty).
082: */
083: public Section() {
084: setType("section");
085: allElements = new ArrayList();
086:
087: }
088:
089: /**
090: * Adds a report element to the band.
091: *
092: * @param element the element that should be added
093: * @throws NullPointerException if the given element is null
094: * @throws IllegalArgumentException if the position is invalid, either
095: * negative or greater than the number of
096: * elements in this band or if the given
097: * element is a parent of this element.
098: */
099: public void addNode(final Node element) {
100: addNode(allElements.size(), element);
101: }
102:
103: /**
104: * Adds a report element to the band. The element will be inserted at the
105: * specified position.
106: *
107: * @param position the position where to insert the element
108: * @param element the element that should be added
109: * @throws NullPointerException if the given element is null
110: * @throws IllegalArgumentException if the position is invalid, either
111: * negative or greater than the number of
112: * elements in this band or if the given
113: * element is a parent of this element.
114: */
115: public void addNode(final int position, final Node element) {
116: if (position < 0) {
117: throw new IllegalArgumentException("Position < 0");
118: }
119: if (position > allElements.size()) {
120: throw new IllegalArgumentException("Position < 0");
121: }
122: if (element == null) {
123: throw new NullPointerException(
124: "Band.addElement(...): element is null.");
125: }
126:
127: // check for component loops ...
128: if (element instanceof Section) {
129: Node band = this ;
130: while (band != null) {
131: if (band == element) {
132: throw new IllegalArgumentException(
133: "adding container's parent to itself");
134: }
135: band = band.getParent();
136: }
137: }
138:
139: // remove the element from its old parent ..
140: // this is the default AWT behaviour when adding Components to Container
141: final Node parent = element.getParent();
142: if (parent != null) {
143: if (parent == this ) {
144: // already a child, wont add twice ...
145: return;
146: }
147:
148: if (parent instanceof Section) {
149: final Section section = (Section) parent;
150: section.removeNode(element);
151: } else {
152: element.setParent(null);
153: }
154: }
155:
156: // add the element, update the childs Parent and the childs stylesheet.
157: allElements.add(position, element);
158: allElementsCached = null;
159:
160: // then add the parents, or the band's parent will be unregistered ..
161: element.setParent(this );
162: }
163:
164: /**
165: * Adds a collection of elements to the band.
166: *
167: * @param elements the element collection.
168: * @throws NullPointerException if one of the given elements is null
169: * @throws IllegalArgumentException if one of the given element is a parent of
170: * this element.
171: */
172: public void addNodes(final Collection elements) {
173: if (elements == null) {
174: throw new NullPointerException(
175: "Band.addElements(...): collection is null.");
176: }
177:
178: final Iterator iterator = elements.iterator();
179: while (iterator.hasNext()) {
180: final Element element = (Element) iterator.next();
181: addNode(element);
182: }
183: }
184:
185: /**
186: * Returns the first element in the list that is known by the given name.
187: *
188: * @param name the element name.
189: * @return the first element with the specified name, or <code>null</code> if
190: * there is no such element.
191: *
192: * @throws NullPointerException if the given name is null.
193: */
194: public Element getElementByName(final String name) {
195: if (name == null) {
196: throw new NullPointerException(
197: "Band.getElement(...): name is null.");
198: }
199:
200: final Node[] elements = getNodeArray();
201: final int elementsSize = elements.length;
202: for (int i = 0; i < elementsSize; i++) {
203: final Node e = elements[i];
204: if (e instanceof Element == false) {
205: continue;
206: }
207: final Element element = (Element) e;
208: final String elementName = element.getName();
209: if (elementName != null) {
210: if (elementName.equals(name)) {
211: return element;
212: }
213: }
214: }
215: return null;
216: }
217:
218: /**
219: * Removes an element from the band.
220: *
221: * @param e the element to be removed.
222: * @throws NullPointerException if the given element is null.
223: */
224: public void removeNode(final Node e) {
225: if (e == null) {
226: throw new NullPointerException();
227: }
228: if (e.getParent() != this ) {
229: // this is none of my childs, ignore the request ...
230: return;
231: }
232:
233: e.setParent(null);
234: allElements.remove(e);
235: allElementsCached = null;
236: }
237:
238: /**
239: * Returns all child-elements of this band as immutable list.
240: *
241: * @return an immutable list of all registered elements for this band.
242: *
243: * @deprecated use <code>getElementArray()</code> instead.
244: */
245: public List getNodes() {
246: return Collections.unmodifiableList(allElements);
247: }
248:
249: /**
250: * Returns the number of elements in this band.
251: *
252: * @return the number of elements of this band.
253: */
254: public int getNodeCount() {
255: return allElements.size();
256: }
257:
258: /**
259: * Returns an array of the elements in the band. If the band is empty, an
260: * empty array is returned.
261: * <p/>
262: * For performance reasons, a shared cached instance is returned. Do not
263: * modify the returned array or live with the consquences.
264: *
265: * @return the elements.
266: */
267: public Node[] getNodeArray() {
268: if (allElementsCached == null) {
269: if (allElements.isEmpty()) {
270: allElementsCached = Section.EMPTY_ARRAY;
271: } else {
272: Node[] elements = new Node[allElements.size()];
273: elements = (Node[]) allElements.toArray(elements);
274: allElementsCached = elements;
275: }
276: }
277: return allElementsCached;
278: }
279:
280: /**
281: * Returns the element stored add the given index.
282: *
283: * @param index the element position within this band
284: * @return the element
285: *
286: * @throws IndexOutOfBoundsException if the index is invalid.
287: */
288: public Node getNode(final int index) {
289: if (allElementsCached == null) {
290: if (allElements.isEmpty()) {
291: allElementsCached = Section.EMPTY_ARRAY;
292: } else {
293: Node[] elements = new Node[allElements.size()];
294: elements = (Node[]) allElements.toArray(elements);
295: allElementsCached = elements;
296: }
297: }
298: return allElementsCached[index];
299: }
300:
301: /**
302: * Returns a string representation of the band and all the elements it
303: * contains, useful mainly for debugging purposes.
304: *
305: * @return a string representation of this band.
306: */
307: public String toString() {
308: final StringBuffer b = new StringBuffer();
309: b.append(this .getClass().getName());
310: b.append("={name=\"");
311: b.append(getName());
312: b.append("\", namespace=\"");
313: b.append(getNamespace());
314: b.append("\", type=\"");
315: b.append(getType());
316: b.append("\", size=\"");
317: b.append(allElements.size());
318: b.append("\"}");
319: return b.toString();
320: }
321:
322: public FlowControlOperation[] getOperationBefore() {
323: if (operationsBefore == null) {
324: return Section.EMPTY_FLOWCONTROL;
325: }
326: if (operationsBeforeCached == null) {
327: operationsBeforeCached = (FlowControlOperation[]) operationsBefore
328: .toArray(Section.EMPTY_FLOWCONTROL);
329: }
330: return operationsBeforeCached;
331: }
332:
333: public FlowControlOperation[] getOperationAfter() {
334: if (operationsAfter == null) {
335: return Section.EMPTY_FLOWCONTROL;
336: }
337: if (operationsAfterCached == null) {
338: operationsAfterCached = (FlowControlOperation[]) operationsAfter
339: .toArray(Section.EMPTY_FLOWCONTROL);
340: }
341: return operationsAfterCached;
342: }
343:
344: public void setOperationBefore(final FlowControlOperation[] before) {
345: if (operationsBefore == null) {
346: operationsBefore = new ArrayList(before.length);
347: } else {
348: operationsBefore.clear();
349: operationsBefore.ensureCapacity(before.length);
350: }
351: for (int i = 0; i < before.length; i++) {
352: operationsBefore.add(before[i]);
353: }
354:
355: operationsBeforeCached = (FlowControlOperation[]) before
356: .clone();
357: }
358:
359: public void setOperationAfter(final FlowControlOperation[] ops) {
360: if (operationsAfter == null) {
361: operationsAfter = new ArrayList(ops.length);
362: } else {
363: operationsAfter.clear();
364: operationsAfter.ensureCapacity(ops.length);
365: }
366: for (int i = 0; i < ops.length; i++) {
367: operationsAfter.add(ops[i]);
368: }
369:
370: operationsAfterCached = (FlowControlOperation[]) ops.clone();
371: }
372:
373: public void addOperationAfter(final FlowControlOperation op) {
374: if (operationsAfter == null) {
375: operationsAfter = new ArrayList();
376: }
377: operationsAfter.add(op);
378: operationsAfterCached = null;
379: }
380:
381: public void addOperationBefore(final FlowControlOperation op) {
382: if (operationsBefore == null) {
383: operationsBefore = new ArrayList();
384: }
385: operationsBefore.add(op);
386: operationsBeforeCached = null;
387: }
388:
389: public boolean isRepeat() {
390: return repeat;
391: }
392:
393: public void setRepeat(final boolean repeat) {
394: this .repeat = repeat;
395: }
396:
397: public Element findFirstChild(final String uri, final String tagName) {
398: final Node[] nodes = getNodeArray();
399: for (int i = 0; i < nodes.length; i++) {
400: final Node node = nodes[i];
401: if (node instanceof Element == false) {
402: continue;
403: }
404: final Element e = (Element) node;
405: if (ObjectUtilities.equal(uri, e.getNamespace())
406: && ObjectUtilities.equal(tagName, e.getType())) {
407: return e;
408: }
409: }
410: return null;
411: }
412:
413: public Object clone() throws CloneNotSupportedException {
414: final Section section = (Section) super .clone();
415: if (operationsAfter != null) {
416: section.operationsAfter = (ArrayList) operationsAfter
417: .clone();
418: }
419: if (operationsBefore != null) {
420: section.operationsBefore = (ArrayList) operationsBefore
421: .clone();
422: }
423: section.allElements = (ArrayList) allElements.clone();
424: section.allElements.clear();
425: final int elementSize = allElements.size();
426: if (allElementsCached != null) {
427: section.allElementsCached = (Node[]) allElementsCached
428: .clone();
429: for (int i = 0; i < allElementsCached.length; i++) {
430: final Node eC = (Node) allElementsCached[i].clone();
431: section.allElements.add(eC);
432: section.allElementsCached[i] = eC;
433: eC.setParent(section);
434: }
435: } else {
436: for (int i = 0; i < elementSize; i++) {
437: final Node e = (Node) allElements.get(i);
438: final Node eC = (Node) e.clone();
439: section.allElements.add(eC);
440: eC.setParent(section);
441: }
442: }
443: return section;
444: }
445: }
|