Java tutorial
/* * 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 <http://www.gnu.org/licenses/>. */ package net.sf.jasperreports.engine.base; import java.io.Serializable; import java.util.Arrays; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import net.sf.jasperreports.engine.JRConstants; import net.sf.jasperreports.engine.JRPrintElement; /** * @author Lucian Chirita (lucianc@users.sourceforge.net) */ public class ElementsBlockList implements ElementStore, Serializable { private static final long serialVersionUID = JRConstants.SERIAL_VERSION_UID; private static final Log log = LogFactory.getLog(ElementsBlockList.class); private static final int INITIAL_SIZE = 3; private static final int MAX_INCREMENT = 200; private int blockCount; private int[] offsets; private ElementsBlock[] blocks; private int size; private transient int lastIndex = -1; public ElementsBlockList(ElementsBlock block) { blocks = new ElementsBlock[INITIAL_SIZE]; offsets = new int[INITIAL_SIZE]; blockCount = 1; blocks[0] = block; offsets[0] = 0; size = block.size(); } public void addBlock(ElementsBlock block) { incrementBlocks(); blocks[blockCount] = block; offsets[blockCount] = size; size += block.size(); ++blockCount; } protected void addBlock() { ElementsBlock block = new ElementsBlock(blocks[0].getContext(), blocks[0].getPage()); addBlock(block); if (log.isDebugEnabled()) { log.debug("created block " + block + " at offset " + size); } } protected void incrementBlocks() { if (blockCount == blocks.length) { int newSize = Math.min((blockCount * 3) / 2 + 1, blockCount + MAX_INCREMENT); ElementsBlock[] newBlocks = new ElementsBlock[newSize]; System.arraycopy(blocks, 0, newBlocks, 0, blockCount); blocks = newBlocks; int[] newOffsets = new int[newSize]; System.arraycopy(offsets, 0, newOffsets, 0, blockCount); offsets = newOffsets; } } public ElementsBlock[] getBlocks() { return Arrays.copyOf(blocks, blockCount); } @Override public int size() { return size; } @Override public JRPrintElement get(int index) { int blockIndex = blockIndex(index); return blocks[blockIndex].get(index - offsets[blockIndex]); } @Override public boolean add(JRPrintElement element) { // allow the last block to overflow. if (!blocks[blockCount - 1].add(element, false)) { // the last block overflowed, create a new one addBlock(); // add the element to the new block blocks[blockCount - 1].add(element, true); } ++size; return true; } @Override public boolean add(int index, JRPrintElement element) { int blockIndex = blockIndex(index); // only allow the last block to overflow. // we're not moving elements across blocks when adding an element // in the middle of a page. // this can lead to bigger blocks, but the case is exotic. boolean last = blockIndex == blockCount - 1; if (last) { if (!blocks[blockIndex].add(index - offsets[blockIndex], element, false)) { // the last block overflowed, create a new one addBlock(); // add the element to the new block blockIndex = blockCount - 1; blocks[blockIndex].add(index - offsets[blockIndex], element, true); } } else { blocks[blockIndex].add(index - offsets[blockIndex], element, true); // increment offsets of subsequent blocks for (int idx = blockIndex + 1; idx < blockCount; ++idx) { ++offsets[blockIndex]; } } ++size; return true; } @Override public JRPrintElement set(int index, JRPrintElement element) { int blockIndex = blockIndex(index); return blocks[blockIndex].set(index - offsets[blockIndex], element); } @Override public JRPrintElement remove(int index) { // FIXME removing elements from an existing block without moving elements // across blocks. this can lead to small blocks if many elements are // removed from a page, such as when we have a group with keep together. int blockIndex = blockIndex(index); ElementsBlock block = blocks[blockIndex]; JRPrintElement element = block.remove(index - offsets[blockIndex]); // decrement offsets of subsequent blocks if (blockIndex + 1 < blockCount) { for (int idx = blockIndex + 1; idx < blockCount; ++idx) { --offsets[idx]; } } // if the block was left empty and this is not the only block, get rid of it if (blockCount > 1 && block.isEmpty()) { // shift the remaining blocks for (int idx = blockIndex + 1; idx < blockCount; ++idx) { blocks[idx - 1] = blocks[idx]; offsets[idx - 1] = offsets[idx]; } --blockCount; blocks[blockCount] = null; offsets[blockCount] = 0; } --size; return element; } @Override public void dispose() { for (int idx = 0; idx < blockCount; ++idx) { blocks[idx].dispose(); } } protected int blockIndex(int index) { if (index < 0) { throw new IndexOutOfBoundsException("index: " + index); } // see if the index falls in the lastIndex block if (lastIndex >= 0 && lastIndex < blockCount) { if (index >= offsets[lastIndex] && (lastIndex + 1 == blockCount || index < offsets[lastIndex + 1])) { return lastIndex; } } int blockIndex = Arrays.binarySearch(offsets, 0, blockCount, index); if (blockIndex < 0) { blockIndex = -blockIndex - 2; } // caching last index for fast serial access lastIndex = blockIndex; return blockIndex; } @Override public JRVirtualPrintPage getPage() { return blocks[0].getPage(); } }