Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* JasperReports - Free Java Reporting Library.
* Copyright (C) 2001 - 2019 TIBCO Software Inc. All rights reserved.
* http://www.jaspersoft.com
*
* Unless you have purchased a commercial license agreement from Jaspersoft,
* the following license terms apply:
*
* This program is part of JasperReports.
*
* JasperReports is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* JasperReports is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with JasperReports. If not, see .
*/
/*
* Contributors:
* Greg Hilton
*/
package net.sf.jasperreports.engine.export;
import java.awt.Color;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import net.sf.jasperreports.engine.JRBoxContainer;
import net.sf.jasperreports.engine.JRLineBox;
import net.sf.jasperreports.engine.JRPrintElement;
import net.sf.jasperreports.engine.JRPrintFrame;
import net.sf.jasperreports.engine.JRPrintPage;
import net.sf.jasperreports.engine.JasperPrint;
import net.sf.jasperreports.engine.type.ModeEnum;
import net.sf.jasperreports.engine.util.JRBoxUtil;
import net.sf.jasperreports.engine.util.Pair;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Utility class used by grid exporters to create a grid for page layout.
*
* @author Lucian Chirita ([email protected])
*/
public class JRGridLayout
{
private static final Log log = LogFactory.getLog(JRGridLayout.class);
private final ExporterNature nature;
private final List elementList;
private final Map cellSizes;
private final Map cellStyles;
private final Map, EmptyGridCell> emptyCells;
private int width;
private int height;
private int offsetX;
private int offsetY;
private CutsInfo xCuts;
private CutsInfo yCuts;
private Grid grid;
private Map boxesCache;
private boolean hasTopMargin = true;
private boolean hasBottomMargin = true;
private boolean hasLeftMargin = true;
private boolean hasRightMargin = true;
private boolean isNested;
/**
* Constructor.
*
* @param elements the elements that should arranged in a grid
* @param width the width available for the grid
* @param height the height available for the grid
* @param offsetX horizontal element position offset
* @param offsetY vertical element position offset
*/
public JRGridLayout(
ExporterNature nature,
List elements,
int width,
int height,
int offsetX,
int offsetY
)
{
this(
nature,
elements,
width,
height,
offsetX,
offsetY,
null //xCuts
);
}
/**
* Constructor.
*
* @param elements the elements that should arranged in a grid
* @param width the width available for the grid
* @param height the height available for the grid
* @param offsetX horizontal element position offset
* @param offsetY vertical element position offset
* @param xCuts An optional list of pre-calculated X cuts.
*/
public JRGridLayout(
ExporterNature nature,
List elements,
int width,
int height,
int offsetX,
int offsetY,
CutsInfo xCuts
)
{
this.nature = nature;
this.elementList = elements;
// TODO lucianc cache these across report pages?
this.cellSizes = new HashMap();
this.cellStyles = new HashMap();
this.emptyCells = new HashMap, EmptyGridCell>();
this.height = height;
this.width = width;
this.offsetX = offsetX;
this.offsetY = offsetY;
this.xCuts = xCuts;
boxesCache = new HashMap();
layoutGrid(null, elements);
}
/**
* Constructor.
*
* @param width the width available for the grid
* @param height the height available for the grid
* @param offsetX horizontal element position offset
* @param offsetY vertical element position offset
*/
protected JRGridLayout(
JRGridLayout parent,
List elements,
int width,
int height,
int offsetX,
int offsetY,
PrintElementIndex parentElementIndex
)
{
this.nature = parent.nature;
this.elementList = parent.elementList;
this.cellSizes = parent.cellSizes;
this.cellStyles = parent.cellStyles;
this.emptyCells = parent.emptyCells;
this.height = height;
this.width = width;
this.offsetX = offsetX;
this.offsetY = offsetY;
//this constructor is called only in nested grids:
this.isNested = true;
boxesCache = new HashMap();
layoutGrid(parentElementIndex, elements);
}
public JRPrintElement getElement(PrintElementIndex parentIndex, int index)
{
// TODO lucianc keep a cache of current element position?
JRPrintElement element;
if (parentIndex == null)
{
element = elementList.get(index);
}
else
{
JRPrintFrame parentFrame = (JRPrintFrame) getElement(parentIndex.getParentIndex(), parentIndex.getIndex());
element = parentFrame.getElements().get(index);
}
return element;
}
/**
* Constructs the element grid.
* @param parentElementIndex
*/
protected void layoutGrid(PrintElementIndex parentElementIndex, List elements)
{
boolean createXCuts = (xCuts == null);
xCuts = createXCuts ? new CutsInfo() : xCuts;
yCuts = nature.isIgnoreLastRow() ? new CutsInfo(0) : new CutsInfo(height);
if(!isNested && nature.isIgnorePageMargins()) //FIXMEXLS left and right margins are not ignored when all pages on a single sheet
{
// TODO lucianc this is an extra virtualization iteration
setMargins(elements);
if(createXCuts)
{
if(hasLeftMargin)
{
xCuts.removeCutOffset(0);
}
}
if(hasTopMargin)
{
yCuts.removeCutOffset(0);
}
if(hasBottomMargin)
{
yCuts.removeCutOffset(height);
}
}
createCuts(elements, offsetX, offsetY, createXCuts);
// add a cut at the width if it's a nested grid, or if the right margin
// is not to be removed and no element goes beyond the width
if (createXCuts && (isNested
|| (!(nature.isIgnorePageMargins() && hasRightMargin)
&& !(xCuts.hasCuts() && xCuts.getLastCutOffset() >= width))))
{
xCuts.addCutOffset(width);
}
xCuts.use();
yCuts.use();
int colCount = Math.max(xCuts.size() - 1, 0);
int rowCount = Math.max(yCuts.size() - 1, 0);
grid = new Grid(rowCount, colCount);
for(int row = 0; row < rowCount; row++)
{
for(int col = 0; col < colCount; col++)
{
GridCellSize size = cellSize(
xCuts.getCutOffset(col + 1) - xCuts.getCutOffset(col),
yCuts.getCutOffset(row + 1) - yCuts.getCutOffset(row),
1,
1
);
grid.set(row, col, emptyCell(size, null));
}
}
setGridElements(parentElementIndex, elements,
offsetX, offsetY,
0, 0, rowCount, colCount);
width = xCuts.getTotalLength();
height = yCuts.getTotalLength();
}
protected GridCellSize cellSize(int width, int height, int colSpan, int rowSpan)
{
GridCellSize key = new GridCellSize(width, height, colSpan, rowSpan);
GridCellSize size = cellSizes.get(key);
if (size == null)
{
size = key;
cellSizes.put(key, size);
if (log.isTraceEnabled())
{
log.trace(this + " added cell size " + size);
}
}
return size;
}
protected void createCuts(List elements, int elementOffsetX, int elementOffsetY, boolean createXCuts)
{
for (Iterator it = elements.iterator(); it.hasNext();)
{
JRPrintElement element = it.next();
if (nature.isToExport(element))
{
if (createXCuts)
{
xCuts.addCutOffset(element.getX() + elementOffsetX);
xCuts.addCutOffset(element.getX() + element.getWidth() + elementOffsetX);
}
yCuts.addCutOffset(element.getY() + elementOffsetY);
yCuts.addCutOffset(element.getY() + element.getHeight() + elementOffsetY);
JRPrintFrame frame = element instanceof JRPrintFrame ? (JRPrintFrame)element : null;
if (frame != null && nature.isDeep(frame))
{
createCuts(
frame.getElements(),
element.getX() + elementOffsetX + frame.getLineBox().getLeftPadding(),
element.getY() + elementOffsetY + frame.getLineBox().getTopPadding(),
createXCuts
);
}
}
}
}
protected void setMargins(List elements)
{
for (Iterator it = elements.iterator(); it.hasNext();)
{
JRPrintElement element = it.next();
if (nature.isToExport(element))
{
if(hasLeftMargin && element.getX() <= 0)
{
hasLeftMargin = false;
}
if(hasRightMargin && element.getX() >= width - element.getWidth())
{
hasRightMargin = false;
}
if(hasTopMargin && element.getY() <= 0)
{
hasTopMargin = false;
}
if(hasBottomMargin && element.getY() >= height - element.getHeight())
{
hasBottomMargin = false;
}
}
}
}
protected void setGridElements(PrintElementIndex parentIndex, List elements,
int elementOffsetX, int elementOffsetY,
int startRow, int startCol, int endRow, int endCol)
{
for (ListIterator it = elements.listIterator(elements.size()); it.hasPrevious();)
{
JRPrintElement element = it.previous();
int elementIndex = it.nextIndex();
if (nature.isToExport(element))
{
int x = element.getX() + elementOffsetX;
int y = element.getY() + elementOffsetY;
int col1 = xCuts.indexOfCutOffset(x);
int row1 = yCuts.indexOfCutOffset(y);
int col2 = xCuts.indexOfCutOffset(x + element.getWidth());
int row2 = yCuts.indexOfCutOffset(y + element.getHeight());
if (!isOverlap(row1, col1, row2, col2))
{
JRPrintFrame frame = element instanceof JRPrintFrame ? (JRPrintFrame)element : null;
if (frame != null && nature.isDeep(frame))
{
PrintElementIndex frameIndex = new PrintElementIndex(parentIndex, elementIndex);
setGridElements(
frameIndex, frame.getElements(),
x + frame.getLineBox().getLeftPadding(),
y + frame.getLineBox().getTopPadding(),
row1, col1, row2, col2
);
setFrameCellsStyle(frame, row1, col1, row2, col2);
}
else
{
setGridElement(element, parentIndex, elementIndex, row1, col1, row2, col2);
}
}
}
}
if (nature.isHorizontallyMergeEmptyCells())
{
horizontallyMergeEmptyCells(startRow, startCol, endRow, endCol);
}
}
protected EmptyGridCell emptyCell(GridCellSize size, GridCellStyle style)
{
Pair key = new Pair(size, style);
EmptyGridCell cell = emptyCells.get(key);
if (cell == null)
{
cell = new EmptyGridCell(size, style);
emptyCells.put(key, cell);
if (log.isDebugEnabled())
{
log.debug(this + " created empty cell for " + size + " and " + style);
}
}
return cell;
}
protected void horizontallyMergeEmptyCells(int startRow, int startCol, int endRow, int endCol)
{
for (int row = startRow; row < endRow; ++row)
{
int startSpan = -1;
int spanWidth = 0;
int col = startCol;
for (; col < endCol; ++col)
{
JRExporterGridCell cell = grid.get(row, col);
if (isEmpty(cell))
{
if (startSpan == -1)
{
startSpan = col;
}
spanWidth += cell.getWidth();
}
else
{
if (startSpan != -1 && col - startSpan > 1)
{
spanEmptyCell(row, startSpan, spanWidth, col - startSpan);
}
startSpan = -1;
spanWidth = 0;
}
}
if (startSpan != -1 && col - startSpan > 1)
{
spanEmptyCell(row, startSpan, spanWidth, col - startSpan);
}
}
}
protected void spanEmptyCell(int row, int col, int spanWidth, int colSpan)
{
EmptyGridCell spanCell = (EmptyGridCell) grid.get(row, col);
GridCellSize newSize = cellSize(spanWidth, spanCell.getHeight(),
colSpan, spanCell.getRowSpan());
grid.set(row, col, emptyCell(newSize, spanCell.getStyle()));
//TODO set OCCUPIED_CELL?
}
protected boolean isEmpty(JRExporterGridCell cell)
{
return cell.getType() == JRExporterGridCell.TYPE_EMPTY_CELL && ((EmptyGridCell) cell).isEmpty();
}
protected boolean isOverlap(int row1, int col1, int row2, int col2)
{
boolean isOverlap = false;
if (nature.isSpanCells())
{
is_overlap_out:
for (int row = row1; row < row2; row++)
{
for (int col = col1; col < col2; col++)
{
if (!isEmpty(grid.get(row, col)))
{
isOverlap = true;
break is_overlap_out;
}
}
}
}
else
{
isOverlap = !isEmpty(grid.get(row1, col1));
}
return isOverlap;
}
protected void setGridElement(JRPrintElement element, PrintElementIndex parentIndex, int elementIndex,
int row1, int col1, int row2, int col2)
{
yCuts.addUsage(row1, Cut.USAGE_NOT_EMPTY);
xCuts.addUsage(col1, Cut.USAGE_NOT_EMPTY);
int rowSpan = nature.isSpanCells() ? row2 - row1 : 1;
int colSpan = nature.isSpanCells() ? col2 - col1 : 1;
JRExporterGridCell gridCell = new ElementGridCell(
this,
parentIndex,
elementIndex,
cellSize(
element.getWidth(),
element.getHeight(),
colSpan,
rowSpan
));
nature.setXProperties(xCuts, element, row1, col1, row2, col2);
nature.setYProperties(yCuts, element, row1, col1, row2, col2);
if (nature.isSpanCells())
{
OccupiedGridCell occupiedGridCell = new OccupiedGridCell(gridCell);
for (int row = row1; row < row2; row++)
{
for (int col = col1; col < col2; col++)
{
grid.set(row, col, occupiedGridCell);
}
yCuts.addUsage(row, Cut.USAGE_SPANNED);
}
for (int col = col1; col < col2; col++)
{
xCuts.addUsage(col, Cut.USAGE_SPANNED);
}
}
if (col2 - col1 != 0 && row2 - row1 != 0)
{
JRLineBox box = (element instanceof JRBoxContainer)?((JRBoxContainer)element).getLineBox():null;
gridCell.setStyle(cellStyle(null, null, box));
if (nature.isBreakBeforeRow(element))
{
yCuts.addUsage(row1, Cut.USAGE_BREAK);
}
if (nature.isBreakAfterRow(element))
{
yCuts.addUsage(row1 + rowSpan, Cut.USAGE_BREAK);
}
grid.set(row1, col1, gridCell);
}
}
protected GridCellStyle cellStyle(Color backcolor, Color forecolor, JRLineBox box)
{
if (backcolor == null && forecolor == null && box == null)
{
return null;
}
GridCellStyle key = new GridCellStyle(backcolor, forecolor, box);
GridCellStyle style = cellStyles.get(key);
if (style == null)
{
style = key;
cellStyles.put(key, style);
if (log.isTraceEnabled())
{
log.trace(this + " added cell style " + style);
}
}
return style;
}
protected void setFrameCellsStyle(JRPrintFrame frame, int row1, int col1, int row2, int col2)
{
Color backcolor = frame.getModeValue() == ModeEnum.OPAQUE ? frame.getBackcolor() : null;
for (int row = row1; row < row2; row++)
{
for (int col = col1; col < col2; col++)
{
JRExporterGridCell cell = grid.get(row, col);
boolean modifiedStyle = false;
Color cellBackcolor = cell.getBackcolor();
if (cellBackcolor == null)
{
if (frame.getModeValue() == ModeEnum.OPAQUE)
{
cellBackcolor = backcolor;
modifiedStyle = true;
}
}
Color cellForecolor = cell.getForecolor();
if (cellForecolor == null)
{
cellForecolor = frame.getForecolor();
modifiedStyle = true;
}
boolean keepLeft = col == col1;
boolean keepRight = col == col2 - cell.getColSpan();
boolean keepTop = row == row1;
boolean keepBottom = row == row2 - cell.getRowSpan();
JRLineBox cellBox = cell.getBox();
if (keepLeft || keepRight || keepTop || keepBottom)
{
BoxKey key = new BoxKey(frame.getLineBox(), cellBox, keepLeft, keepRight, keepTop, keepBottom);
JRLineBox modBox = boxesCache.get(key);
if (modBox == null)
{
modBox = JRBoxUtil.copyBordersNoPadding(frame.getLineBox(), keepLeft, keepRight, keepTop, keepBottom, cellBox);
boxesCache.put(key, modBox);
}
cellBox = modBox;
modifiedStyle = true;
}
if (modifiedStyle)
{
GridCellStyle newStyle = cellStyle(cellBackcolor, cellForecolor, cellBox);
grid.set(row, col, changeStyle(cell, newStyle));
}
}
}
}
protected JRExporterGridCell changeStyle(JRExporterGridCell cell, GridCellStyle newStyle)
{
if (cell.getType() == JRExporterGridCell.TYPE_EMPTY_CELL)
{
// empty cells are shared so they should not be modified
return emptyCell(cell.getSize(), newStyle);
}
// other types of cells can be modified
cell.setStyle(newStyle);
return cell;
}
/**
* Returns the constructed element grid.
*
* @return the constructed element grid
*/
public Grid getGrid()
{
return grid;
}
/**
* Returns the list of cut points on the X axis for the grid.
*
* @return the list of cut points on the X axis for the grid
*/
public CutsInfo getXCuts()
{
return xCuts;
}
/**
* Returns the list of cut points on the Y axis for the grid.
*
* @return the list of cut points on the Y axis for the grid
*/
public CutsInfo getYCuts()
{
return yCuts;
}
/**
* Returns the width available for the grid.
*
* @return the width available for the grid
*/
public int getWidth()
{
return width;
}
public int getColumnWidth(int col)
{
return xCuts.getCutOffset(col + 1) - xCuts.getCutOffset(col);
}
public int getRowHeight(int row)
{
return yCuts.getCutOffset(row + 1) - yCuts.getCutOffset(row);
}
public int getMaxRowHeight(int rowIndex)
{
GridRow row = grid.getRow(rowIndex);
int maxRowHeight = row.get(0).getHeight();
int rowSize = row.size();
for (int col = 0; col < rowSize; col++)
{
JRExporterGridCell cell = row.get(col);
if (cell.getType() != JRExporterGridCell.TYPE_OCCUPIED_CELL)
{
if (maxRowHeight < cell.getHeight())
{
maxRowHeight = cell.getHeight();
}
}
}
return maxRowHeight;
}
public static int getRowHeight(GridRow row)//FIXMEODT are we still using this?
{
JRExporterGridCell firstCell = row.get(0);
if (firstCell.getRowSpan() == 1 && firstCell.getType() != JRExporterGridCell.TYPE_OCCUPIED_CELL) //quick exit
{
return firstCell.getHeight();
}
int rowHeight = 0;
int minSpanIdx = 0;
int colCount = row.size();
int col;
for (col = 0; col < colCount; col++)
{
JRExporterGridCell cell = row.get(col);
if (cell.getType() != JRExporterGridCell.TYPE_OCCUPIED_CELL)
{
if (cell.getRowSpan() == 1)
{
rowHeight = cell.getHeight();
break;
}
if (cell.getRowSpan() < row.get(minSpanIdx).getRowSpan())
{
minSpanIdx = col;
}
}
}
if (col >= colCount) //no cell with rowSpan = 1 was found, getting the height of the cell with min rowSpan
{
rowHeight = row.get(minSpanIdx).getHeight();
}
return rowHeight;
}
/**
* This static method calculates all the X cuts for a list of pages.
*
* @param jasperPrint
* The JasperPrint document.
* @param startPageIndex
* The first page to consider.
* @param endPageIndex
* The last page to consider.
* @param offsetX
* horizontal element position offset
*/
public static CutsInfo calculateXCuts(ExporterNature nature, JasperPrint jasperPrint, int startPageIndex, int endPageIndex, int offsetX)
{
CutsInfo xCuts = new CutsInfo();
List pages = jasperPrint.getPages();
for (int pageIndex = startPageIndex; pageIndex <= endPageIndex; pageIndex++)
{
JRPrintPage page = pages.get(pageIndex);
addXCuts(nature, page.getElements(), offsetX, xCuts);
}
// add a cut at the page width if there are not parts and if no element goes beyond the page width
if (!jasperPrint.hasParts())
{
int width = jasperPrint.getPageWidth();
int lastCut = xCuts.getLastCutOffset();
if (lastCut < width)
{
xCuts.addCutOffset(width);
}
}
return xCuts;
}
/**
* This static method calculates all the X cuts for a list of pages.
*
* @param pages
* The list of pages.
* @param startPageIndex
* The first page to consider.
* @param endPageIndex
* The last page to consider.
* @param width
* The page width
* @param offsetX
* horizontal element position offset
* @deprecated Replaced by {@link #calculateXCuts(ExporterNature, JasperPrint, int, int, int)}.
*/
public static CutsInfo calculateXCuts(ExporterNature nature, List pages, int startPageIndex, int endPageIndex, int width, int offsetX)
{
CutsInfo xCuts = new CutsInfo();
for (int pageIndex = startPageIndex; pageIndex <= endPageIndex; pageIndex++)
{
JRPrintPage page = pages.get(pageIndex);
addXCuts(nature, page.getElements(), offsetX, xCuts);
}
// add a cut at the page width if no element goes beyond the width
int lastCut = xCuts.getLastCutOffset();
if (lastCut < width)
{
xCuts.addCutOffset(width);
}
return xCuts;
}
/**
* This static method calculates the X cuts for a list of print elements and
* stores them in the list indicated by the xCuts parameter.
*
* @param elementsList
* The list of elements to be used to determine the X cuts.
* @param elementOffsetX
* horizontal element position offset
* @param xCuts
* The list to which the X cuts are to be added.
*/
protected static void addXCuts(ExporterNature nature, List elementsList, int elementOffsetX, CutsInfo xCuts)
{
for (Iterator it = elementsList.iterator(); it.hasNext();)
{
JRPrintElement element = it.next();
if (nature.isToExport(element))
{
xCuts.addCutOffset(element.getX() + elementOffsetX);
xCuts.addCutOffset(element.getX() + element.getWidth() + elementOffsetX);
if (element instanceof JRPrintFrame)
{
JRPrintFrame frame = (JRPrintFrame) element;
addXCuts(
nature,
frame.getElements(),
element.getX() + elementOffsetX + frame.getLineBox().getLeftPadding(),
xCuts
);
}
nature.setXProperties(xCuts.getPropertiesMap(), element);
}
}
}
/**
*
*/
protected static class BoxKey
{
final JRLineBox box;
final JRLineBox cellBox;
final boolean left;
final boolean right;
final boolean top;
final boolean bottom;
final int hashCode;
BoxKey(JRLineBox box, JRLineBox cellBox, boolean left, boolean right, boolean top, boolean bottom)
{
this.box = box;
this.cellBox = cellBox;
this.left = left;
this.right = right;
this.top = top;
this.bottom = bottom;
int hash = box.hashCode();
if (cellBox != null)
{
hash = 31*hash + cellBox.hashCode();
}
hash = 31*hash + (left ? 1231 : 1237);
hash = 31*hash + (right ? 1231 : 1237);
hash = 31*hash + (top ? 1231 : 1237);
hash = 31*hash + (bottom ? 1231 : 1237);
hashCode = hash;
}
@Override
public boolean equals(Object obj)
{
if (obj == this)
{
return true;
}
BoxKey b = (BoxKey) obj;
return b.box.equals(box) &&
(b.cellBox == null ? cellBox == null : (cellBox != null && b.cellBox.equals(cellBox))) &&
b.left == left && b.right == right && b.top == top && b.bottom == bottom;
}
@Override
public int hashCode()
{
return hashCode;
}
}
}