org.netxilia.server.rest.html.sheet.impl.SheetModelServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.netxilia.server.rest.html.sheet.impl.SheetModelServiceImpl.java

Source

/*******************************************************************************
 * 
 * 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.server.rest.html.sheet.impl;

import java.security.AccessControlException;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.netxilia.api.INetxiliaSystem;
import org.netxilia.api.display.IStyleService;
import org.netxilia.api.exception.AlreadyExistsException;
import org.netxilia.api.exception.NetxiliaBusinessException;
import org.netxilia.api.exception.NotFoundException;
import org.netxilia.api.exception.StorageException;
import org.netxilia.api.formula.Formula;
import org.netxilia.api.model.Alias;
import org.netxilia.api.model.CellData;
import org.netxilia.api.model.ColumnData;
import org.netxilia.api.model.ISheet;
import org.netxilia.api.model.RowData;
import org.netxilia.api.model.SheetData;
import org.netxilia.api.model.SheetDimensions;
import org.netxilia.api.model.SheetFullName;
import org.netxilia.api.model.SheetType;
import org.netxilia.api.model.SpanTable;
import org.netxilia.api.operation.ISheetOperations;
import org.netxilia.api.reference.AreaReference;
import org.netxilia.api.reference.CellReference;
import org.netxilia.api.reference.Range;
import org.netxilia.api.storage.IJsonSerializer;
import org.netxilia.api.user.IAclService;
import org.netxilia.api.user.Permission;
import org.netxilia.api.utils.CollectionUtils;
import org.netxilia.api.utils.Matrix;
import org.netxilia.api.value.GenericValueType;
import org.netxilia.api.value.RichValue;
import org.netxilia.server.rest.html.sheet.CellModel;
import org.netxilia.server.rest.html.sheet.ColumnModel;
import org.netxilia.server.rest.html.sheet.ISheetModelService;
import org.netxilia.server.rest.html.sheet.RowModel;
import org.netxilia.server.rest.html.sheet.SheetModel;
import org.netxilia.server.util.ILazyElementResolver;
import org.netxilia.server.util.LazyArrayList;
import org.springframework.beans.factory.annotation.Autowired;

public class SheetModelServiceImpl implements ISheetModelService {
    // private static org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(SheetModelServiceImpl.class);

    @Autowired
    private INetxiliaSystem workbookProcessor;

    @Autowired
    private IStyleService styleService;

    @Autowired
    private IJsonSerializer jsonSerializer;

    @Autowired
    private ISheetOperations sheetOperations;

    @Autowired
    private IAclService aclService;
    // @Autowired
    // private IStyleService styleService;

    private int mainExtraRows = 50;
    private int mainExtraCols = 20;

    private int privateExtraRows = 15;
    private int privateExtraCols = 6;

    private int summaryExtraRows = 15;
    private int summaryExtraCols = 20;

    private int defaultColumnWidth = 80;

    private int defaultRowHeight = 20;

    private int pageSize = 120;

    private String emptyCellContent = "<td></td>";
    private String skipCellContent = "";

    private int getExtraRows(SheetType type) {
        switch (type) {
        case normal:
            return mainExtraRows;
        case summary:
            return summaryExtraRows;
        case user:
            return privateExtraRows;
        }
        return 0;
    }

    private int getExtraCols(SheetType type) {
        switch (type) {
        case normal:
            return mainExtraCols;
        case summary:
            return summaryExtraCols;
        case user:
            return privateExtraCols;
        }
        return 0;
    }

    public int getPageSize() {
        return pageSize;
    }

    public void setPageSize(int pageSize) {
        this.pageSize = pageSize;
    }

    public int getMainExtraRows() {
        return mainExtraRows;
    }

    public void setMainExtraRows(int mainExtraRows) {
        this.mainExtraRows = mainExtraRows;
    }

    public int getMainExtraCols() {
        return mainExtraCols;
    }

    public void setMainExtraCols(int mainExtraCols) {
        this.mainExtraCols = mainExtraCols;
    }

    public int getPrivateExtraRows() {
        return privateExtraRows;
    }

    public void setPrivateExtraRows(int privateExtraRows) {
        this.privateExtraRows = privateExtraRows;
    }

    public int getPrivateExtraCols() {
        return privateExtraCols;
    }

    public void setPrivateExtraCols(int privateExtraCols) {
        this.privateExtraCols = privateExtraCols;
    }

    public int getSummaryExtraRows() {
        return summaryExtraRows;
    }

    public void setSummaryExtraRows(int summaryExtraRows) {
        this.summaryExtraRows = summaryExtraRows;
    }

    public int getSummaryExtraCols() {
        return summaryExtraCols;
    }

    public void setSummaryExtraCols(int summaryExtraCols) {
        this.summaryExtraCols = summaryExtraCols;
    }

    public int getDefaultColumnWidth() {
        return defaultColumnWidth;
    }

    public void setDefaultColumnWidth(int defaultColumnWidth) {
        this.defaultColumnWidth = defaultColumnWidth;
    }

    public int getDefaultRowHeight() {
        return defaultRowHeight;
    }

    public void setDefaultRowHeight(int defaultRowHeight) {
        this.defaultRowHeight = defaultRowHeight;
    }

    public IJsonSerializer getJsonSerializer() {
        return jsonSerializer;
    }

    public void setJsonSerializer(IJsonSerializer jsonSerializer) {
        this.jsonSerializer = jsonSerializer;
    }

    private Alias findAliasByReference(SheetData sheet, AreaReference ref) {
        for (Map.Entry<Alias, AreaReference> entry : sheet.getAliases().entrySet()) {
            if (ref.equals(entry.getValue())) {
                return entry.getKey();
            }
        }
        return null;
    }

    @Override
    public SheetModel buildModel(SheetFullName sheet, int start, Formula filter, boolean createSheetIfMissing)
            throws StorageException, NetxiliaBusinessException {
        return buildModel(sheet, false, 0, start, filter, createSheetIfMissing);
    }

    @Override
    public SheetModel buildOverviewModel(SheetFullName sheet) throws StorageException, NetxiliaBusinessException {
        return buildModel(sheet, true, 0, 0, null, false);
    }

    @Override
    public SheetModel buildSummaryModel(SheetFullName summarySheetName, int mainSheetColumnCount,
            boolean createSheetIfMissing) throws StorageException, NetxiliaBusinessException {
        return buildModel(summarySheetName, true, mainSheetColumnCount, 0, null, createSheetIfMissing);
    }

    private SheetModel buildModel(SheetFullName sheetName, boolean overviewMode, int mainSheetColumnCount,
            int startRow, Formula filter, boolean createSheetIfMissing)
            throws StorageException, NetxiliaBusinessException {
        ISheet sheet = null;

        try {
            SheetType type = sheetName.getType();

            try {
                sheet = workbookProcessor.getWorkbook(sheetName.getWorkbookId()).getSheet(sheetName.getSheetName());
            } catch (NotFoundException e) {
                if (createSheetIfMissing) {
                    try {
                        sheet = workbookProcessor.getWorkbook(sheetName.getWorkbookId())
                                .addNewSheet(sheetName.getSheetName(), type);
                    } catch (AlreadyExistsException e1) {
                        // if the sheet was created in the mean time - nothing to do
                    }
                } else {
                    throw new NotFoundException("Cannot find sheet:" + sheetName);
                }
            }

            boolean readOnly = true;
            try {
                aclService.checkPermission(sheet.getFullName(), Permission.write);
                readOnly = false;
            } catch (AccessControlException e) {
                // readonly
            }

            SheetDimensions dim = sheet.getDimensions().getNonBlocking();
            int totalColumns = dim.getColumnCount() + getExtraCols(type);

            // synchronize the number of columns from summary and main sheet
            if (type == SheetType.summary) {
                totalColumns = mainSheetColumnCount;
            }
            SheetData sheetData = sheet.receiveSheet().getNonBlocking();
            List<ColumnModel> columns = new LazyArrayList<ColumnModel>(totalColumns, new LazyColumnResolver());
            List<ColumnData> columnData = sheet.receiveColumns(Range.ALL).getNonBlocking();

            SpanTable spans = new SpanTable(sheetData.getSpans());

            for (int c = 0; c < columnData.size(); ++c) {
                AreaReference fullColumnRef = new AreaReference(
                        new CellReference(null, CellReference.MAX_ROW_INDEX, c));

                Alias alias = findAliasByReference(sheetData, fullColumnRef);
                columns.add(new ColumnModel(columnData.get(c), alias, defaultColumnWidth));
            }

            List<RowModel> rows = Collections.emptyList();
            // int totalRows = 0;

            int firstRow = startRow;
            int lastRow = Math.min(startRow + dim.getRowCount() + getExtraRows(type), pageSize + startRow);
            int rowCount = lastRow - firstRow;

            List<Integer> rowIds = null;
            if (filter != null) {
                rowIds = sheetOperations.filter(sheet, filter).getNonBlocking();
                if (rowIds.size() > 0) {
                    firstRow = rowIds.get(0);
                    lastRow = rowIds.get(rowIds.size() - 1) + 1;
                } else {
                    lastRow = firstRow;
                }
                rowCount = rowIds.size();
            }
            rows = new LazyArrayList<RowModel>(rowCount, new LazyRowResolver(firstRow, totalColumns));
            List<RowData> rowData = sheet.receiveRows(Range.range(firstRow, lastRow)).getNonBlocking();

            Matrix<CellData> cellData = sheet
                    .receiveCells(new AreaReference(new CellReference(sheetName.getSheetName(), firstRow, 0),
                            new CellReference(sheetName.getSheetName(), lastRow, totalColumns)))
                    .getNonBlocking();

            for (Integer rowIndex : rowIds != null ? getRowIdIterable(rowIds)
                    : getRowIdIterable(firstRow, lastRow)) {
                int relativeRowIndex = rowIndex - firstRow;
                if (relativeRowIndex >= rowData.size()) {
                    continue;
                }
                RowData row = rowData.get(relativeRowIndex);
                if (row == null) {
                    continue;
                }

                List<CellModel> cells = new LazyArrayList<CellModel>(totalColumns, new LazyCellResolver());

                for (CellData cell : cellData.getRow(relativeRowIndex)) {
                    CellModel cellModel;

                    int colSpan = cell != null ? spans.getColSpan(cell.getReference()) : 1;
                    int rowSpan = cell != null ? spans.getRowSpan(cell.getReference()) : 1;

                    if (cell == null) {
                        cellModel = new CellModel(emptyCellContent);
                    } else if (colSpan < 0 || rowSpan < 0) {
                        // the cell is part of a merged area
                        cellModel = new CellModel(skipCellContent);
                    } else {
                        ColumnData columnDataForCell = cell.getReference().getColumnIndex() < columnData.size()
                                ? columnData.get(cell.getReference().getColumnIndex())
                                : null;
                        RichValue formattedValue = styleService.formatCell(sheetName.getWorkbookId(), cell, row,
                                columnDataForCell);

                        StringBuilder cellContent = new StringBuilder();
                        cellContent.append("<td");
                        if (cell.getFormula() != null) {
                            cellContent.append(" title='").append(cell.getFormula()).append("'");
                        } else if (cell.getValue() != null) {
                            // XXX assumes Strings are not formatted which may be wrong. other types may also be
                            // skipped (i.e. BINARY)
                            if (cell.getValue().getValueType() != GenericValueType.STRING) {
                                cellContent.append(" title='").append(cell.getValue().getStringValue()).append("'");
                            }
                        }

                        if (formattedValue.getStyles() != null
                                && formattedValue.getStyles().toString().length() > 0) {
                            cellContent.append(" class='").append(formattedValue.getStyles()).append("'");
                        }

                        if (colSpan > 1) {
                            cellContent.append(" colspan='").append(colSpan).append("'");
                        }
                        if (rowSpan > 1) {
                            cellContent.append(" rowspan='").append(rowSpan).append("'");
                        }

                        cellContent.append(">");
                        if (colSpan > 1) {
                            cellContent.append("<div class='merge'>");
                            cellContent.append(formattedValue.getDisplay());
                            cellContent.append("</div>");
                        } else {
                            cellContent.append(formattedValue.getDisplay());
                        }
                        cellContent.append("</td>");
                        cellModel = new CellModel(cellContent.toString());
                    }
                    cells.add(cellModel);
                }
                rows.add(new RowModel(row, defaultRowHeight, cells));
            }

            SheetModel model = new SheetModel(jsonSerializer, overviewMode, sheetName, rows, columns,
                    sheetData.getAliases(), sheetData.getCharts(), sheetData.getSpans(), rowCount, pageSize,
                    readOnly);
            return model;
        } catch (RuntimeException ex) {
            ex.printStackTrace();
            throw ex;
        }
    }

    /**
     * Columns resolvers
     * 
     * @author <a href='mailto:ax.craciun@gmail.com'>Alexandru Craciun</a>
     * 
     */
    private class LazyColumnResolver implements ILazyElementResolver<ColumnModel> {
        private LazyColumnModel columnModel = new LazyColumnModel();

        public ColumnModel get(int index) {
            columnModel.setIndex(index);
            return columnModel;
        }
    }

    private class LazyColumnModel extends ColumnModel {
        private int index;

        public LazyColumnModel() {
            super(null, null, defaultColumnWidth);
        }

        @Override
        public int getWidth() {
            return defaultColumnWidth;
        }

        @Override
        public String getLabel() {
            return CellReference.columnLabel(index);
        }

        public void setIndex(int index) {
            this.index = index;
        }
    }

    /**
     * Row resolver. For inexistent rows will return its index shifted with the start row and empty cells
     * 
     * @author <a href='mailto:ax.craciun@gmail.com'>Alexandru Craciun</a>
     * 
     */
    private class LazyRowResolver implements ILazyElementResolver<RowModel> {
        private LazyRowModel rowModel;
        private int startRow;

        public LazyRowResolver(int startRow, int columnCount) {
            rowModel = new LazyRowModel(columnCount);
            this.startRow = startRow;
        }

        public RowModel get(int index) {
            rowModel.setIndex(startRow + index);
            return rowModel;
        }
    }

    private class LazyRowModel extends RowModel {
        private int index;

        public LazyRowModel(int columnCount) {
            super(null, defaultRowHeight, new LazyArrayList<CellModel>(columnCount, new LazyCellResolver()));
        }

        @Override
        public int getHeight() {
            return defaultRowHeight;
        }

        @Override
        public int getIndex() {

            return index;
        }

        public void setIndex(int index) {
            this.index = index;
        }
    }

    /**
     * CellResolver
     * 
     * @author <a href='mailto:ax.craciun@gmail.com'>Alexandru Craciun</a>
     * 
     */
    private class LazyCellResolver implements ILazyElementResolver<CellModel> {
        private CellModel cellModel = new CellModel(emptyCellContent);

        public CellModel get(int index) {
            return cellModel;
        }
    }

    private Iterable<Integer> getRowIdIterable(List<Integer> rowIds) {
        return rowIds;
    }

    private Iterable<Integer> getRowIdIterable(final int firstRow, final int lastRow) {

        return CollectionUtils.iterable(new Iterator<Integer>() {
            private int i = firstRow;

            @Override
            public boolean hasNext() {
                return i < lastRow;
            }

            @Override
            public Integer next() {
                if (i >= lastRow) {
                    throw new IllegalStateException();
                }
                return i++;
            }

            @Override
            public void remove() {
                throw new UnsupportedOperationException();
            }
        });
    }

}