Source Code Cross Referenced for SheetLayout.java in  » Report » pentaho-report » org » jfree » report » modules » output » table » base » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Report » pentaho report » org.jfree.report.modules.output.table.base 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /**
0002:         * ===========================================
0003:         * JFreeReport : a free Java reporting library
0004:         * ===========================================
0005:         *
0006:         * Project Info:  http://reporting.pentaho.org/
0007:         *
0008:         * (C) Copyright 2001-2007, by Object Refinery Ltd, Pentaho Corporation and Contributors.
0009:         *
0010:         * This library is free software; you can redistribute it and/or modify it under the terms
0011:         * of the GNU Lesser General Public License as published by the Free Software Foundation;
0012:         * either version 2.1 of the License, or (at your option) any later version.
0013:         *
0014:         * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
0015:         * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
0016:         * See the GNU Lesser General Public License for more details.
0017:         *
0018:         * You should have received a copy of the GNU Lesser General Public License along with this
0019:         * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
0020:         * Boston, MA 02111-1307, USA.
0021:         *
0022:         * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
0023:         * in the United States and other countries.]
0024:         *
0025:         * ------------
0026:         * SheetLayout.java
0027:         * ------------
0028:         * (C) Copyright 2001-2007, by Object Refinery Ltd, Pentaho Corporation and Contributors.
0029:         */package org.jfree.report.modules.output.table.base;
0030:
0031:        import java.awt.Color;
0032:        import java.awt.geom.Ellipse2D;
0033:        import java.awt.geom.Line2D;
0034:        import java.awt.geom.Rectangle2D;
0035:        import java.awt.geom.RoundRectangle2D;
0036:        import java.util.ArrayList;
0037:
0038:        import org.jfree.report.layout.model.Border;
0039:        import org.jfree.report.layout.model.BorderCorner;
0040:        import org.jfree.report.layout.model.BorderEdge;
0041:        import org.jfree.report.layout.model.CanvasRenderBox;
0042:        import org.jfree.report.layout.model.RenderBox;
0043:        import org.jfree.report.layout.model.RenderNode;
0044:        import org.jfree.report.layout.model.RenderableReplacedContent;
0045:        import org.jfree.report.layout.model.context.BoxDefinition;
0046:        import org.jfree.report.layout.process.ProcessUtility;
0047:        import org.jfree.report.style.ElementStyleKeys;
0048:        import org.jfree.report.style.StyleSheet;
0049:        import org.jfree.report.util.InstanceID;
0050:        import org.jfree.report.util.geom.StrictBounds;
0051:        import org.jfree.report.util.geom.StrictGeomUtility;
0052:        import org.jfree.util.Log;
0053:
0054:        /**
0055:         * The sheet layout is used to build the background map and to collect the x- and y-cell-borders.
0056:         */
0057:        public class SheetLayout {
0058:            /**
0059:             * How backgrounds for cells get computed
0060:             * --------------------------------------
0061:             *
0062:             * JFreeReport handles 4 background types:
0063:             *
0064:             * Bands
0065:             * -----
0066:             * Bands are no real backgrounds, as they do not influence the output,
0067:             * but they will be used to simplify the computation.
0068:             *
0069:             * Rectangles
0070:             * ----------
0071:             * Define the cell background (fill = true) and all 4 borders of a cell
0072:             * (draw == true).
0073:             *
0074:             * Horizontal & Vertical Lines
0075:             * ----------------
0076:             * These lines define the Top or Left border or a cell. Bottom and right
0077:             * borders get mapped into the Top/Left borders of the next cells.
0078:             *
0079:             * Joining
0080:             * -------
0081:             * When a background element is added, JFreeReport first checks, whether
0082:             * a background has been already defined for that cell position. If not,
0083:             * the given background is used as is.
0084:             *
0085:             * If there is a background defined, a merge operation starts. JFreeReport
0086:             * will try to join both background definitions.
0087:             * (This is done while adding the TableCellBackground to the SheetLayout)
0088:             *
0089:             * New Elements overwrite old elements. That means if there are two
0090:             * conflicting borders or backgrounds at a given position, any old border
0091:             * or background will be replaced as soon as a more current value appears.
0092:             *
0093:             * Lines, which make up the bottom most or right most borders, are held in
0094:             * a zero-width column or zero-height row. These columns are always there,
0095:             * if there is at least one background reaching to right or bottom of the report.
0096:             * A flag indicates, whether these cells are significant. (for validity this
0097:             * flag should mirror the result of an test "All Cells in these Row/Column are
0098:             * empty".
0099:             *
0100:             * These lines are not mapped into bottom cell lines, as the resulting
0101:             * merge would not be predictable and would depend on the order of the
0102:             * split operations. A predictable merge implementation would be by far
0103:             * more complex than this 'hack'.
0104:             *
0105:             * To create a consistent behaviour, rectangle-borders behave like four lines;
0106:             * therefore the bottom and right border of the rectangle will be mapped into
0107:             * top or left border cells.
0108:             */
0109:
0110:            /**
0111:             * An internal flag indicating that the upper or left bounds should be used.
0112:             */
0113:            private static final boolean UPPER_BOUNDS = true;
0114:            private static final boolean LOWER_BOUNDS = false;
0115:
0116:            private static class CellReference {
0117:                private long x;
0118:                private long y;
0119:                private long width;
0120:                private long height;
0121:                private InstanceID contentID;
0122:                private String node;
0123:
0124:                public CellReference(final RenderNode node, final long shift) {
0125:                    this .node = node.toString();
0126:                    this .width = node.getWidth();
0127:                    this .y = node.getY() + shift;
0128:                    this .x = node.getX();
0129:                    this .height = node.getHeight();
0130:                    this .contentID = node.getInstanceId();
0131:                }
0132:
0133:                public long getX() {
0134:                    return x;
0135:                }
0136:
0137:                public long getY() {
0138:                    return y;
0139:                }
0140:
0141:                public long getWidth() {
0142:                    return width;
0143:                }
0144:
0145:                public long getHeight() {
0146:                    return height;
0147:                }
0148:
0149:                public InstanceID getContentID() {
0150:                    return contentID;
0151:                }
0152:
0153:                public String toString() {
0154:                    return "CellReference{" + "x=" + x + ", y=" + y
0155:                            + ", width=" + width + ", height=" + height
0156:                            + ", contentID=" + contentID + ", node='" + node
0157:                            + '\'' + '}';
0158:                }
0159:            }
0160:
0161:            /**
0162:             * A flag, defining whether to use strict layout mode.
0163:             */
0164:            private final boolean strict;
0165:
0166:            /**
0167:             * The XBounds, all vertical cell boundaries (as CoordinateMappings).
0168:             */
0169:            private final TableCutList xBounds;
0170:
0171:            /**
0172:             * The YBounds, all vertical cell boundaries (as CoordinateMappings).
0173:             */
0174:            private final TableCutList yBounds;
0175:
0176:            /**
0177:             * Is a list of lists, contains the merged backgrounds ...
0178:             */
0179:            private GenericObjectTable backend;
0180:            /**
0181:             * Contains the references to the original data passed into this layouter.
0182:             */
0183:            private GenericObjectTable objectIdTable;
0184:
0185:            /**
0186:             * The right border of the grid. This is needed when not being in the strict mode.
0187:             */
0188:            private long xMaxBounds;
0189:            private long yMaxBounds;
0190:            private transient StrictBounds workBounds;
0191:            private boolean ellipseAsRectangle;
0192:            private boolean verboseCellMarker;
0193:
0194:            /**
0195:             * Creates a new TableGrid-object. If strict mode is enabled, all cell bounds are used to create the table grid,
0196:             * resulting in a more complex layout.
0197:             *
0198:             * @param strict the strict mode for the layout.
0199:             */
0200:            public SheetLayout(final boolean strict,
0201:                    final boolean ellipseAsRectangle,
0202:                    final boolean verboseCellMarker) {
0203:                this .ellipseAsRectangle = ellipseAsRectangle;
0204:                this .xBounds = new TableCutList(50);
0205:                this .yBounds = new TableCutList(200);
0206:                this .strict = strict;
0207:                this .xMaxBounds = 0;
0208:                this .yMaxBounds = 0;
0209:                this .backend = new GenericObjectTable(200, 5);
0210:                this .objectIdTable = new GenericObjectTable(200, 5);
0211:                this .verboseCellMarker = verboseCellMarker;
0212:                this .ensureXMapping(0, false);
0213:                this .ensureYMapping(0, false);
0214:            }
0215:
0216:            private TableCellDefinition createBackground(final RenderBox box,
0217:                    final long shift) {
0218:                final TableCellDefinition legacyDefinition = computeLegacyBackground(
0219:                        box, shift);
0220:                if (legacyDefinition != null) {
0221:                    return legacyDefinition;
0222:                }
0223:
0224:                if (box.getBoxDefinition().getBorder().isEmpty() == false) {
0225:                    return new TableCellDefinition(box, shift);
0226:                }
0227:
0228:                final StyleSheet styleSheet = box.getStyleSheet();
0229:                if (styleSheet
0230:                        .getStyleProperty(ElementStyleKeys.BACKGROUND_COLOR) != null) {
0231:                    return new TableCellDefinition(box, shift);
0232:                }
0233:
0234:                if (styleSheet.getStyleProperty(ElementStyleKeys.ANCHOR_NAME) != null) {
0235:                    return new TableCellDefinition(box, shift);
0236:                }
0237:                return null;
0238:            }
0239:
0240:            private TableCellDefinition computeLegacyBackground(
0241:                    final RenderBox box, final long shift) {
0242:
0243:                // For legacy reasons: A single ReplacedContent in a canvas means, we may have a old-style border and
0244:                // background definition.
0245:                if (box instanceof  CanvasRenderBox == false) {
0246:                    return null;
0247:                }
0248:
0249:                final RenderNode firstChild = box.getFirstChild();
0250:                if (firstChild != box.getLastChild()) {
0251:                    return null;
0252:                }
0253:
0254:                if (firstChild instanceof  RenderableReplacedContent == false) {
0255:                    return null;
0256:                }
0257:
0258:                final StyleSheet styleSheet = box.getStyleSheet();
0259:                final RenderableReplacedContent rpc = (RenderableReplacedContent) firstChild;
0260:                final Object rawObject = rpc.getRawObject();
0261:
0262:                final boolean draw = styleSheet
0263:                        .getBooleanStyleProperty(ElementStyleKeys.DRAW_SHAPE);
0264:                if (rawObject instanceof  Line2D && draw) {
0265:                    final TableCellDefinition tableCellDefinition = new TableCellDefinition(
0266:                            box, shift);
0267:                    tableCellDefinition.setLineHint((Line2D) rawObject);
0268:                    return tableCellDefinition;
0269:                }
0270:
0271:                if (rawObject instanceof  Rectangle2D
0272:                        || (ellipseAsRectangle && rawObject instanceof  Ellipse2D)) {
0273:                    final TableCellDefinition tableCellDefinition = new TableCellDefinition(
0274:                            box, shift);
0275:                    if (draw) {
0276:                        // the beast has a border ..
0277:                        final BorderEdge edge = ProcessUtility
0278:                                .produceBorderEdge(box.getStyleSheet());
0279:                        if (edge != null) {
0280:                            tableCellDefinition.setTop(edge);
0281:                            tableCellDefinition.setLeft(edge);
0282:                            tableCellDefinition.setBottom(edge);
0283:                            tableCellDefinition.setRight(edge);
0284:                        }
0285:                    }
0286:                    if (styleSheet
0287:                            .getBooleanStyleProperty(ElementStyleKeys.FILL_SHAPE)) {
0288:                        tableCellDefinition
0289:                                .setBackgroundColor((Color) styleSheet
0290:                                        .getStyleProperty(ElementStyleKeys.PAINT));
0291:                    }
0292:                    return tableCellDefinition;
0293:                }
0294:
0295:                if (rawObject instanceof  RoundRectangle2D) {
0296:                    final TableCellDefinition tableCellDefinition = new TableCellDefinition(
0297:                            box, shift);
0298:                    if (draw) {
0299:                        // the beast has a border ..
0300:                        final BorderEdge edge = ProcessUtility
0301:                                .produceBorderEdge(box.getStyleSheet());
0302:                        if (edge != null) {
0303:                            tableCellDefinition.setTop(edge);
0304:                            tableCellDefinition.setLeft(edge);
0305:                            tableCellDefinition.setBottom(edge);
0306:                            tableCellDefinition.setRight(edge);
0307:                        }
0308:                    }
0309:                    if (styleSheet
0310:                            .getBooleanStyleProperty(ElementStyleKeys.FILL_SHAPE)) {
0311:                        tableCellDefinition
0312:                                .setBackgroundColor((Color) styleSheet
0313:                                        .getStyleProperty(ElementStyleKeys.PAINT));
0314:                    }
0315:                    final RoundRectangle2D rr = (RoundRectangle2D) rawObject;
0316:                    final long arcHeight = StrictGeomUtility.toInternalValue(rr
0317:                            .getArcHeight());
0318:                    final long arcWidth = StrictGeomUtility.toInternalValue(rr
0319:                            .getArcWidth());
0320:                    if (arcHeight > 0 && arcWidth > 0) {
0321:                        final BorderCorner bc = new BorderCorner(arcWidth,
0322:                                arcHeight);
0323:                        tableCellDefinition.setTopLeft(bc);
0324:                        tableCellDefinition.setBottomLeft(bc);
0325:                        tableCellDefinition.setTopRight(bc);
0326:                        tableCellDefinition.setBottomRight(bc);
0327:                    }
0328:                    return tableCellDefinition;
0329:                }
0330:
0331:                return null;
0332:            }
0333:
0334:            /**
0335:             * Adds the bounds of the given TableCellData to the grid. The bounds given must be the same as the bounds of the
0336:             * element, or the layouting might produce surprising results.
0337:             * <p/>
0338:             * This method will do nothing, if the element has a width and height of zero and does not define any anchors.
0339:             *
0340:             * @param element the position that should be added to the grid (might be null).
0341:             * @param shift   the vertical shift which adjusts the visual position of the content.
0342:             * @throws NullPointerException if the bounds are null
0343:             */
0344:            public void add(final RenderBox element, final long shift) {
0345:                final long shiftedY = element.getY() + shift;
0346:                final long elementY;
0347:                if (shiftedY < 0) {
0348:                    if ((shiftedY + element.getHeight()) < 0) {
0349:                        // The box will not be visible at all. (Should not happen in a sane environment ..)
0350:                        Log.debug("THIS BOX WILL BE INVISIBLE: " + element);
0351:                        return;
0352:                    }
0353:
0354:                    elementY = 0;
0355:                } else {
0356:                    elementY = shiftedY;
0357:                }
0358:
0359:                final TableCellDefinition background = createBackground(
0360:                        element, shift);
0361:                if (addLine(element, background, shift, elementY, shiftedY)) {
0362:                    return;
0363:                }
0364:
0365:                final long elementX = element.getX();
0366:                final long elementRightX = (element.getWidth() + elementX);
0367:                final long elementBottomY = element.getHeight() + shiftedY;
0368:
0369:                // collect the bounds and add them to the xBounds and yBounds collection
0370:                // if necessary...
0371:                ensureXMapping(elementX, false);
0372:                ensureYMapping(elementY, false);
0373:
0374:                // an end cut is auxilary, if it is not a background and the layout is not strict
0375:                final boolean aux = (background == null)
0376:                        && (isStrict() == false);
0377:                ensureXMapping(elementRightX, aux);
0378:                ensureYMapping(elementBottomY, aux);
0379:
0380:                // update the collected maximums
0381:                if (xMaxBounds < elementRightX) {
0382:                    xMaxBounds = elementRightX;
0383:                }
0384:                if (yMaxBounds < elementBottomY) {
0385:                    yMaxBounds = elementBottomY;
0386:                }
0387:
0388:                // now add the new element to the table ...
0389:                // the +1 makes sure, that we include the right and bottom element borders in the set
0390:                final int lowerXIndex = xBounds.findKeyPosition(elementX,
0391:                        SheetLayout.LOWER_BOUNDS);
0392:                final int upperXIndex = xBounds.findKeyPosition(elementRightX,
0393:                        SheetLayout.UPPER_BOUNDS);
0394:
0395:                final int lowerYIndex = yBounds.findKeyPosition(elementY,
0396:                        SheetLayout.LOWER_BOUNDS);
0397:                final int upperYIndex = yBounds.findKeyPosition(elementBottomY,
0398:                        SheetLayout.UPPER_BOUNDS);
0399:
0400:                if (background != null) {
0401:                    // this does nothing for yLength == 1 && xLength == 1
0402:                    // in that case, the whole thing did not define an area but a
0403:                    // vertical or horizontal line.
0404:                    processAreaBackground(lowerXIndex, lowerYIndex,
0405:                            upperXIndex, upperYIndex, background);
0406:                }
0407:
0408:                if (ProcessUtility.isContent(element, false,
0409:                        ellipseAsRectangle, false) == false) {
0410:                    return;
0411:                }
0412:
0413:                final boolean hasVerticalPaddings;
0414:                final BoxDefinition sblp = element.getBoxDefinition();
0415:                if (sblp.getPaddingTop() != 0 || sblp.getPaddingBottom() != 0) {
0416:                    final long coordinate = elementBottomY
0417:                            - sblp.getPaddingBottom();
0418:                    if (coordinate > 0) {
0419:                        ensureYMapping(coordinate, false);
0420:                        if (shiftedY >= 0) {
0421:                            ensureYMapping(elementY + sblp.getPaddingTop(),
0422:                                    false);
0423:                        }
0424:                        hasVerticalPaddings = true;
0425:                    } else {
0426:                        hasVerticalPaddings = false;
0427:                    }
0428:                } else {
0429:                    hasVerticalPaddings = false;
0430:                }
0431:
0432:                final boolean hasHorizontalPaddings;
0433:                if (sblp.getPaddingLeft() != 0 || sblp.getPaddingRight() != 0) {
0434:                    // check if the element is a page-spanning element. No top-paddings apply in that case..
0435:                    ensureXMapping(elementX + sblp.getPaddingLeft(), false);
0436:                    ensureXMapping(elementRightX - sblp.getPaddingRight(),
0437:                            false);
0438:                    hasHorizontalPaddings = true;
0439:                } else {
0440:                    hasHorizontalPaddings = false;
0441:                }
0442:
0443:                if (hasHorizontalPaddings == false
0444:                        && hasVerticalPaddings == false) {
0445:                    // now, elements can be both content and background.
0446:                    // mark cells as occupied ..
0447:                    if (isCellAreaOccupied(lowerXIndex, lowerYIndex,
0448:                            upperXIndex, upperYIndex, element.getName()) == false) {
0449:                        final Object cellReference;
0450:                        if (verboseCellMarker) {
0451:                            cellReference = new SheetLayout.CellReference(
0452:                                    element, shift);
0453:                        } else {
0454:                            cellReference = element.getInstanceId();
0455:                        }
0456:
0457:                        final int maxX = Math.max(lowerXIndex + 1, upperXIndex);
0458:                        final int maxY = Math.max(lowerYIndex + 1, upperYIndex);
0459:                        for (int y = lowerYIndex; y < maxY; y++) {
0460:                            // get the index of the current row in the backend-table ...
0461:                            final TableCutList.CutEntry currentRowValue = yBounds
0462:                                    .getValueAt(y);
0463:                            final int currentRowIndex = currentRowValue
0464:                                    .getPosition();
0465:
0466:                            // for every row we iterate over all columns ...
0467:                            // but we do not touch the last column ..
0468:                            for (int x = lowerXIndex; x < maxX; x++) {
0469:                                // again get the column index for the backend table ...
0470:                                final TableCutList.CutEntry currentColumnValue = xBounds
0471:                                        .getValueAt(x);
0472:                                final int currentColumnIndex = currentColumnValue
0473:                                        .getPosition();
0474:
0475:                                objectIdTable.setObject(currentRowIndex,
0476:                                        currentColumnIndex, cellReference);
0477:                            }
0478:                        }
0479:                    }
0480:                } else {
0481:                    final int lowerXPadIndex = xBounds.findKeyPosition(elementX
0482:                            + sblp.getPaddingLeft(), SheetLayout.LOWER_BOUNDS);
0483:                    final int upperXPadIndex = xBounds.findKeyPosition(
0484:                            elementRightX + sblp.getPaddingRight(),
0485:                            SheetLayout.UPPER_BOUNDS);
0486:
0487:                    final int lowerYPadIndex = yBounds.findKeyPosition(elementY
0488:                            + sblp.getPaddingTop(), SheetLayout.LOWER_BOUNDS);
0489:                    final int upperYPadIndex = yBounds.findKeyPosition(
0490:                            elementBottomY - sblp.getPaddingBottom(),
0491:                            SheetLayout.UPPER_BOUNDS);
0492:
0493:                    if (isCellAreaOccupied(lowerXPadIndex, lowerYPadIndex,
0494:                            upperXPadIndex, upperYPadIndex, element.getName()) == false) {
0495:                        final Object cellReference;
0496:                        if (verboseCellMarker) {
0497:                            cellReference = new SheetLayout.CellReference(
0498:                                    element, shift);
0499:                        } else {
0500:                            cellReference = element.getInstanceId();
0501:                        }
0502:
0503:                        final int maxX = Math.max(lowerXPadIndex + 1,
0504:                                upperXPadIndex);
0505:                        final int maxY = Math.max(lowerYPadIndex + 1,
0506:                                upperYPadIndex);
0507:                        for (int y = lowerYPadIndex; y < maxY; y++) {
0508:                            // get the index of the current row in the backend-table ...
0509:                            final TableCutList.CutEntry currentRowValue = yBounds
0510:                                    .getValueAt(y);
0511:                            final int currentRowIndex = currentRowValue
0512:                                    .getPosition();
0513:
0514:                            // for every row we iterate over all columns ...
0515:                            // but we do not touch the last column ..
0516:                            for (int x = lowerXPadIndex; x < maxX; x++) {
0517:                                // again get the column index for the backend table ...
0518:                                final TableCutList.CutEntry currentColumnValue = xBounds
0519:                                        .getValueAt(x);
0520:                                final int currentColumnIndex = currentColumnValue
0521:                                        .getPosition();
0522:
0523:                                objectIdTable.setObject(currentRowIndex,
0524:                                        currentColumnIndex, cellReference);
0525:                            }
0526:                        }
0527:                    }
0528:                }
0529:            }
0530:
0531:            private boolean addLine(final RenderBox element,
0532:                    TableCellDefinition background, final long shift,
0533:                    final long elementY, final long shiftedY) {
0534:                // This method handles several special cases. If the element is a non-area box with borderss,
0535:                // it mapps the borders into a equivalent line-definition.
0536:                final long width = element.getWidth();
0537:                final long height = element.getHeight();
0538:                if (width == 0 && height == 0) {
0539:                    if (background != null) {
0540:                        background.setLineHint(null);
0541:                        if (background.getAnchor() != null) {
0542:                            // Elements that define anchors are an exception. We add it ..
0543:                            return false;
0544:                        }
0545:                    }
0546:                    // this element will be invisible. We do not add it ..
0547:                    return true;
0548:                }
0549:
0550:                // line definitions are treated like boxes that span from zero to the position of the
0551:                // line.
0552:                boolean lineVertical = false;
0553:                long linePosition = 0;
0554:                BorderEdge significantEdge = BorderEdge.EMPTY;
0555:
0556:                final Border border = element.getBoxDefinition().getBorder();
0557:                if (width == 0) {
0558:                    if (BorderEdge.EMPTY.equals(border.getLeft()) == false) {
0559:                        significantEdge = border.getLeft();
0560:                        lineVertical = true;
0561:                        linePosition = element.getX();
0562:                    } else if (BorderEdge.EMPTY.equals(border.getRight()) == false) {
0563:                        significantEdge = border.getRight();
0564:                        lineVertical = true;
0565:                        linePosition = element.getX() + element.getWidth();
0566:                    }
0567:                }
0568:
0569:                boolean lineHorizontal = false;
0570:                if (height == 0) {
0571:                    if (BorderEdge.EMPTY.equals(border.getTop()) == false) {
0572:                        significantEdge = border.getTop();
0573:                        lineHorizontal = true;
0574:                        linePosition = shiftedY;
0575:                    } else if (BorderEdge.EMPTY.equals(border.getBottom()) == false) {
0576:                        significantEdge = border.getBottom();
0577:                        lineHorizontal = true;
0578:                        linePosition = shiftedY + element.getHeight();
0579:                    }
0580:                }
0581:                if (background != null) {
0582:                    final Line2D lineHint = background.getLineHint();
0583:                    if (lineHint != null) {
0584:                        final BorderEdge edge = ProcessUtility
0585:                                .produceBorderEdge(element.getStyleSheet());
0586:                        if (BorderEdge.EMPTY.equals(edge) == false) {
0587:                            if (height > 0
0588:                                    && lineHint.getX1() == lineHint.getX2()) {
0589:                                lineVertical = true;
0590:                                if (lineHint.getX1() == 0) {
0591:                                    linePosition = element.getX();
0592:                                } else {
0593:                                    linePosition = element.getX()
0594:                                            + element.getWidth();
0595:                                }
0596:                            }
0597:                            if (width > 0
0598:                                    && lineHint.getY1() == lineHint.getY2()) {
0599:                                lineHorizontal = true;
0600:                                if (lineHint.getY1() == 0) {
0601:                                    linePosition = shiftedY;
0602:                                } else {
0603:                                    linePosition = shiftedY
0604:                                            + element.getHeight();
0605:                                }
0606:                            }
0607:                            significantEdge = edge;
0608:                        }
0609:                    }
0610:                }
0611:
0612:                if ((lineHorizontal && lineVertical)
0613:                        || (lineHorizontal == false && lineVertical == false)
0614:                        || linePosition < 0
0615:                        || BorderEdge.EMPTY.equals(significantEdge)) {
0616:                    // an invalid definition. it will be ignored ...
0617:                    if (background != null && background.getLineHint() != null) {
0618:                        // this is a line, not a content element, and the line itself is invalid. Ignore it
0619:                        background.setLineHint(null);
0620:                        if (background.getAnchor() != null) {
0621:                            // Elements that define anchors are an exception. We add it ..
0622:                            return false;
0623:                        }
0624:                        return true;
0625:                    }
0626:                    return false;
0627:                }
0628:
0629:                if (background == null) {
0630:                    background = new TableCellDefinition(element, shift);
0631:                }
0632:
0633:                final long elementX = element.getX();
0634:                final long elementRightX = (element.getWidth() + elementX);
0635:                final long elementBottomY = element.getHeight() + shiftedY;
0636:
0637:                final int lowerXIndex;
0638:                final int upperXIndex;
0639:                final int lowerYIndex;
0640:                final int upperYIndex;
0641:
0642:                // Beginn the mapping ..
0643:                if (lineHorizontal) {
0644:                    ensureXMapping(elementX, false);
0645:                    ensureXMapping(elementRightX, false);
0646:                    lowerXIndex = xBounds.findKeyPosition(elementX,
0647:                            SheetLayout.LOWER_BOUNDS);
0648:                    upperXIndex = xBounds.findKeyPosition(elementRightX,
0649:                            SheetLayout.UPPER_BOUNDS);
0650:
0651:                    if (linePosition == 0) {
0652:                        ensureYMapping(elementY, false);
0653:                        lowerYIndex = yBounds.findKeyPosition(shiftedY,
0654:                                SheetLayout.LOWER_BOUNDS);
0655:                        upperYIndex = yBounds.findKeyPosition(
0656:                                elementBottomY + 1, SheetLayout.UPPER_BOUNDS);
0657:                        background.setTop(significantEdge);
0658:                    } else {
0659:                        ensureYMapping(elementBottomY, false);
0660:                        lowerYIndex = yBounds.findKeyPosition(shiftedY - 1,
0661:                                SheetLayout.LOWER_BOUNDS);
0662:                        upperYIndex = yBounds.findKeyPosition(elementBottomY,
0663:                                SheetLayout.UPPER_BOUNDS);
0664:                        background.setBottom(significantEdge);
0665:                    }
0666:                } else // if (lineVertical)
0667:                {
0668:                    ensureYMapping(elementY, false);
0669:                    ensureYMapping(elementBottomY, false);
0670:                    lowerYIndex = yBounds.findKeyPosition(shiftedY,
0671:                            SheetLayout.LOWER_BOUNDS);
0672:                    upperYIndex = yBounds.findKeyPosition(elementBottomY,
0673:                            SheetLayout.UPPER_BOUNDS);
0674:
0675:                    if (linePosition == 0) {
0676:                        ensureXMapping(elementX, false);
0677:                        lowerXIndex = xBounds.findKeyPosition(elementX,
0678:                                SheetLayout.LOWER_BOUNDS);
0679:                        upperXIndex = xBounds.findKeyPosition(elementX + 1,
0680:                                SheetLayout.UPPER_BOUNDS);
0681:                        background.setLeft(significantEdge);
0682:                    } else {
0683:                        ensureXMapping(elementRightX, false);
0684:                        lowerXIndex = xBounds.findKeyPosition(elementX - 1,
0685:                                SheetLayout.LOWER_BOUNDS);
0686:                        upperXIndex = xBounds.findKeyPosition(elementX,
0687:                                SheetLayout.UPPER_BOUNDS);
0688:                        background.setRight(significantEdge);
0689:                    }
0690:                }
0691:
0692:                // update the collected maximums
0693:                if (xMaxBounds < elementRightX) {
0694:                    xMaxBounds = elementRightX;
0695:                }
0696:                if (yMaxBounds < elementBottomY) {
0697:                    yMaxBounds = elementBottomY;
0698:                }
0699:
0700:                processAreaBackground(lowerXIndex, lowerYIndex, upperXIndex,
0701:                        upperYIndex, background);
0702:                background.setLineHint(null);
0703:                return true;
0704:            }
0705:
0706:            private boolean isCellAreaOccupied(final int lowerXIndex,
0707:                    final int lowerYIndex, final int upperXIndex,
0708:                    final int upperYIndex, final String newContent) {
0709:                if (lowerXIndex == upperXIndex || lowerYIndex == upperYIndex) {
0710:                    // not an area object, and therefore not valid ..
0711:                    return false;
0712:                }
0713:
0714:                final int maxX = Math.max(lowerXIndex + 1, upperXIndex);
0715:                final int maxY = Math.max(lowerYIndex + 1, upperYIndex);
0716:                for (int y = lowerYIndex; y < maxY; y++) {
0717:                    // get the index of the current row in the backend-table ...
0718:                    final TableCutList.CutEntry currentRowValue = yBounds
0719:                            .getValueAt(y);
0720:                    final int currentRowIndex = currentRowValue.getPosition();
0721:
0722:                    // for every row we iterate over all columns ...
0723:                    // but we do not touch the last column ..
0724:                    for (int x = lowerXIndex; x < maxX; x++) {
0725:                        // again get the column index for the backend table ...
0726:                        final TableCutList.CutEntry currentColumnValue = xBounds
0727:                                .getValueAt(x);
0728:                        final int currentColumnIndex = currentColumnValue
0729:                                .getPosition();
0730:
0731:                        final Object o = objectIdTable.getObject(
0732:                                currentRowIndex, currentColumnIndex);
0733:                        if (o != null) {
0734:                            Log.warn("Overlapping elements detected. Cell at ("
0735:                                    + currentColumnIndex + ", "
0736:                                    + currentRowIndex + ") is occupied by " + o
0737:                                    + " but content from element " + newContent
0738:                                    + " tried to use the same space.");
0739:                            return true;
0740:                        }
0741:                    }
0742:                }
0743:                return false;
0744:            }
0745:
0746:            private void processAreaBackground(final int lowerXIndex,
0747:                    final int lowerYIndex, final int upperXIndex,
0748:                    final int upperYIndex, final TableCellDefinition background) {
0749:                //    final boolean hasRightBorders = (BorderEdge.EMPTY.equals(background.getRight()) == false);
0750:                //    final boolean hasBottomBorders = (BorderEdge.EMPTY.equals(background.getBottom()) == false);
0751:
0752:                final int maxX = Math.max(lowerXIndex + 1, upperXIndex);
0753:                final int maxY = Math.max(lowerYIndex + 1, upperYIndex);
0754:                for (int y = lowerYIndex; y < maxY; y++) {
0755:                    // get the index of the current row in the backend-table ...
0756:                    final TableCutList.CutEntry currentRowValue = yBounds
0757:                            .getValueAt(y);
0758:                    final int currentRowIndex = currentRowValue.getPosition();
0759:
0760:                    // for every row we iterate over all columns ...
0761:                    // but we do not touch the last column ..
0762:                    for (int x = lowerXIndex; x < maxX; x++) {
0763:                        // again get the column index for the backend table ...
0764:                        final TableCutList.CutEntry currentColumnValue = xBounds
0765:                                .getValueAt(x);
0766:                        final int currentColumnIndex = currentColumnValue
0767:                                .getPosition();
0768:
0769:                        workBounds = computeCellBounds(workBounds,
0770:                                currentColumnValue.getCoordinate(),
0771:                                currentRowValue.getCoordinate());
0772:                        performMergeCellBackground(currentRowIndex,
0773:                                currentColumnIndex, background, workBounds);
0774:                    }
0775:                }
0776:
0777:                //    if (hasRightBorders && (xBounds.getKeyAt(upperXIndex) == xMaxBounds))
0778:                //    {
0779:                //      lastColumnCutIsSignificant = true;
0780:                //    }
0781:                //    if (hasBottomBorders && (yBounds.getKeyAt(upperYIndex) == yMaxBounds))
0782:                //    {
0783:                //      lastRowCutIsSignificant = true;
0784:                //    }
0785:                //
0786:            }
0787:
0788:            /**
0789:             * This method computes the cell bounds for a cell on a given gid position. If the retval parameter is non-null, the
0790:             * computed cell bounds will be copied into the given object to avoid unnecessary object creation.
0791:             *
0792:             * @param retval the bounds, to which the computed result should be copied, or null, if a new object should be
0793:             *               returned.
0794:             * @param xVal   the x coordinates within the grid
0795:             * @param yVal   the y coordinates within the grid
0796:             * @return the computed cell bounds.
0797:             */
0798:            private StrictBounds computeCellBounds(final StrictBounds retval,
0799:                    final long xVal, final long yVal) {
0800:                final long x2Val = xBounds.findKey(xVal + 1,
0801:                        SheetLayout.UPPER_BOUNDS);
0802:                final long y2Val = yBounds.findKey(yVal + 1,
0803:                        SheetLayout.UPPER_BOUNDS);
0804:                if (retval == null) {
0805:                    return new StrictBounds(xVal, yVal, x2Val - xVal, y2Val
0806:                            - yVal);
0807:                }
0808:                retval.setRect(xVal, yVal, x2Val - xVal, y2Val - yVal);
0809:                return retval;
0810:            }
0811:
0812:            private void performMergeCellBackground(final int currentRowIndex,
0813:                    final int currentColumnIndex,
0814:                    final TableCellDefinition background,
0815:                    final StrictBounds bounds) {
0816:                // get the old background ... we will merge this one with the new ..
0817:                final TableCellDefinition oldBackground = (TableCellDefinition) backend
0818:                        .getObject(currentRowIndex, currentColumnIndex);
0819:                final TableCellDefinition newBackground;
0820:                if (oldBackground == null) {
0821:                    // split the element ..
0822:                    newBackground = background.normalize(bounds);
0823:                } else {
0824:                    newBackground = oldBackground.merge(background, bounds);
0825:                }
0826:                backend.setObject(currentRowIndex, currentColumnIndex,
0827:                        newBackground);
0828:            }
0829:
0830:            private void ensureXMapping(final long coordinate, final boolean aux) {
0831:                final TableCutList.CutEntry cut = xBounds.get(coordinate);
0832:                if (cut == null) {
0833:                    final int result = xBounds.size();
0834:                    xBounds.put(coordinate, new TableCutList.CutEntry(
0835:                            coordinate, result, aux));
0836:
0837:                    // backend copy ...
0838:                    final int oldColumn = getPreviousColumnPosition(coordinate);
0839:                    if (coordinate < xMaxBounds) {
0840:                        columnInserted(coordinate, oldColumn, result);
0841:                    }
0842:                } else if (cut.isAuxilary() && aux == false) {
0843:                    cut.makePermanent();
0844:                }
0845:            }
0846:
0847:            /**
0848:             * Splits the background column into two new columns.
0849:             *
0850:             * @param coordinate
0851:             * @param oldColumn
0852:             * @param newColumn
0853:             */
0854:            protected void columnInserted(final long coordinate,
0855:                    final int oldColumn, final int newColumn) {
0856:                //    Log.debug("Inserting new column on position " + coordinate +
0857:                //            " (Col: " + oldColumn + " -> " + newColumn);
0858:                //
0859:                // now copy all entries from old column to new column
0860:                backend.copyColumn(oldColumn, newColumn);
0861:                objectIdTable.copyColumn(oldColumn, newColumn);
0862:
0863:                // handle the backgrounds ..
0864:                StrictBounds rightBounds = null;
0865:                final TableCutList.CutEntry[] entries = yBounds.getRawEntries();
0866:                final int size = yBounds.size();
0867:                for (int i = 0; i < size; i++) {
0868:                    final TableCutList.CutEntry bcut = entries[i];
0869:
0870:                    final int position = bcut.getPosition();
0871:                    final TableCellDefinition originalBackground = (TableCellDefinition) backend
0872:                            .getObject(position, newColumn);
0873:                    if (originalBackground == null) {
0874:                        continue;
0875:                    }
0876:
0877:                    // a column has been inserted. We have to check, whether the background has
0878:                    // borders defined, which might be invalid now.
0879:                    rightBounds = computeCellBounds(rightBounds, coordinate,
0880:                            bcut.getCoordinate());
0881:
0882:                    // the bounds of the old background have to be adjusted too ..
0883:                    final StrictBounds leftBounds = originalBackground
0884:                            .getBounds();
0885:                    final long parentNewWidth = rightBounds.getX()
0886:                            - leftBounds.getX();
0887:                    leftBounds
0888:                            .setRect(leftBounds.getX(), leftBounds.getY(), Math
0889:                                    .max(0, parentNewWidth), leftBounds
0890:                                    .getHeight());
0891:                    // the original cell was split into two new cells ...
0892:                    // the new right border is no longer filled ...
0893:                    // a border was found, but is invalid now.
0894:
0895:                    final TableCellDefinition rightBackground = originalBackground
0896:                            .normalize(rightBounds);
0897:                    final TableCellDefinition leftBackground = originalBackground
0898:                            .normalize(leftBounds);
0899:
0900:                    backend.setObject(position, oldColumn, leftBackground);
0901:                    backend.setObject(position, newColumn, rightBackground);
0902:                }
0903:            }
0904:
0905:            private int getPreviousColumnPosition(final long coordinate) {
0906:                final TableCutList.CutEntry entry = xBounds
0907:                        .getPrevious(coordinate);
0908:                if (entry == null) {
0909:                    return -1;
0910:                }
0911:                return entry.getPosition();
0912:            }
0913:
0914:            protected void rowInserted(final long coordinate, final int oldRow,
0915:                    final int newRow) {
0916:                if (oldRow < 0) {
0917:                    throw new IndexOutOfBoundsException(
0918:                            "OldRow cannot be negative: " + coordinate);
0919:                }
0920:                if (newRow < 0) {
0921:                    throw new IndexOutOfBoundsException(
0922:                            "NewRow cannot be negative: " + coordinate);
0923:                }
0924:                //    Log.debug("Inserting new row on position " + coordinate +
0925:                //            " (Row: " + oldRow + " -> " + newRow);
0926:
0927:                // now copy all entries from old column to new column
0928:                backend.copyRow(oldRow, newRow);
0929:                objectIdTable.copyRow(oldRow, newRow);
0930:
0931:                // handle the backgrounds ..
0932:                StrictBounds cellBounds = null;
0933:
0934:                final TableCutList.CutEntry[] entries = xBounds.getRawEntries();
0935:                final int xEntrySize = xBounds.size();
0936:                for (int i = 0; i < xEntrySize; i++) {
0937:                    final TableCutList.CutEntry bcut = entries[i];
0938:
0939:                    final int position = bcut.getPosition();
0940:                    final TableCellDefinition originalBackground = (TableCellDefinition) backend
0941:                            .getObject(newRow, position);
0942:                    if (originalBackground == null) {
0943:                        continue;
0944:                    }
0945:
0946:                    // a row has been inserted. We have to check, whether the background has
0947:                    // borders defined, which might be invalid now.
0948:                    cellBounds = computeCellBounds(cellBounds, bcut
0949:                            .getCoordinate(), coordinate);
0950:
0951:                    // the bounds of the old background have to be adjusted too ..
0952:                    final StrictBounds bounds = originalBackground.getBounds();
0953:                    final long parentNewHeight = cellBounds.getY()
0954:                            - bounds.getY();
0955:                    bounds.setRect(bounds.getX(), bounds.getY(), bounds
0956:                            .getWidth(), Math.max(0, parentNewHeight));
0957:                    // due to the merging it is possible, that the bottom border
0958:                    // is invalid now.
0959:                    // the Top-Border of the original background is not touched ...
0960:
0961:                    final TableCellDefinition bottomBackground = originalBackground
0962:                            .normalize(cellBounds);
0963:                    final TableCellDefinition topBackground = originalBackground
0964:                            .normalize(bounds);
0965:                    backend.setObject(oldRow, position, topBackground);
0966:                    backend.setObject(newRow, position, bottomBackground);
0967:                }
0968:            }
0969:
0970:            private int getPreviousRowPosition(final long coordinate) {
0971:                final TableCutList.CutEntry entry = yBounds
0972:                        .getPrevious(coordinate);
0973:                if (entry == null) {
0974:                    return -1;
0975:                }
0976:                //    Log.debug ("GetPreviousRow: " + entry.getCoordinate() + " (prev to " + coordinate + ")");
0977:                if (entry.getCoordinate() >= coordinate) {
0978:                    throw new IllegalStateException(
0979:                            "GetPrevious returned an invalid result:"
0980:                                    + entry.getCoordinate() + " (prev to "
0981:                                    + coordinate + ')');
0982:                }
0983:                return entry.getPosition();
0984:            }
0985:
0986:            private void ensureYMapping(final long coordinate, final boolean aux) {
0987:                final TableCutList.CutEntry cut = yBounds.get(coordinate);
0988:                if (cut == null) {
0989:                    final int result = yBounds.size();
0990:                    yBounds.put(coordinate, new TableCutList.CutEntry(
0991:                            coordinate, result, aux));
0992:
0993:                    final int oldRow = getPreviousRowPosition(coordinate);
0994:                    if (coordinate < yMaxBounds) {
0995:                        // oh, an insert operation. Make sure that everyone updates its state.
0996:                        rowInserted(coordinate, oldRow, result);
0997:                    }
0998:                } else if (cut.isAuxilary() && aux == false) {
0999:                    cut.makePermanent();
1000:                }
1001:            }
1002:
1003:            /**
1004:             * Gets the strict mode flag.
1005:             *
1006:             * @return true, if strict mode is enabled, false otherwise.
1007:             */
1008:            public boolean isStrict() {
1009:                return strict;
1010:            }
1011:
1012:            protected GenericObjectTable getLayoutBackend() {
1013:                return backend;
1014:            }
1015:
1016:            public boolean isEmpty() {
1017:                return ((backend.getColumnCount() == 0)
1018:                        && (backend.getRowCount() == 0) && xMaxBounds == 0 && yMaxBounds == 0);
1019:            }
1020:
1021:            /**
1022:             * Returns the position of the given element within the table. The TableRectangle contains row and cell indices, no
1023:             * layout coordinates.
1024:             *
1025:             * @param x      the element bounds for which the table bounds should be found.
1026:             * @param y      the element bounds for which the table bounds should be found.
1027:             * @param width  the element bounds for which the table bounds should be found.
1028:             * @param height the element bounds for which the table bounds should be found.
1029:             * @param rect   the returned rectangle or null, if a new instance should be created
1030:             * @return the filled table rectangle.
1031:             */
1032:            public TableRectangle getTableBounds(final long x, final long y,
1033:                    final long width, final long height, TableRectangle rect) {
1034:                if (rect == null) {
1035:                    rect = new TableRectangle();
1036:                }
1037:                final int x1 = xBounds.findKeyPosition(x,
1038:                        SheetLayout.LOWER_BOUNDS);
1039:                final int y1 = yBounds.findKeyPosition(y,
1040:                        SheetLayout.LOWER_BOUNDS);
1041:                final int x2 = xBounds.findKeyPosition(x + width,
1042:                        SheetLayout.UPPER_BOUNDS);
1043:                final int y2 = yBounds.findKeyPosition(y + height,
1044:                        SheetLayout.UPPER_BOUNDS);
1045:                rect.setRect(x1, y1, x2, y2);
1046:                return rect;
1047:            }
1048:
1049:            /**
1050:             * Returns the position of the given element within the table. The TableRectangle contains row and cell indices, no
1051:             * layout coordinates.
1052:             *
1053:             * @param bounds the element bounds for which the table bounds should be found.
1054:             * @param rect   the returned rectangle or null, if a new instance should be created
1055:             * @return the filled table rectangle.
1056:             */
1057:            public TableRectangle getTableBounds(final StrictBounds bounds,
1058:                    TableRectangle rect) {
1059:                if (rect == null) {
1060:                    rect = new TableRectangle();
1061:                }
1062:                final int x1 = xBounds.findKeyPosition(bounds.getX(),
1063:                        SheetLayout.LOWER_BOUNDS);
1064:                final int y1 = yBounds.findKeyPosition(bounds.getY(),
1065:                        SheetLayout.LOWER_BOUNDS);
1066:                final int x2 = xBounds.findKeyPosition(bounds.getX()
1067:                        + bounds.getWidth(), SheetLayout.UPPER_BOUNDS);
1068:                final int y2 = yBounds.findKeyPosition(bounds.getY()
1069:                        + bounds.getHeight(), SheetLayout.UPPER_BOUNDS);
1070:                rect.setRect(x1, y1, x2, y2);
1071:                return rect;
1072:            }
1073:
1074:            protected int mapColumn(final int xCutIndex) {
1075:                final TableCutList.CutEntry boundsCut = xBounds
1076:                        .getValueAt(xCutIndex);
1077:                if (boundsCut == null) {
1078:                    throw new IllegalStateException("There is no column at "
1079:                            + xCutIndex);
1080:                }
1081:                return boundsCut.getPosition();
1082:            }
1083:
1084:            protected int mapRow(final int yCutIndex) {
1085:                final TableCutList.CutEntry boundsCut = yBounds
1086:                        .getValueAt(yCutIndex);
1087:                if (boundsCut == null) {
1088:                    throw new IllegalStateException("There is no row at "
1089:                            + yCutIndex);
1090:                }
1091:                return boundsCut.getPosition();
1092:            }
1093:
1094:            /**
1095:             * A Callback method to inform the sheet layout, that the current page is complete, and no more content will be
1096:             * added.
1097:             */
1098:            public void pageCompleted() {
1099:                removeAuxilaryBounds();
1100:                clearObjectIdTable();
1101:            }
1102:
1103:            protected void removeAuxilaryBounds() {
1104:                ensureXMapping(this .xMaxBounds, false);
1105:                ensureYMapping(this .yMaxBounds, false);
1106:                // Log.debug("Size: " + getRowCount() + ", " + getColumnCount());
1107:
1108:                final ArrayList removedCuts = new ArrayList();
1109:                final TableCutList.CutEntry[] xEntries = (TableCutList.CutEntry[]) xBounds
1110:                        .getRawEntries().clone();
1111:                final int xEntrySize = xBounds.size();
1112:                for (int i = xEntrySize - 1; i >= 0; i--) {
1113:                    final TableCutList.CutEntry cut = xEntries[i];
1114:                    if (cut.isAuxilary()) {
1115:                        xBounds.remove(cut.getCoordinate());
1116:                        removedCuts.add(cut);
1117:                    }
1118:                }
1119:
1120:                // now join the cuts with their left neighbour ..
1121:                for (int i = 0; i < removedCuts.size(); i++) {
1122:                    // the col-cut that will be removed/merged/whatever ...
1123:                    final TableCutList.CutEntry removedCut = (TableCutList.CutEntry) removedCuts
1124:                            .get(i);
1125:                    final int removedColPosition = removedCut.getPosition();
1126:                    // the col cut marking the cell that will receive the merged content.
1127:                    final TableCutList.CutEntry prevCut = xBounds
1128:                            .getPrevious(removedCut.getCoordinate());
1129:                    final int previousColPosition = prevCut.getPosition();
1130:
1131:                    for (int row = 0; row < getRowCount(); row++) {
1132:                        final int mappedRow = mapRow(row);
1133:                        final TableCellDefinition leftBg = (TableCellDefinition) backend
1134:                                .getObject(mappedRow, previousColPosition);
1135:                        final TableCellDefinition rightBg = (TableCellDefinition) backend
1136:                                .getObject(mappedRow, removedColPosition);
1137:                        if (leftBg == null && rightBg == null) {
1138:                            continue;
1139:                        }
1140:                        if (leftBg == null) {
1141:                            final long x = prevCut.getCoordinate();
1142:                            final long y = getYPosition(row);
1143:                            final StrictBounds bounds = computeCellBounds(null,
1144:                                    x, y);
1145:                            final TableCellDefinition unionBg = rightBg
1146:                                    .normalize(bounds);
1147:                            backend.setObject(mappedRow, previousColPosition,
1148:                                    unionBg);
1149:                            backend.setObject(mappedRow, removedColPosition,
1150:                                    null);
1151:                        } else if (rightBg == null) {
1152:                            final long x = prevCut.getCoordinate();
1153:                            final long y = getYPosition(row);
1154:                            final StrictBounds bounds = computeCellBounds(null,
1155:                                    x, y);
1156:                            final TableCellDefinition unionBg = leftBg
1157:                                    .normalize(bounds);
1158:                            backend.setObject(mappedRow, previousColPosition,
1159:                                    unionBg);
1160:                            backend.setObject(mappedRow, removedColPosition,
1161:                                    null);
1162:                        } else {
1163:                            // now join ..
1164:                            final StrictBounds leftBounds = leftBg.getBounds();
1165:                            final StrictBounds newBounds = rightBg.getBounds()
1166:                                    .createUnion(leftBounds);
1167:                            final TableCellDefinition unionBg = leftBg
1168:                                    .normalize(newBounds);
1169:                            if (unionBg != null) {
1170:                                unionBg.setRight(rightBg.getRight());
1171:                            }
1172:
1173:                            backend.setObject(mappedRow, previousColPosition,
1174:                                    unionBg);
1175:                            backend.setObject(mappedRow, removedColPosition,
1176:                                    null);
1177:                        }
1178:                    }
1179:                }
1180:                removedCuts.clear();
1181:
1182:                final TableCutList.CutEntry[] yEntries = (TableCutList.CutEntry[]) yBounds
1183:                        .getRawEntries().clone();
1184:                final int ySize = yBounds.size();
1185:                for (int i = 0; i < ySize; i++) {
1186:                    final TableCutList.CutEntry cut = yEntries[i];
1187:                    if (cut.isAuxilary()) {
1188:                        removedCuts.add(cut);
1189:                    }
1190:                }
1191:
1192:                yBounds.removeAll(removedCuts);
1193:
1194:                // now join the cuts with their top neighbour ..
1195:                for (int i = 0; i < removedCuts.size(); i++) {
1196:                    // the row-cut that will be removed/merged/whatever ...
1197:                    final TableCutList.CutEntry removedCut = (TableCutList.CutEntry) removedCuts
1198:                            .get(i);
1199:                    final int rowPosition = removedCut.getPosition();
1200:                    // the row cut marking the cell that will receive the merged content.
1201:                    final TableCutList.CutEntry prevCut = yBounds
1202:                            .getPrevious(removedCut.getCoordinate());
1203:                    final int previousRowPosition = prevCut.getPosition();
1204:
1205:                    for (int col = 0; col < getColumnCount(); col++) {
1206:                        final int mappedColumn = mapColumn(col);
1207:                        final TableCellDefinition topBg = (TableCellDefinition) backend
1208:                                .getObject(previousRowPosition, mappedColumn);
1209:                        final TableCellDefinition bottomBg = (TableCellDefinition) backend
1210:                                .getObject(rowPosition, mappedColumn);
1211:                        if (topBg == null && bottomBg == null) {
1212:                            // do nothing ...
1213:                        } else if (topBg == null) {
1214:                            // the cut has been removed already, so that the coordinate given in the boundsCut is
1215:                            // now invalid. It would point to a different location now.
1216:                            // however, the x-positions are still valid.
1217:
1218:                            final long x = getXPosition(col);
1219:                            final long y = prevCut.getCoordinate();
1220:                            final StrictBounds bounds = computeCellBounds(null,
1221:                                    x, y);
1222:                            final TableCellDefinition unionBg = bottomBg
1223:                                    .normalize(bounds);
1224:                            backend.setObject(previousRowPosition,
1225:                                    mappedColumn, unionBg);
1226:                            backend.setObject(rowPosition, mappedColumn, null);
1227:                        } else if (bottomBg == null) {
1228:                            final long x = getXPosition(col);
1229:                            final long y = prevCut.getCoordinate();
1230:                            final StrictBounds bounds = computeCellBounds(null,
1231:                                    x, y);
1232:                            final TableCellDefinition unionBg = topBg
1233:                                    .normalize(bounds);
1234:                            backend.setObject(previousRowPosition,
1235:                                    mappedColumn, unionBg);
1236:                            backend.setObject(rowPosition, mappedColumn, null);
1237:                        } else {
1238:                            // now join ..
1239:                            final StrictBounds topBounds = topBg.getBounds();
1240:                            final StrictBounds newBounds = bottomBg.getBounds()
1241:                                    .createUnion(topBounds);
1242:                            final TableCellDefinition unionBg = topBg
1243:                                    .normalize(newBounds);
1244:                            if (unionBg != null) {
1245:                                unionBg.setBottom(bottomBg.getBottom());
1246:                            }
1247:                            backend.setObject(previousRowPosition,
1248:                                    mappedColumn, unionBg);
1249:                            backend.setObject(rowPosition, mappedColumn, null);
1250:                        }
1251:                    }
1252:                }
1253:            }
1254:
1255:            protected void clearObjectIdTable() {
1256:                objectIdTable.clear();
1257:                objectIdTable.ensureCapacity(backend.getRowCount(), backend
1258:                        .getColumnCount());
1259:            }
1260:
1261:            /**
1262:             * Returns the element at grid-position (x,y). This returns the cell background for a certain cell, or null, if there
1263:             * is no background at that cell.
1264:             *
1265:             * @param row    the row of the requested element
1266:             * @param column the column starting with zero.
1267:             * @return the element at the specified position.
1268:             */
1269:            public TableCellDefinition getBackgroundAt(final int row,
1270:                    final int column) {
1271:                final int mappedRow = mapRow(row);
1272:                final int mappedColumn = mapColumn(column);
1273:                return (TableCellDefinition) backend.getObject(mappedRow,
1274:                        mappedColumn);
1275:            }
1276:
1277:            /**
1278:             * Computes the height of the given row.
1279:             *
1280:             * @param row the row, for which the height should be computed.
1281:             * @return the height of the row.
1282:             * @throws IndexOutOfBoundsException if the row is invalid.
1283:             */
1284:            public long getRowHeight(final int row) {
1285:                final int rowCount = yBounds.size();
1286:                if (row >= rowCount) {
1287:                    throw new IndexOutOfBoundsException("Row " + row
1288:                            + " is invalid. Max valid row is " + (rowCount - 1));
1289:                }
1290:
1291:                final long bottomBorder;
1292:                if ((row + 1) < rowCount) {
1293:                    bottomBorder = yBounds.getKeyAt(row + 1);
1294:                } else {
1295:                    bottomBorder = yMaxBounds;
1296:                }
1297:
1298:                return bottomBorder - yBounds.getKeyAt(row);
1299:            }
1300:
1301:            public long getMaxHeight() {
1302:                return yMaxBounds;
1303:            }
1304:
1305:            public long getMaxWidth() {
1306:                return xMaxBounds;
1307:            }
1308:
1309:            public long getCellWidth(final int startCell) {
1310:                return getCellWidth(startCell, startCell + 1);
1311:            }
1312:
1313:            /**
1314:             * Computes the height of the given row.
1315:             *
1316:             * @param startCell the first cell in the range
1317:             * @param endCell   the last cell included in the cell range
1318:             * @return the height of the row.
1319:             * @throws IndexOutOfBoundsException if the row is invalid.
1320:             */
1321:            public long getCellWidth(final int startCell, final int endCell) {
1322:                if (startCell < 0) {
1323:                    throw new IndexOutOfBoundsException(
1324:                            "Start-Cell must not be negative");
1325:                }
1326:                if (endCell < 0) {
1327:                    throw new IndexOutOfBoundsException(
1328:                            "End-Cell must not be negative");
1329:                }
1330:                if (endCell < startCell) {
1331:                    throw new IndexOutOfBoundsException(
1332:                            "End-Cell must not smaller than end-cell");
1333:                }
1334:
1335:                final long rightBorder;
1336:                if (endCell >= xBounds.size()) {
1337:                    rightBorder = xMaxBounds;
1338:                } else {
1339:                    rightBorder = xBounds.getKeyAt(endCell);
1340:                }
1341:                return rightBorder - xBounds.getKeyAt(startCell);
1342:            }
1343:
1344:            public long getRowHeight(final int startRow, final int endRow) {
1345:                if (startRow < 0) {
1346:                    throw new IndexOutOfBoundsException(
1347:                            "Start-Cell must not be negative");
1348:                }
1349:                if (endRow < 0) {
1350:                    throw new IndexOutOfBoundsException(
1351:                            "End-Cell must not be negative");
1352:                }
1353:                if (endRow < startRow) {
1354:                    throw new IndexOutOfBoundsException(
1355:                            "End-Cell must not smaller than end-cell");
1356:                }
1357:
1358:                final long bottomBorder;
1359:                if (endRow >= yBounds.size()) {
1360:                    bottomBorder = yMaxBounds;
1361:                } else {
1362:                    bottomBorder = yBounds.getKeyAt(endRow);
1363:                }
1364:                return bottomBorder - yBounds.getKeyAt(startRow);
1365:            }
1366:
1367:            /**
1368:             * The current number of columns. Of course, this value begins to be reliable, once the number of columns is known
1369:             * (that is at the end of the layouting process).
1370:             *
1371:             * @return the number columns.
1372:             */
1373:            public int getColumnCount() {
1374:                return Math.max(xBounds.size() - 1, 0);
1375:            }
1376:
1377:            /**
1378:             * The current number of rows. Of course, this value begins to be reliable, once the number of rows is known (that is
1379:             * at the end of the layouting process).
1380:             *
1381:             * @return the number columns.
1382:             */
1383:            public int getRowCount() {
1384:                return Math.max(yBounds.size() - 1, 0);
1385:            }
1386:
1387:            public long getXPosition(final int col) {
1388:                return xBounds.getKeyAt(col);
1389:            }
1390:
1391:            public long getYPosition(final int row) {
1392:                return yBounds.getKeyAt(row);
1393:            }
1394:
1395:            public TableCellDefinition getBackgroundAt(final int x,
1396:                    final int y, final int columnSpan, final int rowSpan) {
1397:                if (rowSpan == 1 && columnSpan == 1) {
1398:                    return getBackgroundAt(x, y);
1399:                }
1400:
1401:                // make a large-scale merge ...
1402:                final StrictBounds bounds = new StrictBounds();
1403:                // First merge all cells on each row. This is a merge in horizontal direction, much like we did on end-Sheet.
1404:                // This way we get a list of 1-col-cells per row, that can then be merged vertically. 
1405:                final TableCellDefinition[] rowBackgrounds = new TableCellDefinition[rowSpan];
1406:                for (int row = 0; row < rowSpan; row += 1) {
1407:                    for (int col = 0; col < columnSpan; col += 1) {
1408:                        final TableCellDefinition rightBackground = getBackgroundAt(
1409:                                y + row, x + col);
1410:                        if (rightBackground == null) {
1411:                            continue;
1412:                        }
1413:                        if (rowBackgrounds[row] == null) {
1414:                            rowBackgrounds[row] = rightBackground;
1415:                        } else {
1416:                            // merge.
1417:                            final TableCellDefinition leftBackground = rowBackgrounds[row];
1418:                            bounds.setRect(leftBackground.getX(),
1419:                                    leftBackground.getY(), rightBackground
1420:                                            .getX()
1421:                                            + rightBackground.getWidth()
1422:                                            - leftBackground.getX(),
1423:                                    leftBackground.getHeight());
1424:                            rowBackgrounds[row] = leftBackground.merge(
1425:                                    rightBackground, bounds);
1426:                        }
1427:
1428:                    }
1429:                }
1430:
1431:                TableCellDefinition topBackground = rowBackgrounds[0];
1432:                for (int i = 1; i < rowBackgrounds.length; i++) {
1433:                    final TableCellDefinition bottomBackground = rowBackgrounds[i];
1434:                    if (bottomBackground == null) {
1435:                        continue;
1436:                    }
1437:                    if (topBackground == null) {
1438:                        topBackground = bottomBackground;
1439:                        continue;
1440:                    }
1441:                    bounds.setRect(topBackground.getX(), topBackground.getY(),
1442:                            topBackground.getWidth(), bottomBackground.getY()
1443:                                    + bottomBackground.getHeight()
1444:                                    - topBackground.getY());
1445:                    topBackground = topBackground.merge(bottomBackground,
1446:                            bounds);
1447:                }
1448:                // normalize before returning ..
1449:                bounds.setRect(getXPosition(x), getYPosition(y), getCellWidth(
1450:                        x, x + columnSpan), getRowHeight(y, y + rowSpan));
1451:                return topBackground.normalize(bounds);
1452:            }
1453:        }
w___w___w___.___j__a_v__a___2__s_.___c_o___m___ | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.