/******************************************************************************* * * Copyright 2010 Alexandru Craciun, and individual contributors as indicated * by the @authors tag. * * This 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. * * This software 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 software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. ******************************************************************************/ package org.netxilia.spi.impl.storage.db; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.lang.math.NumberUtils; import org.apache.log4j.Logger; import org.netxilia.api.display.Styles; import org.netxilia.api.exception.NotFoundException; import org.netxilia.api.exception.StorageException; import org.netxilia.api.model.RowData; import org.netxilia.api.model.RowData.Property; import org.netxilia.api.reference.Range; public abstract class AbstractRowsMapper extends AbstractMapper { private final static Logger log = Logger.getLogger(AbstractRowsMapper.class); abstract public List<DbRowStorageInfo> loadRowsStorageInfo(SheetDbSession data, DbSheetStorageInfo sheetStorage, Range range) throws NotFoundException; abstract public DbRowStorageInfo createStorageInfo(SheetDbSession data, DbSheetStorageInfo sheetStorage, int row) throws NotFoundException; abstract public DbRowStorageInfo insertStorageInfo(SheetDbSession data, DbSheetStorageInfo sheetStorage, int row) throws NotFoundException; abstract public void deleteStorageInfo(SheetDbSession data, DbSheetStorageInfo sheetStorage, int row) throws NotFoundException; abstract protected int getMaxRowId(SheetDbSession data, DbSheetStorageInfo sheetStorage); abstract public int getRowCount(SheetDbSession data, DbSheetStorageInfo sheetStorage); protected DbRowStorageInfo generateKey(SheetDbSession data, DbSheetStorageInfo sheetStorage, DbRowStorageInfo givenPrevRow, int row, int totalRows) throws StorageException, NotFoundException { DbRowStorageInfo prevRow = givenPrevRow; if (prevRow == null && row > 0) { prevRow = data.getStorageService().getRowStorage(data, row - 1); } DbRowStorageInfo nextRow = row < totalRows ? data.getStorageService().getRowStorage(data, row) : null; Integer maxRowId = null; if (givenPrevRow != null) { // this is part of a chained insert - the given previous row contains the last id maxRowId = givenPrevRow.getId() + 1; } else { maxRowId = data.getStorageService().getMaxRowId(data) + 1; } float order = 1; if (prevRow != null) { if (nextRow != null) { order = (prevRow.getOrderBy() + nextRow.getOrderBy()) / 2; } else { order = prevRow.getOrderBy() + 1; } } else { if (nextRow != null) { order = (nextRow.getOrderBy()) / 2; } else { order = 1; } } return new DbRowStorageInfo(maxRowId, order); } public List<RowData> loadRows(SheetDbSession data, DbSheetStorageInfo storageInfo, Range rowsRange) throws NotFoundException { // TODO - improve here List<DbRowStorageInfo> storages = data.getStorageService().loadRowsStorageInfo(data, rowsRange); // map rowId to index in the returned array Map<Integer, Integer> rowIndexes = new HashMap<Integer, Integer>(); for (int i = 0; i < storages.size(); ++i) { rowIndexes.put(storages.get(i).getId(), i + rowsRange.getMin()); } List<Integer> rowIds = null; if (!rowsRange.equals(Range.ALL) && storages.size() > 0) { // if only certain rows are needed, then build the list of desired rowids // otherwise the list stays null - load all the rows rowIds = new ArrayList<Integer>(storages.size()); for (int i = 0; i < storages.size(); ++i) { rowIds.add(storages.get(i).getId()); } } // load only the needed rows List<RowData> foundRows = getProperties(data.getWorkbookData(), storageInfo.getId(), new RowDataFromMap( rowIndexes), ROW_CATEGORY, rowIds, null); // add the found rows RowData[] rowsArray = new RowData[storages.size()]; for (RowData foundRow : foundRows) { if (foundRow != null) { rowsArray[foundRow.getIndex() - rowsRange.getMin()] = foundRow; } } // create the missing rows for (int i = 0; i < rowsArray.length; ++i) { if (rowsArray[i] == null) { rowsArray[i] = new RowData(i + rowsRange.getMin(), 0, null); } } // Collections.sort(rows, new BeanComparator(RowData.Property.index.name())); return Arrays.asList(rowsArray); } public void deleteAll(SheetDbSession data, DbSheetStorageInfo storageInfo) throws StorageException { deleteProperty(data.getWorkbookData(), storageInfo.getId(), ROW_CATEGORY, null, null); } public void saveRow(SheetDbSession data, DbSheetStorageInfo sheetStorage, RowData row, Collection<RowData.Property> properties) throws StorageException, NotFoundException { DbRowStorageInfo rowStorageInfo = data.getStorageService().getOrCreateRowStorage(data, row.getIndex()); // update only modifies separate properties super.setObject(data.getWorkbookData(), sheetStorage.getId(), ROW_CATEGORY, row, rowStorageInfo.getId(), toStringList(properties)); } public void insertRow(SheetDbSession data, DbSheetStorageInfo sheetStorageInfo, RowData row, Collection<Property> properties) throws NotFoundException { DbRowStorageInfo rowStorageInfo = data.getStorageService().insertRowStorage(data, row.getIndex()); // insert super.addObject(data.getWorkbookData(), sheetStorageInfo.getId(), ROW_CATEGORY, row, rowStorageInfo.getId(), toStringList(properties)); } public void deleteRow(SheetDbSession data, DbSheetStorageInfo sheetStorageInfo, int rowIndex) throws NotFoundException { DbRowStorageInfo rowStorageInfo = data.getStorageService().getRowStorage(data, rowIndex); if (rowStorageInfo == null) { throw new StorageException("The row storage is not yet set"); } data.getStorageService().deleteRowStorage(data, rowIndex); deleteProperty(data.getWorkbookData(), sheetStorageInfo.getId(), ROW_CATEGORY, rowStorageInfo.getId(), null); } /** Internal helper that is used to restore a complete sheet from DB */ private class RowDataFromMap implements IInstanceProviderFromMap<RowData> { private final Map<Integer, Integer> rowIndexes; public RowDataFromMap(Map<Integer, Integer> rowIndexes) { this.rowIndexes = rowIndexes; } @Override public RowData newInstance(String category, String objectId, Map<String, String> properties) { try { Integer rowId = Integer.valueOf(objectId); Integer rowIndex = rowIndexes.get(rowId); if (rowIndex == null) { throw new RuntimeException("Unknown row index for row id:" + rowId); } Styles styles = Styles.valueOf(properties.get(RowData.Property.styles.name())); int height = NumberUtils.toInt(properties.get(RowData.Property.height.name()), 0); return new RowData(rowIndex, height, styles); } catch (Exception ex) { log.error("Could not load row description from:" + properties + ":" + ex); return null; } } } }