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