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 2791 2007-05-14 08:55:52Z taqua $ 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 }