/********************************************************************* * * Copyright (C) 2003 Andrew Khan * * This library 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 2.1 of the License, or (at your option) any later version. * * This library 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 this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ***************************************************************************/ package jxl.write.biff; import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; import common.Assert; import common.Logger; import jxl.Cell; import jxl.CellType; import jxl.Range; import jxl.WorkbookSettings; import jxl.biff.SheetRangeImpl; import jxl.write.Blank; import jxl.write.WritableSheet; import jxl.write.WriteException; /** * Contains all the merged cells, and the necessary logic for checking * for intersections and for handling very large amounts of merging */ class MergedCells { /** * The logger */ private static Logger logger = Logger.getLogger(MergedCells.class); /** * The list of merged cells */ private ArrayList ranges; /** * The sheet containing the cells */ private WritableSheet sheet; /** * The maximum number of ranges per sheet */ private static final int maxRangesPerSheet = 1020; /** * Constructor */ public MergedCells(WritableSheet ws) { ranges = new ArrayList(); sheet = ws; } /** * Adds the range to the list of merged cells. Does no checking * at this stage * * @param range the range to add */ void add(Range r) { ranges.add(r); } /** * Used to adjust the merged cells following a row insertion */ void insertRow(int row) { // Adjust any merged cells SheetRangeImpl sr = null; Iterator i = ranges.iterator(); while (i.hasNext()) { sr = (SheetRangeImpl) i.next(); sr.insertRow(row); } } /** * Used to adjust the merged cells following a column insertion */ void insertColumn(int col) { SheetRangeImpl sr = null; Iterator i = ranges.iterator(); while (i.hasNext()) { sr = (SheetRangeImpl) i.next(); sr.insertColumn(col); } } /** * Used to adjust the merged cells following a column removal */ void removeColumn(int col) { SheetRangeImpl sr = null; Iterator i = ranges.iterator(); while (i.hasNext()) { sr = (SheetRangeImpl) i.next(); if (sr.getTopLeft().getColumn() == col && sr.getBottomRight().getColumn() == col) { // The column with the merged cells on has been removed, so get // rid of it from the list i.remove(); } else { sr.removeColumn(col); } } } /** * Used to adjust the merged cells following a row removal */ void removeRow(int row) { SheetRangeImpl sr = null; Iterator i = ranges.iterator(); while (i.hasNext()) { sr = (SheetRangeImpl) i.next(); if (sr.getTopLeft().getRow() == row && sr.getBottomRight().getRow() == row) { // The row with the merged cells on has been removed, so get // rid of it from the list i.remove(); } else { sr.removeRow(row); } } } /** * Gets the cells which have been merged on this sheet * * @return an array of range objects */ Range[] getMergedCells() { Range[] cells = new Range[ranges.size()]; for (int i=0; i < cells.length; i++) { cells[i] = (Range) ranges.get(i); } return cells; } /** * Unmerges the specified cells. The Range passed in should be one that * has been previously returned as a result of the getMergedCells method * * @param r the range of cells to unmerge */ void unmergeCells(Range r) { int index = ranges.indexOf(r); if (index != -1) { ranges.remove(index); } } /** * Called prior to writing out in order to check for intersections */ private void checkIntersections() { ArrayList newcells = new ArrayList(ranges.size()); for (Iterator mci = ranges.iterator(); mci.hasNext() ; ) { SheetRangeImpl r = (SheetRangeImpl) mci.next(); // Check that the range doesn't intersect with any existing range Iterator i = newcells.iterator(); SheetRangeImpl range = null; boolean intersects = false; while (i.hasNext() && !intersects) { range = (SheetRangeImpl) i.next(); if (range.intersects(r)) { logger.warn("Could not merge cells " + r + " as they clash with an existing set of merged cells."); intersects = true; } } if (!intersects) { newcells.add(r); } } ranges = newcells; } /** * Checks the cell ranges for intersections, or if the merged cells * contains more than one item of data */ private void checkRanges() { try { SheetRangeImpl range = null; // Check all the ranges to make sure they only contain one entry for (int i = 0; i < ranges.size(); i++) { range = (SheetRangeImpl) ranges.get(i); // Get the cell in the top left Cell tl = range.getTopLeft(); Cell br = range.getBottomRight(); boolean found = false; for (int c = tl.getColumn(); c <= br.getColumn(); c++) { for (int r = tl.getRow(); r <= br.getRow(); r++) { Cell cell = sheet.getCell(c, r); if (cell.getType() != CellType.EMPTY) { if (!found) { found = true; } else { logger.warn("Range " + range + " contains more than one data cell. " + "Setting the other cells to blank."); Blank b = new Blank(c, r); sheet.addCell(b); } } } } } } catch (WriteException e) { // This should already have been checked - bomb out Assert.verify(false); } } void write(File outputFile) throws IOException { if (ranges.size() == 0) { return; } WorkbookSettings ws = ( (WritableSheetImpl) sheet).getWorkbookSettings(); if (!ws.getMergedCellCheckingDisabled()) { checkIntersections(); checkRanges(); } // If they will all fit into one record, then create a single // record, write them and get out if (ranges.size() < maxRangesPerSheet) { MergedCellsRecord mcr = new MergedCellsRecord(ranges); outputFile.write(mcr); return; } int numRecordsRequired = ranges.size() / maxRangesPerSheet + 1; int pos = 0; for (int i = 0 ; i < numRecordsRequired ; i++) { int numranges = Math.min(maxRangesPerSheet, ranges.size() - pos); ArrayList cells = new ArrayList(numranges); for (int j = 0 ; j < numranges ; j++) { cells.add(ranges.get(pos+j)); } MergedCellsRecord mcr = new MergedCellsRecord(cells); outputFile.write(mcr); pos += numranges; } } }