/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
package org.openoffice.xmerge.converter.xml.sxc.minicalc;
import jmc.Workbook;
import jmc.Worksheet;
import jmc.CellAttributes;
import jmc.CellDescriptor;
import jmc.JMCconstants;
import jmc.JMCException;
import java.io.ByteArrayOutputStream;
import java.io.ByteArrayInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import org.openoffice.xmerge.ConvertData;
import org.openoffice.xmerge.converter.xml.OfficeConstants;
import org.openoffice.xmerge.converter.palm.PalmDB;
import org.openoffice.xmerge.converter.palm.Record;
import org.openoffice.xmerge.converter.palm.PalmDocument;
import org.openoffice.xmerge.util.Debug;
import org.openoffice.xmerge.converter.xml.sxc.SxcDocumentDeserializer;
import org.openoffice.xmerge.converter.xml.sxc.SpreadsheetDecoder;
import org.openoffice.xmerge.converter.xml.sxc.Format;
/**
* This class is used by {@link
* org.openoffice.xmerge.converter.xml.sxc.SxcDocumentDeserializerImpl}
* SxcDocumentDeserializerImpl} to decode the MiniCalc format.
*
*/
final class MinicalcDecoder extends SpreadsheetDecoder {
/** MiniCalc WorkBook to store sheets. */
private Workbook wb;
/** MiniCalc sheet - only one sheet can be open at a time. */
private Worksheet ws;
/** The current cell - only one cell can be active at a time. */
private CellDescriptor cell = null;
/** Format object describing the current cell. */
private Format fmt = null;
/** The password for the WorkBook. */
private String password = null;
/** The number of rows in the current WorkSheet. */
private int maxRows = 0;
/** The number of columns in the current WorkSheet. */
private int maxCols = 0;
/** The names of the worksheets. */
private String[] worksheetNames = null;
/**
* Constructor creates a MiniCalc WorkBook.
*
* @param name The name of the WorkBook.
* @param password The password for the workBook.
*
* @throws IOException If any I/O error occurs.
*/
MinicalcDecoder(String name, String[] worksheetNames, String password) throws IOException {
super(name, password);
fmt = new Format();
this.password = password;
this.worksheetNames = worksheetNames;
try {
wb = new Workbook(name, password);
}
catch (JMCException e) {
Debug.log(Debug.ERROR, "MinicalcDecoder.constructor:" + e.getMessage());
throw new IOException(e.getMessage());
}
}
/**
* This method takes a ConvertData
as input and
* converts it into a MiniCalc WorkSheet. The WorkSheet is then
* added to the WorkBook.
*
* @param InputStream An ConvertData
containing a
* MiniCalc WorkSheet.
*
* @throws IOException If any I/O error occurs.
*/
public void addDeviceContent(ConvertData cd) throws IOException {
try {
PalmDocument palmDoc;
int j = 0;
Enumeration e = cd.getDocumentEnumeration();
while(e.hasMoreElements()) {
palmDoc = (PalmDocument) e.nextElement();
// Convert PDB to WorkBook/WorkSheet format
PalmDB pdb = palmDoc.getPdb();
// This will be done at least once
String sheetName = worksheetNames[j];
// Get number of records in the pdb
int numRecords = pdb.getRecordCount();
// sheetName does not contain the WorkBook name, but we need the
// full name.
String fullSheetName = new String(wb.getWorkbookName() + "-" + sheetName);
// Create a new (empty) WorkSheet
ws = new Worksheet();
// Initialize the WorkSheet
ws.initWorksheet(fullSheetName, numRecords);
// Loop over the number of records in the PDB
for (int i = 0; i < numRecords; i++) {
// Read record i from the PDB
Record rec = pdb.getRecord(i);
byte cBytes[] = rec.getBytes();
// Create an InputStream
ByteArrayInputStream bis = new ByteArrayInputStream(cBytes);
// Get the size of the stream
int bisSize = cBytes.length;
// Add each record to the WorkSheet
ws.readNextRecord(bis, bisSize);
}
// Add the WorkSheet to the WorkBook
wb.addWorksheet(ws);
j++;
}
}
catch (JMCException e) {
Debug.log(Debug.ERROR, "MinicalcDecoder.addPDB:" + e.getMessage());
throw new IOException(e.getMessage());
}
}
/**
* This method returns the number of spreadsheets
* stored in the WorkBook.
*
* @return The number of sheets in the WorkBook.
*/
public int getNumberOfSheets() {
return wb.getNumberOfSheets();
}
/**
* This method gets the requested WorkSheet from the
* WorkBook and sets it as the selected WorkSheet. All
* other "get" methods will now get data from this WorkSheet.
*
* @param sheetIndex The index number of the sheet to open.
*
* @throws IOException If any I/O error occurs.
*/
public void setWorksheet(int sheetIndex) throws IOException {
try {
ws = wb.getWorksheet(sheetIndex);
// Initialize access to the WorkSheet so that we can calculate
// the number of rows and columns
ws.initAccess(password);
maxRows = 0;
maxCols = 0;
while (goToNextCell()) {
maxRows = Math.max(maxRows, cell.getRowNumber());
maxCols = Math.max(maxCols, cell.getColNumber());
}
// Re-initialize access to the WorkSheet
ws.initAccess(password);
}
catch (JMCException e) {
Debug.log(Debug.ERROR, "MinicalcDecoder.setWorksheet:" + e.getMessage());
throw new IOException(e.getMessage());
}
}
/**
* This method returns the name of the current spreadsheet.
*
* @return The name of the current WorkSheet.
*/
public String getSheetName() {
String sheetName = ws.getName();
return sheetName;
}
/**
* This method gets the next cell from the WorkSheet
* and sets it as the selected cell. All other "get"
* methods will now get data from this cell.
*
* @return True if we were able to go to another cell
* in the sheet, false if there were no cells
* left.
*
* @throws IOException If any I/O error occurs.
*/
public boolean goToNextCell() throws IOException {
boolean gotCell = false;
try {
cell = ws.getNextCell();
if (cell != null) {
gotCell = true;
}
// As we read each cell, get its formatting info
readCellFormat();
}
catch (JMCException e) {
Debug.log(Debug.ERROR, "MinicalcDecoder.goToNextCell:" + e.getMessage());
throw new IOException(e.getMessage());
}
return gotCell;
}
/**
* This method returns the row number of the current cell.
*
* @return The row number of the current cell. Returns
* -1 if no cell is currently selected.
*/
public int getRowNumber() {
int row = -1;
if (cell != null) {
row = cell.getRowNumber();
}
return row;
}
/**
* This method returns the number of rows in the current sheet.
*
* @return The number of rows in the current sheet.
*/
public int getNumberOfRows() {
return maxRows;
}
/**
* This method returns the number of columns in the current sheet.
*
* @return The number of columns in the current sheet.
*/
public int getNumberOfColumns() {
return maxCols;
}
/**
* This method returns the col number of the current cell.
*
* @return The col number of the current cell. Returns
* -1 if no cell is currently selected.
*/
public int getColNumber() {
int col = -1;
if (cell != null) {
col = cell.getColNumber();
}
return col;
}
/**
* This method returns the contents of the current cell.
*
* @return The contents of the current cell. Returns
* null if no cell is currently selected.
*/
public String getCellContents() {
String contents = null;
if (cell != null) {
contents = cell.getCellContents();
// Active cell, but no content
if (contents == null)
return new String("");
// Does the cell contain a formula?
if (contents.startsWith("=")) {
contents = parseFormula(contents);
}
// Make sure that any MiniCalc peculiarities are stripped off
if (fmt.getCategory().equalsIgnoreCase(OfficeConstants.CELLTYPE_CURRENCY)) {
contents = currencyRemoveSign(contents);
}
else if (fmt.getCategory().equalsIgnoreCase(OfficeConstants.CELLTYPE_PERCENT)) {
contents = percentRemoveSign(contents);
}
else if (fmt.getCategory().equalsIgnoreCase(OfficeConstants.CELLTYPE_DATE)) {
contents = convertToStarDate(contents);
}
else if (fmt.getCategory().equalsIgnoreCase(OfficeConstants.CELLTYPE_TIME)) {
contents = convertToStarTime(contents);
}
}
return contents;
}
/**
* This method is meant to return the value of the formula cell. However
* in minicalc this value is not used so hence the stubbed function
*
* @return the value fo the formula cell
*/
public String getCellValue() {
return null;
}
/**
*
This method takes a formula and parses it into * StarOffice XML formula format.
* *Many spreadsheets use ',' as a separator. * StarOffice XML format uses ';' as a separator instead.
* *Many spreadsheets use '!' as a separator when refencing * a cell in a different sheet.
* ** Example: =sheet1!A1 ** *
StarOffice XML format uses '.' as a separator instead.
* ** Example: =sheet1.A1 ** * @param formula A formula string. * * @return A StarOffice XML format formula string. */ protected String parseFormula(String formula) { formula = formula.replace(',', ';'); formula = formula.replace('!', '.'); return formula; } /** *
This method returns the type of the data in the current cell.
* *Possible Data Types:
* *String
that contains a
* currency value and removes the $ from the String
.
* If the dollar sign is not the first or last character of the
* input String
, the input String
is
* simply returned.
*
* @param contents The input String
from which to
* remove the dollar sign.
*
* @return The input String
minus the dollar sign.
* If the input String
did not begin or end
* with a dollar sign, contents is returned.
*/
private String currencyRemoveSign(String contents) {
MinicalcDataString mcString = new MinicalcDataString(contents);
String currencyString = mcString.currencyRemoveSign();
return currencyString;
}
/**
* This method takes a String
that contains a percent
* value and removes the % from the String
. If the
* percent sign is not the last character of the input
* String
, the input String
is simply
* returned.
*
* @param contents The input String from which to remove the
* percent sign.
*
* @return The input String
minus the percent sign.
* If the input String
did not begin with
* a percent sign, contents is returned.
*/
private String percentRemoveSign(String contents) {
MinicalcDataString mcString = new MinicalcDataString(contents);
String percentString = mcString.percentRemoveSign();
return percentString;
}
/**
* This method returns takes a String
that contains
* a time value and converts it from MiniCalc format to StarOffice
* XML time format.
*
* @param contents The input String
containing a
* MiniCalc time.
*
* @return The input String
converted to StarOffice
* XML time format.
*/
private String convertToStarTime(String contents) {
MinicalcDataString mcString = new MinicalcDataString(contents);
String timeString = mcString.convertToStarTime();
return timeString;
}
/**
* This method returns takes a String
that contains
* a date value and converts it from MiniCalc format to StarOffice
* XML date format.
*
* @param contents The input String
containing a
* MiniCalc date.
*
* @return The input String
converted to StarOffice
* XML date format.
*/
private String convertToStarDate(String contents) {
MinicalcDataString mcString = new MinicalcDataString(contents);
String dateString = mcString.convertToStarDate();
return dateString;
}
/**
* Return the Format object describing the active cell formatting.
*
* @return The Format object describing the active cell formatting.
*/
public Format getCellFormat() {
return new Format(fmt);
}
/**
* Create the format data for the new cell.
*/
private void readCellFormat() {
// Make sure there are no remnants from the last time
fmt.clearFormatting();
fmt.setCategory(getCellFormatType());
// TODO - Get any more formatting data here
}
}