001: /*
002: * ============================================================================
003: * GNU Lesser General Public License
004: * ============================================================================
005: *
006: * JasperReports - Free Java report-generating library.
007: * Copyright (C) 2001-2006 JasperSoft Corporation http://www.jaspersoft.com
008: *
009: * This library is free software; you can redistribute it and/or
010: * modify it under the terms of the GNU Lesser General Public
011: * License as published by the Free Software Foundation; either
012: * 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,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
017: * Lesser General Public License for more details.
018: *
019: * You should have received a copy of the GNU Lesser General Public
020: * License along with this library; if not, write to the Free Software
021: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
022: *
023: * JasperSoft Corporation
024: * 303 Second Street, Suite 450 North
025: * San Francisco, CA 94107
026: * http://www.jaspersoft.com
027: */
028:
029: /*
030: * Contributors:
031: * Greg Hilton
032: */
033:
034: package net.sf.jasperreports.engine.export;
035:
036: import java.awt.Color;
037: import java.util.ArrayList;
038: import java.util.Collections;
039: import java.util.HashMap;
040: import java.util.Iterator;
041: import java.util.List;
042: import java.util.Map;
043:
044: import net.sf.jasperreports.engine.JRBox;
045: import net.sf.jasperreports.engine.JRElement;
046: import net.sf.jasperreports.engine.JRPrintElement;
047: import net.sf.jasperreports.engine.JRPrintFrame;
048: import net.sf.jasperreports.engine.JRPrintPage;
049: import net.sf.jasperreports.engine.base.JRBaseBox;
050: import net.sf.jasperreports.engine.base.JRBasePrintFrame;
051:
052: /**
053: * Utility class used by grid exporters to create a grid for page layout.
054: *
055: * @author Lucian Chirita (lucianc@users.sourceforge.net)
056: * @version $Id: JRGridLayout.java 1806 2007-08-06 12:36:33Z teodord $
057: */
058: public class JRGridLayout {
059: private final int USAGE_NOT_EMPTY = 1;
060: private final int USAGE_SPANNED = 2;
061:
062: private final ExporterNature nature;
063:
064: private final int width;
065: private final int height;
066: private final int offsetX;
067: private final int offsetY;
068: private final String address;
069:
070: private List xCuts;
071: private List yCuts;
072: private JRExporterGridCell[][] grid;
073: private int[] rowUsage;
074: private int[] colUsage;
075:
076: private Map boxesCache;
077:
078: private int virtualFrameIndex = 0;
079:
080: /**
081: * Constructor.
082: *
083: * @param elements the elements that should arranged in a grid
084: * @param width the width available for the grid
085: * @param height the height available for the grid
086: * @param offsetX horizontal element position offset
087: * @param offsetY vertical element position offset
088: */
089: public JRGridLayout(ExporterNature nature, List elements,
090: int width, int height, int offsetX, int offsetY) {
091: this (nature, elements, width, height, offsetX, offsetY, null //xCuts
092: );
093: }
094:
095: /**
096: * Constructor.
097: *
098: * @param elements the elements that should arranged in a grid
099: * @param width the width available for the grid
100: * @param height the height available for the grid
101: * @param offsetX horizontal element position offset
102: * @param offsetY vertical element position offset
103: * @param xCuts An optional list of pre-calculated X cuts.
104: */
105: public JRGridLayout(ExporterNature nature, List elements,
106: int width, int height, int offsetX, int offsetY, List xCuts) {
107: this .nature = nature;
108: this .height = height;
109: this .width = width;
110: this .offsetX = offsetX;
111: this .offsetY = offsetY;
112: this .address = null;
113: this .xCuts = xCuts;
114:
115: boxesCache = new HashMap();
116:
117: virtualFrameIndex = elements.size();
118:
119: layoutGrid(createWrappers(elements, address));
120:
121: if (nature.isSplitSharedRowSpan()) {
122: splitSharedRowSpanIntoNestedGrids();
123: }
124: }
125:
126: /**
127: * Constructor.
128: *
129: * @param wrappers the element wrappers that should arranged in a grid
130: * @param width the width available for the grid
131: * @param height the height available for the grid
132: * @param offsetX horizontal element position offset
133: * @param offsetY vertical element position offset
134: * @param address element address
135: */
136: protected JRGridLayout(ExporterNature nature,
137: ElementWrapper[] wrappers, int width, int height,
138: int offsetX, int offsetY, String address) {
139: this .nature = nature;
140: this .height = height;
141: this .width = width;
142: this .offsetX = offsetX;
143: this .offsetY = offsetY;
144: this .address = address;
145:
146: boxesCache = new HashMap();
147:
148: layoutGrid(wrappers);
149:
150: if (nature.isSplitSharedRowSpan()) {
151: splitSharedRowSpanIntoNestedGrids();
152: }
153: }
154:
155: /**
156: *
157: */
158: private void createNestedGrid(int row1, int col1, int row2, int col2) {
159: JRBasePrintFrame frame = new JRBasePrintFrame(null);
160: List wrappers = new ArrayList();
161:
162: for (int row = row1; row < row2; row++) {
163: for (int col = col1; col < col2; col++) {
164: JRExporterGridCell gridCell = grid[row][col];
165: grid[row][col] = JRExporterGridCell.OCCUPIED_CELL;
166: ElementWrapper wrapper = gridCell.getWrapper();
167: if (gridCell != JRExporterGridCell.OCCUPIED_CELL
168: && wrapper != null) {
169: wrappers.add(wrapper);
170: frame.addElement(wrapper.getElement());//FIXMEODT do we need this?
171: }
172: }
173: }
174:
175: frame.setWidth(((Integer) xCuts.get(col2)).intValue()
176: - ((Integer) xCuts.get(col1)).intValue());
177: frame.setHeight(((Integer) yCuts.get(row2)).intValue()
178: - ((Integer) yCuts.get(row1)).intValue());
179:
180: String virtualAddress = (address == null ? "" : address + "_")
181: + getNextVirtualFrameIndex();
182:
183: JRExporterGridCell gridCell = new JRExporterGridCell(
184: new ElementWrapper(frame, virtualAddress, null), frame
185: .getWidth(), frame.getHeight(), col2 - col1,
186: row2 - row1);
187:
188: gridCell.setLayout(new JRGridLayout(nature,
189: (ElementWrapper[]) wrappers
190: .toArray(new ElementWrapper[wrappers.size()]),
191: frame.getWidth(), frame.getHeight(), offsetX
192: - ((Integer) xCuts.get(col1)).intValue(),
193: offsetY - ((Integer) yCuts.get(row1)).intValue(),
194: virtualAddress));
195:
196: grid[row1][col1] = gridCell;
197: }
198:
199: /**
200: * Constructs the element grid.
201: */
202: protected void layoutGrid(ElementWrapper[] wrappers) {
203: boolean createXCuts = (xCuts == null);
204: if (createXCuts) {
205: xCuts = new SortedList();
206: xCuts.add(new Integer(0));
207: xCuts.add(new Integer(width));
208: }
209:
210: yCuts = new SortedList();
211: yCuts.add(new Integer(0));
212:
213: createCuts(wrappers, offsetX, offsetY, createXCuts);
214:
215: if (!nature.isIgnoreLastRow())
216: yCuts.add(new Integer(height));
217:
218: int colCount = xCuts.size() - 1;
219: int rowCount = yCuts.size() - 1;
220:
221: grid = new JRExporterGridCell[rowCount][colCount];
222: rowUsage = new int[rowCount];
223: colUsage = new int[colCount];
224:
225: for (int row = 0; row < rowCount; row++) {
226: for (int col = 0; col < colCount; col++) {
227: grid[row][col] = new JRExporterGridCell(
228: null,
229: ((Integer) xCuts.get(col + 1)).intValue()
230: - ((Integer) xCuts.get(col)).intValue(),
231: ((Integer) yCuts.get(row + 1)).intValue()
232: - ((Integer) yCuts.get(row)).intValue(),
233: 1, 1);
234: }
235: }
236:
237: setGridElements(wrappers, offsetX, offsetY, 0, 0, rowCount,
238: colCount);
239: }
240:
241: protected void createCuts(ElementWrapper[] wrappers,
242: int elementOffsetX, int elementOffsetY, boolean createXCuts) {
243: for (int elementIndex = 0; elementIndex < wrappers.length; elementIndex++) {
244: ElementWrapper wrapper = wrappers[elementIndex];
245: JRPrintElement element = wrapper.getElement();
246:
247: int x = element.getX() + elementOffsetX;
248: int y = element.getY() + elementOffsetY;
249:
250: if (nature.isToExport(element)) {
251: if (createXCuts) {
252: xCuts.add(new Integer(x));
253: xCuts.add(new Integer((x + element.getWidth())));
254: }
255:
256: yCuts.add(new Integer(y));
257: yCuts.add(new Integer((y + element.getHeight())));
258: }
259:
260: JRPrintFrame frame = element instanceof JRPrintFrame ? (JRPrintFrame) element
261: : null;
262:
263: if (nature.isDeep() && frame != null) {
264: createCuts(wrapper.getWrappers(), x
265: + frame.getLeftPadding(), y
266: + frame.getTopPadding(), createXCuts);
267: }
268: }
269: }
270:
271: protected void setGridElements(ElementWrapper[] wrappers,
272: int elementOffsetX, int elementOffsetY, int startRow,
273: int startCol, int endRow, int endCol) {
274: for (int elementIndex = wrappers.length - 1; elementIndex >= 0; elementIndex--) {
275: ElementWrapper wrapper = wrappers[elementIndex];
276: JRPrintElement element = wrapper.getElement();
277:
278: boolean toExport = nature.isToExport(element);
279: //JRPrintFrame frame = deep && element instanceof JRPrintFrame ? (JRPrintFrame) element : null;
280: JRPrintFrame frame = element instanceof JRPrintFrame ? (JRPrintFrame) element
281: : null;
282:
283: if (toExport || frame != null) {
284: int x = element.getX() + elementOffsetX;
285: int y = element.getY() + elementOffsetY;
286:
287: int col1 = xCuts.indexOf(new Integer(x));
288: int row1 = yCuts.indexOf(new Integer(y));
289: int col2 = xCuts.indexOf(new Integer(x
290: + element.getWidth()));
291: int row2 = yCuts.indexOf(new Integer(y
292: + element.getHeight()));
293:
294: if (!(toExport && isOverlap(row1, col1, row2, col2))) {
295: if (nature.isDeep() && frame != null) {
296: setGridElements(wrapper.getWrappers(), x
297: + frame.getLeftPadding(), y
298: + frame.getTopPadding(), row1, col1,
299: row2, col2);
300: }
301:
302: if (toExport) {
303: if (nature.isDeep() && frame != null) {
304: setFrameCellsStyle(frame, row1, col1, row2,
305: col2);
306: } else {
307: setGridElement(wrapper, row1, col1, row2,
308: col2);
309: }
310: }
311: }
312: }
313: }
314:
315: if (nature.isHorizontallyMergeEmptyCells()) {
316: horizontallyMergeEmptyCells(startRow, startCol, endRow,
317: endCol);
318: }
319: }
320:
321: protected void horizontallyMergeEmptyCells(int startRow,
322: int startCol, int endRow, int endCol) {
323: for (int row = startRow; row < endRow; ++row) {
324: int startSpan = -1;
325: int spanWidth = 0;
326: int col = startCol;
327: for (; col < endCol; ++col) {
328: JRExporterGridCell cell = grid[row][col];
329: if (cell.isEmpty()) {
330: if (startSpan == -1) {
331: startSpan = col;
332: }
333: spanWidth += cell.getWidth();
334: } else {
335: if (startSpan != -1 && col - startSpan > 1) {
336: JRExporterGridCell spanCell = grid[row][startSpan];
337: spanCell.setColSpan(col - startSpan);
338: spanCell.setWidth(spanWidth);
339: //TODO set OCCUPIED_CELL?
340: }
341: startSpan = -1;
342: spanWidth = 0;
343: }
344: }
345: if (startSpan != -1 && col - startSpan > 1) {
346: JRExporterGridCell spanCell = grid[row][startSpan];
347: spanCell.setColSpan(col - startSpan);
348: spanCell.setWidth(spanWidth);
349: }
350: }
351: }
352:
353: protected boolean isOverlap(int row1, int col1, int row2, int col2) {
354: boolean isOverlap = false;
355: if (nature.isSpanCells()) {
356: is_overlap_out: for (int row = row1; row < row2; row++) {
357: for (int col = col1; col < col2; col++) {
358: if (!grid[row][col].isEmpty()) {
359: isOverlap = true;
360: break is_overlap_out;
361: }
362: }
363: }
364: } else {
365: isOverlap = grid[row1][col1].getWrapper() != null;
366: }
367: return isOverlap;
368: }
369:
370: protected void setGridElement(ElementWrapper wrapper, int row1,
371: int col1, int row2, int col2) {
372: rowUsage[row1] |= USAGE_NOT_EMPTY;
373: colUsage[col1] |= USAGE_NOT_EMPTY;
374:
375: for (int row = row1; row < row2; row++) {
376: for (int col = col1; col < col2; col++) {
377: grid[row][col] = JRExporterGridCell.OCCUPIED_CELL;
378: }
379: rowUsage[row] |= USAGE_SPANNED;
380: }
381:
382: for (int col = col1; col < col2; col++) {
383: colUsage[col] |= USAGE_SPANNED;
384: }
385:
386: if (col2 - col1 != 0 && row2 - row1 != 0) {
387: JRPrintElement element = wrapper.getElement();
388: JRPrintFrame frame = element instanceof JRPrintFrame ? (JRPrintFrame) element
389: : null;
390:
391: JRExporterGridCell gridCell = new JRExporterGridCell(
392: wrapper, element.getWidth(), element.getHeight(),
393: col2 - col1, row2 - row1);
394:
395: if (frame != null)//FIXMEODT if deep, does this make sense?
396: {
397: gridCell.setLayout(new JRGridLayout(nature, wrapper
398: .getWrappers(), frame.getWidth(), frame
399: .getHeight(), 0, //offsetX
400: 0, //offsetY
401: wrapper.getAddress()));
402: }
403:
404: JRBox cellBox = null;
405: if (element instanceof JRBox) {
406: cellBox = (JRBox) element;
407: }
408:
409: gridCell.setBox(cellBox);
410:
411: grid[row1][col1] = gridCell;
412: }
413: }
414:
415: protected void setFrameCellsStyle(JRPrintFrame frame, int row1,
416: int col1, int row2, int col2) {
417: Color backcolor = frame.getMode() == JRElement.MODE_OPAQUE ? frame
418: .getBackcolor()
419: : null;
420:
421: for (int row = row1; row < row2; row++) {
422: for (int col = col1; col < col2; col++) {
423: JRExporterGridCell cell = grid[row][col];
424:
425: if (cell.getBackcolor() == null) {
426: if (frame.getMode() == JRElement.MODE_OPAQUE) {
427: cell.setBackcolor(backcolor);
428: }
429: }
430:
431: if (cell.getForecolor() == null) {
432: cell.setForecolor(frame.getForecolor());
433: }
434:
435: boolean left = col == col1;
436: boolean right = col == col2 - cell.getColSpan();
437: boolean top = row == row1;
438: boolean bottom = row == row2 - cell.getRowSpan();
439:
440: if (left || right || top || bottom) {
441: JRBox cellBox = cell.getBox();
442: Object key = new BoxKey(frame, cellBox, left,
443: right, top, bottom);
444: JRBox modBox = (JRBox) boxesCache.get(key);
445: if (modBox == null) {
446: modBox = new JRBaseBox(frame, left, right, top,
447: bottom, cellBox);
448: boxesCache.put(key, modBox);
449: }
450:
451: cell.setBox(modBox);
452: }
453: }
454: }
455: }
456:
457: private void splitSharedRowSpanIntoNestedGrids() {
458: for (int row = 0; row < grid.length;) {
459: int rowSpan = getSharedRowSpan(row);
460: //negative row span means it is not shared row span
461: if (rowSpan > 0) {
462: splitSharedColSpanIntoNestedGrids(row, row + rowSpan);
463: }
464: row += Math.abs(rowSpan);
465: }
466: }
467:
468: private void splitSharedColSpanIntoNestedGrids(int row1, int row2) {
469: for (int col = 0; col < grid[row1].length;) {
470: int colSpan = getSharedColSpan(row1, row2, col);
471: //negative col span means it is not shared col span
472: if (colSpan > 0) {
473: if (!(row1 == 0 && row2 == grid.length && col == 0 && colSpan == grid[0].length)) {
474: this .createNestedGrid(row1, col, row2, col
475: + colSpan);
476: }
477: }
478: col += Math.abs(colSpan);
479: }
480: }
481:
482: private int getSharedRowSpan(int row1) {
483: int rowSpan = 1;
484: int sharedSpanCount = 0;
485:
486: for (int row = 0; row < rowSpan; row++) {
487: for (int col = 0; col < grid[0].length; col++) {
488: JRExporterGridCell gridCell = grid[row1 + row][col];
489: if (row + gridCell.getRowSpan() > rowSpan) {
490: sharedSpanCount++;
491: rowSpan = row + gridCell.getRowSpan();
492: }
493: }
494: }
495:
496: // we have "shared row span" only if at least two merged cells share at least one row;
497: // negative row span is used to skip row span that is not shared
498: return sharedSpanCount > 1 ? rowSpan : -rowSpan;
499: }
500:
501: private int getSharedColSpan(int row1, int row2, int col1) {
502: int colSpan = 1;
503: boolean isSharedSpan = false;
504:
505: for (int col = 0; col < colSpan; col++) {
506: for (int row = row1; row < row2; row++) {
507: JRExporterGridCell gridCell = grid[row][col1 + col];
508: if (col + gridCell.getColSpan() > colSpan) {
509: isSharedSpan = true;
510: colSpan = col + gridCell.getColSpan();
511: } else if (gridCell.getRowSpan() > 1) {
512: isSharedSpan = true;
513: }
514: }
515: }
516:
517: // we have "shared col span" only if at least two merged cells share at least one col;
518: // negative col span is used to skip col span that is not shared
519: return isSharedSpan ? colSpan : -colSpan;
520: }
521:
522: /**
523: * Returns the constructed element grid.
524: *
525: * @return the constructed element grid
526: */
527: public JRExporterGridCell[][] getGrid() {
528: return grid;
529: }
530:
531: /**
532: * Decides whether a row is empty or not.
533: *
534: * @param rowIdx the row index
535: * @return <code>true</code> iff the row is not empty
536: */
537: public boolean isRowNotEmpty(int rowIdx) {
538: return ((rowUsage[rowIdx] & USAGE_NOT_EMPTY) > 0);
539: }
540:
541: /**
542: * Decides whether a row is occupied by spanning columns or not.
543: *
544: * @param rowIdx the row index
545: * @return <code>true</code> iff the row is not empty
546: */
547: public boolean isRowSpanned(int rowIdx) {
548: return ((rowUsage[rowIdx] & USAGE_SPANNED) > 0);
549: }
550:
551: /**
552: * Decides whether a column is empty or not.
553: *
554: * @param colIdx the column index
555: * @return <code>true</code> iff the column is not empty
556: */
557: public boolean isColNotEmpty(int colIdx) {
558: return ((colUsage[colIdx] & USAGE_NOT_EMPTY) > 0);
559: }
560:
561: /**
562: * Decides whether a column is occupied by spanning rows or not.
563: *
564: * @param colIdx the column index
565: * @return <code>true</code> iff the column is not empty
566: */
567: public boolean isColSpanned(int colIdx) {
568: return ((colUsage[colIdx] & USAGE_SPANNED) > 0);
569: }
570:
571: /**
572: * Returns the list of cut points on the X axis for the grid.
573: *
574: * @return the list of cut points on the X axis for the grid
575: */
576: public List getXCuts() {
577: return xCuts;
578: }
579:
580: /**
581: * Returns the list of cut points on the Y axis for the grid.
582: *
583: * @return the list of cut points on the X axis for the grid
584: */
585: public List getYCuts() {
586: return yCuts;
587: }
588:
589: /**
590: * Returns the width available for the grid.
591: *
592: * @return the width available for the grid
593: */
594: public int getWidth() {
595: return width;
596: }
597:
598: public int getRowHeight(int row) {
599: return ((Integer) yCuts.get(row + 1)).intValue()
600: - ((Integer) yCuts.get(row)).intValue();
601: }
602:
603: public static int getMaxRowHeight(JRExporterGridCell[] row) {
604: int maxRowHeight = row[0].getHeight();
605: for (int col = 0; col < row.length; col++) {
606: JRExporterGridCell cell = row[col];
607:
608: if (cell != JRExporterGridCell.OCCUPIED_CELL) {
609: if (maxRowHeight < cell.getHeight()) {
610: maxRowHeight = cell.getHeight();
611: }
612: }
613: }
614: return maxRowHeight;
615: }
616:
617: public static int getRowHeight(JRExporterGridCell[] row)//FIXMEODT are we still using this?
618: {
619: if (row[0].getRowSpan() == 1
620: && row[0] != JRExporterGridCell.OCCUPIED_CELL) //quick exit
621: {
622: return row[0].getHeight();
623: }
624:
625: int rowHeight = 0;
626: int minSpanIdx = 0;
627:
628: int colCount = row.length;
629:
630: int col;
631: for (col = 0; col < colCount; col++) {
632: JRExporterGridCell cell = row[col];
633:
634: if (cell != JRExporterGridCell.OCCUPIED_CELL) {
635: if (cell.getRowSpan() == 1) {
636: rowHeight = cell.getHeight();
637: break;
638: }
639:
640: if (cell.getRowSpan() < row[minSpanIdx].getRowSpan()) {
641: minSpanIdx = col;
642: }
643: }
644: }
645:
646: if (col >= colCount) //no cell with rowSpan = 1 was found, getting the height of the cell with min rowSpan
647: {
648: rowHeight = row[minSpanIdx].getHeight();
649: }
650:
651: return rowHeight;
652: }
653:
654: /**
655: * This static method calculates all the X cuts for a list of pages.
656: *
657: * @param pages
658: * The list of pages.
659: * @param startPageIndex
660: * The first page to consider.
661: * @param endPageIndex
662: * The last page to consider.
663: * @param width
664: * The page width
665: * @param offsetX
666: * horizontal element position offset
667: */
668: public static List calculateXCuts(ExporterNature nature,
669: List pages, int startPageIndex, int endPageIndex,
670: int width, int offsetX) {
671: List xCuts = new SortedList();
672: xCuts.add(new Integer(0));
673: xCuts.add(new Integer(width));
674: for (int pageIndex = startPageIndex; pageIndex <= endPageIndex; pageIndex++) {
675: JRPrintPage page = (JRPrintPage) pages.get(pageIndex);
676: addXCuts(nature, page.getElements(), offsetX, xCuts);
677: }
678:
679: return xCuts;
680: }
681:
682: /**
683: * This static method calculates the X cuts for a list of print elements and
684: * stores them in the list indicated by the xCuts parameter.
685: *
686: * @param elementsList
687: * The list of elements to be used to determine the X cuts.
688: * @param elementOffsetX
689: * horizontal element position offset
690: * @param xCuts
691: * The list to which the X cuts are to be added.
692: */
693: protected static void addXCuts(ExporterNature nature,
694: List elementsList, int elementOffsetX, List xCuts) {
695: for (Iterator it = elementsList.iterator(); it.hasNext();) {
696: JRPrintElement element = ((JRPrintElement) it.next());
697: int x = element.getX() + elementOffsetX;
698:
699: if (nature.isToExport(element)) {
700: xCuts.add(new Integer(x));
701: xCuts.add(new Integer(x + element.getWidth()));
702: }
703:
704: if (element instanceof JRPrintFrame) {
705: JRPrintFrame frame = (JRPrintFrame) element;
706: addXCuts(nature, frame.getElements(), x
707: + frame.getLeftPadding(), xCuts);
708: }
709: }
710: }
711:
712: /**
713: *
714: */
715: protected int getNextVirtualFrameIndex() {
716: return virtualFrameIndex++;
717: }
718:
719: /**
720: *
721: */
722: private static ElementWrapper[] createWrappers(List elementsList,
723: String parentAddress) {
724: ElementWrapper[] wrappers = new ElementWrapper[elementsList
725: .size()];
726:
727: for (int elementIndex = 0; elementIndex < elementsList.size(); elementIndex++) {
728: JRPrintElement element = ((JRPrintElement) elementsList
729: .get(elementIndex));
730:
731: String address = (parentAddress == null ? ""
732: : parentAddress + "_")
733: + elementIndex;
734:
735: wrappers[elementIndex] = new ElementWrapper(element,
736: address,
737: element instanceof JRPrintFrame ? createWrappers(
738: ((JRPrintFrame) element).getElements(),
739: address) : null);
740: }
741:
742: return wrappers;
743: }
744:
745: protected static class SortedList extends ArrayList {
746: private static final long serialVersionUID = 6232843428269907513L;
747:
748: public boolean add(Object o) {
749: int idx = Collections.binarySearch(this , o);
750:
751: if (idx >= 0) {
752: return false;
753: }
754:
755: add(-idx - 1, o);
756: return true;
757: }
758:
759: public int indexOf(Object o) {
760: int idx = Collections.binarySearch(this , o);
761:
762: if (idx < 0) {
763: idx = -1;
764: }
765:
766: return idx;
767: }
768: }
769:
770: protected static class BoxKey {
771: final JRBox box;
772: final JRBox cellBox;
773: final boolean left;
774: final boolean right;
775: final boolean top;
776: final boolean bottom;
777: final int hashCode;
778:
779: BoxKey(JRBox box, JRBox cellBox, boolean left, boolean right,
780: boolean top, boolean bottom) {
781: this .box = box;
782: this .cellBox = cellBox;
783: this .left = left;
784: this .right = right;
785: this .top = top;
786: this .bottom = bottom;
787:
788: int hash = box.hashCode();
789: if (cellBox != null) {
790: hash = 31 * hash + cellBox.hashCode();
791: }
792: hash = 31 * hash + (left ? 1231 : 1237);
793: hash = 31 * hash + (right ? 1231 : 1237);
794: hash = 31 * hash + (top ? 1231 : 1237);
795: hash = 31 * hash + (bottom ? 1231 : 1237);
796: hashCode = hash;
797: }
798:
799: public boolean equals(Object obj) {
800: if (obj == this ) {
801: return true;
802: }
803:
804: BoxKey b = (BoxKey) obj;
805:
806: return b.box.equals(box)
807: && (b.cellBox == null ? cellBox == null
808: : (cellBox != null && b.cellBox
809: .equals(cellBox)))
810: && b.left == left && b.right == right
811: && b.top == top && b.bottom == bottom;
812: }
813:
814: public int hashCode() {
815: return hashCode;
816: }
817: }
818:
819: }
|