Source for org.jfree.report.structure.Section

   1: /**
   2:  * ========================================
   3:  * JFreeReport : a free Java report library
   4:  * ========================================
   5:  *
   6:  * Project Info:  http://reporting.pentaho.org/
   7:  *
   8:  * (C) Copyright 2000-2007, by Object Refinery Limited, Pentaho Corporation and Contributors.
   9:  *
  10:  * This library is free software; you can redistribute it and/or modify it under the terms
  11:  * of the GNU Lesser General Public License as published by the Free Software Foundation;
  12:  * either version 2.1 of the License, or (at your option) any later version.
  13:  *
  14:  * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
  15:  * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  16:  * See the GNU Lesser General Public License for more details.
  17:  *
  18:  * You should have received a copy of the GNU Lesser General Public License along with this
  19:  * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
  20:  * Boston, MA 02111-1307, USA.
  21:  *
  22:  * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
  23:  * in the United States and other countries.]
  24:  *
  25:  * ------------
  26:  * $Id: Section.java 2791 2007-05-14 08:55:52Z taqua $
  27:  * ------------
  28:  * (C) Copyright 2000-2005, by Object Refinery Limited.
  29:  * (C) Copyright 2005-2007, by Pentaho Corporation.
  30:  */
  31: 
  32: package org.jfree.report.structure;
  33: 
  34: import java.util.ArrayList;
  35: import java.util.Collection;
  36: import java.util.Collections;
  37: import java.util.Iterator;
  38: import java.util.List;
  39: 
  40: import org.jfree.report.flow.FlowControlOperation;
  41: import org.jfree.util.ObjectUtilities;
  42: 
  43: /**
  44:  * A report section is a collection of other elements and sections.
  45:  * <p/>
  46:  * This implementation is not synchronized, to take care that you externally
  47:  * synchronize it when using multiple threads to modify instances of this
  48:  * class.
  49:  * <p/>
  50:  * Trying to add a parent of an band as child to the band, will result in an
  51:  * exception.
  52:  * <p/>
  53:  * The attribute and style expressions added to the element are considered
  54:  * unnamed and stateless. To define a named, statefull state expression, one
  55:  * would create an ordinary named expression or function and would then
  56:  * reference that expression from within a style or attribute expression.
  57:  *
  58:  * @author Thomas Morgner
  59:  */
  60: public class Section extends Element
  61: {
  62:   /**
  63:    * An empty array to prevent object creation.
  64:    */
  65:   private static final Node[] EMPTY_ARRAY = new Node[0];
  66:   private static final FlowControlOperation[] EMPTY_FLOWCONTROL = new FlowControlOperation[0];
  67:   /**
  68:    * All the elements for this band, stored by name.
  69:    */
  70:   private ArrayList allElements;
  71: 
  72:   /**
  73:    * Cached elements.
  74:    */
  75:   private transient Node[] allElementsCached;
  76: 
  77:   private ArrayList operationsBefore;
  78:   private ArrayList operationsAfter;
  79:   private transient FlowControlOperation[] operationsBeforeCached;
  80:   private transient FlowControlOperation[] operationsAfterCached;
  81:   private boolean repeat;
  82: 
  83:   /**
  84:    * Constructs a new band (initially empty).
  85:    */
  86:   public Section()
  87:   {
  88:     setType("section");
  89:     allElements = new ArrayList();
  90: 
  91:   }
  92: 
  93:   /**
  94:    * Adds a report element to the band.
  95:    *
  96:    * @param element the element that should be added
  97:    * @throws NullPointerException     if the given element is null
  98:    * @throws IllegalArgumentException if the position is invalid, either
  99:    *                                  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: }