diff options
Diffstat (limited to 'xmerge/java/org/openoffice/xmerge/converter/xml/sxc')
90 files changed, 18033 insertions, 0 deletions
diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/BookSettings.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/BookSettings.java new file mode 100644 index 000000000000..3643654ca906 --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/BookSettings.java @@ -0,0 +1,232 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: BookSettings.java,v $ + * $Revision: 1.3 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +package org.openoffice.xmerge.converter.xml.sxc; + +import java.util.Vector; +import java.util.Enumeration; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.NodeList; +import org.w3c.dom.Node; +import org.w3c.dom.Element; +import java.awt.Point; + +import org.openoffice.xmerge.converter.xml.OfficeConstants; +import org.openoffice.xmerge.util.Debug; +import org.openoffice.xmerge.util.XmlUtil; + +/** + * This is a class representing the different attributes for a worksheet + * contained in settings.xml. + * + * @author Martin Maher + */ +public class BookSettings implements OfficeConstants { + + /** A w3c <code>Document</code>. */ + private org.w3c.dom.Document settings = null; + + private boolean hasColumnRowHeaders = true; + private String activeSheet = new String(); + private Vector worksheetSettings = new Vector(); + + /** + * Default Constructor for a <code>BookSettings</code> + * + * @param dimension if it's a row the height, a column the width + * @param repeated + */ + public BookSettings(Node root) { + readNode(root); + } + + /** + * Default Constructor for a <code>BookSettings</code> + * + * @param worksheetSettings if it's a row the height, a column the width + */ + public BookSettings(Vector worksheetSettings) { + this.worksheetSettings = worksheetSettings; + } + + /** + * + */ + public void setColumnRowHeaders(boolean hasColumnRowHeaders) { + this.hasColumnRowHeaders = hasColumnRowHeaders; + } + + /** + * + */ + public boolean hasColumnRowHeaders() { + return hasColumnRowHeaders; + } + + /** + * Gets the <code>Vector</code> of <code>SheetSettings</code> + * + * @return <code>Vector</code> of <code>SheetSettings</code> + */ + public Vector getSheetSettings() { + return worksheetSettings; + } + + /** + * Gets the active sheet name + * + * @return the active sheet name + */ + public String getActiveSheet() { + + return activeSheet; + } + + /** + * Sets the active sheet name + * + * @param activeSheet the active sheet name + */ + public void setActiveSheet(String activeSheet) { + + this.activeSheet = activeSheet; + } + + + /** + * Adds an XML entry for a particular setting + * + * @param root the root node at which to add the xml entry + * @param attriute the name of the attribute to add + * @param type the attribute type (int, short etc) + * @param value the value of the attribute + */ + private void addConfigItem(Node root, String attribute, String type, String value) { + + Element configItem = settings.createElement(TAG_CONFIG_ITEM); + configItem.setAttribute(ATTRIBUTE_CONFIG_NAME, attribute); + configItem.setAttribute(ATTRIBUTE_CONFIG_TYPE, type); + + configItem.appendChild(settings.createTextNode(value)); + + root.appendChild(configItem); + } + + /** + * Writes out a settings.xml entry for this BookSettings object + * + * @param settings a <code>Document</code> object representing the settings.xml + * @param root the root xml node to add to + */ + public void writeNode(org.w3c.dom.Document settings, Node root) { + + this.settings = settings; + Element configItemMapNamed = (Element) settings.createElement(TAG_CONFIG_ITEM_MAP_NAMED); + configItemMapNamed.setAttribute(ATTRIBUTE_CONFIG_NAME, "Tables"); + for(Enumeration e = worksheetSettings.elements();e.hasMoreElements();) { + SheetSettings s = (SheetSettings) e.nextElement(); + s.writeNode(settings, configItemMapNamed); + } + addConfigItem(root, "ActiveTable", "string", activeSheet); + String booleanValue = Boolean.toString(hasColumnRowHeaders); + addConfigItem(root, "HasColumnRowHeaders", "boolean", booleanValue); + root.appendChild(configItemMapNamed); + } + + /** + * Sets a variable based on a String value read from XML + * + * @param name xml name of the attribute to set + * @param value String value fo the attribute + */ + public void addAttribute(String name, String value) { + + if(name.equals("ActiveTable")) { + activeSheet = value; + } else if(name.equals("HasColumnRowHeaders")) { + Boolean b = Boolean.valueOf(value); + hasColumnRowHeaders = b.booleanValue(); + } + } + + /** + * Reads document settings from xml and inits SheetSettings variables + * + * @param root XML Node to read from + */ + public void readNode(Node root) { + + if (root.hasChildNodes()) { + + NodeList nodeList = root.getChildNodes(); + int len = nodeList.getLength(); + for (int i = 0; i < len; i++) { + Node child = nodeList.item(i); + + if (child.getNodeType() == Node.ELEMENT_NODE) { + String nodeName = child.getNodeName(); + + if (nodeName.equals(TAG_CONFIG_ITEM)) { + + NamedNodeMap cellAtt = child.getAttributes(); + + Node configNameNode = + cellAtt.getNamedItem(ATTRIBUTE_CONFIG_NAME); + + String name = configNameNode.getNodeValue(); + NodeList nodeList2 = child.getChildNodes(); + int len2 = nodeList2.getLength(); + String s = ""; + for (int j = 0; j < len2; j++) { + Node child2 = nodeList2.item(j); + if (child2.getNodeType() == Node.TEXT_NODE) { + s = child2.getNodeValue(); + } + } + addAttribute(name, s); + + } else if (nodeName.equals(TAG_CONFIG_ITEM_MAP_NAMED)) { + + readNode(child); + + } else if (nodeName.equals(TAG_CONFIG_ITEM_MAP_ENTRY)) { + + SheetSettings s = new SheetSettings(child); + worksheetSettings.add(s); + + } else { + + Debug.log(Debug.TRACE, "<OTHERS " + XmlUtil.getNodeInfo(child) + " />"); + } + } + } + } + } +} diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/CellStyle.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/CellStyle.java new file mode 100644 index 000000000000..5d4eae4da4b4 --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/CellStyle.java @@ -0,0 +1,518 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: CellStyle.java,v $ + * $Revision: 1.8 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +package org.openoffice.xmerge.converter.xml.sxc; + +import java.awt.Color; +import java.io.IOException; + +import org.w3c.dom.NodeList; +import org.w3c.dom.Node; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Element; + +import org.openoffice.xmerge.Document; +import org.openoffice.xmerge.ConverterCapabilities; +import org.openoffice.xmerge.converter.xml.OfficeDocument; +import org.openoffice.xmerge.converter.xml.Style; +import org.openoffice.xmerge.converter.xml.StyleCatalog; +import org.openoffice.xmerge.converter.xml.sxw.SxwDocument; +import org.openoffice.xmerge.util.Debug; + +/** + * Represents a text <code>Style</code> in an OpenOffice document. + * + * @author Martin Maher + */ +public class CellStyle extends Style implements Cloneable { + + private Format fmt = new Format(); + + /** + * Constructor for use when going from DOM to client device format. + * + * @param Node The <i>style:style</i> <code>Node</code> containing + * the <code>Style</code>. (This <code>Node</code> is + * assumed have a <i>family</i> attribute of <i>text</i>). + * @param sc The <code>StyleCatalog</code>, which is used for + * looking up ancestor <code>Style</code> objects. + */ + public CellStyle(Node node, StyleCatalog sc) { + super(node, sc); + + // Run through the attributes of this node, saving + // the ones we're interested in. + NamedNodeMap attrNodes = node.getAttributes(); + if (attrNodes != null) { + int len = attrNodes.getLength(); + for (int i = 0; i < len; i++) { + Node attr = attrNodes.item(i); + handleAttribute(attr.getNodeName(), attr.getNodeValue()); + } + } + + // Look for children. Only ones we care about are "style:properties" + // nodes. If any are found, recursively traverse them, passing + // along the style element to add properties to. + if (node.hasChildNodes()) { + NodeList children = node.getChildNodes(); + int len = children.getLength(); + for (int i = 0; i < len; i++) { + Node child = children.item(i); + String name = child.getNodeName(); + if (name.equals("style:properties")) { + NamedNodeMap childAttrNodes = child.getAttributes(); + if (childAttrNodes != null) { + int nChildAttrNodes = childAttrNodes.getLength(); + for (int j = 0; j < nChildAttrNodes; j++) { + Node attr = childAttrNodes.item(j); + handleAttribute(attr.getNodeName(), + attr.getNodeValue()); + } + } + } + } + } + } + + + /** + * Constructor for use when going from client device format to DOM + * + * @param name Name of cell <code>Style</code>. Can be null. + * @param family Family of text <code>Style</code> (usually + * <i>text</i>). Can be null. + * @param parent Name of parent text <code>Style</code>, or null + * for none. + * @param fmt size in points. + * @param sc The <code>StyleCatalog</code>, which is used for + * looking up ancestor <code>Style</code> objects. + */ + public CellStyle(String name, String family, String parent,Format fmt, StyleCatalog sc) { + super(name, family, parent, sc); + this.fmt = fmt; + } + + /** + * Returns the <code>Format</code> object for this particular style + * + * @return the <code>Format</code> object + */ + public Format getFormat() { + return fmt; + } + + /** + * Parse a color specification of the form <i>#rrggbb</i> + * + * @param value <code>Color</code> specification to parse. + * + * @return The <code>Color</code> associated the value. + */ + private Color parseColorString(String value) { + // Assume color value is of form #rrggbb + String r = value.substring(1, 3); + String g = value.substring(3, 5); + String b = value.substring(5, 7); + int red = 0; + int green = 0; + int blue = 0; + try { + red = Integer.parseInt(r, 16); + green = Integer.parseInt(g, 16); + blue = Integer.parseInt(b, 16); + } catch (NumberFormatException e) { + Debug.log(Debug.ERROR, "Problem parsing a color string", e); + } + return new Color(red, green, blue, 0); + } + + + /** + * Set an attribute. + * + * @param attr The attribute to set. + * @param value The attribute value to set. + */ + private void handleAttribute(String attr, String value) { + + if (attr.equals("fo:font-weight")) { + fmt.setAttribute(Format.BOLD, value.equals("bold")); + } + + else if (attr.equals("fo:font-style")) { + if (value.equals("italic") || value.equals("oblique")) + fmt.setAttribute(Format.ITALIC, true); + else if (value.equals("normal")) + fmt.setAttribute(Format.ITALIC, false); + } + + else if (attr.equals("style:text-underline")) { + fmt.setAttribute(Format.UNDERLINE, !value.equals("none")); + } + + else if (attr.equals("style:text-crossing-out")) { + fmt.setAttribute(Format.STRIKETHRU, !value.equals("none")); + } + + else if (attr.equals("style:text-position")) { + if (value.startsWith("super ")) + fmt.setAttribute(Format.SUPERSCRIPT, true); + else if (value.startsWith("sub ")) + fmt.setAttribute(Format.SUBSCRIPT, true); + else if (value.startsWith("0% ")) + fmt.setAttribute(Format.SUPERSCRIPT | Format.SUBSCRIPT, false); + else { + String firstPart = value.substring(0, value.indexOf(" ")); + if (firstPart.endsWith("%")) { + firstPart = firstPart.substring(0, value.indexOf("%")); + int amount; + try { + amount = Integer.parseInt(firstPart); + } catch (NumberFormatException e) { + amount = 0; + Debug.log(Debug.ERROR, "Problem with style:text-position tag", e); + } + if (amount < 0) fmt.setAttribute(Format.SUBSCRIPT, true); + else if (amount > 0) fmt.setAttribute(Format.SUPERSCRIPT, false); + } + } + } + + else if (attr.equals("fo:font-size")) { + if (value.endsWith("pt")) { + String num = value.substring(0, value.length() - 2); + fmt.setFontSize(Integer.parseInt(num)); + } + } + + else if (attr.equals("style:font-name")) + fmt.setFontName(value); + + else if (attr.equals("fo:color")) + fmt.setForeground(parseColorString(value)); + + else if (attr.equals("fo:background-color")) + fmt.setBackground(parseColorString(value)); + + else if (attr.equals("fo:text-align")) { + if(value.equals("center")) { + fmt.setAlign(Format.CENTER_ALIGN); + } else if(value.equals("end")) { + fmt.setAlign(Format.RIGHT_ALIGN); + } else if(value.equals("start")) { + fmt.setAlign(Format.LEFT_ALIGN); + } + } + + else if (attr.equals("fo:vertical-align")) { + if(value.equals("top")) { + fmt.setVertAlign(Format.TOP_ALIGN); + } else if(value.equals("middle")) { + fmt.setVertAlign(Format.MIDDLE_ALIGN); + } else if(value.equals("bottom")) { + fmt.setVertAlign(Format.BOTTOM_ALIGN); + } + } + + else if (attr.equals("fo:border")) { + fmt.setAttribute(Format.TOP_BORDER, !value.equals("none")); + fmt.setAttribute(Format.BOTTOM_BORDER, !value.equals("none")); + fmt.setAttribute(Format.LEFT_BORDER, !value.equals("none")); + fmt.setAttribute(Format.RIGHT_BORDER, !value.equals("none")); + } + else if (attr.equals("fo:border-top")) { + fmt.setAttribute(Format.TOP_BORDER, !value.equals("none")); + } + else if (attr.equals("fo:border-bottom")) { + fmt.setAttribute(Format.BOTTOM_BORDER, !value.equals("none")); + } + else if (attr.equals("fo:border-left")) { + fmt.setAttribute(Format.LEFT_BORDER, !value.equals("none")); + } + else if (attr.equals("fo:border-right")) { + fmt.setAttribute(Format.RIGHT_BORDER, !value.equals("none")); + } + else if (attr.equals("fo:wrap-option")) { + fmt.setAttribute(Format.WORD_WRAP, value.equals("wrap")); + } + + else if (isIgnored(attr)) {} + + else { + Debug.log(Debug.INFO, "CellStyle Unhandled: " + attr + "=" + value); + } + } + + + /** + * Return a <code>Style</code> object corresponding to this one, + * but with all of the inherited information from parent + * <code>Style</code> objects filled in. The object returned will + * be a new object, not a reference to this object, even if it does + * not need any information added. + * + * @return The <code>StyleCatalog</code> in which to look up + * ancestors. + */ + public Style getResolved() { + // Create a new object to return, which is a clone of this one. + CellStyle resolved = null; + try { + resolved = (CellStyle)this.clone(); + } catch (Exception e) { + Debug.log(Debug.ERROR, "Can't clone", e); + } + + // Look up the parentStyle. (If there is no style catalog + // specified, we can't do any lookups.) + CellStyle parentStyle = null; + if (sc != null) { + if (parent != null) { + parentStyle = (CellStyle)sc.lookup(parent, family, null, + this.getClass()); + if (parentStyle == null) + Debug.log(Debug.ERROR, "parent style lookup of " + + parent + " failed!"); + else + parentStyle = (CellStyle)parentStyle.getResolved(); + + } else if (!name.equals("DEFAULT_STYLE")) { + parentStyle = (CellStyle)sc.lookup("DEFAULT_STYLE", null, + null, this.getClass()); + } + } + + // If we found a parent, for any attributes which we don't have + // set, try to get the values from the parent. + if (parentStyle != null) { + parentStyle = (CellStyle)parentStyle.getResolved(); + Format parentFormat = parentStyle.getFormat(); + Format resolvedFormat = resolved.getFormat(); + + if ((fmt.getAlign() == Format.LEFT_ALIGN) && (parentFormat.getAlign() != Format.LEFT_ALIGN)) + resolvedFormat.setAlign(parentFormat.getAlign()); + if ((fmt.getVertAlign() == Format.BOTTOM_ALIGN) && (parentFormat.getVertAlign() != Format.BOTTOM_ALIGN)) + resolvedFormat.setVertAlign(parentFormat.getVertAlign()); + if ((fmt.getFontSize() == 0) && (parentFormat.getFontSize() != 0)) + resolvedFormat.setFontSize(parentFormat.getFontSize()); + if ((fmt.getFontName() == null) && (parentFormat.getFontName() != null)) + resolvedFormat.setFontName(parentFormat.getFontName()); + if ((fmt.getForeground() == null) && (parentFormat.getForeground() != null)) + resolvedFormat.setForeground(parentFormat.getForeground()); + if ((fmt.getBackground() == null) && (parentFormat.getBackground() != null)) + resolvedFormat.setBackground(parentFormat.getBackground()); + for (int m = Format.BOLD; m <= Format.SUBSCRIPT; m = m << 1) { + if ((fmt.getAttribute(m)) && (parentFormat.getAttribute(m))) { + resolvedFormat.setAttribute(m, parentFormat.getAttribute(m)); + } + } + + } + return resolved; + } + + + /** + * Create a new <code>Node</code> in the <code>Document</code>, and + * write this <code>Style</code> to it. + * + * @param parentDoc Parent <code>Document</code> of the + * <code>Node</code> to create. + * @param name Name to use for the new <code>Node</code> (e.g. + * <i>style:style</i>) + * + * @return Created <code>Node</code>. + */ + public Node createNode(org.w3c.dom.Document parentDoc, String name) { + Element node = parentDoc.createElement(name); + writeAttributes(node); + return node; + } + + + /** + * Return true if <code>style</code> specifies as much or less + * than this <code>Style</code>, and nothing it specifies + * contradicts this <code>Style</code>. + * + * @param style The <code>Style</code> to check. + * + * @return true if <code>style</code> is a subset, false + * otherwise. + */ + public boolean isSubset(Style style) { + if (style.getClass() != this.getClass()) + return false; + CellStyle tStyle = (CellStyle)style; + + Format rhs = tStyle.getFormat(); + + if(!fmt.isSubset(rhs)) + return false; + + return true; + } + + + /** + * Write this <code>Style</code> object's attributes to a + * <code>Node</code> in the <code>Document</code>. + * + * @param node The <code>Node</code> to add <code>Style</code> + * attributes. + */ + public void writeAttributes(Element node) { + + if (fmt.getAlign()==Format.RIGHT_ALIGN) + node.setAttribute("fo:text-align", "end"); + + if (fmt.getAlign()==Format.LEFT_ALIGN) + node.setAttribute("fo:text-align", "start"); + + if (fmt.getAlign()==Format.CENTER_ALIGN) + node.setAttribute("fo:text-align", "center"); + + if (fmt.getVertAlign()==Format.TOP_ALIGN) + node.setAttribute("fo:vertical-align", "top"); + + if (fmt.getVertAlign()==Format.MIDDLE_ALIGN) + node.setAttribute("fo:vertical-align", "middle"); + + if (fmt.getVertAlign()==Format.BOTTOM_ALIGN) + node.setAttribute("fo:vertical-align", "bottom"); + + if (fmt.getAttribute(Format.BOLD)) + node.setAttribute("fo:font-weight", "bold"); + + if (fmt.getAttribute(Format.ITALIC)) + node.setAttribute("fo:font-style", "italic"); + + if (fmt.getAttribute(Format.UNDERLINE)) + node.setAttribute("style:text-underline", "single"); + + if (fmt.getAttribute(Format.STRIKETHRU)) + node.setAttribute("style:text-crossing-out", "single-line"); + + if (fmt.getAttribute(Format.SUPERSCRIPT)) + node.setAttribute("style:text-position", "super 58%"); + + if (fmt.getAttribute(Format.SUBSCRIPT)) + node.setAttribute("style:text-position", "sub 58%"); + + if (fmt.getFontSize() != 0) { + Integer fs = new Integer(fmt.getFontSize()); + node.setAttribute("fo:font-size", fs.toString() + "pt"); + } + + if (fmt.getFontName() != null) + node.setAttribute("style:font-name", fmt.getFontName()); + + if (fmt.getForeground() != null) + node.setAttribute("fo:color", buildColorString(fmt.getForeground())); + + if (fmt.getBackground() != null) + node.setAttribute("fo:background-color", + buildColorString(fmt.getBackground())); + + if (fmt.getAttribute(Format.TOP_BORDER)) + node.setAttribute("fo:border-top", "0.0008inch solid #000000"); + + if (fmt.getAttribute(Format.BOTTOM_BORDER)) + node.setAttribute("fo:border-bottom", "0.0008inch solid #000000"); + + if (fmt.getAttribute(Format.RIGHT_BORDER)) + node.setAttribute("fo:border-right", "0.0008inch solid #000000"); + + if (fmt.getAttribute(Format.LEFT_BORDER)) + node.setAttribute("fo:border-left", "0.0008inch solid #000000"); + + if (fmt.getAttribute(Format.WORD_WRAP)) + node.setAttribute("fo:wrap-option", "wrap"); + + } + + + /** + * Given a <code>Color</code>, return a string of the form + * <i>#rrggbb</i>. + * + * @param c The <code>Color</code> value. + * + * @return The <code>Color</code> value in the form <i>#rrggbb</i>. + */ + private String buildColorString(Color c) { + int v[] = new int[3]; + v[0] = c.getRed(); + v[1] = c.getGreen(); + v[2] = c.getBlue(); + String colorString = new String("#"); + for (int i = 0; i <= 2; i++) { + String xx = Integer.toHexString(v[i]); + if (xx.length() < 2) + xx = "0" + xx; + colorString += xx; + } + return colorString; + } + + + private static String[] ignored = { + "style:text-autospace", "style:text-underline-color", + "fo:margin-left", "fo:margin-right", "fo:text-indent", + "fo:margin-top", "fo:margin-bottom", "text:line-number", + "text:number-lines", "style:country-asian", + "style:font-size-asian", "style:font-name-complex", + "style:language-complex", "style:country-complex", + "style:font-size-complex", "style:punctuation-wrap", + "fo:language", "fo:country", + "style:font-name-asian", "style:language-asian", + "style:line-break", "fo:keep-with-next" + }; + + + /* + * This code checks whether an attribute is one that we + * intentionally ignore. + * + * @param attribute The attribute to check. + * + * @return true if <code>attribute</code> can be ignored, + * otherwise false. + */ + private boolean isIgnored(String attribute) { + for (int i = 0; i < ignored.length; i++) { + if (ignored[i].equals(attribute)) + return true; + } + return false; + } +} + diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/ColumnRowInfo.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/ColumnRowInfo.java new file mode 100644 index 000000000000..a179633de71c --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/ColumnRowInfo.java @@ -0,0 +1,198 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: ColumnRowInfo.java,v $ + * $Revision: 1.4 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +package org.openoffice.xmerge.converter.xml.sxc; + +/** + * This is a class to define a table-column structure. This can then be + * used by plugins to write or read their own column types. + * + * @author Martin Maher + */ +public class ColumnRowInfo { + + final public static int COLUMN = 0x01; + final public static int ROW = 0x02; + + final private static int DEFAULTROWSIZE_MIN = 250; + final private static int DEFAULTROWSIZE_MAX = 260; + + private int type; + private int dimension = 0; + private int repeated = 1; + private boolean userDefined = true; + private Format fmt = new Format(); + + /** + * Constructor for a <code>ColumnRowInfo</code> + * + * @param dimension if it's a row the height, a column the width + * @param repeated + */ + public ColumnRowInfo(int type) { + + this.type = type; + } + + /** + * Constructor for a <code>ColumnRowInfo</code> + * + * @param dimension if it's a row the height, a column the width + * @param repeated how many times it is repeated + * @param type whether Row or column record + */ + public ColumnRowInfo(int dimension, int repeated, int type) { + + this.dimension = dimension; + this.repeated = repeated; + this.type = type; + } + + /** + * Constructor that includes userDefined field + * + * @param userDefined whether the record is manually set + */ + public ColumnRowInfo(int dimension, int repeated, int type, boolean userDefined) { + + this(dimension, repeated, type); + this.userDefined = userDefined; + } + + /** + * sets the definition + * + * @param newDefinition sets the definition + */ + public void setFormat(Format fmt) { + + this.fmt = fmt; + } + + /** + * returns Name of the definition + * + * @return the name which identifies the definition + */ + public Format getFormat() { + + return fmt; + } + + /** + * returns Name of the definition + * + * @return the name which identifies the definition + */ + public int getSize() { + + return dimension; + } + + /** + * sets the definition + * + * @param newDefinition sets the definition + */ + public void setSize(int dimension) { + + this.dimension = dimension; + } + /** + * Returns the definition itself + * + * @return the definition + */ + public int getRepeated() { + + return repeated; + } + + /** + * Returns the base Cell address + * + * @return the base cell address + */ + public void setRepeated(int repeated) { + + this.repeated = repeated; + } + + /** + * Returns the definition itself + * + * @return the definition + */ + public boolean isRow() { + + if(type==ROW) + return true; + else + return false; + } + + /** + * Returns the base Cell address + * + * @return the base cell address + */ + public boolean isColumn() { + + if(type==COLUMN) + return true; + else + return false; + } + + /** + * Test if the row height as been set manually + * + * @return true if user defined otherwise false + */ + public boolean isUserDefined() { + + return userDefined; + } + + /** + * Test if the row height is default + * + * @return true if default otherwise false + */ + public boolean isDefaultSize() { + + if( type==ROW && + dimension>DEFAULTROWSIZE_MIN && + dimension<DEFAULTROWSIZE_MAX) + return true; + else + return false; + } +} diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/ColumnStyle.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/ColumnStyle.java new file mode 100644 index 000000000000..2b9c169ff352 --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/ColumnStyle.java @@ -0,0 +1,309 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: ColumnStyle.java,v $ + * $Revision: 1.3 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +package org.openoffice.xmerge.converter.xml.sxc; + +import java.awt.Color; +import java.io.IOException; + +import org.w3c.dom.NodeList; +import org.w3c.dom.Node; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Element; + +import org.openoffice.xmerge.Document; +import org.openoffice.xmerge.ConverterCapabilities; +import org.openoffice.xmerge.converter.xml.OfficeDocument; +import org.openoffice.xmerge.converter.xml.Style; +import org.openoffice.xmerge.converter.xml.StyleCatalog; +import org.openoffice.xmerge.util.Debug; +import org.openoffice.xmerge.util.TwipsConverter; + +/** + * Represents a text <code>Style</code> in an OpenOffice document. + * + * @author Martin Maher + */ +public class ColumnStyle extends Style implements Cloneable { + + private int colWidth = 0; + /** + * Constructor for use when going from DOM to client device format. + * + * @param Node The <i>style:style</i> <code>Node</code> containing + * the <code>Style</code>. (This <code>Node</code> is + * assumed have a <i>family</i> attribute of <i>text</i>). + * @param sc The <code>StyleCatalog</code>, which is used for + * looking up ancestor <code>Style</code> objects. + */ + public ColumnStyle(Node node, StyleCatalog sc) { + super(node, sc); + + // Run through the attributes of this node, saving + // the ones we're interested in. + NamedNodeMap attrNodes = node.getAttributes(); + if (attrNodes != null) { + int len = attrNodes.getLength(); + for (int i = 0; i < len; i++) { + Node attr = attrNodes.item(i); + handleAttribute(attr.getNodeName(), attr.getNodeValue()); + } + } + + // Look for children. Only ones we care about are "style:properties" + // nodes. If any are found, recursively traverse them, passing + // along the style element to add properties to. + if (node.hasChildNodes()) { + NodeList children = node.getChildNodes(); + int len = children.getLength(); + for (int i = 0; i < len; i++) { + Node child = children.item(i); + String name = child.getNodeName(); + if (name.equals("style:properties")) { + NamedNodeMap childAttrNodes = child.getAttributes(); + if (childAttrNodes != null) { + int nChildAttrNodes = childAttrNodes.getLength(); + for (int j = 0; j < nChildAttrNodes; j++) { + Node attr = childAttrNodes.item(j); + handleAttribute(attr.getNodeName(), + attr.getNodeValue()); + } + } + } + } + } + } + + + /** + * Constructor for use when going from client device format to DOM + * + * @param name Name of text <code>Style</code>. Can be null. + * @param family Family of text <code>Style</code> (usually + * <i>text</i>). Can be null. + * @param parent Name of parent text <code>Style</code>, or null + * for none. + * @param mask the width of this column + * @param sc The <code>StyleCatalog</code>, which is used for + * looking up ancestor <code>Style</code> objects. + */ + public ColumnStyle(String name, String family, String parent,int colWidth, StyleCatalog sc) { + super(name, family, parent, sc); + this.colWidth = colWidth; + } + + /** + * Returns the width of this column + * + * @return the <code>Format</code> object + */ + public int getColWidth() { + return colWidth; + } + + /** + * Sets the width of this column + * + * @return the <code>Format</code> object + */ + public void setColWidth(int colWidth) { + + this.colWidth = colWidth; + } + + /** + * Parse a colwidth in the form "1.234cm" to twips + * + * @param value <code>String</code> specification to parse. + * + * @return The twips equivalent. + */ + private int parseColWidth(String value) { + + int width = 255; // Default value + + if(value.indexOf("cm")!=-1) { + float widthCM = Float.parseFloat(value.substring(0,value.indexOf("c"))); + width = TwipsConverter.cm2twips(widthCM); + } else if(value.indexOf("inch")!=-1) { + float widthInch = Float.parseFloat(value.substring(0,value.indexOf("i"))); + width = TwipsConverter.inches2twips(widthInch); + } + + return (width); + } + + + /** + * Set an attribute. + * + * @param attr The attribute to set. + * @param value The attribute value to set. + */ + private void handleAttribute(String attr, String value) { + + if (attr.equals("style:column-width")) { + colWidth = parseColWidth(value); + } + else { + Debug.log(Debug.INFO, "ColumnStyle Unhandled: " + attr + "=" + value); + } + } + + + /** + * Return a <code>Style</code> object corresponding to this one, + * but with all of the inherited information from parent + * <code>Style</code> objects filled in. The object returned will + * be a new object, not a reference to this object, even if it does + * not need any information added. + * + * @return The <code>StyleCatalog</code> in which to look up + * ancestors. + */ + public Style getResolved() { + // Create a new object to return, which is a clone of this one. + ColumnStyle resolved = null; + try { + resolved = (ColumnStyle)this.clone(); + } catch (Exception e) { + Debug.log(Debug.ERROR, "Can't clone", e); + } + + // Look up the parentStyle. (If there is no style catalog + // specified, we can't do any lookups.) + ColumnStyle parentStyle = null; + if (sc != null) { + if (parent != null) { + parentStyle = (ColumnStyle)sc.lookup(parent, family, null, + this.getClass()); + if (parentStyle == null) + Debug.log(Debug.ERROR, "parent style lookup of " + + parent + " failed!"); + else + parentStyle = (ColumnStyle)parentStyle.getResolved(); + + } else if (!name.equals("DEFAULT_STYLE")) { + parentStyle = (ColumnStyle)sc.lookup("DEFAULT_STYLE", null, + null, this.getClass()); + } + } + + // If we found a parent, for any attributes which we don't have + // set, try to get the values from the parent. + if (parentStyle != null) { + parentStyle = (ColumnStyle)parentStyle.getResolved(); + + if ((colWidth == 0) && (parentStyle.getColWidth() != 0)) + resolved.setColWidth(parentStyle.getColWidth()); + } + return resolved; + } + + + /** + * Create a new <code>Node</code> in the <code>Document</code>, and + * write this <code>Style</code> to it. + * + * @param parentDoc Parent <code>Document</code> of the + * <code>Node</code> to create. + * @param name Name to use for the new <code>Node</code> (e.g. + * <i>style:style</i>) + * + * @return Created <code>Node</code>. + */ + public Node createNode(org.w3c.dom.Document parentDoc, String name) { + Element node = parentDoc.createElement(name); + writeAttributes(node); + return node; + } + + + /** + * Return true if <code>style</code> specifies as much or less + * than this <code>Style</code>, and nothing it specifies + * contradicts this <code>Style</code>. + * + * @param style The <code>Style</code> to check. + * + * @return true if <code>style</code> is a subset, false + * otherwise. + */ + public boolean isSubset(Style style) { + if (style.getClass() != this.getClass()) + return false; + ColumnStyle tStyle = (ColumnStyle)style; + + if(colWidth!=tStyle.getColWidth()) + return false; + + return true; + } + + + /** + * Write this <code>Style</code> object's attributes to a + * <code>Node</code> in the <code>Document</code>. + * + * @param node The <code>Node</code> to add <code>Style</code> + * attributes. + */ + public void writeAttributes(Element node) { + + if(colWidth!=0) { + String width = TwipsConverter.twips2cm(colWidth) + "cm"; + node.setAttribute("style:column-width", width); + } + } + + + private static String[] ignored = { + "fo:break-before", "fo:keep-with-next" + }; + + + /* + * This code checks whether an attribute is one that we + * intentionally ignore. + * + * @param attribute The attribute to check. + * + * @return true if <code>attribute</code> can be ignored, + * otherwise false. + */ + private boolean isIgnored(String attribute) { + for (int i = 0; i < ignored.length; i++) { + if (ignored[i].equals(attribute)) + return true; + } + return false; + } +} + diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/DocumentMergerImpl.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/DocumentMergerImpl.java new file mode 100644 index 000000000000..3ae4f16acfcf --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/DocumentMergerImpl.java @@ -0,0 +1,202 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: DocumentMergerImpl.java,v $ + * $Revision: 1.3 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +package org.openoffice.xmerge.converter.xml.sxc; + +import org.w3c.dom.Node; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; + +import org.openoffice.xmerge.Document; +import org.openoffice.xmerge.DocumentMerger; +import org.openoffice.xmerge.MergeException; +import org.openoffice.xmerge.ConverterCapabilities; +import org.openoffice.xmerge.converter.xml.OfficeConstants; +import org.openoffice.xmerge.merger.DiffAlgorithm; +import org.openoffice.xmerge.merger.Difference; +import org.openoffice.xmerge.merger.Iterator; +import org.openoffice.xmerge.merger.DiffAlgorithm; +import org.openoffice.xmerge.merger.NodeMergeAlgorithm; +import org.openoffice.xmerge.merger.diff.IteratorRowCompare; +import org.openoffice.xmerge.merger.diff.RowIterator; +import org.openoffice.xmerge.merger.merge.SheetMerge; +import org.openoffice.xmerge.merger.merge.PositionBaseRowMerge; +import org.openoffice.xmerge.merger.MergeAlgorithm; +import org.openoffice.xmerge.util.XmlUtil; +import org.openoffice.xmerge.util.Debug; + + +/** + * Generic small device implementation of <code>DocumentMerger</code> for + * the {@link + * org.openoffice.xmerge.converter.xml.sxc.SxcPluginFactory + * SxcPluginFactory}. Used with SXC <code>Document</code> objects.</p> + */ +public class DocumentMergerImpl implements DocumentMerger { + + private ConverterCapabilities cc_; + private org.openoffice.xmerge.Document orig = null; + + /** + * Constructor + * + * @param doc The original "Office" <code>Document</code> + * to merge. + * @param cc The <code>ConverterCapabilities</code>. + */ + public DocumentMergerImpl(org.openoffice.xmerge.Document doc, ConverterCapabilities cc) { + cc_ = cc; + this.orig = doc; + } + + public void merge(Document modifiedDoc) throws MergeException { + + SxcDocument sdoc1 = (SxcDocument)orig; + SxcDocument sdoc2 = (SxcDocument)modifiedDoc; + + org.w3c.dom.Document doc1 = sdoc1.getContentDOM(); + org.w3c.dom.Document doc2 = sdoc2.getContentDOM(); + + Element elem1 = doc1.getDocumentElement(); + Element elem2 = doc2.getDocumentElement(); + + // get table name + NodeList workSheetList1 = + elem1.getElementsByTagName(OfficeConstants.TAG_TABLE); + NodeList workSheetList2 = + elem2.getElementsByTagName(OfficeConstants.TAG_TABLE); + + int numOfWorkSheet = workSheetList1.getLength(); + + for (int i=0; i < numOfWorkSheet; i++) { + Node workSheet = workSheetList1.item(i); + + // try to match the workSheet + Node matchingWorkSheet = matchWorkSheet(workSheet, workSheetList2); + + if (matchingWorkSheet != null) { + + // need to put it into a row Iterator + // use a straight comparsion algorithm then do a merge on it + Iterator i1 = new RowIterator(cc_, workSheet); + Iterator i2 = new RowIterator(cc_, matchingWorkSheet); + + // find out the diff + DiffAlgorithm diffAlgo = new IteratorRowCompare(); + + // find out the paragrah level diffs + Difference[] diffResult = diffAlgo.computeDiffs(i1, i2); + + if (Debug.isFlagSet(Debug.INFO)) { + Debug.log(Debug.INFO, "Diff Result: "); + + for (int j = 0; j < diffResult.length; j++) { + Debug.log(Debug.INFO, diffResult[j].debug()); + } + } + + // merge back the result + NodeMergeAlgorithm rowMerger = new PositionBaseRowMerge(cc_); + MergeAlgorithm merger = new SheetMerge(cc_, rowMerger); + + Iterator result = null; + + merger.applyDifference(i1, i2, diffResult); + } + } + + numOfWorkSheet = workSheetList2.getLength(); + + // for those workSheet from target don't have a matching one + // in the original workSheet list, we add it + + // find out the office body node first + NodeList officeBodyList = + elem1.getElementsByTagName(OfficeConstants.TAG_OFFICE_BODY); + + Node officeBody = officeBodyList.item(0); + + // for each WorkSheets, try to see whether we have it or not + for (int j=0; j < numOfWorkSheet; j++) { + Node workSheet= workSheetList2.item(j); + + // try to match the workSheet + // + Node matchingWorkSheet = matchWorkSheet(workSheet, workSheetList1); + + // add the new WorkSheet to the original document iff match not + // found + // + if (matchingWorkSheet == null) { + Node cloneNode = XmlUtil.deepClone(officeBody, workSheet); + officeBody.appendChild(cloneNode); + } + } + } + + /** + * Try to find a WorkSheet from the modified WorkSheetList that + * has a matching table name from the original WorkSheet. + * + * @param orgSheet The original WorkSheet. + * @param modSheetList The modified WorkSheet. + * + * @return The Node in modSheetList that matches the orgSheet. + */ + private Node matchWorkSheet(Node orgSheet, NodeList modSheetList) { + + Node matchSheet = null; + + String orgTableName = ((Element)orgSheet).getAttribute( + OfficeConstants.ATTRIBUTE_TABLE_NAME); + + if (orgTableName == null) + return null; + + int numOfWorkSheet = modSheetList.getLength(); + + String modTableName; + + for (int i=0; i < numOfWorkSheet; i++) { + modTableName = ((Element)modSheetList.item(i)).getAttribute( + OfficeConstants.ATTRIBUTE_TABLE_NAME); + if (modTableName == null) + continue; + + if (orgTableName.equals(modTableName)) { + matchSheet = modSheetList.item(i); + break; + } + } + + return matchSheet; + } +} + diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/Format.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/Format.java new file mode 100644 index 000000000000..8e58ea0dd968 --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/Format.java @@ -0,0 +1,480 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: Format.java,v $ + * $Revision: 1.13 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +package org.openoffice.xmerge.converter.xml.sxc; + +import java.awt.Color; + +import org.openoffice.xmerge.util.Debug; + +/** + * This class specifies the format for a given spreadsheet cell. + * + * @author Mark Murnane + * @author Martin Maher (Extended Style Support) + */ +public class Format implements Cloneable { + + /** Horizontal Alignment Constants. */ + final public static int RIGHT_ALIGN = 0x01; + final public static int CENTER_ALIGN = 0x02; + final public static int LEFT_ALIGN = 0x03; + final public static int JUST_ALIGN = 0x04; + + /** Vertical Alignment Constants. */ + final public static int TOP_ALIGN = 0x01; + final public static int MIDDLE_ALIGN = 0x02; + final public static int BOTTOM_ALIGN = 0x03; + + /** Indicates <i>bold</i> text. */ + final public static int BOLD = 0x01; + /** Indicates <i>italic</i> text. */ + final public static int ITALIC = 0x02; + /** Indicates <i>underlined</i> text. */ + final public static int UNDERLINE = 0x04; + /** Indicates <i>strike-through</i> in the text. */ + final public static int STRIKETHRU = 0x08; + /** Indicates <i>superscripted</i> text. */ + final public static int SUPERSCRIPT = 0x10; + /** Indicates <i>subscripted</i> text. */ + final public static int SUBSCRIPT = 0x20; + + final public static int LEFT_BORDER = 0x40; + final public static int RIGHT_BORDER = 0x80; + final public static int TOP_BORDER = 0x100; + final public static int BOTTOM_BORDER = 0x200; + + final public static int WORD_WRAP = 0x400; + + private int align; + private int vertAlign; + private String category; + private String value; + private String formatSpecifier; + private int decimalPlaces; + + /** Font name. */ + private String fontName; + /** Font size in points. */ + protected int sizeInPoints; + + private Color foreground, background; + + /** Values of text attributes. */ + protected int attributes = 0; + /** Bitwise mask of text attributes. */ + protected int mask = 0; + + /** + * Constructor for creating a new <code>Format</code>. + */ + public Format() { + clearFormatting(); + } + + /** + * Constructor that creates a new <code>Format</code> object + * by setting all the format attributes. + * + */ + public Format(int attributes, int fontSize, String fontName) { + + this.attributes = attributes; + sizeInPoints = fontSize; + this.fontName = fontName; + } + + /** + * Constructor for creating a new <code>Format</code> object + * based on an existing one. + * + * @param fmt <code>Format</code> to copy. + */ + public Format(Format fmt) { + category = fmt.getCategory(); + value = fmt.getValue(); + formatSpecifier = fmt.getFormatSpecifier(); + decimalPlaces = fmt.getDecimalPlaces(); + + attributes = fmt.attributes; + mask = fmt.mask; + + fontName = fmt.getFontName(); + align = fmt.getAlign(); + vertAlign = fmt.getVertAlign(); + foreground = fmt.getForeground(); + background = fmt.getBackground(); + sizeInPoints = fmt.sizeInPoints; + } + + + /** + * Reset this <code>Format</code> description. + */ + public void clearFormatting() { + category = ""; + value = ""; + formatSpecifier = ""; + decimalPlaces = 0; + attributes = 0; + mask = 0; + sizeInPoints = 10; + align = LEFT_ALIGN; + vertAlign = BOTTOM_ALIGN; + fontName = ""; + foreground = null; + background = null; + } + + /** + * Set one or more text attributes to <i>on</i>. + * + * @param flags Flag attributes to set <i>on</i>. + */ + public void setAttribute(int flags, boolean toggle) { + mask |= flags; + if(toggle) { + attributes |= flags; + } else { + attributes &= ~flags; + } + } + + /** + * Return true if the <code>attribute</code> is set to <i>on</i> + * + * @param attribute Attribute to check ({@link #BOLD}, + * {@link #ITALIC}, etc.) + * + * @return true if <code>attribute</code> is set to <i>on</i>, + * otherwise false. + */ + public boolean getAttribute(int attribute) { + if ((mask & attribute) == 0) + return false; + return (!((attributes & attribute) == 0)); + } + + /** + * Return true if text <code>attribute</code> is set in this + * <code>Style</code>.An attribute that is set may have a + * value of <i>on</i> or <i>off</i>. + * + * @param attribute The attribute to check ({@link #BOLD}, + * {@link #ITALIC}, etc.). + * + * @return true if text <code>attribute</code> is set in this + * <code>Style</code>, false otherwise. + */ + public boolean isSet(int attribute) { + return (!((mask & attribute) == 0)); + } + + + /** + * Set the formatting category of this object, ie number, date, + * currency.The <code>OfficeConstants</code> class contains string + * constants for the category types. + * + * @see org.openoffice.xmerge.converter.xml.OfficeConstants + * + * @param newCategory The name of the category to be set. + */ + public void setCategory(String newCategory) { + category = newCategory; + } + + /** + * Return the formatting category of the object. + * + * @see org.openoffice.xmerge.converter.xml.OfficeConstants + * + * @return The formatting category of the object. + */ + public String getCategory() { + return category; + } + + /** + * In the case of Formula returns the value of the formula. + * + * @return The value of the formula + */ + public String getValue() { + return value; + } + + /** + * In the case of formula the contents are set as the formula string and + * the value of the formula is a formatting attribute. + * + * @param newValue the formuala value + */ + public void setValue(String newValue) { + value = newValue; + } + + + /** + * Set the <code>Format</code> specifier for this category. + * + * @param formatString The new <code>Format</code> specifier. + */ + public void setFormatSpecifier(String formatString) { + formatSpecifier = formatString; + } + + + /** + * Get the <code>Format</code> specifier for this category. + * + * @return <code>Format</code> specifier for this category. + */ + public String getFormatSpecifier() { + return formatSpecifier; + } + + + /** + * Set the precision of the number to be displayed. + * + * @param precision The number of decimal places to display. + */ + public void setDecimalPlaces(int precision) { + decimalPlaces = precision; + } + + + /** + * Get the number of decimal places displayed. + * + * @return Number of decimal places. + */ + public int getDecimalPlaces() { + return decimalPlaces; + } + + + /** + * Set the font used for this cell. + * + * @param fontName The name of the font. + */ + public void setFontName(String fontName) { + this.fontName = fontName; + } + + + /** + * Get the font used for this cell. + * + * @return The font name. + */ + public String getFontName() { + return fontName; + } + + /** + * Set the font used for this cell. + * + * @param fontName The name of the font. + */ + public void setFontSize(int fontSize) { + sizeInPoints = fontSize; + } + + + /** + * Get the font used for this cell. + * + * @return The font name. + */ + public int getFontSize() { + return sizeInPoints; + } + + /** + * Set the alignmen used for this cell. + * + * @param fontName The name of the font. + */ + public void setVertAlign(int vertAlign) { + this.vertAlign = vertAlign; + } + + + /** + * Get the alignment used for this cell. + * + * @return The font name. + */ + public int getVertAlign() { + return vertAlign; + } + + /** + * Set the alignmen used for this cell. + * + * @param fontName The name of the font. + */ + public void setAlign(int align) { + this.align = align; + } + + + /** + * Get the alignment used for this cell. + * + * @return The font name. + */ + public int getAlign() { + return align; + } + /** + * Set the Foreground <code>Color</code> for this cell. + * + * @param color A <code>Color</code> object representing the + * foreground color. + */ + public void setForeground(Color c) { + if(c!=null) + foreground = new Color(c.getRGB()); + } + + + /** + * Get the Foreground <code>Color</code> for this cell. + * + * @return Foreground <code>Color</code> value. + */ + public Color getForeground() { + return foreground; + } + + + /** + * Set the Background <code>Color</code> for this cell + * + * @param color A <code>Color</code> object representing + * the background color. + */ + public void setBackground(Color c) { + if(c!=null) + background = new Color(c.getRGB()); + } + + + /** + * Get the Foreground <code>Color</code> for this cell + * + * @return Background <code>Color</code> value + */ + public Color getBackground() { + return background; + } + + /** + * Get the Foreground <code>Color</code> for this cell + * + * @return Background <code>Color</code> value + */ + public String toString() { + return new String("Value : " + getValue() + " Category : " + getCategory()); + } + + /** + * Tests if the current <code>Format</code> object has default attribute + * values. + * + * @return true if it contains default value + */ + public boolean isDefault() { + + Format rhs = new Format(); + + if (rhs.attributes!= attributes) + return false; + + if (foreground!=rhs.foreground) + return false; + + if (background!=rhs.background) + return false; + + if (rhs.align!= align) + return false; + + if (rhs.vertAlign!= vertAlign) + return false; + + return true; + } + + /** + * Return true if <code>style</code> specifies as much or less + * than this <code>Style</code>, and nothing it specifies + * contradicts this <code>Style</code>. + * + * @param style The <code>Style</code> to check. + * + * @return true if <code>style</code> is a subset, false + * otherwise. + */ + public boolean isSubset(Format rhs) { + if (rhs.getClass() != this.getClass()) + return false; + + if (rhs.attributes!= attributes) + return false; + + if (rhs.sizeInPoints != 0) { + if (sizeInPoints != rhs.sizeInPoints) + return false; + } + + if (fontName!=rhs.fontName) + return false; + + if (foreground!=rhs.foreground) + return false; + + if (background!=rhs.background) + return false; + + if (rhs.align!= align) + return false; + + if (rhs.vertAlign!= vertAlign) + return false; + + return true; + } +} + diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/NameDefinition.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/NameDefinition.java new file mode 100644 index 000000000000..65f0979c4ed2 --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/NameDefinition.java @@ -0,0 +1,219 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: NameDefinition.java,v $ + * $Revision: 1.5 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +package org.openoffice.xmerge.converter.xml.sxc; + +import org.w3c.dom.NodeList; +import org.w3c.dom.Node; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; + +import org.openoffice.xmerge.converter.xml.OfficeConstants; +import org.openoffice.xmerge.util.Debug; +import org.openoffice.xmerge.util.XmlUtil; + +/** + * This is a class to define a Name Definition structure. This can then be + * used by plugins to write or read their own definition types. + * + * @author Martin Maher + */ +public class NameDefinition implements OfficeConstants { + + private String name; // name which identifies the definition + private String definition; // the definition itself + private String baseCellAddress; // the basecelladdress + private boolean rangeType = false; // true if definition of type range + private boolean expressionType = false; // true if definition of type expression + + /** + * Default Constructor for a <code>NameDefinition</code> + * + */ + public NameDefinition() { + + } + + /** + * Constructor that takes a <code>Node</code> to build a + * <code>NameDefinition</code> + * + * @param root XML Node to read from + */ + public NameDefinition(Node root) { + readNode(root); + } + + /** + * Default Constructor for a <code>NameDefinition</code> + * + */ + public NameDefinition(String name, String definition, String + baseCellAddress, boolean rangeType, boolean expressionType ) { + this.name = name; + this.definition = definition; + this.baseCellAddress = baseCellAddress; + this.rangeType = rangeType; + this.expressionType = expressionType; + } + + /** + * returns Name of the definition + * + * @return the name which identifies the definition + */ + public String getName() { + + return name; + } + /** + * sets the definition + * + * @param newDefinition sets the definition + */ + public void setDefinition(String newDefinition) { + + definition = newDefinition; + } + /** + * Returns the definition itself + * + * @return the definition + */ + public String getDefinition() { + + return definition; + } + + /** + * Returns the base Cell address + * + * @return the base cell address + */ + public String getBaseCellAddress() { + + return baseCellAddress; + } + + /** + * Tests if definition is of type expression + * + * @return whether or not this name definition is of type expression + */ + public boolean isExpressionType() { + return expressionType; + } + + /** + * Tests if definition is of type range + * + * @return whether or not this name definition is of type range + */ + public boolean isRangeType() { + return rangeType; + } + + /** + * Writes out a content.xml entry for this NameDefinition object + * + * @param settings a <code>Document</code> object representing the settings.xml + * @param root the root xml node to add to + */ + public void writeNode(org.w3c.dom.Document doc, Node root) { + + if(isRangeType()) { + + Debug.log(Debug.TRACE, "Found Range Name : " + getName()); + Element namedRangeElement = (Element) doc.createElement(TAG_TABLE_NAMED_RANGE); + namedRangeElement.setAttribute(ATTRIBUTE_TABLE_NAME, getName()); + namedRangeElement.setAttribute(ATTRIBUTE_TABLE_BASE_CELL_ADDRESS, getBaseCellAddress()); + namedRangeElement.setAttribute(ATTRIBUTE_TABLE_CELL_RANGE_ADDRESS, getDefinition()); + root.appendChild(namedRangeElement); + } else if (isExpressionType()) { + + Debug.log(Debug.TRACE, "Found Expression Name : " + getName()); + Element namedExpressionElement = (Element) doc.createElement(TAG_TABLE_NAMED_EXPRESSION); + namedExpressionElement.setAttribute(ATTRIBUTE_TABLE_NAME, getName()); + namedExpressionElement.setAttribute(ATTRIBUTE_TABLE_BASE_CELL_ADDRESS,getBaseCellAddress()); + namedExpressionElement.setAttribute(ATTRIBUTE_TABLE_EXPRESSION, getDefinition()); + root.appendChild(namedExpressionElement); + } else { + + Debug.log(Debug.TRACE, "Unknown Name Definition : " + getName()); + } + } + + /** + * Reads document settings from xml and inits Settings variables + * + * @param root XML Node to read from + */ + public void readNode(Node root) { + + String nodeName = root.getNodeName(); + NamedNodeMap cellAtt = root.getAttributes(); + + if (nodeName.equals(TAG_TABLE_NAMED_RANGE)) { + + Node tableNameNode = + cellAtt.getNamedItem(ATTRIBUTE_TABLE_NAME); + Node tableBaseCellAddress = + cellAtt.getNamedItem(ATTRIBUTE_TABLE_BASE_CELL_ADDRESS); + Node tableCellRangeAddress = + cellAtt.getNamedItem(ATTRIBUTE_TABLE_CELL_RANGE_ADDRESS); + Debug.log(Debug.TRACE,"Named-range : " + tableNameNode.getNodeValue()); + // Create a named-range name definition + name = tableNameNode.getNodeValue(); + definition = tableCellRangeAddress.getNodeValue(); + baseCellAddress = tableBaseCellAddress.getNodeValue(); + expressionType = true; + rangeType = false; + + } else if (nodeName.equals(TAG_TABLE_NAMED_EXPRESSION)) { + + Node tableNameNode = + cellAtt.getNamedItem(ATTRIBUTE_TABLE_NAME); + Node tableBaseCellAddress = + cellAtt.getNamedItem(ATTRIBUTE_TABLE_BASE_CELL_ADDRESS); + Node tableExpression= + cellAtt.getNamedItem(ATTRIBUTE_TABLE_EXPRESSION); + Debug.log(Debug.TRACE,"Named-expression: " + tableNameNode.getNodeValue()); + // Create a named-range name definition + name = tableNameNode.getNodeValue(); + definition = tableExpression.getNodeValue(); + baseCellAddress = tableBaseCellAddress.getNodeValue(); + expressionType = false; + rangeType = true; + } else { + Debug.log(Debug.TRACE, "<OTHERS " + XmlUtil.getNodeInfo(root) + " />"); + } + } + +} diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/RowStyle.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/RowStyle.java new file mode 100644 index 000000000000..71839e238d43 --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/RowStyle.java @@ -0,0 +1,308 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: RowStyle.java,v $ + * $Revision: 1.3 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +package org.openoffice.xmerge.converter.xml.sxc; + +import java.io.IOException; + +import org.w3c.dom.NodeList; +import org.w3c.dom.Node; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Element; + +import org.openoffice.xmerge.Document; +import org.openoffice.xmerge.ConverterCapabilities; +import org.openoffice.xmerge.converter.xml.OfficeDocument; +import org.openoffice.xmerge.converter.xml.Style; +import org.openoffice.xmerge.converter.xml.StyleCatalog; +import org.openoffice.xmerge.util.Debug; +import org.openoffice.xmerge.util.TwipsConverter; + +/** + * Represents a text <code>Style</code> in an OpenOffice document. + * + * @author Martin Maher + */ +public class RowStyle extends Style implements Cloneable { + + private int rowHeight = 255; + /** + * Constructor for use when going from DOM to client device format. + * + * @param Node The <i>style:style</i> <code>Node</code> containing + * the <code>Style</code>. (This <code>Node</code> is + * assumed have a <i>family</i> attribute of <i>text</i>). + * @param sc The <code>StyleCatalog</code>, which is used for + * looking up ancestor <code>Style</code> objects. + */ + public RowStyle(Node node, StyleCatalog sc) { + super(node, sc); + + // Run through the attributes of this node, saving + // the ones we're interested in. + NamedNodeMap attrNodes = node.getAttributes(); + if (attrNodes != null) { + int len = attrNodes.getLength(); + for (int i = 0; i < len; i++) { + Node attr = attrNodes.item(i); + handleAttribute(attr.getNodeName(), attr.getNodeValue()); + } + } + + // Look for children. Only ones we care about are "style:properties" + // nodes. If any are found, recursively traverse them, passing + // along the style element to add properties to. + if (node.hasChildNodes()) { + NodeList children = node.getChildNodes(); + int len = children.getLength(); + for (int i = 0; i < len; i++) { + Node child = children.item(i); + String name = child.getNodeName(); + if (name.equals("style:properties")) { + NamedNodeMap childAttrNodes = child.getAttributes(); + if (childAttrNodes != null) { + int nChildAttrNodes = childAttrNodes.getLength(); + for (int j = 0; j < nChildAttrNodes; j++) { + Node attr = childAttrNodes.item(j); + handleAttribute(attr.getNodeName(), + attr.getNodeValue()); + } + } + } + } + } + } + + + /** + * Constructor for use when going from client device format to DOM + * + * @param name Name of text <code>Style</code>. Can be null. + * @param family Family of text <code>Style</code> (usually + * <i>text</i>). Can be null. + * @param parent Name of parent text <code>Style</code>, or null + * for none. + * @param mask The height of this row + * @param sc The <code>StyleCatalog</code>, which is used for + * looking up ancestor <code>Style</code> objects. + */ + public RowStyle(String name, String family, String parent,int rowHeight, StyleCatalog sc) { + super(name, family, parent, sc); + this.rowHeight=rowHeight; + } + + /** + * Returns the height of this row + * + * @return the <code>Format</code> object + */ + public int getRowHeight() { + return rowHeight; + } + + /** + * Sets the height of this row + * + * @return the <code>Format</code> object + */ + public void setRowHeight(int RowHeight) { + + this.rowHeight = rowHeight; + } + /** + * Parse a colheight in the form "1.234cm" to twips + * + * @param value <code>String</code> specification to parse. + * + * @return The twips equivalent. + */ + private int parseRowHeight(String value) { + + int height = 255; // Default value + + if(value.indexOf("cm")!=-1) { + float heightCM = Float.parseFloat(value.substring(0,value.indexOf("c"))); + height = TwipsConverter.cm2twips(heightCM); + } else if(value.indexOf("inch")!=-1) { + float heightInch = Float.parseFloat(value.substring(0,value.indexOf("i"))); + height = TwipsConverter.inches2twips(heightInch); + } + + return (height); + + } + + + /** + * Set an attribute. + * + * @param attr The attribute to set. + * @param value The attribute value to set. + */ + private void handleAttribute(String attr, String value) { + + if (attr.equals("style:row-height")) { + rowHeight = parseRowHeight(value); + } + else { + Debug.log(Debug.INFO, "RowStyle Unhandled: " + attr + "=" + value); + } + } + + + /** + * Return a <code>Style</code> object corresponding to this one, + * but with all of the inherited information from parent + * <code>Style</code> objects filled in. The object returned will + * be a new object, not a reference to this object, even if it does + * not need any information added. + * + * @return The <code>StyleCatalog</code> in which to look up + * ancestors. + */ + public Style getResolved() { + // Create a new object to return, which is a clone of this one. + RowStyle resolved = null; + try { + resolved = (RowStyle)this.clone(); + } catch (Exception e) { + Debug.log(Debug.ERROR, "Can't clone", e); + } + + // Look up the parentStyle. (If there is no style catalog + // specified, we can't do any lookups.) + RowStyle parentStyle = null; + if (sc != null) { + if (parent != null) { + parentStyle = (RowStyle)sc.lookup(parent, family, null, + this.getClass()); + if (parentStyle == null) + Debug.log(Debug.ERROR, "parent style lookup of " + + parent + " failed!"); + else + parentStyle = (RowStyle)parentStyle.getResolved(); + + } else if (!name.equals("DEFAULT_STYLE")) { + parentStyle = (RowStyle)sc.lookup("DEFAULT_STYLE", null, + null, this.getClass()); + } + } + + // If we found a parent, for any attributes which we don't have + // set, try to get the values from the parent. + if (parentStyle != null) { + parentStyle = (RowStyle)parentStyle.getResolved(); + + if ((rowHeight == 0) && (parentStyle.getRowHeight() != 0)) + resolved.setRowHeight(parentStyle.getRowHeight()); + } + return resolved; + } + + + /** + * Create a new <code>Node</code> in the <code>Document</code>, and + * write this <code>Style</code> to it. + * + * @param parentDoc Parent <code>Document</code> of the + * <code>Node</code> to create. + * @param name Name to use for the new <code>Node</code> (e.g. + * <i>style:style</i>) + * + * @return Created <code>Node</code>. + */ + public Node createNode(org.w3c.dom.Document parentDoc, String name) { + Element node = parentDoc.createElement(name); + writeAttributes(node); + return node; + } + + + /** + * Return true if <code>style</code> specifies as much or less + * than this <code>Style</code>, and nothing it specifies + * contradicts this <code>Style</code>. + * + * @param style The <code>Style</code> to check. + * + * @return true if <code>style</code> is a subset, false + * otherwise. + */ + public boolean isSubset(Style style) { + if (style.getClass() != this.getClass()) + return false; + RowStyle tStyle = (RowStyle)style; + + if(rowHeight!=tStyle.getRowHeight()) + return false; + + return true; + } + + + /** + * Write this <code>Style</code> object's attributes to a + * <code>Node</code> in the <code>Document</code>. + * + * @param node The <code>Node</code> to add <code>Style</code> + * attributes. + */ + public void writeAttributes(Element node) { + + if(rowHeight!=0) { + String height = TwipsConverter.twips2cm(rowHeight) + "cm"; + node.setAttribute("style:row-height", height); + } + } + + + private static String[] ignored = { + "fo:break-before", "fo:keep-with-next" + }; + + + /* + * This code checks whether an attribute is one that we + * intentionally ignore. + * + * @param attribute The attribute to check. + * + * @return true if <code>attribute</code> can be ignored, + * otherwise false. + */ + private boolean isIgnored(String attribute) { + for (int i = 0; i < ignored.length; i++) { + if (ignored[i].equals(attribute)) + return true; + } + return false; + } +} + diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/SheetSettings.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/SheetSettings.java new file mode 100644 index 000000000000..765da090611c --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/SheetSettings.java @@ -0,0 +1,377 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: SheetSettings.java,v $ + * $Revision: 1.5 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +package org.openoffice.xmerge.converter.xml.sxc; + +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.NodeList; +import org.w3c.dom.Node; +import org.w3c.dom.Element; +import java.awt.Point; + +import org.openoffice.xmerge.converter.xml.OfficeConstants; +import org.openoffice.xmerge.util.Debug; + +/** + * This is a class representing the different attributes for a worksheet + * contained in settings.xml. + * + * @author Martin Maher + */ +public class SheetSettings implements OfficeConstants { + + /** A w3c <code>Document</code>. */ + private org.w3c.dom.Document settings = null; + + private String sheetName; + private int cursorX = 0; + private int cursorY = 0; + private int splitTypeX; + private int splitTypeY; + private int splitPointX = 0; + private int splitPointY = 0; + private int posLeft = 0; + private int posRight = 0; + private int posBottom = 0; + private int posTop = 0; + private int paneNumber = 2; + + final public static int NONE = 0x00; + final public static int SPLIT = 0x01; + final public static int FREEZE = 0x02; + + + /** + * Default Constructor for a <code>ColumnRowInfo</code> + * + */ + public SheetSettings() { + } + + /** + * Constructor that takes a <code>Node</code> to build a <code>SheetSettings</code> + * + * @param root XML Node to read from + */ + public SheetSettings(Node root) { + readNode(root); + } + + /** + * Constructor for a <code>ColumnRowInfo</code> + * + * @param dimension if it's a row the height, a column the width + * @param repeated + */ + public SheetSettings(String name) { + sheetName = name; + } + + /** + * sets the position of the acitve cell + * + * @param activeCell the current curor position + */ + public void setCursor(Point activeCell) { + + cursorX = (int) activeCell.getX(); + cursorY = (int) activeCell.getY(); + } + + /** + * Gets the position of the acitve cell + * + * @return The position as a <code>Point</code> + */ + public Point getCursor() { + + return (new Point(cursorX, cursorY)); + } + + /** + * Sets the position of the freeze + * + * @param splitPoint the point at where the split occurs + */ + public void setFreeze(Point splitPoint) { + + splitTypeX = FREEZE; + splitTypeY = FREEZE; + splitPointX = (int) splitPoint.getX(); + splitPointY = (int) splitPoint.getY(); + } + + /** + * Sets the position of the split + * + * @param splitPoint the point at where the split occurs + */ + public void setSplit(Point splitPoint) { + + splitTypeX = SPLIT; + splitTypeY = SPLIT; + splitPointX = (int) splitPoint.getX(); + splitPointY = (int) splitPoint.getY(); + } + + /** + * sets the position and type of the split + * + * @return The position as a <code>Point</code> where the split occurs + */ + public Point getSplit() { + + return (new Point(splitPointX, splitPointY)); + } + + /** + * sets the position and type of the split + * + * @return The position as a <code>Point</code> where the split occurs + */ + public Point getSplitType() { + + return (new Point(splitTypeX, splitTypeY)); + } + + /** + * Sets the top row visible in the lower pane and the leftmost column + * visibile in the right pane. + * + * @param top The top row visible in the lower pane + * @param left The leftmost column visibile in the right pane + */ + public void setTopLeft(int top, int left) { + + posLeft = left; + posTop = top; + } + + /** + * Gets the the leftmost column visibile in the right pane. + * + * @return the 0-based index to the column + */ + public int getLeft() { + + return posLeft; + } + /** + * Sets the top row visible in the lower pane and the leftmost column + * visibile in the right pane. + * + * @param top The top row visible in the lower pane + * @param left The leftmost column visibile in the right pane + */ + public int getTop() { + + return posTop; + } + + /** + * Gets the active Panel + * 0 - Bottom Right, 1 - Top Right + * 2 - Bottom Left, 3 - Top Left + * + * @return int representing the active panel + */ + public int getPaneNumber() { + + return paneNumber; + } + + /** + * Sets the sheetname this settings object applies to + * + * @param sheetName the name of the worksheet + */ + public void setSheetName(String sheetName) { + + this.sheetName = sheetName; + + } + + /** + * Sets the active pane number + * 0 - Bottom Right, 1 - Top Right + * 2 - Bottom Left, 3 - Top Left + * + * @param paneNumber the pane number + */ + public void setPaneNumber(int paneNumber) { + + this.paneNumber = paneNumber; + } + + /** + * Gets the name of the worksheet these <code>Settings</code> apply to + * + * @return the name of the worksheet + */ + public String getSheetName() { + + return sheetName; + } + + /** + * Adds an XML entry for a particular setting + * + * @param root the root node at which to add the xml entry + * @param attriute the name of the attribute to add + * @param type the attribute type (int, short etc) + * @param value the value of the attribute + */ + private void addConfigItem(Node root, String attribute, String type, String value) { + + Element configItem = settings.createElement(TAG_CONFIG_ITEM); + configItem.setAttribute(ATTRIBUTE_CONFIG_NAME, attribute); + configItem.setAttribute(ATTRIBUTE_CONFIG_TYPE, type); + + configItem.appendChild(settings.createTextNode(value)); + + root.appendChild(configItem); + } + + /** + * Writes out a settings.xml entry for this SheetSettings object + * + * @param settings a <code>Document</code> object representing the settings.xml + * @param root the root xml node to add to + */ + public void writeNode(org.w3c.dom.Document settings, Node root) { + + this.settings = settings; + Element configItemMapEntry = (Element) settings.createElement(TAG_CONFIG_ITEM_MAP_ENTRY); + configItemMapEntry.setAttribute(ATTRIBUTE_CONFIG_NAME, getSheetName()); + addConfigItem(configItemMapEntry, "CursorPositionX", "int", Integer.toString(cursorX)); + addConfigItem(configItemMapEntry, "CursorPositionY", "int", Integer.toString(cursorY)); + + String splitMode = Integer.toString(splitTypeX); + if(splitPointX==0) { + splitMode = "0"; + } + addConfigItem(configItemMapEntry, "HorizontalSplitMode", "short", splitMode); + + splitMode = Integer.toString(splitTypeY); + if(splitPointY==0) { + splitMode = "0"; + } + addConfigItem(configItemMapEntry, "VerticalSplitMode", "short", splitMode); + + addConfigItem(configItemMapEntry, "HorizontalSplitPosition", "int", Integer.toString(splitPointX)); + addConfigItem(configItemMapEntry, "VerticalSplitPosition", "int", Integer.toString(splitPointY)); + addConfigItem(configItemMapEntry, "ActiveSplitRange", "short", Integer.toString(paneNumber)); + + addConfigItem(configItemMapEntry, "PositionLeft", "int", "0"); + addConfigItem(configItemMapEntry, "PositionRight", "int", Integer.toString(posLeft)); + addConfigItem(configItemMapEntry, "PositionTop", "int", "0"); + addConfigItem(configItemMapEntry, "PositionBottom", "int", Integer.toString(posTop)); + root.appendChild(configItemMapEntry); + } + + /** + * Sets a variable based on a String value read from XML + * + * @param name xml name of the attribute to set + * @param value String value fo the attribute + */ + public void addAttribute(String name, String value) { + + if(name.equals("CursorPositionX")) { + cursorX = Integer.parseInt(value); + } else if(name.equals("CursorPositionY")) { + cursorY = Integer.parseInt(value); + + } else if(name.equals("HorizontalSplitPosition")) { + splitPointX = Integer.parseInt(value); + } else if(name.equals("VerticalSplitPosition")) { + splitPointY = Integer.parseInt(value); + } else if(name.equals("ActiveSplitRange")) { + paneNumber = Integer.parseInt(value); + + } else if(name.equals("PositionRight")) { + posLeft = Integer.parseInt(value); + } else if(name.equals("PositionBottom")) { + posTop = Integer.parseInt(value); + + } else if(name.equals("HorizontalSplitMode")) { + splitTypeX = Integer.parseInt(value); + } else if(name.equals("VerticalSplitMode")) { + splitTypeY = Integer.parseInt(value); + } + } + + /** + * Reads document settings from xml and inits SheetSettings variables + * + * @param root XML Node to read from + */ + public void readNode(Node root) { + + NamedNodeMap sheetAtt = root.getAttributes(); + + Node sheetNameNode = sheetAtt.getNamedItem(ATTRIBUTE_CONFIG_NAME); + + sheetName = sheetNameNode.getNodeValue(); + + if (root.hasChildNodes()) { + + NodeList nodeList = root.getChildNodes(); + int len = nodeList.getLength(); + for (int i = 0; i < len; i++) { + Node child = nodeList.item(i); + + if (child.getNodeType() == Node.ELEMENT_NODE) { + String nodeName = child.getNodeName(); + + if (nodeName.equals(TAG_CONFIG_ITEM)) { + + NamedNodeMap cellAtt = child.getAttributes(); + + Node configNameNode = + cellAtt.getNamedItem(ATTRIBUTE_CONFIG_NAME); + + String name = configNameNode.getNodeValue(); + NodeList nodeList2 = child.getChildNodes(); + int len2 = nodeList2.getLength(); + String s = ""; + for (int j = 0; j < len2; j++) { + Node child2 = nodeList2.item(j); + if (child2.getNodeType() == Node.TEXT_NODE) { + s = child2.getNodeValue(); + } + } + addAttribute(name, s); + } + } + } + } + } +} diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/SpreadsheetDecoder.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/SpreadsheetDecoder.java new file mode 100644 index 000000000000..5a6408ba6694 --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/SpreadsheetDecoder.java @@ -0,0 +1,184 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: SpreadsheetDecoder.java,v $ + * $Revision: 1.7 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +package org.openoffice.xmerge.converter.xml.sxc; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Enumeration; + +import org.openoffice.xmerge.converter.xml.sxc.Format; +import org.openoffice.xmerge.ConvertData; + +/** + * This class is a abstract class for encoding a "Device" + * <code>Document</code> format into an alternative spreadsheet format. + * + * @author Mark Murnane + */ +public abstract class SpreadsheetDecoder { + + /** + * Constructor for creating new <code>SpreadsheetDecoder</code>. + */ + public SpreadsheetDecoder(String name, String password) throws IOException { + } + + /** + * Returns the total number of sheets in the WorkBook. + * + * @return The number of sheets in the WorkBook. + */ + public abstract int getNumberOfSheets(); + + /** + * Returns an Enumeration to a Vector of <code>NameDefinition</code>. + * + * @return The Enumeration + */ + public abstract Enumeration getNameDefinitions(); + + /** + * Returns an <code>BookSettings</code> + * + * @return The Enumeration + */ + public abstract BookSettings getSettings(); + + /** + * Returns an Enumeration to a Vector of <code>ColumnRowInfo</code>. + * + * @return The Enumeration + */ + public abstract Enumeration getColumnRowInfos(); + + /** + * Returns the number of populated rows in the current WorkSheet. + * + * @return the number of populated rows in the current WorkSheet. + */ + public abstract int getNumberOfRows(); + + + /** + * Returns the number of populated columns in the current WorkSheet. + * + * @return The number of populated columns in the current WorkSheet. + */ + public abstract int getNumberOfColumns(); + + + /** + * Returns the name of the current WorkSheet. + * + * @return Name of the current WorkSheet. + */ + public abstract String getSheetName(); + + + /** + * Returns the number of the active column. + * + * @return The number of the active column. + */ + public abstract int getColNumber(); + + + /** + * Returns the number of the active row. + * + * @return The number of the active row. + */ + public abstract int getRowNumber(); + + + /** + * Sets the active WorkSheet. + * + * @param sheetIndex The index of the sheet to be made active. + * + * @throws IOException If any I/O error occurs. + */ + public abstract void setWorksheet(int sheetIndex) throws IOException; + + + /** + * Move on the next populated cell in the current WorkSheet. + * + * @return true if successful, false otherwise. + * + * @throws IOException If any I/O error occurs. + */ + public abstract boolean goToNextCell() throws IOException; + + + /** + * Return the contents of the active cell. + * + * @return The cell contents. + */ + public abstract String getCellContents(); + + /** + * Return the value of the active cell. Used in the case of Formula where + * the cell contents and the cell value are not the same thing. + * + * @return The cell value. + */ + public abstract String getCellValue(); + + /** + * Return the data type of the active cell. + * + * @return The cell data type. + */ + public abstract String getCellDataType(); + + + /** + * Return a <code>Format</code> object describing the active cells + * formatting. + * + * @return <code>Format</code> object for the cell. + */ + public abstract Format getCellFormat(); + + + /** + * Add the contents of a <code>ConvertData</code> to the workbook. + * + * @param cd The <code>ConvertData</code> containing the + * content. + * + * @throws IOException If any I/O error occurs. + */ + public abstract void addDeviceContent(ConvertData cd) throws IOException; +} + diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/SpreadsheetEncoder.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/SpreadsheetEncoder.java new file mode 100644 index 000000000000..81e6914b1bfe --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/SpreadsheetEncoder.java @@ -0,0 +1,134 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: SpreadsheetEncoder.java,v $ + * $Revision: 1.6 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +package org.openoffice.xmerge.converter.xml.sxc; + +import java.io.IOException; +import java.util.Vector; + +import org.openoffice.xmerge.util.IntArrayList; + +/** + * <p>This class is a abstract class for encoding an SXC into an + * alternative spreadsheet format.</p> + * + * <p>TODO - Add appropriate exceptions to each of the methods.</p> + * + * @author Mark Murnane + */ +public abstract class SpreadsheetEncoder { + + + /** + * Creates new SpreadsheetEncoder. + * + * @param name The name of the WorkBook to be created. + * @param password An optional password for the WorkBook. + * + * @throws IOException If any I/O error occurs. + */ + public SpreadsheetEncoder(String name, String password) throws IOException { }; + + + /** + * Create a new WorkSheet within the WorkBook. + * + * @param sheetName The name of the WorkSheet. + * + * @throws IOException If any I/O error occurs. + */ + public abstract void createWorksheet(String sheetName) throws IOException; + + + /** + * Set a cell's formatting options via a separately create + * <code>Format</code> object. + * + * @param row The row number of the cell to be changed + * @param column The column number of the cell to be changed + * @param fmt Object containing formatting settings for this cell. + */ + public abstract void setCellFormat(int row, int column, Format fmt); + + + /** + * Add a cell to the current WorkSheet. + * + * @param row The row number of the cell + * @param column The column number of the cell + * @param fmt The <code>Format</code> object describing the + * appearance of this cell. + * @param cellContents The text or formula of the cell's contents. + */ + public abstract void addCell(int row, int column, + Format fmt, String cellContents) throws IOException; + + + /** + * Get the number of sheets in the WorkBook. + * + * @return The number of sheets in the WorkBook. + */ + public abstract int getNumberOfSheets(); + + + /** + * Get the names of the sheets in the WorkBook. + * + * @param sheet The required sheet. + */ + public abstract String getSheetName(int sheet); + + + /** + * Set the width of the columns in the WorkBook. + * + * @param columnWidths An <code>IntArrayList</code> of column + * widths. + */ + public abstract void setColumnRows(Vector columnRows) throws IOException; + + /** + * Set the width of the columns in the WorkBook. + * + * @param columnWidths An <code>IntArrayList</code> of column + * widths. + */ + public abstract void setNameDefinition(NameDefinition nd) throws IOException; + + /** + * Set the width of the columns in the WorkBook. + * + * @param columnWidths An <code>IntArrayList</code> of column + * widths. + */ + public abstract void addSettings(BookSettings s) throws IOException; +} + diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/SxcConstants.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/SxcConstants.java new file mode 100644 index 000000000000..793fe47bfc23 --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/SxcConstants.java @@ -0,0 +1,53 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: SxcConstants.java,v $ + * $Revision: 1.4 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + + +package org.openoffice.xmerge.converter.xml.sxc; + + +/** + * Interface defining constants for Sxc attributes. + * + * @author Martin Maher + */ +public interface SxcConstants { + + /** Family name for column styles. */ + public static final String COLUMN_STYLE_FAMILY = "table-column"; + + /** Family name for row styles. */ + public static final String ROW_STYLE_FAMILY = "table-row"; + + /** Family name for table-cell styles. */ + public static final String TABLE_CELL_STYLE_FAMILY = "table-cell"; + + /** Name of the default style. */ + public static final String DEFAULT_STYLE = "Default"; +} diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/SxcDocument.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/SxcDocument.java new file mode 100644 index 000000000000..8a76b4260fc5 --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/SxcDocument.java @@ -0,0 +1,96 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: SxcDocument.java,v $ + * $Revision: 1.4 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +package org.openoffice.xmerge.converter.xml.sxc; + +import org.w3c.dom.Document; +import org.openoffice.xmerge.converter.xml.OfficeDocument; +import org.openoffice.xmerge.converter.xml.OfficeConstants; + +/** + * This class is an implementation of <code>OfficeDocument</code> for + * the SXC format. + */ +public class SxcDocument extends OfficeDocument { + + /** + * Constructor with arguments to set <code>name</code>. + * + * @param name The name of the <code>Document</code> + */ + public SxcDocument(String name) { + super(name); + } + + + /** + * Constructor with arguments to set <code>name</code>, the + * <code>namespaceAware</code> flag, and the <code>validating</code> + * flag. + * + * @param name The name of the <code>Document</code>. + * @param namespaceAware The value of the <code>namespaceAware</code> + * flag. + * @param validating The value of the <code>validating</code> flag. + */ + public SxcDocument(String name, boolean namespaceAware, boolean validating) { + + super(name, namespaceAware, validating); + } + + /** + * Returns the Office file extension for the SXC format. + * + * @return The Office file extension for the SXC format. + */ + protected String getFileExtension() { + return OfficeConstants.SXC_FILE_EXTENSION; + } + + /** + * Returns the Office attribute for the SXC format. + * + * @return The Office attribute for the SXC format. + */ + protected String getOfficeClassAttribute() { + return OfficeConstants.SXC_TYPE; + } + + /** + * Method to return the MIME type of the document. + * + * @return String The document's MIME type. + */ + protected final String getDocumentMimeType() { + return OfficeConstants.SXC_MIME_TYPE; + } + +} + diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/SxcDocumentDeserializer.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/SxcDocumentDeserializer.java new file mode 100644 index 000000000000..01e9987172b4 --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/SxcDocumentDeserializer.java @@ -0,0 +1,797 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: SxcDocumentDeserializer.java,v $ + * $Revision: 1.14 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +package org.openoffice.xmerge.converter.xml.sxc; + +import org.w3c.dom.NodeList; +import org.w3c.dom.Node; +import org.w3c.dom.Element; + +import java.awt.Point; +import java.io.IOException; +import java.util.Enumeration; + +import org.openoffice.xmerge.Document; +import org.openoffice.xmerge.ConvertData; +import org.openoffice.xmerge.ConvertException; +import org.openoffice.xmerge.DocumentDeserializer; +import org.openoffice.xmerge.converter.xml.OfficeConstants; +import org.openoffice.xmerge.converter.xml.sxc.SxcDocument; +import org.openoffice.xmerge.converter.xml.sxc.BookSettings; +import org.openoffice.xmerge.converter.xml.sxc.SheetSettings; +import org.openoffice.xmerge.converter.xml.sxc.NameDefinition; +import org.openoffice.xmerge.converter.xml.sxc.CellStyle; +import org.openoffice.xmerge.converter.xml.Style; +import org.openoffice.xmerge.converter.xml.StyleCatalog; +import org.openoffice.xmerge.util.Debug; + +/** + * <p>General spreadsheet implementation of <code>DocumentDeserializer</code> + * for the {@link + * org.openoffice.xmerge.converter.xml.sxc.SxcPluginFactory + * SxcPluginFactory}. Used with SXC <code>Document</code> objects.</p> + * + * <p>The <code>deserialize</code> method uses a <code>DocDecoder</code> + * to read the device spreadsheet format into a <code>String</code> + * object, then it calls <code>buildDocument</code> to create a + * <code>SxcDocument</code> object from it.</p> + * + * @author Paul Rank + * @author Mark Murnane + * @author Martin Maher + */ +public abstract class SxcDocumentDeserializer implements OfficeConstants, + DocumentDeserializer { + + /** + * A <code>SpreadsheetDecoder</code> object for decoding from + * device formats. + */ + private SpreadsheetDecoder decoder = null; + + /** A w3c <code>Document</code>. */ + private org.w3c.dom.Document settings = null; + + /** A w3c <code>Document</code>. */ + private org.w3c.dom.Document doc = null; + + /** An <code>ConvertData</code> object assigned to this object. */ + private ConvertData cd = null; + + /** A style catalog for the workbook */ + private StyleCatalog styleCat = null; + + private int textStyles = 1; + private int colStyles = 1; + private int rowStyles = 1; + + /** + * Constructor. + * + * @param cd <code>ConvertData</code> consisting of a + * device content object. + */ + public SxcDocumentDeserializer(ConvertData cd) { + this.cd = cd; + } + + + /** + * This abstract method will be implemented by concrete subclasses + * and will return an application-specific Decoder. + * + * @param workbook The WorkBook to read. + * @param password The WorkBook password. + * + * @return The appropriate <code>SpreadSheetDecoder</code>. + * + * @throws IOException If any I/O error occurs. + */ + public abstract SpreadsheetDecoder createDecoder(String workbook, String[] worksheetNames, String password) + throws IOException; + + + /** + * <p>This method will return the name of the WorkBook from the + * <code>ConvertData</code>. Allows for situations where the + * WorkBook name differs from the Device Content name.</p> + * + * <p>Implemented in the Deserializer as the Decoder's constructor requires + * a name.</p> + * + * @param cd The <code>ConvertData</code> containing the Device + * content. + * + * @return The WorkBook name. + */ + protected abstract String getWorkbookName(ConvertData cd) throws IOException; + + + /** + * This method will return the name of the WorkSheet from the + * <code>ConvertData</code>. + * + * @param cd The <code>ConvertData</code> containing the Device + * content. + * + * @return The WorkSheet names. + */ + protected abstract String[] getWorksheetNames(ConvertData cd) throws IOException; + + + /** + * <p>Method to convert a set of "Device" + * <code>Document</code> objects into a <code>SxcDocument</code> + * object and returns it as a <code>Document</code>.</p> + * + * <p>This method is not thread safe for performance reasons. + * This method should not be called from within two threads. + * It would be best to call this method only once per object + * instance.</p> + * + * @return document An <code>SxcDocument</code> consisting + * of the data converted from the input + * stream. + * + * @throws ConvertException If any conversion error occurs. + * @throws IOException If any I/O error occurs. + */ + public Document deserialize() throws ConvertException, + IOException { + + // Get the name of the WorkBook from the ConvertData. + String[] worksheetNames = getWorksheetNames(cd); + String workbookName = getWorkbookName(cd); + + // Create a document + SxcDocument sxcDoc = new SxcDocument(workbookName); + sxcDoc.initContentDOM(); + sxcDoc.initSettingsDOM(); + + // Default to an initial 5 entries in the catalog. + styleCat = new StyleCatalog(5); + + doc = sxcDoc.getContentDOM(); + settings = sxcDoc.getSettingsDOM(); + initFontTable(); + // Little fact for the curious reader: workbookName should + // be the name of the StarCalc file minus the file extension suffix. + + // Create a Decoder to decode the DeviceContent to a spreadsheet document + // TODO - we aren't using a password in StarCalc, so we can + // use any value for password here. If StarCalc XML supports + // passwords in the future, we should try to get the correct + // password value here. + // + decoder = createDecoder(workbookName, worksheetNames, "password"); + + Debug.log(Debug.TRACE, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); + Debug.log(Debug.TRACE, "<DEBUGLOG>"); + + decoder.addDeviceContent(cd); + decode(); + + Debug.log(Debug.TRACE, "</DEBUGLOG>"); + + return sxcDoc; + } + + /** + * This initializes a font table so we can imclude some basic font + * support for spreadsheets. + * + */ + private void initFontTable() { + + String fontTable[]= new String[] { "Tahoma", "Tahoma", "swiss", "variable", + "Courier New", "'Courier New'", "modern", "fixed"}; + // Traverse to the office:body element. + // There should only be one. + NodeList list = doc.getElementsByTagName(TAG_OFFICE_FONT_DECLS); + Node root = list.item(0); + + for(int i=0;i<fontTable.length;) { + + // Create an element node for the table + Element tableElement = (Element) doc.createElement(TAG_STYLE_FONT_DECL); + + tableElement.setAttribute(ATTRIBUTE_STYLE_NAME, fontTable[i++]); + tableElement.setAttribute(ATTRIBUTE_FO_FONT_FAMILY, fontTable[i++]); + tableElement.setAttribute(ATTRIBUTE_FO_FONT_FAMILY_GENERIC, fontTable[i++]); + tableElement.setAttribute(ATTRIBUTE_STYLE_FONT_PITCH, fontTable[i++]); + + root.appendChild(tableElement); + } + + } + + /** + * Outer level method used to decode a WorkBook + * into a <code>Document</code>. + * + * @throws IOException If any I/O error occurs. + */ + protected void decode() throws IOException { + + // Get number of worksheets + int numSheets = decoder.getNumberOfSheets(); + // #i33702# - check for an Empty InputStream. + if(numSheets == 0) + { + System.err.println("Error decoding invalid Input stream"); + return; + } + + // Traverse to the office:body element. + // There should only be one. + NodeList list = doc.getElementsByTagName(TAG_OFFICE_BODY); + Node node = list.item(0); + + for (int i = 0; i < numSheets; i++) { + + // Set the decoder to the correct worksheet + decoder.setWorksheet(i); + + int len = list.getLength(); + + if (len > 0) { + + // Process the spreadsheet + processTable(node); + } + } + + // Add the Defined Name table if there is one + Enumeration nameDefinitionTable = decoder.getNameDefinitions(); + if(nameDefinitionTable.hasMoreElements()) { + processNameDefinition(node, nameDefinitionTable); + } + + // add settings + NodeList settingsList = settings.getElementsByTagName(TAG_OFFICE_SETTINGS); + Node settingsNode = settingsList.item(0);; + processSettings(settingsNode); + + } + + + + /** + * This method process the settings portion + * of the <code>Document</code>. + * + * @param root The root <code>Node</code> of the + * <code>Document</code> we are building. This + * <code>Node</code> should be a TAG_OFFICE_SETTINGS + * tag. + */ + protected void processSettings(Node root) { + + Element configItemSetEntry = (Element) settings.createElement(TAG_CONFIG_ITEM_SET); + configItemSetEntry.setAttribute(ATTRIBUTE_CONFIG_NAME, "view-settings"); + Element configItemMapIndexed = (Element) settings.createElement(TAG_CONFIG_ITEM_MAP_INDEXED); + configItemMapIndexed.setAttribute(ATTRIBUTE_CONFIG_NAME, "Views"); + Element configItemMapEntry = (Element) settings.createElement(TAG_CONFIG_ITEM_MAP_ENTRY); + BookSettings bs = (BookSettings) decoder.getSettings(); + bs.writeNode(settings, configItemMapEntry); + + configItemMapIndexed.appendChild(configItemMapEntry); + configItemSetEntry.appendChild(configItemMapIndexed); + root.appendChild(configItemSetEntry); + } + + /** + * This method process a Name Definition Table and generates a portion + * of the <code>Document</code>. + * + * @param root The root <code>Node</code> of the + * <code>Document</code> we are building. This + * <code>Node</code> should be a TAG_OFFICE_BODY + * tag. + * + * @throws IOException If any I/O error occurs. + */ + protected void processNameDefinition(Node root, Enumeration eNameDefinitions) throws IOException { + + Debug.log(Debug.TRACE, "<NAMED-EXPRESSIONS>"); + + Element namedExpressionsElement = (Element) doc.createElement(TAG_NAMED_EXPRESSIONS); + + while(eNameDefinitions.hasMoreElements()) { + + NameDefinition tableEntry = (NameDefinition) eNameDefinitions.nextElement(); + tableEntry.writeNode(doc, namedExpressionsElement); + } + + root.appendChild(namedExpressionsElement); + + Debug.log(Debug.TRACE, "</NAMED-EXPRESSIONS>"); + } + + /** + * This method process a WorkSheet and generates a portion + * of the <code>Document</code>. A spreadsheet is represented + * as a table Node in StarOffice XML format. + * + * @param root The root <code>Node</code> of the + * <code>Document</code> we are building. This + * <code>Node</code> should be a TAG_OFFICE_BODY + * tag. + * + * @throws IOException If any I/O error occurs. + */ + protected void processTable(Node root) throws IOException { + + Debug.log(Debug.TRACE, "<TABLE>"); + + // Create an element node for the table + Element tableElement = (Element) doc.createElement(TAG_TABLE); + + // Get the sheet name + String sheetName = decoder.getSheetName(); + + // Set the table name attribute + tableElement.setAttribute(ATTRIBUTE_TABLE_NAME, sheetName); + + // TODO - style currently hardcoded - get real value + // Set table style-name attribute + tableElement.setAttribute(ATTRIBUTE_TABLE_STYLE_NAME, "Default"); + + // Append the table element to the root node + root.appendChild(tableElement); + + Debug.log(Debug.TRACE, "<SheetName>" + sheetName + "</SheetName>"); + + // add the various different table-columns + processColumns(tableElement); + + // Get each cell and add to doc + processCells(tableElement); + + Debug.log(Debug.TRACE, "</TABLE>"); + } + + /** + * <p>This method process the cells in a <code>Document</code> + * and generates a portion of the <code>Document</code>.</p> + * + * <p>This method assumes that records are sorted by + * row and then column.</p> + * + * @param root The <code>Node</code> of the <code>Document</code> + * we are building that we will append our cell + * <code>Node</code> objects. This <code>Node</code> + * should be a TAG_TABLE tag. + * + * @throws IOException If any I/O error occurs. + */ + protected void processColumns(Node root) throws IOException { + + for(Enumeration e = decoder.getColumnRowInfos();e.hasMoreElements();) { + + ColumnRowInfo ci = (ColumnRowInfo) e.nextElement(); + if(ci.isColumn()) { + ColumnStyle cStyle = new ColumnStyle("Default",SxcConstants.COLUMN_STYLE_FAMILY, + SxcConstants.DEFAULT_STYLE, ci.getSize(), null); + + Style result[] = (Style[]) styleCat.getMatching(cStyle); + String styleName; + if(result.length==0) { + + cStyle.setName("co" + colStyles++); + styleName = cStyle.getName(); + Debug.log(Debug.TRACE,"No existing style found, adding " + styleName); + styleCat.add(cStyle); + } else { + ColumnStyle existingStyle = (ColumnStyle) result[0]; + styleName = existingStyle.getName(); + Debug.log(Debug.TRACE,"Existing style found : " + styleName); + } + + // Create an element node for the new row + Element colElement = (Element) doc.createElement(TAG_TABLE_COLUMN); + colElement.setAttribute(ATTRIBUTE_TABLE_STYLE_NAME, styleName); + if(ci.getRepeated()!=1) { + String repeatStr = String.valueOf(ci.getRepeated()); + colElement.setAttribute(ATTRIBUTE_TABLE_NUM_COLUMNS_REPEATED, repeatStr); + } + root.appendChild(colElement); + } + } + } + + /** + * <p>This method process the cells in a <code>Document</code> + * and generates a portion of the <code>Document</code>.</p> + * + * <p>This method assumes that records are sorted by + * row and then column.</p> + * + * @param root The <code>Node</code> of the <code>Document</code> + * we are building that we will append our cell + * <code>Node</code> objects. This <code>Node</code> + * should be a TAG_TABLE tag. + * + * @throws IOException If any I/O error occurs. + */ + protected void processCells(Node root) throws IOException { + + // The current row element + Element rowElement = null; + + // The current cell element + Element cellElement = null; + + // The row number - we may not have any rows (empty sheet) + // so set to zero. + int row = 0; + + // The column number - This is the expected column number of + // the next cell we are reading. + int col = 1; + + // The number of columns in the spreadsheet + int lastColumn = decoder.getNumberOfColumns(); + + // + Node autoStylesNode = null; + + // Loop over all cells in the spreadsheet + while (decoder.goToNextCell()) { + + // Get the row number + int newRow = decoder.getRowNumber(); + + // Is the cell in a new row, or part of the current row? + if (newRow != row) { + + // Make sure that all the cells in the previous row + // have been entered. + if (col <= lastColumn && rowElement != null) { + int numSkippedCells = lastColumn - col + 1; + addEmptyCells(numSkippedCells, rowElement); + } + + // log an end row - if we already have a row + if (row != 0) { + Debug.log(Debug.TRACE, "</tr>"); + } + + // How far is the new row from the last row? + int deltaRows = newRow - row; + + // Check if we have skipped any rows + if (deltaRows > 1) { + // Add in empty rows + addEmptyRows(deltaRows-1, root, lastColumn); + } + + // Re-initialize column (since we are in a new row) + col = 1; + + // Create an element node for the new row + rowElement = (Element) doc.createElement(TAG_TABLE_ROW); + + + for(Enumeration e = decoder.getColumnRowInfos();e.hasMoreElements();) { + ColumnRowInfo cri = (ColumnRowInfo) e.nextElement(); + if(cri.isRow() && cri.getRepeated()==newRow-1) { + // We have the correct Row BIFFRecord for this row + RowStyle rStyle = new RowStyle("Default",SxcConstants.ROW_STYLE_FAMILY, + SxcConstants.DEFAULT_STYLE, cri.getSize(), null); + + Style result[] = (Style[]) styleCat.getMatching(rStyle); + String styleName; + if(result.length==0) { + + rStyle.setName("ro" + rowStyles++); + styleName = rStyle.getName(); + Debug.log(Debug.TRACE,"No existing style found, adding " + styleName); + styleCat.add(rStyle); + } else { + RowStyle existingStyle = (RowStyle) result[0]; + styleName = existingStyle.getName(); + Debug.log(Debug.TRACE,"Existing style found : " + styleName); + } + rowElement.setAttribute(ATTRIBUTE_TABLE_STYLE_NAME, styleName); + // For now we will not use the repeat column attribute + } + } + + // Append the row element to the root node + root.appendChild(rowElement); + + // Update row number + row = newRow; + + Debug.log(Debug.TRACE, "<tr>"); + } + + // Get the column number of the current cell + int newCol = decoder.getColNumber(); + + // Check to see if some columns were skipped + if (newCol != col) { + + // How many columns have we skipped? + int numColsSkipped = newCol - col; + + addEmptyCells(numColsSkipped, rowElement); + + // Update the column number to account for the + // skipped cells + col = newCol; + } + + // Lets start dealing with the cell data + Debug.log(Debug.TRACE, "<td>"); + + // Get the cell's contents + String cellContents = decoder.getCellContents(); + + // Get the type of the data in the cell + String cellType = decoder.getCellDataType(); + + // Get the cell format + Format fmt = decoder.getCellFormat(); + + // Create an element node for the cell + cellElement = (Element) doc.createElement(TAG_TABLE_CELL); + + Node bodyNode = doc.getElementsByTagName(TAG_OFFICE_BODY).item(0); + + // Not every document has an automatic style tag + autoStylesNode = doc.getElementsByTagName( + TAG_OFFICE_AUTOMATIC_STYLES).item(0); + + if (autoStylesNode == null) { + autoStylesNode = doc.createElement(TAG_OFFICE_AUTOMATIC_STYLES); + doc.insertBefore(autoStylesNode, bodyNode); + } + + CellStyle tStyle = new + CellStyle( "Default",SxcConstants.TABLE_CELL_STYLE_FAMILY, + SxcConstants.DEFAULT_STYLE, fmt, null); + String styleName; + Style result[] = (Style[]) styleCat.getMatching(tStyle); + if(result.length==0) { + + tStyle.setName("ce" + textStyles++); + styleName = tStyle.getName(); + Debug.log(Debug.TRACE,"No existing style found, adding " + styleName); + styleCat.add(tStyle); + } else { + CellStyle existingStyle = (CellStyle) result[0]; + styleName = existingStyle.getName(); + Debug.log(Debug.TRACE,"Existing style found : " + styleName); + } + + cellElement.setAttribute(ATTRIBUTE_TABLE_STYLE_NAME, styleName); + + // Store the cell data into the appropriate attributes + processCellData(cellElement, cellType, cellContents); + + // Append the cell element to the row node + rowElement.appendChild(cellElement); + + // Append the cellContents as a text node + Element textElement = (Element) doc.createElement(TAG_PARAGRAPH); + cellElement.appendChild(textElement); + textElement.appendChild(doc.createTextNode(cellContents)); + + Debug.log(Debug.TRACE, cellContents); + Debug.log(Debug.TRACE, "</td>"); + + // Increment to the column number of the next expected cell + col++; + } + + // Make sure that the last row is padded correctly + if (col <= lastColumn && rowElement != null) { + int numSkippedCells = lastColumn - col + 1; + addEmptyCells(numSkippedCells, rowElement); + } + + // Now write the style catalog to the document + if(autoStylesNode!=null) { + Debug.log(Debug.TRACE,"Well the autostyle node was found!!!"); + NodeList nl = styleCat.writeNode(doc, "dummy").getChildNodes(); + int nlLen = nl.getLength(); // nl.item reduces the length + for (int i = 0; i < nlLen; i++) { + autoStylesNode.appendChild(nl.item(0)); + } + } + + if (row != 0) { + + // The sheet does have rows, so write out a /tr + Debug.log(Debug.TRACE, "</tr>"); + } + } + + + /** + * This method will add empty rows to the <code>Document</code>. + * It is called when the conversion process encounters + * a row (or rows) that do not contain any data in its cells. + * + * @param numEmptyRows The number of empty rows that we + * need to add to the <code>Document</code>. + * @param root The <code>Node</code> of the + * <code>Document</code> we are building + * that we will append our empty row + * <code>Node</code> objects. This + * <code>Node</code> should be a TAG_TABLE + * tag. + * @param numEmptyCells The number of empty cells in the + * empty row. + */ + protected void addEmptyRows(int numEmptyRows, Node root, int numEmptyCells) { + + // Create an element node for the row + Element rowElement = (Element) doc.createElement(TAG_TABLE_ROW); + + // TODO - style currently hardcoded - get real value + // Set row style-name attribute + rowElement.setAttribute(ATTRIBUTE_TABLE_STYLE_NAME, "Default"); + + // Set cell number-rows-repeated attribute + rowElement.setAttribute(ATTRIBUTE_TABLE_NUM_ROWS_REPEATED, + Integer.toString(numEmptyRows)); + + // Append the row element to the root node + root.appendChild(rowElement); + + // Open Office requires the empty row to have an empty cell (or cells) + addEmptyCells(numEmptyCells, rowElement); + + // Write empty rows to the log + for (int i = 0; i < numEmptyRows; i++) { + Debug.log(Debug.TRACE, "<tr />"); + } + + } + + + /** + * This method will add empty cells to the <code>Document</code>. + * It is called when the conversion process encounters a row + * that contains some cells without data. + * + * @param numColsSkipped The number of empty cells + * that we need to add to the + * current row. + * @param row The <code>Node</code> of the + * <code>Document</code> we + * are building that we will + * append our empty cell + * <code>Node</code> objects. + * This <code>Node</code> should + * be a TAG_TABLE_ROW tag. + */ + protected void addEmptyCells(int numColsSkipped, Node row) { + + // Create an empty cellElement + Element cellElement = (Element) doc.createElement(TAG_TABLE_CELL); + + // TODO - style currently hardcoded - get real value + // Set cell style-name attribute + cellElement.setAttribute(ATTRIBUTE_TABLE_STYLE_NAME, "Default"); + + // If we skipped more than 1 cell, we must set the + // appropriate attribute + if (numColsSkipped > 1) { + + // Set cell number-columns-repeated attribute + cellElement.setAttribute(ATTRIBUTE_TABLE_NUM_COLUMNS_REPEATED, + Integer.toString(numColsSkipped)); + } + + // Append the empty cell element to the row node + row.appendChild(cellElement); + + // Write empty cells to the log + for (int i = 0; i < numColsSkipped; i++) { + Debug.log(Debug.TRACE, "<td />"); + } + } + + + /** + * This method process the data in a cell and sets + * the appropriate attributes on the cell <code>Element</code>. + * + * @param cellElement A TAG_TABLE_CELL <code>Element</code> + * that we will be adding attributes to + * based on the type of data in the cell. + * @param type The type of data contained in the cell. + * @param contents The contents of the data contained in + * the cell. + */ + protected void processCellData(Element cellElement, String type, + String contents) { + + // Set cell value-type attribute + cellElement.setAttribute(ATTRIBUTE_TABLE_VALUE_TYPE, type); + + // Does the cell contain a formula? + if (contents.startsWith("=")) { + + cellElement.setAttribute(ATTRIBUTE_TABLE_FORMULA, contents); + + cellElement.setAttribute(ATTRIBUTE_TABLE_VALUE, decoder.getCellValue()); + // String data does not require any additional attributes + } else if (!type.equals(CELLTYPE_STRING)) { + + if (type.equals(CELLTYPE_TIME)) { + + // Data returned in StarOffice XML format, so store in + // attribute + cellElement.setAttribute(ATTRIBUTE_TABLE_TIME_VALUE, + contents); + + } else if (type.equals(CELLTYPE_DATE)) { + + // Data returned in StarOffice XML format, so store in + // attribute + cellElement.setAttribute(ATTRIBUTE_TABLE_DATE_VALUE, + contents); + + } else if (type.equals(CELLTYPE_BOOLEAN)) { + + // StarOffice XML format requires stored boolean value + // to be in lower case + cellElement.setAttribute(ATTRIBUTE_TABLE_BOOLEAN_VALUE, + contents.toLowerCase()); + + } else if (type.equals(CELLTYPE_CURRENCY)) { + // TODO - StarOffice XML format requires a correct style to + // display currencies correctly. Need to implement styles. + // TODO - USD is for US currencies. Need to pick up + // the correct currency location from the source file. + cellElement.setAttribute(ATTRIBUTE_TABLE_CURRENCY, "USD"); + + // Data comes stripped of currency symbols + cellElement.setAttribute(ATTRIBUTE_TABLE_VALUE, contents); + + } else if (type.equals(CELLTYPE_PERCENT)) { + // Data comes stripped of percent signs + cellElement.setAttribute(ATTRIBUTE_TABLE_VALUE, contents); + + } else { + // Remaining data types use table-value attribute + + cellElement.setAttribute(ATTRIBUTE_TABLE_VALUE, contents); + } + } + } + +} + diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/SxcDocumentSerializer.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/SxcDocumentSerializer.java new file mode 100644 index 000000000000..bf0516c5345c --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/SxcDocumentSerializer.java @@ -0,0 +1,995 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: SxcDocumentSerializer.java,v $ + * $Revision: 1.22 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +package org.openoffice.xmerge.converter.xml.sxc; + +import java.awt.Color; + +import org.w3c.dom.NodeList; +import org.w3c.dom.Node; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Element; + +import java.io.IOException; +import java.util.Vector; +import java.util.Enumeration; + +import org.openoffice.xmerge.Document; +import org.openoffice.xmerge.ConvertData; +import org.openoffice.xmerge.ConvertException; +import org.openoffice.xmerge.DocumentSerializer; + +import org.openoffice.xmerge.converter.xml.OfficeConstants; +import org.openoffice.xmerge.converter.xml.sxc.SxcDocument; +import org.openoffice.xmerge.converter.xml.ParaStyle; +import org.openoffice.xmerge.converter.xml.sxc.CellStyle; +import org.openoffice.xmerge.converter.xml.StyleCatalog; + +import org.openoffice.xmerge.util.Debug; +import org.openoffice.xmerge.util.XmlUtil; + +/** + * <p>General spreadsheet implementation of <code>DocumentSerializer</code> + * for the {@link + * org.openoffice.xmerge.converter.xml.sxc.SxcPluginFactory + * SxcPluginFactory}. Used with SXC <code>Document</code> objects.</p> + * + * <p>The <code>serialize</code> method traverses the DOM + * <code>Document</code> from the given <code>Document</code> object. + * It uses a <code>DocEncoder</code> object for the actual conversion + * of contents to the device spreadsheet format.</p> + * + * @author Paul Rank + * @author Mark Murnane + */ +public abstract class SxcDocumentSerializer implements OfficeConstants, + DocumentSerializer { + + /** The cell foreground <code>Color</code>. */ + private Color foreground = Color.black; + + /** The cell background <code>Color</code>. */ + private Color background = Color.white; + + /** The cell format. */ + private long format = 0; + + /** <code>Format</code> object describing the cell. */ + private Format fmt = null; + + /** The row number. */ + private int rowID = 1; + + /** The column number. */ + private int colID = 1; + + /** The number of times the current row is repeated. */ + private int rowsRepeated = 1; + + /** The number of times the current column is repeated. */ + private int colsRepeated = 1; + + /** The number of times the current column is repeated. */ + private StyleCatalog styleCat = null; + /** + * An array of column widths of the current worksheet. Width is + * measured in number of characters. + */ + private Vector ColumnRowList; + + /** Width, in characters, of the current cell display data. */ + private int displayWidth = 0; + + /** + * A <code>SpreadsheetEncoder</code> object for encoding to + * appropriate format. + */ + protected SpreadsheetEncoder encoder = null; + + /** <code>SxcDocument</code> object that this converter processes. */ + protected SxcDocument sxcDoc = null; + + + /** + * Constructor. + * + * @param document Input <code>SxcDocument</code> + * <code>Document</code>. + */ + public SxcDocumentSerializer(Document document) { + fmt = new Format(); + sxcDoc = (SxcDocument) document; + } + + + /** + * <p>Method to convert a DOM <code>Document</code> into + * "Device" <code>Document</code> objects.</p> + * + * <p>This method is not thread safe for performance reasons. + * This method should not be called from within two threads. + * It would be best to call this method only once per object + * instance.</p> + * + * @return <code>ConvertData</code> containing "Device" + * <code>Document</code> objects. + * + * @throws ConvertException If any conversion error occurs. + * @throws IOException If any I/O error occurs. + */ + public abstract ConvertData serialize() throws ConvertException, + IOException; + + + /** + * This method traverses <i>office:settings</i> <code>Element</code>. + * + * @param node <i>office:settings</i> <code>Node</code>. + * + * @throws IOException If any I/O error occurs. + */ + public void traverseSettings(Node node) throws IOException { + if (node.hasChildNodes()) { + + NodeList nodeList = node.getChildNodes(); + int len = nodeList.getLength(); + for (int i = 0; i < len; i++) { + Node child = nodeList.item(i); + + if (child.getNodeType() == Node.ELEMENT_NODE) { + String nodeName = child.getNodeName(); + + if (nodeName.equals(TAG_CONFIG_ITEM_SET)) { + + traverseSettings(child); + + } else if (nodeName.equals(TAG_CONFIG_ITEM_MAP_INDEXED)) { + + traverseSettings(child); + + } else if (nodeName.equals(TAG_CONFIG_ITEM_MAP_ENTRY)) { + + BookSettings bs = new BookSettings(child); + encoder.addSettings(bs); + + } else { + + Debug.log(Debug.TRACE, "<OTHERS " + XmlUtil.getNodeInfo(child) + " />"); + } + } + } + } + } + + /* + * Handles the loading of defined styles from the style.xml file as well + * as automatic styles from the content.xml file. + * + * Any change to a defined style, such as a short bold section, falls into + * the latter category. + */ + protected void loadStyles(SxcDocument sxcDoc) { + + org.w3c.dom.Document contentDom = sxcDoc.getContentDOM(); + + styleCat = new StyleCatalog(25); + + NodeList nl = null; + String families[] = new String[] { SxcConstants.COLUMN_STYLE_FAMILY, + SxcConstants.ROW_STYLE_FAMILY, + SxcConstants.TABLE_CELL_STYLE_FAMILY }; + Class classes[] = new Class[] { ColumnStyle.class, + RowStyle.class, + CellStyle.class}; + + /* + * Process the content XML for any other style info. + * Should only be automatic types here. + */ + nl = contentDom.getElementsByTagName(TAG_OFFICE_AUTOMATIC_STYLES); + if (nl.getLength() != 0) { + styleCat.add(nl.item(0), families, classes, null, false); + } + } + + /** + * This method traverses <i>office:body</i> <code>Element</code>. + * + * @param node <i>office:body</i> <code>Node</code>. + * + * @throws IOException If any I/O error occurs. + */ + protected void traverseBody(Node node) throws IOException { + + Debug.log(Debug.TRACE, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); + Debug.log(Debug.TRACE, "<DEBUGLOG>"); + + if (node.hasChildNodes()) { + + NodeList nodeList = node.getChildNodes(); + int len = nodeList.getLength(); + + for (int i = 0; i < len; i++) { + Node searchNode = nodeList.item(i); + if (searchNode.getNodeType() == Node.ELEMENT_NODE) { + + String nodeName = searchNode.getNodeName(); + + if (nodeName.equals(TAG_NAMED_EXPRESSIONS)) { + + traverseNamedExpressions(searchNode); + + } else { + + Debug.log(Debug.TRACE, "Skipping " + XmlUtil.getNodeInfo(searchNode) + " />"); + } + } + } + + for (int i = 0; i < len; i++) { + Node child = nodeList.item(i); + + if (child.getNodeType() == Node.ELEMENT_NODE) { + String nodeName = child.getNodeName(); + + if (nodeName.equals(TAG_TABLE)) { + + traverseTable(child); + + } else { + + Debug.log(Debug.TRACE, "<OTHERS " + XmlUtil.getNodeInfo(child) + " />"); + } + } + } + } + + Debug.log(Debug.TRACE, "</DEBUGLOG>"); + } + + + /** + * This method traverses the <i>table:table</i> element + * <code>Node</code>. + * + * @param node A <i>table:table</i> <code>Node</code>. + * + * @throws IOException If any I/O error occurs. + */ + protected void traverseNamedExpressions(Node node) throws IOException { + + Debug.log(Debug.TRACE, "<NAMED:EXPRESSIONS>"); + + NamedNodeMap att = node.getAttributes(); + + if (node.hasChildNodes()) { + + NodeList nodeList = node.getChildNodes(); + int len = nodeList.getLength(); + + for (int i = 0; i < len; i++) { + Node child = nodeList.item(i); + + if (child.getNodeType() == Node.ELEMENT_NODE) { + NameDefinition nd = new NameDefinition(child); + encoder.setNameDefinition(nd); + } + } + } + + Debug.log(Debug.TRACE, "</NAMED:EXPRESSIONS>"); + } + + /** + * This method traverses the <i>table:table</i> element + * <code>Node</code>. + * + * @param node A <i>table:table</i> <code>Node</code>. + * + * @throws IOException If any I/O error occurs. + */ + protected void traverseTable(Node node) throws IOException { + + Debug.log(Debug.TRACE, "<TABLE>"); + + ColumnRowList = new Vector(); + + // Get table attributes + // TODO - extract style from attribute + + NamedNodeMap att = node.getAttributes(); + + String tableName = + att.getNamedItem(ATTRIBUTE_TABLE_NAME).getNodeValue(); + + rowID = 1; + + encoder.createWorksheet(tableName); + + if (node.hasChildNodes()) { + + NodeList nodeList = node.getChildNodes(); + int len = nodeList.getLength(); + + for (int i = 0; i < len; i++) { + Node child = nodeList.item(i); + + if (child.getNodeType() == Node.ELEMENT_NODE) { + String nodeName = child.getNodeName(); + + if (nodeName.equals(TAG_TABLE_ROW)) { + // TODO - handle all the possible rows + // spelled out in the entities + + traverseTableRow(child); + + } else if (nodeName.equals(TAG_TABLE_COLUMN)) { + + traverseTableColumn(child); + + } else if (nodeName.equals(TAG_TABLE_SCENARIO)) { + + // TODO + + } else { + + Debug.log(Debug.TRACE, "<OTHERS " + XmlUtil.getNodeInfo(child) + " />"); + } + } + } + } + + // Add column width info to the current sheet + encoder.setColumnRows(ColumnRowList); + + Debug.log(Debug.TRACE, "</TABLE>"); + } + + /** + * This method traverses the <i>table:table-row</i> element + * <code>Node</code>. + * + * @param node A <i>table:table-row</i> <code>Node</code>. + * + * @throws IOException If any I/O error occurs. + */ + protected void traverseTableRow(Node node) throws IOException { + + // Get the attributes of the row + NamedNodeMap cellAtt = node.getAttributes(); + + if (cellAtt != null) { + + Node rowStyle = + cellAtt.getNamedItem(ATTRIBUTE_TABLE_STYLE_NAME); + + Node tableNumRowRepeatingNode = cellAtt.getNamedItem(ATTRIBUTE_TABLE_NUM_ROWS_REPEATED); + int repeatedRows = 1; + + if(tableNumRowRepeatingNode!=null) { + String repeatStr = tableNumRowRepeatingNode.getNodeValue(); + Debug.log(Debug.TRACE, "traverseTableRow() repeated-rows : " + repeatStr); + repeatedRows = Integer.parseInt(repeatStr); + } + + String styleName = new String(""); + + if ( rowStyle != null) { + styleName = rowStyle.getNodeValue(); + } + if(styleName.equalsIgnoreCase("Default") || styleName.length()==0) { + + Debug.log(Debug.TRACE, "No defined Row Style Attribute was found"); + + } else { + + RowStyle rStyle = ( RowStyle)styleCat.lookup(styleName, + SxcConstants.ROW_STYLE_FAMILY, null, + RowStyle.class); + + int rowHeight = rStyle.getRowHeight(); + + Debug.log(Debug.TRACE, "traverseTableRow() Row Height : " + rowHeight); + ColumnRowInfo ri = new ColumnRowInfo( rowHeight, + repeatedRows, + ColumnRowInfo.ROW, + rowHeight!=0); + ColumnRowList.add(ri); + } + + // Get the attribute representing the number of rows repeated + Node rowsRepeatedNode = + cellAtt.getNamedItem(ATTRIBUTE_TABLE_NUM_ROWS_REPEATED); + + // There is a number of rows repeated attribute: + if (rowsRepeatedNode != null) { + + // Get the number of times the row is repeated + String rowsRepeatedString = rowsRepeatedNode.getNodeValue(); + + Integer rowsRepeatedInt = new Integer(rowsRepeatedString); + + rowsRepeated = rowsRepeatedInt.intValue(); + + } else { + + // The row is not repeated + rowsRepeated = 1; + } + } + + Debug.log(Debug.TRACE, "<TR>"); + + if (node.hasChildNodes()) { + + NodeList nodeList = node.getChildNodes(); + int len = nodeList.getLength(); + + for (int i = 0; i < len; i++) { + Node child = nodeList.item(i); + + if (child.getNodeType() == Node.ELEMENT_NODE) { + String nodeName = child.getNodeName(); + + if (nodeName.equals(TAG_TABLE_CELL)) { + + traverseCell(child); + + } else { + + Debug.log(Debug.TRACE, "<OTHERS " + XmlUtil.getNodeInfo(child) + " />"); + } + } + } + } + + // Increase the row counter by the number of rows which are repeated + rowID += rowsRepeated; + + // Re-initialize number of rows repeated before processing the next + // row data. + rowsRepeated = 1; + + // When starting a new row, set the column counter back to the + // first column. + colID = 1; + + // Re-initialize number of columns repeated before processing + // the next row data. + colsRepeated = 1; + + Debug.log(Debug.TRACE, "</TR>"); + } + + + /** + * This method traverses the <i>table:table-column</i> + * <code>Node</code>. Not yet implemented. + * + * @param node A <i>table:table-column</i> <code>Node</code>. + * + * @throws IOException If any I/O error occurs. + */ + protected void traverseTableColumn(Node node) throws IOException { + + Debug.log(Debug.TRACE, "traverseColumn() : "); + NamedNodeMap cellAtt = node.getAttributes(); + Node tableStyleNode = cellAtt.getNamedItem(ATTRIBUTE_TABLE_STYLE_NAME); + Node tableNumColRepeatingNode = cellAtt.getNamedItem(ATTRIBUTE_TABLE_NUM_COLUMNS_REPEATED); + Node tableDefaultCellStyle = cellAtt.getNamedItem(ATTRIBUTE_DEFAULT_CELL_STYLE); + + int repeatedColumns = 1; + int columnWidth = 0; + ColumnRowInfo col = new ColumnRowInfo(ColumnRowInfo.COLUMN); + + if(tableNumColRepeatingNode!=null) { + Debug.log(Debug.TRACE, "traverseColumn() repeated-cols : " + tableNumColRepeatingNode.getNodeValue()); + repeatedColumns = Integer.parseInt(tableNumColRepeatingNode.getNodeValue()); + col.setRepeated(repeatedColumns); + } + + String cellStyleName = new String(""); + + if(tableDefaultCellStyle!=null) { + cellStyleName = tableDefaultCellStyle.getNodeValue(); + + Debug.log(Debug.TRACE, "traverseColumn() default-cell-style : " + cellStyleName); + } + + if(cellStyleName.equalsIgnoreCase("Default") || cellStyleName.length()==0) { + + Debug.log(Debug.TRACE, "No default cell Style Attribute was found"); + + } else { + + CellStyle cellStyle = (CellStyle)styleCat.lookup(cellStyleName, + SxcConstants.TABLE_CELL_STYLE_FAMILY, null, + CellStyle.class); + Format defaultFmt = new Format(cellStyle.getFormat()); + col.setFormat(defaultFmt); + } + + String styleName = new String(""); + + if(tableStyleNode!=null) { + styleName = tableStyleNode.getNodeValue(); + } + + if(styleName.equalsIgnoreCase("Default") || styleName.length()==0) { + + Debug.log(Debug.TRACE, "No defined Style Attribute was found"); + + } else { + + ColumnStyle cStyle = (ColumnStyle)styleCat.lookup(styleName, + SxcConstants.COLUMN_STYLE_FAMILY, null, + ColumnStyle.class); + + columnWidth = cStyle.getColWidth(); + col.setSize(columnWidth); + Debug.log(Debug.TRACE, "traverseColumn() Column Width : " + columnWidth); + + } + ColumnRowList.add(col); + } + + /** + * This method traverses a <i>table:table-cell</i> element + * <code>Node</code>. + * + * @param node a <i>table:table-cell</i> <code>Node</code>. + * + * @throws IOException if any I/O error occurs. + */ + protected void traverseCell(Node node) throws IOException { + + NamedNodeMap cellAtt = node.getAttributes(); + + int debug_i=0; + Node debug_attrib = null; + fmt.clearFormatting(); + if (cellAtt == null || cellAtt.item(0) == null) + { + Debug.log(Debug.INFO, "No Cell Attributes\n"); + // return; + } + else + { + while ((debug_attrib = cellAtt.item(debug_i++)) != null) + { + Debug.log(Debug.INFO, "Cell Attribute " + debug_i + + ": " + debug_attrib.getNodeName() + " : " + + debug_attrib.getNodeValue() + "\n"); + } + } + + // Get the type of data in the cell + Node tableValueTypeNode = + cellAtt.getNamedItem(ATTRIBUTE_TABLE_VALUE_TYPE); + + // Get the number of columns this cell is repeated + Node colsRepeatedNode = + cellAtt.getNamedItem(ATTRIBUTE_TABLE_NUM_COLUMNS_REPEATED); + + // Get the style type + Node tableStyleNode = + cellAtt.getNamedItem(ATTRIBUTE_TABLE_STYLE_NAME); + + String styleName = new String(""); + + if(tableStyleNode!=null) { + styleName = tableStyleNode.getNodeValue(); + } + + if(styleName.equalsIgnoreCase("Default")) { + + Debug.log(Debug.TRACE, "No defined Style Attribute was found"); + + } else if(styleName.length()!=0) { + + CellStyle cStyle = (CellStyle)styleCat.lookup(styleName, + SxcConstants.TABLE_CELL_STYLE_FAMILY, null, + CellStyle.class); + + Format definedFormat = cStyle.getFormat(); + fmt = new Format(definedFormat); + } + + // There is a number of cols repeated attribute + if (colsRepeatedNode != null) { + + // Get the number of times the cell is repeated + String colsRepeatedString = colsRepeatedNode.getNodeValue(); + + Integer colsRepeatedInt = new Integer(colsRepeatedString); + colsRepeated = colsRepeatedInt.intValue(); + } else { + + // The cell is not repeated + colsRepeated = 1; + } + + + // if there is no style we need to check to see if there is a default + // cell style defined in the table-column's + + if (fmt.isDefault() && styleName.length()==0) { + int index = 1; + for(Enumeration e = ColumnRowList.elements();e.hasMoreElements();) { + ColumnRowInfo cri = (ColumnRowInfo) e.nextElement(); + if(cri.isColumn()) { + if(colID>=index && colID<(index+cri.getRepeated())) { + fmt = new Format(cri.getFormat()); + } + index += cri.getRepeated(); + } + } + } + + + // for (int j = 0; j < colsRepeated; j++) { + + + if (tableValueTypeNode != null) { + + // Make sure we initialize to 0 the width of the current cell + displayWidth = 0; + + String cellType = + tableValueTypeNode.getNodeValue(); + + if (cellType.equalsIgnoreCase(CELLTYPE_STRING)) { + + // has text:p tag + fmt.setCategory(CELLTYPE_STRING); + Node tableStringValueNode = cellAtt.getNamedItem(ATTRIBUTE_TABLE_STRING_VALUE); + Debug.log(Debug.TRACE,"Cell Type String : " + tableStringValueNode); + if(tableStringValueNode != null) { + fmt.setValue(tableStringValueNode.getNodeValue()); + } + + } else if (cellType.equalsIgnoreCase(CELLTYPE_FLOAT)) { + + // has table:value attribute + // has text:p tag + + // Determine the number of decimal places StarCalc + // is displaying for this floating point output. + fmt.setCategory(CELLTYPE_FLOAT); + fmt.setDecimalPlaces(getDecimalPlaces(node)); + Node tableValueNode = cellAtt.getNamedItem(ATTRIBUTE_TABLE_VALUE); + fmt.setValue(tableValueNode.getNodeValue()); + + + } else if (cellType.equalsIgnoreCase(CELLTYPE_TIME)) { + + // has table:time-value attribute + // has text:p tag - which is the value we convert + + fmt.setCategory(CELLTYPE_TIME); + Node tableTimeNode = cellAtt.getNamedItem(ATTRIBUTE_TABLE_TIME_VALUE); + fmt.setValue(tableTimeNode.getNodeValue()); + + } else if (cellType.equalsIgnoreCase(CELLTYPE_DATE)) { + + // has table:date-value attribute + // has text:p tag - which is the value we convert + + fmt.setCategory(CELLTYPE_DATE); + Node tableDateNode = cellAtt.getNamedItem(ATTRIBUTE_TABLE_DATE_VALUE); + fmt.setValue(tableDateNode.getNodeValue()); + + } else if (cellType.equalsIgnoreCase(CELLTYPE_CURRENCY)) { + + // has table:currency + // has table:value attribute + // has text:p tag + + fmt.setCategory(CELLTYPE_CURRENCY); + fmt.setDecimalPlaces(getDecimalPlaces(node)); + Node tableValueNode = cellAtt.getNamedItem(ATTRIBUTE_TABLE_VALUE); + fmt.setValue(tableValueNode.getNodeValue()); + + } else if (cellType.equalsIgnoreCase(CELLTYPE_BOOLEAN)) { + + // has table:boolean-value attribute + // has text:p tag - which is the value we convert + + fmt.setCategory(CELLTYPE_BOOLEAN); + Node tableBooleanNode = cellAtt.getNamedItem(ATTRIBUTE_TABLE_BOOLEAN_VALUE); + fmt.setValue(tableBooleanNode.getNodeValue()); + + } else if (cellType.equalsIgnoreCase(CELLTYPE_PERCENT)) { + + // has table:value attribute + // has text:p tag + + fmt.setCategory(CELLTYPE_PERCENT); + fmt.setDecimalPlaces(getDecimalPlaces(node)); + Node tableValueNode = cellAtt.getNamedItem(ATTRIBUTE_TABLE_VALUE); + fmt.setValue(tableValueNode.getNodeValue()); + + } else { + + Debug.log(Debug.TRACE,"No defined value type" + cellType); + // Should never get here + + } + } + + Node tableFormulaNode = cellAtt.getNamedItem(ATTRIBUTE_TABLE_FORMULA); + + if(tableFormulaNode != null) + { + if(tableValueTypeNode == null) { // If there is no value-type Node we must assume string-value + fmt.setCategory(CELLTYPE_STRING); + Node tableStringValueNode = cellAtt.getNamedItem(ATTRIBUTE_TABLE_STRING_VALUE); + fmt.setValue(tableStringValueNode.getNodeValue()); + } + String cellFormula = tableFormulaNode.getNodeValue(); + addCell(cellFormula); + } else { + + // Text node, Date node, or Time node + // + Debug.log(Debug.INFO, + "TextNode, DateNode, TimeNode or BooleanNode\n"); + // This handles the case where we have style information but no content + if (node.hasChildNodes()) { + NodeList childList = node.getChildNodes(); + int len = childList.getLength(); + + for (int i = 0; i < len; i++) { + Node child = childList.item(i); + if (child.getNodeType() == Node.ELEMENT_NODE) { + String childName = child.getNodeName(); + if (childName.equals(TAG_PARAGRAPH)) { + traverseParagraph(child); + } + } + } + } else if(!fmt.isDefault()) { + addCell(""); + } + } + + // Clear out format for current cell after it is written + format = 0; + + // Increase the column counter by the number of times the + // last cell was repeated. + colID += colsRepeated; + + // Re-initialize the number of columns repeated before processing + // the next cell data. + colsRepeated = 1; + + } + + + /** + * This method traverses the <i>text:p</i> element <code>Node</code>. + * + * @param node A <i>text:p</i> <code>Node</code>. + * + * @throws IOException If any I/O error occurs. + */ + protected void traverseParagraph(Node node) throws IOException { + + NamedNodeMap cellAtt = node.getAttributes(); + + int debug_i=0; + Node debug_attrib = null; + if (cellAtt == null || cellAtt.item(0) == null) + { + Debug.log(Debug.INFO, "No Paragraph Attributes\n"); + } + else + { + while ((debug_attrib = cellAtt.item(debug_i++)) != null) + { + Debug.log(Debug.INFO, "Paragraph Attribute " + debug_i + + ": " + debug_attrib.getNodeName() + " : " + + debug_attrib.getNodeValue() + "\n"); + } + } + + if (node.hasChildNodes()) { + + NodeList nodeList = node.getChildNodes(); + + int len = nodeList.getLength(); + + StringBuffer buffer = new StringBuffer(); + + for (int i = 0; i < len; i++) { + + Node child = nodeList.item(i); + + // TODO: need to handle space/tabs/newline nodes later + short nodeType = child.getNodeType(); + + switch (nodeType) { + + case Node.TEXT_NODE: + buffer.append(child.getNodeValue()); + break; + + case Node.ENTITY_REFERENCE_NODE: + + NodeList nodeList2 = child.getChildNodes(); + int len2 = nodeList2.getLength(); + + for (int j = 0; j < len2; j++) { + Node child2 = nodeList2.item(j); + + if (child2.getNodeType() == Node.TEXT_NODE) { + buffer.append(child2.getNodeValue()); + } + } + + break; + } + } + + String s = buffer.toString(); + // displayWidth = calculateContentWidth(s); + addCell(s); + + } + } + + + /** + * This method will take the input cell value and add + * it to the spreadsheet <code>Document</code> we are currently + * encoding. This method correctly handles cells that are + * repeated in either the row, cell, or both directions. + * + * @param cellValue The contents of the cell we want to add + * to the spreadsheet <code>Document</code>. + * + * @throws IOException If any I/O error occurs. + */ + protected void addCell(String cellValue) throws IOException { + + int col = colID; + int row = rowID; + + for (int i = 0; i < rowsRepeated; i++) { + + // Log the columns when there are rowsRepeated. + if (i > 0) { + Debug.log(Debug.TRACE, "</TR>"); + Debug.log(Debug.TRACE, "<TR>"); + } + + col = colID; + + for (int j = 0; j < colsRepeated; j++) { + + Debug.log(Debug.TRACE, "<TD>"); + + + // Add the cell data to the encoded spreadsheet document + encoder.addCell(row, col, fmt, cellValue); + + Debug.log(Debug.TRACE, cellValue); + Debug.log(Debug.TRACE, "</TD>"); + + col++; + } + + row++; + + } + + } + + + + /** + * This method takes a <i>table:table-cell</i> <code>Node</code> + * and traverses down to the <i>text:p</i> tag. The value is + * extracted from the <i>text:p</i> tag and the number of decimal + * places is calculated. + * + * @param node A <i>table:table-cell</i> <code>Node</code>. + * + * @return The number of decimal places in the display + * string of the data in the input <code>Node</code>. + */ + protected int getDecimalPlaces(Node node) { + + int decimals = 0; + + Element element = null; + + // cast org.w3c.dom.Node to org.w3c.dom.Element + if (node instanceof Element) { + element = (Element) node; + } else { + return decimals; + } + + // Traverse to the text:p element, there should only be one. + NodeList list = element.getElementsByTagName(TAG_PARAGRAPH); + + if (list.getLength() != 1) { + return decimals; + } + + Node paragraph = list.item(0); + + if (paragraph.hasChildNodes()) { + + NodeList nodeList = paragraph.getChildNodes(); + + int len = nodeList.getLength(); + + for (int j = 0; j < len; j++) { + + Node child = nodeList.item(j); + + if (child.getNodeType() == Node.TEXT_NODE) { + + String s = child.getNodeValue(); + + // displayWidth = calculateContentWidth(s); + + int k = s.lastIndexOf("."); + if (k > 0) { + s = s.substring(k+1); + decimals = s.length(); + } + } + } + } + + return decimals; + } + + /* + * Utility method to retrieve a Node attribute. + */ + private String getAttribute (Node node, String attribute) { + NamedNodeMap attrNodes = node.getAttributes(); + + if (attrNodes != null) { + Node attr = attrNodes.getNamedItem(attribute); + if (attr != null) { + return attr.getNodeValue(); + } + } + return null; + } + +} + diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/SxcPluginFactory.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/SxcPluginFactory.java new file mode 100644 index 000000000000..5ad490f332dc --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/SxcPluginFactory.java @@ -0,0 +1,86 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: SxcPluginFactory.java,v $ + * $Revision: 1.4 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +package org.openoffice.xmerge.converter.xml.sxc; + +import java.io.InputStream; +import java.io.IOException; + +import org.openoffice.xmerge.util.registry.ConverterInfo; +import org.openoffice.xmerge.ConvertData; +import org.openoffice.xmerge.PluginFactory; +import org.openoffice.xmerge.Document; +import org.openoffice.xmerge.DocumentMergerFactory; + + +/** + * General implementation of the <code>PluginFactory</code> interface + * for SXC <code>Document</code> objects. + * + * @see org.openoffice.xmerge.DocumentDeserializer + * @see org.openoffice.xmerge.DocumentMerger + * @see org.openoffice.xmerge.DocumentSerializer + */ +public abstract class SxcPluginFactory + extends PluginFactory implements DocumentMergerFactory { + + + /** + * Constructor that caches the <code>ConvertInfo</code> that + * corresponds to the registry information for this plug-in. + * + * @param ci <code>ConvertInfo</code> object. + */ + public SxcPluginFactory(ConverterInfo ci) { + super(ci); + } + + + public Document createOfficeDocument(String name, InputStream is) + throws IOException { + + // read zipped XML stream + // + SxcDocument doc = new SxcDocument(name); + doc.read(is); + return doc; + } + + public Document createOfficeDocument(String name, InputStream is,boolean isZip) + throws IOException { + + // read zipped XML stream + // + SxcDocument doc = new SxcDocument(name); + doc.read(is,isZip); + return doc; + } +} + diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/build.xml b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/build.xml new file mode 100644 index 000000000000..d8219c1f46a5 --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/build.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + + DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + + Copyright 2008 by Sun Microsystems, Inc. + + OpenOffice.org - a multi-platform office productivity suite + + $RCSfile: build.xml,v $ + + $Revision: 1.9 $ + + This file is part of OpenOffice.org. + + OpenOffice.org is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + OpenOffice.org 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 version 3 for more details + (a copy is included in the LICENSE file that accompanied this code). + + You should have received a copy of the GNU Lesser General Public License + version 3 along with OpenOffice.org. If not, see + <http://www.openoffice.org/license.html> + for a copy of the LGPLv3 License. + +--> +<project name="xmrg_jooxcx_sxc" default="main" basedir="."> + + <!-- ================================================================= --> + <!-- settings --> + <!-- ================================================================= --> + + <!-- project prefix, used for targets and build.lst --> + <property name="prj.prefix" value="xmrg"/> + + <!-- name of this sub target used in recursive builds --> + <property name="target" value="xmrg_jooxcx_sxc"/> + + <!-- relative path to project directory --> + <property name="prj" value="../../../../../../.."/> + + <!-- start of java source code package structure --> + <property name="java.dir" value="${prj}/java"/> + + <!-- path component for current java package --> + <property name="package" + value="org/openoffice/xmerge/converter/xml/sxc"/> + + <!-- define how to handle CLASSPATH environment --> + <property name="build.sysclasspath" value="ignore"/> + + <!-- classpath settings for javac tasks --> + <path id="classpath"> + <pathelement location="${build.class}"/> + <pathelement location="${solar.jar}/parser.jar"/> + <pathelement location="${solar.jar}/jaxp.jar"/> + <pathelement location="${solar.jar}/xerces.jar"/> + </path> + + <!-- set wether we want to compile with or without deprecation --> + <property name="deprecation" value="on"/> + + <!-- ================================================================= --> + <!-- solar build environment targets --> + <!-- ================================================================= --> + + <target name="build_dir" unless="build.dir"> + <property name="build.dir" value="${out}"/> + </target> + + <target name="solar" depends="build_dir" if="solar.update"> + <property name="solar.properties" + value="${solar.bin}/solar.properties"/> + </target> + + <target name="init" depends="solar"> + <property name="build.compiler" value="classic"/> + <property file="${solar.properties}"/> + <property file="${build.dir}/class/solar.properties"/> + </target> + + <target name="info"> + <echo message="--------------------"/> + <echo message="${target}"/> + <echo message="--------------------"/> + </target> + + + <!-- ================================================================= --> + <!-- custom targets --> + <!-- ================================================================= --> + + <!-- the main target, called in recursive builds --> + <target name="main" depends="info,prepare,compile"/> + + <!-- prepare output directories --> + <target name="prepare" depends="init" if="build.class"> + <mkdir dir="${build.dir}"/> + <mkdir dir="${build.class}"/> + </target> + + <!-- compile java sources in ${package} --> + <target name="compile" depends="prepare" if="build.class"> + <javac srcdir="${java.dir}" + destdir="${build.class}" + debug="${debug}" + deprecation="${deprecation}" + optimize="${optimize}"> + <classpath refid="classpath"/> + <include name="${package}/CellStyle.java"/> + <include name="${package}/ColumnStyle.java"/> + <include name="${package}/ColumnRowInfo.java"/> + <include name="${package}/RowStyle.java"/> + <include name="${package}/SxcConstants.java"/> + <include name="${package}/SxcDocument.java"/> + <include name="${package}/Format.java"/> + <include name="${package}/BookSettings.java"/> + <include name="${package}/SheetSettings.java"/> + <include name="${package}/NameDefinition.java"/> + <include name="${package}/SpreadsheetDecoder.java"/> + <include name="${package}/SpreadsheetEncoder.java"/> + <include name="${package}/SxcDocumentDeserializer.java"/> + <include name="${package}/SxcDocumentSerializer.java"/> + <include name="${package}/DocumentMergerImpl.java"/> + <include name="${package}/SxcPluginFactory.java"/> + + </javac> + </target> + + <!-- clean up --> + <target name="clean" depends="prepare"> + <delete includeEmptyDirs="true"> + <fileset dir="${build.class}"> + <patternset> + <include name="${package}/*.class"/> + </patternset> + </fileset> + </delete> + </target> + +</project> + diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/makefile.mk b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/makefile.mk new file mode 100644 index 000000000000..1c2e06d1ef57 --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/makefile.mk @@ -0,0 +1,36 @@ +#*************************************************************************** +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2008 by Sun Microsystems, Inc. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# $RCSfile: makefile.mk,v $ +# +# $Revision: 1.3 $ +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org 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 version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#*************************************************************************** + +TARGET=xmrg_jooxcx_sxc +PRJ=../../../../../../.. + +.INCLUDE : ant.mk +ALLTAR: ANTBUILD diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/minicalc/ConverterCapabilitiesImpl.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/minicalc/ConverterCapabilitiesImpl.java new file mode 100644 index 000000000000..54ce2e236a4d --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/minicalc/ConverterCapabilitiesImpl.java @@ -0,0 +1,116 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: ConverterCapabilitiesImpl.java,v $ + * $Revision: 1.3 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +package org.openoffice.xmerge.converter.xml.sxc.minicalc; + +import org.openoffice.xmerge.ConverterCapabilities; +import org.openoffice.xmerge.converter.xml.OfficeConstants; + + +/** + * <p>MiniCalc implementation of <code>ConverterCapabilities</code> for + * the {@link + * org.openoffice.xmerge.converter.xml.sxc.minicalc.PluginFactoryImpl + * PluginFactoryImpl}.</p> + * + * <p>Used with StarCalc SXC to/from MiniCalc conversions. The + * <code>ConverterCapibilies</code> specify which "Office" + * <code>Document</code> tags and attributes are supported on the + * "Device" <code>Document</code> format.</p> + */ +public final class ConverterCapabilitiesImpl + implements ConverterCapabilities { + + public boolean canConvertTag(String tag) { + + if (OfficeConstants.TAG_OFFICE_BODY.equals(tag)) + return true; + else if (OfficeConstants.TAG_PARAGRAPH.equals(tag)) + return true; + else if (OfficeConstants.TAG_TABLE.equals(tag)) + return true; + else if (OfficeConstants.TAG_TABLE_ROW.equals(tag)) + return true; + else if (OfficeConstants.TAG_TABLE_COLUMN.equals(tag)) + return false; + // TODO - we currently do not handle the table column tag + else if (OfficeConstants.TAG_TABLE_SCENARIO.equals(tag)) + return false; + // TODO - we currently do not handle the table scenario tag + else if (OfficeConstants.TAG_TABLE_CELL.equals(tag)) + return true; + + return false; + } + + public boolean canConvertAttribute(String tag, + String attribute) { + + if (OfficeConstants.TAG_TABLE.equals(tag)) { + + if (OfficeConstants.ATTRIBUTE_TABLE_NAME.equals(attribute)) + return true; + + } else if (OfficeConstants.TAG_TABLE_CELL.equals(tag)) { + + if (OfficeConstants.ATTRIBUTE_TABLE_VALUE_TYPE.equals(attribute)) + return true; + else if (OfficeConstants.ATTRIBUTE_TABLE_FORMULA. + equals(attribute)) + return true; + else if (OfficeConstants.ATTRIBUTE_TABLE_VALUE.equals(attribute)) + return true; + else if (OfficeConstants.ATTRIBUTE_TABLE_BOOLEAN_VALUE. + equals(attribute)) + return true; + else if (OfficeConstants.ATTRIBUTE_TABLE_CURRENCY. + equals(attribute)) + return true; + else if (OfficeConstants.ATTRIBUTE_TABLE_TIME_VALUE. + equals(attribute)) + return true; + else if (OfficeConstants.ATTRIBUTE_TABLE_DATE_VALUE. + equals(attribute)) + return true; + else if (OfficeConstants.ATTRIBUTE_TABLE_NUM_COLUMNS_REPEATED. + equals(attribute)) + return true; + + } else if (OfficeConstants.TAG_TABLE_ROW.equals(tag)) { + + if (OfficeConstants.ATTRIBUTE_TABLE_NUM_ROWS_REPEATED. + equals(attribute)) + return true; + } + + return false; + } +} + diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/minicalc/MinicalcConstants.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/minicalc/MinicalcConstants.java new file mode 100644 index 000000000000..c14178ee96e1 --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/minicalc/MinicalcConstants.java @@ -0,0 +1,48 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: MinicalcConstants.java,v $ + * $Revision: 1.3 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +package org.openoffice.xmerge.converter.xml.sxc.minicalc; + +import org.openoffice.xmerge.converter.palm.PdbUtil; + +/** + * Constants used for encoding and decoding the MiniCalc format. + * + * @author Herbie Ong + */ +interface MinicalcConstants { + + /** Creator ID. */ + public static final int CREATOR_ID = PdbUtil.intID("PiMC"); + + /** Type ID. */ + public static final int TYPE_ID = PdbUtil.intID("Data"); +} + diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/minicalc/MinicalcDataString.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/minicalc/MinicalcDataString.java new file mode 100644 index 000000000000..77bfe75539b8 --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/minicalc/MinicalcDataString.java @@ -0,0 +1,548 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: MinicalcDataString.java,v $ + * $Revision: 1.3 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +package org.openoffice.xmerge.converter.xml.sxc.minicalc; + +/** + * This class is used by <code>MinicalcDecoder</code> to manipulate a + * <code>String</code> containing MiniCalc cell data. + * + * @author Paul Rank + */ +public class MinicalcDataString { + + /** The String representation of the MiniCalc data. */ + private String data = null; + + + /** + * Constructor stores the MiniCalc data <code>String</code>. + * + * @param data A <code>String</code> containing MiniCalc + * cell data. + */ + public MinicalcDataString(String data) { + this.data = data; + } + + + /** + * Checks if the MiniCalc data <code>String</code> is a <i>formula</i>. + * + * @return true if the MiniCalc data <code>String</code> is a + * <i>formula</i>, false if the MiniCalc data <code>String</code> + * is not a <i>formula</i>. + */ + public boolean isFormula() { + + if (data.startsWith("=")) { + return true; + } + + return false; + } + + + /** + * Checks if the MiniCalc data <code>String</code> is a <i>percentage</i>. + * + * @return true if the MiniCalc data <code>String</code> is a + * <i>percentage</i>, false if the MiniCalc data + * <code>String</code> is not a <i>percentage</i>. + */ + public boolean isPercent() { + + if (data.endsWith("%")) { + return true; + } + + return false; + } + + + /** + * Checks if the MiniCalc data <code>String</code> is a + * <i>boolean</i> value. + * + * @return true if the MiniCalc data <code>String</code> is + * a <i>boolean</i>, false if the MiniCalc data + * <code>String</code> is not a <i>boolean</i>. + */ + public boolean isBoolean() { + + if (data.equalsIgnoreCase("false") || + data.equalsIgnoreCase("true")) { + return true; + } + + return false; + } + + + /** + * Checks if the MiniCalc data <code>String</code> is a <i>date</i>. + * + * @return true if the MiniCalc data <code>String</code> is + * a <i>date</i>, false if the MiniCalc data <code>String</code> + * is not a <i>date</i>. + */ + public boolean isDate() { + + // Starting index into the date string - month + int start = 0; + + // Search for "/", which separates month from day + int end = data.indexOf("/"); + + // Separator was found + if (end > 0) { + + String monthString = data.substring(start, end); + + try { + Float f = Float.valueOf(monthString); + if ((f.intValue() < 0) || (f.intValue() > 12)) { + return false; + } + } + catch (NumberFormatException e) { + + // no, it is not a currency type + return false; + } + + // start is now the starting index of day + start = end+1; + + // Search for "/", which separates day from year + end = data.indexOf("/", start); + + // Separator was found + if (end > 0) { + + String dayString = data.substring(start, end); + + try { + Float f = Float.valueOf(dayString); + if ((f.intValue() < 0) || (f.intValue() > 31)) + return false; + } + catch (NumberFormatException e) { + // no, it is not a currency type + return false; + } + } else { + return false; + } + + // start is now at the starting index of the year + start = end + 1; + + String yearString = data.substring(start); + try { + Float f = Float.valueOf(yearString); + if (f.intValue() < 0) { + return false; + } + } + catch (NumberFormatException e) { + // no, it is not a currency type + return false; + } + + } else { + return false; + } + + return true; + } + + + /** + * Checks if the MiniCalc data <code>String</code> is a <i>time</i>. + * + * @return true if the MiniCalc data <code>String</code> + * is a <i>time</i>, false if the MiniCalc data + * <code>String</code> is not a <i>time</i>. + */ + public boolean isTime() { + + // Starting index into the time string - hour + int start = 0; + + // Search for ":", which separates hour from minute + int end = data.indexOf(":"); + + + // Separator was found + if (end > 0) { + + String hourString = data.substring(start, end); + try { + Float f = Float.valueOf(hourString); + if ((f.intValue() < 0) || (f.intValue() > 24)) + return false; + } + catch (NumberFormatException e) { + // no, it is not a time type + return false; + } + + // start is now the starting index of minute + start = end+1; + + // Search for ":", which separates minute from second + end = data.indexOf(":", start); + + // Separator was found + if (end > 0) { + + String minuteString = data.substring(start, end); + + try { + Float f = Float.valueOf(minuteString); + if ((f.intValue() < 0) || (f.intValue() > 60)) + return false; + } + catch (NumberFormatException e) { + // no, it is not a time type + return false; + } + + // start is now at the starting index of the seconds + start = end+1; + + // The seconds are in the string + if (data.length() > start) { + + String secondString = data.substring(start); + + + try { + Float f = Float.valueOf(secondString); + if ((f.intValue() < 0) || (f.intValue() > 60)) + return false; + } + catch (NumberFormatException e) { + // no, it is not a time type + return false; + } + } + + } + + return true; + + } + + return false; + } + + + /** + * Checks if the MiniCalc data <code>String</code> is a <i>currency</i> + * value. + * + * @return true if the MiniCalc data <code>String</code> is + * a <i>currency</i>, false if the MiniCalc data + * <code>String</code> is not a <i>currency</i>. + */ + public boolean isCurrency() { + + boolean result = false; + + // TODO - we currently only check for US currencies + + if (data.endsWith("$")) { + String number = data.substring(0, data.length()-1); + try { + Float f = Float.valueOf(number); + result = true; + } + catch (NumberFormatException e) { + // no, it is not a currency type + result = false; + } + } + + else if (data.startsWith("$")) { + String number = data.substring(1, data.length()); + try { + Float f = Float.valueOf(number); + result = true; + } + catch (NumberFormatException e) { + // no, it is not a currency type + result = false; + } + } + + return result; + + } + + + /** + * This method removes the percent sign from the MiniCalc data + * <code>String</code>. If the percent sign is not the last + * character of the MiniCalc data <code>String</code>, the + * MiniCalc data <code>String</code> is returned. + * + * @return The MiniCalc data <code>String</code> minus the + * percent sign. If the MiniCalc data <code>String</code> + * does not begin with a percent sign, the MiniCalc data + * <code>String</code> is returned. + */ + public String percentRemoveSign() { + + String number = data; + + if (data.endsWith("%")) { + // "%" is the last character, so remove + number = data.substring(0, data.length()-1); + + try { + Float f = Float.valueOf(number); + float f1 = f.floatValue()/100f; + Float f2 = new Float(f1); + number = f2.toString(); + } + catch (NumberFormatException e) { + // no, it is not a float type + } + } + + return number; + } + + + /** + * This method removes the currency sign from the MiniCalc data + * <code>String</code>. If the currency sign is not the first or + * last character of the MiniCalc data <code>String</code>, the + * MiniCalc data <code>String</code> is returned. + * + * @return The MiniCalc data <code>String</code> minus the currency + * sign. If the MiniCalc data <code>String</code> does not + * begin or end with a currency sign, the MiniCalc + * data <code>String</code> is returned. + */ + public String currencyRemoveSign() { + + String number = data; + + // TODO - only works with US currencies + + if (data.endsWith("$")) { + + number = data.substring(0, data.length()-1); + + } else if (data.startsWith("$")) { + + number = data.substring(1, data.length()); + } + + return number; + + } + + + /** + * <p>This method converts a MiniCalc date from MiniCalc + * format to StarOffice XML format.</p> + * + * <p>MiniCalc format:</p> + * + * <p><blockquote> + * MM/DD/YY or MM/DD/YYYY + * </blockquote></p> + * + * <p>StarOffice XML format:</p> + * + * <p><blockquote> + * YYYY-MM-DD + * </blockquote></p> + * + * @return The MiniCalc date converted to StarOffice XML + * format. + */ + public String convertToStarDate() { + + // The output date string + String out; + + String monthString = "01"; + String dayString = "01"; + String yearString = "1900"; + + // Starting index into the date string - month + int start = 0; + + // Search for "/", which separates month from day + int end = data.indexOf("/"); + + // Separator was found + if (end > 0) { + + monthString = data.substring(start, end); + + Integer monthInt = new Integer(monthString); + + // Make sure month is 2 digits + if (monthInt.intValue() < 10) { + monthString = "0" + monthString; + } + + // start is now the starting index of day + start = end+1; + + // Search for "/", which separates day from year + end = data.indexOf("/", start); + + // Separator was found + if (end > 0) { + + dayString = data.substring(start, end); + + Integer dayInt = new Integer(dayString); + + // Make sure day is 2 digits + if (dayInt.intValue() < 10) { + dayString = "0" + dayString; + } + + // start is now at the starting index of the year + start = end + 1; + + // The year is in the string + if (data.length() > start) { + + yearString = data.substring(start); + + Integer yearInt = new Integer(yearString); + int year = yearInt.intValue(); + + if (year < 31) { + + // MiniCalc years between 0 and 30 correspond to + // 2000 - 2030 + year += 2000; + + } else if (year < 100) { + + // MiniCalc years between 31 and 99 correspond + // to 1931 - 1999 + year += 1900; + } + + yearString = Integer.toString(year); + } + } + } + + // Set out to StarOffice XML date format + out = yearString + "-" + monthString + "-" + dayString; + + return out; + } + + + /** + * This method converts the MiniCalc time from MiniCalc + * format to StarOffice XML format. + * + * <p>MiniCalc format:</p> + * + * <p><blockquote> + * hh:mm:ss + * </blockquote></p> + * + * <p>StarOffice XML format:</p> + * + * <p><blockquote> + * PThhHmmMssS + * </blockquote></p> + * + * @return The MiniCalc time converted to StarOffice XML + * format. + */ + public String convertToStarTime() { + + // The output time string + String out; + + String hourString = "00"; + String minuteString = "00"; + String secondString = "00"; + + // Starting index into the time string - hour + int start = 0; + + // Search for ":", which separates hour from minute + int end = data.indexOf(":"); + + // Separator was found + if (end > 0) { + + hourString = data.substring(start, end); + + // start is now the starting index of minute + start = end+1; + + // Search for ":", which separates minute from second + end = data.indexOf(":", start); + + // Separator was found + if (end > 0) { + + minuteString = data.substring(start, end); + + // start is now at the starting index of the seconds + start = end+1; + + // The seconds are in the string + if (data.length() > start) { + + secondString = data.substring(start); + } + + } + } + + // TODO - PT is for pacific time, where can we get the + // localized value from? + + // Set to StarOffice XML time format + out = "PT"+hourString+"H"+minuteString+"M"+secondString+"S"; + + return out; + } +} + diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/minicalc/MinicalcDecoder.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/minicalc/MinicalcDecoder.java new file mode 100644 index 000000000000..1df98ae65455 --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/minicalc/MinicalcDecoder.java @@ -0,0 +1,747 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: MinicalcDecoder.java,v $ + * $Revision: 1.5 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +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. + * + * @author Paul Rank + */ +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()); + // e.printStackTrace(); + + } + } + + + /** + * This method takes a <code>ConvertData</code> as input and + * converts it into a MiniCalc WorkSheet. The WorkSheet is then + * added to the WorkBook. + * + * @param InputStream An <code>ConvertData</code> 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()); + // e.printStackTrace(); + + } + } + + + /** + * 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()); + // e.printStackTrace(); + + } + + 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; + } + + /** + * <p>This method takes a formula and parses it into + * StarOffice XML formula format.</p> + * + * <p>Many spreadsheets use ',' as a separator. + * StarOffice XML format uses ';' as a separator instead.</p> + * + * <p>Many spreadsheets use '!' as a separator when refencing + * a cell in a different sheet.</p> + * + * <blockquote> + * Example: =sheet1!A1 + * </blockquote> + * + * <p>StarOffice XML format uses '.' as a separator instead.</p> + * + * <blockquote> + * Example: =sheet1.A1 + * </blockquote> + * + * @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; + } + + /** + * <p>This method returns the type of the data in the current cell.</p> + * + * <p>Possible Data Types:</p> + * + * <ul><li> + * Percent - MiniCalc can store as a number or as a string. + * + * When stored as a string, the % sign is stored in the + * string . The MiniCalc format is "string". + * Example 10.1% is stored as the string "10.1%" + * + * When stored as a number, the decimal representation + * is stored. The MiniCalc format is "percentage". + * Example: 10.1% is stored as "0.101" + * </li><li> + * Currency - MiniCalc stores currency as a number with the format + * set to "currency". + * A user can also enter a value with a dollar sign + * (example $18.56) into MiniCalc and not set the format + * as currency. We treat this type of data as a + * currency data type. + * </li><li> + * Boolean - MiniCalc stores in a string as "true" or "false" + * </li><li> + * + * Date - MiniCalc stores a date in a string as either + * MM/DD/YY or MM/DD/YYYY. Any variation from the above + * format will not be considered a date. + * </li><li> + * Time - MiniCalc stores a time in a string as hh:mm:ss. Any + * variation from this format will not be considered a time. + * </li><li> + * Float - MiniCalc stores as a number and it is not percent + * or currency. + * </li><li> + * String - MiniCalc stores as a string (surprise). Doesn't parse + * to any of the other data types. + * </li><li> + * @return The type of the data in the current cell. + * </li></ul> + */ + public String getCellDataType() { + + boolean isNumber = false; + + // Get format value set on the cell in MiniCalc + String format = getCellFormatType(); + + // Initialize the data type to the format + String type = format; + + String contents = getCellContents(); + + if (contents != null) { + + MinicalcDataString data = new MinicalcDataString(contents); + + // Check if it is a formula + if (data.isFormula()) { + Debug.log(Debug.INFO, " " + contents + " Is a Function Format = " + + format + "\n"); + return type; + } + + try { + // Check to see if it is a number + Double d = Double.valueOf(contents); + isNumber = true; + Debug.log(Debug.INFO, " " + contents + " Is a Number Format = " + format); + + } catch (NumberFormatException e) { + Debug.log(Debug.INFO, " " + contents + " Not a Number Format= " + format); + // no, it is not a number + isNumber = false; + } + + + if (isNumber) { + + // Numbers are Float, Currency, and Percent + if (format.equals(OfficeConstants.CELLTYPE_CURRENCY)) { + + type = OfficeConstants.CELLTYPE_CURRENCY; + + } else if (format.equals(OfficeConstants.CELLTYPE_PERCENT)) { + + type = OfficeConstants.CELLTYPE_PERCENT; + + } else { + + type = OfficeConstants.CELLTYPE_FLOAT; + } + + } else if (data.isBoolean()) { + + // Data is a Boolean type + type = OfficeConstants.CELLTYPE_BOOLEAN; + + } else if (data.isDate()) { + + // Data is a Date type + type = OfficeConstants.CELLTYPE_DATE; + + } else if (data.isTime()) { + + // Data is a Time type + type = OfficeConstants.CELLTYPE_TIME; + + } else if (data.isPercent()) { + + // Data is percent + type = OfficeConstants.CELLTYPE_PERCENT; + + } else if (data.isCurrency()) { + + // Data is a Currency type + type = OfficeConstants.CELLTYPE_CURRENCY; + + } else { + + // Data can't be float, since it isn't a number + + // We've already tried parsing it as all other data + // types, the only remaining option is a string + type = OfficeConstants.CELLTYPE_STRING; + } + } + + Debug.log(Debug.INFO, " TYPE = " + type + "\n"); + + return type; + } + + + /** + * This method returns the format of the data in the current cell. + * + * @return The format of the data in the current cell. + */ + String getCellFormatType() { + + // Set type to default data type + String type = OfficeConstants.CELLTYPE_FLOAT; + + if (cell != null) { + + // Get the attributes for the current cell + CellAttributes att = cell.getCellAttributes(); + + if (att != null) { + + // Extract the format info from the attributes + long format = att.getFormat(); + + // The cell type is stored in bits 5-8 + long cellType = format & 0x000000F0L; + + // The number of decimal places is stored in bits 1-4 + long decimals = format & 0x0000000FL; + + if (cellType == JMCconstants.FF_FORMAT_GENERIC) { + + // MiniCalc stores both Strings and Booleans + // in the generic type. We must check the contents + // to differentiate between the two. + + // Get cell's contents + String contents = getCellContents(); + + if (contents.equalsIgnoreCase("false") || + contents.equalsIgnoreCase("true")) { + + type = OfficeConstants.CELLTYPE_BOOLEAN; + + + } else { + + type = OfficeConstants.CELLTYPE_STRING; + + } + + } else if (cellType == JMCconstants.FF_FORMAT_DECIMAL) { + + type = OfficeConstants.CELLTYPE_FLOAT; + + } else if (cellType == JMCconstants.FF_FORMAT_TIME) { + + type = OfficeConstants.CELLTYPE_TIME; + + } else if (cellType == JMCconstants.FF_FORMAT_DATE) { + + type = OfficeConstants.CELLTYPE_DATE; + + } else if (cellType == JMCconstants.FF_FORMAT_CURRENCY) { + + type = OfficeConstants.CELLTYPE_CURRENCY; + + } else if (cellType == JMCconstants.FF_FORMAT_PERCENT) { + + type = OfficeConstants.CELLTYPE_PERCENT; + } + + } + } + + return type; + } + + + /** + * This method takes a <code>String</code> that contains a + * currency value and removes the $ from the <code>String</code>. + * If the dollar sign is not the first or last character of the + * input <code>String</code>, the input <code>String</code> is + * simply returned. + * + * @param contents The input <code>String</code> from which to + * remove the dollar sign. + * + * @return The input <code>String</code> minus the dollar sign. + * If the input <code>String</code> 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 <code>String</code> that contains a percent + * value and removes the % from the <code>String</code>. If the + * percent sign is not the last character of the input + * <code>String</code>, the input <code>String</code> is simply + * returned. + * + * @param contents The input String from which to remove the + * percent sign. + * + * @return The input <code>String</code> minus the percent sign. + * If the input <code>String</code> 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 <code>String</code> that contains + * a time value and converts it from MiniCalc format to StarOffice + * XML time format. + * + * @param contents The input <code>String</code> containing a + * MiniCalc time. + * + * @return The input <code>String</code> 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 <code>String</code> that contains + * a date value and converts it from MiniCalc format to StarOffice + * XML date format. + * + * @param contents The input <code>String</code> containing a + * MiniCalc date. + * + * @return The input <code>String</code> 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 + } +} + diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/minicalc/MinicalcEncoder.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/minicalc/MinicalcEncoder.java new file mode 100644 index 000000000000..60f68474554d --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/minicalc/MinicalcEncoder.java @@ -0,0 +1,585 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: MinicalcEncoder.java,v $ + * $Revision: 1.4 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +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.awt.Color; + +import java.io.ByteArrayOutputStream; +import java.io.ByteArrayInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import org.openoffice.xmerge.converter.palm.Record; +import org.openoffice.xmerge.util.Debug; +import org.openoffice.xmerge.util.IntArrayList; + +import org.openoffice.xmerge.converter.xml.sxc.SpreadsheetEncoder; +import org.openoffice.xmerge.converter.xml.sxc.Format; +import org.openoffice.xmerge.converter.xml.OfficeConstants; + +/** + * This class is used by {@link + * org.openoffice.xmerge.converter.xml.sxc.SxcDocumentSerializerImpl + * SxcDocumentSerializerImpl} to encode the MiniCalc format. + * + * @author Paul Rank + */ +final class MinicalcEncoder extends SpreadsheetEncoder { + + /** MiniCalc WorkBook to store sheets. */ + private Workbook wb; + + /** MiniCalc sheet - only one sheet can be open at a time. */ + private Worksheet ws; + + /** + * Estimate of the number of Palm pixels per character. Used for + * estimating the width of a cell on a Palm device. + */ + private final static int pixelsPerChar = 6; + + /** + * The minimum width (in pixels) that we allow a column to be set to + * on a Palm device. + */ + private final static int minWidth = 10; + + /** + * The maximum width (in pixels) that we allow a column to be set to + * on a Palm device. + */ + private final static int maxWidth = 80; + + + /** + * Constructor creates a MiniCalc WorkBook. + * + * @param log Log object for logging. + * @param name The name of the WorkBook. + * @param password The password for the WorkBook. + * + * @throws IOException If any I/O error occurs. + */ + MinicalcEncoder(String name, String password) throws IOException { + + super(name, password); + + try { + wb = new Workbook(name, password); + } + catch (JMCException e) { + Debug.log(Debug.ERROR, "new Workbook threw exception:" + e.getMessage()); + throw new IOException(e.getMessage()); + } + } + + + /** + * This method creates a WorkSheet belonging to the + * WorkBook. + * + * @param sheetName The name of the WorkSheet. + * + * @throws IOException If any I/O error occurs. + */ + public void createWorksheet(String sheetName) throws IOException { + + try { + ws = wb.createWorksheet(sheetName); + } + catch (JMCException e) { + Debug.log(Debug.ERROR, "wb.createWorksheet threw exception:" + e.getMessage()); + throw new IOException(e.getMessage()); + } + } + + + /** + * This method gets the number of sheets in the WorkBook. + * + * @return The number of sheets in the WorkBook. + */ + public int getNumberOfSheets() { + + int numSheets = wb.getNumberOfSheets(); + return numSheets; + } + + + /** + * This method encodes the MiniCalc WorkBook information + * into an palm <code>Record</code> array in MiniCalc + * database format. + * + * @return Array of <code>Record</code> holding MiniCalc + * contents. + * + * @throws IOException If any I/O error occurs. + */ + public Record[] getRecords(int sheetID) throws IOException { + + // Get the WorkSheet for the input sheetID + ws = wb.getWorksheet(sheetID); + + // Need to call ws.initWrite() before we start querying the WorkSheet + try { + ws.initWrite(); + } + catch (JMCException e) { + Debug.log(Debug.ERROR, "ws.initWrite in getRecords:" + e.getMessage()); + throw new IOException(e.getMessage()); + } + + // Get the number of records in the WorkSheet + int numRecords = ws.getNumberOfRecords(); + + // Create the Record array + Record[] allRecords = new Record[numRecords]; + + + // Get each record from the WorkSheet and store in allRecords[] + try { + for (int i = 0; i < allRecords.length; i++) { + + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + + int length = ws.writeNextRecord(bos); + + byte cBytes[] = bos.toByteArray(); + + allRecords[i] = new Record(cBytes); + } + } + catch (Exception e) { + Debug.log(Debug.ERROR, "ws.writeNextRecord in getRecords:" + e.getMessage()); + throw new IOException(e.getMessage()); + } + + return allRecords; + } + + + /** + * A cell reference in a StarOffice formula looks like + * [.C2] (for cell C2). MiniCalc is expecting cell references + * to look like C2. This method strips out the braces and + * the period. + * + * @param formula A StarOffice formula <code>String</code>. + * + * @return A MiniCalc formula <code>String</code>. + */ + protected String parseFormula(String formula) { + + StringBuffer inFormula = new StringBuffer(formula); + StringBuffer outFormula = new StringBuffer(); + + boolean inBrace = false; + boolean firstCharAfterBrace = false; + boolean firstCharAfterColon = false; + + int len = inFormula.length(); + + for (int in = 0; in < len; in++) { + switch (inFormula.charAt(in)) { + case '[': + // We are now inside a StarOffice cell reference. + // We also need to strip out the '[' + inBrace = true; + + // If the next character is a '.', we want to strip it out + firstCharAfterBrace = true; + break; + + case ']': + // We are exiting a StarOffice cell reference + // We are stripping out the ']' + inBrace = false; + break; + + case ':': + // We have a cell range reference. + // May need to strip out the leading '.' + if (inBrace) + firstCharAfterColon = true; + outFormula.append(inFormula.charAt(in)); + break; + + case '.': + if (inBrace == true) { + if (firstCharAfterBrace == false && + firstCharAfterColon == false) { + // Not the first character after the open brace. + // We have hit a separator between a sheet reference + // and a cell reference. MiniCalc uses a ! as + // this type of separator. + outFormula.append('!'); + } + else { + firstCharAfterBrace = false; + firstCharAfterColon = false; + // Since we are in a StarOffice cell reference, + // and we are the first character, we need to + // strip out the '.' + } + break; + } else { + // We hit valid data, lets add it to the formula string + outFormula.append(inFormula.charAt(in)); + break; + } + + case ';': + // StarOffice XML format uses ';' as a separator. MiniCalc (and + // many spreadsheets) use ',' as a separator instead. + outFormula.append(','); + break; + + default: + // We hit valid data, lets add it to the formula string + outFormula.append(inFormula.charAt(in)); + + // Need to make sure that firstCharAfterBrace is not true. + firstCharAfterBrace = false; + break; + } + } + + return outFormula.toString(); + } + + /** + * Add a cell to the current WorkSheet. + * + * @param row The row number of the cell. + * @param column The column number of the cell. + * @param fmt The <code>Format</code> object describing + * the appearance of this cell. + * @param cellContents The text or formula of the cell's contents. + * + * @throws IOException If any I/O error occurs. + */ + public void addCell(int row, int column, Format fmt, String cellContents) throws IOException { + + CellAttributes ca = new CellAttributes(getFormat(fmt), + fmt.getForeground(), + fmt.getBackground()); + if (cellContents.startsWith("=")) { + cellContents = parseFormula(cellContents); + Debug.log(Debug.INFO, "YAHOO Found Formula" + cellContents); + } + + CellDescriptor cellDes = new CellDescriptor(row, column, ca, cellContents); + + try { + ws.putCell(cellDes); + } + catch (JMCException jmce) { + Debug.log(Debug.ERROR, "ws.putCell threw exception: " + jmce.getMessage()); + throw new IOException(jmce.getMessage()); + } + } + + + /** + * Set the width of the columns in the WorkBook. + * + * @param columnWidths An <code>IntArrayList</code> of column + * widths. + */ + public void setColumnWidths(IntArrayList columnWidths) throws IOException { + // Get the number of columns + int numColumns = columnWidths.size(); + + // Return if there are no columns in the listr + if (numColumns == 0) { + return; + } + + // Need to set the FORM_FLAGS_NONDEFAULT flag for the column widths + // to be used in MiniCalc + long format = JMCconstants.FORM_FLAGS_NONDEFAULT; + + CellAttributes ca = new CellAttributes(format); + + try { + for (int i = 0; i < numColumns; i++) { + // Get the column width in Palm pixels + int width = columnWidths.get(i) * pixelsPerChar; + + // Check limits on column width + if (width < minWidth) { + width = minWidth; + } else if (width > maxWidth) { + width = maxWidth; + } + + // Add the column descriptor to the WorkSheet + ws.putColumn(i + 1, width, ca); + } + } + catch (JMCException jmce) { + Debug.log(Debug.ERROR, "ws.putColumn threw exception: " + jmce.getMessage()); + throw new IOException(jmce.getMessage()); + } + } + + + /** + * This method sets the format of a cell to <i>string</i>. + * + * @param format The cell format-may already contain display info, + * such as alignment or font type. + * + * @return The updated format of the cell. + */ + private long setFormatString(long format) { + + format = clearCellFormatType(format); + + // Set format to generic, since MiniCalc does not have a string type. + format = format | JMCconstants.FF_FORMAT_GENERIC; + + return format; + } + + + /** + * This method sets the format of a cell to <i>floating point</i>. + * + * @param format The cell format. May already contain + * display info, such as alignment or + * font type. + * @param decimalPlaces The number of decimal places to + * set in the floating point number. + * + * @return The updated format of the cell. + */ + private long setFormatFloat(long format, int decimalPlaces) { + + format = clearCellFormatType(format); + + // Set format to floating point with correct number of decimal places + format = format | JMCconstants.FF_FORMAT_DECIMAL | decimalPlaces; + + return format; + } + + + /** + * This method sets the format of a cell to <i>time</i>. + * + * @param format The cell format-may already contain display info, + * such as alignment or font type. + * + * @return The updated format of the cell. + */ + private long setFormatTime(long format) { + + format = clearCellFormatType(format); + + // Set format to time. + format = format | JMCconstants.FF_FORMAT_TIME; + + return format; + } + + + /** + * This method sets the format of a cell to <i>date</i>. + * + * @param format The cell format-may already contain display info, + * such as alignment or font type. + * + * @return The updated format of the cell. + */ + private long setFormatDate(long format) { + + format = clearCellFormatType(format); + + // Set format to date. + format = format | JMCconstants.FF_FORMAT_DATE; + + return format; + } + + + /** + * This method sets the format of a cell to <i>currency</i>. + * + * @param format The cell format-may already contain + * display info, such as alignment or + * font type. + * @param decimalPlaces The number of decimal places to set. + * + * @return The updated format of the cell. + */ + private long setFormatCurrency(long format, int decimalPlaces) { + + format = clearCellFormatType(format); + + // Set format to Currency with correct number of decimal places + format = format | JMCconstants.FF_FORMAT_CURRENCY | decimalPlaces; + + return format; + } + + + /** + * This method sets the format of a cell to <i>boolean</i>. + * + * @param format The cell format-may already contain display info, + * such as alignment or font type. + * + * @return The updated format of the cell. + */ + private long setFormatBoolean(long format) { + + format = clearCellFormatType(format); + + // Set format to generic, since MiniCalc does not have a Boolean type. + format = format | JMCconstants.FF_FORMAT_GENERIC; + + return format; + } + + + /** + * This method sets the format of a cell to <i>percent</i>. + * + * @param format The cell format-may already contain + * display info, such as alignment or + * font type. + * @param decimalPlaces The number of decimal places to set. + * + * @return The updated format of the cell. + */ + private long setFormatPercent(long format, int decimalPlaces) { + + format = clearCellFormatType(format); + + // Set format to Percent with correct number of decimal places + format = format | JMCconstants.FF_FORMAT_PERCENT | decimalPlaces; + + return format; + } + + + /** + * This method clears out the format bits associated with + * the type of data (<i>float</i>, <i>time</i>, etc...) in + * a cell. + * + * @param format The original format for the cell. + * + * @return The updated cell format with the bits associated + * with the type of data (float, time, etc...) + * zeroed out. + */ + private long clearCellFormatType(long format) { + + // First 4 bits are for the number of decimal places + // bits 5-8 are for the data format (float, time, etc...) + + // Clear out first 8 bits + format = format & 0xFFFFFFFFFFFFFF00L; + + return format; + } + + + /** + * Set a cell's formatting options via a separately create + * <code>Format</code> object. + * + * @param row The row number of the cell to be changed. + * @param column The column number of the cell to be changed. + * @param fmt Object containing formatting settings for + * this cell. + */ + public void setCellFormat(int row, int column, Format fmt) { + } + + + /** + * Get the names of the sheets in the WorkBook. + * + * @param sheet The required sheet. + */ + public String getSheetName(int sheet) { + return wb.getWorksheet(sheet).getName(); + } + + + /* + * This method returns a MiniCalc style format from the + * <code>Format</code> object. + */ + private long getFormat(Format fmt) + { + String category = fmt.getCategory(); + + if (category.equalsIgnoreCase(OfficeConstants.CELLTYPE_BOOLEAN)) { + return setFormatBoolean(0); + } + else if (category.equalsIgnoreCase(OfficeConstants.CELLTYPE_CURRENCY)) { + return setFormatCurrency(0, fmt.getDecimalPlaces()); + } + else if (category.equalsIgnoreCase(OfficeConstants.CELLTYPE_DATE)) { + return setFormatDate(0); + } + else if (category.equalsIgnoreCase(OfficeConstants.CELLTYPE_FLOAT)) { + return setFormatFloat(0, fmt.getDecimalPlaces()); + } + else if (category.equalsIgnoreCase(OfficeConstants.CELLTYPE_PERCENT)) { + return setFormatPercent(0, fmt.getDecimalPlaces()); + } + else if (category.equalsIgnoreCase(OfficeConstants.CELLTYPE_STRING)) { + return setFormatString(0); + } + else if (category.equalsIgnoreCase(OfficeConstants.CELLTYPE_TIME)) { + return setFormatTime(0); + } + else { + // Should never get here, but just in case + System.out.println("XXXXX Formatting information not found"); + return 0; + } + } +} + diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/minicalc/PluginFactoryImpl.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/minicalc/PluginFactoryImpl.java new file mode 100644 index 000000000000..a3b1d53abaff --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/minicalc/PluginFactoryImpl.java @@ -0,0 +1,132 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: PluginFactoryImpl.java,v $ + * $Revision: 1.3 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +package org.openoffice.xmerge.converter.xml.sxc.minicalc; + +import org.openoffice.xmerge.ConvertData; +import org.openoffice.xmerge.Document; +import org.openoffice.xmerge.DocumentSerializer; +import org.openoffice.xmerge.DocumentSerializerFactory; +import org.openoffice.xmerge.DocumentDeserializer; +import org.openoffice.xmerge.DocumentDeserializerFactory; +import org.openoffice.xmerge.DocumentMerger; +import org.openoffice.xmerge.ConverterCapabilities; +import org.openoffice.xmerge.converter.xml.sxc.DocumentMergerImpl; +import org.openoffice.xmerge.converter.xml.sxc.SxcPluginFactory; +import org.openoffice.xmerge.converter.palm.PalmDocument; +import org.openoffice.xmerge.util.registry.ConverterInfo; +import java.io.IOException; +import java.io.InputStream; + +/** + * <p>MiniCalc implementation of the <code>PluginFactory</code>. + * This encapsulates conversion of StarCalc XML format to and from + * MiniCalc format.</p> + * + * <p>The superclass produces a particular + * {@link org.openoffice.xmerge.Document Document} + * object, i.e. {@link + * org.openoffice.xmerge.converter.xml.sxc.SxcDocument + * SxcDocument} that the converters in this class works with. Thus, + * this class only implements the methods that produces the converters, + * i.e. {@link + * org.openoffice.xmerge.DocumentSerializer + * DocumentSerializer} and {@link + * org.openoffice.xmerge.DocumentDeserializer + * DocumentDeserializer}; + * as well as the {@link + * org.openoffice.xmerge.ConverterCapabilities + * ConverterCapabilities} object that is specific to this format + * conversion. That superclass also produces a {@link + * org.openoffice.xmerge.DocumentMerger DocumentMerger} + * object, i.e. {@link + * org.openoffice.xmerge.converter.xml.sxc.DocumentMergerImpl + * DocumentMergerImpl} which this class derives the functionality.</p> + */ +public final class PluginFactoryImpl extends SxcPluginFactory + implements DocumentDeserializerFactory, DocumentSerializerFactory { + + /** ConverterCapabilities object for this type of conversion. */ + private final static ConverterCapabilities converterCap = + new ConverterCapabilitiesImpl(); + + + public PluginFactoryImpl(ConverterInfo ci) { + super(ci); + } + + + /** + * Returns an instance of <code>DocumentSerializerImpl</code>, + * which is an implementation of <code>DocumentSerializer</code> + * interface. + * + * @param doc <code>Document</code> object to be + * converted/serialized. + * + * @return A <code>DocumentSerializerImpl</code> object. + */ + public DocumentSerializer createDocumentSerializer(Document doc) { + + return new SxcDocumentSerializerImpl(doc); + } + + + /** + * Returns an instance of <code>DocumentDeserializerImpl</code>, + * which is an implementation of <code>DocumentDeserializer</code> + * interface. + * + * @param cd <code>ConvertData</code> object for reading data + * which will be converted back to a + * <code>Document</code> object. + * + * @return A <code>DocumentDeserializerImpl</code> object. + */ + public DocumentDeserializer createDocumentDeserializer(ConvertData cd) { + + return new SxcDocumentDeserializerImpl(cd); + } + + + public Document createDeviceDocument(String name, InputStream is) + throws IOException { + + PalmDocument palmDoc = new PalmDocument(is); + return palmDoc; + } + + public DocumentMerger createDocumentMerger(Document doc) { + + DocumentMergerImpl merger = new DocumentMergerImpl(doc, converterCap); + return merger; + } +} + diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/minicalc/SxcDocumentDeserializerImpl.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/minicalc/SxcDocumentDeserializerImpl.java new file mode 100644 index 000000000000..1161b5522ca1 --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/minicalc/SxcDocumentDeserializerImpl.java @@ -0,0 +1,141 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: SxcDocumentDeserializerImpl.java,v $ + * $Revision: 1.3 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +package org.openoffice.xmerge.converter.xml.sxc.minicalc; + +import org.openoffice.xmerge.converter.xml.sxc.SxcDocumentDeserializer; +import org.openoffice.xmerge.converter.xml.sxc.SpreadsheetDecoder; +import org.openoffice.xmerge.converter.palm.PalmDB; +import org.openoffice.xmerge.ConvertData; +import org.openoffice.xmerge.converter.palm.PalmDocument; + +import java.io.IOException; +import java.util.Enumeration; + +/** + * <p>MiniCalc implementation of <code>DocumentDeserializer</code> + * for the {@link + * org.openoffice.xmerge.converter.xml.sxc.minicalc.PluginFactoryImpl + * PluginFactoryImpl}.</p> + * + * <p>This converts a set of files in MiniCalc PDB format to a StarOffice DOM.</p> + * + * @author Mark Murnane + */ +public final class SxcDocumentDeserializerImpl extends SxcDocumentDeserializer { + + /** + * Creates new <code>SxcDocumentDeserializerImpl</code>. + * + * @param cd <code>ConvertData</code> Input data to convert. + */ + public SxcDocumentDeserializerImpl(ConvertData cd) { + super(cd); + } + + + /** + * This method will be implemented by concrete subclasses and will + * return an application-specific decoder. + * + * @param workbook The WorkBook name. + * @param worksheetNames An array of WorkSheet names. + * @param password The password. + * + * @return An application-specific <code>SpreadsheetDecoder</code>. + */ + public SpreadsheetDecoder createDecoder(String workbook, + String[] worksheetNames, String password) throws IOException { + + return new MinicalcDecoder(workbook, worksheetNames, password); + } + + + /** + * This method will return the name of the WorkBook from the + * <code>ConvertData</code>. Allows for situations where the + * WorkBook name differs from the PDB name. + * + * Implemented in the Deserializer as the Decoder's constructor + * requires a name. + * + * @param cd The <code>ConvertData</code>. + * + * @return The name of the WorkBook. + */ + protected String getWorkbookName(ConvertData cd) + throws IOException { + + Enumeration e = cd.getDocumentEnumeration(); + PalmDocument palmDoc = (PalmDocument) e.nextElement(); + String workbookName = palmDoc.getName(); + + // Search for "-", which separates workbook from worksheet + int end = workbookName.indexOf("-"); + + if (end > 0) { + workbookName = workbookName.substring(0, end); + } + + return workbookName; + } + + + /** + * This method will return an array of WorkSheet names from the + * <code>ConvertData</code>. + * + * @param cd The <code>ConvertData</code>. + * + * @return The name of the WorkSheet. + */ + protected String[] getWorksheetNames(ConvertData cd) + throws IOException { + int numberOfPDBs = cd.getNumDocuments(); + String worksheetName[] = new String[numberOfPDBs]; + int i=0; + Enumeration e = cd.getDocumentEnumeration(); + while (e.hasMoreElements()) { + PalmDocument palmDoc = (PalmDocument) e.nextElement(); + worksheetName[i] = palmDoc.getName(); + + // Search for the "-", which seperates workbook from worksheet + int start = worksheetName[i].indexOf("-"); + + if (start != -1) { + worksheetName[i] = worksheetName[i].substring(start + 1); + } + i++; + } + + return worksheetName; + } +} + diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/minicalc/SxcDocumentSerializerImpl.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/minicalc/SxcDocumentSerializerImpl.java new file mode 100644 index 000000000000..69b584fadf09 --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/minicalc/SxcDocumentSerializerImpl.java @@ -0,0 +1,144 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: SxcDocumentSerializerImpl.java,v $ + * $Revision: 1.4 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +package org.openoffice.xmerge.converter.xml.sxc.minicalc; + +import java.awt.Color; + +import org.w3c.dom.NodeList; +import org.w3c.dom.Node; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Element; + +import java.io.IOException; + +import org.openoffice.xmerge.Document; +import org.openoffice.xmerge.ConvertData; +import org.openoffice.xmerge.ConvertException; +import org.openoffice.xmerge.converter.palm.PalmDB; +import org.openoffice.xmerge.converter.palm.Record; +import org.openoffice.xmerge.converter.palm.PalmDocument; + +import jmc.JMCconstants; + +import org.openoffice.xmerge.converter.xml.sxc.Format; +import org.openoffice.xmerge.converter.xml.sxc.SxcDocumentSerializer; + +/** + * <p>MiniCalc implementation of <code>SxcDocumentDeserializer</code> + * for the {@link + * org.openoffice.xmerge.converter.xml.sxc.minicalc.PluginFactoryImpl + * PluginFactoryImpl}.</p> + * + * <p>This converts StarOffice XML format to a set of files in + * MiniCalc PDB format.</p> + * + * @author Paul Rank + * @author Mark Murnane + */ +public final class SxcDocumentSerializerImpl extends SxcDocumentSerializer { + + + /** + * Constructor. + * + * @param document The <code>Document</code> to convert. + */ + public SxcDocumentSerializerImpl(Document document) { + super(document); + } + + + public ConvertData serialize() throws ConvertException, IOException { + + + // Get the server side document name. This value should not + // contain a path or the file extension. + String docName = sxcDoc.getName(); + + // TODO - get real values for password when implemnted in XML + // Passwords are not currently stored in StarCalc XML format. + String password = null; + + encoder = new MinicalcEncoder(docName, password); + + // get dom document + org.w3c.dom.Document domDoc = sxcDoc.getContentDOM(); + + // Traverse to the office:body element. + // There should only be one. + NodeList list = domDoc.getElementsByTagName(TAG_OFFICE_BODY); + int len = list.getLength(); + + if (len > 0) { + Node node = list.item(0); + traverseBody(node); + } + + // Get the number of sheets in the workbook + // This will equal the number of PDBs we need + ConvertData cd = new ConvertData(); + int numSheets = encoder.getNumberOfSheets(); + + for (int i = 0; i < numSheets; i++) { + + // Get records for sheet i + Record records[] = ((MinicalcEncoder) encoder).getRecords(i); + + // Get the sheet name for sheet i + String fullSheetName = new String(docName + + "-" + + encoder.getSheetName(i)); + + // Create a PalmDB object + PalmDocument palmDoc = new PalmDocument(fullSheetName, + MinicalcConstants.CREATOR_ID, + MinicalcConstants.TYPE_ID, JMCconstants.AppVersion, + PalmDB.PDB_HEADER_ATTR_BACKUP, records); + + cd.addDocument(palmDoc); + } + + + // OutputStream os = new FileOutputStream(docName); + + //pdbSet.write(os); + //os.flush(); + + //ConvertDataEntry cde = new ConvertDataOutputStream(os, docName); + //cd.addCDE(cde); + + return cd; + } + + + +} + diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/minicalc/build.xml b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/minicalc/build.xml new file mode 100644 index 000000000000..c1898c2fa3d5 --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/minicalc/build.xml @@ -0,0 +1,139 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + + DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + + Copyright 2008 by Sun Microsystems, Inc. + + OpenOffice.org - a multi-platform office productivity suite + + $RCSfile: build.xml,v $ + + $Revision: 1.4 $ + + This file is part of OpenOffice.org. + + OpenOffice.org is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + OpenOffice.org 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 version 3 for more details + (a copy is included in the LICENSE file that accompanied this code). + + You should have received a copy of the GNU Lesser General Public License + version 3 along with OpenOffice.org. If not, see + <http://www.openoffice.org/license.html> + for a copy of the LGPLv3 License. + +--> +<project name="xmrg_jooxcxs_minicalc" default="main" basedir="."> + + <!-- ================================================================= --> + <!-- settings --> + <!-- ================================================================= --> + + <!-- project prefix, used for targets and build.lst --> + <property name="prj.prefix" value="xmrg"/> + + <!-- name of this sub target used in recursive builds --> + <property name="target" value="xmrg_jooxcxs_minicalc"/> + + <!-- relative path to project directory --> + <property name="prj" value="../../../../../../../.."/> + + <!-- start of java source code package structure --> + <property name="java.dir" value="${prj}/java"/> + + <!-- path component for current java package --> + <property name="package" + value="org/openoffice/xmerge/converter/xml/sxc/minicalc"/> + + <!-- define how to handle CLASSPATH environment --> + <property name="build.sysclasspath" value="ignore"/> + + <!-- classpath settings for javac tasks --> + <path id="classpath"> + <pathelement location="${build.class}"/> + <pathelement location="${solar.jar}/parser.jar"/> + <pathelement location="${solar.jar}/jaxp.jar"/> + <pathelement location="${solar.jar}/xerces.jar"/> + <pathelement location="${solar.jar}/jmc.jar"/> + </path> + + <!-- set wether we want to compile with or without deprecation --> + <property name="deprecation" value="on"/> + + <!-- ================================================================= --> + <!-- solar build environment targets --> + <!-- ================================================================= --> + + <target name="build_dir" unless="build.dir"> + <property name="build.dir" value="${out}"/> + </target> + + <target name="solar" depends="build_dir" if="solar.update"> + <property name="solar.properties" + value="${solar.bin}/solar.properties"/> + </target> + + <target name="init" depends="solar"> + <property name="build.compiler" value="classic"/> + <property file="${solar.properties}"/> + <property file="${build.dir}/class/solar.properties"/> + </target> + + <target name="info"> + <echo message="--------------------"/> + <echo message="${target}"/> + <echo message="--------------------"/> + </target> + + + <!-- ================================================================= --> + <!-- custom targets --> + <!-- ================================================================= --> + + <!-- the main target, called in recursive builds --> + <target name="main" depends="info,prepare,compile"/> + + <!-- prepare output directories --> + <target name="prepare" depends="init" if="build.class"> + <mkdir dir="${build.dir}"/> + <mkdir dir="${build.class}"/> + </target> + + <!-- compile java sources in ${package} --> + <target name="compile" depends="prepare" if="build.class"> + <javac srcdir="${java.dir}" + destdir="${build.class}" + debug="${debug}" + deprecation="${deprecation}" + optimize="${optimize}"> + <classpath refid="classpath"/> + <include name="${package}/MinicalcConstants.java"/> + <include name="${package}/MinicalcDecoder.java"/> + <include name="${package}/MinicalcEncoder.java"/> + <include name="${package}/MinicalcDataString.java"/> + <include name="${package}/SxcDocumentDeserializerImpl.java"/> + <include name="${package}/SxcDocumentSerializerImpl.java"/> + <include name="${package}/ConverterCapabilitiesImpl.java"/> + <include name="${package}/PluginFactoryImpl.java"/> + </javac> + </target> + + <!-- clean up --> + <target name="clean" depends="prepare"> + <delete includeEmptyDirs="true"> + <fileset dir="${build.class}"> + <patternset> + <include name="${package}/*.class"/> + </patternset> + </fileset> + </delete> + </target> + +</project> + diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/minicalc/converter.xml b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/minicalc/converter.xml new file mode 100644 index 000000000000..263021fa324d --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/minicalc/converter.xml @@ -0,0 +1,47 @@ +<?xml version="1.0"?> +<!-- + + DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + + Copyright 2008 by Sun Microsystems, Inc. + + OpenOffice.org - a multi-platform office productivity suite + + $RCSfile: converter.xml,v $ + + $Revision: 1.4 $ + + This file is part of OpenOffice.org. + + OpenOffice.org is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + OpenOffice.org 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 version 3 for more details + (a copy is included in the LICENSE file that accompanied this code). + + You should have received a copy of the GNU Lesser General Public License + version 3 along with OpenOffice.org. If not, see + <http://www.openoffice.org/license.html> + for a copy of the LGPLv3 License. + +--> +<converters> + <converter type="staroffice/sxc" version="1.0"> + <converter-display-name> + Minicalc 6.4 + </converter-display-name> + <converter-description> + StarCalc XML to/from Minicalc 6.4 conversion + </converter-description> + <converter-vendor>OpenOffice.org</converter-vendor> + <converter-class-impl> + org.openoffice.xmerge.converter.xml.sxc.minicalc.PluginFactoryImpl + </converter-class-impl> + <converter-target type="application/x-minicalc" /> + </converter> +</converters> + diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/minicalc/makefile.mk b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/minicalc/makefile.mk new file mode 100644 index 000000000000..78d4211e162d --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/minicalc/makefile.mk @@ -0,0 +1,36 @@ +#*************************************************************************** +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2008 by Sun Microsystems, Inc. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# $RCSfile: makefile.mk,v $ +# +# $Revision: 1.3 $ +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org 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 version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#*************************************************************************** + +TARGET=xmrg_jooxcxs_minicalc +PRJ=../../../../../../../.. + +.INCLUDE : ant.mk +ALLTAR: ANTBUILD diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/minicalc/package.html b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/minicalc/package.html new file mode 100644 index 000000000000..191ed77ec344 --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/minicalc/package.html @@ -0,0 +1,55 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- + + DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + + Copyright 2008 by Sun Microsystems, Inc. + + OpenOffice.org - a multi-platform office productivity suite + + $RCSfile: package.html,v $ + + $Revision: 1.3 $ + + This file is part of OpenOffice.org. + + OpenOffice.org is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + OpenOffice.org 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 version 3 for more details + (a copy is included in the LICENSE file that accompanied this code). + + You should have received a copy of the GNU Lesser General Public License + version 3 along with OpenOffice.org. If not, see + <http://www.openoffice.org/license.html> + for a copy of the LGPLv3 License. + +--> +<html> +<head> +<title>org.openoffice.xmerge.converter.xml.sxc.minicalc package</title> +</head> + +<body bgcolor="white"> + +<p>Provides the tools for doing the conversion of StarWriter XML to +and from MiniCalc format.</p> + +<p>It follows the {@link org.openoffice.xmerge} framework for the conversion process.</p> + +<p>Since it converts to/from a Palm application format, these converters +follow the <a href=../../../../converter/palm/package-summary.html#streamformat> +PalmDB stream format</a> for writing out to the Palm sync client or reading +in from the Palm sync client.</p> + +<p>Note that <code>PluginFactoryImpl</code> also provides a +<code>DocumentMerger</code> object, i.e. {@link org.openoffice.xmerge.converter.xml.sxw.aportisdoc.DocumentMergerImpl DocumentMergerImpl}. +This functionality was derived from its superclass +{@link org.openoffice.xmerge.converter.xml.sxw.SxwPluginFactory SxwPluginFactory}.</p> + +</body> +</html> diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/package.html b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/package.html new file mode 100644 index 000000000000..258d0158af1d --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/package.html @@ -0,0 +1,42 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- + + DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + + Copyright 2008 by Sun Microsystems, Inc. + + OpenOffice.org - a multi-platform office productivity suite + + $RCSfile: package.html,v $ + + $Revision: 1.3 $ + + This file is part of OpenOffice.org. + + OpenOffice.org is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + OpenOffice.org 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 version 3 for more details + (a copy is included in the LICENSE file that accompanied this code). + + You should have received a copy of the GNU Lesser General Public License + version 3 along with OpenOffice.org. If not, see + <http://www.openoffice.org/license.html> + for a copy of the LGPLv3 License. + +--> +<html> +<head> +<title>org.openoffice.xmerge.converter.xml.sxc package</title> +</head> + +<body bgcolor="white"> +<p>Provides base implementation of StarCalc XML conversion to and from +different "Device" <code>Document</code> formats.</p> + +</body> +</html> diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/ConverterCapabilitiesImpl.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/ConverterCapabilitiesImpl.java new file mode 100644 index 000000000000..2e51ee685e21 --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/ConverterCapabilitiesImpl.java @@ -0,0 +1,116 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: ConverterCapabilitiesImpl.java,v $ + * $Revision: 1.3 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +package org.openoffice.xmerge.converter.xml.sxc.pexcel; + +import org.openoffice.xmerge.ConverterCapabilities; +import org.openoffice.xmerge.converter.xml.OfficeConstants; + + +/** + * <p>Pocket Excel implementation of <code>ConverterCapabilities</code> for + * the {@link + * org.openoffice.xmerge.converter.xml.sxc.pexcel.PluginFactoryImpl + * PluginFactoryImpl}.</p> + * + * <p>Used with StarCalc SXC to/from Pocket Excel conversions. The + * <code>ConverterCapibilies</code> specify which "Office" + * <code>Document</code> tags and attributes are supported on the + * "Device" <code>Document</code> format.</p> + */ +public final class ConverterCapabilitiesImpl + implements ConverterCapabilities { + + public boolean canConvertTag(String tag) { + + if (OfficeConstants.TAG_OFFICE_BODY.equals(tag)) + return true; + else if (OfficeConstants.TAG_PARAGRAPH.equals(tag)) + return true; + else if (OfficeConstants.TAG_TABLE.equals(tag)) + return true; + else if (OfficeConstants.TAG_TABLE_ROW.equals(tag)) + return true; + else if (OfficeConstants.TAG_TABLE_COLUMN.equals(tag)) + return false; + // TODO - we currently do not handle the table column tag + else if (OfficeConstants.TAG_TABLE_SCENARIO.equals(tag)) + return false; + // TODO - we currently do not handle the table scenario tag + else if (OfficeConstants.TAG_TABLE_CELL.equals(tag)) + return true; + + return false; + } + + public boolean canConvertAttribute(String tag, + String attribute) { + + if (OfficeConstants.TAG_TABLE.equals(tag)) { + + if (OfficeConstants.ATTRIBUTE_TABLE_NAME.equals(attribute)) + return true; + + } else if (OfficeConstants.TAG_TABLE_CELL.equals(tag)) { + + if (OfficeConstants.ATTRIBUTE_TABLE_VALUE_TYPE.equals(attribute)) + return true; + else if (OfficeConstants.ATTRIBUTE_TABLE_FORMULA. + equals(attribute)) + return true; + else if (OfficeConstants.ATTRIBUTE_TABLE_VALUE.equals(attribute)) + return true; + else if (OfficeConstants.ATTRIBUTE_TABLE_BOOLEAN_VALUE. + equals(attribute)) + return true; + else if (OfficeConstants.ATTRIBUTE_TABLE_CURRENCY. + equals(attribute)) + return true; + else if (OfficeConstants.ATTRIBUTE_TABLE_TIME_VALUE. + equals(attribute)) + return true; + else if (OfficeConstants.ATTRIBUTE_TABLE_DATE_VALUE. + equals(attribute)) + return true; + else if (OfficeConstants.ATTRIBUTE_TABLE_NUM_COLUMNS_REPEATED. + equals(attribute)) + return true; + + } else if (OfficeConstants.TAG_TABLE_ROW.equals(tag)) { + + if (OfficeConstants.ATTRIBUTE_TABLE_NUM_ROWS_REPEATED. + equals(attribute)) + return true; + } + + return false; + } +} + diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/PluginFactoryImpl.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/PluginFactoryImpl.java new file mode 100644 index 000000000000..389b0169e49b --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/PluginFactoryImpl.java @@ -0,0 +1,133 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: PluginFactoryImpl.java,v $ + * $Revision: 1.5 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +package org.openoffice.xmerge.converter.xml.sxc.pexcel; + +import java.io.IOException; +import java.io.InputStream; + +import org.openoffice.xmerge.ConvertData; +import org.openoffice.xmerge.Document; +import org.openoffice.xmerge.DocumentSerializer; +import org.openoffice.xmerge.DocumentSerializerFactory; +import org.openoffice.xmerge.DocumentDeserializer; +import org.openoffice.xmerge.DocumentDeserializerFactory; +import org.openoffice.xmerge.DocumentMerger; +import org.openoffice.xmerge.ConverterCapabilities; +import org.openoffice.xmerge.converter.xml.sxc.DocumentMergerImpl; +import org.openoffice.xmerge.converter.xml.sxc.SxcPluginFactory; +import org.openoffice.xmerge.converter.xml.sxc.pexcel.records.Workbook; +import org.openoffice.xmerge.util.registry.ConverterInfo; + +/** + * <p>Pocket Excel implementation of the <code>PluginFactory</code>. + * This encapsulates conversion of StarCalc XML format to and from + * Pocket Excel format.</p> + * + * <p>The superclass produces a particular + * {@link org.openoffice.xmerge.Document Document} + * object, i.e. {@link + * org.openoffice.xmerge.converter.xml.sxc.SxcDocument + * SxcDocument} that the converters in this class works with. Thus, + * this class only implements the methods that produces the converters, + * i.e. {@link + * org.openoffice.xmerge.DocumentSerializer + * DocumentSerializer} and {@link + * org.openoffice.xmerge.DocumentDeserializer + * DocumentDeserializer}; + * as well as the {@link + * org.openoffice.xmerge.ConverterCapabilities + * ConverterCapabilities} object that is specific to this format + * conversion. That superclass also produces a {@link + * org.openoffice.xmerge.DocumentMerger DocumentMerger} + * object, i.e. {@link + * org.openoffice.xmerge.converter.xml.sxc.DocumentMergerImpl + * DocumentMergerImpl} which this class derives the functionality.</p> + */ +public final class PluginFactoryImpl extends SxcPluginFactory + implements DocumentDeserializerFactory, DocumentSerializerFactory { + + /** ConverterCapabilities object for this type of conversion. */ + private final static ConverterCapabilities converterCap = + new ConverterCapabilitiesImpl(); + + + public PluginFactoryImpl(ConverterInfo ci) { + super(ci); + } + + + /** + * Returns an instance of <code>DocumentSerializerImpl</code>, + * which is an implementation of <code>DocumentSerializer</code> + * interface. + * + * @param doc <code>Document</code> object to be + * converted/serialized. + * + * @return A <code>DocumentSerializerImpl</code> object. + */ + public DocumentSerializer createDocumentSerializer(Document doc) { + + return new SxcDocumentSerializerImpl(doc); + } + + + /** + * Returns an instance of <code>DocumentDeserializerImpl</code>, + * which is an implementation of <code>DocumentDeserializer</code> + * interface. + * + * @param cd <code>ConvertData</code> object for reading data + * which will be converted back to a + * <code>Document</code> object. + * + * @return A <code>DocumentDeserializerImpl</code> object. + */ + public DocumentDeserializer createDocumentDeserializer(ConvertData cd) { + + return new SxcDocumentDeserializerImpl(cd); + } + + + public Document createDeviceDocument(String name, InputStream is) + throws IOException { + + Workbook wb = new Workbook(name, is); + return wb; + } + + public DocumentMerger createDocumentMerger(Document doc) { + + DocumentMergerImpl merger = new DocumentMergerImpl(doc, converterCap); + return merger; + } +} + diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/PocketExcelConstants.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/PocketExcelConstants.java new file mode 100644 index 000000000000..22382e0faf8c --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/PocketExcelConstants.java @@ -0,0 +1,71 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: PocketExcelConstants.java,v $ + * $Revision: 1.3 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + + +package org.openoffice.xmerge.converter.xml.sxc.pexcel; + + +/** + * Interface defining constants for Pocket Excel attributes. + * + * @author Martin Maher + */ +public interface PocketExcelConstants { + /** File extension for Pocket Word files. */ + public static final String FILE_EXTENSION = ".pxl"; + + /** Constants for pexcel BIFF records */ + public static final int BLANK_CELL = 0x01; + public static final int NUMBER_CELL = 0x03; + public static final int LABEL_CELL = 0x04; + public static final int BOOLERR_CELL = 0x05; + public static final int FORMULA_CELL = 0x06; + public static final int FORMULA_STRING = 0x07; + public static final int ROW_DESCRIPTION = 0x08; + public static final int BOF_RECORD = 0x09; + public static final int EOF_MARKER = 0x0A; + public static final int DEFINED_NAME = 0x18; + public static final int CURRENT_SELECTION = 0x1D; + public static final int NUMBER_FORMAT = 0x1E; + public static final int DEFAULT_ROW_HEIGHT = 0x25; + public static final int FONT_DESCRIPTION = 0x31; + public static final int WINDOW_INFO = 0x3D; + public static final int SHEET_WINDOW_INFO = 0x3E; + public static final int PANE_INFO = 0x41; + public static final int CODEPAGE = 0x42; + public static final int DEF_COL_WIDTH = 0x55; + public static final int COLINFO = 0x7D; + public static final int BOUND_SHEET = 0x85; + public static final int EXTENDED_FORMAT = 0xE0; + + /** Colour lookup table for mapping pexcel color values + (See util/ColourConverter.java */ + public short cLookup[] = { 0, 14, 15, 1, 2, 3, 4, 7, 6, 5, 8, 9, 10, 13, 12, 11 }; +} diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/PocketExcelDecoder.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/PocketExcelDecoder.java new file mode 100644 index 000000000000..c5f263766da2 --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/PocketExcelDecoder.java @@ -0,0 +1,449 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: PocketExcelDecoder.java,v $ + * $Revision: 1.23 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +package org.openoffice.xmerge.converter.xml.sxc.pexcel; + +import java.io.ByteArrayOutputStream; +import java.io.ByteArrayInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Stack; +import java.util.LinkedList; +import java.util.Vector; +import java.util.Enumeration; +import java.util.NoSuchElementException; + +import org.openoffice.xmerge.ConvertData; +import org.openoffice.xmerge.converter.xml.OfficeConstants; +import org.openoffice.xmerge.util.Debug; +import org.openoffice.xmerge.converter.xml.sxc.SheetSettings; +import org.openoffice.xmerge.converter.xml.sxc.BookSettings; +import org.openoffice.xmerge.converter.xml.sxc.SxcDocumentDeserializer; +import org.openoffice.xmerge.converter.xml.sxc.SpreadsheetDecoder; +import org.openoffice.xmerge.converter.xml.sxc.Format; +import org.openoffice.xmerge.converter.xml.sxc.NameDefinition; +import org.openoffice.xmerge.converter.xml.sxc.ColumnRowInfo; +import org.openoffice.xmerge.converter.xml.sxc.pexcel.records.*; + +/** + * This class is used by {@link + * org.openoffice.xmerge.converter.xml.sxc.SxcDocumentDeserializerImpl} + * SxcDocumentDeserializerImpl} to decode the Pocket Excel format. + * + * @author Paul Rank + */ +final class PocketExcelDecoder extends SpreadsheetDecoder { + + private Workbook wb; + private Worksheet ws; + private CellValue cell; + private int maxRows = 0; + private int maxCols = 0; + private int wsIndex; + private Enumeration cellValue; + private Format fmt = null; + + /** + * Constructor creates a Pocket Excel WorkBook. + * + * @param name The name of the WorkBook. + * @param worksheetNames set of Strings equivalent to the worksheets + * contained in the workbook + * @param password The password for the workBook. + * + * @throws IOException If any I/O error occurs. + */ + PocketExcelDecoder(String name, String[] worksheetNames, String password) throws IOException { + super(name, password); + + fmt = new Format(); + } + + + /** + * This method takes a <code>ConvertData</code> as input and + * converts it into a PocketWord WorkSheet. The WorkSheet is then + * added to the WorkBook. + * + * @param InputStream An <code>ConvertData</code> containing a + * Pocket Excel WorkSheet. + * + * @throws IOException If any I/O error occurs. + */ + public void addDeviceContent(ConvertData cd) throws IOException { + + Enumeration e = cd.getDocumentEnumeration(); + wb = (Workbook) e.nextElement(); + } + + + /** + * This method returns the number of spreadsheets + * stored in the WorkBook. + * + * @return The number of sheets in the WorkBook. + */ + public int getNumberOfSheets() { + + Vector v = wb.getWorksheetNames(); + Debug.log(Debug.TRACE,"Total Number of Sheets : " + v.size()); + return (v.size()); + } + + /** + * This method returns the number of spreadsheets + * stored in the WorkBook. + * + * @return The number of sheets in the WorkBook. + */ + public Enumeration getNameDefinitions() { + + Enumeration e = wb.getDefinedNames(); + Vector nameDefinitionVector = new Vector(); + while(e.hasMoreElements()) { + DefinedName dn = (DefinedName)e.nextElement(); + NameDefinition nameDefinitionEntry = dn.getNameDefinition(); + nameDefinitionVector.add(nameDefinitionEntry); + } + Debug.log(Debug.TRACE,"Getting " + nameDefinitionVector.size() + " DefinedName records"); + return (nameDefinitionVector.elements()); + } + + /** + * This method returns an enumeration of Settings object(s), + * one for each worksheet + * + * @return An enumerattion of <code>Settings</code> + */ + public BookSettings getSettings() { + + return (wb.getSettings()); + } + /** + * This method returns the number of spreadsheets + * stored in the WorkBook. + * + * @return The number of sheets in the WorkBook. + */ + public Enumeration getColumnRowInfos() { + + Vector colRowVector = new Vector(); + + // Collect Columns from worksheet and add them to the vector + for(Enumeration e = ws.getColInfos();e.hasMoreElements();) { + ColInfo ci = (ColInfo)e.nextElement(); + int repeated = ci.getLast() - ci.getFirst() + 1; + ColumnRowInfo colInfo = new ColumnRowInfo( ci.getColWidth(), + repeated, + ColumnRowInfo.COLUMN); + colRowVector.add(colInfo); + } + + // Collect Rows from worksheet and add them to the vector + for(Enumeration e = ws.getRows();e.hasMoreElements();) { + Row rw = (Row)e.nextElement(); + // We will use the repeat field for number (unlike columns rows + // cannot be repeated, we have unique record for each row in pxl + int repeated = rw.getRowNumber(); + ColumnRowInfo rowInfo = new ColumnRowInfo( rw.getRowHeight(), + repeated, + ColumnRowInfo.ROW); + colRowVector.add(rowInfo); + } + Debug.log(Debug.TRACE,"Getting " + colRowVector.size() + " ColRowInfo records"); + return (colRowVector.elements()); + } + + /** + * 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 { + Debug.log(Debug.TRACE,"Setting to worksheet : " + sheetIndex); + ws = wb.getWorksheet(sheetIndex); + cellValue = ws.getCellEnumerator(); + wsIndex = sheetIndex; + while(goToNextCell()) { + maxRows = Math.max(maxRows, cell.getRow()); + maxCols = Math.max(maxCols, cell.getCol()); + } + cellValue = ws.getCellEnumerator(); + Debug.log(Debug.TRACE,"Max Cols : " + maxCols + " MaxRows : " + maxRows); + } + + + /** + * This method returns the name of the current spreadsheet. + * + * @return The name of the current WorkSheet. + */ + public String getSheetName() { + + String wsName = wb.getSheetName(wsIndex); + Debug.log(Debug.TRACE,"The name of the current Worksheet is : " + wsName); + return wsName; + } + + + /** + * 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 success = false; + + try { + cell = (CellValue) cellValue.nextElement(); + Debug.log(Debug.TRACE,"Current Cell : " + cell.getString()); + readCellFormat(); + success = true; + } catch (NoSuchElementException e) { + Debug.log(Debug.TRACE,"Could't find current cell"); + } + + return success; + } + + + /** + * 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.getRow(); + Debug.log(Debug.TRACE,"cell row is " + row); + } + 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.getCol(); + Debug.log(Debug.TRACE,"cell col is " + col); + } + 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 = new String(""); + + if (cell != null) { + try { + contents = cell.getString(); + if (contents.startsWith("=")) { + contents = parseFormula(contents); + } + } + catch (IOException e) { + System.err.println("Could Not retrieve Cell contents"); + System.err.println("Setting contents of cell(" + cell.getRow() + + "," + cell.getCol() + ") to an empty string"); + System.err.println("Error msg: " + e.getMessage()); + } + } + + return contents; + } + + /** + * <p>This method takes a formula and parses it into + * StarOffice XML formula format.</p> + * + * <p>Many spreadsheets use ',' as a separator. + * StarOffice XML format uses ';' as a separator instead.</p> + * + * <p>Many spreadsheets use '!' as a separator when refencing + * a cell in a different sheet.</p> + * + * <blockquote> + * Example: =sheet1!A1 + * </blockquote> + * + * <p>StarOffice XML format uses '.' as a separator instead.</p> + * + * <blockquote> + * Example: =sheet1.A1 + * </blockquote> + * + * @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 contents of the current cell. + * + * @return The contents of the current cell. Returns + * null if no cell is currently selected. + */ + public String getCellValue() { + + String contents = new String(""); + + if (cell != null) { + try { + contents = ((Formula)cell).getValue(); + } + catch (IOException e) { + System.err.println("Could Not retrieve Cell value"); + System.err.println("Setting value of cell(" + cell.getRow() + + "," + cell.getCol() + ") to an empty string"); + System.err.println("Error msg: " + e.getMessage()); + } + } + return contents; + } + + /** + * <p>This method returns the type of the data in the current cell. + * Currently the only type supported is String.</p> + * + * @return The type of the data in the current cell. + */ + public String getCellDataType() { + + String type = OfficeConstants.CELLTYPE_STRING; + + if(cell instanceof FloatNumber) + type = OfficeConstants.CELLTYPE_FLOAT; + if(cell instanceof Formula) + type = OfficeConstants.CELLTYPE_FLOAT; + + return type; + } + + + /** + * 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() throws IOException { + + fmt.clearFormatting(); + + Debug.log(Debug.TRACE," ixfe for Current Cell " + cell.getIxfe()); + ExtendedFormat xf = wb.getExtendedFormat(cell.getIxfe()); + Debug.log(Debug.TRACE," ixfnt for Current Cell " + xf.getFontIndex()); + FontDescription fd = wb.getFontDescription(xf.getFontIndex()); + + fmt.setAttribute(Format.ITALIC, fd.isItalic()); + fmt.setAttribute(Format.BOLD, fd.isBold()); + fmt.setAttribute(Format.UNDERLINE, fd.isUnderline()); + fmt.setForeground(fd.getForeground()); + + fmt.setBackground(xf.getBackground()); + fmt.setAlign(xf.getAlign()); + fmt.setVertAlign(xf.getVertAlign()); + fmt.setAttribute(Format.WORD_WRAP, xf.isWordWrap()); + + fmt.setAttribute(Format.TOP_BORDER, xf.isBorder(ExtendedFormat.TOP_BORDER)); + fmt.setAttribute(Format.BOTTOM_BORDER, xf.isBorder(ExtendedFormat.BOTTOM_BORDER)); + fmt.setAttribute(Format.RIGHT_BORDER, xf.isBorder(ExtendedFormat.RIGHT_BORDER)); + fmt.setAttribute(Format.LEFT_BORDER, xf.isBorder(ExtendedFormat.LEFT_BORDER)); + + fmt.setFontName(fd.getFont()); + fmt.setFontSize(fd.getFontSize()); + + fmt.setCategory(getCellDataType()); + + } +} + diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/PocketExcelEncoder.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/PocketExcelEncoder.java new file mode 100644 index 000000000000..44e9de76394d --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/PocketExcelEncoder.java @@ -0,0 +1,298 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: PocketExcelEncoder.java,v $ + * $Revision: 1.17 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +package org.openoffice.xmerge.converter.xml.sxc.pexcel; + +import java.io.ByteArrayOutputStream; +import java.io.ByteArrayInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.Vector; + +import org.openoffice.xmerge.util.Debug; +import org.openoffice.xmerge.util.IntArrayList; + +import org.openoffice.xmerge.converter.xml.sxc.SpreadsheetEncoder; +import org.openoffice.xmerge.converter.xml.sxc.Format; +import org.openoffice.xmerge.converter.xml.sxc.BookSettings; +import org.openoffice.xmerge.converter.xml.sxc.SheetSettings; +import org.openoffice.xmerge.converter.xml.sxc.NameDefinition; +import org.openoffice.xmerge.converter.xml.OfficeConstants; +import org.openoffice.xmerge.converter.xml.sxc.pexcel.records.Workbook; + +/** + * This class is used by {@link + * org.openoffice.xmerge.converter.xml.sxc.SxcDocumentSerializerImpl + * SxcDocumentSerializerImpl} to encode the Pocket Excel format. + * + * @author Martin Maher + */ +final class PocketExcelEncoder extends SpreadsheetEncoder { + + private Workbook wb; + + /** + * Constructor creates a Pocket Excel WorkBook. + * + * @param name The name of the WorkBook. + * @param password The password for the WorkBook. + * + * @throws IOException If any I/O error occurs. + */ + PocketExcelEncoder(String name, String password) throws IOException { + + super(name, password); + wb = new Workbook(name); + + } + + + /** + * This method creates a WorkSheet belonging to the + * WorkBook. + * + * @param sheetName The name of the WorkSheet. + * + * @throws IOException If any I/O error occurs. + */ + public void createWorksheet(String sheetName) throws IOException { + + wb.addWorksheet(sheetName); + } + + + /** + * This method gets the number of sheets in the WorkBook. + * + * @return The number of sheets in the WorkBook. + */ + public int getNumberOfSheets() { + + Vector v = wb.getWorksheetNames(); + return (v.size()); + } + + + /** + * This method returns the Workbook created. + * + * @return Returns a <code>Workbook</code> + * + * @throws IOException If any I/O error occurs. + */ + public Workbook getWorkbook() throws IOException { + + return wb; + } + + /** + * This method converts a String containing a formula in infix notation + * to a String in Reverse Polish Notation (RPN) + * + * @return a parsed pexcel formula in RPN + */ + protected String parseFormula(String formula) { + + Debug.log(Debug.TRACE,"Strip Formula (Before) : " + formula); + + StringBuffer inFormula = new StringBuffer(formula); + StringBuffer outFormula = new StringBuffer(); + + boolean inBrace = false; + boolean firstCharAfterBrace = false; + boolean firstCharAfterColon = false; + + int len = inFormula.length(); + + for (int in = 0; in < len; in++) { + switch (inFormula.charAt(in)) { + case '[': + // We are now inside a StarOffice cell reference. + // We also need to strip out the '[' + Debug.log(Debug.TRACE,"brace Found"); + inBrace = true; + + // If the next character is a '.', we want to strip it out + firstCharAfterBrace = true; + break; + + case ']': + // We are exiting a StarOffice cell reference + // We are stripping out the ']' + inBrace = false; + break; + case '.': + if (inBrace == true && (firstCharAfterBrace == true || + firstCharAfterColon == true) ) { + + Debug.log(Debug.TRACE,"dot Found and in brace"); + // Since we are in a StarOffice cell reference, + // and we are the first character, we need to + // strip out the '.' + firstCharAfterBrace = false; + firstCharAfterColon = false; + + } else if(firstCharAfterColon == true) { + firstCharAfterColon = false; + } else { + outFormula.append(inFormula.charAt(in)); + } + break; + + case ':': + // We have a cell range reference. + // May need to strip out the leading '.' + firstCharAfterColon = true; + outFormula.append(inFormula.charAt(in)); + break; + + case ';': + // StarOffice XML format uses ';' as a separator. MiniCalc (and + // many spreadsheets) use ',' as a separator instead. + outFormula.append(','); + break; + + default: + // We hit valid data, lets add it to the formula string + outFormula.append(inFormula.charAt(in)); + + // Need to make sure that firstCharAfterBrace is not true. + firstCharAfterBrace = false; + break; + } + } + + Debug.log(Debug.TRACE,"Strip Formula (After) : " + outFormula); + return outFormula.toString(); + } + + /** + * Add a cell to the current WorkSheet. + * + * @param row The row number of the cell. + * @param column The column number of the cell. + * @param fmt The <code>Format</code> object describing + * the appearance of this cell. + * @param cellContents The text or formula of the cell's contents. + * + * @throws IOException If any I/O error occurs. + */ + public void addCell(int row, int column, Format fmt, String cellContents) throws IOException { + + if (cellContents.startsWith("=")) { + cellContents = parseFormula(cellContents); + Debug.log(Debug.TRACE,"Parsing Formula " + cellContents); + } + wb.addCell(row, column, fmt, cellContents); + } + + + /** + * Set the width of the columns in the WorkBook. + * + * @param columnWidths An <code>IntArrayList</code> of column + * widths. + */ + public void setColumnRows(Vector columnRows) throws IOException { + + wb.addColInfo(columnRows); + } + + /** + * Set the width of the columns in the WorkBook. + * + * @param columnWidths An <code>IntArrayList</code> of column + * widths. + */ + public void setNameDefinition(NameDefinition nd) throws IOException { + + String parsedName = nd.getDefinition(); + nd.setDefinition(parseFormula(parsedName)); + + wb.addNameDefinition(nd); + } + + /** + * Set the width of the columns in the WorkBook. + * + * @param columnWidths An <code>IntArrayList</code> of column + * widths. + */ + public void addSettings(BookSettings s) throws IOException { + + wb.addSettings(s); + } + + /** + * This method sets the format of a cell to <i>string</i>. + * + * @param format The cell format-may already contain display info, + * such as alignment or font type. + * + * @return The updated format of the cell. + */ + private long setFormatString(long format) { + + return 0; + } + + + /** + * Set a cell's formatting options via a separately create + * <code>Format</code> object. + * + * @param row The row number of the cell to be changed. + * @param column The column number of the cell to be changed. + * @param fmt Object containing formatting settings for + * this cell. + */ + public void setCellFormat(int row, int column, Format fmt) { + Debug.log(Debug.TRACE,"bold : " + fmt.getAttribute(Format.BOLD) + + ",Italic : " + fmt.getAttribute(Format.ITALIC) + + ",Underline : " + fmt.getAttribute(Format.UNDERLINE)); + } + + + /** + * Get the names of the sheets in the WorkBook. + * + * @param sheet The required sheet. + */ + public String getSheetName(int sheet) { + + Vector v = wb.getWorksheetNames(); + String wsName = (String) (v.elementAt(sheet)); + + return wsName; + } + + +} + diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/SxcDocumentDeserializerImpl.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/SxcDocumentDeserializerImpl.java new file mode 100644 index 000000000000..5bca66b92b92 --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/SxcDocumentDeserializerImpl.java @@ -0,0 +1,132 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: SxcDocumentDeserializerImpl.java,v $ + * $Revision: 1.6 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +package org.openoffice.xmerge.converter.xml.sxc.pexcel; + +import java.io.IOException; +import java.util.Enumeration; +import java.util.Vector; + +import org.openoffice.xmerge.util.Debug; +import org.openoffice.xmerge.ConvertData; +import org.openoffice.xmerge.converter.xml.sxc.SpreadsheetDecoder; +import org.openoffice.xmerge.converter.xml.sxc.SxcDocumentDeserializer; +import org.openoffice.xmerge.converter.xml.sxc.pexcel.PocketExcelDecoder; +import org.openoffice.xmerge.converter.xml.sxc.pexcel.records.Workbook; + + +/** + * <p>Pocket Excel implementation of <code>DocumentDeserializer</code> + * for the {@link + * org.openoffice.xmerge.converter.xml.sxc.pexcel.PluginFactoryImpl + * PluginFactoryImpl}.</p> + * + * <p>This converts a set of files in Pocket Excel PXL format to a StarOffice DOM.</p> + * + * @author Mark Murnane + */ +public final class SxcDocumentDeserializerImpl extends SxcDocumentDeserializer { + + /** + * Creates new <code>SxcDocumentDeserializerImpl</code>. + * + * @param cd <code>ConvertData</code> Input data to convert. + */ + public SxcDocumentDeserializerImpl(ConvertData cd) { + super(cd); + } + + + /** + * This method will be implemented by concrete subclasses and will + * return an application-specific decoder. + * + * @param workbook The WorkBook name. + * @param worksheetNames An array of WorkSheet names. + * @param password The password. + * + * @return An application-specific <code>SpreadsheetDecoder</code>. + */ + public SpreadsheetDecoder createDecoder(String workbook, + String[] worksheetNames, String password) throws IOException { + + return new PocketExcelDecoder(workbook, worksheetNames, password); + } + + + /** + * This method will return the name of the WorkBook from the + * <code>ConvertData</code>. Allows for situations where the + * WorkBook name differs from the PDB name. + * + * Implemented in the Deserializer as the Decoder's constructor + * requires a name. + * + * @param cd The <code>ConvertData</code>. + * + * @return The name of the WorkBook. + */ + protected String getWorkbookName(ConvertData cd) + throws IOException { + + Enumeration e = cd.getDocumentEnumeration(); + Workbook wb = (Workbook) e.nextElement(); + + String workbookName = wb.getName(); + return workbookName; + } + + + /** + * This method will return an array of WorkSheet names from the + * <code>ConvertData</code>. + * + * @param cd The <code>ConvertData</code>. + * + * @return The name of the WorkSheet. + */ + protected String[] getWorksheetNames(ConvertData cd) + throws IOException { + + Enumeration e = cd.getDocumentEnumeration(); + Workbook wb = (Workbook) e.nextElement(); + Vector v = wb.getWorksheetNames(); + e = v.elements(); + String worksheetNames[] = new String[v.size()]; + int i = 0; + while(e.hasMoreElements()) { + worksheetNames[i] = (String) e.nextElement(); + Debug.log(Debug.TRACE,"Worksheet Name : " + worksheetNames[i]); + i++; + } + return worksheetNames; + } +} + diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/SxcDocumentSerializerImpl.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/SxcDocumentSerializerImpl.java new file mode 100644 index 000000000000..ff3de98c3478 --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/SxcDocumentSerializerImpl.java @@ -0,0 +1,139 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: SxcDocumentSerializerImpl.java,v $ + * $Revision: 1.7 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +package org.openoffice.xmerge.converter.xml.sxc.pexcel; + +import java.awt.Color; + +import org.w3c.dom.NodeList; +import org.w3c.dom.Node; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Element; + +import java.io.IOException; + +import org.openoffice.xmerge.Document; +import org.openoffice.xmerge.ConvertData; +import org.openoffice.xmerge.ConvertException; +import org.openoffice.xmerge.converter.xml.sxc.Format; +import org.openoffice.xmerge.converter.xml.sxc.SxcDocumentSerializer; +import org.openoffice.xmerge.converter.xml.sxc.pexcel.records.Workbook; +import org.openoffice.xmerge.converter.xml.StyleCatalog; + +/** + * <p>Pocket Excel implementation of <code>SxcDocumentDeserializer</code> + * for the {@link + * org.openoffice.xmerge.converter.xml.sxc.pexcel.PluginFactoryImpl + * PluginFactoryImpl}.</p> + * + * <p>This converts StarOffice XML format to a set of files in + * Pocket Excel PXL format.</p> + * + * @author Paul Rank + * @author Mark Murnane + */ +public final class SxcDocumentSerializerImpl extends SxcDocumentSerializer { + + + /** + * Constructor. + * + * @param document The <code>Document</code> to convert. + */ + public SxcDocumentSerializerImpl(Document document) { + super(document); + } + + + public ConvertData serialize() throws ConvertException, IOException { + + // Get the server side document name. This value should not + // contain a path or the file extension. + String docName = sxcDoc.getName(); + + // TODO - get real values for password when implemnted in XML + // Passwords are not currently stored in StarCalc XML format. + String password = null; + + encoder = new PocketExcelEncoder(docName, password); + + // get dom document + org.w3c.dom.Document domDoc = sxcDoc.getContentDOM(); + + // load the styles + loadStyles(sxcDoc); + // Traverse to the office:body element. + // There should only be one. + NodeList list = domDoc.getElementsByTagName(TAG_OFFICE_BODY); + int len = list.getLength(); + + if (len > 0) { + Node node = list.item(0); + traverseBody(node); + } + + // get settings for this document + org.w3c.dom.Document settingsDoc = sxcDoc.getSettingsDOM(); + if(settingsDoc!=null) { + NodeList settingsList = settingsDoc.getElementsByTagName(TAG_OFFICE_SETTINGS); + int slen = settingsList.getLength(); + + if (slen > 0) { + Node settingsNode = settingsList.item(0); + traverseSettings(settingsNode); + } + } + + // Get the number of sheets in the workbook + // This will equal the number of PDBs we need + ConvertData cd = new ConvertData(); + Workbook wb = ((PocketExcelEncoder) encoder).getWorkbook(); + cd.addDocument(wb); + + return cd; + } + + + /** + * A cell reference in a StarOffice formula looks like + * [.C2] (for cell C2). MiniCalc is expecting cell references + * to look like C2. This method strips out the braces and + * the period. + * + * @param formula A StarOffice formula <code>String</code>. + * + * @return A MiniCalc formula <code>String</code>. + */ + protected String parseFormula(String formula) { + + return null; + } +} + diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/build.xml b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/build.xml new file mode 100644 index 000000000000..02c1e44a9ea7 --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/build.xml @@ -0,0 +1,136 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + + DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + + Copyright 2008 by Sun Microsystems, Inc. + + OpenOffice.org - a multi-platform office productivity suite + + $RCSfile: build.xml,v $ + + $Revision: 1.5 $ + + This file is part of OpenOffice.org. + + OpenOffice.org is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + OpenOffice.org 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 version 3 for more details + (a copy is included in the LICENSE file that accompanied this code). + + You should have received a copy of the GNU Lesser General Public License + version 3 along with OpenOffice.org. If not, see + <http://www.openoffice.org/license.html> + for a copy of the LGPLv3 License. + +--> +<project name="xmrg_jooxcxs_pexcel" default="main" basedir="."> + + <!-- ================================================================= --> + <!-- settings --> + <!-- ================================================================= --> + + <!-- project prefix, used for targets and build.lst --> + <property name="prj.prefix" value="xmrg"/> + + <!-- name of this sub target used in recursive builds --> + <property name="target" value="xmrg_jooxcxs_pexcel"/> + + <!-- relative path to project directory --> + <property name="prj" value="../../../../../../../.."/> + + <!-- start of java source code package structure --> + <property name="java.dir" value="${prj}/java"/> + + <!-- path component for current java package --> + <property name="package" + value="org/openoffice/xmerge/converter/xml/sxc/pexcel"/> + + <!-- define how to handle CLASSPATH environment --> + <property name="build.sysclasspath" value="ignore"/> + + <!-- classpath settings for javac tasks --> + <path id="classpath"> + <pathelement location="${build.class}"/> + <pathelement location="${solar.jar}/parser.jar"/> + <pathelement location="${solar.jar}/jaxp.jar"/> + </path> + + <!-- set wether we want to compile with or without deprecation --> + <property name="deprecation" value="on"/> + + <!-- ================================================================= --> + <!-- solar build environment targets --> + <!-- ================================================================= --> + + <target name="build_dir" unless="build.dir"> + <property name="build.dir" value="${out}"/> + </target> + + <target name="solar" depends="build_dir" if="solar.update"> + <property name="solar.properties" + value="${solar.bin}/solar.properties"/> + </target> + + <target name="init" depends="solar"> + <property name="build.compiler" value="classic"/> + <property file="${solar.properties}"/> + <property file="${build.dir}/class/solar.properties"/> + </target> + + <target name="info"> + <echo message="--------------------"/> + <echo message="${target}"/> + <echo message="--------------------"/> + </target> + + + <!-- ================================================================= --> + <!-- custom targets --> + <!-- ================================================================= --> + + <!-- the main target, called in recursive builds --> + <target name="main" depends="info,prepare,compile"/> + + <!-- prepare output directories --> + <target name="prepare" depends="init" if="build.class"> + <mkdir dir="${build.dir}"/> + <mkdir dir="${build.class}"/> + </target> + + <!-- compile java sources in ${package} --> + <target name="compile" depends="prepare" if="build.class"> + <javac srcdir="${java.dir}" + destdir="${build.class}" + debug="${debug}" + deprecation="${deprecation}" + optimize="${optimize}"> + <classpath refid="classpath"/> + <include name="${package}/PocketExcelConstants.java"/> + <include name="${package}/PocketExcelDecoder.java"/> + <include name="${package}/PocketExcelEncoder.java"/> + <include name="${package}/SxcDocumentDeserializerImpl.java"/> + <include name="${package}/SxcDocumentSerializerImpl.java"/> + <include name="${package}/ConverterCapabilitiesImpl.java"/> + <include name="${package}/PluginFactoryImpl.java"/> + </javac> + </target> + + <!-- clean up --> + <target name="clean" depends="prepare"> + <delete includeEmptyDirs="true"> + <fileset dir="${build.class}"> + <patternset> + <include name="${package}/*.class"/> + </patternset> + </fileset> + </delete> + </target> + +</project> + diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/converter.xml b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/converter.xml new file mode 100644 index 000000000000..1d6627afe49c --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/converter.xml @@ -0,0 +1,47 @@ +<?xml version="1.0"?> +<!-- + + DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + + Copyright 2008 by Sun Microsystems, Inc. + + OpenOffice.org - a multi-platform office productivity suite + + $RCSfile: converter.xml,v $ + + $Revision: 1.3 $ + + This file is part of OpenOffice.org. + + OpenOffice.org is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + OpenOffice.org 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 version 3 for more details + (a copy is included in the LICENSE file that accompanied this code). + + You should have received a copy of the GNU Lesser General Public License + version 3 along with OpenOffice.org. If not, see + <http://www.openoffice.org/license.html> + for a copy of the LGPLv3 License. + +--> +<converters> + <converter type="staroffice/sxc" version="1.0"> + <converter-display-name> + Pocket Excel 2.0 + </converter-display-name> + <converter-description> + StarCalc XML to/from Pocket Excel 2.0 conversion + </converter-description> + <converter-vendor>OpenOffice.org</converter-vendor> + <converter-class-impl> + org.openoffice.xmerge.converter.xml.sxc.pexcel.PluginFactoryImpl + </converter-class-impl> + <converter-target type="application/x-pocket-excel" /> + </converter> +</converters> + diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/makefile.mk b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/makefile.mk new file mode 100644 index 000000000000..c8d22159c375 --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/makefile.mk @@ -0,0 +1,36 @@ +#*************************************************************************** +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2008 by Sun Microsystems, Inc. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# $RCSfile: makefile.mk,v $ +# +# $Revision: 1.3 $ +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org 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 version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#*************************************************************************** + +TARGET=xmrg_jooxcxs_pexcel +PRJ=../../../../../../../.. + +.INCLUDE : ant.mk +ALLTAR: ANTBUILD diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/package.html b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/package.html new file mode 100644 index 000000000000..c7da1abfbc56 --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/package.html @@ -0,0 +1,45 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!-- + + DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + + Copyright 2008 by Sun Microsystems, Inc. + + OpenOffice.org - a multi-platform office productivity suite + + $RCSfile: package.html,v $ + + $Revision: 1.3 $ + + This file is part of OpenOffice.org. + + OpenOffice.org is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + OpenOffice.org 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 version 3 for more details + (a copy is included in the LICENSE file that accompanied this code). + + You should have received a copy of the GNU Lesser General Public License + version 3 along with OpenOffice.org. If not, see + <http://www.openoffice.org/license.html> + for a copy of the LGPLv3 License. + +--> +<html> +<head> +<title>org.openoffice.xmerge.converter.xml.sxc.pexcel package</title> +</head> + +<body bgcolor="white"> + +<p>Provides the tools for doing the conversion of StarWriter XML to +and from Pocket Excel format.</p> + +<p>It follows the {@link org.openoffice.xmerge} framework for the conversion process.</p> + +</body> +</html> diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/BIFFRecord.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/BIFFRecord.java new file mode 100644 index 000000000000..685d6153c621 --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/BIFFRecord.java @@ -0,0 +1,65 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: BIFFRecord.java,v $ + * $Revision: 1.4 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +package org.openoffice.xmerge.converter.xml.sxc.pexcel.records; + +import java.io.DataInputStream; +import java.io.OutputStream; +import java.io.InputStream; +import java.io.IOException; + + public interface BIFFRecord { + + /** + * Get the type of the record. In the BIFF file format each record has a type + * designated with a byte value. See @link PocketExcelBiffConstants + * for a list of the BIFF constants and what they mean. + * + * @return byte The BIFF record value. + */ + public short getBiffType(); + + /** + * Read from the input stream <b>NB</b>The input stream is assumed to be in + * Little Endian format. The Biff identifier is expected to be in the stream. + * + * @param input The InputStream to read from. + * @return The number of bytes that were read in. + */ + public int read(InputStream input) throws IOException; + + /** + * Writes the record, including the BIFF record byte to the outputstream + * @param output The output stream to write to in LittleEndian format. + */ + public void write(OutputStream output) throws IOException; + +} + diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/BeginningOfFile.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/BeginningOfFile.java new file mode 100644 index 000000000000..a7b328f6cf8a --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/BeginningOfFile.java @@ -0,0 +1,119 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: BeginningOfFile.java,v $ + * $Revision: 1.5 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +package org.openoffice.xmerge.converter.xml.sxc.pexcel.records; + +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; + +import org.openoffice.xmerge.util.Debug; +import org.openoffice.xmerge.util.EndianConverter; +import org.openoffice.xmerge.converter.xml.sxc.pexcel.PocketExcelConstants; + +/** + * This class describes the beginning of file. It is the + * the Biff record that marks the beginning of a a worbook + * or the beginning of worksheets in the workbook + * + */ +public class BeginningOfFile implements BIFFRecord { + + private byte[] version = new byte[2]; + private byte[] subStream = new byte[2]; + + /** + * Constructor that initializes the member values. + * + * @param ver Version Number + * Substream type (workbook = 0x05, worksheet = 0x10) + */ + public BeginningOfFile(boolean global) { + setVersion((short) 271); + if(global) + setSubStreamWBGlobal(); + else + setSubStreamWorkSheet(); + // this.subStream = EndianConverter.writeShort(dt); + } + + public BeginningOfFile(InputStream is) throws IOException { + read(is); + } + + private void setVersion(short version) { + this.version = EndianConverter.writeShort(version); + } + + int getVersion() { + return EndianConverter.readShort(version); + } + + private void setSubStreamWBGlobal() { + // subStream = new byte[] {0x05}; + subStream = EndianConverter.writeShort((short) 0x05); + } + + private void setSubStreamWorkSheet() { + // subStream = new byte[] {0x10}; + subStream = EndianConverter.writeShort((short) 0x10); + } + + int getSubStreamType() { + return EndianConverter.readShort(subStream); + } + + public int read(InputStream input) throws IOException { + int numBytesRead = input.read(version); + numBytesRead += input.read(subStream); + Debug.log(Debug.TRACE,"\tVersion : "+ EndianConverter.readShort(version) + + " Stream : " + EndianConverter.readShort(subStream)); + + return numBytesRead; + } + + public void write(OutputStream output) throws IOException { + + output.write(getBiffType()); + output.write(version); + output.write(subStream); + + Debug.log(Debug.TRACE, "Writing BeginningOfFile record"); + } + + /** + * Get the hex code for this particular <code>BIFFRecord</code> + * + * @return the hex code for <code>BeginningOfFile</code> + */ + public short getBiffType() { + return PocketExcelConstants.BOF_RECORD; + } +} diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/BlankCell.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/BlankCell.java new file mode 100644 index 000000000000..3bd70f0c7a91 --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/BlankCell.java @@ -0,0 +1,119 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: BlankCell.java,v $ + * $Revision: 1.5 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +package org.openoffice.xmerge.converter.xml.sxc.pexcel.records; + +import java.io.DataInputStream; +import java.io.OutputStream; +import java.io.InputStream; +import java.io.IOException; + +import org.openoffice.xmerge.util.Debug; +import org.openoffice.xmerge.util.EndianConverter; +import org.openoffice.xmerge.converter.xml.sxc.pexcel.PocketExcelConstants; + +/** + * Represents a BIFF Record that describes a blank cell + */ +public class BlankCell extends CellValue { + + /** + * Constructs a BlankCell <code>InputStream</code> + * + * @param is InputStream containing a BlankCell. + */ + public BlankCell(InputStream is) throws IOException { + read(is); + } + + /** + * Constructs a <code>BlankCell</code> using specified attributes + * + * @param row row number + * @param col column number + * @param cellContents contents of the cell + * @param ixfe font index + */ + public BlankCell(int row, int column, int ixfe) throws IOException { + + setRow(row); + setCol(column); + setIxfe(ixfe); + } + + /** + * Get the hex code for this particular <code>BIFFRecord</code> + * + * @return the hex code for <code>BlankCell</code> + */ + public short getBiffType() { + return PocketExcelConstants.BLANK_CELL; + } + + public void write(OutputStream output) throws IOException { + + output.write(getBiffType()); + output.write(rw); + output.write(col); + output.write(ixfe); + + Debug.log(Debug.TRACE, "Writing BlankCell record"); + + } + + /** + * Reads a BlankCell <code>InputStream</code> + * + * @param is InputStream containing a BlankCell. + */ + public int read(InputStream input) throws IOException { + + int numOfBytesRead = input.read(rw); + numOfBytesRead++; + col += input.read(); + numOfBytesRead += input.read(ixfe); + + Debug.log(Debug.TRACE, "\tRow : "+ EndianConverter.readShort(rw) + + " Column : " + col + + " ixfe : " + EndianConverter.readShort(ixfe)); + + return numOfBytesRead; + } + + /** + * Gets the <code>String</code> representing the cells contents + * + * @return the <code>String</code> representing the cells contents + */ + public String getString() throws IOException { + + return (new String("")); + } +} diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/BoolErrCell.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/BoolErrCell.java new file mode 100644 index 000000000000..6fca4ddd57d2 --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/BoolErrCell.java @@ -0,0 +1,130 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: BoolErrCell.java,v $ + * $Revision: 1.7 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +package org.openoffice.xmerge.converter.xml.sxc.pexcel.records; + +import java.io.DataInputStream; +import java.io.OutputStream; +import java.io.InputStream; +import java.io.IOException; + + +import org.openoffice.xmerge.util.Debug; +import org.openoffice.xmerge.util.EndianConverter; +import org.openoffice.xmerge.converter.xml.sxc.pexcel.PocketExcelConstants; + +/** + * Represents a BIFF Record that describes a Boolean or Error value + */ +public class BoolErrCell extends CellValue { + + private byte bBoolErr; + private byte fError; + + /** + * Constructs a BoolErrCell from arguments + * + * @param row row number + * @param col column number + * @param ixfe font index + * @param bBoolErr Boolean value or error value + * @param fError Boolean error flag + */ + public BoolErrCell(int row, int column, int ixfe, int bBoolErr, int fError) throws IOException { + + setIxfe(ixfe); + this.bBoolErr = (byte)bBoolErr; + this.fError = (byte)fError; + setRow(row); + setCol(column); + } + + /** + * Constructs a BoolErrCell from the <code>InputStream</code> + * + * @param is InputStream containing a BoolErrCell + */ + public BoolErrCell(InputStream is) throws IOException { + read(is); + } + + /** + * Get the hex code for this particular <code>BIFFRecord</code> + * + * @return the hex code for <code>BoolErrCEll</code> + */ + public short getBiffType() { + return PocketExcelConstants.BOOLERR_CELL; + } + + /** + * Writes a <code>BoolErrCell</code> to the specified <code>Outputstream</code> + * + * @param os the <code>OutputStream</code> to write to + */ + public void write(OutputStream output) throws IOException { + + output.write(getBiffType()); + + super.write(output); + + output.write(bBoolErr); + output.write(fError); + + Debug.log(Debug.TRACE,"Writing BoolErrCell record"); + } + + /** + * Reads a BoolErrCell from the <code>InputStream</code> + * + * @param is InputStream containing a BoolErrCell + */ + public int read(InputStream input) throws IOException { + + int numOfBytesRead = super.read(input); + + bBoolErr = (byte) input.read(); + fError = (byte) input.read(); + numOfBytesRead += 2; + + Debug.log(Debug.TRACE, " bBoolErr : " + bBoolErr + + " fError : " + fError); + return numOfBytesRead; + } + + /** + * Gets the <code>String</code> representing the cells contents + * + * @return the <code>String</code> representing the cells contents + */ + public String getString() throws IOException { + return ("Error Cell"); + } +} diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/BoundSheet.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/BoundSheet.java new file mode 100644 index 000000000000..db34aa600fb9 --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/BoundSheet.java @@ -0,0 +1,140 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: BoundSheet.java,v $ + * $Revision: 1.6 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +package org.openoffice.xmerge.converter.xml.sxc.pexcel.records; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.io.OutputStream; +import java.io.InputStream; + +import org.openoffice.xmerge.util.Debug; +import org.openoffice.xmerge.util.EndianConverter; +import org.openoffice.xmerge.converter.xml.sxc.pexcel.PocketExcelConstants; + +/** + * Represents a BoundSheet Record which describes the name of a worksheet + */ +public class BoundSheet implements BIFFRecord { + + private byte reserved; + private byte cch; + private byte[] sheetName; + + /** + * Constructs a pocket Excel Document assigns it the document name passed in + * + * @param name name of the worksheet represented + */ + public BoundSheet(String name) throws IOException { + setSheetName(name); + reserved = 0; + } + + /** + * Constructs a pocket Excel Document from the + * <code>InputStream</code> and assigns it the document name passed in + * + * @param is InputStream containing a Pocket Excel Data file. + */ + public BoundSheet(InputStream is) throws IOException { + read(is); + } + + /** + * Sets the worksheet name. The sheetname length must be doubled as the + * String is stored in unicode format. + * + * @param sheetname worksheet name + */ + void setSheetName(String sheetName) throws IOException { + this.cch = (byte) sheetName.length(); + this.sheetName = new byte[cch*2]; + this.sheetName = sheetName.getBytes("UTF-16LE"); + } + + public String getSheetName() { + String name; + + try { + name = new String(sheetName, "UTF-16LE"); + } catch (UnsupportedEncodingException e){ + name = "unknown"; + } + return name; + } + + /** + * Get the hex code for this particular <code>BIFFRecord</code> + * + * @return the hex code for <code>BoundSheet</code> + */ + public short getBiffType() { + return PocketExcelConstants.BOUND_SHEET; + } + + /** + * Write this particular <code>BIFFRecord</code> to the <code>OutputStream</code> + * + * @param ouput the <code>OutputStream</code> + */ + public void write(OutputStream output) throws IOException { + + output.write(getBiffType()); + output.write(reserved); + output.write(cch); + output.write(sheetName); + + Debug.log(Debug.TRACE,"Writing BoundSheet record"); + } + + /** + * Reads a BoundSheet from the <code>InputStream</code> The byte array + * must be twice the size of the String as it uses unicode. + * + * @param is InputStream containing the record data + */ + public int read(InputStream input) throws IOException { + + reserved = (byte) input.read(); + cch = (byte) input.read(); + int numOfBytesRead = 2; + int strLen = cch*2; + sheetName = new byte[strLen]; + numOfBytesRead += input.read(sheetName, 0, strLen); + + Debug.log(Debug.TRACE,"\tReserved : "+ reserved + + " cch : " + cch + + " sheetName : " + new String(sheetName,"UTF-16LE")); + + return numOfBytesRead; + } + +} diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/CellValue.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/CellValue.java new file mode 100644 index 000000000000..2bb22b35666f --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/CellValue.java @@ -0,0 +1,141 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: CellValue.java,v $ + * $Revision: 1.6 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +package org.openoffice.xmerge.converter.xml.sxc.pexcel.records; + +import java.io.DataInputStream; +import java.io.OutputStream; +import java.io.InputStream; +import java.io.IOException; + +import org.openoffice.xmerge.util.Debug; +import org.openoffice.xmerge.util.EndianConverter; +import org.openoffice.xmerge.converter.xml.sxc.pexcel.PocketExcelConstants; + +public abstract class CellValue implements BIFFRecord { + + protected byte[] rw = new byte[2]; + protected byte col; + protected byte[] ixfe = new byte[2]; + + /** + * Get the row number of this cell + * + * @return the row number of this cell + */ + public int getRow() { + return EndianConverter.readShort(rw) + 1; + } + + /** + * Set the row number of this cell + * + * @param row sets the row number for this cell + */ + public void setRow(int row) { + this.rw = EndianConverter.writeShort((short) (row - 1)); + } + /** + * Get the Index to the <code>ExtendedFormat</code> + * + * @return the index number of this cell's <code>ExtendedFormat</code> + */ + public int getIxfe() { + return EndianConverter.readShort(ixfe); + } + + /** + * Sets the Index to the <code>ExtendedFormat</code> + * + * @param ixfe sets the index number for this cell's <code>ExtendedFormat</code> + */ + public void setIxfe(int ixfe) { + this.ixfe = EndianConverter.writeShort((short) (ixfe)); + } + + /** + * Get the column number of this cell + * + * @return the column number of this cell + */ + public int getCol() { + return col + 1; // The cols start at 1 + } + + /** + * Set the row number of this cell + * + * @param col sets the row number for this cell + */ + public void setCol(int col) { + this.col = (byte) (col - 1); // The cols start at 1 + } + + /** + * Writes basic cell value attributes to the specified <code>Outputstream</code> + * + * @param os the <code>OutputStream</code> to write to + */ + public void write(OutputStream output) throws IOException { + + output.write(rw); + output.write(col); + output.write(ixfe); + } + + /** + * Writes a<code>LabelCell</code> to the specified <code>Outputstream</code> + * + * @param os the <code>OutputStream</code> to write to + */ + public int read(InputStream input) throws IOException { + + int numOfBytesRead = input.read(rw); + col += input.read(); + numOfBytesRead++; + numOfBytesRead += input.read(ixfe); + + Debug.log(Debug.TRACE, "\tRow : "+ EndianConverter.readShort(rw) + + " Column : " + col + + " ixfe : " + EndianConverter.readShort(ixfe)); + + return numOfBytesRead; + } + + + /** + * Returns the contents of the cell as a String + * + * @return the contents of the cell + */ + abstract public String getString() throws IOException; + +} + diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/CodePage.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/CodePage.java new file mode 100644 index 000000000000..55184582ebdb --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/CodePage.java @@ -0,0 +1,111 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: CodePage.java,v $ + * $Revision: 1.5 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +package org.openoffice.xmerge.converter.xml.sxc.pexcel.records; + +import java.io.DataInputStream; +import java.io.OutputStream; +import java.io.InputStream; +import java.io.IOException; + +import org.openoffice.xmerge.util.Debug; +import org.openoffice.xmerge.util.EndianConverter; +import org.openoffice.xmerge.converter.xml.sxc.pexcel.PocketExcelConstants; + +/** + * Represents the codepage for the document. There is a number of unknown + * fields which are hardcoded at construction + */ +public class CodePage implements BIFFRecord { + + private byte[] codepage = new byte[2]; + private byte[] unknown1 = new byte[2]; + private byte[] unknown2 = new byte[2]; + private byte unknown3; + + /** + * Constructs a pocket Excel Codepage + */ + public CodePage() { + codepage = new byte[] {(byte)0xE4, (byte)0x04}; + unknown1 = new byte[] {(byte)0x8C, (byte)0x01}; + unknown2 = new byte[] {(byte)0x00, (byte)0x01}; + unknown3 = 0x00; + } + + /** + * Constructs a pocket Excel Codepage from the<code>InputStream</code> + * + * @param is InputStream containing a Pocket Excel Data file. + */ + public CodePage(InputStream is) throws IOException { + read(is); + } + + /** + * Get the hex code for this particular <code>BIFFRecord</code> + * + * @return the hex code for <code>BoundSheet</code> + */ + public short getBiffType() { + return PocketExcelConstants.CODEPAGE; + } + + public int read(InputStream input) throws IOException { + + int numOfBytesRead = input.read(codepage); + numOfBytesRead += input.read(unknown1); + numOfBytesRead += input.read(unknown2); + // numOfBytesRead += input.read(unknown3); + unknown3 = (byte) input.read(); + numOfBytesRead++; + + Debug.log(Debug.TRACE,"\tcodepage : "+ EndianConverter.readShort(codepage) + + " unknown1 : " + EndianConverter.readShort(unknown1) + + " unknown2 : " + EndianConverter.readShort(unknown2) + + " unknown3 : " + unknown3); + + return numOfBytesRead; + } + + public void write(OutputStream output) throws IOException { + + output.write(getBiffType()); + output.write(codepage); + output.write(unknown1); + output.write(unknown2); + output.write(unknown3); + + Debug.log(Debug.TRACE,"Writing CodePage record"); + + + } + +} diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/ColInfo.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/ColInfo.java new file mode 100644 index 000000000000..d3e61e1b4fec --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/ColInfo.java @@ -0,0 +1,161 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: ColInfo.java,v $ + * $Revision: 1.8 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +package org.openoffice.xmerge.converter.xml.sxc.pexcel.records; + +import java.io.IOException; +import java.io.DataInputStream; +import java.io.InputStream; +import java.io.OutputStream; + +import org.openoffice.xmerge.util.Debug; +import org.openoffice.xmerge.util.EndianConverter; +import org.openoffice.xmerge.converter.xml.sxc.pexcel.PocketExcelConstants; + +/** + * ColInfo describes the formatting for a column + * + */ +public class ColInfo implements BIFFRecord { + + private byte[] colFirst = new byte[2]; // first column this formatting applies to + private byte[] colLast = new byte[2]; // last column this formatting applies to + private byte[] colDX = new byte[2]; // column width + private byte[] ixfe = new byte[2]; // index for formatting + private byte grbit; // options flags + private float scale = (float) 2.5; // 1.798; + + /** + * Constructs a pocket Excel Document from the + * <code>InputStream</code> and assigns it the document name passed in + * + * @param colFirst the first column this formatting applies to + * @param colLast last column this formatting applies to + * @param coldx column width + * @param grbit options flags + */ + public ColInfo(int colFirst, int colLast, int colDX, int ixfe) { + this.colFirst = EndianConverter.writeShort((short)colFirst); + this.colLast = EndianConverter.writeShort((short)colLast); + colDX *= scale; + this.colDX = EndianConverter.writeShort((short)colDX); + this.ixfe = EndianConverter.writeShort((short)ixfe); + this.grbit = 0x00; + } + + /** + * Construct a ColInfo from the InputStream + * + * @param is the <code>Inputstream</code> to read from + */ + public ColInfo(InputStream is) throws IOException { + read(is); + } + + /** + * Reads ColInfo record from the InputStream + * + * @param input the InputStream to read from + * @return the number of bytes read + */ + public int read(InputStream input) throws IOException { + + int numOfBytesRead = input.read(colFirst); + numOfBytesRead += input.read(colLast); + numOfBytesRead += input.read(colDX); + short scaledDX = (short) (EndianConverter.readShort(colDX) / scale); + colDX = EndianConverter.writeShort(scaledDX); + numOfBytesRead += input.read(ixfe); + grbit = (byte) input.read(); + numOfBytesRead ++; + + Debug.log(Debug.TRACE,"\tcolFirst : "+ EndianConverter.readShort(colFirst) + + " colLast : " + EndianConverter.readShort(colLast) + + " colDX : " + EndianConverter.readShort(colDX) + + " ixfe : " + EndianConverter.readShort(ixfe) + + " grbit : " + grbit); + + return numOfBytesRead; + } + + /** + * Get the hex code for this particular <code>BIFFRecord</code> + * + * @return the hex code for <code>ColInfo</code> + */ + public short getBiffType() { + return PocketExcelConstants.COLINFO; + } + /** + * Get the width of this column + * + * @return the width of this column + */ + public short getColWidth() { + return EndianConverter.readShort(colDX); + } + + /** + * Get the hex code for this particular <code>BIFFRecord</code> + * + * @return the hex code for <code>ColInfo</code> + */ + public short getFirst() { + return EndianConverter.readShort(colFirst); + } + + /** + * Get the hex code for this particular <code>BIFFRecord</code> + * + * @return the hex code for <code>ColInfo</code> + */ + public short getLast() { + return EndianConverter.readShort(colLast); + } + + /** + * Writes a ColInfo to the specified <code>Outputstream</code> + * + * @param os the <code>OutputStream</code> to write to + */ + public void write(OutputStream output) throws IOException { + + output.write(getBiffType()); + output.write(colFirst); + output.write(colLast); + output.write(colDX); + output.write(ixfe); + output.write(grbit); + + Debug.log(Debug.TRACE,"Writing ColInfo record"); + + } + +} diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/DefColWidth.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/DefColWidth.java new file mode 100644 index 000000000000..12a451334386 --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/DefColWidth.java @@ -0,0 +1,98 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: DefColWidth.java,v $ + * $Revision: 1.5 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +package org.openoffice.xmerge.converter.xml.sxc.pexcel.records; + +import java.io.DataInputStream; +import java.io.OutputStream; +import java.io.InputStream; +import java.io.IOException; + +import org.openoffice.xmerge.util.Debug; +import org.openoffice.xmerge.util.EndianConverter; +import org.openoffice.xmerge.converter.xml.sxc.pexcel.PocketExcelConstants; + +/** + * Represents a BIFF record defiuning the defualt column width + */ +public class DefColWidth implements BIFFRecord { + + private byte[] grbit = new byte[2]; + private byte[] coldx = new byte[2]; + private byte[] ixfe = new byte[2]; + +/** + * Constructs a pocket Excel Document from the + * <code>InputStream</code> and assigns it the document name passed in + * + * @param is InputStream containing a Pocket Excel Data file. + */ + public DefColWidth() { + grbit = new byte[] {0x00, 0x00}; + coldx = new byte[] {0x00, 0x09}; + ixfe = new byte[] {0x00, 0x00}; + } + + public DefColWidth(InputStream is) throws IOException { + read(is); + } + + /** + * Get the hex code for this particular <code>BIFFRecord</code> + * + * @return the hex code for <code>DefColWidth</code> + */ + public short getBiffType() { + return PocketExcelConstants.DEF_COL_WIDTH; + } + + public void write(OutputStream output) throws IOException { + + output.write(getBiffType()); + output.write(grbit); + output.write(coldx); + output.write(ixfe); + + Debug.log(Debug.TRACE, "Writing DefColWidth record"); + } + + public int read(InputStream input) throws IOException { + + int numOfBytesRead = input.read(grbit); + numOfBytesRead += input.read(coldx); + numOfBytesRead += input.read(ixfe); + + Debug.log(Debug.TRACE,"\tgrbit : "+ EndianConverter.readShort(grbit) + + " coldx : " + EndianConverter.readShort(coldx) + + " ixfe : " + EndianConverter.readShort(ixfe)); + return 0; + } + +} diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/DefRowHeight.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/DefRowHeight.java new file mode 100644 index 000000000000..d924f339fe37 --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/DefRowHeight.java @@ -0,0 +1,100 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: DefRowHeight.java,v $ + * $Revision: 1.5 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +package org.openoffice.xmerge.converter.xml.sxc.pexcel.records; + +import java.io.DataInputStream; +import java.io.OutputStream; +import java.io.InputStream; +import java.io.IOException; + +import org.openoffice.xmerge.util.Debug; +import org.openoffice.xmerge.util.EndianConverter; +import org.openoffice.xmerge.converter.xml.sxc.pexcel.PocketExcelConstants; + +/** + * Represents a BIFF record defiuning the default row height + */ +public class DefRowHeight implements BIFFRecord { + + private byte[] unknown1 = new byte[2]; + private byte[] unknown2 = new byte[2]; + + /** + * Constructs a pocket Excel Document from the + * <code>InputStream</code> and assigns it the document name passed in + * + * @param is InputStream containing a Pocket Excel Data file. + */ + public DefRowHeight() { + unknown1 = new byte[] {(byte)0x00, (byte)0x00}; + unknown2 = new byte[] {(byte)0xFF, (byte)0x00}; + } + + /** + * Constructs a DefRowHeight from the <code>InputStream</code> + * + * @param is InputStream containing a Pocket Excel Data file. + */ + public DefRowHeight(InputStream is) throws IOException { + read(is); + } + + /** + * Get the hex code for this particular <code>BIFFRecord</code> + * + * @return the hex code for <code>DefRowHeight</code> + */ + public short getBiffType() { + return PocketExcelConstants.DEFAULT_ROW_HEIGHT; + } + + public int read(InputStream input) throws IOException { + + int numOfBytesRead = input.read(unknown1); + numOfBytesRead += input.read(unknown2); + + Debug.log(Debug.TRACE,"\tunknown1 : "+ EndianConverter.readShort(unknown1) + + " unknown2 : " + EndianConverter.readShort(unknown2)); + return numOfBytesRead; + } + + public void write(OutputStream output) throws IOException { + + output.write(getBiffType()); + output.write(unknown1); + output.write(unknown2); + + Debug.log(Debug.TRACE,"Writing DefRowHeight record"); + + + } + +} diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/DefinedName.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/DefinedName.java new file mode 100644 index 000000000000..3fad2410162e --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/DefinedName.java @@ -0,0 +1,230 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: DefinedName.java,v $ + * $Revision: 1.7 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +package org.openoffice.xmerge.converter.xml.sxc.pexcel.records; + +import java.io.DataInputStream; +import java.io.OutputStream; +import java.io.InputStream; +import java.io.IOException; +import java.io.UnsupportedEncodingException; + +import org.openoffice.xmerge.util.Debug; +import org.openoffice.xmerge.util.EndianConverter; +import org.openoffice.xmerge.converter.xml.sxc.pexcel.records.Workbook; +import org.openoffice.xmerge.converter.xml.sxc.pexcel.records.formula.FormulaHelper; +import org.openoffice.xmerge.converter.xml.sxc.NameDefinition; +import org.openoffice.xmerge.converter.xml.sxc.pexcel.PocketExcelConstants; + +/** + * Represents a BIFF Record representing a defined name in the workbook + */ +public class DefinedName implements BIFFRecord { + + private byte[] grbit = new byte[2]; + private byte cch; + private byte[] cce = new byte[2]; + private byte[] ixals = new byte[2]; + private byte[] rgch; + private byte[] rgce; + private FormulaHelper fh = new FormulaHelper(); + private String definition = new String(""); + private Workbook wb; + + /** + * Constructs a Defined Name from the <code>InputStream</code> + * + * @param is InputStream containing the record data + */ + public DefinedName(NameDefinition nd, Workbook wb) throws IOException { + + fh.setWorkbook(wb); + this.wb = wb; + String name = nd.getName(); + + // we have to insert an = to stop the formulaParser throwing an exception + definition = "=" + nd.getDefinition(); + + cch = (byte)name.length(); + rgch = new byte[cch*2]; + rgch = name.getBytes("UTF-16LE"); + grbit = EndianConverter.writeShort((short)0); + ixals[0] = (byte)0xFF;ixals[1] = (byte)0xFF; + } + /** + * Constructs a Defined Name from the <code>InputStream</code> + * + * @param is InputStream containing the record data + */ + public DefinedName(InputStream is, Workbook wb) throws IOException { + + read(is); + fh.setWorkbook(wb); + this.wb = wb; + } + + /** + * Get the hex code for this particular <code>BIFFRecord</code> + * + * @return the hex code for <code>DefinedName</code> + */ + public short getBiffType() { + return PocketExcelConstants.DEFINED_NAME; + } + + /** + * Reads a Defined Name from the <code>InputStream</code> The byte array + * must be twice the size of the String as it uses unicode. + * + * @param is InputStream containing the record data + */ + public int read(InputStream input) throws IOException { + + int numOfBytesRead = input.read(grbit); + cch = (byte) input.read(); + numOfBytesRead++; + numOfBytesRead += input.read(cce); + numOfBytesRead += input.read(ixals); + + rgch = new byte[cch*2]; + input.read(rgch, 0, cch*2); + + rgce = new byte[EndianConverter.readShort(cce)]; + input.read(rgce, 0, EndianConverter.readShort(cce)); + + + + Debug.log(Debug.TRACE, "\tgrbit : "+ EndianConverter.readShort(grbit) + + " cch : " + cch + + " cce : " + EndianConverter.readShort(cce) + + " ixals : " + EndianConverter.readShort(ixals) + + "\n\trgch : " + rgch + + " rgce : " + rgce); + + return numOfBytesRead; + } + + /** + * Write this particular <code>BIFFRecord</code> to the <code>OutputStream</code> + * + * @param ouput the <code>OutputStream</code> + */ + public void write(OutputStream output) throws IOException { + + try { + Debug.log(Debug.TRACE,"Writing out " + definition); + rgce = fh.convertCalcToPXL(definition); + cce = EndianConverter.writeShort((short) rgce.length); + } catch(Exception e) { + Debug.log(Debug.TRACE,"Error in Parsing Name Definition"); + cce = EndianConverter.writeShort((short) 0); + } + + + output.write(getBiffType()); + output.write(grbit); + output.write(cch); + output.write(cce); + output.write(ixals); + output.write(rgch); + if(rgce.length!=0) + output.write(rgce); + + Debug.log(Debug.TRACE,"Writing DefinedName record"); + } + + /** + * Returns definition name. This is public because the + * <code>TokenDecoder</code> has to substitue the Name token with this + * String when writing out to sxc + * + * @return the <code>String</code> containing the name + */ + public String getName() { + String name; + + try { + name = new String(rgch, "UTF-16LE"); + } catch (UnsupportedEncodingException e){ + name = "unknown"; + } + return name; + } + + /** + * Returns a definition table which can be used by the pocket excel + * decoder to build a complete definitions table for writing to the sxc + * document + */ + public NameDefinition getNameDefinition() { + + String baseCellAddress; + getDefinition(); // This must be called first so we know the type + + baseCellAddress = "$" + wb.getSheetName(0) + ".A1"; + + NameDefinition nd = new NameDefinition(getName(),definition, baseCellAddress, isRangeType(), isExpressionType()); + return nd; + } + + /** + * Returns the definition + * + * @return the <code>String</code> containing the definition + */ + private String getDefinition() { + // pexcel sometimes creates Name definition with no defintion, bug?? + if(EndianConverter.readShort(cce)!=0) { + definition = fh.convertPXLToCalc(rgce); + definition = definition.substring(1); // remove the '=' + definition = definition.replace(',', ';'); + } + return definition; + } + + /** + * Returns the defintion + * + * @return the <code>String</code> containing the definition + */ + private boolean isRangeType() { + + return fh.isRangeType(); + } + /** + * Returns the defintion + * + * @return the <code>String</code> containing the definition + */ + private boolean isExpressionType() { + + return fh.isExpressionType(); + } +} diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/Eof.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/Eof.java new file mode 100644 index 000000000000..77a4e97835cf --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/Eof.java @@ -0,0 +1,75 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: Eof.java,v $ + * $Revision: 1.5 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +package org.openoffice.xmerge.converter.xml.sxc.pexcel.records; + +import java.io.DataInputStream; +import java.io.OutputStream; +import java.io.InputStream; +import java.io.IOException; + +import org.openoffice.xmerge.util.Debug; +import org.openoffice.xmerge.util.EndianConverter; +import org.openoffice.xmerge.converter.xml.sxc.pexcel.PocketExcelConstants; + +/** + * Represents a BIFF Record used to mark the end of a section of file + */ +public class Eof implements BIFFRecord { + + /** + * Constructor + */ + public Eof() { + } + + /** + * Get the hex code for this particular <code>BIFFRecord</code> + * + * @return the hex code for <code>BeginningOfFile</code> + */ + public short getBiffType() { + return PocketExcelConstants.EOF_MARKER; + } + + public int read(InputStream input) throws IOException { + return 0; + } + + public void write(OutputStream output) throws IOException { + + output.write(getBiffType()); + + Debug.log(Debug.TRACE,"Writing Eof record"); + + + } + +} diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/ExtendedFormat.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/ExtendedFormat.java new file mode 100644 index 000000000000..254c2f2bddf4 --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/ExtendedFormat.java @@ -0,0 +1,388 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: ExtendedFormat.java,v $ + * $Revision: 1.11 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +package org.openoffice.xmerge.converter.xml.sxc.pexcel.records; + +import java.io.DataInputStream; +import java.io.OutputStream; +import java.io.InputStream; +import java.io.IOException; +import java.awt.Color; + +import org.openoffice.xmerge.util.Debug; +import org.openoffice.xmerge.util.EndianConverter; +import org.openoffice.xmerge.util.ColourConverter; +import org.openoffice.xmerge.converter.xml.sxc.Format; +import org.openoffice.xmerge.converter.xml.sxc.pexcel.PocketExcelConstants; + +/** + * Represents a BIFF Record descibing extended formatting information + * + */ +public class ExtendedFormat implements BIFFRecord, +org.openoffice.xmerge.converter.xml.OfficeConstants { + + private byte[] ixfnt = new byte[2]; // Index to Font Record + private byte[] ixnf = new byte[2]; + private byte[] fattributes = new byte[4]; + private byte[] fBaseAttr = new byte[2]; // base attribute flags + private byte[] fTextAttr = new byte[2]; // text attribute flags + private byte[] icvFore = new byte[2]; // Background colour of the cell + private byte[] icvFill = new byte[2]; + private byte bRight; // Right border Style + private byte bTop; // Top border style + private byte bLeft; // Left border style + private byte bBottom; // Bottom border style + private byte backstyle; + private byte borderstyle; + + public static final int TOP_BORDER = 0x01; + public static final int LEFT_BORDER = 0x02; + public static final int BOTTOM_BORDER = 0x04; + public static final int RIGHT_BORDER = 0x08; + + // Horizontal Alignment Styles + public static final int NORMAL_ALIGN = 0x00; + public static final int LEFT_ALIGN = 0x01; + public static final int CENTER_ALIGN = 0x02; + public static final int RIGHT_ALIGN = 0x03; + + // Vertical Alignment Styles + public static final int TOP_ALIGN = 0x10; + public static final int MIDDLE_ALIGN = 0x20; + public static final int BOTTOM_ALIGN = 0x30; + + public static final int WORD_WRAP = 0x08; + + /** + * Constructs an <code>ExtendedFormat</code> from the + * <code>InputStream</code> + * + * @param is <code>InputStream</code> to read from + */ + public ExtendedFormat(InputStream is) throws IOException { + read(is); + } + + /** + * Constructs a pocket Excel Document using defualt values and sets the + * font index using the specified attribute + * + * @param ixfnt index of the font this format should use + */ + public ExtendedFormat(int ixfnt, Format fmt) { + + this.ixfnt = EndianConverter.writeShort((short)ixfnt); + String category = fmt.getCategory(); + if(category.equalsIgnoreCase(CELLTYPE_CURRENCY)) { + this.ixnf = EndianConverter.writeShort((short) 0); + } else if(category.equalsIgnoreCase(CELLTYPE_DATE)) { + this.ixnf = EndianConverter.writeShort((short) 0x12); + } else if(category.equalsIgnoreCase(CELLTYPE_TIME)) { + this.ixnf = EndianConverter.writeShort((short) 0x1E); + } else { + this.ixnf = EndianConverter.writeShort((short) 0); + } + this.fattributes = new byte[] {(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF}; + this.fBaseAttr = new byte[] {(byte)0x02,(byte)0x00}; + + this.fTextAttr = new byte[] {(byte)0x00, (byte)0x00}; + + int align = fmt.getAlign(); + + // Horizontal alignment + if(align==Format.CENTER_ALIGN) { + fTextAttr[0] |= CENTER_ALIGN; + } else if(align==Format.LEFT_ALIGN) { + fTextAttr[0] |= LEFT_ALIGN; + } else if(align==Format.RIGHT_ALIGN) { + fTextAttr[0] |= RIGHT_ALIGN; + } else { + fTextAttr[0] |= NORMAL_ALIGN; + } + + int vertAlign = fmt.getVertAlign(); + + // Vertical alignment + if(vertAlign==Format.TOP_ALIGN) { + fTextAttr[0] |= TOP_ALIGN; + } else if(vertAlign==Format.BOTTOM_ALIGN) { + fTextAttr[0] |= BOTTOM_ALIGN; + } else if(vertAlign==Format.MIDDLE_ALIGN) { + fTextAttr[0] |= MIDDLE_ALIGN; + } else { + fTextAttr[0] |= BOTTOM_ALIGN; + } + + if(fmt.getAttribute(Format.WORD_WRAP)) { + fTextAttr[0] |= WORD_WRAP; + } + + if(fmt.getAttribute(Format.LEFT_BORDER)) { + fTextAttr[1] |= LEFT_BORDER; + } + if(fmt.getAttribute(Format.RIGHT_BORDER)) { + fTextAttr[1] |= RIGHT_BORDER; + } + if(fmt.getAttribute(Format.TOP_BORDER)) { + fTextAttr[1] |= TOP_BORDER; + } + if(fmt.getAttribute(Format.BOTTOM_BORDER)) { + fTextAttr[1] |= BOTTOM_BORDER; + } + + Color background = fmt.getBackground(); + if( background != null ) { + ColourConverter cc = new ColourConverter(PocketExcelConstants.cLookup); + icvFill = EndianConverter.writeShort(cc.convertFromRGB(background)); + } else { + icvFill = new byte[] {(byte)0xFF,(byte)0x00}; + } + + icvFore = new byte[] {(byte)0xFF,(byte)0x00}; + + bRight = (byte) 0xFF; + bTop = (byte) 0xFF; + bLeft = (byte) 0xFF; + bBottom = (byte) 0xFF; + backstyle = (byte) 0x00; + borderstyle = (byte) 0x00; + + } + + /** + * Get the font index this format uses + * + * @return the font index + */ + public int getFontIndex() { + return EndianConverter.readShort(ixfnt); + } + + /** + * Get the font index this format uses + * + * @return the font index + */ + public int getFormatIndex() { + return EndianConverter.readShort(ixnf); + } + + /** + * Get the font index this format uses + * + * @return the font index + */ + public int getTextAttr() { + return EndianConverter.readShort(fTextAttr); + } + + /** + * Get the background color this format uses + * + * @return the background color + */ + public Color getBackground() { + short rgb = EndianConverter.readShort(icvFill); + Color c = null; + if(rgb!=0xFF) { + ColourConverter cc = new ColourConverter(PocketExcelConstants.cLookup); + c = cc.convertToRGB(rgb); + } + return c; + } + + /** + * Get the Vertical alignment for this Format + * + * @return the alignment + */ + public int getVertAlign() { + + int mask = MIDDLE_ALIGN | BOTTOM_ALIGN | TOP_ALIGN; + int masked = fTextAttr[0] & mask; + + if(masked == MIDDLE_ALIGN) + return Format.MIDDLE_ALIGN; + + if(masked == BOTTOM_ALIGN) + return Format.BOTTOM_ALIGN; + + if(masked == TOP_ALIGN) + return Format.TOP_ALIGN; + + return Format.BOTTOM_ALIGN; + } + + /** + * Get the alignment for this Format + * + * @return the alignment + */ + public int getAlign() { + + int mask = LEFT_ALIGN | CENTER_ALIGN | RIGHT_ALIGN; + int masked = fTextAttr[0] & mask; + + if(masked == MIDDLE_ALIGN) + return Format.LEFT_ALIGN; + + if(masked == CENTER_ALIGN) + return Format.CENTER_ALIGN; + + if(masked == RIGHT_ALIGN) + return Format.RIGHT_ALIGN; + + return Format.LEFT_ALIGN; + } + + /** + * Is the word wrap set + * + * @return true if it is selected + */ + public boolean isWordWrap() { + return (!((fTextAttr[0] & WORD_WRAP) == 0)); + } + /** + * Get the border style + * + * @param side the side to test + * @return true if it is selected + */ + public boolean isBorder(int side) { + return (!((fTextAttr[1] & side) == 0)); + } + + /** + * Compare two ExtendedFormat to see if the font index is the same + * + * @param the ExtendedFormat to be used in the comaprison + * @return boolean if the two are the same otherwise false + */ + public boolean compareTo(ExtendedFormat rhs) { + + if(EndianConverter.readShort(icvFill) != + EndianConverter.readShort(rhs.icvFill)) + return false; + + if(this.getTextAttr() != rhs.getTextAttr()) + return false; + + if(this.getVertAlign() != rhs.getVertAlign()) + return false; + + if(this.getAlign() != rhs.getAlign()) + return false; + + if (this.getFontIndex() != rhs.getFontIndex()) + return false; + + if (this.getFormatIndex() != rhs.getFormatIndex()) + return false; + + return true; + } + + /** + * Get the hex code for this particular <code>BIFFRecord</code> + * + * @return the hex code for <code>ExtendedFormat</code> + */ + public short getBiffType() { + return PocketExcelConstants.EXTENDED_FORMAT; + } + + /** + * Reads the extended format from the <code>Inputstream</code> + * + * @param input the <code>Inputstream</code>to read + * @return toal number of bytes read + */ + public int read(InputStream input) throws IOException { + + int numOfBytesRead = input.read(ixfnt); + numOfBytesRead += input.read(ixnf); + numOfBytesRead += input.read(fattributes); + numOfBytesRead += input.read(fBaseAttr); + numOfBytesRead += input.read(fTextAttr); + numOfBytesRead += input.read(icvFore); + numOfBytesRead += input.read(icvFill); + bRight = (byte) input.read(); + bTop = (byte) input.read(); + bLeft = (byte) input.read(); + bBottom = (byte) input.read(); + backstyle = (byte) input.read(); + borderstyle = (byte) input.read(); + numOfBytesRead += 6; + + Debug.log(Debug.TRACE,"\tixfnt : "+ EndianConverter.readShort(ixfnt) + + " ixnf : " + EndianConverter.readShort(ixnf) + + " fattributes : " + EndianConverter.readInt(fattributes) + + " fBaseAttr : " + EndianConverter.readShort(fBaseAttr) + + "\n\tfTextAttr : " + EndianConverter.readShort(fTextAttr) + + " icvFore : " + EndianConverter.readShort(icvFore) + + " icvFill : " + EndianConverter.readShort(icvFill) + + " bRight : " + bRight + + "\n\tbTop : " + bTop + + " bLeft : " + bLeft + + " bBottom : " + bBottom + + " backstyle : " + backstyle + + " borderstyle : " + borderstyle); + return numOfBytesRead; + } + + /** + * Writes the ExtendedFormat to the <code>Outputstream<code> + * + * @param output the <code>Outputstream</code>to write to + */ + public void write(OutputStream output) throws IOException { + + output.write(getBiffType()); + output.write(ixfnt); + output.write(ixnf); + output.write(fattributes); + output.write(fBaseAttr); + output.write(fTextAttr); + output.write(icvFore); + output.write(icvFill); + output.write(bRight); + output.write(bTop); + output.write(bLeft); + output.write(bBottom); + output.write(backstyle); + output.write(borderstyle); + + Debug.log(Debug.TRACE,"Writing ExtendedFormat record"); + + } + +} diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/FloatNumber.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/FloatNumber.java new file mode 100644 index 000000000000..db80eccb9cb8 --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/FloatNumber.java @@ -0,0 +1,124 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: FloatNumber.java,v $ + * $Revision: 1.6 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +package org.openoffice.xmerge.converter.xml.sxc.pexcel.records; + +import java.io.DataInputStream; +import java.io.OutputStream; +import java.io.InputStream; +import java.io.IOException; + +import org.openoffice.xmerge.util.Debug; +import org.openoffice.xmerge.util.EndianConverter; +import org.openoffice.xmerge.converter.xml.sxc.pexcel.PocketExcelConstants; + +/** + * Represents a BIFF Record describing a floating point + */ +public class FloatNumber extends CellValue { + + protected byte[] num = new byte[8]; + + /** + * Constructs a pocket Excel Document from the + * <code>InputStream</code> and assigns it the document name passed in + * + * @param is InputStream containing a Pocket Excel Data file. + */ + public FloatNumber(InputStream is) throws IOException { + read(is); + } + + /** + * Constructs a <code>FloatNumber</code> using specified attributes + * + * @param row row number + * @param col column number + * @param cellContents contents of the cell + * @param ixfe font index + */ + public FloatNumber(int row, int column, String cellContents, int ixfe) throws IOException { + + setIxfe(ixfe); + setRow(row); + setCol(column); + double cellLong = (double) Double.parseDouble(cellContents); + num = EndianConverter.writeDouble(cellLong); + } + + /** + * Get the hex code for this particular <code>BIFFRecord</code> + * + * @return the hex code for <code>FloatNumber</code> + */ + public short getBiffType() { + return PocketExcelConstants.NUMBER_CELL; + } + + /** + * Reads a<code>FloatNumber</code> from the specified <code>InputStream</code> + * + * @param input the <code>InputStram</code> to read from + */ + public int read(InputStream input) throws IOException { + + int numOfBytesRead = super.read(input); + + numOfBytesRead += input.read(num); + + Debug.log(Debug.TRACE," num : " + getString()); + return numOfBytesRead; + } + + public void write(OutputStream output) throws IOException { + + output.write(getBiffType()); + + super.write(output); + + output.write(num); + + Debug.log(Debug.TRACE,"Writing FloatNumber record"); + } + + + /** + * Gets the numerical value the cell represents + * + * @return the <code>String</code> representing a double value + */ + public String getString() throws IOException { + + double value = EndianConverter.readDouble(num); + Double myDo = new Double(value); + return myDo.toString(); + } + +} diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/FontDescription.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/FontDescription.java new file mode 100644 index 000000000000..50ff9e5e64d7 --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/FontDescription.java @@ -0,0 +1,290 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: FontDescription.java,v $ + * $Revision: 1.7 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +package org.openoffice.xmerge.converter.xml.sxc.pexcel.records; + +import java.io.DataInputStream; +import java.io.OutputStream; +import java.io.InputStream; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.awt.Color; + +import org.openoffice.xmerge.converter.xml.sxc.Format; +import org.openoffice.xmerge.util.Debug; +import org.openoffice.xmerge.util.EndianConverter; +import org.openoffice.xmerge.util.ColourConverter; +import org.openoffice.xmerge.converter.xml.sxc.pexcel.PocketExcelConstants; + + +/** + * Represents a BIFF Record descibing a font used + */ +public class FontDescription implements BIFFRecord { + + private byte[] dwHeight = new byte[2]; + private byte[] grbit = new byte[2]; + private byte[] icvFore = new byte[2]; + private byte[] bls = new byte[2]; + private byte[] Reserved2 = new byte[2]; + private byte uls; + private byte bFamily; + private byte bCharSet; + private byte Reserved3; + private byte cch; + private byte[] rgch; + + public static final int UNDERLINE = 0x01; + public static final int ITALIC = 0x02; + + /** + * Constructs a FontDescription from the bold italic and undelrine attributes + * + * @param italic Italic attribute + * @param bold Bold attribute + * @param underline Underline attribute + */ + public FontDescription(Format fmt) throws IOException { + + Debug.log(Debug.TRACE,"Building FontDescriptor based on Format : " + fmt); + + this.dwHeight = EndianConverter.writeShort((short) (fmt.getFontSize()*20)); + + grbit = new byte[] {(byte)0x00, (byte)0x00}; + bls = EndianConverter.writeShort((short) 400); + uls = 0; + + if (fmt.getAttribute(Format.ITALIC)) + grbit[0] |= ITALIC; + + if (fmt.getAttribute(Format.BOLD)) + bls = EndianConverter.writeShort((short) 700); + + if (fmt.getAttribute(Format.UNDERLINE)) + uls |= UNDERLINE; + + + bFamily = 0; + bCharSet = 0; + + String fontName = fmt.getFontName(); + if( !fontName.equals("Tahoma") && + !fontName.equals("Courier New")) { + // We will set our default font to be Tahoma + fontName = new String("Tahoma"); + } + + cch = (byte) fontName.length(); + rgch = fontName.getBytes("UTF-16LE"); + + Color foreground = fmt.getForeground(); + if( foreground != null ) { + ColourConverter cc = new ColourConverter(PocketExcelConstants.cLookup); + icvFore = EndianConverter.writeShort(cc.convertFromRGB(foreground)); + } else { + icvFore = new byte[] {(byte)0xFF,(byte)0x00}; + } + + Reserved2 = EndianConverter.writeShort((short) 0); + Reserved3 = 0; + + } + + /** + * Tests if this font descriptor defines italic + * + * @return true if italic otherwise false + */ + public boolean isItalic() { + + return (EndianConverter.readShort(grbit) == 2); + } + + /** + * Tests if this font descriptor defines underline + * + * @return true if underline otherwise false + */ + public boolean isUnderline() { + + return (uls == 1); + } + + /** + * Tests if this font descriptor defines bold + * + * @return true if bold otherwise false + */ + public boolean isBold() { + + return (EndianConverter.readShort(bls) == 700); + } + + /** + * Get the background color this format uses + * + * @return the background color + */ + public Color getForeground() { + short rgb = EndianConverter.readShort(icvFore); + Color c = null; + if(rgb!=0xFF) { + ColourConverter cc = new ColourConverter(PocketExcelConstants.cLookup); + c = cc.convertToRGB(rgb); + } + return c; + } + + /** + * Compares current font descriptor against one passed in + * + * @return true if attrbitues are the same + */ + public boolean compareTo(FontDescription rhs) { + + if(EndianConverter.readShort(icvFore) != + EndianConverter.readShort(rhs.icvFore)) + return false; + + if (EndianConverter.readShort(dwHeight) != + EndianConverter.readShort(dwHeight)) + return false; + + if (this.getFont() != rhs.getFont()) + return false; + + if (this.isBold() != rhs.isBold()) + return false; + + if (this.isUnderline() != rhs.isUnderline()) + return false; + + if (this.isItalic() != rhs.isItalic()) + return false; + + return true; + } + + + /** + * Constructs a Font Description from the <code>InputStream</code> + * + * @param is InputStream containing a <code>FontDescription</code> + */ + public FontDescription(InputStream is) throws IOException { + read(is); + } + + /** + * Get the hex code for this particular <code>BIFFRecord</code> + * + * @return the hex code for <code>FontDescription</code> + */ + public short getBiffType() { + return PocketExcelConstants.FONT_DESCRIPTION; + } + + /** + * Get the Font size + * + */ + public int getFontSize() { + return EndianConverter.readShort(dwHeight)/20; + } + + /** + * Get the font name + * + */ + public String getFont() { + + String name; + + try { + name = new String(rgch, "UTF-16LE"); + } catch (UnsupportedEncodingException e){ + name = "Tahoma"; + } + return name; + } + + /** + * Constructs a Font Description from the <code>InputStream</code> + * + * @param is InputStream containing a <code>FontDescription</code> + */ + public int read(InputStream input) throws IOException { + + int numOfBytesRead = input.read(dwHeight); + numOfBytesRead += input.read(grbit); + numOfBytesRead += input.read(icvFore); + numOfBytesRead += input.read(bls); + numOfBytesRead += input.read(Reserved2); + uls = (byte) input.read(); + bFamily = (byte) input.read(); + bCharSet = (byte) input.read(); + Reserved3 = (byte) input.read(); + cch = (byte) input.read(); + numOfBytesRead += 5; + + rgch = new byte[cch*2]; + input.read(rgch, 0, cch*2); + + Debug.log(Debug.TRACE,"\tdwHeight : "+ EndianConverter.readShort(dwHeight) + + " grbit : " + EndianConverter.readShort(grbit) + + " bls : " + EndianConverter.readShort(bls) + + " uls : " + uls + + "\n\tFamily : " + bFamily + + " bCharSet : " + bCharSet + + " cch : " + cch + + " rgch : " + new String(rgch,"UTF-16LE")); + + return numOfBytesRead; + } + + public void write(OutputStream output) throws IOException { + + output.write(getBiffType()); + output.write(dwHeight); + output.write(grbit); + output.write(icvFore); + output.write(bls); + output.write(Reserved2); + output.write(uls); + output.write(bFamily); + output.write(bCharSet); + output.write(Reserved3); + output.write(cch); + output.write(rgch); + + Debug.log(Debug.TRACE,"Writing FontDescription record"); + } + +} diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/Formula.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/Formula.java new file mode 100644 index 000000000000..b2f74f1f6be9 --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/Formula.java @@ -0,0 +1,266 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: Formula.java,v $ + * $Revision: 1.11 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +package org.openoffice.xmerge.converter.xml.sxc.pexcel.records; + +import java.io.DataInputStream; +import java.io.OutputStream; +import java.io.InputStream; +import java.io.IOException; +import java.util.Vector; +import java.util.Enumeration; +import java.util.Calendar; +import java.util.Date; +import java.text.DateFormat; + +import org.openoffice.xmerge.util.Debug; +import org.openoffice.xmerge.util.EndianConverter; +import org.openoffice.xmerge.converter.xml.OfficeConstants; +import org.openoffice.xmerge.converter.xml.sxc.Format; +import org.openoffice.xmerge.converter.xml.sxc.pexcel.records.formula.FormulaHelper; +import org.openoffice.xmerge.converter.xml.sxc.pexcel.records.Workbook; +import org.openoffice.xmerge.converter.xml.sxc.pexcel.PocketExcelConstants; + + +/** + * Represents a BIFF Record describing a formula + */ +public class Formula extends CellValue implements OfficeConstants { + + private byte[] num = new byte[8]; + private byte grbit; + private byte[] cce = new byte[2]; + private byte[] rgce; + private FormulaHelper fh = new FormulaHelper(); + + /** + * Constructs a <code>Formula</code> using specified attributes + * + * @param row row number + * @param col column number + * @param cellContents contents of the cell + * @param ixfe font index + * @param value the value of the cell + */ + public Formula(int row, int column, String cellContents, int ixfe, Format fmt, Workbook wb) + throws Exception { + + fh.setWorkbook(wb); + + setRow(row); + setCol(column); + setIxfe(ixfe); + setFormula(cellContents); + + String category = fmt.getCategory(); + String value = fmt.getValue(); + + if(category.equalsIgnoreCase(CELLTYPE_BOOLEAN)) { + num[0]=(byte)0x01; + num[1]=(byte)0x00; + if(value.equalsIgnoreCase("true")) { + num[2]=(byte)0x01; + } else { + num[2]=(byte)0x00; + } + num[3]=(byte)0x00;num[4]=(byte)0x00;num[5]=(byte)0x00; + num[6]=(byte)0xFF;num[7]=(byte)0xFF; + } else if(category.equalsIgnoreCase(CELLTYPE_DATE)) { + Debug.log(Debug.TRACE,"Date Formula"); + num = EndianConverter.writeDouble(toExcelSerialDate(fmt.getValue())); + } else if(category.equalsIgnoreCase(CELLTYPE_TIME)) { + Debug.log(Debug.TRACE,"Time Formula"); + num = EndianConverter.writeDouble(toExcelSerialTime(fmt.getValue())); + } else if(category.equalsIgnoreCase(CELLTYPE_PERCENT)) { + Debug.log(Debug.TRACE,"Percent Formula"); + double percent = (double) Double.parseDouble(fmt.getValue()); + num = EndianConverter.writeDouble(percent); + } else if(category.equalsIgnoreCase(CELLTYPE_CURRENCY)) { + Debug.log(Debug.TRACE,"Currency Formula"); + } else if(category.equalsIgnoreCase(CELLTYPE_STRING)) { + Debug.log(Debug.TRACE,"String Formula"); + num[0]=(byte)0x00; + num[1]=(byte)0x00; + num[2]=(byte)0x00; + num[3]=(byte)0x00; + num[4]=(byte)0x00; + num[5]=(byte)0x00; + num[6]=(byte)0xFF; + num[7]=(byte)0xFF; + } else { + Debug.log(Debug.TRACE,"Float Formula"); + double cellLong = (double) Double.parseDouble(fmt.getValue()); + num = EndianConverter.writeDouble(cellLong); + } + } + + /** + * Translates a <code>String</code> written in infix which represents a + * formula into a byte[] what can be written to pocket excel file. + * + * @param formula string + */ + public void setFormula(String inFormula) throws Exception { + + rgce = fh.convertCalcToPXL(inFormula); + cce = EndianConverter.writeShort((short) rgce.length); + } + + /** + * Constructs a pocket Excel formula from the + * <code>InputStream</code> + * + * @param is InputStream containing a Pocket Excel Data file. + */ + public Formula(InputStream is, Workbook wb) throws IOException { + read(is); + fh.setWorkbook(wb); + } + + /** + * Get the hex code for this particular <code>BIFFRecord</code> + * + * @return the hex code for <code>Formula</code> + */ + public short getBiffType() { + return PocketExcelConstants.FORMULA_CELL; + } + + /** + * Reads the formula data members from the stream. Byte arrays for Strings + * are doubled as they are stored as unicode + * + * @return total number of bytes read + */ + public int read(InputStream input) throws IOException { + + int numOfBytesRead = super.read(input); + + numOfBytesRead += input.read(num); + grbit = (byte) input.read(); + numOfBytesRead ++; + numOfBytesRead += input.read(cce); + + int strLen = EndianConverter.readShort(cce); + rgce = new byte[strLen]; + input.read(rgce, 0, strLen); + + Debug.log(Debug.TRACE, " num : " + num + + "\n\tgrbit : " + grbit + + " cce : " + EndianConverter.readShort(cce) + + " rgce : " + new String(rgce,"UTF-16LE") + + "\n" + numOfBytesRead + " Bytes Read"); + + return numOfBytesRead; + } + + /** + * Writes the Formula record to the <code>OutputStream</code> + * + * @param the <code>OutputStream</code> being written to + */ + public void write(OutputStream output) throws IOException { + + output.write(getBiffType()); + + super.write(output); + + output.write(num); + output.write(grbit); + output.write(cce); + output.write(rgce); + + Debug.log(Debug.TRACE,"Writing Formula record"); + } + + /** + * Gets the <code>String</code> representing the cell value + * + * @return the <code>String</code> representing the cell value + */ + public String getValue() throws IOException { + + double value = EndianConverter.readDouble(num); + Double myDo = new Double(value); + return myDo.toString(); + } + + /** + * Gets the <code>String</code> representing the cells contents + * + * @return the <code>String</code> representing the cells contents + */ + public String getString() throws IOException { + + return fh.convertPXLToCalc(rgce); + } + + /** + * Excel dates are the number of days since 1/1/1900. This method converts + * to this date. + * + * @param s String representing a date in the form YYYY-MM-DD + * @return The excel serial date + */ + public long toExcelSerialDate(String s) throws IOException { + + int year = Integer.parseInt(s.substring(0,4)); + int month = Integer.parseInt(s.substring(5,7)); + int day = Integer.parseInt(s.substring(8,10)); + + long serialDate = (1461 * (year + 4800 + (month - 14) / 12)) / 4 + + (367 * (month - 2 - 12 * ((month - 14) / 12))) / 12 - + (3 * ((year + 4900 + (month - 14) / 12)) / 100) / 4 + + day - 2415019 - 32075; + + return serialDate; + } + + /** + * Excel times are a fraction of a 24 hour day expressed in seconds. This method converts + * to this time. + * + * @param s String representing a time in the form ??HH?MM?SS? + * @return The excel serial time + */ + public double toExcelSerialTime(String s) throws IOException { + + int hours = Integer.parseInt(s.substring(2,4)); + int mins = Integer.parseInt(s.substring(5,7)); + int secs = Integer.parseInt(s.substring(8,10)); + + int timeSecs = (hours*3600) + (mins*60) + (secs); + + double d = (double) timeSecs / (24 * 3600); + + return d; + } + +} diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/LabelCell.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/LabelCell.java new file mode 100644 index 000000000000..aafe992cad47 --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/LabelCell.java @@ -0,0 +1,143 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: LabelCell.java,v $ + * $Revision: 1.6 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +package org.openoffice.xmerge.converter.xml.sxc.pexcel.records; + +import java.io.DataInputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; + +import org.openoffice.xmerge.util.Debug; +import org.openoffice.xmerge.util.EndianConverter; +import org.openoffice.xmerge.converter.xml.sxc.pexcel.PocketExcelConstants; + + +/** + * Reperesent a BIFF Record descibing a cell containing a string + */ +public class LabelCell extends CellValue { + + private byte[] cch = new byte[2]; + private byte[] rgch; + + /** + * Constructs a <code>LabelCell</code> using specified attributes + * + * @param row row number + * @param col column number + * @param cellContents contents of the cell + * @param ixfe font index + */ + public LabelCell(int row, int column, String cellContents, int ixfe) + throws IOException { + + setLabel(cellContents); + setRow(row); + setCol(column); + setIxfe(ixfe); + } + + /** + * Reads a LabelCell from the <code>InputStream</code> + * + * @param is the <code>Inputstream</code> to read from + */ + public LabelCell(InputStream is) throws IOException { + read(is); + } + + /** + * Writes a <code>LabelCell</code> to the specified <code>Outputstream</code> + * + * @param os the <code>OutputStream</code> to write to + */ + public void write(OutputStream output) throws IOException { + + output.write(getBiffType()); + + super.write(output); + + output.write(cch); + output.write(rgch); + + Debug.log(Debug.TRACE,"Writing Label record"); + } + + /** + * Get the hex code for this particular <code>BIFFRecord</code> + * + * @return the hex code for <code>LabelCell</code> + */ + public short getBiffType() { + return PocketExcelConstants.LABEL_CELL; + } + + /** + * Reads a<code>LabelCell</code> from the specified <code>InputStream</code> + * + * @param input the <code>InputStram</code> to read from + */ + public int read(InputStream input) throws IOException { + + int numOfBytesRead = super.read(input); + + numOfBytesRead += input.read(cch); + + int strLen = EndianConverter.readShort(cch)*2; + rgch = new byte[strLen]; + input.read(rgch, 0, strLen); + + Debug.log(Debug.TRACE, " cch : " + EndianConverter.readShort(cch) + + " rgch : " + new String(rgch, "UTF-16LE")); + + return numOfBytesRead; + } + + + /** + * Gets the <code>String</code> representing the cells contents + * + * @return the <code>String</code> representing the cells contents + */ + public String getString() throws IOException { + return (new String(rgch,"UTF-16LE")); + } + + /** + * Sets the <code>String</code> representing the cells contents + * + * @return the <code>String</code> representing the cells contents + */ + private void setLabel(String cellContents) throws IOException { + rgch = cellContents.getBytes("UTF-16LE"); + cch = EndianConverter.writeShort((short)cellContents.length()); + } +} diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/NumberFormat.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/NumberFormat.java new file mode 100644 index 000000000000..0735b401a6b8 --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/NumberFormat.java @@ -0,0 +1,98 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: NumberFormat.java,v $ + * $Revision: 1.5 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +package org.openoffice.xmerge.converter.xml.sxc.pexcel.records; + +import java.io.DataInputStream; +import java.io.OutputStream; +import java.io.InputStream; +import java.io.IOException; + +import org.openoffice.xmerge.util.Debug; +import org.openoffice.xmerge.util.EndianConverter; +import org.openoffice.xmerge.converter.xml.sxc.pexcel.PocketExcelConstants; + + +/** + * Represents a BIFF Record describing a number format + */ +public class NumberFormat implements BIFFRecord { + + private byte cce; + private byte[] rgch; + + /** + * Constructs a NumberFormat Record from the <code>InputStream</code> + * + * @param is InputStream containing the record data + */ + public NumberFormat(InputStream is) throws IOException { + read(is); + } + + /** + * Get the hex code for this particular <code>BIFFRecord</code> + * + * @return the hex code for <code>NumberFormat</code> + */ + public short getBiffType() { + return PocketExcelConstants.NUMBER_FORMAT; + } + + /** + * Reads the NumberFormat from the <code>InputStream</code> Byte array + * containg strings are doubled in length becuse they use unicode + * + * @return the total number of bytes read + */ + public int read(InputStream input) throws IOException { + + cce = (byte) input.read(); + int numOfBytesRead = 1; + + rgch = new byte[cce*2]; + numOfBytesRead += input.read(rgch, 0, cce*2); + + Debug.log(Debug.TRACE, "\tcce : "+ cce + + " rgch : " + new String(rgch,"UTF-16LE")); + + return numOfBytesRead; + } + + public void write(OutputStream output) throws IOException { + + output.write(getBiffType()); + output.write(cce); + output.write(rgch); + + Debug.log(Debug.TRACE,"Writing NumberFormat record"); + } + +} diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/Pane.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/Pane.java new file mode 100644 index 000000000000..4155a658ae40 --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/Pane.java @@ -0,0 +1,222 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: Pane.java,v $ + * $Revision: 1.7 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +package org.openoffice.xmerge.converter.xml.sxc.pexcel.records; + +import java.io.DataInputStream; +import java.io.OutputStream; +import java.io.InputStream; +import java.io.IOException; +import java.awt.Point; + +import org.openoffice.xmerge.util.Debug; +import org.openoffice.xmerge.util.EndianConverter; +import org.openoffice.xmerge.converter.xml.sxc.pexcel.PocketExcelConstants; +import org.openoffice.xmerge.converter.xml.sxc.SheetSettings; + +/** + * Represents a BIFF Record that describes the number and position of unfrozen + * panes. + */ +public class Pane implements BIFFRecord { + + private byte[] x = new byte[2]; + private byte[] y = new byte[2]; + private byte[] rwTop = new byte[2]; + private byte[] colLeft = new byte[2]; + private byte pnnAcct; + + /** + * Default Constructor + */ + public Pane() { + pnnAcct = (byte) 0x02; // Default setting + } + + /** + * Constructs a Pane Record from the <code>InputStream</code> + * + * @param is InputStream containing a Pane record + */ + public Pane(InputStream is) throws IOException { + read(is); + } + + /** + * Get the hex code for this particular <code>BIFFRecord</code> + * + * @return the hex code for <code>Pane</code> + */ + public short getBiffType() { + return PocketExcelConstants.PANE_INFO; + } + + /** + * Gets the split point for this pane, in the case of splits this will be + * in twips. + * + * @return the split point + */ + public Point getSplitPoint() { + + int xTwips = EndianConverter.readShort(x)/11; + int yTwips = EndianConverter.readShort(y)/15; + return (new Point(xTwips, yTwips)); + } + + /** + * Gets the freeze point for this pane, in the case of freezes this will + * be a zero-based index to either the column or row. + * + * @return the freeze point + */ + public Point getFreezePoint() { + + return (new Point(EndianConverter.readShort(x), + EndianConverter.readShort(y))); + } + + /** + * Sets the split point for this pane, coordinates are in column row units + * if the split type is freeze or twips if split type is split. + * + * @param splitType contains the X and Y split types (freeze or split) + * @param p the split point + */ + public void setSplitPoint(Point splitType, Point p) { + + if(splitType.getX()==SheetSettings.SPLIT + || splitType.getY()==SheetSettings.SPLIT) { + int yTwips = (int) p.getY(); + short yPxl = (short) (yTwips * 15); + y = EndianConverter.writeShort(yPxl); + int xTwips = (int) p.getX(); + short xPxl = (short) (xTwips * 11); + x = EndianConverter.writeShort(xPxl); + } else { + y = EndianConverter.writeShort((short) p.getY()); + x = EndianConverter.writeShort((short) p.getX()); + } + + } + + /** + * Get the hex code for this particular <code>BIFFRecord</code> + * + * @return the hex code for <code>Pane</code> + */ + public void setPaneNumber(int paneNumber) { + pnnAcct = (byte) paneNumber; + } + + /** + * Get the pane number of the active pane + * 0 - bottom right, 1 - top right + * 2 - bottom left, 3 - top left + * + * @return the hex code for <code>Pane</code> + */ + public int getPaneNumber() { + return pnnAcct; + } + + /** + * Set the top row visible in the lower pane + * + * @param top 0-based inex of the top row + */ + public void setTop(int top) { + rwTop = EndianConverter.writeShort((short)top); + } + + /** + * Set leftmost column visible in the right pane + * + * @param left 0-based index of the leftmost column + */ + public void setLeft(int left) { + colLeft = EndianConverter.writeShort((short)left); + } + + /** + * Get the top row visible in the lower pane + * + * @return the hex code for <code>Pane</code> + */ + public int getTop() { + return EndianConverter.readShort(rwTop); + } + + /** + * Get leftmost column visible in the right pane + * + * @return 0-based index of the column + */ + public int getLeft() { + return EndianConverter.readShort(colLeft); + } + + + /** + * Reads a <code>Pane</code> record from the <code>InputStream</code> + * + * @param input <code>InputStream</code> to read from + * @return the total number of bytes read + */ + public int read(InputStream input) throws IOException { + + int numOfBytesRead = input.read(x); + numOfBytesRead += input.read(y); + numOfBytesRead += input.read(rwTop); + numOfBytesRead += input.read(colLeft); + pnnAcct = (byte) input.read(); + numOfBytesRead++; + + Debug.log(Debug.TRACE, "\tx : "+ EndianConverter.readShort(x) + + " y : " + EndianConverter.readShort(y) + + " rwTop : " + EndianConverter.readShort(rwTop) + + " colLeft : " + EndianConverter.readShort(colLeft) + + " pnnAcct : " + pnnAcct); + + return numOfBytesRead; + } + + public void write(OutputStream output) throws IOException { + + output.write(getBiffType()); + output.write(x); + output.write(y); + output.write(rwTop); + output.write(colLeft); + output.write(pnnAcct); + + Debug.log(Debug.TRACE,"Writing Pane record"); + } +} diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/Row.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/Row.java new file mode 100644 index 000000000000..5ffb49da45d5 --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/Row.java @@ -0,0 +1,142 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: Row.java,v $ + * $Revision: 1.7 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +package org.openoffice.xmerge.converter.xml.sxc.pexcel.records; + +import java.io.DataInputStream; +import java.io.OutputStream; +import java.io.InputStream; +import java.io.IOException; + +import org.openoffice.xmerge.util.Debug; +import org.openoffice.xmerge.util.EndianConverter; +import org.openoffice.xmerge.converter.xml.sxc.pexcel.PocketExcelConstants; + + +/** + * Represents s BIFF Record that describes the format of a column + */ +public class Row implements BIFFRecord { + + private byte[] rw = new byte[2]; + private byte[] miyRw = new byte[2]; + private byte[] grbit = new byte[2]; + private byte[] ixfe = new byte[2]; + private float scale = (float) 1; + + /** + * Constructs a pocket Excel Document from the + * <code>InputStream</code> and assigns it the document name passed in + * + * @param rw Zero based row number + * @param miyRw row height + */ + public Row(int rw, int miyRw, boolean userDefined) { + this.rw = EndianConverter.writeShort((short) rw); + miyRw *= scale; + this.miyRw = EndianConverter.writeShort((short) miyRw); + if(userDefined) { + grbit = EndianConverter.writeShort((short) 2); + } else { + grbit = EndianConverter.writeShort((short) 0); + } + ixfe = EndianConverter.writeShort((short) 0); + } + + /** + * Constructs a Row fro man <code>InputStream</code> + * + * @param is InputStream containing a Pane Record + */ + public Row(InputStream is) throws IOException { + read(is); + } + + /** + * Get the hex code for this particular <code>BIFFRecord</code> + * + * @return the hex code for <code>Row</code> + */ + public short getBiffType() { + return PocketExcelConstants.ROW_DESCRIPTION; + } + + /** + * Get the height of this row + * + * @return the height of this row + */ + public short getRowHeight() { + return EndianConverter.readShort(miyRw); + } + + /** + * Get the rown number for this style + * + * @return the row this style applies to + */ + public short getRowNumber() { + return EndianConverter.readShort(rw); + } + + /** + * Reads a Row from an <code>InputStream</code> + * + * @param is InputStream containing a Pane Record + */ + public int read(InputStream input) throws IOException { + + int numOfBytesRead = input.read(rw); + numOfBytesRead += input.read(miyRw); + short scaledHeight = (short) (EndianConverter.readShort(miyRw) / scale); + miyRw = EndianConverter.writeShort(scaledHeight); + numOfBytesRead += input.read(grbit); + numOfBytesRead += input.read(ixfe); + + Debug.log(Debug.TRACE,"\trw : "+ EndianConverter.readShort(rw) + + " miyRw : " + EndianConverter.readShort(miyRw) + + " grbit : " + EndianConverter.readShort(grbit) + + " ixfe : " + EndianConverter.readShort(ixfe)); + return numOfBytesRead; + } + + public void write(OutputStream output) throws IOException { + + output.write(getBiffType()); + output.write(rw); + output.write(miyRw); + output.write(grbit); + output.write(ixfe); + + Debug.log(Debug.TRACE,"Writing Row record"); + + } + +} diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/Selection.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/Selection.java new file mode 100644 index 000000000000..bafafc9c371f --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/Selection.java @@ -0,0 +1,146 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: Selection.java,v $ + * $Revision: 1.6 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +package org.openoffice.xmerge.converter.xml.sxc.pexcel.records; + +import java.io.DataInputStream; +import java.io.OutputStream; +import java.io.InputStream; +import java.io.IOException; +import java.awt.Point; + +import org.openoffice.xmerge.util.Debug; +import org.openoffice.xmerge.util.EndianConverter; +import org.openoffice.xmerge.converter.xml.sxc.pexcel.PocketExcelConstants; + + +/** + * Represents a BIFF Record that describes the selected area of a worksheet + */ +public class Selection implements BIFFRecord { + + private byte[] rwTop = new byte[2]; + private byte colLeft; + private byte[] rwBottom = new byte[2]; + private byte colRight; + private byte[] rwActive = new byte[2]; + private byte colActive; + + /** + * Default Constructor + */ + public Selection() { + this.rwTop = EndianConverter.writeShort((short) 0); + this.colLeft = 0; + this.rwBottom = EndianConverter.writeShort((short) 0); + this.colRight = 0; + this.rwActive = EndianConverter.writeShort((short) 0); + this.colActive = 0; + + } + + /** + * Constructs a Selection Record from the <code>InputStream</code> + * + * @param is InputStream containing a Pocket Excel Data file. + */ + public Selection(InputStream is) throws IOException { + read(is); + } + + /** + * Get the hex code for this particular <code>BIFFRecord</code> + * + * @return the hex code for <code>Selection</code> + */ + public short getBiffType() { + return PocketExcelConstants.CURRENT_SELECTION; + } + + /** + * Get the hex code for this particular <code>BIFFRecord</code> + * + * @return the hex code for <code>Selection</code> + */ + public Point getActiveCell() { + Point p = new Point(colActive, EndianConverter.readShort(rwActive)); + return p; + } + + /** + * Get the hex code for this particular <code>BIFFRecord</code> + * + * @return the hex code for <code>Selection</code> + */ + public void setActiveCell(Point p) { + + colActive = (byte) p.getX(); + rwActive = EndianConverter.writeShort((short) p.getY()); + } + + /** + * Reads a Selection Record from the <code>InputStream</code> + * + * @param is InputStream containing a Pocket Excel Data file. + */ + public int read(InputStream input) throws IOException { + + int numOfBytesRead = input.read(rwTop); + colLeft += (byte) input.read(); + numOfBytesRead += input.read(rwBottom); + colRight += (byte) input.read(); + numOfBytesRead += input.read(rwActive); + colActive += (byte) input.read(); + numOfBytesRead += 3; + + Debug.log(Debug.TRACE,"\trwTop : "+ EndianConverter.readShort(rwTop) + + " colLeft : " + colLeft + + " rwBottom : " + EndianConverter.readShort(rwBottom) + + " colRight : "+ colRight + + " rwActive : " + EndianConverter.readShort(rwActive) + + " colActive : " + colActive); + + return numOfBytesRead; + } + + public void write(OutputStream output) throws IOException { + + output.write(getBiffType()); + output.write(rwTop); + output.write(colLeft); + output.write(rwBottom); + output.write(colRight); + output.write(rwActive); + output.write(colActive); + + Debug.log(Debug.TRACE,"Writing Selection record"); + } + +} diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/StringValue.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/StringValue.java new file mode 100644 index 000000000000..772affb2e817 --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/StringValue.java @@ -0,0 +1,128 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: StringValue.java,v $ + * $Revision: 1.7 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +package org.openoffice.xmerge.converter.xml.sxc.pexcel.records; + +import java.io.DataInputStream; +import java.io.OutputStream; +import java.io.InputStream; +import java.io.IOException; +import java.io.UnsupportedEncodingException; + +import org.openoffice.xmerge.util.Debug; +import org.openoffice.xmerge.util.EndianConverter; +import org.openoffice.xmerge.converter.xml.sxc.pexcel.PocketExcelConstants; + + +/** + * Represents a BIFF Record that describes the value of a formula that + * evaluates to a string + */ +public class StringValue implements BIFFRecord { + + private byte[] cch = new byte[2]; + private byte[] rgch; + + /** + * Constructs a StringValue Record from an <code>InputStream</code> + * + * @param is InputStream containing a StringValue Record + */ + public StringValue(String str) throws IOException { + cch = EndianConverter.writeShort((short) str.length()); + rgch = new byte[str.length()]; + rgch = str.getBytes("UTF-16LE"); + } + + /** + * Constructs a StringValue Record from an <code>InputStream</code> + * + * @param is InputStream containing a StringValue Record + */ + public StringValue(InputStream is) throws IOException { + read(is); + } + + /** + * Get the hex code for this particular <code>BIFFRecord</code> + * + * @return the hex code for <code>StringValue</code> + */ + public short getBiffType() { + return PocketExcelConstants.FORMULA_STRING; + } + + /** + * Reads a StringVlaue Record from an <code>InputStream</code> + * + * @param is InputStream containing a StringValue Record + */ + public int read(InputStream input) throws IOException { + + cch[0] = (byte) input.read(); + cch[1] = (byte) input.read(); + int numOfBytesRead = 1; + + int strlen = EndianConverter.readShort(cch)*2; + rgch = new byte[strlen]; + numOfBytesRead += input.read(rgch, 0, strlen); + + Debug.log(Debug.TRACE,"\tcch : "+ cch + + " rgch : " + rgch); + + return numOfBytesRead; + } + + public void write(OutputStream output) throws IOException { + + output.write(getBiffType()); + output.write(cch); + output.write(rgch); + + Debug.log(Debug.TRACE,"Writing StringValue record"); + } + + /** + * Gets the <code>String</code> representing the cells contents + * + * @return the <code>String</code> representing the cells contents + */ + public String getString() throws IOException { + String name; + + try { + name = new String(rgch, "UTF-16LE"); + } catch (UnsupportedEncodingException e){ + name = "unknown"; + } + return name; + } + +} diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/UnsupportedFormulaException.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/UnsupportedFormulaException.java new file mode 100644 index 000000000000..2446e86089ed --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/UnsupportedFormulaException.java @@ -0,0 +1,46 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: UnsupportedFormulaException.java,v $ + * $Revision: 1.4 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +package org.openoffice.xmerge.converter.xml.sxc.pexcel.records; + +import java.lang.Exception; + +import org.openoffice.xmerge.util.Debug; + +/** + * At the moment any functions within a formula will result in this exception + * being thrown. + */ +public class UnsupportedFormulaException extends Exception { + + public UnsupportedFormulaException(String message){ + super(message); + } +} diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/Window1.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/Window1.java new file mode 100644 index 000000000000..24b8aa92f580 --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/Window1.java @@ -0,0 +1,119 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: Window1.java,v $ + * $Revision: 1.6 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +package org.openoffice.xmerge.converter.xml.sxc.pexcel.records; + +import java.io.DataInputStream; +import java.io.OutputStream; +import java.io.InputStream; +import java.io.IOException; + +import org.openoffice.xmerge.util.Debug; +import org.openoffice.xmerge.util.EndianConverter; +import org.openoffice.xmerge.converter.xml.sxc.pexcel.PocketExcelConstants; + + +/** + * Represents a BIFF REcord that describes workbook window attributes + */ +public class Window1 implements BIFFRecord { + + private byte[] grbit = new byte[2]; + private byte[] itabCur = new byte[2]; // index of selected worksheet + + /** + * Constructor + */ + public Window1() { + grbit = EndianConverter.writeShort((short) 0); + itabCur = EndianConverter.writeShort((short) 0); + } + + /** + * Constructs a Window1 Record from an <code>InputStream</code> + * + * @param is InputStream containing a Window1 Record + */ + public Window1(InputStream is) throws IOException{ + read(is); + } + + /** + * Set the number of the active sheet + * + * @param activeSheet number of the active sheet + */ + public void setActiveSheet(int activeSheet) { + itabCur = EndianConverter.writeShort((short) activeSheet); + } + + /** + * Get the number of the active sheet + * + * @return number of the active sheet + */ + public int getActiveSheet() { + return EndianConverter.readShort(itabCur); + } + + /** + * Get the hex code for this particular <code>BIFFRecord</code> + * + * @return the hex code for <code>Window1</code> + */ + public short getBiffType() { + return PocketExcelConstants.WINDOW_INFO; + } + + /** + * Reads a Window1 Record from an <code>InputStream</code> + * + * @param is InputStream containing a Window1 Record + */ + public int read(InputStream input) throws IOException { + + int numOfBytesRead = input.read(grbit); + numOfBytesRead += input.read(itabCur); + + Debug.log(Debug.TRACE,"\tgrbit : "+ EndianConverter.readShort(grbit) + + " itabCur : " + EndianConverter.readShort(itabCur)); + + return numOfBytesRead; + } + + public void write(OutputStream output) throws IOException { + + output.write(getBiffType()); + output.write(grbit); + output.write(itabCur); + + Debug.log(Debug.TRACE,"Writing Window1 record"); + } +} diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/Window2.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/Window2.java new file mode 100644 index 000000000000..e587de7ea3b4 --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/Window2.java @@ -0,0 +1,158 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: Window2.java,v $ + * $Revision: 1.6 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +package org.openoffice.xmerge.converter.xml.sxc.pexcel.records; + +import java.io.DataInputStream; +import java.io.OutputStream; +import java.io.InputStream; +import java.io.IOException; +import java.awt.Point; + +import org.openoffice.xmerge.util.Debug; +import org.openoffice.xmerge.util.EndianConverter; +import org.openoffice.xmerge.converter.xml.sxc.pexcel.PocketExcelConstants; +import org.openoffice.xmerge.converter.xml.sxc.SheetSettings; + + +/** + * Represents a BIFF Record that describes worksheet window attributes + */ +public class Window2 implements BIFFRecord { + + private final static int FROZEN = 0x08; + private final static int NOSPLIT = 0x01; + + private byte[] rwTop = new byte[2]; + private byte colLeft; + private byte[] grbit = new byte[2]; + + /** + * Constructor + */ + public Window2() { + this.rwTop = EndianConverter.writeShort((short) 0); + this.colLeft = 0; + this.grbit = EndianConverter.writeShort((short) 0); + } + + /** + * Constructs a Window2 Record from an <code>InputStream</code> + * + * @param is InputStream containing a Window2 Record + */ + public Window2(InputStream is) throws IOException { + read(is); + } + + /** + * Get the hex code for this particular <code>BIFFRecord</code> + * + * @return the hex code for <code>Window2</code> + */ + public short getBiffType() { + return PocketExcelConstants.SHEET_WINDOW_INFO; + } + + /** + * Sets the split type for this pane, the split type is the same for both + * x and y so we only test against one. + * + * @param splitType the split type based on types defined in + * <code>sheetSettings</code> + */ + public void setSplitType(Point splitType) { + if(splitType.getX()==SheetSettings.SPLIT) { + grbit[0] &= ~FROZEN; + grbit[1] &= ~NOSPLIT; + } else { + grbit[0] |= FROZEN; + grbit[1] |= NOSPLIT; + } + } + + /** + * This method tests if this object describes a freeze + * + * @return true if freeze otherwise false + */ + public boolean isFrozen() { + if((grbit[0] & FROZEN) != FROZEN) + return false; + + if((grbit[1] & NOSPLIT) != NOSPLIT) + return false; + + return true; + } + + /** + * This method tests if this object describes a split + * + * @return true if split otherwise false + */ + public boolean isSplit() { + if((grbit[0] & FROZEN) == FROZEN) + return false; + + if((grbit[1] & NOSPLIT) == NOSPLIT) + return false; + + return true; + } + + /** + * Reads a Window2 Record from an <code>InputStream</code> + * + * @param is InputStream containing a Window2 Record + */ + public int read(InputStream input) throws IOException { + + int numOfBytesRead = input.read(rwTop); + colLeft = (byte) input.read(); + numOfBytesRead++; + numOfBytesRead += input.read(grbit); + + Debug.log(Debug.TRACE,"\trwTop : "+ EndianConverter.readShort(rwTop) + + " colLeft : " + colLeft + + " grbit : " + EndianConverter.readShort(grbit)); + return numOfBytesRead; + } + + public void write(OutputStream output) throws IOException { + + output.write(getBiffType()); + output.write(rwTop); + output.write(colLeft); + output.write(grbit); + + Debug.log(Debug.TRACE,"Writing Window2 record"); + } +} diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/Workbook.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/Workbook.java new file mode 100644 index 000000000000..d29efd8d9938 --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/Workbook.java @@ -0,0 +1,543 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: Workbook.java,v $ + * $Revision: 1.17 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +package org.openoffice.xmerge.converter.xml.sxc.pexcel.records; + +import java.io.OutputStream; +import java.io.InputStream; +import java.io.IOException; +import java.util.Vector; +import java.util.Enumeration; + +import org.openoffice.xmerge.converter.xml.OfficeConstants; +import org.openoffice.xmerge.converter.xml.sxc.Format; +import org.openoffice.xmerge.converter.xml.sxc.NameDefinition; +import org.openoffice.xmerge.converter.xml.sxc.BookSettings; +import org.openoffice.xmerge.converter.xml.sxc.SheetSettings; +import org.openoffice.xmerge.util.Debug; +import org.openoffice.xmerge.util.IntArrayList; +import org.openoffice.xmerge.converter.xml.sxc.pexcel.PocketExcelConstants; +import org.openoffice.xmerge.converter.xml.sxc.ColumnRowInfo; + +/** + * This class is used by <code> PxlDocument</code> to maintain pexcel + * workbooks. + * + * @author Martin Maher + */ +public class Workbook implements org.openoffice.xmerge.Document, +OfficeConstants { + + private Vector fonts = new Vector(); + private Vector extendedFormats = new Vector(); + private Vector worksheets = new Vector(); + private Vector boundsheets = new Vector(); + private Vector definedNames = new Vector(); + private static final CodePage cp; + private static final Window1 win1; + private static final BeginningOfFile bof;; + private static final Eof eof; + private String fileName; + + static { + cp = new CodePage(); + win1 = new Window1(); + bof = new BeginningOfFile(true); + eof = new Eof(); + } + + + /** + * Constructs a pocket Excel Workbook with the name of the file passed in + * as an argument. Also fills out a basic header block containing the + * minimum number of objects that can be created at this time. + * + * @param name Name of the Pocket Excel Data file. (excluding the file + * extension) + */ + public Workbook(String name) throws IOException { + fileName = name + PocketExcelConstants.FILE_EXTENSION; + Format defaultFormat = new Format(); + FontDescription fd = new FontDescription(defaultFormat); + fonts.add(fd); + ExtendedFormat xf = new ExtendedFormat(0, defaultFormat); + extendedFormats.add(xf); + } + + /** + * Constructs a pocket Excel Workbook from the + * <code>InputStream</code> and assigns it the document name passed in + * + * @param is InputStream containing a Pocket Excel Data file. + */ + public Workbook(String name, InputStream is) throws IOException { + read(is); + fileName = name; + } + + /** + * Writes the current workbook to the <code>Outputstream</code> + * + * @param os The destination outputstream + */ + public void write(OutputStream os) throws IOException { + bof.write(os); + cp.write(os); + for(Enumeration e = definedNames.elements();e.hasMoreElements();) { + DefinedName dn = (DefinedName) e.nextElement(); + dn.write(os); + } + win1.write(os); + for(Enumeration e = fonts.elements();e.hasMoreElements();) { + FontDescription fd = (FontDescription) e.nextElement(); + fd.write(os); + } + for(Enumeration e = extendedFormats.elements();e.hasMoreElements();) { + ExtendedFormat xf = (ExtendedFormat) e.nextElement(); + xf.write(os); + } + for(Enumeration e = boundsheets.elements();e.hasMoreElements();) { + BoundSheet bs = (BoundSheet) e.nextElement(); + bs.write(os); + } + eof.write(os); + + for(Enumeration e = worksheets.elements();e.hasMoreElements();) { + Worksheet ws = (Worksheet) e.nextElement(); + ws.write(os); + } + } + + /** + * Reads a workbook from the <code>InputStream</code> and contructs a + * workbook object from it + * + * @param is InputStream containing a Pocket Excel Data file. + */ + public void read(InputStream is) throws IOException { + + boolean done = false; + + int b = 0; + while (!done) + { + b = is.read(); + if (b == -1) + { + Debug.log(Debug.TRACE,"End of file reached"); + break; + } + + switch (b) + { + case PocketExcelConstants.DEFINED_NAME: + Debug.log(Debug.TRACE,"NAME: Defined Name (18h)"); + DefinedName dn = new DefinedName(is, this); + definedNames.add(dn); + break; + + case PocketExcelConstants.BOF_RECORD: + Debug.log(Debug.TRACE,"BOF Record"); + bof.read(is); + break; + + case PocketExcelConstants.EOF_MARKER: + Debug.log(Debug.TRACE,"EOF Marker"); + eof.read(is); + Worksheet ws = new Worksheet(this); + while(ws.read(is)) { + worksheets.add(ws); + ws = new Worksheet(this); + } + break; + + case PocketExcelConstants.FONT_DESCRIPTION: + Debug.log(Debug.TRACE,"FONT: Font Description (31h)"); + FontDescription fd = new FontDescription(is); + fonts.add(fd); + break; + + case PocketExcelConstants.WINDOW_INFO: + Debug.log(Debug.TRACE,"WINDOW1: Window Information (3Dh) [PXL 2.0]"); + win1.read(is); + break; + + case PocketExcelConstants.CODEPAGE: + Debug.log(Debug.TRACE,"CODEPAGE : Codepage and unknown fields (42h)"); + cp.read(is); + break; + + case PocketExcelConstants.BOUND_SHEET: + Debug.log(Debug.TRACE,"BOUNDSHEET: Sheet Information (85h)"); + BoundSheet bs = new BoundSheet(is); + boundsheets.add(bs); + break; + + case PocketExcelConstants.EXTENDED_FORMAT: + Debug.log(Debug.TRACE,"XF: Extended Format (E0h) [PXL 2.0]"); + ExtendedFormat xf = new ExtendedFormat(is); + extendedFormats.add(xf); + break; + + default: + b = is.read(); + break; + } + + } + is.close(); + } + + /** + * Adds a font recrod to the workbook + * + * @param f the font record to add + */ + public int addFont(FontDescription f) { + + boolean alreadyExists = false; + int i = 0; + + for(Enumeration e = fonts.elements();e.hasMoreElements();) { + FontDescription fd = (FontDescription) e.nextElement(); + if(fd.compareTo(f)) { + alreadyExists = true; + break; + } else { + i++; + } + } + + if(!alreadyExists) + fonts.add(f); + + return i; + } + + /** + * Adds a ExtendedFormat record to the workbook + * + * @param f the font recrod to add + */ + public int addExtendedFormat(Format fmt) throws IOException { + + FontDescription fd = new FontDescription(fmt); + int ixfnt = addFont(fd); + ExtendedFormat xf = new ExtendedFormat(ixfnt, fmt); + + boolean alreadyExists = false; + int i = 0; + + for(Enumeration e = extendedFormats.elements();e.hasMoreElements();) { + ExtendedFormat currentXF = (ExtendedFormat) e.nextElement(); + if(xf.compareTo(currentXF)) { + alreadyExists = true; + break; + } else if(!alreadyExists) { + i++; + } + } + + if(!alreadyExists) + extendedFormats.add(xf); + + return i; + } + + /** + * Gets a worksheet at a particular index from mthe current workbook. + * + * @param index the index of the worksheet to retrieve + */ + public Worksheet getWorksheet(int index) { + + return ((Worksheet) worksheets.elementAt(index)); + } + + /** + * Returns a FontDescription indictated by the + * index parameter passed in to the method + * + * @param ixfnt index to the FontDescriptions, this is a 0 based index + * @return FontDescription indexed by ixfe + */ + public FontDescription getFontDescription(int ixfnt) { + + return (FontDescription) fonts.elementAt(ixfnt); + } + + /** + * Returns a ExtendedFormat indictated by the + * index parameter passed in to the method + * + * @param ixfe index to the FontDescriptions, this is a 0 based index + * @return FontDescription indexed by ixfe + */ + public ExtendedFormat getExtendedFormat(int ixfe) { + + return (ExtendedFormat) extendedFormats.elementAt(ixfe); + } + + /** + * Returns an enumeration of DefinedNames for this workbook + * + * @return Enumeration for the DefinedNames + */ + public Enumeration getDefinedNames() { + + return definedNames.elements(); + } + + /** + * Returns an enumeration of <code>Settings</code> for this workbook + * + * @return Enumeration of <code>Settings</code> + */ + public BookSettings getSettings() { + + Vector settingsVector = new Vector(); + int index = 0; + for(Enumeration e = worksheets.elements();e.hasMoreElements();) { + Worksheet ws = (Worksheet) e.nextElement(); + SheetSettings s = ws.getSettings(); + s.setSheetName(getSheetName(index++)); + settingsVector.add(s); + } + BookSettings bs = new BookSettings(settingsVector); + String activeSheetName = getSheetName(win1.getActiveSheet()); + bs.setActiveSheet(activeSheetName); + return bs; + } + + /** + * Returns a <code>Vector</code> containing all the worksheet Names + * + * @return a <code>Vector</code> containing all the worksheet Names + */ + public Vector getWorksheetNames() { + + Vector wsNames = new Vector(); + + for(int i = 0;i < boundsheets.size();i++) { + wsNames.add(getSheetName(i)); + } + + return wsNames; + } + + /** + * Returns the name of the worksheet at the specified index + * + * @return a <code>String</code> containing the name of the worksheet + */ + public String getSheetName(int index) { + BoundSheet bs = (BoundSheet) boundsheets.elementAt(index); + + return bs.getSheetName(); + } + + /** + * Adds a <code>Worksheet</code> to the workbook. + * + * @return name the name of the <code>Worksheet</code> to be added + */ + public void addWorksheet(String name) throws IOException { + + BoundSheet bs = new BoundSheet(name); + boundsheets.add(bs); + + Worksheet ws = new Worksheet(); + worksheets.add(ws); + } + + /** + * Adds a cell to the current worksheet. + * + * @return the name of the <code>Worksheet</code> to be added + */ + public void addCell(int row,int col, Format fmt, String cellContents) + throws IOException { + + Worksheet currentWS = (Worksheet) worksheets.elementAt(worksheets.size()-1); + int ixfe = addExtendedFormat(fmt); + + String category = fmt.getCategory(); + + // Now the formatting is out of the way add the cell + Debug.log(Debug.TRACE,"Cell Format: " + fmt); + Debug.log(Debug.TRACE,"Row : " + row); + Debug.log(Debug.TRACE,"Col : " + col); + if(cellContents.startsWith("=")) { + try { + Formula f = new Formula(row, col, cellContents, ixfe, fmt, this); + currentWS.addCell(f); + if(category.equalsIgnoreCase(CELLTYPE_STRING)) { + StringValue sv = new StringValue(fmt.getValue()); + currentWS.addCell(sv); + } + } catch(Exception e) { + Debug.log(Debug.TRACE, "Parsing Exception thrown : " + e.getMessage()); + BoolErrCell errorCell = new BoolErrCell(row, col, ixfe, 0x2A, 1); + currentWS.addCell(errorCell); + } + } else if(category.equalsIgnoreCase(OfficeConstants.CELLTYPE_FLOAT)) { + try { + FloatNumber num = new FloatNumber(row, col, cellContents, ixfe); + currentWS.addCell(num); + } catch(Exception e) { + Debug.log(Debug.TRACE,"Error could not parse Float " + cellContents); + LabelCell lc = new LabelCell(row, col, cellContents, ixfe); + currentWS.addCell(lc); + } + } else { + if(cellContents.length()==0) { + Debug.log(Debug.TRACE, "Blank Cell"); + BlankCell b = new BlankCell(row, col, ixfe); + currentWS.addCell(b); + } else { + Debug.log(Debug.TRACE, "Label Cell : " + cellContents); + LabelCell lc = new LabelCell(row, col, cellContents, ixfe); + currentWS.addCell(lc); // three because we assume the last three + // Records in any worksheet is the selection, + // window2 and eof Records + } + } + } + + /** + * Will create a number of ColInfo records based on the column widths + * based in. + * + * @param columnRows <code>Vector</code> of <code>ColumnRowInfo</code> + */ + public void addColInfo(Vector columnRows) throws IOException { + + Worksheet currentWS = (Worksheet) worksheets.elementAt(worksheets.size()-1); + + int nCols = 0; + int nRows = 0; + + Debug.log(Debug.TRACE,"Workbook: addColInfo()"); + for(Enumeration e = columnRows.elements();e.hasMoreElements();) { + ColumnRowInfo cri =(ColumnRowInfo) e.nextElement(); + int ixfe = 0; + int size = cri.getSize(); + int repeated = cri.getRepeated(); + if(cri.isColumn()) { + Debug.log(Debug.TRACE,"Workbook: adding ColInfo width = " + size); + ColInfo newColInfo = new ColInfo( nCols, + nCols+repeated-1, + size, ixfe); + currentWS.addCol(newColInfo); + nCols += repeated; + } else if(cri.isRow()) { + + Debug.log(Debug.TRACE,"Workbook: adding Row Height = " + size); + if(!cri.isDefaultSize()) { + for(int i=0;i<repeated;i++) { + Row newRow = new Row(nRows++, size, cri.isUserDefined()); + currentWS.addRow(newRow); + } + } else { + // If it is the Default Row we don't need to add it + nRows += repeated; + } + + } + } + } + + /** + * Will create a number of ColInfo recrods based on the column widths + * based in. + * + * @param an integer list representing the column widths + */ + public void addNameDefinition(NameDefinition nameDefinition) throws IOException { + + DefinedName dn = new DefinedName(nameDefinition, this); + definedNames.add(dn); + } + + /** + * Adds the <code>BookSettings</code> for this workbook. + * + * @param book the <code>BookSettings</code> to add + */ + public void addSettings(BookSettings book) throws IOException { + + int index = 0; + Vector sheetSettings = book.getSheetSettings(); + String activeSheetName = book.getActiveSheet(); + + for(Enumeration e = worksheets.elements();e.hasMoreElements();) { + Worksheet ws = (Worksheet) e.nextElement(); + String name = getSheetName(index++); + if(activeSheetName.equals(name)) { + win1.setActiveSheet(index-1); + } + for(Enumeration eSettings = sheetSettings.elements();eSettings.hasMoreElements();) { + SheetSettings s = (SheetSettings) eSettings.nextElement(); + if(name.equals(s.getSheetName())) { + ws.addSettings(s); + } + } + } + } + + /** + * Return the filename of the pxl document without the file extension + * + * @return filename without the file extension + */ + public String getName() { + + // We have to strip off the file extension + int end = fileName.lastIndexOf("."); + String name; + if( end >= 0) // check in case the filename is already stripped + name = fileName.substring(0, end); + else + name = fileName; + + return name; + } + + /** + * Returns the filename of the pxl document with the file extension + * + * @return filename with the file extension + */ + public String getFileName() { + + return fileName; + } + +} diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/Worksheet.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/Worksheet.java new file mode 100644 index 000000000000..d0e4f829722a --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/Worksheet.java @@ -0,0 +1,323 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: Worksheet.java,v $ + * $Revision: 1.10 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +package org.openoffice.xmerge.converter.xml.sxc.pexcel.records; + +import java.io.OutputStream; +import java.io.InputStream; +import java.io.IOException; +import java.util.Vector; +import java.util.Enumeration; +import java.awt.Point; + +import org.openoffice.xmerge.util.IntArrayList; +import org.openoffice.xmerge.util.Debug; +import org.openoffice.xmerge.converter.xml.sxc.SheetSettings; +import org.openoffice.xmerge.converter.xml.sxc.pexcel.PocketExcelConstants; + + +/** + * This class is used by <code>PxlDocument</code> to maintain pexcel + * worksheets. + * + * @author Martin Maher + */ +public class Worksheet { + + private String name; + private Workbook wb; + private Vector rows = new Vector(); + private Vector colInfo = new Vector(); + private Vector cells = new Vector(); + private DefColWidth dcw = new DefColWidth(); + private DefRowHeight drh = new DefRowHeight(); + private Window2 win2 = new Window2(); + private Selection sel = new Selection(); + private Pane p = new Pane(); + private BeginningOfFile bof; + private Eof eof; + + /** + * Writes the current workbook to the <code>Outputstream</code> + * + * @param os The destination outputstream + */ + public Worksheet(Workbook wb) { + this.wb = wb; + } + + /** + * Default Contructor + * + * @param os The destination outputstream + */ + public Worksheet() { + } + + /** + * Writes the current workbook to the <code>Outputstream</code> + * + * @param os The destination outputstream + */ + public void write(OutputStream os) throws IOException { + + bof = new BeginningOfFile(false); + bof.write(os); + dcw.write(os); + for(Enumeration e = colInfo.elements();e.hasMoreElements();) { + ColInfo ci = (ColInfo) e.nextElement(); + ci.write(os); + } + drh.write(os); + for(Enumeration e = rows.elements();e.hasMoreElements();) { + Row rw = (Row) e.nextElement(); + rw.write(os); + } + for(Enumeration e = cells.elements();e.hasMoreElements();) { + BIFFRecord cv = (BIFFRecord) e.nextElement(); + cv.write(os); + } + win2.write(os); + p.write(os); + sel.write(os); + eof = new Eof(); + eof.write(os); + } + + /** + * Reads a worksheet from the <code>InputStream</code> and contructs a + * workbook object from it + * + * @param is InputStream containing a Pocket Excel Data file. + */ + public boolean read(InputStream is) throws IOException { + + int b = is.read(); + + if (b==-1) + return false; + + while(b!=-1) { + switch (b) + { + case PocketExcelConstants.BLANK_CELL: + Debug.log(Debug.TRACE,"Blank Cell (01h)"); + BlankCell bc = new BlankCell(is); + cells.add(bc); + break; + + case PocketExcelConstants.NUMBER_CELL: + Debug.log(Debug.TRACE,"NUMBER: Cell Value, Floating-Point Number (03h)"); + FloatNumber fn = new FloatNumber(is); + cells.add(fn); + break; + + case PocketExcelConstants.LABEL_CELL: + Debug.log(Debug.TRACE,"LABEL: Cell Value, String Constant (04h)"); + LabelCell lc = new LabelCell(is); + cells.add(lc); + break; + + case PocketExcelConstants.BOOLERR_CELL: + Debug.log(Debug.TRACE,"BOOLERR: Cell Value, Boolean or Error (05h)"); + BoolErrCell bec = new BoolErrCell(is); + break; + + case PocketExcelConstants.FORMULA_CELL: + Debug.log(Debug.TRACE,"FORMULA: Cell Formula (06h)"); + Formula f = new Formula(is, wb); + cells.add(f); + break; + + case PocketExcelConstants.FORMULA_STRING: + Debug.log(Debug.TRACE,"String Value of a Formula (07h)"); + StringValue sv = new StringValue(is); + break; + + case PocketExcelConstants.ROW_DESCRIPTION: + Debug.log(Debug.TRACE,"ROW: Describes a Row (08h)"); + Row rw = new Row(is); + rows.add(rw); + break; + + case PocketExcelConstants.BOF_RECORD: + Debug.log(Debug.TRACE,"BOF Record"); + bof = new BeginningOfFile(is); + break; + + case PocketExcelConstants.EOF_MARKER: + Debug.log(Debug.TRACE,"EOF Marker"); + eof = new Eof(); + return true; + + case PocketExcelConstants.CURRENT_SELECTION: + Debug.log(Debug.TRACE,"SELECTION: Current Selection (1Dh)"); + sel = new Selection(is); + break; + + case PocketExcelConstants.NUMBER_FORMAT: + Debug.log(Debug.TRACE,"FORMAT: Number Format (1Eh)"); + NumberFormat nf = new NumberFormat(is); + break; + + case PocketExcelConstants.DEFAULT_ROW_HEIGHT: + Debug.log(Debug.TRACE,"DEFAULTROWHEIGHT: Default Row Height (25h)"); + drh = new DefRowHeight(is); + break; + + case PocketExcelConstants.SHEET_WINDOW_INFO: + Debug.log(Debug.TRACE,"WINDOW2: Sheet Window Information (3Eh) [PXL 2.0]"); + win2 = new Window2(is); + break; + + case PocketExcelConstants.PANE_INFO: + Debug.log(Debug.TRACE,"PANE: Number of Panes and their Position (41h) [PXL 2.0]"); + p = new Pane(is); + break; + + case PocketExcelConstants.DEF_COL_WIDTH: + Debug.log(Debug.TRACE,"DEFCOLWIDTH: Default Column Width (55h) [PXL 2.0]"); + dcw = new DefColWidth(is); + break; + + case PocketExcelConstants.COLINFO: + Debug.log(Debug.TRACE,"COLINFO: Column Formatting Information (7Dh) [PXL 2.0]"); + ColInfo ci = new ColInfo(is); + colInfo.add(ci); + break; + + default: + break; + } + b = is.read(); + + } + Debug.log(Debug.TRACE,"Leaving Worksheet:"); + + return true; + } + + /** + * Returns an enumerator which will be used to access individual cells + * + * @return an enumerator to the worksheet cells + */ + public Enumeration getCellEnumerator() throws IOException { + return (cells.elements()); + } + + /** + * Adds a cell to this worksheet. Current valdid celltypes are + * <code>FloatNumber</code>, <code>LabelCell</code> or <code>Formula</code> + * + * @param f the font recrod to add + */ + public void addCell(BIFFRecord br) { + cells.add(br); + } + + /** + * Adds a number of ColInfo Records to the worksheet base on a list of + * clumnwidths passed in + * + * @param list of column widths + */ + public void addRow(Row r) { + rows.add(r); + } + + /** + * Adds a number of ColInfo Records to the worksheet base on a list of + * clumnwidths passed in + * + * @param list of column widths + */ + public void addCol(ColInfo c) { + colInfo.add(c); + } + /** + * Returns an <code>Enumeration</code> to the ColInfo's for this worksheet + * + * @return an <code>Enumeration</code> to the ColInfo's + */ + public void addSettings(SheetSettings s) { + + sel.setActiveCell(s.getCursor()); + p.setLeft(s.getLeft()); + p.setTop(s.getTop()); + p.setPaneNumber(s.getPaneNumber()); + Point split = s.getSplit(); + if(split.getX()!=0 || split.getY()!=0) { + p.setSplitPoint(s.getSplitType(), split); + win2.setSplitType(s.getSplitType()); + } + } + + /** + * Returns an <code>Enumeration</code> to the ColInfo's for this worksheet + * + * @return an <code>Enumeration</code> to the ColInfo's + */ + public Enumeration getColInfos() { + + return (colInfo.elements()); + } + + /** + * Returns a <code>SheetSettings</code> object containing a collection of data + * contained in <code>Pane</code>, <code>Window2</code> and + * <code>Selection</code> + * + * @return an <code>SheetSettings</code> + */ + public SheetSettings getSettings() { + + SheetSettings s = new SheetSettings(); + s.setCursor(sel.getActiveCell()); + if(win2.isFrozen()) { + s.setFreeze(p.getFreezePoint()); + } else if(win2.isSplit()) { + s.setSplit(p.getSplitPoint()); + } + s.setPaneNumber(p.getPaneNumber()); + s.setTopLeft(p.getTop(), p.getLeft()); + return s; + } + /** + * Returns an <code>Enumeration</code> to the Rows for this worksheet + * + * @return an <code>Enumeration</code> to the Rows + */ + public Enumeration getRows() { + + return (rows.elements()); + } + +} diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/build.xml b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/build.xml new file mode 100644 index 000000000000..a30c348c84f3 --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/build.xml @@ -0,0 +1,156 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + + DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + + Copyright 2008 by Sun Microsystems, Inc. + + OpenOffice.org - a multi-platform office productivity suite + + $RCSfile: build.xml,v $ + + $Revision: 1.6 $ + + This file is part of OpenOffice.org. + + OpenOffice.org is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + OpenOffice.org 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 version 3 for more details + (a copy is included in the LICENSE file that accompanied this code). + + You should have received a copy of the GNU Lesser General Public License + version 3 along with OpenOffice.org. If not, see + <http://www.openoffice.org/license.html> + for a copy of the LGPLv3 License. + +--> +<project name="xmrg_jooxcxsp_records" default="main" basedir="."> + + <!-- ================================================================= --> + <!-- settings --> + <!-- ================================================================= --> + + <!-- project prefix, used for targets and build.lst --> + <property name="prj.prefix" value="xmrg"/> + + <!-- name of this sub target used in recursive builds --> + <property name="target" value="xmrg_jooxcxsp_records"/> + + <!-- relative path to project directory --> + <property name="prj" value="../../../../../../../../.."/> + + <!-- start of java source code package structure --> + <property name="java.dir" value="${prj}/java"/> + + <!-- path component for current java package --> + <property name="package" + value="org/openoffice/xmerge/converter/xml/sxc/pexcel/records"/> + + <!-- define how to handle CLASSPATH environment --> + <property name="build.sysclasspath" value="ignore"/> + + <!-- classpath settings for javac tasks --> + <path id="classpath"> + <pathelement location="${build.class}"/> + <pathelement location="${solar.jar}/parser.jar"/> + <pathelement location="${solar.jar}/jaxp.jar"/> + </path> + + <!-- set wether we want to compile with or without deprecation --> + <property name="deprecation" value="on"/> + + <!-- ================================================================= --> + <!-- solar build environment targets --> + <!-- ================================================================= --> + + <target name="build_dir" unless="build.dir"> + <property name="build.dir" value="${out}"/> + </target> + + <target name="solar" depends="build_dir" if="solar.update"> + <property name="solar.properties" + value="${solar.bin}/solar.properties"/> + </target> + + <target name="init" depends="solar"> + <property name="build.compiler" value="classic"/> + <property file="${solar.properties}"/> + <property file="${build.dir}/class/solar.properties"/> + </target> + + <target name="info"> + <echo message="--------------------"/> + <echo message="${target}"/> + <echo message="--------------------"/> + </target> + + + <!-- ================================================================= --> + <!-- custom targets --> + <!-- ================================================================= --> + + <!-- the main target, called in recursive builds --> + <target name="main" depends="info,prepare,compile"/> + + <!-- prepare output directories --> + <target name="prepare" depends="init" if="build.class"> + <mkdir dir="${build.dir}"/> + <mkdir dir="${build.class}"/> + </target> + + <!-- compile java sources in ${package} --> + <target name="compile" depends="prepare" if="build.class"> + <javac srcdir="${java.dir}" + destdir="${build.class}" + debug="${debug}" + deprecation="${deprecation}" + optimize="${optimize}"> + <classpath refid="classpath"/> + <include name="${package}/BIFFRecord.java"/> + <include name="${package}/CellValue.java"/> + <include name="${package}/Workbook.java"/> + <include name="${package}/Worksheet.java"/> + <include name="${package}/BeginningOfFile.java"/> + <include name="${package}/BlankCell.java"/> + <include name="${package}/BoolErrCell.java"/> + <include name="${package}/BoundSheet.java"/> + <include name="${package}/CodePage.java"/> + <include name="${package}/ColInfo.java"/> + <include name="${package}/DefColWidth.java"/> + <include name="${package}/DefRowHeight.java"/> + <include name="${package}/DefinedName.java"/> + <include name="${package}/Eof.java"/> + <include name="${package}/ExtendedFormat.java"/> + <include name="${package}/FloatNumber.java"/> + <include name="${package}/FontDescription.java"/> + <include name="${package}/UnsupportedFormulaException.java"/> + <include name="${package}/Formula.java"/> + <include name="${package}/LabelCell.java"/> + <include name="${package}/NumberFormat.java"/> + <include name="${package}/Pane.java"/> + <include name="${package}/Row.java"/> + <include name="${package}/Selection.java"/> + <include name="${package}/StringValue.java"/> + <include name="${package}/Window1.java"/> + <include name="${package}/Window2.java"/> + </javac> + </target> + + <!-- clean up --> + <target name="clean" depends="prepare"> + <delete includeEmptyDirs="true"> + <fileset dir="${build.class}"> + <patternset> + <include name="${package}/*.class"/> + </patternset> + </fileset> + </delete> + </target> + +</project> + diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/formula/FormulaCompiler.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/formula/FormulaCompiler.java new file mode 100644 index 000000000000..49474e5de25f --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/formula/FormulaCompiler.java @@ -0,0 +1,275 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: FormulaCompiler.java,v $ + * $Revision: 1.4 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + + +package org.openoffice.xmerge.converter.xml.sxc.pexcel.records.formula; + +import java.util.*; +import org.openoffice.xmerge.util.Debug; + +/** + * FormulaCompiler converts Calc formula string into PocketXL bytes + * and PocketXL formula bytes into Calc Formula strings + * + * For converting from infix to Reverse Polish (or Postfix) notation the string is + * converted into a vector of Tokens and then re-ordered based on a modified version + * of the standard Infix to RPN conversion algorithms. + * <pre> + * Infix2Rpn(tokens) + * while have more tokens + * if token is operand + * push to stack + * else if token is function, argument separater, or open bracket + * push token + * extract tokens to matching close bracket into param + * Infix2Rpn(param) + * else if token is close bracket + * pop from stack into result until close bracket or function + * else + * while stack.top.priority >= token.priority + * add stack.pop to result + * push token onto stack + * </pre> + * For converting from RPN to Infix the following algorithm is applied: + * <pre> + * while have more tokens + * if token is operand + * push token to stack + * else if token is function or operator + * pop from stack number of args required by token + * apply token to params to make expr + * push expr to stack + * return stack.pop + * </pre> + */ +public class FormulaCompiler { + /** + * Constructs a FormulaCompiler object + */ + public FormulaCompiler() { + } + + private boolean isPercent(Token pt) { + return pt.getTokenID() == TokenConstants.TPERCENT; + } + + private boolean isOpenBrace(Token pt) { + return pt.getTokenID() == TokenConstants.TPAREN; + } + + private boolean isCloseBrace(Token pt) { + return pt.getValue().compareTo(")") == 0; + } + + private boolean isParamDelimiter(Token pt) { + return pt.getTokenID() == TokenConstants.TARGSEP; + } + + private boolean isBinaryOperator(Token pt) { + return false; + } + + /** + * Re-order into Infix format + * @param tokens The tokens in RPN form + * @return The vector of tokens re-ordered in Infix notation + */ + public Vector RPN2Infix(Vector tokens) { + Vector infixExpr = new Vector(15); + ListIterator iter = tokens.listIterator(); + Stack evalStack = new Stack(); + Stack args = new Stack(); + + while (iter.hasNext()) { + Token pt = (Token)iter.next(); + if (pt.isOperand()) { + Vector expr = new Vector(5); + expr.add(pt); + evalStack.push(expr); + } else if (pt.isOperator() || pt.isFunction()) { + args.clear(); + for (int i=0; i< pt.getNumArgs(); i++) { + args.push(evalStack.pop()); + } + evalStack.push(makeExpression(pt, args)); + } + } + return (Vector)evalStack.elementAt(0); + } + + /** + * Convert the infix expression to RPN. Note that open brackets are saved onto the stack to preserve the users bracketing. + * <p>Also note that the open bracket following functions is not pushed onto the stack - it is always implied when + * writing out results + * + * @param tokens The vector of tokens in Infix form + * + * @return A vector of tokens for the expression in Reverse Polish Notation order + */ + public Vector infix2RPN(Vector tokens) { + Vector rpnExpr = new Vector(15); + Stack evalStack = new Stack(); + ListIterator iter = tokens.listIterator(); + while (iter.hasNext()) { + Token pt = (Token)iter.next(); + + if (pt.isOperand()) { //Operands are output immediately + rpnExpr.add(pt); + } else if (pt.isFunction() || isParamDelimiter(pt) || isOpenBrace(pt)) { //Extract parameters after afunction or comma + evalStack.push(pt); + if (pt.isFunction()) { + iter.next(); + } + Vector param = extractParameter(iter); + Debug.log(Debug.TRACE, "Extracted parameter " + param); + rpnExpr.addAll(infix2RPN(param)); + } else if (isCloseBrace(pt)) { //Pop off stack till you meet a function or an open bracket + Token tmpTok = null; + boolean bPop = true; + while (bPop) { + if (evalStack.isEmpty()) { + bPop = false; + } else { + tmpTok = (Token)evalStack.pop(); + //if (!(isOpenBrace(tmpTok) || isParamDelimiter(tmpTok))) { //Don't output brackets and commas + if (!isParamDelimiter(tmpTok)) { //Don't output commas + rpnExpr.add(tmpTok); + } + if (tmpTok.isFunction() || isOpenBrace(tmpTok)) { + bPop = false; + } + } + } + } else { + if (!evalStack.isEmpty()) { + while (!evalStack.isEmpty() && + (((Token)evalStack.peek()).getTokenPriority() >=pt.getTokenPriority())) { + Token topTok = (Token)evalStack.peek(); + if (topTok.isFunction() || isOpenBrace(topTok)) { + break; + } + rpnExpr.add(evalStack.pop()); + } + } + evalStack.push(pt); + } + } + + while (!evalStack.isEmpty()) { + Token topTok = (Token)evalStack.peek(); + if (!(isOpenBrace(topTok) || isParamDelimiter(topTok))) { //Don't output brackets and commas + rpnExpr.add(evalStack.pop()); + } + else + { + evalStack.pop(); + } + } + return rpnExpr; + } + + /** + * Extract a parameter or bracketed sub-expression + * @param iter an iterator into the list + * @return A complete sub-expression + */ + protected Vector extractParameter(ListIterator iter) { + Vector param = new Vector(5); + int subExprCount = 0; + + while (iter.hasNext()) { + Token pt = (Token)iter.next(); + Debug.log(Debug.TRACE, "Token is " + pt + " and subExprCount is " + subExprCount); + if (isOpenBrace(pt)) { + subExprCount++; + param.add(pt); + } else if (isCloseBrace(pt)) { + if (subExprCount == 0) { + iter.previous(); + return param; + } else { + subExprCount--; + param.add(pt); + } + } else if (isParamDelimiter(pt) && (subExprCount == 0)) { + iter.previous(); + return param; + } else { + param.add(pt); + } + } + return param; + } + + /** + * Given the operator and it's operators + * @param pt The operator token + * @param args The arguments for this operator + * @return A correctly ordered expression + */ + protected Vector makeExpression(Token pt, Stack args) { + Vector tmp = new Vector(5); + TokenFactory tf = new TokenFactory(); + if (pt.isOperator()) { + if (pt.getNumArgs()==2) { //Binary operator + tmp.addAll((Vector)args.pop()); + tmp.add(pt); + tmp.addAll((Vector)args.pop()); + } else if (pt.getNumArgs() == 1) { + if(isPercent(pt)) { + tmp.addAll((Vector)args.elementAt(0)); + tmp.add(pt); + } else { + tmp.add(pt); + tmp.addAll((Vector)args.elementAt(0)); + } + if (isOpenBrace(pt)) { + tmp.add(tf.getOperatorToken(")",1)); + } + } + } else if (pt.isFunction()) { + tmp.add(pt); + tmp.add(tf.getOperatorToken("(",1)); + if (!args.isEmpty()) { + Vector v = (Vector)args.pop(); + tmp.addAll(v); + } + while (!args.isEmpty()) { + tmp.add(tf.getOperatorToken(",",1)); + Vector v = (Vector)args.pop(); + tmp.addAll(v); + + } + tmp.add(tf.getOperatorToken(")",1)); + } + + return tmp; + } +} diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/formula/FormulaHelper.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/formula/FormulaHelper.java new file mode 100644 index 000000000000..2e4e773a5f27 --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/formula/FormulaHelper.java @@ -0,0 +1,156 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: FormulaHelper.java,v $ + * $Revision: 1.5 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +package org.openoffice.xmerge.converter.xml.sxc.pexcel.records.formula; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Vector; +import java.util.Enumeration; + +import org.openoffice.xmerge.util.Debug; +import org.openoffice.xmerge.converter.xml.sxc.pexcel.records.Workbook; + +/** + * This Helper class provides a simplified interface to conversion between PocketXL formula representation + * and Calc formula representation.<p> + * The class is used by {@link org.openoffice.xmerge.converter.xml.sxc.pexcel.Records.Formula} + */ +public class FormulaHelper { + + private static FormulaParser parser; + private static FormulaCompiler compiler; + private static TokenEncoder encoder; + private static TokenDecoder decoder; + private boolean rangeType = false; + private boolean expressionType = false; + + static { + parser = new FormulaParser(); + compiler = new FormulaCompiler(); + encoder = new TokenEncoder(); + decoder = new TokenDecoder(); + } + + /** + * Sets the workbook cache so that global data such as + * <code>DefinedNames</code>, <code>Boundsheets</code> can be read + * + * @param wb Wrokbook object containing all the global data + */ + public void setWorkbook(Workbook wb) { + + encoder.setWorkbook(wb); + decoder.setWorkbook(wb); + parser.setWorkbook(wb); + } + + /** + * Convertes a string representation of a calc formula into an array of PocketXL bytes + * @param formula The Formula String (e.g. 1+SUM(A1,B1)) + * + * @throws UnsupportedFunctionException Thrown if a function in the formula is nto supported by Pocket Excel + * @throws FormulaParsingException Thrown when the formula is not well formed + * + */ + public byte[] convertCalcToPXL(String formula) throws UnsupportedFunctionException, FormulaParsingException { + + Vector parseTokens = parser.parse(formula); + Vector rpnTokens = compiler.infix2RPN(parseTokens); + + ByteArrayOutputStream bytes = null; + try { + bytes = new ByteArrayOutputStream(); + for (Enumeration e = rpnTokens.elements(); e.hasMoreElements();) { + Token t = (Token)e.nextElement(); + bytes.write(encoder.getByte(t)); + } + } catch (IOException e) { + } + + return bytes.toByteArray(); + } + + /** + * Converts a PocketXL byte array into a Calc function string + * @param formula A byte array that contains the PocketXL bytes for a formula + * + */ + public String convertPXLToCalc(byte[] formula) { + + Vector parseTokens = decoder.getTokenVector(formula); + Vector infixTokens = compiler.RPN2Infix(parseTokens); + + StringBuffer buff = new StringBuffer(); + for (Enumeration e = infixTokens.elements();e.hasMoreElements();) { + Token t = (Token)e.nextElement(); + buff.append(t.toString()); + // If we are parsing a Name definition we need to know if it is of + // type range or expression + if(!t.isOperand()) { + expressionType = true; + } + } + if(!expressionType) { + rangeType = true; + } + return "=" + buff.toString(); + } + + /** + * Returns a boolean indicating whether or not the byte[] parsed is of + * type range. This means it contains only a cell reference and no + * operators. This is necessry because the syntax for range and expression + * types differs. This is only of interest when dealing with + * <code>DefinedNames</code> and not <code>Formula</code> + * + * @return a boolean true if of type range otherwise false + * + */ + public boolean isRangeType() { + + return rangeType; + } + + /** + * Returns a boolean indicating whether or not the byte[] parsed is of + * type expression. This means it contains operators. This is necessry + * because the syntax for range and expression types differs. This is + * only of interest when dealing with <code>DefinedNames</code> and not + * <code>Formula</code> + * + * @return a boolean true if of type expression otherwise false + * + */ + public boolean isExpressionType() { + + return expressionType; + } +} diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/formula/FormulaParser.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/formula/FormulaParser.java new file mode 100644 index 000000000000..2422a5297b96 --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/formula/FormulaParser.java @@ -0,0 +1,567 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: FormulaParser.java,v $ + * $Revision: 1.11 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + + +package org.openoffice.xmerge.converter.xml.sxc.pexcel.records.formula; + + +import java.io.*; +import java.util.Vector; +import java.util.Enumeration; + +import org.openoffice.xmerge.converter.xml.sxc.pexcel.records.Workbook; +import org.openoffice.xmerge.util.Debug; + +/** + * This is the Formula Parser based on an article written by Jack Crenshaw. It is a + * top down parser with some basic error handling. It handles + * +,-,*,/,>,<,>=,<=,=,<>, unary + and - as well as functions. + * The BNF notation for this parser is + * <pre> + * <expression> ::= <unary op> <term> [<addop>|<logop> <term>] + * <term> ::= <factor> [<mulop> <factor>] + * <factor> ::= <number>[%] | <CellRef> | <QuoteString> | <expression> + * </pre> + */ +public class FormulaParser { + + private char look; + private String formulaStr; + private int index = 1; + private TokenFactory tokenFactory; + private Vector tokenVector; + private Workbook wb; + + /** + * Default constructor + */ + public FormulaParser() { + + Debug.log(Debug.TRACE,"Creating a Formula Parser"); + tokenFactory = new TokenFactory(); + tokenVector = new Vector(); + } + + /** + * + */ + public void setWorkbook(Workbook wb) { + + this.wb = wb; + } + + /** + * Parse method for parsing from a String to a byte[] + * + * @param formula A <code>String</code> representation of a formula + * starting with the '=' character + * @return A <code>Vector</code> containing the parsed <code>Token</code>s + */ + public Vector parse(String formula) throws FormulaParsingException { + + index = 1; + look = ' '; + tokenVector.clear(); + if(formula.startsWith("=")) { + formulaStr = formula; + Debug.log(Debug.TRACE,"Creating a Formula Parser for " + formulaStr); + getChar(); + expression(); + } else { + throw new FormulaParsingException("No equals found!" + makeErrorString()); + } + return tokenVector; + } + + /** + * Identify + and - operators + * + * @param c The character which is to be identified + * @return A boolean returning the result of the comparison + */ + private boolean isAddOp(char c) { + return (c == '-') || (c == '+'); + } + + /** + * Determine if the current character is a multiop + * + * @return A boolean returning the result of the comparison + */ + private boolean isMultiOp() { + return look=='*' || look =='/' || look == '^' || look == '&'; + } + + /** + * Identify <, >, <=, >=, =, <> using the index to find the current character(s) + * + * @return A boolean returning the result of the comparison + */ + private boolean isLogicalOp() { + if (!isLogicalOpChar(look)) { + return false; + } else if ((index+1) >= formulaStr.length()) {//logical operators in their own right : if at end then return true + return true; + } else if (!isLogicalOpChar(formulaStr.charAt(index))) { // we have >, < or = on their own + return true; + } else if ((look == '<') && ((formulaStr.charAt(index) == '>') || formulaStr.charAt(index) == '=')) { // <>, or <= + return true; + } else if ((look == '>') && (formulaStr.charAt(index) == '=')) { // >= + return true; + } + + return false; + } + + /** + * Identify <, >, <=, >=, =, <> + * + * @param The <code>String</code> which is to be identified + * @return A boolean returning the result of the comparison + */ + private boolean isLogicalOp(String op) { + return ((op.compareTo(">") == 0) || + (op.compareTo("<") == 0) || + (op.compareTo(">=") == 0) || + (op.compareTo("<=") == 0) || + (op.compareTo("=") == 0) || + (op.compareTo("<>") == 0)); + } + + + /** + * Identify characters that MAY be logical operator characters + * + * @param c The character which is to be identified + * @return A boolean returning the result of the comparison + */ + private boolean isLogicalOpChar(char c) { + return (c == '>') || (c == '<') || (c == '='); + } + + /** + * Identify special Cell Reference charaters + * + * @param c The character which is to be identified + * @return A boolean returning the result of the comparison + */ + private boolean isCellRefSpecialChar(char c) { + return (c == ':') || (c == '$') || (c == '.'); + } + + /** + * Identify letters + * + * @param c The character which is to be identified + * @return A boolean returning the result of the comparison + */ + private boolean isAlpha(char c) { + return(Character.isLetter(c)); + } + + /** + * Identify numbers + * + * @param c The character which is to be identified + * @return A boolean returning the result of the comparison + */ + private boolean isDigit(char c) { + return(Character.isDigit(c)); + } + + /** + * Identify numbers + * + * @param c The character which is to be identified + * @return A boolean returning the result of the comparison + */ + private boolean isPercent(char c) { + return (c == '%'); + } + + /** + * Identify letters or numbers + * + * @param c The character which is to be identified + * @return A boolean returning the result of the comparison + */ + private boolean isAlphaNum(char c) { + return(isAlpha(c) || isDigit(c)); + } + + /** + * Identify valid Characters for cell references + * + * @param c The character which is to be identified + * @return A boolean returning the result of the comparison + */ + private boolean isCellRefChar(char c) { + return(isAlpha(c) || isDigit(c) || isCellRefSpecialChar(c)); + } + + /** + * Test if current character is a match and move to next character + * + * @param c The character which is to be matched + */ + private void match(char c) throws FormulaParsingException { + + if(look==c) { + Debug.log(Debug.TRACE,"Operator Found : " + look); + getChar(); + skipWhite(); + } + else + throw new FormulaParsingException("Unexpected character '" + c + "'" + makeErrorString()); + } + + /** + * Test if current character is a match and move to next character + * + * @param symbol The <code>String</code> to be matched. + */ + private void match(String symbol) throws FormulaParsingException { + + int numChars = symbol.length(); + boolean bContinue = true; + for (int i=0;i<numChars && bContinue; i++) { + if (look == symbol.charAt(i)) { + bContinue = getChar(); + skipWhite(); + } else { + throw new FormulaParsingException("Unexpected character '" + symbol + "'" + makeErrorString()); + } + } + } + + /** + * Skip over whitespaces (ie. spaces and tabs) + */ + private void skipWhite() throws FormulaParsingException { + + boolean success = true; + + while(Character.isWhitespace(look) && success) { + success = getChar(); + } + } + + /** + * This is a factor for multiplication and division operators + */ + private void factor() throws FormulaParsingException { + if(isAddOp(look)) { // handle unary addop + Character ch = new Character(look); + match(look); + tokenVector.add(tokenFactory.getOperatorToken(ch.toString(), 1)); + } + if(look=='(') { + match('('); + tokenVector.add(tokenFactory.getOperatorToken("(", 1)); + expression(); + match(')'); + tokenVector.add(tokenFactory.getOperatorToken(")", 1)); + } else if(isDigit(look)){ + getNum(); + } else { + ident(); + } + } + + /** + * Pulls the next character from the <code>String</code> + * + * @return boolean false if the end if the statement + * is reached otherwise true + */ + private boolean getChar() throws FormulaParsingException { + + boolean success = true; + + if(index<formulaStr.length()) { + look = formulaStr.charAt(index); + index++; + if(look==',') + success = false; + } else { + success = false; + } + return success; + } + + /** + * Parses the number of arguments in a function + * + * @return The number of arguments + */ + private int arguments() throws FormulaParsingException { + int numArgs; + + skipWhite(); + if(look==')') + numArgs = 0; + else + numArgs = 1; + + while(look!=')') { + expression(); + if(look==',') { + numArgs++; + match(','); + tokenVector.add(tokenFactory.getOperatorToken(",", 1)); + } + } + return numArgs; + } + + /** + * Test to see if we have come across a cell reference or a Name + * Definition. + */ + private boolean isCellRef(String s) { + char c; + boolean result = false; + + for(int i = 0;i<s.length();i++) { + c = s.charAt(i); + if(isCellRefSpecialChar(c)) { + result = true; + break; + } + } + + // if it is a simple cell reference then there will not be a cell + // reference 'special char' so we should also look for a digit + if(!result) { + if(isDigit(s.charAt(1)) || isDigit(s.charAt(2))) { + result = true; + } + } + return result; + } + + /** + * Test to see if we have come across a cell reference or a function and + * add the resulting toek nto the tokenVector. + */ + private void ident() throws FormulaParsingException { + + String cell = getTokenString(); + if(look=='(') { + Debug.log(Debug.TRACE,"Found Function : " + cell); + + int index = tokenVector.size(); + match('('); + tokenVector.add(tokenFactory.getOperatorToken("(", 1)); + int numArgs = arguments(); + match(')'); + tokenVector.add(tokenFactory.getOperatorToken(")", 1)); + tokenVector.insertElementAt(tokenFactory.getFunctionToken(cell, numArgs), index); + } else { + + if(cell.indexOf('.')!=-1) { + String cellRef = cell.substring(cell.indexOf('.') + 1, cell.length()); + if(cellRef.indexOf(':')!=-1) { + tokenVector.add(tokenFactory.getOperandToken(cell, "3D_CELL_AREA_REFERENCE")); + } else { + tokenVector.add(tokenFactory.getOperandToken(cell, "3D_CELL_REFERENCE")); + } + } else if(cell.indexOf(':')!=-1) { + tokenVector.add(tokenFactory.getOperandToken(cell, "CELL_AREA_REFERENCE")); + } else if(isCellRef(cell)) { + tokenVector.add(tokenFactory.getOperandToken(cell, "CELL_REFERENCE")); + } else { + tokenVector.add(tokenFactory.getOperandToken(cell, "NAME")); + } + } + } + + /** + * Will keep pulling valid logical operators from the formula and return + * the resultant <code>String</code>. + * + * @return a <code>String<code> representing a logical operator + */ + private String getLogicalOperator() throws FormulaParsingException { + String op = new String(); + boolean status; + + do { + op += look; + status = getChar(); + } while(isLogicalOpChar(look) && status); + skipWhite(); + return op; + } + + /** + * Keeps pulling characters from the statement until we get an + * operator and returns the resulting string. + * + * @return A <code>String</code>representing the next token + */ + private String getTokenString() throws FormulaParsingException { + + if(!isAlpha(look) && look!='$') + throw new FormulaParsingException("Expected Cell Reference" + makeErrorString()); + else { + String cell = new String(); + boolean status; + do { + cell += look; + status = getChar(); + } while(isCellRefChar(look) && status); + skipWhite(); + return cell; + } + } + + /** + * Keeps pulling numbers from the statement and add the resulting integer + * token to the tokenVector. + */ + private void getNum() throws FormulaParsingException { + + Debug.log(Debug.TRACE,"getNum : "); + if(!isDigit(look)) + throw new FormulaParsingException("Expected Integer" + makeErrorString()); + else { + String num = new String(); + boolean status; + + do { + num += look; + status = getChar(); + } while((isDigit(look) || ((look == '.') && isDigit(formulaStr.charAt(index)))) && status); + skipWhite(); + tokenVector.add(tokenFactory.getOperandToken(num, "INTEGER")); + if(isPercent(look)) { + match(look); + tokenVector.add(tokenFactory.getOperatorToken("%", 1)); + Debug.log(Debug.TRACE,"Added Percent token to Vector: "); + } + Debug.log(Debug.TRACE,"Number parsed : " + num); + } + } + + + /** + * Term will parse multiplication/division expressions + */ + private void term() throws FormulaParsingException { + factor(); + while(isMultiOp()) { + multiOp(Character.toString(look)); + } + } + + /** + * Expression is the entry point for the parser. It is the code + * that parses addition/subtraction expressions. + */ + private void expression() throws FormulaParsingException { + + if (look == '"') { //Extract a quoted string... + StringBuffer buff = new StringBuffer(); + boolean success = true; + success = getChar(); + while (look != '"' && success) { + buff.append(look); + success = getChar(); + } + + if (look != '"') { //We've reached the end of the string without getting a closing quote + throw new FormulaParsingException("Expected closing quote." + makeErrorString()); + } else { + tokenVector.add(tokenFactory.getOperandToken(buff.toString(), "STRING")); + getChar(); //Move on to the next character + } + } else { + term(); + } + while(isAddOp(look) || isLogicalOp()) { + if (isAddOp(look)) { + addOp(Character.toString(look)); + } else if (isLogicalOp()) { + logicalOp(); + } + } + } + + /** + * Test to see if the next token (represented as a <code>String</code>) is + * the same as the String passed in. Move the index along to the end of + * that String and add that <code>Token</code> to the tokenVector. Then + * call <code>term</code> to parse the right hand side of the operator. + * + * @param op A <code>String</code> representing the operator + */ + private void addOp(String op) throws FormulaParsingException { + match(op); + tokenVector.add(tokenFactory.getOperatorToken(op, 2)); + term(); + } + + /** + * Test to see if the next token (represented as a <code>String</code>) is + * the same as the String passed in. Move the index along to the end of + * that String and add that <code>Token</code> to the tokenVector. Then + * call <code>factor</code> to parse the right hand side of the operator. + * + * @param op A <code>String</code> representing the operator + */ + private void multiOp(String op) throws FormulaParsingException { + match(op); + tokenVector.add(tokenFactory.getOperatorToken(op, 2)); + factor(); + } + + /** + * Pull a logical operator starting at the current index, add a token for + * that operator to the tokenVector and call <code>term<code> to parse the + * right hand side of the operator + */ + private void logicalOp() throws FormulaParsingException { + String op = getLogicalOperator(); + tokenVector.add(tokenFactory.getOperatorToken(op, 2)); + term(); + } + + private String makeErrorString() { + StringBuffer buff = new StringBuffer(); + for (int i=0; i<index-1; i++) { + buff.append(' '); + } + + buff.append('^'); + return "\n\t" + formulaStr + "\n\t" + buff.toString(); + } + } + diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/formula/FormulaParsingException.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/formula/FormulaParsingException.java new file mode 100644 index 000000000000..f961bcbf49a0 --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/formula/FormulaParsingException.java @@ -0,0 +1,49 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: FormulaParsingException.java,v $ + * $Revision: 1.3 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + + +package org.openoffice.xmerge.converter.xml.sxc.pexcel.records.formula; + +/* + * If the formula failed to be parsed properly this exception will be thrown + * + * Martin Maher + */ + +import java.io.*; + +import org.openoffice.xmerge.util.Debug; + +public class FormulaParsingException extends Exception { + + public FormulaParsingException(String message) { + super(message); + } + } diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/formula/FunctionLookup.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/formula/FunctionLookup.java new file mode 100644 index 000000000000..c8205b6d4754 --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/formula/FunctionLookup.java @@ -0,0 +1,210 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: FunctionLookup.java,v $ + * $Revision: 1.8 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + + +package org.openoffice.xmerge.converter.xml.sxc.pexcel.records.formula; + +import org.openoffice.xmerge.util.Debug; + +import java.util.HashMap; + +public class FunctionLookup extends SymbolLookup { + + private HashMap stringToArgs = null; + + /** + * The default constructor - invokes {@link #initialize() initialize()} + */ + public FunctionLookup() { + initialize(); + } + + /** + * Initialize the lookup table for functions + */ + public void initialize() { + if ((stringToID != null) || (idToString != null) || (stringToArgs !=null)) { + return; + } + stringToID = new HashMap(); + idToString = new HashMap(); + stringToArgs = new HashMap(); + + // Functions with Variable number of Arguments + // Math and Trig + addEntry("SUM", TokenConstants.TSUM, -1); + addEntry("MIN", TokenConstants.TMIN, -1); + addEntry("PRODUCT", TokenConstants.TPRODUCT, -1); + addEntry("LOG", TokenConstants.TLOG, -1); + addEntry("SUMIF", TokenConstants.TSUMIF, -1); + addEntry("TRUNC", TokenConstants.TRUNC, -1); + // Financial + addEntry("DDB", TokenConstants.TDDB, -1); + addEntry("FV", TokenConstants.TFV, -1); + addEntry("IRR", TokenConstants.TIRR, -1); + addEntry("NPER", TokenConstants.TNPER, -1); + addEntry("NPV", TokenConstants.TNPV, -1); + addEntry("PMT", TokenConstants.TPMT, -1); + addEntry("PV", TokenConstants.TPV, -1); + addEntry("RATE", TokenConstants.TRATE, -1); + // Statistical + addEntry("AVERAGE", TokenConstants.TAVERAGE, -1); + addEntry("COUNT", TokenConstants.TCOUNT, -1); + addEntry("COUNTA", TokenConstants.TCOUNTA, -1); + addEntry("MAX", TokenConstants.TMAX, -1 ); + addEntry("MIN", TokenConstants.TMIN, -1); + addEntry("STDEV", TokenConstants.TSTDEV, -1 ); + addEntry("STDEVP", TokenConstants.TSTDEVP, -1 ); + addEntry("VAR", TokenConstants.TVAR, -1); + addEntry("VARP", TokenConstants.TVARP, -1); + // Lookup + addEntry("CHOOSE", TokenConstants.TCHOOSE, -1); + addEntry("HLOOKUP", TokenConstants.THLOOKUP, -1); + addEntry("INDEX", TokenConstants.TINDEX, -1); + addEntry("MATCH", TokenConstants.TMATCH, -1) ; + addEntry("VLOOKUP", TokenConstants.TVLOOKUP, -1); + // Text + addEntry("RIGHT", TokenConstants.TRIGHT, -1); + addEntry("SUBSTITUTE", TokenConstants.TSUBSTITUTE, -1); + addEntry("FIND", TokenConstants.TFIND, -1); + addEntry("LEFT", TokenConstants.TLEFT, -1); + // Logical + addEntry("AND", TokenConstants.TAND, -1 ); + addEntry("IF", TokenConstants.TIF, -1) ; + addEntry("OR", TokenConstants.TOR, -1); + + // Functions with Fixed number of Arguments + // Math and Trig + addEntry("ABS", TokenConstants.TABS, 1); + addEntry("ACOS", TokenConstants.TACOS, 1); + addEntry("ASIN", TokenConstants.TASIN, 1); + addEntry("ATAN", TokenConstants.TATAN, 1); + addEntry("ATAN2", TokenConstants.TATAN2, 1); + addEntry("COS", TokenConstants.TCOS, 1); + addEntry("COUNTIF", TokenConstants.TCOUNTIF, 1); + addEntry("DEGREES", TokenConstants.TDEGREES, 1); + addEntry("EXP", TokenConstants.TEXP, 1); + addEntry("FACT", TokenConstants.TFACT, 1); + addEntry("INT", TokenConstants.TINTE, 1); + addEntry("LN", TokenConstants.TLN, 1); + addEntry("LOG10", TokenConstants.TLOG10, 1); + addEntry("MOD", TokenConstants.TMOD, 1); + addEntry("PI", TokenConstants.TPI, 0); + addEntry("POWER", TokenConstants.TPOWERF, 2); + addEntry("RADIANS", TokenConstants.TRADIANS, 1); + addEntry("RAND", TokenConstants.TRAND, 1); + addEntry("ROUND", TokenConstants.TROUND, 1); + addEntry("SQRT", TokenConstants.TSQRT, 1); + addEntry("TAN", TokenConstants.TTAN, 1); + addEntry("SIN", TokenConstants.TSIN, 1); + // Financial + addEntry("SLN", TokenConstants.TSLN, 3); + addEntry("SYD", TokenConstants.TSYD, 4); + // Date and Time + addEntry("DATE", TokenConstants.TDATE, 3); + addEntry("DATEVALUE", TokenConstants.TDATEVALUE, 1); + addEntry("DAY", TokenConstants.TDAY, 1); + addEntry("HOUR", TokenConstants.THOUR, 1); + addEntry("MINUTE", TokenConstants.TMINUTE, 1 ); + addEntry("MONTH", TokenConstants.TMONTH, 1); + addEntry("NOW", TokenConstants.TNOW, 0); + addEntry("SECOND", TokenConstants.TSECOND, 1); + addEntry("TIME", TokenConstants.TTIME, 3); + addEntry("TIMEVALUE", TokenConstants.TTIMEVALUE, 1); + addEntry("YEAR", TokenConstants.TYEAR, 1); + // Statistical + addEntry("COUNTBLANK", TokenConstants.TCOUNTBLANK, 1); + // lookup + addEntry("COLUMNS", TokenConstants.TCOLUMNS, 1); + addEntry("ROWS", TokenConstants.TROWS, 1); + // Database + addEntry("DAVERAGE", TokenConstants.TDAVAERAGE, 3); + addEntry("DCOUNT", TokenConstants.TDCOUNT, 3); + addEntry("DCOUNTA", TokenConstants.TDCOUNTA, 2); + addEntry("DGET", TokenConstants.TDGET, 3); + addEntry("DMAX", TokenConstants.TDMAX, 3); + addEntry("DMIN", TokenConstants.TDMIN, 3); + addEntry("DPRODUCT", TokenConstants.TDPRODUCT, 3); + addEntry("DSTDEV", TokenConstants.TDSTDEV, 3); + addEntry("DSTDEVP", TokenConstants.TDSTDEVP, 3) ; + addEntry("DSUM", TokenConstants.TDSUM, 3); + addEntry("DVAR", TokenConstants.TDVAR, 3); + addEntry("DVARP", TokenConstants.TDVARP, 3); + // Text + addEntry("EXACT", TokenConstants.TEXACT, 2); + addEntry("LEN", TokenConstants.TLEN, 1); + addEntry("LOWER", TokenConstants.TLOWER, 1); + addEntry("MID", TokenConstants.TMID, 3); // ?????? + addEntry("PROPER", TokenConstants.TPROPER, 1); + addEntry("REPLACE", TokenConstants.TREPLACE, 4); + addEntry("REPT", TokenConstants.TREPT, 2); + addEntry("T", TokenConstants.TT, 1); + addEntry("TRIM", TokenConstants.TRIM, 1); + addEntry("UPPER", TokenConstants.TUPPER, 1); + addEntry("VALUE", TokenConstants.TVALUE, 1); + // Logical + addEntry("FALSE", TokenConstants.TFALSE, 0); + addEntry("NOT", TokenConstants.TNOT, 1); + addEntry("TRUE", TokenConstants.TTRUE, 0); + // Informational + addEntry("ERRORTYPE", TokenConstants.TERRORTYPE, 1); + addEntry("ISBLANK", TokenConstants.TISBLANK, 1); + addEntry("ISERR", TokenConstants.TISERR, 1); + addEntry("ISERROR", TokenConstants.TISERROR, 1); + addEntry("ISLOGICAL", TokenConstants.TISLOGICAL, 1); + addEntry("ISNA", TokenConstants.TISNA, 1); + addEntry("ISNONTEXT", TokenConstants.TISNONTEXT, 1); + addEntry("ISNUMBER", TokenConstants.TISNUMBER, 1); + addEntry("ISTEXT", TokenConstants.TISTEXT, 1); + addEntry("N", TokenConstants.TN, 1); + addEntry("NA", TokenConstants.TNA, 0); + + } + + /** + * Associate a function with an identifier and specifiy the number of arguments for that function + * @param symbol The function string that will act as the key in the lookup table + * @param id The identifier for the function + * @param args The number of arguments this function requires + */ + public void addEntry(String symbol, int id, int args) { + addEntry(symbol, id); + stringToArgs.put(symbol, new Integer(args)); + } + + /** + * Retrieve the number of arguments for this function + * @param symbol The function name + * @return The number of arguments required by this function + */ + public int getArgCountFromString(String symbol) { + return ((Integer)stringToArgs.get(symbol)).intValue(); + } +} diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/formula/OperandLookup.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/formula/OperandLookup.java new file mode 100644 index 000000000000..e8a547d1b9d6 --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/formula/OperandLookup.java @@ -0,0 +1,68 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: OperandLookup.java,v $ + * $Revision: 1.7 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + + +package org.openoffice.xmerge.converter.xml.sxc.pexcel.records.formula; +import java.util.HashMap; + +import org.openoffice.xmerge.util.Debug; + +/** + * A lookup table containing information about operands + */ +public class OperandLookup extends SymbolLookup { + + /** + * The default constructor - invokes {@link #initialize() initialize()} + */ + public OperandLookup() { + initialize(); + } + + /** + * Initialize the lookup table for operands + */ + public void initialize() { + if ((stringToID != null) || (idToString != null)) { + return; + } + stringToID = new HashMap(); + idToString = new HashMap(); + addEntry("CELL_REFERENCE", TokenConstants.TREF); + addEntry("CELL_AREA_REFERENCE", TokenConstants.TAREA); + addEntry("INTEGER", TokenConstants.TNUM); + addEntry("NUMBER", TokenConstants.TNUM); + addEntry("STRING", TokenConstants.TSTRING); + addEntry("NAME", TokenConstants.TNAME); + addEntry("3D_CELL_REFERENCE", TokenConstants.TREF3D); + addEntry("3D_CELL_AREA_REFERENCE", TokenConstants.TAREA3D); + } + +} diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/formula/OperatorLookup.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/formula/OperatorLookup.java new file mode 100644 index 000000000000..0e376a6d0152 --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/formula/OperatorLookup.java @@ -0,0 +1,79 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: OperatorLookup.java,v $ + * $Revision: 1.5 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + + +package org.openoffice.xmerge.converter.xml.sxc.pexcel.records.formula; + +import java.util.HashMap; + +import org.openoffice.xmerge.util.Debug; + +/** + * A lookup table containing information about operators + */ +public class OperatorLookup extends SymbolLookup { + + /** + * The default constructor - invokes {@link #initialize() initialize()} + */ + public OperatorLookup() { + initialize(); + } + + /** + * Initialize the lookup table for operators + */ + public void initialize() { + if ((stringToID != null) || (idToString != null)) { + return; + } + stringToID = new HashMap(); + idToString = new HashMap(); + addEntry("UNARY_PLUS", TokenConstants.TUPLUS); + addEntry("UNARY_MINUS", TokenConstants.TUMINUS); + addEntry("%", TokenConstants.TPERCENT); + addEntry("+", TokenConstants.TADD); + addEntry("-", TokenConstants.TSUB); + addEntry("*", TokenConstants.TMUL); + addEntry("/", TokenConstants.TDIV); + addEntry(",", TokenConstants.TARGSEP); + addEntry("^", TokenConstants.TPOWER); + addEntry("&", TokenConstants.TCONCAT); + addEntry("(", TokenConstants.TPAREN); + addEntry(")", TokenConstants.TCLOSEPAREN); + addEntry("<", TokenConstants.TLESS); + addEntry(">", TokenConstants.TGREATER); + addEntry(">=", TokenConstants.TGTEQUALS); + addEntry("<=", TokenConstants.TLESSEQUALS); + addEntry("=", TokenConstants.TEQUALS); + addEntry("<>", TokenConstants.TNEQUALS); + } + +} diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/formula/ParseToken.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/formula/ParseToken.java new file mode 100644 index 000000000000..41912b046e7c --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/formula/ParseToken.java @@ -0,0 +1,46 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: ParseToken.java,v $ + * $Revision: 1.3 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + + +package org.openoffice.xmerge.converter.xml.sxc.pexcel.records.formula; + +public interface ParseToken +{ + public boolean isOperand(); + public boolean isOperator(); + public int getTokenType(); + + //GENERIC TOKENS (MOSTLY UNUSED + public static final int TOKEN_OPERATOR = 1; + public static final int TOKEN_OPERAND = 2; + public static final int TOKEN_FUNCTION_FIXED = 3; + public static final int TOKEN_FUNCTION_VARIABLE = 4; + +} diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/formula/PrecedenceTable.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/formula/PrecedenceTable.java new file mode 100644 index 000000000000..265334f553f1 --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/formula/PrecedenceTable.java @@ -0,0 +1,89 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: PrecedenceTable.java,v $ + * $Revision: 1.5 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + + +package org.openoffice.xmerge.converter.xml.sxc.pexcel.records.formula; + +import java.util.HashMap; + +/** + * This class defines the precedence applied to each operator when performing a conversion + * {@link org.openoffice.xmerge.converter.xml.sxc.pexcel.Records.formula.FormulaCompiler.infix2 from infix to RPN.}. + */ +public class PrecedenceTable { + public static final int DEFAULT_PRECEDENCE = 0; + public static final int EQNEQ_PRECEDENCE = 1; // =, <> + public static final int GTLTEQ_PRECEDENCE = 1; // >=, <= + public static final int GTLT_PRECEDENCE = 2; // >, < + public static final int ADDOP_PRECEDENCE = 4; // +, - + public static final int MULTOP_PRECEDENCE = 5; // *, / + public static final int FACTOR_PRECEDENCE = 6; // ^ + public static final int CONCAT_PRECEDENCE = 6; // & + public static final int UNARY_PRECEDENCE = 7; // !, Unary +, Unary - + public static final int PAREN_PRECEDENCE = 8; // (, ) + public static final int FUNCTION_PRECEDENCE = 8; + public static final int COMMA_PRECEDENCE = 8; + + private static HashMap map; + static { + map = new HashMap(); + + map.put("%", new Integer(UNARY_PRECEDENCE)); + map.put("+", new Integer(ADDOP_PRECEDENCE)); + map.put("-", new Integer(ADDOP_PRECEDENCE)); + map.put("*", new Integer(MULTOP_PRECEDENCE)); + map.put("/", new Integer(MULTOP_PRECEDENCE)); + map.put("(", new Integer(PAREN_PRECEDENCE)); + map.put(")", new Integer(PAREN_PRECEDENCE)); + map.put(",", new Integer(COMMA_PRECEDENCE)); + map.put(">", new Integer(GTLT_PRECEDENCE)); + map.put("<", new Integer(GTLT_PRECEDENCE)); + map.put("=", new Integer(EQNEQ_PRECEDENCE)); + map.put("&", new Integer(CONCAT_PRECEDENCE)); + map.put("^", new Integer(FACTOR_PRECEDENCE)); + map.put(">=", new Integer(GTLTEQ_PRECEDENCE)); + map.put("<=", new Integer(GTLTEQ_PRECEDENCE)); + map.put("<>", new Integer(EQNEQ_PRECEDENCE)); + map.put("FUNCTION", new Integer(FUNCTION_PRECEDENCE)); + } + + /** + * Retrieve the precedence value for a given operator. + * @param op Look up the precedence for this operator + * @return an integer representing the integer value of the operator + */ + public static int getPrecedence(String op) { + Object obj = map.get(op); + if (obj == null) { + return DEFAULT_PRECEDENCE; + } + return ((Integer)obj).intValue(); + } +} diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/formula/SymbolLookup.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/formula/SymbolLookup.java new file mode 100644 index 000000000000..9c9ba638b448 --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/formula/SymbolLookup.java @@ -0,0 +1,85 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: SymbolLookup.java,v $ + * $Revision: 1.4 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + + +package org.openoffice.xmerge.converter.xml.sxc.pexcel.records.formula; + +import java.util.HashMap; + +/** + * This interface defines the attributes of a lookup table for this plugin. + * Symbols will generally be either operators (_, -, *, etc) or funtion names. + */ +public abstract class SymbolLookup { + + protected HashMap stringToID = null; + protected HashMap idToString = null; + + /** + * Perform lookup table specific initialization. This would typically entail loading values into + * the lookup table. It is best to optimize this process so that data is loaded statically and shared + * across all instances of the lookup table. + */ + abstract public void initialize(); + + /** + * Associate a symbol with a numeric value in the lookup table + * @param symbol The symbol that will act as the key in the lookup table + * @param value The value to be associated with a given symbol + */ + public void addEntry(String symbol, int id) { + Integer iObj = new Integer(id); + stringToID.put(symbol, iObj); + idToString.put(iObj, symbol); + } + + /** + * Retrieve the symbol associated with a given identifier + * @param id The identfier for which we need to retieve the symbol string + * @return The string associated with this identifier in the lookup table. + */ + public String getStringFromID(int id) { + return (String)idToString.get(new Integer(id)); + } + + /** + * Retrieve the identifier associated with a given symbol + * @param symbol The symbol for which we need to retieve the identifier + * @throws UnsupportedFunctionException Thown when the symbol is not found in the lookup table + * @return The identifier associated with this string in the lookup table. + */ + public int getIDFromString(String symbol) throws UnsupportedFunctionException { + Integer i = (Integer)stringToID.get(symbol); + if (i == null) + throw new UnsupportedFunctionException("Token '" + symbol + "' not supported by Pocket Excel"); + + return ((Integer)stringToID.get(symbol)).intValue(); + } +} diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/formula/Token.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/formula/Token.java new file mode 100644 index 000000000000..4ec1884dae8b --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/formula/Token.java @@ -0,0 +1,157 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: Token.java,v $ + * $Revision: 1.3 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + + +package org.openoffice.xmerge.converter.xml.sxc.pexcel.records.formula; + + +import java.io.*; + +/** + * A Token is the basic building block of any formula. + * A Token can be of four types (Operator, Operand, Function with fixed + * arguments and function with a variable number of arguments. Each type can + * have numerous id's. Thetypes are define in <code>ParseToken</code> and the + * id's are defined in <code>TokenConstants</code>. The other member variables + * are priority which is returned from the <code>PrecedenceTable</code>, the + * value which is the String equivalent of the token (eg. "+", "$A$12", "SUM") + * and the number of arguments which is only valid for operators and functions. + * Tokens should never be created directly and instead are created by the + * <code>TokenFactory</code> + */ +public class Token implements ParseToken { + + private String value; + private int type; // operator, operand, function fixed, function variable + private int id; // cell reference, SUM, integer + private int priority; + private int numArgs=-1; + + public Token(String op, int type, int id, int args) { + this.value = op; + this.type = type; + this.id = id; + this.numArgs = args; + if(type==ParseToken.TOKEN_FUNCTION_VARIABLE) { + priority = PrecedenceTable.getPrecedence("FUNCTION"); + } else if(type==ParseToken.TOKEN_OPERATOR) { + priority = PrecedenceTable.getPrecedence(op); + } else { + priority = PrecedenceTable.getPrecedence("DEFAULT"); + } + } + + /** + * Checks if the current token is an operator + * + * @return A <code>boolean</code> result of the comaparison + */ + public boolean isOperator() { + return type == ParseToken.TOKEN_OPERATOR; + } + + /** + * Checks if the current token is an operand + * + * @return A <code>boolean</code> result of the comaparison + */ + public boolean isOperand() { + return type == ParseToken.TOKEN_OPERAND; + } + + /** + * Checks if the current token is a function + * + * @return A <code>boolean</code> result of the comaparison + */ + public boolean isFunction() { + return (type==ParseToken.TOKEN_FUNCTION_FIXED) || (type==ParseToken.TOKEN_FUNCTION_VARIABLE); + } + + /** + * Returns the token type. This can be one of four values (TOKEN_OPERATOR, + * TOKEN_OPERAND, TOKEN_FUNCTION_FIXED, TOKEN_FUNCTION_VARIABLE) defined in + * <code>ParseToken</code> + * + * @return A <code>boolean</code> result of the comparison + */ + public int getTokenType() { + + return type; + } + + /** + * Returns the ID of this token. This ID is equivalent to the pexcel hex + * value and is defined in <code>ParseToken</code> + * + * @return Returns the id of this token + */ + public int getTokenID() { + + return id; + } + + /** + * Returns the <code>String</code> equivalent of this token + * + * @return The <code>String</code> representing this Token + */ + public String getValue() { + return value; + } + + /** + * Returns the number of arguments if this token represents an operator or + * function. Otherwise returns -1. + * + * @return The number of arguments + */ + public int getNumArgs() { + return numArgs; + } + + /** + * Checks if the current token is an operator + * + * @return A <code>boolean</code> result of the comparison + */ + public int getTokenPriority() { + return priority; + } + + /** + * Returns the <code>String</code> equivalent of this token + * + * @return The <code>String</code> representing this Token + */ + public String toString() { + return getValue(); + } +} diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/formula/TokenConstants.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/formula/TokenConstants.java new file mode 100644 index 000000000000..b8b37be7772e --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/formula/TokenConstants.java @@ -0,0 +1,209 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: TokenConstants.java,v $ + * $Revision: 1.7 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + + +package org.openoffice.xmerge.converter.xml.sxc.pexcel.records.formula; + +import java.io.*; + +public interface TokenConstants { + + // Unary Operator Tokens + public static final int TUPLUS = 0x12; + public static final int TUMINUS = 0x13; + public static final int TPERCENT = 0x14; + public static final int TPAREN = 0x15; + // Binary Operator Tokens + public static final int TADD = 0x03; + public static final int TSUB = 0x04; + public static final int TMUL = 0x05; + public static final int TDIV = 0x06; + public static final int TPOWER = 0x07; + public static final int TCONCAT = 0x08; + + //Logical operators + public static final int TLESS = 0x09; + public static final int TLESSEQUALS = 0x0A; + public static final int TEQUALS = 0x0B; + public static final int TGTEQUALS = 0x0C; + public static final int TGREATER = 0x0D; + public static final int TNEQUALS = 0x0E; + + // Function Operator Tokens + public static final int TFUNC = 0x41; + public static final int TFUNCVAR = 0x42; + + // Constant Operand Tokens + public static final int TSTRING = 0x17; + public static final int TINT = 0x1E; + public static final int TNUM = 0x1F; + // Operand Tokens + public static final int TREF = 0x44; + public static final int TAREA = 0x25; + public static final int TNAME = 0x23; + public static final int TREF3D = 0x3A; + public static final int TAREA3D = 0x3B; + + // + public static final int TARGSEP = 0x1001; + public static final int TCLOSEPAREN = 0x1002; + + // Variable argument Functions + // Math and Trig + public static final int TSUM = 0x04; + public static final int TPRODUCT = 0xB7; + public static final int TSUMIF = 0x0159; + public static final int TLOG = 0x6D; + public static final int TRUNC = 0xC5; + // Financial + public static final int TDDB = 0x90; + public static final int TFV = 0x39; + public static final int TIRR = 0x3E; + public static final int TNPER = 0x3A; + public static final int TNPV = 0x0B; + public static final int TPMT = 0x3B; + public static final int TPV = 0x38; + public static final int TRATE = 0x3C; + // Statistical + public static final int TAVERAGE = 0x05; + public static final int TCOUNT = 0x00; + public static final int TCOUNTA = 0xA9; + public static final int TMAX = 0x07; + public static final int TMIN = 0x06; + public static final int TSTDEV = 0x0C; + public static final int TSTDEVP = 0xC1; + public static final int TVAR = 0x2E; + public static final int TVARP = 0xC2; + // Lookup + public static final int TCHOOSE = 0x64; + public static final int THLOOKUP = 0x65; + public static final int TINDEX = 0x1D; + public static final int TMATCH = 0x40; + public static final int TVLOOKUP = 0x66; + // Text + public static final int TRIGHT = 0x74; + public static final int TSUBSTITUTE = 0x78; + public static final int TFIND = 0x7c; + public static final int TLEFT = 0x73; + // Logical + public static final int TAND = 0x24; // 42 + public static final int TIF = 0x01; // 42 + public static final int TOR = 0x25; // 42 + + // Fixed argument Functions + // Math and Trig + public static final int TABS = 0x18; + public static final int TACOS = 0x63; + public static final int TASIN = 0x62; + public static final int TATAN = 0x12; + public static final int TATAN2 = 0x61; + public static final int TCOS = 0x10; + public static final int TSIN = 0x0F; + + public static final int TCOUNTIF = 0x015A; + public static final int TDEGREES = 0x0157; + public static final int TEXP = 0x15; + public static final int TFACT = 0xB8; + public static final int TINTE = 0x19; + public static final int TLN = 0x16; + + public static final int TLOG10 = 0x17; + public static final int TMOD = 0x27; + public static final int TPI = 0x13; + + public static final int TPOWERF = 0x0151; + public static final int TRADIANS = 0x0156; + public static final int TRAND = 0x3F; + public static final int TROUND = 0x1B; + public static final int TSQRT = 0x14; + public static final int TTAN = 0x11; + + public static final int TSLN = 0x8E; + public static final int TSYD = 0x8F; + + // Date and Time + public static final int TDATE = 0x41; + public static final int TDATEVALUE = 0x8C; + public static final int TDAY = 0x43; + public static final int THOUR = 0x47; + public static final int TMINUTE = 0x48; + public static final int TMONTH = 0x44; + public static final int TNOW = 0x4A; + public static final int TSECOND = 0x49; + public static final int TTIME = 0x42; + public static final int TTIMEVALUE = 0x8D; + public static final int TYEAR = 0x45; + // Statistical + public static final int TCOUNTBLANK = 0x015B ; + // lookup + public static final int TCOLUMNS = 0x4D; + public static final int TROWS = 0x4C; + // Database + public static final int TDAVAERAGE = 0x2A; + public static final int TDCOUNT = 0x28; + public static final int TDCOUNTA = 0xC7; + public static final int TDGET = 0xEB; + public static final int TDMAX = 0x2C; + public static final int TDMIN = 0x2B; + public static final int TDPRODUCT = 0xBD; + public static final int TDSTDEV = 0x2D; + public static final int TDSTDEVP = 0xC3; + public static final int TDSUM = 0x29; + public static final int TDVAR = 0x2F; + public static final int TDVARP = 0xC4; + // Text + public static final int TEXACT = 0x75; + public static final int TLEN = 0x20; + public static final int TLOWER = 0x70; + public static final int TMID = 0x1F; // ?????? + public static final int TPROPER = 0x72; + public static final int TREPLACE = 0x77; + public static final int TREPT = 0x1E; + public static final int TT = 0x82; + public static final int TRIM = 0x76; + public static final int TUPPER = 0x71; + public static final int TVALUE = 0x21; + // Logical + public static final int TFALSE = 0x23; + public static final int TNOT = 0x26; + public static final int TTRUE = 0x22; + // Informational + public static final int TERRORTYPE = 0x05; + public static final int TISBLANK = 0x81; + public static final int TISERR = 0x7E; + public static final int TISERROR = 0x03; + public static final int TISLOGICAL = 0xC6; + public static final int TISNA = 0x02; + public static final int TISNONTEXT = 0xBE; + public static final int TISNUMBER = 0x80; + public static final int TISTEXT = 0x7F; + public static final int TN = 0x83; + public static final int TNA = 0x0A; +} diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/formula/TokenDecoder.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/formula/TokenDecoder.java new file mode 100644 index 000000000000..3390463e3756 --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/formula/TokenDecoder.java @@ -0,0 +1,501 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: TokenDecoder.java,v $ + * $Revision: 1.8 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + + +package org.openoffice.xmerge.converter.xml.sxc.pexcel.records.formula; + +import java.io.*; +import java.util.Vector; +import java.util.Enumeration; + +import org.openoffice.xmerge.util.Debug; +import org.openoffice.xmerge.util.EndianConverter; +import org.openoffice.xmerge.converter.xml.sxc.pexcel.records.DefinedName; +import org.openoffice.xmerge.converter.xml.sxc.pexcel.records.Workbook; + +/** + * The TokenDecoder decodes a byte[] to an equivalent <code>String</code>. The only + * public method apart from the default constructor is the getTokenVector method. + * This method takes an entire formula as a pexcel byte[] and decodes it into + * a series of <code>Token</code>s. It adds these to a <code>Vector</code> which + * is returned once all the tokens have been decoded. The Decoder supports + * the following tokens.<br><br> + * + * Operands Floating point's, Cell references (absolute and relative), + * cell ranges<br> + * Operators +,-,*,/,<,>.<=,>=,<><br> + * Functions All pexcel fixed and varaible argument functions + * + */ +public class TokenDecoder { + + private TokenFactory tf; + private FunctionLookup fl; + private OperatorLookup operatorLookup; + private OperandLookup operandLookup; + private Workbook wb; + + /** + * Default Constructor initializes the <code>TokenFactory</code> for generating + * <code>Token</code> and the <code>SymbolLookup</code> for generating + * Strings from hex values. + */ + public TokenDecoder() { + tf = new TokenFactory(); + fl = new FunctionLookup(); + operatorLookup = new OperatorLookup(); + operandLookup = new OperandLookup(); + } + + /** + * Sets global workbook data needed for defined names + */ + public void setWorkbook(Workbook wb) { + + Debug.log(Debug.TRACE, "TokenDecoder : setWorkbook"); + this.wb = wb; + } + + /** + * Returns a <code>Vector</code> of <code>Token</code> decoded from a + * byte[]. The byte[] is first converted to a + * <code>ByteArrayInputStream</code> as this is the easiest way of reading + * bytes. + * + * @param formula A Pocket Excel Formula byte[] + * @return A <code>Vector</code> of deoded <code>Token</code> + */ + public Vector getTokenVector(byte[] formula) { + + Vector v = new Vector(); + + ByteArrayInputStream bis = new ByteArrayInputStream(formula); + int b = 0 ; + Token t; + + while (bis.available()!=0) + { + b = bis.read(); + + switch (b) { + + case TokenConstants.TAREA3D: + Debug.log(Debug.TRACE, "Decoded 3D Area Cell Reference: "); + v.add(read3DCellAreaRefToken(bis)); + Debug.log(Debug.TRACE, "Decoded 3D Area Cell Reference: " + v.lastElement()); + break; + case TokenConstants.TREF3D: + Debug.log(Debug.TRACE, "Decoded 3D Cell Reference: "); + v.add(read3DCellRefToken(bis)); + Debug.log(Debug.TRACE, "Decoded 3D Cell Reference: " + v.lastElement()); + break; + case TokenConstants.TREF : + v.add(readCellRefToken(bis)); + Debug.log(Debug.TRACE, "Decoded Cell Reference: " + v.lastElement()); + break; + case TokenConstants.TAREA : + v.add(readCellAreaRefToken(bis)); + Debug.log(Debug.TRACE, "Decoded Cell Area Reference: " + v.lastElement()); + break; + case TokenConstants.TNUM : + v.add(readNumToken(bis)); + Debug.log(Debug.TRACE, "Decoded number : " + v.lastElement()); + break; + case TokenConstants.TFUNCVAR : + v.add(readFunctionVarToken(bis)); + Debug.log(Debug.TRACE, "Decoded variable argument function: " + v.lastElement()); + break; + case TokenConstants.TFUNC : + v.add(readFunctionToken(bis)); + Debug.log(Debug.TRACE, "Decoded function: " + v.lastElement()); + break; + case TokenConstants.TSTRING : + v.add(readStringToken(bis)); + Debug.log(Debug.TRACE, "Decoded string: " + v.lastElement()); + break; + case TokenConstants.TNAME : + v.add(readNameToken(bis)); + Debug.log(Debug.TRACE, "Decoded defined name: " + v.lastElement()); + break; + case TokenConstants.TUPLUS: + case TokenConstants.TUMINUS: + case TokenConstants.TPERCENT: + v.add(readOperatorToken(b, 1)); + Debug.log(Debug.TRACE, "Decoded Unary operator : " + v.lastElement()); + break; + case TokenConstants.TADD : + case TokenConstants.TSUB : + case TokenConstants.TMUL : + case TokenConstants.TDIV : + case TokenConstants.TLESS : + case TokenConstants.TLESSEQUALS : + case TokenConstants.TEQUALS : + case TokenConstants.TGTEQUALS : + case TokenConstants.TGREATER : + case TokenConstants.TNEQUALS : + v.add(readOperatorToken(b, 2)); + Debug.log(Debug.TRACE, "Decoded Binary operator : " + v.lastElement()); + break; + + default : + Debug.log(Debug.TRACE, "Unrecognized byte : " + b); + } + } + return v; + } + + /** + * Converts a zero based integer to a char (eg. a=0, b=1). + * It assumes the integer is less than 26. + * + * @param i A 0 based index + * @return The equivalent character + */ + private char int2Char(int i) { + return (char) ('A' + i); + } + + /** + * Reads a Cell Reference token from the <code>ByteArrayInputStream</code> + * + * @param bis The <code>ByteArrayInputStream</code> from which we read the + * bytes. + * @return The decoded String <code>Token</code> + */ + private Token readStringToken(ByteArrayInputStream bis) { + + int len = ((int)bis.read())*2; + int options = (int)bis.read(); + Debug.log(Debug.TRACE,"String length is " + len + " and Options Flag is " + options); + byte [] stringBytes = new byte[len]; + int numRead =0; + if ((numRead = bis.read(stringBytes, 0, len)) != len) { + Debug.log(Debug.TRACE,"Expected " + len + " bytes. Could only read " + numRead + " bytes."); + //throw new IOException("Expected " + len + " bytes. Could only read " + numRead + " bytes."); + } + StringBuffer outputString = new StringBuffer(); + outputString.append('"'); + try { + Debug.log(Debug.TRACE,"Using LE encoding"); + outputString.append(new String(stringBytes, "UTF-16LE")); + } catch (IOException eIO) { + outputString.append(new String(stringBytes)); //fall back to default encoding + } + outputString.append('"'); + + return (tf.getOperandToken(outputString.toString(), "STRING")); + } + + /** + * Reads a Defined Name token from the <code>ByteArrayInputStream</code> + * + * @param bis The <code>ByteArrayInputStream</code> from which we read the + * bytes. + * @return The decoded Name <code>Token</code> + */ + private Token readNameToken(ByteArrayInputStream bis) { + byte buffer[] = new byte[2]; + buffer[0] = (byte) bis.read(); + buffer[1] = (byte) bis.read(); + int nameIndex = EndianConverter.readShort(buffer); + bis.skip(12); // the next 12 bytes are unused + Enumeration e = wb.getDefinedNames(); + int i = 1; + while(i<nameIndex) { + e.nextElement(); + i++; + } + Debug.log(Debug.TRACE,"Name index is " + nameIndex); + DefinedName dn = (DefinedName)e.nextElement(); + Debug.log(Debug.TRACE,"DefinedName is " + dn.getName()); + return (tf.getOperandToken(dn.getName(), "NAME")); + } + + /** + * Reads a Cell Reference token from the <code>ByteArrayInputStream</code> + * + * @param bis The <code>ByteArrayInputStream</code> from which we read the + * bytes. + * @return The decoded Cell Reference <code>Token</code> + */ + private Token readCellRefToken(ByteArrayInputStream bis) { + + byte buffer[] = new byte[2]; + String outputString = new String(); + + buffer[0] = (byte) bis.read(); + buffer[1] = (byte) bis.read(); + int formulaRow = EndianConverter.readShort(buffer); + int relativeFlags = (formulaRow & 0xC000)>>14; + formulaRow &= 0x3FFF; + int formulaCol = (byte) bis.read(); + + outputString = int2CellStr(formulaRow, formulaCol, relativeFlags); + + return (tf.getOperandToken(outputString,"CELL_REFERENCE")); + } + + /** + * Reads a Cell Reference token from the <code>ByteArrayInputStream</code> + * + * @param bis The <code>ByteArrayInputStream</code> from which we read the + * bytes. + * @return The decoded Cell Reference <code>Token</code> + */ + private Token read3DCellRefToken(ByteArrayInputStream bis) { + + byte buffer[] = new byte[2]; + String outputString = new String(); + + bis.skip(10); + + buffer[0] = (byte) bis.read(); + buffer[1] = (byte) bis.read(); + int Sheet1 = EndianConverter.readShort(buffer); + buffer[0] = (byte) bis.read(); + buffer[1] = (byte) bis.read(); + int Sheet2 = EndianConverter.readShort(buffer); + + buffer[0] = (byte) bis.read(); + buffer[1] = (byte) bis.read(); + int formulaRow = EndianConverter.readShort(buffer); + int relativeFlags = (formulaRow & 0xC000)>>14; + formulaRow &= 0x3FFF; + int formulaCol = (byte) bis.read(); + String cellRef = "." + int2CellStr(formulaRow, formulaCol, relativeFlags); + if(Sheet1 == Sheet2) { + outputString = "$" + wb.getSheetName(Sheet1) + cellRef; + } else { + outputString = "$" + wb.getSheetName(Sheet1) + cellRef + ":$" + wb.getSheetName(Sheet2) + cellRef; + } + + return (tf.getOperandToken(outputString,"3D_CELL_REFERENCE")); + } + + /** + * Reads a Cell Reference token from the <code>ByteArrayInputStream</code> + * + * @param bis The <code>ByteArrayInputStream</code> from which we read the + * bytes. + * @return The decoded Cell Reference <code>Token</code> + */ + private Token read3DCellAreaRefToken(ByteArrayInputStream bis) { + + byte buffer[] = new byte[2]; + String outputString = new String(); + + bis.skip(10); + + buffer[0] = (byte) bis.read(); + buffer[1] = (byte) bis.read(); + int Sheet1 = EndianConverter.readShort(buffer); + buffer[0] = (byte) bis.read(); + buffer[1] = (byte) bis.read(); + int Sheet2 = EndianConverter.readShort(buffer); + + buffer[0] = (byte) bis.read(); + buffer[1] = (byte) bis.read(); + int formulaRow1 = EndianConverter.readShort(buffer); + int relativeFlags1 = (formulaRow1 & 0xC000)>>14; + formulaRow1 &= 0x3FFF; + + buffer[0] = (byte) bis.read(); + buffer[1] = (byte) bis.read(); + int formulaRow2 = EndianConverter.readShort(buffer); + int relativeFlags2 = (formulaRow2 & 0xC000)>>14; + formulaRow2 &= 0x3FFF; + + int formulaCol1 = (byte) bis.read(); + int formulaCol2 = (byte) bis.read(); + + String cellRef1 = "." + int2CellStr(formulaRow1, formulaCol1, relativeFlags1); + String cellRef2 = int2CellStr(formulaRow2, formulaCol2, relativeFlags2); + + if(Sheet1 == Sheet2) { + outputString = "$" + wb.getSheetName(Sheet1) + cellRef1 + ":" + cellRef2; + } else { + outputString = "$" + wb.getSheetName(Sheet1) + cellRef1 + ":$" + wb.getSheetName(Sheet2) + "." + cellRef2; + } + + return (tf.getOperandToken(outputString,"3D_CELL_AREA_REFERENCE")); + } + + /** + * Converts a row and col 0 based index to a spreadsheet cell reference. + * It also has a relativeFlags which indicates whether or not the + * Cell Reference is relative or absolute (Absolute is denoted with '$') + * + * 00 = absolute row, absolute col + * 01 = absolute row, relative col + * 10 = relative row, absolute col + * 11 = relative row, relative col + * + * @param row The cell reference 0 based index to the row + * @param col The cell reference 0 based index to the row + * @param relativeFlags Flags indicating addressing of row and column + * @return A <code>String</code> representing a cell reference + */ + private String int2CellStr(int row, int col, int relativeFlags) { + String outputString = ""; + int firstChar = (col + 1) / 26; + + if((relativeFlags & 1) == 0) { + outputString += "$"; + } + + if(firstChar>0) { + int secondChar = (col + 1) % 26; + outputString += Character.toString(int2Char(firstChar - 1)) + Character.toString(int2Char(secondChar - 1)); + } else { + outputString += Character.toString(int2Char(col)); + } + if((relativeFlags & 2) == 0) { + outputString += "$"; + } + outputString += Integer.toString(row+1); + return outputString; + } + + /** + * Reads a Cell Area Reference (cell range) <code>Token</code> from + * the <code>ByteArrayInputStream</code> + * + * @param bis The <code>ByteArrayInputStream</code> from which we read the + * bytes. + * @return The equivalent Cell Area Reference (cell range) + * <code>Token</code> + */ + private Token readCellAreaRefToken(ByteArrayInputStream bis) { + byte buffer[] = new byte[2]; + int formulaRow1, formulaRow2; + int formulaCol1, formulaCol2; + + String outputString = new String(); + + buffer[0] = (byte) bis.read(); + buffer[1] = (byte) bis.read(); + formulaRow1 = EndianConverter.readShort(buffer); + int relativeFlags1 = (formulaRow1 & 0xC000)>>14; + formulaRow1 &= 0x3FFF; + buffer[0] = (byte) bis.read(); + buffer[1] = (byte) bis.read(); + formulaRow2 = EndianConverter.readShort(buffer); + int relativeFlags2 = (formulaRow2 & 0xC000)>>14; + formulaRow2 &= 0x3FFF; + + formulaCol1 = (byte) bis.read(); + formulaCol2 = (byte) bis.read(); + + outputString = int2CellStr(formulaRow1, formulaCol1, relativeFlags1); + outputString += (":" + int2CellStr(formulaRow2, formulaCol2, relativeFlags2)); + + return (tf.getOperandToken(outputString,"CELL_AREA_REFERENCE")); + } + + + /** + * Reads a Number (floating point) token from the <code>ByteArrayInputStream</code> + * + * @param bis The <code>ByteArrayInputStream</code> from which we read the + * bytes. + * @return The decoded Integer <code>Token</code> + */ + private Token readNumToken(ByteArrayInputStream bis) { + + byte numBuffer[] = new byte[8]; + + for(int j=0;j<8;j++) { + numBuffer[j]=(byte) bis.read(); + } + + return (tf.getOperandToken(Double.toString(EndianConverter.readDouble(numBuffer)),"NUMBER")); + } + + /** + * Read an Operator token from the <code>ByteArrayInputStream</code> + * + * @param b A Pocket Excel number representing an operator. + * @param args The number of arguments this operator takes. + * @return The decoded Operator <code>Token</code> + */ + private Token readOperatorToken(int b, int args) { + + Token t; + + if(b==TokenConstants.TUPLUS) { + t = tf.getOperatorToken("+", args); + } else if(b==TokenConstants.TUMINUS) { + t = tf.getOperatorToken("-", args); + } else { + t = tf.getOperatorToken(operatorLookup.getStringFromID(b), args); + } + return t; + } + + /** + * Read a Function token from the <code>ByteArrayInputStream</code> + * This function can have any number of arguments and this number is read + * in with the record + * + * @param bis The <code>ByteArrayInputStream</code> from which we read the + * bytes. + * @return The decoded variable argument Function <code>Token</code> + */ + private Token readFunctionVarToken(ByteArrayInputStream bis) { + + int numArgs = 0; + numArgs = bis.read(); + byte buffer[] = new byte[2]; + buffer[0] = (byte) bis.read(); + buffer[1] = (byte) bis.read(); + int functionID = EndianConverter.readShort(buffer); + return (tf.getFunctionToken(fl.getStringFromID(functionID),numArgs)); + } + + /** + * Read a Function token from the <code>ByteArrayInputStream</code> + * This function has a fixed number of arguments which it will get + * from <code>FunctionLookup</code>. + * + * @param bis The <code>ByteArrayInputStream</code> from which we read the + * bytes. + * @return The decoded fixed argument Function <code>Token</code> + */ + private Token readFunctionToken(ByteArrayInputStream bis) { + + byte buffer[] = new byte[2]; + buffer[0] = (byte) bis.read(); + buffer[1] = (byte) bis.read(); + int functionID = EndianConverter.readShort(buffer); + String functionName = fl.getStringFromID(functionID); + return (tf.getFunctionToken(functionName,fl.getArgCountFromString(functionName))); + } + +} diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/formula/TokenEncoder.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/formula/TokenEncoder.java new file mode 100644 index 000000000000..d2002df2819a --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/formula/TokenEncoder.java @@ -0,0 +1,564 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: TokenEncoder.java,v $ + * $Revision: 1.8 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + + +package org.openoffice.xmerge.converter.xml.sxc.pexcel.records.formula; + +import java.io.*; +import java.util.Vector; +import java.util.Enumeration; + +import org.openoffice.xmerge.util.Debug; +import org.openoffice.xmerge.util.EndianConverter; +import org.openoffice.xmerge.converter.xml.sxc.pexcel.records.Workbook; +import org.openoffice.xmerge.converter.xml.sxc.pexcel.records.BoundSheet; +import org.openoffice.xmerge.converter.xml.sxc.pexcel.records.DefinedName; + +/** + * The TokenEncoder encodes a Token to an equivalent pexcel byte[]. The only + * public method apart from the default constructor is the getByte method. + * This method picks an encoder based onthe Token's type or id field and uses + * that encoder to return a byte[] which it returns. This Encoder supports + * Operands Floating point's, Cell references (absolute and relative), + * cell ranges + * Operators +,-,*,/,<,>.<=,>=,<> + * Functions All pexcel fixed and varaible argument functions + * + */ +public class TokenEncoder { + + private FunctionLookup fl; + private String parseString; + private int index; + private Workbook wb; + + /** + * Default Constructor + */ + public TokenEncoder() { + + parseString = new String(); + fl = new FunctionLookup(); + } + + /** + * Sets global workbook data needed for defined names + */ + public void setWorkbook(Workbook wb) { + + this.wb = wb; + } + + + /** + * Return the byte[] equivalent of a <code>Token</code>. The various + * encoders return <code>Vector</code> of <code>Byte</code> instead + * of byte[] because the number of bytes returned varies with each + * <code>Token</code> encoded. After the encoding is finished the Vector + * in converted to a byte[]. + * + * @param t The <code>Token</code> to be encoded + * @return An equivalent Pocket Excel byte[] + */ + public byte[] getByte(Token t) throws IOException { + + Vector tmpByteArray = null; // we use this cause we don't know till after + // the encoding takes place how big the byte [] will be + //index=0; // This class is declared static in + // FormulaHelper so better make sure our index is 0 + if(t.getTokenType()==ParseToken.TOKEN_OPERATOR) { + tmpByteArray = operatorEncoder(t); + } else if (t.getTokenType()==ParseToken.TOKEN_FUNCTION_VARIABLE || t.getTokenType()==ParseToken.TOKEN_FUNCTION_FIXED){ + tmpByteArray = functionEncoder(t); + } else { // Operands and functions + switch(t.getTokenID()) { + case TokenConstants.TNAME : + tmpByteArray = nameDefinitionEncoder(t); + break; + case TokenConstants.TREF3D : + tmpByteArray = threeDCellRefEncoder(t); + break; + case TokenConstants.TAREA3D: + tmpByteArray = threeDAreaRefEncoder(t); + break; + case TokenConstants.TREF : + tmpByteArray = cellRefEncoder(t); + break; + case TokenConstants.TAREA : + tmpByteArray = areaRefEncoder(t); + break; + case TokenConstants.TNUM : + tmpByteArray = numEncoder(t); + break; + case TokenConstants.TSTRING : + tmpByteArray = stringEncoder(t); + break; + default : + Debug.log(Debug.ERROR, "Encoder found unrecognized Token"); + } + } + + byte cellRefArray[] = new byte[tmpByteArray.size()]; + int i = 0; + String s = new String(); + for(Enumeration e = tmpByteArray.elements();e.hasMoreElements();) { + Byte tmpByte = (Byte) e.nextElement(); + s = s + tmpByte + " "; + cellRefArray[i] = tmpByte.byteValue(); + i++; + } + Debug.log(Debug.TRACE, "Encoding Token " + t.getValue() + " as [" + s + "]"); + return cellRefArray; + } + + /** + * An Operator Encoder. + * + * @param t <code>Token</code> to be encoded + * @return A <code>Vector</code> of pexcel <code>Byte</code> + */ + private Vector operatorEncoder(Token t) { + + Vector tmpByteArray = new Vector(); + tmpByteArray.add(new Byte((byte)t.getTokenID())); + return tmpByteArray; + } + + + /** + * A String Encoder. + * + * @param t <code>Token</code> to be encoded + * @return A <code>Vector</code> of pexcel <code>Byte</code> + */ + private Vector stringEncoder(Token t) throws IOException{ + + Vector tmpByteArray = new Vector(); + tmpByteArray.add(new Byte((byte)t.getTokenID())); + tmpByteArray.add(new Byte((byte)(t.getValue().length()))); + tmpByteArray.add(new Byte((byte)0x01)); + byte [] stringBytes = t.getValue().getBytes("UTF-16LE"); + for (int i=0; i<stringBytes.length; i++) { + tmpByteArray.add(new Byte(stringBytes[i])); + } + return tmpByteArray; + } + + + /** + * An Integer Encoder. + * + * @param t <code>Token</code> to be encoded + * @return A <code>Vector</code> of pexcel <code>Byte</code> + */ + private Vector numEncoder(Token t) { + + Vector tmpByteArray = new Vector(); + + double cellLong = (double) Double.parseDouble(t.getValue()); + tmpByteArray.add(new Byte((byte)t.getTokenID())); + byte[] tempByte = EndianConverter.writeDouble(cellLong); + for(int byteIter=0;byteIter<tempByte.length;byteIter++) { + tmpByteArray.add(new Byte(tempByte[byteIter])); + } + return tmpByteArray; + } + + /** + * Converts a char to an int. It is zero based + * so a=0, b=1 etc. + * + * @param ch the character to be converted + * @return -1 if not a character otherwise a 0 based index + */ + private int char2int(char ch) { + if(!Character.isLetter(ch)) + return -1; + + ch = Character.toUpperCase(ch); + return ch-'A'; + } + + /** + * Identify letters + * + * @param c The character which is to be identified + * @return A boolean returning the result of the comparison + */ + private boolean isAlpha(char c) { + return(Character.isLetter(c)); + } + + /** + * Identify numbers + * + * @param c The character which is to be identified + * @return A boolean returning the result of the comparison + */ + private boolean isDigit(char c) { + return(Character.isDigit(c)); + } + + /** + * Identify letters or numbers + * + * @param c The character which is to be identified + * @return A boolean returning the result of the comparison + */ + private boolean isAlphaNum(char c) { + return(isAlpha(c) || isDigit(c)); + } + + /** + * Parses a column reference and returns it's integer equivalent. (eg. + * A=0, D=3, BA=27) + * + * @return an 0 based index to a column + */ + private int column() { + char ch = parseString.charAt(index); + String columnStr = new String(); + int col = 0; + + while(isAlpha(ch)) { + columnStr += ch; + index++; + ch = parseString.charAt(index); + } + + if(columnStr.length()==1) { + col = char2int(columnStr.charAt(0)); + } else if (columnStr.length()==2) { + col = char2int(columnStr.charAt(0)) + 1; + col = (col*26) + char2int(columnStr.charAt(1)); + } else { + Debug.log(Debug.ERROR, "Invalid Column Reference " + columnStr ); + } + + + return col; + } + + /** + * Parses a column reference and returns it's integer equivalent. (eg. + * A=0, D=3, BA=27) + * + * @return an 0 based index to a column + */ + private int row() { + char ch = parseString.charAt(index); + String rowStr = new String(); + int row = 0; + boolean status = true; + + do { + rowStr += ch; + index++; + if(index>=parseString.length()) { + status = false; + } else { + ch = parseString.charAt(index); + } + } while(isDigit(ch) && status); + return Integer.parseInt(rowStr)-1; // Pexcel uses a 0 based index + } + + /** + * A Cell Reference Encoder (It supports absolute and relative addressing) + * + * @param t <code>Token</code> to be encoded + * @return A <code>Vector</code> of pexcel <code>Byte</code> + */ + private byte[] encodeCellCoordinates(String cellCoordinates) { + int col = 0, row = 0; + int addressing = 0xC000; + + index = 0; + parseString = cellCoordinates; + Debug.log(Debug.TRACE,"Encoding cell coordinates " + cellCoordinates); + if(cellCoordinates.charAt(index)=='$') { + addressing &= 0x8000; + index++; + } + col = column(); + if(cellCoordinates.charAt(index)=='$') { + addressing &= 0x4000; + index++; + } + row = row(); // Pexcel uses a 0 based index + row |= addressing; + byte tokenBytes[] = new byte[3]; + tokenBytes[0] = (byte)row; + tokenBytes[1] = (byte)(row>>8); + tokenBytes[2] = (byte)col; + return tokenBytes; + } + + /** + * A name definition Encoder + * + * @param t <code>Token</code> to be encoded + * @return A <code>Vector</code> of pexcel <code>Byte</code> + */ + private Vector nameDefinitionEncoder(Token t) { + + Vector tmpByteArray = new Vector(); + + String nameString = t.getValue(); + Debug.log(Debug.TRACE,"NameDefinitionEncoder : " + nameString); + tmpByteArray.add(new Byte((byte)t.getTokenID())); + Enumeration e = wb.getDefinedNames(); + DefinedName dn; + String name; + int definedNameIndex = 0; + do { + dn = (DefinedName)e.nextElement(); + name = dn.getName(); + Debug.log(Debug.TRACE,"Name pulled from DefinedName : " + name); + definedNameIndex++; + } while(!nameString.equalsIgnoreCase(name) && e.hasMoreElements()); + + tmpByteArray.add(new Byte((byte)definedNameIndex)); + tmpByteArray.add(new Byte((byte)0x00)); + + for(int i = 0;i < 12;i++) { + tmpByteArray.add(new Byte((byte)0x00)); + } + + return tmpByteArray; + } + /** + * A Cell Reference Encoder. It supports absolute and relative addressing + * but not sheetnames. + * + * @param t <code>Token</code> to be encoded + * @return A <code>Vector</code> of pexcel <code>Byte</code> + */ + private Vector cellRefEncoder(Token t) { + + Vector tmpByteArray = new Vector(); + + tmpByteArray.add(new Byte((byte)t.getTokenID())); + byte cellRefBytes[] = encodeCellCoordinates(t.getValue()); + for(int i = 0;i < cellRefBytes.length;i++) { + tmpByteArray.add(new Byte(cellRefBytes[i])); + } + return tmpByteArray; + } + + /** + * This function will find the sheetname index for a given String + * + * @param t <code>Token</code> to be encoded + * @return A <code>Vector</code> of pexcel <code>Byte</code> + */ + private short findSheetIndex(String s) { + + short sheetIndex = 0; + String savedName; + String sheetName; + if (s.startsWith("$")) { + sheetName = s.substring(1,s.length()); // Remove $ + } else { + sheetName = s.substring(0,s.length()); + } + Debug.log(Debug.TRACE,"Searching for Worksheet : " + sheetName); + Vector names = wb.getWorksheetNames(); + Enumeration e = names.elements(); + do { + savedName = (String) e.nextElement(); + sheetIndex++; + } while(!savedName.equalsIgnoreCase(sheetName) && e.hasMoreElements()); + + Debug.log(Debug.TRACE,"Setting sheetindex to " + sheetIndex); + return (short)(sheetIndex-1); + } + + /** + * A 3D Cell reference encoder + * + * @param t <code>Token</code> to be encoded + * @return A <code>Vector</code> of pexcel <code>Byte</code> + */ + private Vector threeDCellRefEncoder(Token t) { + + Vector tmpByteArray = new Vector(); + parseString = t.getValue(); + Debug.log(Debug.TRACE,"Encoding 3D Cell reference " + t); + tmpByteArray.add(new Byte((byte)t.getTokenID())); + tmpByteArray.add(new Byte((byte)0xFF)); + tmpByteArray.add(new Byte((byte)0xFF)); + for(int i = 0;i < 8;i++) { + tmpByteArray.add(new Byte((byte)0x00)); + } + + String sheetRef = parseString.substring(0, parseString.indexOf('.') + 1); + if (sheetRef.indexOf(':')!=-1) { + sheetRef = parseString.substring(0, parseString.indexOf(':')); + short sheetNum1 = findSheetIndex(sheetRef); + sheetRef = parseString.substring(parseString.indexOf(':') + 1, parseString.length()); + short sheetNum2 = findSheetIndex(sheetRef); + tmpByteArray.add(new Byte((byte)sheetNum1)); + tmpByteArray.add(new Byte((byte)0x00)); + tmpByteArray.add(new Byte((byte)sheetNum2)); + tmpByteArray.add(new Byte((byte)0x00)); + } else { + sheetRef = parseString.substring(0, parseString.indexOf('.')); + short sheetNum = findSheetIndex(sheetRef); + tmpByteArray.add(new Byte((byte)sheetNum)); + tmpByteArray.add(new Byte((byte)0x00)); + tmpByteArray.add(new Byte((byte)sheetNum)); + tmpByteArray.add(new Byte((byte)0x00)); + } + String s = parseString.substring(parseString.indexOf('.') + 1, parseString.length()); + Debug.log(Debug.TRACE,"Parsing : " + s); + byte cellRefBytes[] = encodeCellCoordinates(s); + for(int i = 0;i < cellRefBytes.length;i++) { + tmpByteArray.add(new Byte(cellRefBytes[i])); + } + return tmpByteArray; + } + /** + * A 3D Area Reference Encoder. + * + * @param t <code>Token</code> to be encoded + * @return A <code>Vector</code> of pexcel <code>Byte</code> + */ + private Vector threeDAreaRefEncoder(Token t) { + + Vector tmpByteArray = new Vector(); + parseString = t.getValue(); + Debug.log(Debug.TRACE,"Encoding 3D Area reference " + t); + tmpByteArray.add(new Byte((byte)t.getTokenID())); + tmpByteArray.add(new Byte((byte)0xFF)); + tmpByteArray.add(new Byte((byte)0xFF)); + for(int i = 0;i < 8;i++) { + tmpByteArray.add(new Byte((byte)0x00)); + } + + String param1= parseString.substring(0, parseString.indexOf(':')); + String cellRef1 = param1.substring(parseString.indexOf('.') + 1, param1.length()); + String sheetRef1 = param1.substring(0, param1.indexOf('.')); + short sheetNum1 = findSheetIndex(sheetRef1); + + String param2 = parseString.substring(parseString.indexOf(':') + 1, parseString.length()); + Debug.log(Debug.TRACE,"param2: " + param2); + String cellRef2 = param2.substring(param2.indexOf('.') + 1, param2.length()); + Debug.log(Debug.TRACE,"cellRef2: " + cellRef2); + + if(param2.indexOf('.')==-1) { + tmpByteArray.add(new Byte((byte)sheetNum1)); + tmpByteArray.add(new Byte((byte)0x00)); + tmpByteArray.add(new Byte((byte)sheetNum1)); + tmpByteArray.add(new Byte((byte)0x00)); + } else { + String sheetRef2 = param2.substring(0, param2.indexOf('.')); + short sheetNum2 = findSheetIndex(sheetRef2); + tmpByteArray.add(new Byte((byte)sheetNum1)); + tmpByteArray.add(new Byte((byte)0x00)); + tmpByteArray.add(new Byte((byte)sheetNum2)); + tmpByteArray.add(new Byte((byte)0x00)); + } + + byte cellRefBytes1[] = encodeCellCoordinates(cellRef1); + byte cellRefBytes2[] = encodeCellCoordinates(cellRef2); + + tmpByteArray.add(new Byte(cellRefBytes1[0])); + tmpByteArray.add(new Byte(cellRefBytes1[1])); + + tmpByteArray.add(new Byte(cellRefBytes2[0])); + tmpByteArray.add(new Byte(cellRefBytes2[1])); + + tmpByteArray.add(new Byte(cellRefBytes1[2])); + tmpByteArray.add(new Byte(cellRefBytes2[2])); + + return tmpByteArray; + } + + /** + * A Cell Range Encoder. + * + * @param t <code>Token</code> to be encoded + * @return A <code>Vector</code> of pexcel <code>Byte</code> + */ + private Vector areaRefEncoder(Token t) { + + Vector tmpByteArray = new Vector(); + + tmpByteArray.add(new Byte((byte)t.getTokenID())); + String param = t.getValue(); + String cellRef1 = new String(); + String cellRef2 = new String(); + + if(param.indexOf(':')==-1) { + Debug.log(Debug.ERROR, "Invalid Cell Range, could not find :"); + } else { + cellRef1 = param.substring(0, param.indexOf(':')); + cellRef2 = param.substring(param.indexOf(':') + 1, param.length()); + } + byte cellRefBytes1[] = encodeCellCoordinates(cellRef1); + byte cellRefBytes2[] = encodeCellCoordinates(cellRef2); + + tmpByteArray.add(new Byte(cellRefBytes1[0])); + tmpByteArray.add(new Byte(cellRefBytes1[1])); + + tmpByteArray.add(new Byte(cellRefBytes2[0])); + tmpByteArray.add(new Byte(cellRefBytes2[1])); + + tmpByteArray.add(new Byte(cellRefBytes1[2])); + tmpByteArray.add(new Byte(cellRefBytes2[2])); + + return tmpByteArray; + } + + /** + * A Function Encoder. + * + * @param t <code>Token</code> to be encoded + * @return A <code>Vector</code> of pexcel <code>Byte</code> + */ + private Vector functionEncoder(Token t) { + Vector tmpByteArray = new Vector(); + + int id = t.getTokenID(); + if(t.getTokenType()==ParseToken.TOKEN_FUNCTION_VARIABLE) { + tmpByteArray.add(new Byte((byte)TokenConstants.TFUNCVAR)); + tmpByteArray.add(new Byte((byte)t.getNumArgs())); + } else { + tmpByteArray.add(new Byte((byte)TokenConstants.TFUNC)); + } + + tmpByteArray.add(new Byte((byte)id)); + tmpByteArray.add(new Byte((byte)(id>>8))); + return tmpByteArray; + } + + +} diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/formula/TokenFactory.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/formula/TokenFactory.java new file mode 100644 index 000000000000..427b56383dfc --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/formula/TokenFactory.java @@ -0,0 +1,126 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: TokenFactory.java,v $ + * $Revision: 1.6 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + + +package org.openoffice.xmerge.converter.xml.sxc.pexcel.records.formula; + +import java.io.*; +import java.util.Vector; +import java.util.Enumeration; + +import org.openoffice.xmerge.util.Debug; + +/** + * This is the Factory class responsible for creating a <code>Token</code>. + * It has three methods for returning three different types of Tokens + * (Operator, Operand and Function). + * This utility class is used by either the <code>FormulaParser</code> or the + * <code>FormulaDecoder</code>. + */ +public class TokenFactory { + + private OperatorLookup operatorLookup; + private OperandLookup operandLookup; + private FunctionLookup fl; + + /** + * Default Constructor + */ + public TokenFactory() { + operatorLookup = new OperatorLookup(); + operandLookup = new OperandLookup(); + fl = new FunctionLookup(); + } + + /** + * The Factory method for creating function Tokens + * + * @return The created <code>Token</code> + */ + public Token getFunctionToken(String s, int args) { + Token t = null; + // We will have to fix this later to include fixed function tokens + // Also will need to handle errors where functions names are incorrect??? + Debug.log(Debug.TRACE,"TokenFactory creating function Token : " + s); + try { + t = new Token(s, ParseToken.TOKEN_FUNCTION_VARIABLE, fl.getIDFromString(s), args); + } catch (UnsupportedFunctionException eFn) { + + Debug.log(Debug.ERROR, eFn.getMessage()); + } + return t; + } + + /** + * The Factory method for creating operator Tokens + * + * @return The created <code>Token</code> + */ + public Token getOperatorToken(String s, int args) { + + Token t = null; + + Debug.log(Debug.TRACE,"TokenFactory creating operator Token : " + s); + try { + if(args==1) { + if(s.equals("+")) { + t = new Token(s, ParseToken.TOKEN_OPERATOR, operatorLookup.getIDFromString("UNARY_PLUS"), args); + } else if (s.equals("-")) { + t = new Token(s, ParseToken.TOKEN_OPERATOR, operatorLookup.getIDFromString("UNARY_MINUS"), args); + } else { + t = new Token(s, ParseToken.TOKEN_OPERATOR, operatorLookup.getIDFromString(s), args); + } + } else { + t = new Token(s, ParseToken.TOKEN_OPERATOR, operatorLookup.getIDFromString(s), args); + } + } catch (UnsupportedFunctionException eFn) { + Debug.log(Debug.ERROR, eFn.getMessage()); + } + return t; + } + + /** + * The Factory method for creating Operand Tokens + * + * @return The created <code>Token</code> + */ + public Token getOperandToken(String s, String type) { + Token t = null; + + Debug.log(Debug.TRACE,"TokenFactory creating operand (" + type + ") Token : " + s); + try { + t = new Token(s, ParseToken.TOKEN_OPERAND, operandLookup.getIDFromString(type), 0); + } catch (UnsupportedFunctionException eFn) { + Debug.log(Debug.ERROR, eFn.getMessage()); + } + + return t; + } +} diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/formula/UnsupportedFunctionException.java b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/formula/UnsupportedFunctionException.java new file mode 100644 index 000000000000..6eeae73cb0b8 --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/formula/UnsupportedFunctionException.java @@ -0,0 +1,44 @@ +/************************************************************************ + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: UnsupportedFunctionException.java,v $ + * $Revision: 1.3 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + + +package org.openoffice.xmerge.converter.xml.sxc.pexcel.records.formula; + +/* + * Exception thrown when a function specified in a calc formula has no equivalent in Pocket Excel + * + * @author : Mike Hayes + */ + +public class UnsupportedFunctionException extends Exception { + UnsupportedFunctionException(String message) { + super(message); + } +} diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/formula/build.xml b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/formula/build.xml new file mode 100644 index 000000000000..05865f52c740 --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/formula/build.xml @@ -0,0 +1,145 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + + DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + + Copyright 2008 by Sun Microsystems, Inc. + + OpenOffice.org - a multi-platform office productivity suite + + $RCSfile: build.xml,v $ + + $Revision: 1.3 $ + + This file is part of OpenOffice.org. + + OpenOffice.org is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + OpenOffice.org 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 version 3 for more details + (a copy is included in the LICENSE file that accompanied this code). + + You should have received a copy of the GNU Lesser General Public License + version 3 along with OpenOffice.org. If not, see + <http://www.openoffice.org/license.html> + for a copy of the LGPLv3 License. + +--> +<project name="xmrg_jooxcxspr_formula" default="main" basedir="."> + + <!-- ================================================================= --> + <!-- settings --> + <!-- ================================================================= --> + + <!-- project prefix, used for targets and build.lst --> + <property name="prj.prefix" value="xmrg"/> + + <!-- name of this sub target used in recursive builds --> + <property name="target" value="xmrg_jooxcxspr_formula"/> + + <!-- relative path to project directory --> + <property name="prj" value="../../../../../../../../../.."/> + + <!-- start of java source code package structure --> + <property name="java.dir" value="${prj}/java"/> + + <!-- path component for current java package --> + <property name="package" + value="org/openoffice/xmerge/converter/xml/sxc/pexcel/records/formula"/> + + <!-- define how to handle CLASSPATH environment --> + <property name="build.sysclasspath" value="ignore"/> + + <!-- classpath settings for javac tasks --> + <path id="classpath"> + <pathelement location="${build.class}"/> + <pathelement location="${solar.jar}/parser.jar"/> + <pathelement location="${solar.jar}/jaxp.jar"/> + </path> + + <!-- set wether we want to compile with or without deprecation --> + <property name="deprecation" value="on"/> + + <!-- ================================================================= --> + <!-- solar build environment targets --> + <!-- ================================================================= --> + + <target name="build_dir" unless="build.dir"> + <property name="build.dir" value="${out}"/> + </target> + + <target name="solar" depends="build_dir" if="solar.update"> + <property name="solar.properties" + value="${solar.bin}/solar.properties"/> + </target> + + <target name="init" depends="solar"> + <property name="build.compiler" value="classic"/> + <property file="${solar.properties}"/> + <property file="${build.dir}/class/solar.properties"/> + </target> + + <target name="info"> + <echo message="--------------------"/> + <echo message="${target}"/> + <echo message="--------------------"/> + </target> + + + <!-- ================================================================= --> + <!-- custom targets --> + <!-- ================================================================= --> + + <!-- the main target, called in recursive builds --> + <target name="main" depends="info,prepare,compile"/> + + <!-- prepare output directories --> + <target name="prepare" depends="init" if="build.class"> + <mkdir dir="${build.dir}"/> + <mkdir dir="${build.class}"/> + </target> + + <!-- compile java sources in ${package} --> + <target name="compile" depends="prepare" if="build.class"> + <javac srcdir="${java.dir}" + destdir="${build.class}" + debug="${debug}" + deprecation="${deprecation}" + optimize="${optimize}"> + <classpath refid="classpath"/> + <include name="${package}/FormulaCompiler.java"/> + <include name="${package}/FormulaHelper.java"/> + <include name="${package}/FormulaParser.java"/> + <include name="${package}/FormulaParsingException.java"/> + <include name="${package}/FunctionLookup.java"/> + <include name="${package}/OperandLookup.java"/> + <include name="${package}/Operatorookup.java"/> + <include name="${package}/ParseToken.java"/> + <include name="${package}/PrecedenceTable.java"/> + <include name="${package}/SymbolLookup.java"/> + <include name="${package}/Token.java"/> + <include name="${package}/TokenConstants.java"/> + <include name="${package}/TokenDecoder.java"/> + <include name="${package}/TokenEncoder.java"/> + <include name="${package}/TokenFactory.java"/> + <include name="${package}/UnsupportedFunctionException.java"/> + </javac> + </target> + + <!-- clean up --> + <target name="clean" depends="prepare"> + <delete includeEmptyDirs="true"> + <fileset dir="${build.class}"> + <patternset> + <include name="${package}/*.class"/> + </patternset> + </fileset> + </delete> + </target> + +</project> + diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/formula/makefile.mk b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/formula/makefile.mk new file mode 100644 index 000000000000..47db7a258006 --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/formula/makefile.mk @@ -0,0 +1,36 @@ +#*************************************************************************** +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2008 by Sun Microsystems, Inc. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# $RCSfile: makefile.mk,v $ +# +# $Revision: 1.3 $ +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org 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 version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#*************************************************************************** + +TARGET=xmrg_jooxcxs_pexcel +PRJ=../../../../../../../../.. + +.INCLUDE : ant.mk +ALLTAR: ANTBUILD diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/formula/package.html b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/formula/package.html new file mode 100644 index 000000000000..7e719f53bebe --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/formula/package.html @@ -0,0 +1,44 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<html> +<head> +<!-- + + DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + + Copyright 2008 by Sun Microsystems, Inc. + + OpenOffice.org - a multi-platform office productivity suite + + $RCSfile: package.html,v $ + + $Revision: 1.3 $ + + This file is part of OpenOffice.org. + + OpenOffice.org is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + OpenOffice.org 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 version 3 for more details + (a copy is included in the LICENSE file that accompanied this code). + + You should have received a copy of the GNU Lesser General Public License + version 3 along with OpenOffice.org. If not, see + <http://www.openoffice.org/license.html> + for a copy of the LGPLv3 License. + +--> + + <title>org.openoffice.xmerge.converter.xml.sxc.pexcel.records.formula package</title> + +</head> + <body bgcolor="white"> + +<p> This package contains the classes necessary for converting pexcel formula +to and from StarCalc Formula.</p> + +</body> +</html> diff --git a/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/package.html b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/package.html new file mode 100644 index 000000000000..79188bf45310 --- /dev/null +++ b/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/package.html @@ -0,0 +1,49 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<html> +<head> +<!-- + + DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + + Copyright 2008 by Sun Microsystems, Inc. + + OpenOffice.org - a multi-platform office productivity suite + + $RCSfile: package.html,v $ + + $Revision: 1.4 $ + + This file is part of OpenOffice.org. + + OpenOffice.org is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + OpenOffice.org 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 version 3 for more details + (a copy is included in the LICENSE file that accompanied this code). + + You should have received a copy of the GNU Lesser General Public License + version 3 along with OpenOffice.org. If not, see + <http://www.openoffice.org/license.html> + for a copy of the LGPLv3 License. + +--> + + <title>org.openoffice.xmerge.converter.xml.sxc.pexcel.records package</title> + +</head> + <body bgcolor="white"> + +<p> This package contains the objects that represent BIFF Records for the +pocket excel format. Each one implements the BIFF Record abstract class which +contains three basic functions (read, write and getBiffType). BIFF Records +which are not used do not contain setter's or getter's for their member +variables as some records have a large number of variables. It should be up +to the implementer which attributes get set and which get set to default +values.</p> + +</body> +</html> |