diff options
Diffstat (limited to 'xmerge/source/xmerge/java/org/openoffice/xmerge/converter')
50 files changed, 13588 insertions, 0 deletions
diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/dom/DOMDocument.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/dom/DOMDocument.java new file mode 100644 index 000000000000..83c2270e5e61 --- /dev/null +++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/dom/DOMDocument.java @@ -0,0 +1,415 @@ +/************************************************************************* + * + * 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: DOMDocument.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.dom; + +import java.io.InputStream; +import java.io.OutputStream; +import java.io.StringWriter; +import java.io.ByteArrayOutputStream; +import java.io.IOException; + + +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.ParserConfigurationException; + +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.Transformer; +import javax.xml.transform.stream.StreamResult; +import javax.xml.transform.dom.DOMSource; + +import org.w3c.dom.Node; +import org.w3c.dom.Element; +import org.w3c.dom.Document; +import org.xml.sax.SAXException; + +import org.openoffice.xmerge.util.Resources; +import org.openoffice.xmerge.util.Debug; + +/** + * An implementation of <code>Document</code> for + * StarOffice documents. + */ +public class DOMDocument + implements org.openoffice.xmerge.Document { + + /** Factory for <code>DocumentBuilder</code> objects. */ + private static DocumentBuilderFactory factory = + DocumentBuilderFactory.newInstance(); + + /** DOM <code>Document</code> of content.xml. */ + private Document contentDoc = null; + + private String documentName = null; + private String fileName = null; + private String fileExt = null; + + /** Resources object. */ + private Resources res = null; + + + /** + * Default constructor. + * + * @param name <code>Document</code> name. + * @param ext <code>Document</code> extension. + */ + public DOMDocument(String name,String ext) + { + this(name,ext,true, false); + } + + /** + * Returns the file extension of the <code>Document</code> + * represented. + * + * @return file extension of the <code>Document</code>. + */ + protected String getFileExtension() { + return fileExt; + } + + + /** + * Constructor with arguments to set <code>namespaceAware</code> + * and <code>validating</code> flags. + * + * @param name <code>Document</code> name (may or may not + * contain extension). + * @param ext <code>Document</code> extension. + * @param namespaceAware Value for <code>namespaceAware</code> flag. + * @param validating Value for <code>validating</code> flag. + */ + public DOMDocument(String name, String ext,boolean namespaceAware, boolean validating) { + + res = Resources.getInstance(); + factory.setValidating(validating); + factory.setNamespaceAware(namespaceAware); + this.fileExt = ext; + this.documentName = trimDocumentName(name); + this.fileName = documentName + getFileExtension(); + } + + + /** + * Removes the file extension from the <code>Document</code> + * name. + * + * @param name Full <code>Document</code> name with extension. + * + * @return Name of <code>Document</code> without the extension. + */ + private String trimDocumentName(String name) { + String temp = name.toLowerCase(); + String ext = getFileExtension(); + + if (temp.endsWith(ext)) { + // strip the extension + int nlen = name.length(); + int endIndex = nlen - ext.length(); + name = name.substring(0,endIndex); + } + + return name; + } + + + /** + * Return a DOM <code>Document</code> object of the document content + * file. Note that a content DOM is not created when the constructor + * is called. So, either the <code>read</code> method or the + * <code>initContentDOM</code> method will need to be called ahead + * on this object before calling this method. + * + * @return DOM <code>Document</code> object. + */ + public Document getContentDOM() { + + return contentDoc; + } + + /** + * Sets the Content of the <code>Document</code> to the contents of the + * supplied <code>Node</code> list. + * + * @return DOM <code>Document</code> object. + */ + public void setContentDOM( Node newDom) { + contentDoc=(Document)newDom; + } + + + /** + * Return the name of the <code>Document</code>. + * + * @return The name of <code>Document</code>. + */ + public String getName() { + + return documentName; + } + + + /** + * Return the file name of the <code>Document</code>, possibly + * with the standard extension. + * + * @return The file name of <code>Document</code>. + */ + public String getFileName() { + + return fileName; + } + + + /** + * Read the Office <code>Document</code> from the specified + * <code>InputStream</code>. + * + * @param is Office document <code>InputStream</code>. + * + * @throws IOException If any I/O error occurs. + */ + public void read(InputStream is) throws IOException { + Debug.log(Debug.INFO, "reading file"); + DocumentBuilder builder = null; + try { + builder = factory.newDocumentBuilder(); + } catch (ParserConfigurationException ex) { + System.out.println("Error:"+ ex); + //throw new OfficeDocumentException(ex); + } + try { + + contentDoc= builder.parse(is); + + + } catch (SAXException ex) { + System.out.println("Error:"+ ex); + //throw new OfficeDocumentException(ex); + } + } + + + /** + * Write out content to the supplied <code>OutputStream</code>. + * + * @param os XML <code>OutputStream</code>. + * + * @throws IOException If any I/O error occurs. + */ + public void write(OutputStream os) throws IOException { + + // set bytes for writing to output stream + byte contentBytes[] = docToBytes(contentDoc); + + os.write(contentBytes); + } + + + /** + * <p>Write out a <code>org.w3c.dom.Document</code> object into a + * <code>byte</code> array.</p> + * + * <p>TODO: remove dependency on com.sun.xml.tree.XmlDocument + * package!</p> + * + * @param Document DOM <code>Document</code> object. + * + * @return <code>byte</code> array of DOM <code>Document</code> + * object. + * + * @throws IOException If any I/O error occurs. + */ + private byte[] docToBytes(Document doc) + throws IOException { + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + java.lang.reflect.Constructor con; + java.lang.reflect.Method meth; + + String domImpl = doc.getClass().getName(); + + System.err.println("type b " + domImpl); + + /* + * We may have multiple XML parsers in the Classpath. + * Depending on which one is first, the actual type of + * doc may vary. Need a way to find out which API is being + * used and use an appropriate serialization method. + */ + try { + // First of all try for JAXP 1.0 + if (domImpl.equals("com.sun.xml.tree.XmlDocument")) { + System.out.println("Using JAXP"); + Class jaxpDoc = Class.forName("com.sun.xml.tree.XmlDocument"); + + // The method is in the XMLDocument class itself, not a helper + meth = jaxpDoc.getMethod("write", + new Class[] { Class.forName("java.io.OutputStream") } ); + + meth.invoke(doc, new Object [] { baos } ); + } + else if (domImpl.equals("org.apache.crimson.tree.XmlDocument")) + { + System.out.println("Using Crimson"); + Class crimsonDoc = Class.forName("org.apache.crimson.tree.XmlDocument"); + // The method is in the XMLDocument class itself, not a helper + meth = crimsonDoc.getMethod("write", + new Class[] { Class.forName("java.io.OutputStream") } ); + + meth.invoke(doc, new Object [] { baos } ); + } + else if (domImpl.equals("org.apache.xerces.dom.DocumentImpl") + || domImpl.equals("org.apache.xerces.dom.DeferredDocumentImpl")) { + System.out.println("Using Xerces"); + // Try for Xerces + Class xercesSer = + Class.forName("org.apache.xml.serialize.XMLSerializer"); + + // Get the OutputStream constructor + // May want to use the OutputFormat parameter at some stage too + con = xercesSer.getConstructor(new Class [] + { Class.forName("java.io.OutputStream"), + Class.forName("org.apache.xml.serialize.OutputFormat") } ); + + + // Get the serialize method + meth = xercesSer.getMethod("serialize", + new Class [] { Class.forName("org.w3c.dom.Document") } ); + + + // Get an instance + Object serializer = con.newInstance(new Object [] { baos, null } ); + + + // Now call serialize to write the document + meth.invoke(serializer, new Object [] { doc } ); + } + else if (domImpl.equals("gnu.xml.dom.DomDocument")) { + System.out.println("Using GNU"); + + Class gnuSer = Class.forName("gnu.xml.dom.ls.DomLSSerializer"); + + // Get the serialize method + meth = gnuSer.getMethod("serialize", + new Class [] { Class.forName("org.w3c.dom.Node"), + Class.forName("java.io.OutputStream") } ); + + // Get an instance + Object serializer = gnuSer.newInstance(); + + // Now call serialize to write the document + meth.invoke(serializer, new Object [] { doc, baos } ); + } + else { + // We dont have another parser + try { + DOMSource domSource = new DOMSource(doc); + StringWriter writer = new StringWriter(); + StreamResult result = new StreamResult(writer); + TransformerFactory tf = TransformerFactory.newInstance(); + Transformer transformer = tf.newTransformer(); + transformer.transform(domSource, result); + return writer.toString().getBytes(); + } + catch (Exception e) { + // We don't have another parser + throw new IOException("No appropriate API (JAXP/Xerces) to serialize XML document: " + domImpl); + } + } + } + catch (ClassNotFoundException cnfe) { + throw new IOException(cnfe.toString()); + } + catch (Exception e) { + // We may get some other errors, but the bottom line is that + // the steps being executed no longer work + throw new IOException(e.toString()); + } + + byte bytes[] = baos.toByteArray(); + + return bytes; + } + + + /** + * Initializes a new DOM <code>Document</code> with the content + * containing minimum XML tags. + * + * @throws IOException If any I/O error occurs. + */ + public final void initContentDOM() throws IOException { + contentDoc = createDOM(""); + + } + + /** + * <p>Creates a new DOM <code>Document</code> containing minimum + * OpenOffice XML tags.</p> + * + * <p>This method uses the subclass + * <code>getOfficeClassAttribute</code> method to get the + * attribute for <i>office:class</i>.</p> + * + * @param rootName root name of <code>Document</code>. + * + * @throws IOException If any I/O error occurs. + */ + private final Document createDOM(String rootName) throws IOException { + + Document doc = null; + + try { + + DocumentBuilder builder = factory.newDocumentBuilder(); + doc = builder.newDocument(); + + } catch (ParserConfigurationException ex) { + System.out.println("Error:"+ ex); + + + } + + Element root = (Element) doc.createElement(rootName); + doc.appendChild(root); + + + return doc; + } + +} + + + + diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/dom/package.html b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/dom/package.html new file mode 100644 index 000000000000..31b21e19cc5c --- /dev/null +++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/dom/package.html @@ -0,0 +1,59 @@ +<!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.palm package</title> + +</head> + <body bgcolor="white"> +<p>Provides classes for converting basic document types to/from a <code> +DOMDocument</code> object, which can be used by the framework. </p> +<p>This package provides classes that handle the writing of data to an <code> + OutputStream</code> object for the {@link org.openoffice.xmerge.DocumentSerializer +DocumentSerializer} interface for; as well as the reading of data from an +<code>InputStream</code> object for the framework's {@link org.openoffice.xmerge.DocumentDeserializer +DocumentDeserializer} interface. Both these framework interfaces are simply +converters from server-side documents to device specific documents and vice-versa. + </p> +<a name="streamformat"> +<h2></h2> +</a> +<p></p> +<h2>Important Note</h2> +<p>Methods in these classes are not thread safe for performance reasons. +Users of these classes will have to make sure that the usage of these classes +are done in a proper manner. Possibly more on this later.</p> +</body> +</html> diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/palm/PalmDB.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/palm/PalmDB.java new file mode 100644 index 000000000000..00f0f91a1691 --- /dev/null +++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/palm/PalmDB.java @@ -0,0 +1,472 @@ +/************************************************************************* + * + * 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: PalmDB.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.palm; + +import java.io.OutputStream; +import java.io.InputStream; +import java.io.DataOutputStream; +import java.io.DataInputStream; +import java.io.IOException; +import java.io.UnsupportedEncodingException; + +/** + * <p>This class contains data for a single Palm database for use during + * a conversion process.</p> + * + * <p>It contains zero or more <code>Record</code> objects stored in an + * array. The index of the <code>Record</code> object in the array is + * the <code>Record</code> id or number for that specific <code>Record</code> object. + * Note that this class does not check for maximum number of Records + * allowable in an actual PDB.</p> + * + * <p>This class also contains the PDB name associated with the Palm + * database it represents. A PDB name consists of 32 bytes of a + * certain encoding (extended ASCII in this case).</p> + * + * <p>The non default constructors take in a name parameter which may not + * be the exact PDB name to be used. The name parameter in + * <code>String</code> or <code>byte</code> array are converted to an exact + * <code>NAME_LENGTH</code> byte array. If the length of the name is less + * than <code>NAME_LENGTH</code>, it is padded with '\0' characters. If it + * is more, it gets truncated. The last character in the resulting byte + * array is always a '\0' character. The resulting byte array is stored in + * <code>bName</code>, and a corresponding String object <code>sName</code> + * that contains characters without the '\0' characters.</p> + * + * <p>The {@link #write write} method is called within the + * {@link org.openoffice.xmerge.converter.palm.PalmDocument#write + * PalmDocument.write} method for writing out its data to the <code>OutputStream</code> + * object.</p> + * + * <p>The {@link #read read} method is called within the + * {@link org.openoffice.xmerge.converter.palm.PalmDocument#read + * PalmDocument.read} method for reading in its data from the <code>InputStream</code> + * object.</p> + * + * @author Akhil Arora, Herbie Ong + * @see PalmDocument + * @see Record + */ + +public final class PalmDB { + + /* Backup attribute for a PDB. This corresponds to dmHdrAttrBackup. */ + public final static short PDB_HEADER_ATTR_BACKUP = 0x0008; + + /** Number of bytes for the name field in the PDB. */ + public final static int NAME_LENGTH = 32; + + /** List of <code>Record</code> objects. */ + private Record[] records; + + /** PDB name in bytes. */ + private byte[] bName = null; + + /** PDB name in String. */ + private String sName = null; + + /** Creator ID. */ + private int creatorID = 0; + + /** Type ID */ + private int typeID = 0; + + /** + * PDB version. Palm UInt16. + * It is treated as a number here, since there is no unsigned 16 bit + * in Java, int is used instead, but only 2 bytes are written out or + * read in. + */ + private int version = 0; + + /** + * PDB attribute - flags for the database. + * Palm UInt16. Unsignedness should be irrelevant. + */ + private short attribute = 0; + + + /** + * Default constructor. + * + * @param creatorID The PDB Creator ID. + * @param typeID The PDB Type ID. + * @param version The PDB header version. + * @param attribute The PDB header attribute. + */ + public PalmDB(int creatorID, int typeID, int version, short attribute) { + + records = new Record[0]; + setAttributes(creatorID, typeID, version, attribute); + } + + + /** + * Constructor to create <code>PalmDB</code> object with + * <code>Record</code> objects. <code>recs.length</code> + * can be zero for an empty PDB. + * + * @param name Suggested PDB name in a <code>String</code>. + * @param creatorID The PDB Creator ID. + * @param typeID The PDB Type ID. + * @param version The PDB header version. + * @param attribute The PDB header attribute. + * @param recs Array of <code>Record</code> objects. + * + * @throws UnsupportedEncodingException If <code>name</code> is + * not properly encoded. + * @throws NullPointerException If <code>recs</code> is null. + */ + public PalmDB(String name, int creatorID, int typeID, int version, + short attribute, Record[] recs) + throws UnsupportedEncodingException { + + this(name.getBytes(PdbUtil.ENCODING), creatorID, typeID, version, + attribute, recs); + } + + + /** + * Constructor to create object with <code>Record</code> + * objects. <code>recs.length</code> can be zero for an + * empty PDB. + * + * @param name Suggested PDB name in a <code>byte</code> + * array. + * @param creatorID The PDB Creator ID. + * @param typeID The PDB Type ID. + * @param version The PDB header version. + * @param attribute The PDB header attribute. + * @param recs Array of <code>Record</code> objects. + * + * @throws UnsupportedEncodingException If <code>name</code> is + * not properly encoded. + * @throws NullPointerException If recs is null. + */ + public PalmDB(byte[] name, int creatorID, int typeID, int version, + short attribute, Record[] recs) throws UnsupportedEncodingException { + + store(name); + + records = new Record[recs.length]; + System.arraycopy(recs, 0, records, 0, recs.length); + setAttributes(creatorID, typeID, version, attribute); + } + + + /** + * Set the attributes for the <code>PalmDB</code> object. + * + * @param creatorID The PDB Creator ID. + * @param typeID The PDB Type ID. + * @param version The PDB header version. + * @param attribute The PDB header attribute. + */ + public void setAttributes (int creatorID, int typeID, int version, short attribute) { + this.creatorID = creatorID; + this.typeID = typeID; + this.version = version; + this.attribute = attribute; + } + + + /** + * This private method is mainly used by the constructors above. + * to store bytes into name and also create a <code>String</code> + * representation. and also by the <code>read</code> method. + * + * TODO: Note that this method assumes that the <code>byte</code> + * array parameter contains one character per <code>byte</code>, + * else it would truncate improperly. + * + * @param bytes PDB name in <code>byte</code> array. + * + * @throws UnsupportedEncodingException If ENCODING is + * not supported. + */ + private void store(byte[] bytes) throws UnsupportedEncodingException { + + // note that this will initialize all bytes in name to 0. + bName = new byte[NAME_LENGTH]; + + // determine minimum length to copy over from bytes to bName. + // Note that the last byte in bName has to be '\0'. + + int lastIndex = NAME_LENGTH - 1; + + int len = (bytes.length < lastIndex)? bytes.length: lastIndex; + + int i; + + for (i = 0; i < len; i++) { + + if (bytes[i] == 0) { + break; + } + + bName[i] = bytes[i]; + } + + // set sName, no need to include the '\0' character. + sName = new String(bName, 0, i, PdbUtil.ENCODING); + } + + + /** + * Returns creator ID. + * + * @return The creator ID. + */ + public int getCreatorID() { + + return creatorID; + } + + + /** + * Returns type ID. + * + * @return The type ID. + */ + public int getTypeID() { + + return typeID; + } + + + /** + * Returns attribute flag. + * + * @return The attribute flag. + */ + public short getAttribute() { + + return attribute; + } + + + /** + * Returns version. + * + * @return The version. + */ + public int getVersion() { + + return version; + } + + + /** + * Return the number of Records contained in this + * PDB <code>PalmDB</code> object. + * + * @return Number of <code>Record</code> objects. + */ + public int getRecordCount() { + + return records.length; + } + + + /** + * Return the specific <code>Record</code> object associated + * with the <code>Record</code> number. + * + * @param index <code>Record</code> index number. + * + * @return The <code>Record</code> object in the specified index + * + * @throws ArrayIndexOutOfBoundsException If index is out of bounds. + */ + public Record getRecord(int index) { + + return records[index]; + } + + + /** + * Return the list of <code>Record</code> objects. + * + * @return The array of <code>Record</code> objects. + */ + public Record[] getRecords() { + + return records; + } + + /** + * Return the PDB name associated with this object. + * + * @return The PDB name. + */ + public String getPDBNameString() { + + return sName; + } + + + /** + * Return the PDB name associated with this object in + * <code>byte</code> array of exact length of 32 bytes. + * + * @return The PDB name in <code>byte</code> array of + * length 32. + */ + public byte[] getPDBNameBytes() { + + return bName; + } + + + /** + * Write out the number of Records followed by what + * will be written out by each <code>Record</code> object. + * + * @param os The <code>OutputStream</code> to write the + * object. + * + * @throws IOException If any I/O error occurs. + */ + public void write(OutputStream os) throws IOException { + + DataOutputStream out = new DataOutputStream(os); + + // write out PDB name + out.write(bName); + + // write out 2 bytes for number of records + out.writeShort(records.length); + + // let each Record object write out its own info. + for (int i = 0; i < records.length; i++) + records[i].write(out); + } + + /** + * Read the necessary data to create a PDB from + * the <code>InputStream</code>. + * + * @param is The <code>InputStream</code> to read data + * in order to restore the object. + * + * @throws IOException If any I/O error occurs. + */ + public void read(InputStream is) throws IOException { + + DataInputStream in = new DataInputStream(is); + + // read in the PDB name. + byte[] bytes = new byte[NAME_LENGTH]; + in.readFully(bytes); + store(bytes); + + // read in number of records + int nrec = in.readUnsignedShort(); + records = new Record[nrec]; + + // read in the Record infos + for (int i = 0; i < nrec; i++) { + + records[i] = new Record(); + records[i].read(in); + } + } + + /** + * Override equals method of <code>Object</code>. + * + * Two <code>PalmDB</code> objects are equal if they contain + * the same information, i.e. PDB name and Records. + * + * This is used primarily for testing purposes only for now. + * + * @param obj A <code>PalmDB</code> <code>Object</code> to + * compare. + * + * @return true if <code>obj</code> is equal to this, otherwise + * false. + */ + public boolean equals(Object obj) { + + boolean bool = false; + + if (obj instanceof PalmDB) { + + PalmDB pdb = (PalmDB) obj; + + checkLabel: { + + // compare sName + + if (!sName.equals(pdb.sName)) { + + break checkLabel; + } + + // compare bName + + if (bName.length != pdb.bName.length) { + + break checkLabel; + } + + for (int i = 0; i < bName.length; i++) { + + if (bName[i] != pdb.bName[i]) { + + break checkLabel; + } + } + + // compare each Record + + if (records.length != pdb.records.length) { + + break checkLabel; + } + + for (int i = 0; i < records.length; i++) { + + if (!records[i].equals(pdb.records[i])) { + + break checkLabel; + } + } + + // all checks done + bool = true; + } + } + + return bool; + } +} + diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/palm/PalmDocument.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/palm/PalmDocument.java new file mode 100644 index 000000000000..f039dab382ee --- /dev/null +++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/palm/PalmDocument.java @@ -0,0 +1,180 @@ +/************************************************************************* + * + * 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: PalmDocument.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.palm; + +import java.io.IOException; +import java.io.InputStream; +import java.io.ByteArrayOutputStream; + +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; + +import org.openoffice.xmerge.Document; + +/** + * <p> A <code>PalmDocument</code> is palm implementaion of the + * <code>Docuemnt</code> interface.</p> + * + * <p>This implementation allows the Palm device format to be + * read via an <code>InputStream</code> and written via an + * <code>OutputStream</code>.</p> + * + * @author Martin Maher + */ + +public class PalmDocument + implements Document { + + /** + * The internal representation of a pdb. + */ + private PalmDB pdb; + + /** + * The file name. + */ + private String fileName; + + /** + * Constructor to create a <code>PalmDocument</code> + * from an <code>InputStream</code>. + * + * @param is <code>InputStream</code> containing a PDB. + * + * @throws IOException If any I/O error occurs. + */ + public PalmDocument(InputStream is) throws IOException { + read(is); + } + + + /** + * Constructor to create a <code>PalmDocument</code> with + * <code>Record</code> objects. <code>recs.length</code> + * can be zero for an empty PDB. + * + * @param name Suggested PDB name in <code>String</code>. + * @param creatorID The PDB Creator ID. + * @param typeID The PDB Type ID. + * @param version The PDB header version. + * @param attribute The PDB header attribute. + * @param recs Array of <code>Record</code> objects. + * + * @throws NullPointerException If <code>recs</code> is null. + */ + public PalmDocument(String name, int creatorID, int typeID, int version, + short attribute, Record[] recs) + throws UnsupportedEncodingException { + pdb = new PalmDB(name, creatorID, typeID, version, attribute, recs); + fileName = pdb.getPDBNameString(); + } + + + /** + * Reads in a file from the <code>InputStream</code>. + * + * @param is <code>InputStream</code> to read in its content. + * + * @throws IOException If any I/O error occurs. + */ + + public void read(InputStream is) throws IOException { + PdbDecoder decoder = new PdbDecoder(); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + byte[] buf = new byte[4096]; + int n = 0; + while ((n = is.read(buf)) > 0) { + baos.write(buf, 0, n); + } + byte[] bytearr = baos.toByteArray(); + pdb = decoder.parse(bytearr); + fileName = pdb.getPDBNameString(); + } + + + /** + * Writes the <code>PalmDocument</code> to an <code>OutputStream</code>. + * + * @param is The <code>OutputStream</code> to write the content. + * + * @throws IOException If any I/O error occurs. + */ + public void write(OutputStream os) throws IOException { + PdbEncoder encoder = new PdbEncoder(pdb); + encoder.write(os); + } + + + /** + * Returns the <code>PalmDB</code> contained in this object. + * + * @return The <code>PalmDB</code>. + */ + public PalmDB getPdb() { + return pdb; + } + + + /** + * Sets the <code>PalmDocument</code> to a new <code>PalmDB</code> + * value. + * + * @param pdb The new <code>PalmDB</code> value. + */ + public void setPdb(PalmDB pdb) { + this.pdb = pdb; + + String name = pdb.getPDBNameString(); + fileName = name; + } + + + /** + * Returns the name of the file. + * + * @return The name of the file represented in the + * <code>PalmDocument</code>. + */ + public String getFileName() { + return fileName + ".pdb"; + } + + + /** + * Returns the <code>Document</code> name. + * + * @return The <code>Document</code> name. + */ + public String getName() { + return fileName; + } +} + diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/palm/PdbDecoder.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/palm/PdbDecoder.java new file mode 100644 index 000000000000..f6df00664b27 --- /dev/null +++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/palm/PdbDecoder.java @@ -0,0 +1,236 @@ +/************************************************************************* + * + * 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: PdbDecoder.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.palm; + +import java.io.RandomAccessFile; +import java.io.IOException; +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; + +/** + * <p>Provides functionality to decode a PDB formatted file into + * a <code>PalmDB</code> object given an <code>InputStream</code>. + * This class is only used by the <code>PalmDB</code> object.</p> + * + * <p>Sample usage:</p> + * + * <blockquote><pre><code> + * PdbDecoder decoder = new PdbDecoder("sample.pdb"); + * PalmDB palmDB = decoder.parse(); + * </code></pre></blockquote> + * + * <p>This decoder has the following assumptions on the PDB file:</p> + * + * <p><ol> + * <li>There is only one RecordList section in the PDB.</li> + * <li>The <code>Record</code> indices in the RecordList are sorted in + * order, i.e. the first <code>Record</code> index refers to + * <code>Record</code> 0, and so forth.</li> + * <li>The raw <code>Record</code> in the <code>Record</code> section + * are sorted as well in order, i.e. first <code>Record</code> + * comes ahead of second <code>Record</code>, etc.</li> + * </ol></p> + * + * <p>Other decoders assume these as well.</p> + * + * @author Herbie Ong + * @see PalmDB + * @see Record + */ +public final class PdbDecoder { + + + /** + * <p>This method decodes a PDB file into a <code>PalmDB</code> + * object.</p> + * + * <p>First, the header data is read using the <code>PdbHeader</code> + * <code>read</code> method. Next, the RecordList section is + * read and the <code>Record</code> offsets are stored for use when + * parsing the Records. Based on these offsets, the bytes + * corresponding to each <code>Record</code> are read and each is + * stored in a <code>Record</code> object. Lastly, the data is + * used to create a <code>PalmDB</code> object.</p> + * + * @param fileName PDB file name. + * + * @throws IOException If I/O error occurs. + */ + public PalmDB parse(String fileName) throws IOException { + + RandomAccessFile file = new RandomAccessFile(fileName, "r"); + + // read the PDB header + PdbHeader header = new PdbHeader(); + header.read(file); + + Record recArray[] = new Record[header.numRecords]; + if (header.numRecords != 0) { + + // read in the record indices + offsets + + int recOffset[] = new int[header.numRecords]; + byte recAttrs[] = new byte[header.numRecords]; + + for (int i = 0; i < header.numRecords; i++) { + + recOffset[i] = file.readInt(); + + // read in attributes (1 byte) + unique id (3 bytes) + // take away the unique id, store the attributes + + int attr = file.readInt(); + recAttrs[i] = (byte) (attr >>> 24); + } + + + // read the records + + int len = 0; + byte[] bytes = null; + + int lastIndex = header.numRecords - 1; + + for (int i = 0; i < lastIndex; i++) { + + file.seek(recOffset[i]); + len = recOffset[i+1] - recOffset[i]; + bytes = new byte[len]; + file.readFully(bytes); + recArray[i] = new Record(bytes, recAttrs[i]); + } + + // last record + file.seek(recOffset[lastIndex]); + len = (int) file.length() - recOffset[lastIndex]; + bytes = new byte[len]; + file.readFully(bytes); + recArray[lastIndex] = new Record(bytes, recAttrs[lastIndex]); + + } + + file.close(); + + // create PalmDB and return it + PalmDB pdb = new PalmDB(header.pdbName, header.creatorID, + header.typeID, header.version, header.attribute, recArray); + + return pdb; + } + + /** + * <p>This method decodes a PDB file into a <code>PalmDB</code> + * object.</p> + * + * <p>First, the header data is read using the <code>PdbHeader</code> + * <code>read</code> method. Next, the RecordList section is + * read and the <code>Record</code> offsets are stored for use when + * parsing the Records. Based on these offsets, the bytes + * corresponding to each <code>Record</code> are read and each is + * stored in a <code>Record</code> object. Lastly, the data is + * used to create a <code>PalmDB</code> object.</p> + * + * @param b <code>byte[]</code> containing PDB. + * + * @throws IOException If I/O error occurs. + */ + + public PalmDB parse(byte[] b) throws IOException { + + ByteArrayInputStream bais = new ByteArrayInputStream(b); + DataInputStream dis = new DataInputStream(bais); + + // read the PDB header + + PdbHeader header = new PdbHeader(); + header.read(dis); + + Record recArray[] = new Record[header.numRecords]; + if (header.numRecords != 0) { + + // read in the record indices + offsets + + int recOffset[] = new int[header.numRecords]; + byte recAttrs[] = new byte[header.numRecords]; + + for (int i = 0; i < header.numRecords; i++) { + + recOffset[i] = dis.readInt(); + + // read in attributes (1 byte) + unique id (3 bytes) + // take away the unique id, store the attributes + + int attr = dis.readInt(); + recAttrs[i] = (byte) (attr >>> 24); + } + + // read the records + + int len = 0; + byte[] bytes = null; + + int lastIndex = header.numRecords - 1; + + for (int i = 0; i < lastIndex; i++) { + + //dis.seek(recOffset[i]); + dis.reset(); + dis.skip(recOffset[i]); + len = recOffset[i+1] - recOffset[i]; + bytes = new byte[len]; + dis.readFully(bytes); + recArray[i] = new Record(bytes, recAttrs[i]); + } + + // last record + + dis.reset(); + len = (int) dis.available() - recOffset[lastIndex]; + dis.skip(recOffset[lastIndex]); + bytes = new byte[len]; + dis.readFully(bytes); + recArray[lastIndex] = new Record(bytes, recAttrs[lastIndex]); + } + + + + // create PalmDB and return it + + PalmDB pdb = new PalmDB(header.pdbName, header.creatorID, + header.typeID, header.version, header.attribute, recArray); + + return pdb; + } + + + +} + diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/palm/PdbEncoder.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/palm/PdbEncoder.java new file mode 100644 index 000000000000..2081290ccf47 --- /dev/null +++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/palm/PdbEncoder.java @@ -0,0 +1,199 @@ +/************************************************************************* + * + * 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: PdbEncoder.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.palm; + +import java.io.OutputStream; +import java.io.BufferedOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.Date; + +/** + * <p>Provides functionality to encode a <code>PalmDB</code> object + * into a PDB formatted file given a file <code>OutputStream</code>. + * This class is only used by the <code>PalmDB</code> object.</p> + * + * <p>One needs to create one <code>PdbEncoder</code> object per + * <code>PalmDB</code> object to be encoded. This class keeps + * the PDB header data and functionality in the <code>PdbHeader</code> + * class.</p> + * + * <p>Sample usage:</p> + * + * <blockquote><pre><code> + * PdbEncoder encoder = new PdbEncoder(palmDB, "STRW", "data"); + * encoder.write(new FileOutputStream("sample.pdb")); + * </code></pre></blockquote> + * + * @author Herbie Ong + * @see PalmDB + * @see Record + */ +public final class PdbEncoder { + + /** PDB header. */ + private PdbHeader header = null; + + /** the PalmDB object. */ + private PalmDB db = null; + + /** + * The pattern for unique_id=0x00BABE(start). + */ + private final static int START_UNIQUE_ID = 0x00BABE; + + + /** + * Constructor. + * + * @param db The <code>PalmDB</code> to be encoded. + */ + public PdbEncoder(PalmDB db) { + + header = new PdbHeader(); + header.version = db.getVersion(); + + header.attribute = db.getAttribute(); + + this.db = db; + + header.pdbName = db.getPDBNameBytes(); + header.creatorID = db.getCreatorID(); + header.typeID = db.getTypeID(); + + // set the following dates to current date + Date date = new Date(); + header.creationDate = (date.getTime() / 1000) + PdbUtil.TIME_DIFF; + header.modificationDate = header.creationDate; + + header.numRecords = db.getRecordCount(); + } + + + /** + * <p>Write out a PDB into the given <code>OutputStream</code>.</p> + * + * <p>First, write out the header data by using the + * <code>PdbHeader</code> <code>write</code> method. Next, + * calculate the RecordList section and write it out. + * Lastly, write out the bytes corresponding to each + * <code>Record</code>.</p> + * + * <p>The RecordList section contains a list of + * <code>Record</code> index info, where each <code>Record</code> + * index info contains:</p> + * + * <p><ul> + * <li>4 bytes local offset of the <code>Record</code> from the + * top of the PDB.</li> + * <li>1 byte of <code>Record</code> attribute.</li> + * <li>3 bytes unique <code>Record</code> ID.</li> + * </ul></p> + * + * <p>There should be a total of <code>header.numRecords</code> + * of <code>Record</code> index info</p>. + * + * @param os <code>OutputStream</code> to write out PDB. + * + * @throws IOException If I/O error occurs. + */ + public void write(OutputStream os) throws IOException { + + BufferedOutputStream bos = new BufferedOutputStream(os); + DataOutputStream dos = new DataOutputStream(bos); + + // write out the PDB header + header.write(dos); + + if (header.numRecords > 0) { + + // compute for recOffset[] + + int recOffset[] = new int[header.numRecords]; + byte recAttr[] = new byte[header.numRecords]; + + // first recOffset will be at PdbUtil.HEADER_SIZE + all the + // record indices (@ 8 bytes each) + recOffset[0] = PdbUtil.HEADER_SIZE + (header.numRecords * 8); + + int lastIndex = header.numRecords - 1; + + for (int i = 0; i < lastIndex; i++) { + + Record rec = db.getRecord(i); + int size = rec.getSize(); + recAttr[i] = rec.getAttributes(); + + recOffset[i+1] = recOffset[i] + size; + } + + // grab the last record's attribute. + + Record lastRec = db.getRecord(lastIndex); + recAttr[lastIndex] = lastRec.getAttributes(); + + + int uid = START_UNIQUE_ID; + + for (int i = 0; i < header.numRecords; i++) { + + // write out each record offset + dos.writeInt(recOffset[i]); + + // write out record attribute (recAttr) and + // unique ID (uid) in 4 bytes (int) chunk. + // unique ID's have to be unique, thus + // increment each time. + int attr = (((int) recAttr[i]) << 24 ); + attr |= uid; + dos.writeInt(attr); + uid++; + } + + // write out the raw records + + for (int i = 0; i < header.numRecords; i++) { + + Record rec = db.getRecord(i); + byte bytes[] = rec.getBytes(); + dos.write(bytes); + } + + } else { + + // placeholder bytes if there are no records in the list. + dos.writeShort(0); + } + + dos.flush(); + } +} + diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/palm/PdbHeader.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/palm/PdbHeader.java new file mode 100644 index 000000000000..c79acf6d20bb --- /dev/null +++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/palm/PdbHeader.java @@ -0,0 +1,165 @@ +/************************************************************************* + * + * 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: PdbHeader.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.palm; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; + +/** + * <p>Class used only internally by <code>PdbEncoder</code> and + * <code>PdbDecoder</code> to store, read and write a PDB header.</p> + * + * <p>Note that fields are intended to be accessible only at the + * package level.</p> + * + * <p>Some of the fields are internally represented using a + * larger type since Java does not have unsigned types. + * Some are not since they are not relevant for now. + * The <code>read</code> and <code>write</code> methods should + * handle them properly.</p> + * + * @author Herbie Ong + * @see PalmDB + * @see Record + */ +final class PdbHeader { + + + /** Name of the database. 32 bytes. */ + byte[] pdbName = null; + + /** + * Flags for the database. Palm UInt16. Unsignedness should be + * irrelevant. + */ + short attribute = 0; + + /** Application-specific version for the database. Palm UInt16. */ + int version = 0; + + /** Date created. Palm UInt32. */ + long creationDate = 0; + + /** Date last modified. Palm UInt32. */ + long modificationDate = 0; + + /** Date last backup. Palm UInt32. */ + long lastBackupDate = 0; + + /** + * Incremented every time a <code>Record</code> is + * added, deleted or modified. Palm UInt32. + */ + long modificationNumber = 0; + + /** Optional field. Palm UInt32. Unsignedness should be irrelevant. */ + int appInfoID = 0; + + /** Optional field. Palm UInt32. Unsignedness should be irrelevant. */ + int sortInfoID = 0; + + /** Database type ID. Palm UInt32. Unsignedness should be irrelevant. */ + int typeID = 0; + + /** Database creator ID. Palm UInt32. Unsignedness should be irrelevant. */ + int creatorID = 0; + + /** ??? */ + int uniqueIDSeed = 0; + + /** See numRecords. 4 bytes. */ + int nextRecordListID = 0; + + /** + * Number of Records stored in the database header. + * If all the <code>Record</code> entries cannot fit in the header, + * then <code>nextRecordList</code> has the local ID of a + * RecordList that contains the next set of <code>Record</code>. + * Palm UInt16. + */ + int numRecords = 0; + + + /** + * Read in the data for the PDB header. Need to + * preserve the unsigned value for some of the fields. + * + * @param di A <code>DataInput</code> object. + * + * @throws IOException If any I/O error occurs. + */ + public void read(DataInput in) throws IOException { + + pdbName = new byte[PalmDB.NAME_LENGTH]; + in.readFully(pdbName); + attribute = in.readShort(); + version = in.readUnsignedShort(); + creationDate = ((long) in.readInt()) & 0xffffffffL; + modificationDate = ((long) in.readInt()) & 0xffffffffL; + lastBackupDate = ((long) in.readInt()) & 0xffffffffL; + modificationNumber = ((long) in.readInt()) & 0xffffffffL; + appInfoID = in.readInt(); + sortInfoID = in.readInt(); + creatorID = in.readInt(); + typeID = in.readInt(); + uniqueIDSeed = in.readInt(); + nextRecordListID = in.readInt(); + numRecords = in.readUnsignedShort(); + } + + + /** + * Write out PDB header data. + * + * @param out A <code>DataOutput</code> object. + * + * @throws IOException If any I/O error occurs. + */ + public void write(DataOutput out) throws IOException { + + out.write(pdbName); + out.writeShort(attribute); + out.writeShort(version); + out.writeInt((int) creationDate); + out.writeInt((int) modificationDate); + out.writeInt((int) lastBackupDate); + out.writeInt((int) modificationNumber); + out.writeInt(appInfoID); + out.writeInt(sortInfoID); + out.writeInt(typeID); + out.writeInt(creatorID); + out.writeInt(uniqueIDSeed); + out.writeInt(nextRecordListID); + out.writeShort(numRecords); + } +} + diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/palm/PdbUtil.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/palm/PdbUtil.java new file mode 100644 index 000000000000..cb50b9d7cb76 --- /dev/null +++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/palm/PdbUtil.java @@ -0,0 +1,108 @@ +/************************************************************************* + * + * 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: PdbUtil.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.palm; + +/** + * Contains common static methods and constants for use within the package. + * + * @author Herbie Ong + */ +public final class PdbUtil { + + /** Difference in seconds from Jan 01, 1904 to Jan 01, 1970. */ + final static long TIME_DIFF = 2082844800; + + /** Encoding scheme used. */ + final static String ENCODING = "8859_1"; + + /** Size of a PDB header in bytes. */ + final static int HEADER_SIZE = 78; + + + /** + * <p>This method converts a 4 letter string into the Palm ID + * integer.</p> + * + * <p>It is normally used to convert the Palm creator ID string into + * the integer version of it. Also use for data types, etc.</p> + * + * @param s Four character <code>String</code>. + * + * @return Palm ID representing the <code>String</code>. + * + * @throws ArrayIndexOutOfBoundsException If <code>String</code> + * parameter contains less than four characters. + */ + public static int intID(String s) { + + int id = -1; + int temp = 0; + + // grab the first char and put it in the high bits + // note that we only want 8 lower bits of it. + temp = (int) s.charAt(0); + id = temp << 24; + + // grab the second char and add it in. + temp = ((int) s.charAt(1)) & 0x00ff; + id += temp << 16; + + // grab the second char and add it in. + temp = ((int) s.charAt(2)) & 0x00ff; + id += temp << 8; + + // grab the last char and add it in + id += ((int) s.charAt(3)) & 0x00ff; + + return id; + } + + + /** + * This method converts an integer into a <code>String</code> + * given the Palm ID format. + * + * @param i Palm ID. + * + * @return <code>String</code> representation. + */ + public static String stringID(int i) { + + char ch[] = new char[4]; + ch[0] = (char) (i >>> 24); + ch[1] = (char) ((i >> 16) & 0x00ff); + ch[2] = (char) ((i >> 8) & 0x00ff); + ch[3] = (char) (i & 0x00ff); + + return new String(ch); + } +} + diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/palm/Record.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/palm/Record.java new file mode 100644 index 000000000000..6475fdbc9537 --- /dev/null +++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/palm/Record.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: Record.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.palm; + +import java.io.OutputStream; +import java.io.InputStream; +import java.io.DataOutputStream; +import java.io.DataInputStream; +import java.io.IOException; + +/** + * <p>Contains the raw bytes for a <code>Record</code> in a PDB.</p> + * + * <p>Note that it is not associated with a <code>Record</code> number + * or ID.</p> + * + * @author Akhil Arora, Herbie Ong + * @see PalmDocument + * @see PalmDB + */ +public final class Record { + + /** <code>Record</code> <code>byte</code> array. */ + private byte[] data; + + /** <code>Record</code> attributes. */ + private byte attributes = 0; + + + /** + * Default constructor. + */ + public Record() { + + data = new byte[0]; + } + + + /** + * <p>Constructor to create a <code>Record</code> filled with + * bytes.</p> + * + * <p>Note that this does not check for 64k <code>Record</code> + * sizes. User of this class must check for that.</p> + * + * @param d <code>byte</code> array contents for this object. + */ + public Record(byte[] d) { + + this(d, (byte) 0); + } + + + /** + * <p>Constructor to create a <code>Record</code> filled with + * bytes and assign <code>Record</code> attributes.</p> + * + * <p>Note that this does not check for 64k <code>Record</code> + * sizes. User of this class must check for that.</p> + * + * @param d <code>byte</code> array contents for this object. + * @param attrs <code>Record</code> attributes. + */ + public Record(byte[] d, byte attrs) { + + data = new byte[d.length]; + attributes = attrs; + System.arraycopy(d, 0, data, 0, d.length); + } + + + /** + * This method returns the number of bytes in this object. + * + * @return Number of bytes in this object. + */ + public int getSize() { + + return data.length; + } + + + /** + * This method returns the contents of this <code>Object</code>. + * + * @return Contents in <code>byte</code> array + */ + public byte[] getBytes() { + + return data; + } + + + /** + * <p>This method returns the <code>Record</code> attributes.</p> + * + * <blockquote><pre> + * <code>Record</code> attributes consists of (from high to low bit) + * + * delete (1) - dirty (1) - busy (1) - secret (1) - category (4) + * </pre></blockquote> + * + * @return <code>Record</code> attribute. + */ + public byte getAttributes() { + + return attributes; + } + + + /** + * Write out the <code>Record</code> attributes and + * <code>Record</code> length followed by the data in this + * <code>Record</code> object. + * + * @param out The <code>OutputStream</code> to write the object. + * + * @throws IOException If any I/O error occurs. + */ + public void write(OutputStream outs) throws IOException { + + DataOutputStream out = new DataOutputStream(outs); + out.writeByte(attributes); + out.writeShort(data.length); + out.write(data); + } + + + /** + * Read the necessary data to create a PDB from + * the <code>InputStream</code>. + * + * @param in The <code>InputStream</code> to read data from + * in order to restore the <code>object</code>. + * + * @throws IOException If any I/O error occurs. + */ + public void read(InputStream ins) throws IOException { + + DataInputStream in = new DataInputStream(ins); + attributes = in.readByte(); + int len = in.readUnsignedShort(); + data = new byte[len]; + in.readFully(data); + } + + + /** + * <p>Override equals method of <code>Object</code>.</p> + * + * <p>Two <code>Record</code> objects are equal if they contain + * the same bytes in the array and the same attributes.</p> + * + * <p>This is used primarily for testing purposes only for now.</p> + * + * @param obj A <code>Record</code> object to compare with + * + * @return true if obj is equal, otherwise false. + */ + public boolean equals(Object obj) { + + boolean bool = false; + + if (obj instanceof Record) { + + Record rec = (Record) obj; + + checkLabel: { + + if (rec.getAttributes() != attributes) { + + break checkLabel; + } + + if (rec.getSize() == data.length) { + + for (int i = 0; i < data.length; i++) { + + if (data[i] != rec.data[i]) { + break checkLabel; + } + } + + bool = true; + } + } + } + return bool; + } +} + diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/palm/package.html b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/palm/package.html new file mode 100644 index 000000000000..8f5159b23d6d --- /dev/null +++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/palm/package.html @@ -0,0 +1,146 @@ +<!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.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. + + #************************************************************************* + --> +<html> +<head> +<title>org.openoffice.xmerge.converter.palm package</title> +</head> + +<body bgcolor="white"> + +<p>Provides classes for converting Palm database data to/from a +<code>PalmDocument</code> object, which can be used by the framework. + +<p>This package provides classes that handle the writing of data to +an <code>OutputStream</code> object for the +{@link org.openoffice.xmerge.DocumentSerializer DocumentSerializer} +interface for; as well as the reading of data from an <code>InputStream</code> +object for the framework's +{@link org.openoffice.xmerge.DocumentDeserializer DocumentDeserializer} +interface. Both these framework interfaces are simply converters from +server-side documents to device specific documents and vice-versa. +Since all Palm databases have a general record oriented format, a Palm +database converter specific I/O stream format is specified for the Palm +sync client application to handle the byte stream in a generic way. +This also means that Palm database converters should read and/or write +using this I/O stream format as specified in the next section.</p> + +<a name="streamformat"> +<h2>Palm database converter specific I/O stream format</h2> +</a> + +<p>Note that the format of the byte stream is not exactly that of a PDB +file encoding. It does not need to contain the PDB header information +nor record indices section. Instead, it contains the following ...</p> + +<pre> + set header + 4 bytes - creator id + 4 bytes - type id + 2 bytes - PDB header version + 2 bytes - PDB header attribute + unsigned 2 bytes - number of PDB data to follow + + for each PDB, + 32 bytes - name of PDB i + unsigned 2 bytes - number of records in PDB i + + for each record contained in PDB i, + 1 byte - record attributes + unsigned 2 bytes - size of record j in PDB i + x bytes - data +</pre> + +<p>Note that each PDB section is appended by another if there is more +than one.</p> + +<p>Since the <code>PalmDocument</code> class takes care of the writing +and reading of this format through its <code>write</code> and +<code>read</code> methods, respectively, this format shall also be +referred to as the <b>PalmDocument stream format</b>.</p> + +<h2>Usage of the classes for the specified I/O stream</h2> + +<p>When converting from a server document to device document(s), the +framework requires writing the device document(s) to an +<code>OutputStream</code> object via the <code>DocumentSerializer</code> +interface. Note that a single server document may be converted +into multiple PDB's on the Palm device. Each worksheet in the document +is converted into a <code>PalmDocument</code> . Thus, if there is more +than one worksheet in the document, more than one <code>PalmDocument</code> +will be produced by the <code>DocumentSerializer</code>.</p> + +<p>A <code>DocumentSerializer</code> creates a <code>ConvertData</code> object, +which contains all of the <code>PalmDocuments</code>. The +{@link org.openoffice.xmerge.converter.palm.PalmDocument#write write} +method to write to the given <code>OutputStream</code>. The <code>PalmDocument</code> +object will take care of writing the data in the +<a href=#streamformat>specified format</a>.</p> + +<p>A <code>DocumentDeserializer</code> can use the <code>PalmDocument</code> object's +{@link org.openoffice.xmerge.converter.palm.PalmDocument#read read} +method to fill in all the <code>PalmDocument</code> object's data.</p> + +<h2>PDB file encoding/decoding</h2> + +<p>The <code>PalmDocument</code> object's read and write functions are provided +by the <code>PdbDecoder</code> and <code>PdbEncoder</code> objects. The +<code>PdbEncoder</code> class provides the functionality of encoding a +<code>PalmDB</code> object into an <code>InputStream</code>, while the +<code>PdbDecoder</code> class provides the functionality of decoding a +PDB file into an <code>OutputStream</code>.</p> + +<p>Refer to the class description of each for usage.</p> + +<h2>Important Note</h2> + +<p>Methods in these classes are not thread safe for performance reasons. +Users of these classes will have to make sure that the usage of these classes +are done in a proper manner. Possibly more on this later.</p> + +<h2>TODO list</h2> + +<p><ol> +<li>Merge the PalmDB, PdbDecoder and PdbEncoder classes into the + PalmDocument class.</li> +<li>After reading more on the palm file format spec, I realized + that there are certain optional fields that may need to be addressed + still, like the appInfo block and sortInfo block.</li> +<li>The current PdbDecoder only returns a PalmDB object. There are other + information that we may want to expose from the PDB decoding process.</li> +<li>Investigate on different language encoding on the Palm and how that + affects the PDB name.</li> +</ol></p> + +</body> +</html> diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/EmbeddedBinaryObject.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/EmbeddedBinaryObject.java new file mode 100644 index 000000000000..30052140bdae --- /dev/null +++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/EmbeddedBinaryObject.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: EmbeddedBinaryObject.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; + +import org.w3c.dom.Document; +import org.w3c.dom.DOMException; +import org.w3c.dom.Element; + + +/** + * This class represents embedded object's in an OpenOffice.org document that + * have a binary representation. + */ +public class EmbeddedBinaryObject extends EmbeddedObject { + + /** The object's binary representation. */ + protected byte[] objData = null; + + /** + * Constructor for an embedded object stored using an XML representation. + * + * @param name The name of the object. + * @param type The mime-type of the object. See the class summary. + */ + public EmbeddedBinaryObject(String name, String type) { + super(name, type); + } + + + /** + * Package private constructor for use when reading an object from a + * compressed SX? file. + * + * @param name The name of the object. + * @param type The mime-type of the object. See the class summary. + * @param source The OfficeZip representation of the SX? file that stores + * the object. + */ + EmbeddedBinaryObject(String name, String type, OfficeZip source) { + super(name, type, source); + } + + + /** + * This method returns the data for this object. + * + * @return A <code>byte</code> array containing the object's data. + */ + public byte[] getBinaryData() { + + if (objData == null) { + // See if we came from a Zip file + if (zipFile != null) { + objData = zipFile.getNamedBytes(objName); + } + } + + return objData; + } + + + /** + * Sets the data for this object. + * + * @param data A <code>byte</code> array containing data for the object. + */ + public void setBinaryData(byte[] data) { + objData = data; + hasChanged = true; + } + + /** + * Package private method for writing the data of the EmbeddedObject to a + * SX? file. + * + * @param zip An <code>OfficeZip</code> instance representing the file + * the data is to be written to. + */ + void write(OfficeZip zip) { + if (hasChanged) { + zip.setNamedBytes(objName, objData); + } + } + + + /** + * Package private method that constructs the manifest.xml entries for this + * embedded object. + * + * @return Document <code>Document</code> containing the manifest entries. + */ + void writeManifestData(Document manifestDoc) throws DOMException { + Element objNode = manifestDoc.createElement(OfficeConstants.TAG_MANIFEST_FILE); + + objNode.setAttribute(OfficeConstants.ATTRIBUTE_MANIFEST_FILE_TYPE, objType); + objNode.setAttribute(OfficeConstants.ATTRIBUTE_MANIFEST_FILE_PATH, objName); + + manifestDoc.getDocumentElement().appendChild(objNode); + } + +} + diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/EmbeddedObject.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/EmbeddedObject.java new file mode 100644 index 000000000000..00a3e55d5493 --- /dev/null +++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/EmbeddedObject.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: EmbeddedObject.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; + +import java.io.IOException; + +import org.w3c.dom.Document; +import org.w3c.dom.DOMException; + + +public abstract class EmbeddedObject { + protected String objName; + protected String objType; + + /** Representation of the file from which this object was read. */ + protected OfficeZip zipFile = null; + + /** Flag indicating if this document has changed since reading or is new. */ + protected boolean hasChanged = false; + + /** + * Constructor for an embedded object stored using an XML representation. + * + * @param name The name of the object. + * @param type The mime-type of the object. See the class summary. + */ + public EmbeddedObject(String name, String type) { + objName = name; + objType = type; + + hasChanged = true; + } + + + /** + * Package private constructor for use when reading an object from a + * compressed SX? file. + * + * @param name The name of the object. + * @param type The mime-type of the object. See the class summary. + * @param source The OfficeZip representation of the SX? file that stores + * the object. + */ + EmbeddedObject(String name, String type, OfficeZip source) { + this(name, type); + zipFile = source; + } + + + /** + * Retrieves the name of the embedded object represented by an instance of + * this class. + * + * <b>N.B.</b>The name referes to the name as found in the + * <code>META-INF/manifest.xml</code> file. + * + * @return The name of the object. + */ + public final String getName() { + return objName; + } + + + /** + * Retrieves the type of the embedded object represented by an instance of + * this class. + * + * The <code>META-INF/manifest.xml</code> file currently represents the + * type of an object using MIME types. + */ + public final String getType() { + return objType; + } + + /** + * Package private method for writing the data of the EmbeddedObject to a + * SX? file. + * + * @param zip An <code>OfficeZip</code> instance representing the file + * the data is to be written to. + */ + abstract void write(OfficeZip zip) throws IOException; + + /** + * Package private method that constructs the manifest.xml entries for this + * embedded object. + * + * @return Document <code>Document</code> containing the manifest entries. + */ + abstract void writeManifestData(Document manifestDoc) throws DOMException; +}
\ No newline at end of file diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/EmbeddedXMLObject.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/EmbeddedXMLObject.java new file mode 100644 index 000000000000..b3f8f5769b7d --- /dev/null +++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/EmbeddedXMLObject.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: EmbeddedXMLObject.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; + +import java.io.IOException; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import org.w3c.dom.Document; +import org.w3c.dom.DOMException; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +import org.xml.sax.SAXException; + +/** + * This class represents those embedded objects in an OpenOffice.org document + * that have an XML representation. Currently, according to the OpenOffice.org + * File Format 1.0 document, there are 6 such objects: + * + * Formulae created with Math (application/vnd.sun.xml.math) + * Charts created with Chart (application/vnd.sun.xml.chart) + * Spreadsheets created with Calc (application/vnd.sun.xml.calc) + * Text created with Writer (application/vnd.sun.xml.writer) + * Drawings created with Draw (application/vnd.sun.xml.draw) + * Presentations created with Impress (application/vnd.sun.xml.impress) + * + * These object types are stored using a combination of content, settings and styles + * XML files. + */ +public class EmbeddedXMLObject extends EmbeddedObject { + + // Entries for the subdocuments that constitute this object; + protected Document contentDOM = null; + protected Document settingsDOM = null; + protected Document stylesDOM = null; + + private DocumentBuilder builder = null; + + /** + * Constructor for an embedded object stored using an XML representation. + * + * @param name The name of the object. + * @param type The mime-type of the object. See the class summary. + */ + public EmbeddedXMLObject(String name, String type) { + super(name, type); + } + + /** + * Package private constructor for use when reading an object from a + * compressed SX? file. + * + * @param name The name of the object. + * @param type The mime-type of the object. See the class summary. + * @param source The OfficeZip representation of the SX? file that stores + * the object. + */ + EmbeddedXMLObject(String name, String type, OfficeZip source) { + super(name, type, source); + } + + + /** + * Returns the content data for this embedded object. + * + * @return DOM represenation of "content.xml" + * + * @throws SAXException If any parser error occurs + * @throws IOException If any IO error occurs + */ + public Document getContentDOM() throws SAXException, IOException { + + if (contentDOM == null) { + contentDOM = getNamedDOM("content.xml"); + } + + return contentDOM; + } + + + /** + * Sets the content data for the embedded object. + * + * @param content DOM representation of the object's content. + */ + public void setContentDOM(Document content) { + contentDOM = content; + hasChanged = true; + } + + + /** + * Returns the settings data for this embedded object. + * + * @return DOM represenation of "settings.xml" + * + * @throws SAXException If any parser error occurs + * @throws IOException If any IO error occurs + */ + public Document getSettingsDOM() throws SAXException, IOException { + + if (settingsDOM == null) { + settingsDOM = getNamedDOM("settings.xml"); + } + + return settingsDOM; + } + + + /** + * Sets the settings data for the embedded object. + * + * @param styles DOM representation of the object's styles. + */ + public void setSettingsDOM(Document settings) { + settingsDOM = settings; + hasChanged = true; + } + + + /** + * Returns the style data for this embedded object. + * + * @return DOM represenation of "styles.xml" + * + * @throws SAXException If any parser error occurs + * @throws IOException If any IO error occurs + */ + public Document getStylesDOM() throws SAXException, IOException { + + if (stylesDOM == null) { + stylesDOM = getNamedDOM("styles.xml"); + } + + return stylesDOM; + } + + + /** + * Sets the styles data for the embedded object. + * + * @param styles DOM representation of the object's styles. + */ + public void setStylesDOM(Document styles) { + stylesDOM = styles; + hasChanged = true; + } + + + /** + * This method extracts the data for the given XML file from the SX? file + * and creates a DOM representation of it. + * + * @param name The name of the XML file to retrieve. It is paired with + * the object name to access the SX? file. + * + * @return DOM representation of the named XML file. + * + * @throws SAXException If any parser error occurs + * @throws IOException If any IO error occurs + */ + private Document getNamedDOM(String name) throws SAXException, IOException { + if (zipFile == null) { + return null; + } + + try { + if (builder == null) { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + + factory.setValidating(false); + builder = factory.newDocumentBuilder(); + } + + byte[] data = zipFile.getNamedBytes(new String(objName + "/" + name)); + if (data != null) { + return OfficeDocument.parse(builder, data); + } + else { + return null; + } + + } + catch (SAXException se) { + throw se; + } + catch (IOException ioe) { + throw ioe; + } + catch (ParserConfigurationException pce) { + throw new SAXException(pce); + } + } + + + /** + * Package private method for writing the data of the EmbeddedObject to a + * SX? file. + * + * @param zip An <code>OfficeZip</code> instance representing the file + * the data is to be written to. + */ + void write(OfficeZip zip) throws IOException { + if (hasChanged == true) { + if (contentDOM != null) { + zip.setNamedBytes(new String(objName + "/content.xml"), + OfficeDocument.docToBytes(contentDOM)); + } + if (settingsDOM != null) { + zip.setNamedBytes(new String(objName + "/settings.xml"), + OfficeDocument.docToBytes(settingsDOM)); + } + if (stylesDOM != null) { + zip.setNamedBytes(new String(objName + "/styles.xml"), + OfficeDocument.docToBytes(stylesDOM)); + } + } + } + + /** + * Package private method that constructs the manifest.xml entries for this + * embedded object. + * + * @param manifestDoc <code>Document</code> containing the manifest entries. + */ + void writeManifestData(Document manifestDoc) throws DOMException { + Node root = manifestDoc.getDocumentElement(); + + if (contentDOM != null) { + Element contentNode = manifestDoc.createElement(OfficeConstants.TAG_MANIFEST_FILE); + + contentNode.setAttribute(OfficeConstants.ATTRIBUTE_MANIFEST_FILE_TYPE, "text/xml"); + contentNode.setAttribute(OfficeConstants.ATTRIBUTE_MANIFEST_FILE_PATH, + new String(objName + "/content.xml")); + + root.appendChild(contentNode); + } + + if (settingsDOM != null) { + Element settingsNode = manifestDoc.createElement(OfficeConstants.TAG_MANIFEST_FILE); + + settingsNode.setAttribute(OfficeConstants.ATTRIBUTE_MANIFEST_FILE_TYPE, "text/xml"); + settingsNode.setAttribute(OfficeConstants.ATTRIBUTE_MANIFEST_FILE_PATH, + new String(objName + "/settings.xml")); + + root.appendChild(settingsNode); + } + + if (stylesDOM != null) { + Element stylesNode = manifestDoc.createElement(OfficeConstants.TAG_MANIFEST_FILE); + + stylesNode.setAttribute(OfficeConstants.ATTRIBUTE_MANIFEST_FILE_TYPE, "text/xml"); + stylesNode.setAttribute(OfficeConstants.ATTRIBUTE_MANIFEST_FILE_PATH, + new String(objName + "/styles.xml")); + } + + + Element objectNode = manifestDoc.createElement(OfficeConstants.TAG_MANIFEST_FILE); + + objectNode.setAttribute(OfficeConstants.ATTRIBUTE_MANIFEST_FILE_TYPE, objType); + objectNode.setAttribute(OfficeConstants.ATTRIBUTE_MANIFEST_FILE_PATH, + new String(objName + "/")); + + root.appendChild(objectNode); + } + +} diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/OfficeConstants.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/OfficeConstants.java new file mode 100644 index 000000000000..020546141ad5 --- /dev/null +++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/OfficeConstants.java @@ -0,0 +1,442 @@ +/************************************************************************* + * + * 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: OfficeConstants.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; + +/** + * This interface contains constants for StarOffice XML tags, + * attributes (StarCalc cell types, etc.). + * + * @author Herbie Ong, Paul Rank, Martin Maher + */ +public interface OfficeConstants { + + /** Element tag for <i>office:document</i>, this is the root tag. */ + public final static String TAG_OFFICE_DOCUMENT = "office:document"; + + /** + * Element tag for <i>office:document-content</i>, this is the root + * tag in content.xml. + */ + public final static String TAG_OFFICE_DOCUMENT_CONTENT = "office:document-content"; + + /** + * Element tag for <i>office:document-settings</i>, this is the root + * tag in content.xml. + */ + public final static String TAG_OFFICE_DOCUMENT_SETTINGS= "office:document-settings"; + + /** + * Element tag for <i>office:document-meta</i>, this is the root + * tag in content.xml. + */ + public final static String TAG_OFFICE_DOCUMENT_META= "office:document-meta"; + + /** + * Element tag for <i>office:document-styles</i>, this is the root tag + * in styles.xml. + */ + public final static String TAG_OFFICE_DOCUMENT_STYLES = "office:document-styles"; + + /** + * Attribute tag for <i>office:class</i> of element + * <i>office:document</i>. + */ + public final static String ATTRIBUTE_OFFICE_CLASS = "office:class"; + + /** Element tag for <i>office:styles</i>. */ + public final static String TAG_OFFICE_STYLES = "office:styles"; + + /** Element tag for <i>office:meta</i>. */ + public final static String TAG_OFFICE_META = "office:meta"; + + /** Element tag for <i>office:automatic-styles</i>. */ + public final static String TAG_OFFICE_AUTOMATIC_STYLES = "office:automatic-styles"; + + /** Element tag for <i>office:master-styles</i>. */ + public final static String TAG_OFFICE_MASTER_STYLES = "office:master-styles"; + + /** Element tag for <i>office:body</i>. */ + public final static String TAG_OFFICE_BODY = "office:body"; + + /** Element tag for <i>office:settings</i>. */ + public final static String TAG_OFFICE_SETTINGS = "office:settings"; + + //Adding + + /** Element tag for <i>text:variable-set</i>. */ + public final static String TAG_TEXT_VARIABLE_SET = "text:variable-set"; + + /** Element tag for <i>text:variable-get</i>. */ + public final static String TAG_TEXT_VARIABLE_GET = "text:variable-get"; +/** Element tag for <i>text:expression</i>. */ + public final static String TAG_TEXT_EXPRESSION = "text:expression"; + +/** Element tag for <i>text:user-field-get</i>. */ + public final static String TAG_TEXT_USER_FIELD_GET = "text:user-field-get"; + +/** Element tag for <i>text:page-variable-get</i>. */ + public final static String TAG_TEXT_PAGE_VARIABLE_GET = "text:page-variable-get"; +/** Element tag for <i>text:sequence</i>. */ + public final static String TAG_TEXT_SEQUENCE = "text:sequence"; + + /** Element tag for <i>text:text-input</i>. */ + public final static String TAG_TEXT_VARIABLE_INPUT = "text:variable-input"; + /** Element tag for <i>text:time</i>. */ + public final static String TAG_TEXT_TIME = "text:time"; + + /** Element tag for <i>text:page-count</i>. */ + public final static String TAG_TEXT_PAGE_COUNT = "text:page-count"; + /** Element tag for <i>text:page-number</i>. */ + public final static String TAG_TEXT_PAGE_NUMBER = "text:page-number"; + /** Element tag for <i>text:author-initials</i>. */ + public final static String TAG_TEXT_AUTHOR_INITIALS = "text:author-initials"; + /** Element tag for <i>text:subject</i>. */ + public final static String TAG_TEXT_SUBJECT = "text:subject"; + /** Element tag for <i>text:title</i>. */ + public final static String TAG_TEXT_TITLE = "text:title"; + /** Element tag for <i>text:creation-time</i>. */ + public final static String TAG_TEXT_CREATION_TIME = "text:creation-time"; + + /** Element tag for <i>text:date</i>. */ + public final static String TAG_TEXT_DATE = "text:date"; + /** Element tag for <i>text:text-input</i>. */ + public final static String TAG_TEXT_TEXT_INPUT = "text:text-input"; + + +//end adding + + /** Element tag for <i>office:font-decls</i>. */ + public final static String TAG_OFFICE_FONT_DECLS = "office:font-decls"; + + /** Element tag for <i>style:font-decl</i>. */ + public final static String TAG_STYLE_FONT_DECL = "style:font-decl"; + + /** Attribute tag for <i>style:name</i> of element <i>style:name</i>. */ + public final static String ATTRIBUTE_STYLE_NAME = "style:name"; + + /** + * Attribute tag for <i>style:font-pitch</i> of element + * <i>style:font-pitch</i>. + */ + public final static String ATTRIBUTE_STYLE_FONT_PITCH = "style:font-pitch"; + + /** + * Attribute tag for <i>fo:font-family</i> of element + * <i>fo:font-family</i>. + */ + public final static String ATTRIBUTE_FO_FONT_FAMILY = "fo:font-family"; + + /** + * Attribute tag for <i>fo:font-family</i> of element + * <i>fo:font-family</i>. + */ + public final static String ATTRIBUTE_FO_FONT_FAMILY_GENERIC = "fo:font-family-generic"; + + /** Element tag for <i>text:p</i>. */ + public final static String TAG_PARAGRAPH = "text:p"; + + /** Element tag for <i>text:</i>. */ + public final static String TAG_TEXT = "text:"; + + /** Element tag for <i>text:h</i>. */ + public final static String TAG_HEADING = "text:h"; + + /** Element tag for <i>text:s</i>. */ + public final static String TAG_SPACE = "text:s"; + + /** Element tag for <i>text:tab-stop</i>. */ + public final static String TAG_TAB_STOP = "text:tab-stop"; + + /** Element tag for <i>text:line-break</i>. */ + public final static String TAG_LINE_BREAK = "text:line-break"; + + /** Element tag for <i>text:span</i>. */ + public final static String TAG_SPAN = "text:span"; + + /** Element tag for <i>text:a</i>. */ + public final static String TAG_HYPERLINK = "text:a"; + + /** Element tag for <i>text:bookmark</i>. */ + public final static String TAG_BOOKMARK = "text:bookmark"; + + /** Element tag for <i>text:bookmark-start</i>. */ + public final static String TAG_BOOKMARK_START = "text:bookmark-start"; + + /** Element tag for <i>text:unordered-list</i>. */ + public final static String TAG_UNORDERED_LIST = "text:unordered-list"; + + /** Element tag for <i>text:ordered-list</i>. */ + public final static String TAG_ORDERED_LIST = "text:ordered-list"; + + /** Element tag for <i>text:list-header</i>. */ + public final static String TAG_LIST_HEADER = "text:list-header"; + + /** Element tag for <i>text:list-item</i>. */ + public final static String TAG_LIST_ITEM = "text:list-item"; + + /** Attribute tag for <i>text:c</i> of element <i>text:s</i>. */ + public final static String ATTRIBUTE_SPACE_COUNT = "text:c"; + + /** + * Attribute tag for <i>text:style-name</i> of element + * <i>text:style-name</i>. + */ + public final static String ATTRIBUTE_TEXT_STYLE_NAME = "text:style-name"; + + /** Element tag for <i>table:table</i>. */ + public final static String TAG_TABLE = "table:table"; + + /** Element tag for <i>table:named-expression</i>. */ + public final static String TAG_NAMED_EXPRESSIONS = "table:named-expressions"; + + /** Element tag for <i>table:named-range</i>. */ + public final static String TAG_TABLE_NAMED_RANGE= "table:named-range"; + + /** Element tag for <i>table:named-expression</i>. */ + public final static String TAG_TABLE_NAMED_EXPRESSION= "table:named-expression"; + + /** + * Attribute tag for <i>table:name</i> of element + * <i>table:table</i>. + */ + public final static String ATTRIBUTE_TABLE_NAME = "table:name"; + + /** + * Attribute tag for <i>table:expression</i> of element + * <i>table:named-range</i>. + */ + public final static String ATTRIBUTE_TABLE_EXPRESSION = "table:expression"; + + /** + * Attribute tag for <i>table:base-cell-address</i> of element + * <i>table:named-range</i>. + */ + public final static String ATTRIBUTE_TABLE_BASE_CELL_ADDRESS = "table:base-cell-address"; + + /** + * Attribute tag for <i>table:cell-range-address</i> of element + * <i>table:named-range</i>. + */ + public final static String ATTRIBUTE_TABLE_CELL_RANGE_ADDRESS = "table:cell-range-address"; + + /** Element tag for <i>table:table-row</i>. */ + public final static String TAG_TABLE_ROW = "table:table-row"; + + /** Element tag for <i>table:table-column</i>. */ + public final static String TAG_TABLE_COLUMN = "table:table-column"; + + /** + * Attribute tag for <i>table:default-cell-style-name</i> + * of element <i>table:table-column</i>. + */ + public final static String ATTRIBUTE_DEFAULT_CELL_STYLE = "table:default-cell-style-name"; + + /** Element tag for <i>table:scenario</i>. */ + public final static String TAG_TABLE_SCENARIO = "table:scenario"; + + /** Element tag for <i>table:table-cell</i>. */ + public final static String TAG_TABLE_CELL = "table:table-cell"; + + /** + * Attribute tag for <i>table:value-type</i> of element + * <i>table:table-cell</i>. + */ + public final static String ATTRIBUTE_TABLE_VALUE_TYPE = "table:value-type"; + + /** + * Attribute tag for <i>table:number-columns-repeated</i> + * of element <i>table:table-cell</i>. + */ + public final static String ATTRIBUTE_TABLE_NUM_COLUMNS_REPEATED = + "table:number-columns-repeated"; + + /** + * Attribute tag for <i>table:number-rows-repeated</i> + * of element <i>table:table-row</i>. + */ + public final static String ATTRIBUTE_TABLE_NUM_ROWS_REPEATED = + "table:number-rows-repeated"; + + /** + * Attribute tag for <i>table:formula</i> of element + * <i>table:table-cell</i>. + */ + public final static String ATTRIBUTE_TABLE_FORMULA = "table:formula"; + + /** + * Attribute tag for <i>table:value</i> of element + * <i>table:table-cell</i>. + */ + public final static String ATTRIBUTE_TABLE_VALUE = "table:value"; + + /** + * Attribute tag for <i>table:date-value</i> of element + * <i>table:table-cell</i>. + */ + public final static String ATTRIBUTE_TABLE_DATE_VALUE = "table:date-value"; + + /** + * Attribute tag for <i>table:time-value</i> of element + * <i>table:table-cell</i>. + */ + public final static String ATTRIBUTE_TABLE_TIME_VALUE = "table:time-value"; + + /** + * Attribute tag for <i>table:string-value</i> of element + * <i>table:table-cell</i>. + */ + public final static String ATTRIBUTE_TABLE_STRING_VALUE = + "table:string-value"; + + /** + * Attribute tag for <i>table:time-boolean-value</i> of element + * <i>table:table-cell</i>. + */ + public final static String ATTRIBUTE_TABLE_BOOLEAN_VALUE = + "table:boolean-value"; + + /** Attribute tag for <i>table:style-name</i> of table elements. */ + public final static String ATTRIBUTE_TABLE_STYLE_NAME = "table:style-name"; + + /** + * Attribute tag for <i>table:currency</i> of element + * <i>table:table-cell</i>. + */ + public final static String ATTRIBUTE_TABLE_CURRENCY = "table:currency"; + + /** The cell contains data of type <i>string</i>. */ + public final static String CELLTYPE_STRING = "string"; + + /** The cell contains data of type <i>float</i>. */ + public final static String CELLTYPE_FLOAT = "float"; + + /** The cell contains data of type <i>time</i>. */ + public final static String CELLTYPE_TIME = "time"; + + /** The cell contains data of type <i>date</i>. */ + public final static String CELLTYPE_DATE = "date"; + + /** The cell contains data of type <i>currency</i>. */ + public final static String CELLTYPE_CURRENCY = "currency"; + + /** The cell contains data of type <i>boolean</i>. */ + public final static String CELLTYPE_BOOLEAN = "boolean"; + + /** The cell contains data of type <i>percent</i>. */ + public final static String CELLTYPE_PERCENT = "percentage"; + + /** StarWriter XML file extension. */ + public final static String SXW_FILE_EXTENSION = ".sxw"; + + /** StarWriter XML <i>office:class</i> value. */ + public final static String SXW_TYPE = "text"; + + /** StarCalc XML file extension. */ + public final static String SXC_FILE_EXTENSION = ".sxc"; + + /** StarCalc XML <i>office:class</i> value. */ + public final static String SXC_TYPE = "spreadsheet"; + + /** Element tag for <i>manifest:manifest</i>entry in Manifest XML */ + public final static String TAG_MANIFEST_ROOT = "manifest:manifest"; + + /** Element tag for <i>manifest:file-entry</i> entry in Manifest XML. */ + public final static String TAG_MANIFEST_FILE = "manifest:file-entry"; + + /** + * Attribute tag for <i>manifest:media-type</i> of element + * <i>manifest:file-entry</i>. + */ + public final static String ATTRIBUTE_MANIFEST_FILE_TYPE = "manifest:media-type"; + + /** + * Attribute tag for <i>manifest:full-path</i> of element + * <i>manifest:file-entry</i>. + */ + public final static String ATTRIBUTE_MANIFEST_FILE_PATH = "manifest:full-path"; + + // Tags and Elements for the settings.xml + + /** Element tag for <i>config:config-item</i>. */ + public final static String TAG_CONFIG_ITEM = "config:config-item"; + + /** Element tag for <i>config:config-item-set</i>. */ + public final static String TAG_CONFIG_ITEM_SET = "config:config-item-set"; + + /** Element tag for <i>config:config-item-map-indexed</i>. */ + public final static String TAG_CONFIG_ITEM_MAP_INDEXED = "config:config-item-map-indexed"; + + /** Element tag for <i>config:config-item-map-named</i>. */ + public final static String TAG_CONFIG_ITEM_MAP_NAMED = "config:config-item-map-named"; + + /** Element tag for <i>config:config-item-map-entry</i>. */ + public final static String TAG_CONFIG_ITEM_MAP_ENTRY= "config:config-item-map-entry"; + + /** + * Attribute tag for <i>config:name</i> of element + * <i>config:config-item</i>. + */ + public final static String ATTRIBUTE_CONFIG_NAME = "config:name"; + + /** + * Attribute tag for <i>config:type</i> of element + * <i>config:config-item</i>. + */ + public final static String ATTRIBUTE_CONFIG_TYPE = "config:type"; + + + /** StarWriter XML MIME type. */ + public final static String SXW_MIME_TYPE = "application/vnd.sun.xml.writer"; + + /** StarWriter XML Template MIME type. */ + public final static String STW_MIME_TYPE = "application/vnd.sun.xml.writer.template"; + + /** StarCalc XML MIME type. */ + public final static String SXC_MIME_TYPE = "application/vnd.sun.xml.calc"; + + /** StarCalc XML Template MIME type. */ + public final static String STC_MIME_TYPE = "application/vnd.sun.xml.calc.template"; + + /** StarImpress XML MIME type. */ + public final static String SXI_MIME_TYPE = "application/vnd.sun.xml.impress"; + + /** StarImpress XML Template MIME type. */ + public final static String STI_MIME_TYPE = "application/vnd.sun.xml.impress.template"; + + /** StarDraw XML MIME type. */ + public final static String SXD_MIME_TYPE = "application/vnd.sun.xml.draw"; + + /** StarMath XML MIME type. */ + public final static String SXM_MIME_TYPE = "application/vnd.sun.xml.math"; + + /** StarWriter Global XML MIME Type */ + public final static String SXG_MIME_TYPE = "application/vnd.sun.xml.writer.global"; +} diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/OfficeDocument.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/OfficeDocument.java new file mode 100644 index 000000000000..bc62cc0a7e2a --- /dev/null +++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/OfficeDocument.java @@ -0,0 +1,1268 @@ +/************************************************************************* + * + * 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: OfficeDocument.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; + +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Reader; +import java.io.BufferedReader; +import java.io.StringReader; +import java.io.StringWriter; +import java.io.InputStreamReader; +import java.io.ByteArrayOutputStream; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.util.Iterator; +import java.util.Map; +import java.util.HashMap; + +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.ParserConfigurationException; + +import org.w3c.dom.Node; +import org.w3c.dom.Element; +import org.w3c.dom.Document; +import org.w3c.dom.DOMImplementation; +import org.w3c.dom.DocumentType; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; +import org.w3c.dom.NamedNodeMap; +import org.xml.sax.SAXException; + +import javax.xml.transform.*; +import javax.xml.transform.dom.*; +import javax.xml.transform.stream.*; + +import org.openoffice.xmerge.util.Resources; +import org.openoffice.xmerge.util.Debug; + +/** + * An implementation of <code>Document</code> for + * StarOffice documents. + */ +public abstract class OfficeDocument + implements org.openoffice.xmerge.Document, + OfficeConstants { + + /** Factory for <code>DocumentBuilder</code> objects. */ + private static DocumentBuilderFactory factory = + DocumentBuilderFactory.newInstance(); + + /** DOM <code>Document</code> of content.xml. */ + private Document contentDoc = null; + + /** DOM <code>Document</code> of meta.xml. */ + private Document metaDoc = null; + + /** DOM <code>Document</code> of settings.xml. */ + private Document settingsDoc = null; + + /** DOM <code>Document</code> of content.xml. */ + private Document styleDoc = null; + + /** DOM <code>Docuemtn</code> of META-INF/manifest.xml. */ + private Document manifestDoc = null; + + private String documentName = null; + private String fileName = null; + + /** Resources object. */ + private Resources res = null; + + /** + * <code>OfficeZip</code> object to store zip contents from + * read <code>InputStream</code>. Note that this member + * will still be null if it was initialized using a template + * file instead of reading from a StarOffice zipped + * XML file. + */ + private OfficeZip zip = null; + + /** Collection to keep track of the embedded objects in the document. */ + private Map embeddedObjects = null; + + /** + * Default constructor. + * + * @param name <code>Document</code> name. + */ + public OfficeDocument(String name) + { + this(name, true, false); + } + + + /** + * Constructor with arguments to set <code>namespaceAware</code> + * and <code>validating</code> flags. + * + * @param name <code>Document</code> name (may or may not + * contain extension). + * @param namespaceAware Value for <code>namespaceAware</code> flag. + * @param validating Value for <code>validating</code> flag. + */ + public OfficeDocument(String name, boolean namespaceAware, boolean validating) { + + res = Resources.getInstance(); + factory.setValidating(validating); + factory.setNamespaceAware(namespaceAware); + this.documentName = trimDocumentName(name); + this.fileName = documentName + getFileExtension(); + } + + + /** + * Removes the file extension from the <code>Document</code> + * name. + * + * @param name Full <code>Document</code> name with extension. + * + * @return Name of <code>Document</code> without the extension. + */ + private String trimDocumentName(String name) { + String temp = name.toLowerCase(); + String ext = getFileExtension(); + + if (temp.endsWith(ext)) { + // strip the extension + int nlen = name.length(); + int endIndex = nlen - ext.length(); + name = name.substring(0,endIndex); + } + + return name; + } + + + /** + * Return a DOM <code>Document</code> object of the content.xml + * file. Note that a content DOM is not created when the constructor + * is called. So, either the <code>read</code> method or the + * <code>initContentDOM</code> method will need to be called ahead + * on this object before calling this method. + * + * @return DOM <code>Document</code> object. + */ + public Document getContentDOM() { + + return contentDoc; + } + + /** + * Return a DOM <code>Document</code> object of the meta.xml + * file. Note that a content DOM is not created when the constructor + * is called. So, either the <code>read</code> method or the + * <code>initContentDOM</code> method will need to be called ahead + * on this object before calling this method. + * + * @return DOM <code>Document</code> object. + */ + public Document getMetaDOM() { + + return metaDoc; + } + + + /** + * Return a DOM <code>Document</code> object of the settings.xml + * file. Note that a content DOM is not created when the constructor + * is called. So, either the <code>read</code> method or the + * <code>initContentDOM</code> method will need to be called ahead + * on this object before calling this method. + * + * @return DOM <code>Document</code> object. + */ + public Document getSettingsDOM() { + + return settingsDoc; + } + + + /** + * Sets the content tree of the document. + * + * @param newDom <code>Node</code> containing the new content tree. + */ + public void setContentDOM( Node newDom) { + contentDoc = (Document)newDom; + } + + + /** + * Sets the meta tree of the document. + * + * @param newDom <code>Node</code> containing the new meta tree. + */ + public void setMetaDOM (Node newDom) { + metaDoc = (Document)newDom; + } + + + /** + * Sets the settings tree of the document. + * + * @param newDom <code>Node</code> containing the new settings tree. + */ + public void setSettingsDOM (Node newDom) { + settingsDoc = (Document)newDom; + } + + + /** + * Sets the style tree of the document. + * + * @param newDom <code>Node</code> containing the new style tree. + */ + public void setStyleDOM (Node newDom) { + styleDoc = (Document)newDom; + } + + + /** + * Return a DOM <code>Document</code> object of the style.xml file. + * Note that this may return null if there is no style DOM. + * Note that a style DOM is not created when the constructor + * is called. Depending on the <code>InputStream</code>, a + * <code>read</code> method may or may not build a style DOM. When + * creating a new style DOM, call the <code>initStyleDOM</code> method + * first. + * + * @return DOM <code>Document</code> object. + */ + public Document getStyleDOM() { + + return styleDoc; + } + + + /** + * Return the name of the <code>Document</code>. + * + * @return The name of <code>Document</code>. + */ + public String getName() { + + return documentName; + } + + + /** + * Return the file name of the <code>Document</code>, possibly + * with the standard extension. + * + * @return The file name of <code>Document</code>. + */ + public String getFileName() { + + return fileName; + } + + + /** + * Returns the file extension for this type of + * <code>Document</code>. + * + * @return The file extension of <code>Document</code>. + */ + protected abstract String getFileExtension(); + + + /** + * Returns all the embedded objects (graphics, formulae, etc.) present in + * this document. + * + * @return An <code>Iterator</code> of <code>EmbeddedObject</code> objects. + */ + public Iterator getEmbeddedObjects() { + + if (embeddedObjects == null && manifestDoc != null) { + embeddedObjects = new HashMap(); + + // Need to read the manifest file and construct a list of objects + NodeList nl = manifestDoc.getElementsByTagName(TAG_MANIFEST_FILE); + + // Dont create the HashMap if there are no embedded objects + int len = nl.getLength(); + for (int i = 0; i < len; i++) { + Node n = nl.item(i); + + NamedNodeMap attrs = n.getAttributes(); + + String type = attrs.getNamedItem(ATTRIBUTE_MANIFEST_FILE_TYPE).getNodeValue(); + String path = attrs.getNamedItem(ATTRIBUTE_MANIFEST_FILE_PATH).getNodeValue(); + + + /* + * According to OpenOffice.org XML File Format document (ver. 1) + * there are only two types of embedded object: + * + * Objects with an XML representation. + * Objects without an XML representation. + * + * The former are represented by one or more XML files. + * The latter are in binary form. + */ + if (type.startsWith("application/vnd.sun.xml")) + { + if (path.equals("/")) { + // Exclude the main document entries + continue; + } + // Take off the trailing '/' + String name = path.substring(0, path.length() - 1); + embeddedObjects.put(name, new EmbeddedXMLObject(name, type, zip)); + } + else if (type.equals("text/xml")) { + // XML entries are either embedded StarOffice doc entries or main + // document entries + continue; + } + else { // FIX (HJ): allows empty MIME type + embeddedObjects.put(path, new EmbeddedBinaryObject(path, type, zip)); + } + } + } + + return embeddedObjects.values().iterator(); + } + + /** + * Returns the embedded object corresponding to the name provided. + * The name should be stripped of any preceding path characters, such as + * '/', '.' or '#'. + * + * @param name The name of the embedded object to retrieve. + * + * @return An <code>EmbeddedObject</code> instance representing the named + * object. + */ + public EmbeddedObject getEmbeddedObject(String name) { + if (name == null) { + return null; + } + + if (embeddedObjects == null) { + getEmbeddedObjects(); + } + + if (embeddedObjects.containsKey(name)) { + return (EmbeddedObject)embeddedObjects.get(name); + } + else { + return null; + } + } + + + /** + * Adds a new embedded object to the document. + * + * @param embObj An instance of <code>EmbeddedObject</code>. + */ + public void addEmbeddedObject(EmbeddedObject embObj) { + if (embObj == null) { + return; + } + + if (embeddedObjects == null) { + embeddedObjects = new HashMap(); + } + + embeddedObjects.put(embObj.getName(), embObj); + } + + + /** + * Read the Office <code>Document</code> from the given + * <code>InputStream</code>. + * + * @param is Office document <code>InputStream</code>. + * + * @throws IOException If any I/O error occurs. + */ + public void read(InputStream is) throws IOException { + + Debug.log(Debug.INFO, "reading Office file"); + + DocumentBuilder builder = null; + + try { + builder = factory.newDocumentBuilder(); + } catch (ParserConfigurationException ex) { + throw new OfficeDocumentException(ex); + } + + // read in Office zip file format + + zip = new OfficeZip(); + zip.read(is); + + // grab the content.xml and + // parse it into contentDoc. + + byte contentBytes[] = zip.getContentXMLBytes(); + + if (contentBytes == null) { + + throw new OfficeDocumentException("Entry content.xml not found in file"); + } + + try { + + contentDoc = parse(builder, contentBytes); + + } catch (SAXException ex) { + + throw new OfficeDocumentException(ex); + } + + // if style.xml exists, grab the style.xml + // parse it into styleDoc. + + byte styleBytes[] = zip.getStyleXMLBytes(); + + if (styleBytes != null) { + + try { + + styleDoc = parse(builder, styleBytes); + + } catch (SAXException ex) { + + throw new OfficeDocumentException(ex); + } + } + + byte metaBytes[] = zip.getMetaXMLBytes(); + + if (metaBytes != null) { + + try { + + metaDoc = parse(builder, metaBytes); + + } catch (SAXException ex) { + + throw new OfficeDocumentException(ex); + } + } + + byte settingsBytes[] = zip.getSettingsXMLBytes(); + + if (settingsBytes != null) { + + try { + + settingsDoc = parse(builder, settingsBytes); + + } catch (SAXException ex) { + + throw new OfficeDocumentException(ex); + } + } + + + // Read in the META-INF/manifest.xml file + byte manifestBytes[] = zip.getManifestXMLBytes(); + + if (manifestBytes != null) { + + try { + manifestDoc = parse(builder, manifestBytes); + } catch (SAXException ex) { + throw new OfficeDocumentException(ex); + } + } + + } + + + /** + * Read the Office <code>Document</code> from the given + * <code>InputStream</code>. + * + * @param is Office document <code>InputStream</code>. + * @param isZip <code>boolean</code> Identifies whether + * a file is zipped or not + * + * @throws IOException If any I/O error occurs. + */ + public void read(InputStream is, boolean isZip) throws IOException { + + Debug.log(Debug.INFO, "reading Office file"); + + DocumentBuilder builder = null; + + try { + builder = factory.newDocumentBuilder(); + } catch (ParserConfigurationException ex) { + throw new OfficeDocumentException(ex); + } + + if (isZip) + { + read(is); + } + else{ + try{ + //System.out.println("\nParsing Input stream, validating?: "+builder.isValidating()); + //contentDoc= builder.parse((InputStream)is); + + Reader r = secondHack(is); + InputSource ins = new InputSource(r); + org.w3c.dom.Document newDoc = builder.parse(ins); + //org.w3c.dom.Document newDoc = builder.parse((InputStream)is); + Element rootElement=newDoc.getDocumentElement(); + + NodeList nodeList; + Node tmpNode; + Node rootNode = (Node)rootElement; + if (newDoc !=null){ + /*content*/ + contentDoc = createDOM(TAG_OFFICE_DOCUMENT_CONTENT); + rootElement=contentDoc.getDocumentElement(); + rootNode = (Node)rootElement; + + // FIX (HJ): Include office:font-decls in content DOM + nodeList= newDoc.getElementsByTagName(TAG_OFFICE_FONT_DECLS); + if (nodeList.getLength()>0){ + tmpNode = contentDoc.importNode(nodeList.item(0),true); + rootNode.appendChild(tmpNode); + } + + nodeList= newDoc.getElementsByTagName(TAG_OFFICE_AUTOMATIC_STYLES); + if (nodeList.getLength()>0){ + tmpNode = contentDoc.importNode(nodeList.item(0),true); + rootNode.appendChild(tmpNode); + } + + nodeList= newDoc.getElementsByTagName(TAG_OFFICE_BODY); + if (nodeList.getLength()>0){ + tmpNode = contentDoc.importNode(nodeList.item(0),true); + rootNode.appendChild(tmpNode); + } + + /*Styles*/ + styleDoc = createDOM(TAG_OFFICE_DOCUMENT_STYLES); + rootElement=styleDoc.getDocumentElement(); + rootNode = (Node)rootElement; + + // FIX (HJ): Include office:font-decls in styles DOM + nodeList= newDoc.getElementsByTagName(TAG_OFFICE_FONT_DECLS); + if (nodeList.getLength()>0){ + tmpNode = styleDoc.importNode(nodeList.item(0),true); + rootNode.appendChild(tmpNode); + } + + nodeList= newDoc.getElementsByTagName(TAG_OFFICE_STYLES); + if (nodeList.getLength()>0){ + tmpNode = styleDoc.importNode(nodeList.item(0),true); + rootNode.appendChild(tmpNode); + } + + // FIX (HJ): Include office:automatic-styles in styles DOM + nodeList= newDoc.getElementsByTagName(TAG_OFFICE_AUTOMATIC_STYLES); + if (nodeList.getLength()>0){ + tmpNode = styleDoc.importNode(nodeList.item(0),true); + rootNode.appendChild(tmpNode); + } + + // FIX (HJ): Include office:master-styles in styles DOM + nodeList= newDoc.getElementsByTagName(TAG_OFFICE_MASTER_STYLES); + if (nodeList.getLength()>0){ + tmpNode = styleDoc.importNode(nodeList.item(0),true); + rootNode.appendChild(tmpNode); + } + + /*Settings*/ + settingsDoc = createDOM(TAG_OFFICE_DOCUMENT_SETTINGS); + rootElement=settingsDoc.getDocumentElement(); + rootNode = (Node)rootElement; + nodeList= newDoc.getElementsByTagName(TAG_OFFICE_SETTINGS); + if (nodeList.getLength()>0){ + tmpNode = settingsDoc.importNode(nodeList.item(0),true); + rootNode.appendChild(tmpNode); + } + /*Meta*/ + metaDoc = createDOM(TAG_OFFICE_DOCUMENT_META); + rootElement=metaDoc.getDocumentElement(); + rootNode = (Node)rootElement; + nodeList= newDoc.getElementsByTagName(TAG_OFFICE_META); + if (nodeList.getLength()>0){ + tmpNode = metaDoc.importNode(nodeList.item(0),true); + rootNode.appendChild(tmpNode); + } + } + } + catch (SAXException ex) { + throw new OfficeDocumentException(ex); + } + } + + } + + + + /** + * Parse given <code>byte</code> array into a DOM + * <code>Document</code> object using the + * <code>DocumentBuilder</code> object. + * + * @param builder <code>DocumentBuilder</code> object for parsing. + * @param bytes <code>byte</code> array for parsing. + * + * @return Resulting DOM <code>Document</code> object. + * + * @throws SAXException If any parsing error occurs. + */ + static Document parse(DocumentBuilder builder, byte bytes[]) + throws SAXException, IOException { + + Document doc = null; + + ByteArrayInputStream is = new ByteArrayInputStream(bytes); + + // TODO: replace hack with a more appropriate fix. + + Reader r = hack(is); + InputSource ins = new InputSource(r); + doc = builder.parse(ins); + + return doc; + } + + + /** + * Method to return the MIME type of the document. + * + * @return String The document's MIME type. + */ + protected abstract String getDocumentMimeType(); + + + /** + * Write out Office ZIP file format. + * + * @param os XML <code>OutputStream</code>. + * + * @throws IOException If any I/O error occurs. + */ + public void write(OutputStream os) throws IOException { + if (zip == null) { + zip = new OfficeZip(); + } + + initManifestDOM(); + + Element domEntry; + Element manifestRoot = manifestDoc.getDocumentElement(); + + // The EmbeddedObjects come first. + Iterator embObjs = getEmbeddedObjects(); + while (embObjs.hasNext()) { + EmbeddedObject obj = (EmbeddedObject)embObjs.next(); + obj.writeManifestData(manifestDoc); + + obj.write(zip); + } + + // Add in the entry for the Pictures directory. Always present. + domEntry = manifestDoc.createElement(TAG_MANIFEST_FILE); + domEntry.setAttribute(ATTRIBUTE_MANIFEST_FILE_PATH, "Pictures/"); + domEntry.setAttribute(ATTRIBUTE_MANIFEST_FILE_TYPE, ""); + manifestRoot.appendChild(domEntry); + + // Write content to the Zip file and then write any of the optional + // data, if it exists. + zip.setContentXMLBytes(docToBytes(contentDoc)); + + domEntry = manifestDoc.createElement(TAG_MANIFEST_FILE); + domEntry.setAttribute(ATTRIBUTE_MANIFEST_FILE_PATH, "content.xml"); + domEntry.setAttribute(ATTRIBUTE_MANIFEST_FILE_TYPE, "text/xml"); + + manifestRoot.appendChild(domEntry); + + if (styleDoc != null) { + zip.setStyleXMLBytes(docToBytes(styleDoc)); + + domEntry = manifestDoc.createElement(TAG_MANIFEST_FILE); + domEntry.setAttribute(ATTRIBUTE_MANIFEST_FILE_PATH, "styles.xml"); + domEntry.setAttribute(ATTRIBUTE_MANIFEST_FILE_TYPE, "text/xml"); + manifestRoot.appendChild(domEntry); + } + + if (metaDoc != null) { + zip.setMetaXMLBytes(docToBytes(metaDoc)); + + domEntry = manifestDoc.createElement(TAG_MANIFEST_FILE); + domEntry.setAttribute(ATTRIBUTE_MANIFEST_FILE_PATH, "meta.xml"); + domEntry.setAttribute(ATTRIBUTE_MANIFEST_FILE_TYPE, "text/xml"); + manifestRoot.appendChild(domEntry); + } + + if (settingsDoc != null) { + zip.setSettingsXMLBytes(docToBytes(settingsDoc)); + + domEntry = manifestDoc.createElement(TAG_MANIFEST_FILE); + domEntry.setAttribute(ATTRIBUTE_MANIFEST_FILE_PATH, "settings.xml"); + domEntry.setAttribute(ATTRIBUTE_MANIFEST_FILE_TYPE, "text/xml"); + manifestRoot.appendChild(domEntry); + } + + zip.setManifestXMLBytes(docToBytes(manifestDoc)); + + zip.write(os); + } + + + /** + * Write out Office ZIP file format. + * + * @param os XML <code>OutputStream</code>. + * @param isZip <code>boolean</code> + * + * @throws IOException If any I/O error occurs. + */ + public void write(OutputStream os, boolean isZip) throws IOException { + + // Create an OfficeZip object if one does not exist. + if (isZip){ + write(os); + } + else{ + try{ + DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance(); + DocumentBuilder builder= builderFactory.newDocumentBuilder(); + DOMImplementation domImpl = builder.getDOMImplementation(); + DocumentType docType =domImpl.createDocumentType("office:document","-//OpenOffice.org//DTD OfficeDocument 1.0//EN",null); + org.w3c.dom.Document newDoc = domImpl.createDocument("http://openoffice.org/2000/office","office:document",null); + + + Element rootElement=newDoc.getDocumentElement(); + rootElement.setAttribute("xmlns:office","http://openoffice.org/2000/office"); + rootElement.setAttribute("xmlns:style","http://openoffice.org/2000/style" ); + rootElement.setAttribute("xmlns:text","http://openoffice.org/2000/text"); + rootElement.setAttribute("xmlns:table","http://openoffice.org/2000/table"); + + rootElement.setAttribute("xmlns:draw","http://openoffice.org/2000/drawing"); + rootElement.setAttribute("xmlns:fo","http://www.w3.org/1999/XSL/Format" ); + rootElement.setAttribute("xmlns:xlink","http://www.w3.org/1999/xlink" ); + rootElement.setAttribute("xmlns:dc","http://purl.org/dc/elements/1.1/" ); + rootElement.setAttribute("xmlns:meta","http://openoffice.org/2000/meta" ); + rootElement.setAttribute("xmlns:number","http://openoffice.org/2000/datastyle" ); + rootElement.setAttribute("xmlns:svg","http://www.w3.org/2000/svg" ); + rootElement.setAttribute("xmlns:chart","http://openoffice.org/2000/chart" ); + rootElement.setAttribute("xmlns:dr3d","http://openoffice.org/2000/dr3d" ); + rootElement.setAttribute("xmlns:math","http://www.w3.org/1998/Math/MathML" ); + rootElement.setAttribute("xmlns:form","http://openoffice.org/2000/form" ); + rootElement.setAttribute("xmlns:script","http://openoffice.org/2000/script" ); + rootElement.setAttribute("xmlns:config","http://openoffice.org/2001/config" ); + // #i41033# OASIS format needs the "office:class" set. + if(getDocumentMimeType() == SXC_MIME_TYPE) + rootElement.setAttribute("office:class","spreadsheet" ); + else if(getDocumentMimeType() == SXW_MIME_TYPE) + rootElement.setAttribute("office:class","text" ); + rootElement.setAttribute("office:version","1.0"); + + + NodeList nodeList; + Node tmpNode; + Node rootNode = (Node)rootElement; + if (metaDoc !=null){ + nodeList= metaDoc.getElementsByTagName(TAG_OFFICE_META); + if (nodeList.getLength()>0){ + tmpNode = newDoc.importNode(nodeList.item(0),true); + rootNode.appendChild(tmpNode); + } + }if (styleDoc !=null){ + nodeList= styleDoc.getElementsByTagName(TAG_OFFICE_STYLES); + if (nodeList.getLength()>0){ + tmpNode = newDoc.importNode(nodeList.item(0),true); + rootNode.appendChild(tmpNode); + } + + }if (settingsDoc !=null){ + nodeList= settingsDoc.getElementsByTagName(TAG_OFFICE_SETTINGS); + if (nodeList.getLength()>0){ + tmpNode = newDoc.importNode(nodeList.item(0),true); + rootNode.appendChild(tmpNode); + } + } + if (contentDoc !=null){ + nodeList= contentDoc.getElementsByTagName(TAG_OFFICE_AUTOMATIC_STYLES); + if (nodeList.getLength()>0){ + tmpNode = newDoc.importNode(nodeList.item(0),true); + rootNode.appendChild(tmpNode); + } + + nodeList= contentDoc.getElementsByTagName(TAG_OFFICE_BODY); + if (nodeList.getLength()>0){ + tmpNode = newDoc.importNode(nodeList.item(0),true); + rootNode.appendChild(tmpNode); + } + } + + byte contentBytes[] = docToBytes(newDoc); + //System.out.println(new String(contentBytes)); + os.write(contentBytes); + } + catch(Exception exc){ + System.out.println("\nException in OfficeDocument.write():" +exc); + } + //byte contentBytes[] = docToBytes(contentDoc); + } + } + + + /** + * <p>Write out a <code>org.w3c.dom.Document</code> object into a + * <code>byte</code> array.</p> + * + * <p>TODO: remove dependency on com.sun.xml.tree.XmlDocument + * package!</p> + * + * @param Document DOM <code>Document</code> object. + * + * @return <code>byte</code> array of DOM <code>Document</code> + * object. + * + * @throws IOException If any I/O error occurs. + */ + static byte[] docToBytes(Document doc) + throws IOException { + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + java.lang.reflect.Constructor con; + java.lang.reflect.Method meth; + + String domImpl = doc.getClass().getName(); + + /* + * We may have multiple XML parsers in the Classpath. + * Depending on which one is first, the actual type of + * doc may vary. Need a way to find out which API is being + * used and use an appropriate serialization method. + */ + + try { + // First of all try for JAXP 1.0 + if (domImpl.equals("com.sun.xml.tree.XmlDocument")) { + + Debug.log(Debug.INFO, "Using JAXP"); + + Class jaxpDoc = Class.forName("com.sun.xml.tree.XmlDocument"); + + // The method is in the XMLDocument class itself, not a helper + meth = jaxpDoc.getMethod("write", + new Class[] { Class.forName("java.io.OutputStream") } ); + + meth.invoke(doc, new Object [] { baos } ); + } + else if (domImpl.equals("org.apache.crimson.tree.XmlDocument")) + { + Debug.log(Debug.INFO, "Using Crimson"); + + Class crimsonDoc = Class.forName("org.apache.crimson.tree.XmlDocument"); + // The method is in the XMLDocument class itself, not a helper + meth = crimsonDoc.getMethod("write", + new Class[] { Class.forName("java.io.OutputStream") } ); + + meth.invoke(doc, new Object [] { baos } ); + } + else if (domImpl.equals("org.apache.xerces.dom.DocumentImpl") + || domImpl.equals("org.apache.xerces.dom.DeferredDocumentImpl")) { + + Debug.log(Debug.INFO, "Using Xerces"); + + // Try for Xerces + Class xercesSer = + Class.forName("org.apache.xml.serialize.XMLSerializer"); + + // Get the OutputStream constructor + // May want to use the OutputFormat parameter at some stage too + con = xercesSer.getConstructor(new Class [] + { Class.forName("java.io.OutputStream"), + Class.forName("org.apache.xml.serialize.OutputFormat") } ); + + + // Get the serialize method + meth = xercesSer.getMethod("serialize", + new Class [] { Class.forName("org.w3c.dom.Document") } ); + + + // Get an instance + Object serializer = con.newInstance(new Object [] { baos, null } ); + + + // Now call serialize to write the document + meth.invoke(serializer, new Object [] { doc } ); + } + else if (domImpl.equals("gnu.xml.dom.DomDocument")) { + Debug.log(Debug.INFO, "Using GNU"); + + Class gnuSer = Class.forName("gnu.xml.dom.ls.DomLSSerializer"); + + // Get the serialize method + meth = gnuSer.getMethod("serialize", + new Class [] { Class.forName("org.w3c.dom.Node"), + Class.forName("java.io.OutputStream") } ); + + // Get an instance + Object serializer = gnuSer.newInstance(); + + // Now call serialize to write the document + meth.invoke(serializer, new Object [] { doc, baos } ); + } + else { + try { + DOMSource domSource = new DOMSource(doc); + StringWriter writer = new StringWriter(); + StreamResult result = new StreamResult(writer); + TransformerFactory tf = TransformerFactory.newInstance(); + Transformer transformer = tf.newTransformer(); + transformer.transform(domSource, result); + return writer.toString().getBytes(); + } + catch (Exception e) { + // We don't have another parser + throw new IOException("No appropriate API (JAXP/Xerces) to serialize XML document: " + domImpl); + } + } + } + catch (ClassNotFoundException cnfe) { + throw new IOException(cnfe.toString()); + } + catch (Exception e) { + // We may get some other errors, but the bottom line is that + // the steps being executed no longer work + throw new IOException(e.toString()); + } + + byte bytes[] = baos.toByteArray(); + + return bytes; + } + + + /** + * Initializes a new DOM <code>Document</code> with the content + * containing minimum OpenOffice XML tags. + * + * @throws IOException If any I/O error occurs. + */ + public final void initContentDOM() throws IOException { + + contentDoc = createDOM(TAG_OFFICE_DOCUMENT_CONTENT); + + // this is a work-around for a bug in Office6.0 - not really + // needed but StarCalc 6.0 will crash without this tag. + Element root = contentDoc.getDocumentElement(); + + Element child = contentDoc.createElement(TAG_OFFICE_FONT_DECLS); + root.appendChild(child); + + child = contentDoc.createElement(TAG_OFFICE_AUTOMATIC_STYLES); + root.appendChild(child); + + child = contentDoc.createElement(TAG_OFFICE_BODY); + root.appendChild(child); + } + + /** + * Initializes a new DOM <code>Document</code> with the content + * containing minimum OpenOffice XML tags. + * + * @throws IOException If any I/O error occurs. + */ + public final void initSettingsDOM() throws IOException { + + settingsDoc = createSettingsDOM(TAG_OFFICE_DOCUMENT_SETTINGS); + + // this is a work-around for a bug in Office6.0 - not really + // needed but StarCalc 6.0 will crash without this tag. + Element root = settingsDoc.getDocumentElement(); + + Element child = settingsDoc.createElement(TAG_OFFICE_SETTINGS); + root.appendChild(child); + } + + /** + * Initializes a new DOM Document with styles + * containing minimum OpenOffice XML tags. + * + * @throws IOException If any I/O error occurs. + */ + public final void initStyleDOM() throws IOException { + + styleDoc = createDOM(TAG_OFFICE_DOCUMENT_STYLES); + } + + /** + * <p>Creates a new DOM <code>Document</code> containing minimum + * OpenOffice XML tags.</p> + * + * <p>This method uses the subclass + * <code>getOfficeClassAttribute</code> method to get the + * attribute for <i>office:class</i>.</p> + * + * @param rootName root name of <code>Document</code>. + * + * @throws IOException If any I/O error occurs. + */ + private final Document createSettingsDOM(String rootName) throws IOException { + + Document doc = null; + + try { + + DocumentBuilder builder = factory.newDocumentBuilder(); + doc = builder.newDocument(); + + } catch (ParserConfigurationException ex) { + + throw new OfficeDocumentException(ex); + + } + + Element root = (Element) doc.createElement(rootName); + doc.appendChild(root); + + root.setAttribute("xmlns:office", "http://openoffice.org/2000/office"); + root.setAttribute("xmlns:xlink", "http://openoffice.org/1999/xlink"); + root.setAttribute("xmlns:config", "http://openoffice.org/2001/config"); + root.setAttribute("office:version", "1.0"); + + return doc; + } + + + /** + * <p>Creates a new DOM <code>Document</code> containing minimum + * OpenOffice XML tags.</p> + * + * <p>This method uses the subclass + * <code>getOfficeClassAttribute</code> method to get the + * attribute for <i>office:class</i>.</p> + * + * @param rootName root name of <code>Document</code>. + * + * @throws IOException If any I/O error occurs. + */ + private final Document createDOM(String rootName) throws IOException { + + Document doc = null; + + try { + + DocumentBuilder builder = factory.newDocumentBuilder(); + doc = builder.newDocument(); + + } catch (ParserConfigurationException ex) { + + throw new OfficeDocumentException(ex); + + } + + Element root = (Element) doc.createElement(rootName); + doc.appendChild(root); + + root.setAttribute("xmlns:office", "http://openoffice.org/2000/office"); + root.setAttribute("xmlns:style", "http://openoffice.org/2000/style"); + root.setAttribute("xmlns:text", "http://openoffice.org/2000/text"); + root.setAttribute("xmlns:table", "http://openoffice.org/2000/table"); + root.setAttribute("xmlns:draw", "http://openoffice.org/2000/drawing"); + root.setAttribute("xmlns:fo", "http://www.w3.org/1999/XSL/Format"); + root.setAttribute("xmlns:xlink", "http://www.w3.org/1999/xlink"); + root.setAttribute("xmlns:number", "http://openoffice.org/2000/datastyle"); + root.setAttribute("xmlns:svg", "http://www.w3.org/2000/svg"); + root.setAttribute("xmlns:chart", "http://openoffice.org/2000/chart"); + root.setAttribute("xmlns:dr3d", "http://openoffice.org/2000/dr3d"); + root.setAttribute("xmlns:math", "http://www.w3.org/1998/Math/MathML"); + root.setAttribute("xmlns:form", "http://openoffice.org/2000/form"); + root.setAttribute("xmlns:script", "http://openoffice.org/2000/script"); + root.setAttribute("office:class", getOfficeClassAttribute()); + root.setAttribute("office:version", "1.0"); + + return doc; + } + + + /** + * Return the <i>office:class</i> attribute value. + * + * @return The attribute value. + */ + protected abstract String getOfficeClassAttribute(); + + + /** + * <p>Hacked code to filter <!DOCTYPE> tag before + * sending stream to parser.</p> + * + * <p>This hacked code needs to be changed later on.</p> + * + * <p>Issue: using current jaxp1.0 parser, there is no way + * to turn off processing of dtds. Current set of dtds + * have bugs, processing them will throw exceptions.</p> + * + * <p>This is a simple hack that assumes the whole <!DOCTYPE> + * tag are all in the same line. This is sufficient for + * current StarOffice 6.0 generated XML files. Since this + * hack really needs to go away, I don't want to spend + * too much time in making it a perfect hack.</p> + * FIX (HJ): Removed requirement for DOCTYPE to be in one line + * FIX (HJ): No longer removes newlines + * + * @param is <code>InputStream</code> to be filtered. + * + * @return Reader value without the <!DOCTYPE> tag. + * + * @throws IOException If any I/O error occurs. + */ + private static Reader hack(InputStream is) throws IOException { + + BufferedReader br = new BufferedReader(new InputStreamReader(is, "UTF-8")); + StringBuffer buffer = new StringBuffer(); + + String str = null; + + while ((str = br.readLine()) != null) { + + int sIndex = str.indexOf("<!DOCTYPE"); + + if (sIndex > -1) { + + buffer.append(str.substring(0, sIndex)); + + int eIndex = str.indexOf('>', sIndex + 8 ); + + if (eIndex > -1) { + + buffer.append(str.substring(eIndex + 1, str.length())); + // FIX (HJ): Preserve the newline + buffer.append("\n"); + + } else { + + // FIX (HJ): More than one line. Search for '>' in following lines + boolean bOK = false; + while ((str = br.readLine())!=null) { + eIndex = str.indexOf('>'); + if (eIndex>-1) { + buffer.append(str.substring(eIndex+1)); + // FIX (HJ): Preserve the newline + buffer.append("\n"); + bOK = true; + break; + } + } + + if (!bOK) { throw new IOException("Invalid XML"); } + } + + } else { + + buffer.append(str); + // FIX (HJ): Preserve the newline + buffer.append("\n"); + } + } + + StringReader r = new StringReader(buffer.toString()); + return r; + } + + /** + * <p>Transform the InputStream to a Reader Stream.</p> + * + * <p>This hacked code needs to be changed later on.</p> + * + * <p>Issue: the new oasis input file stream means + * that the old input stream fails. see #i33702# </p> + * + * @param is <code>InputStream</code> to be filtered. + * + * @return Reader value of the InputStream(). + * + * @throws IOException If any I/O error occurs. + */ + private static Reader secondHack(InputStream is) throws IOException { + + BufferedReader br = new BufferedReader(new InputStreamReader(is, "UTF-8")); + char[] charArray = new char[4096]; + StringBuffer sBuf = new StringBuffer(); + int n = 0; + while ((n=br.read(charArray, 0, charArray.length)) > 0) + sBuf.append(charArray, 0, n); + + // ensure there is no trailing garbage after the end of the stream. + int sIndex = sBuf.lastIndexOf("</office:document>"); + sBuf.delete(sIndex, sBuf.length()); + sBuf.append("</office:document>"); + StringReader r = new StringReader(sBuf.toString()); + return r; + } + + + /** + * Method to create the initial entries in the manifest.xml file stored + * in an SX? file. + */ + private void initManifestDOM() throws IOException { + + try { + DocumentBuilder builder = factory.newDocumentBuilder(); + DOMImplementation domImpl = builder.getDOMImplementation(); + + DocumentType docType = domImpl.createDocumentType(TAG_MANIFEST_ROOT, + "-//OpenOffice.org//DTD Manifest 1.0//EN", + "Manifest.dtd"); + manifestDoc = domImpl.createDocument("manifest", TAG_MANIFEST_ROOT, docType); + } catch (ParserConfigurationException ex) { + throw new OfficeDocumentException(ex); + } + + // Add the <manifest:manifest> entry + Element manifestRoot = manifestDoc.getDocumentElement(); + + manifestRoot.setAttribute("xmlns:manifest", "http://openoffice.org/2001/manifest"); + + Element docRoot = manifestDoc.createElement(TAG_MANIFEST_FILE); + + docRoot.setAttribute(ATTRIBUTE_MANIFEST_FILE_PATH, "/"); + docRoot.setAttribute(ATTRIBUTE_MANIFEST_FILE_TYPE, getDocumentMimeType()); + + manifestRoot.appendChild(docRoot); + } +} + diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/OfficeDocumentException.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/OfficeDocumentException.java new file mode 100644 index 000000000000..b6a77d6d57f7 --- /dev/null +++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/OfficeDocumentException.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: OfficeDocumentException.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; + +import java.io.IOException; + +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; + +import org.openoffice.xmerge.util.Resources; + +/** + * Used by OfficeDocument to encapsulate exceptions. It will add + * more details to the message string if it is of type + * <code>SAXParseException</code>. + * + * @author Herbie Ong + */ + +public final class OfficeDocumentException extends IOException { + + StringBuffer message = null; + + + /** + * Constructor, capturing additional information from the + * <code>SAXException</code>. + * + * @param e The <code>SAXException</code>. + */ + public OfficeDocumentException(SAXException e) { + super(e.toString()); + message = new StringBuffer(); + if (e instanceof SAXParseException) { + String msgParseError = + Resources.getInstance().getString("PARSE_ERROR"); + String msgLine = + Resources.getInstance().getString("LINE"); + String msgColumn = + Resources.getInstance().getString("COLUMN"); + String msgPublicId = + Resources.getInstance().getString("PUBLIC_ID"); + String msgSystemId = + Resources.getInstance().getString("SYSTEM_ID"); + SAXParseException spe = (SAXParseException) e; + message.append(msgParseError); + message.append(": "); + message.append(msgLine); + message.append(": "); + message.append(spe.getLineNumber()); + message.append(", "); + message.append(msgColumn); + message.append(": "); + message.append(spe.getColumnNumber()); + message.append(", "); + message.append(msgSystemId); + message.append(": "); + message.append(spe.getSystemId()); + message.append(", "); + message.append(msgPublicId); + message.append(": "); + message.append(spe.getPublicId()); + message.append("\n"); + } + + // if there exists an embedded exception + Exception ex = e.getException(); + if (ex != null) { + message.append(ex.getMessage()); + } + } + + + /** + * Constructor, creates exception with provided message. + * + * @param s Message value for the exception. + */ + public OfficeDocumentException(String s) { + super(s); + } + + + /** + * Constructor, creates exception with the message + * corresponding to the message value of the provided + * exception. + * + * @param e The Exception. + */ + public OfficeDocumentException(Exception e) { + super(e.getMessage()); + } + + + /** + * Returns the message value for the <code>Exception</code>. + * + * @return The message value for the <code>Exception</code>. + */ + public String getMessage() { + return message.toString() + super.getMessage(); + } +} + diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/OfficeZip.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/OfficeZip.java new file mode 100644 index 000000000000..1ce1e3bba90a --- /dev/null +++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/OfficeZip.java @@ -0,0 +1,461 @@ +/************************************************************************* + * + * 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: OfficeZip.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; + +import java.util.List; +import java.util.ListIterator; +import java.util.LinkedList; +import java.util.zip.ZipInputStream; +import java.util.zip.ZipOutputStream; +import java.util.zip.ZipEntry; +import java.util.zip.CRC32; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; +import java.io.ByteArrayOutputStream; + +import org.openoffice.xmerge.util.Debug; + +/** + * Class used by {@link + * org.openoffice.xmerge.converter.OfficeDocument + * OfficeDocument} to handle reading and writing + * from a ZIP file, as well as storing ZIP entries. + * + * @author Herbie Ong + */ +class OfficeZip { + + /** File name of the XML file in a zipped document. */ + private final static String CONTENTXML = "content.xml"; + + private final static String STYLEXML = "styles.xml"; + private final static String METAXML = "meta.xml"; + private final static String SETTINGSXML = "settings.xml"; + private final static String MANIFESTXML = "META-INF/manifest.xml"; + + private final static int BUFFERSIZE = 1024; + + private List entryList = null; + + private int contentIndex = -1; + private int styleIndex = -1; + private int metaIndex = -1; + private int settingsIndex = -1; + private int manifestIndex = -1; + + /** Default constructor. */ + OfficeZip() { + + entryList = new LinkedList(); + } + + + /** + * <p>Read each zip entry in the <code>InputStream</code> object + * and store in entryList both the <code>ZipEntry</code> object + * as well as the bits of each entry. Call this method before + * calling the <code>getContentXMLBytes</code> method or the + * <code>getStyleXMLBytes</code> method.</p> + * + * <p>Keep track of the CONTENTXML and STYLEXML using + * contentIndex and styleIndex, respectively.</p> + * + * @param is <code>InputStream</code> object to read. + * + * @throws IOException If any I/O error occurs. + */ + void read(InputStream is) throws IOException { + + ZipInputStream zis = new ZipInputStream(is); + ZipEntry ze = null; + int i = -1; + + while ((ze = zis.getNextEntry()) != null) { + + String name = ze.getName(); + + Debug.log(Debug.TRACE, "reading entry: " + name); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + int len = 0; + byte buffer[] = new byte[BUFFERSIZE]; + + while ((len = zis.read(buffer)) > 0) { + baos.write(buffer, 0, len); + } + + byte bytes[] = baos.toByteArray(); + Entry entry = new Entry(ze,bytes); + + entryList.add(entry); + + i++; + + if (name.equalsIgnoreCase(CONTENTXML)) { + contentIndex = i; + } + else if (name.equalsIgnoreCase(STYLEXML)) { + styleIndex = i; + } + else if (name.equalsIgnoreCase(METAXML)) { + metaIndex = i; + } + else if (name.equalsIgnoreCase(SETTINGSXML)) { + settingsIndex = i; + } + else if (name.equalsIgnoreCase(MANIFESTXML)) { + manifestIndex = i; + } + + } + + zis.close(); + } + + + /** + * This method returns the CONTENTXML file in a + * <code>byte</code> array. It returns null if there is no + * CONTENTXML in this zip file. + * + * @return CONTENTXML in a <code>byte</code> array. + */ + byte[] getContentXMLBytes() { + + return getEntryBytes(contentIndex); + } + + + /** + * This method returns the STYLEXML file in a + * <code>byte</code> array. It returns null if there is + * no STYLEXML in this zip file. + * + * @return STYLEXML in a <code>byte</code> array. + */ + byte[] getStyleXMLBytes() { + + return getEntryBytes(styleIndex); + } + + /** + * This method returns the METAXML file in a + * <code>byte</code> array. It returns null if there is + * no METAXML in this zip file. + * + * @return METAXML in a <code>byte</code> array. + */ + byte[] getMetaXMLBytes() { + return getEntryBytes(metaIndex); + } + + /** + * This method returns the SETTINGSXML file in a + * <code>byte</code> array. It returns null if there is + * no SETTINGSXML in this zip file. + * + * @return SETTINGSXML in a <code>byte</code> array. + */ + byte[] getSettingsXMLBytes() { + return getEntryBytes(settingsIndex); + } + + /** + * This method returns the MANIFESTXML file in a <code>byte</code> array. + * It returns null if there is no MANIFESTXML in this zip file. + * + * @return MANIFESTXML in a <code>byte</code> array. + */ + byte[] getManifestXMLBytes() { + return getEntryBytes(manifestIndex); + } + + /** + * This method returns the bytes corresponding to the entry named in the + * parameter. + * + * @param name The name of the entry in the Zip file to retrieve. + * + * @return The data for the named entry in a <code>byte</code> array or + * <code>null</code> if no entry is found. + */ + byte[] getNamedBytes(String name) { + + // The list is not sorted, and sorting it for a binary search would + // invalidate the indices stored for the main files. + + // Could improve performance by caching the name and index when + // iterating through the ZipFile in read(). + for (int i = 0; i < entryList.size(); i++) { + Entry e = (Entry)entryList.get(i); + + if (e.zipEntry.getName().equals(name)) { + return getEntryBytes(i); + } + } + + return null; + } + + + /** + * This method sets the bytes for the named entry. It searches for a + * matching entry in the LinkedList. If no entry is found, a new one is + * created. + * + * Writing of data is defferred to setEntryBytes(). + * + * @param name The name of the entry to search for. + * @param bytes The new data to write. + */ + void setNamedBytes(String name, byte[] bytes) { + for (int i = 0; i < entryList.size(); i++) { + Entry e = (Entry)entryList.get(i); + + if (e.zipEntry.getName().equals(name)) { + setEntryBytes(i, bytes, name); + return; + } + } + + // If we're here, no entry was found. Call setEntryBytes with an index + // of -1 to insert a new entry. + setEntryBytes(-1, bytes, name); + } + + /** + * Used by the <code>getContentXMLBytes</code> method and the + * <code>getStyleXMLBytes</code> method to return the + * <code>byte</code> array from the corresponding + * <code>entry</code> in the <code>entryList</code>. + * + * @param index Index of <code>Entry</code> object in + * <code>entryList</code>. + * + * @return <code>byte</code> array associated in that + * <code>Entry</code> object or null, if there is + * not such <code>Entry</code>. + */ + private byte[] getEntryBytes(int index) { + + byte[] bytes = null; + + if (index > -1) { + Entry entry = (Entry) entryList.get(index); + bytes = entry.bytes; + } + return bytes; + } + + + /** + * Set or replace the <code>byte</code> array for the + * CONTENTXML file. + * + * @param bytes <code>byte</code> array for the + * CONTENTXML file. + */ + void setContentXMLBytes(byte bytes[]) { + + contentIndex = setEntryBytes(contentIndex, bytes, CONTENTXML); + } + + + /** + * Set or replace the <code>byte</code> array for the + * STYLEXML file. + * + * @param bytes <code>byte</code> array for the + * STYLEXML file. + */ + void setStyleXMLBytes(byte bytes[]) { + + styleIndex = setEntryBytes(styleIndex, bytes, STYLEXML); + } + + + /** + * Set or replace the <code>byte</code> array for the + * METAXML file. + * + * @param bytes <code>byte</code> array for the + * METAXML file. + */ + void setMetaXMLBytes(byte bytes[]) { + + metaIndex = setEntryBytes(metaIndex, bytes, METAXML); + } + + + /** + * Set or replace the <code>byte</code> array for the + * SETTINGSXML file. + * + * @param bytes <code>byte</code> array for the + * SETTINGSXML file. + */ + void setSettingsXMLBytes(byte bytes[]) { + + settingsIndex = setEntryBytes(settingsIndex, bytes, SETTINGSXML); + } + + + /** + * Set or replace the <code>byte</code> array for the MANIFESTXML file. + * + * @param bytes <code>byte</code> array for the MANIFESTXML file. + */ + void setManifestXMLBytes(byte bytes[]) { + manifestIndex = setEntryBytes(manifestIndex, bytes, MANIFESTXML); + } + + /** + * <p>Used by the <code>setContentXMLBytes</code> method and + * the <code>setStyleXMLBytes</code> to either replace an + * existing <code>Entry</code>, or create a new entry in + * <code>entryList</code>.</p> + * + * <p>If there is an <code>Entry</code> object within + * entryList that corresponds to the index, replace the + * <code>ZipEntry</code> info.</p> + * + * @param index Index of <code>Entry</code> to modify. + * @param bytes <code>Entry</code> value. + * @param name Name of <code>Entry</code>. + * + * @return Index of value added or modified in entryList + */ + private int setEntryBytes(int index, byte bytes[], String name) { + + if (index > -1) { + + // replace existing entry in entryList + + Entry entry = (Entry) entryList.get(index); + name = entry.zipEntry.getName(); + int method = entry.zipEntry.getMethod(); + + ZipEntry ze = createZipEntry(name, bytes, method); + + entry.zipEntry = ze; + entry.bytes= bytes; + + } else { + + // add a new entry into entryList + ZipEntry ze = createZipEntry(name, bytes, ZipEntry.DEFLATED); + Entry entry = new Entry(ze, bytes); + entryList.add(entry); + index = entryList.size() - 1; + } + + return index; + } + + + /** + * Write out the ZIP entries into the <code>OutputStream</code> + * object. + * + * @param os <code>OutputStream</code> object to write ZIP. + * + * @throws IOException If any ZIP I/O error occurs. + */ + void write(OutputStream os) throws IOException { + + Debug.log(Debug.TRACE, "Writing out the following entries into zip."); + + ZipOutputStream zos = new ZipOutputStream(os); + + ListIterator iterator = entryList.listIterator(); + + while (iterator.hasNext()) { + + Entry entry = (Entry) iterator.next(); + ZipEntry ze = entry.zipEntry; + + String name = ze.getName(); + + Debug.log(Debug.TRACE, "... " + name); + + zos.putNextEntry(ze); + zos.write(entry.bytes); + } + + zos.close(); + } + + + /** + * Creates a <code>ZipEntry</code> object based on the given params. + * + * @param name Name for the <code>ZipEntry</code>. + * @param bytes <code>byte</code> array for <code>ZipEntry</code>. + * @param method ZIP method to be used for <code>ZipEntry</code>. + * + * @return A <code>ZipEntry</code> object. + */ + private ZipEntry createZipEntry(String name, byte bytes[], int method) { + + ZipEntry ze = new ZipEntry(name); + + ze.setMethod(method); + ze.setSize(bytes.length); + + CRC32 crc = new CRC32(); + crc.reset(); + crc.update(bytes); + ze.setCrc(crc.getValue()); + + ze.setTime(System.currentTimeMillis()); + + return ze; + } + + /** + * This inner class is used as a data structure for holding + * a <code>ZipEntry</code> info and its corresponding bytes. + * These are stored in entryList. + */ + private class Entry { + + ZipEntry zipEntry = null; + byte bytes[] = null; + + Entry(ZipEntry zipEntry, byte bytes[]) { + this.zipEntry = zipEntry; + this.bytes = bytes; + } + } +} + diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/ParaStyle.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/ParaStyle.java new file mode 100644 index 000000000000..69d4f66f1164 --- /dev/null +++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/ParaStyle.java @@ -0,0 +1,604 @@ +/************************************************************************* + * + * 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: ParaStyle.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; + +import org.w3c.dom.NodeList; +import org.w3c.dom.Node; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Element; + +import org.openoffice.xmerge.util.Debug; + + +abstract class conversionAlgorithm { + int I(String val) { + return 0; + } +} + + /* + * This algorithm expects only values in millimeters, e.g. "20.3mm". + */ +class horizSize extends conversionAlgorithm { + int I(String value) { + if (value.endsWith("mm")) { + float size = (float)0.0; + String num = value.substring(0, value.length() - 2); + try { + size = Float.parseFloat(num); + } catch (Exception e) { + Debug.log(Debug.ERROR, "Error parsing " + value, e); + } + size *= 100; + return (int)size; + } else { + Debug.log(Debug.ERROR, "Unexpected value (" + value + + ") in horizSize.I()"); + return 0; + } + } +} + + +/* + * This algorithm does line height - can be either millimeters or + * a percentage. + */ +class lineHeight extends conversionAlgorithm { + int I(String value) { + if (value.endsWith("mm")) { + float size = (float)0.0; + String num = value.substring(0, value.length() - 2); + try { + size = Float.parseFloat(num); + } catch (Exception e) { + Debug.log(Debug.ERROR, "Error parsing " + value, e); + } + size *= 100; + return (int)size; + } else if (value.endsWith("%")) { + float size = (float)0.0; + String num = value.substring(0, value.length() - 1); + try { + size = Float.parseFloat(num); + } catch (Exception e) { + Debug.log(Debug.ERROR, "Error parsing " + value, e); + } + int retval = (int) size; + retval |= ParaStyle.LH_PCT; + return retval; + } + return 0; + } +} + + +/** + * This class converts alignment values. + */ +class alignment extends conversionAlgorithm { + int I(String value) { + if (value.equals("end")) + return ParaStyle.ALIGN_RIGHT; + if (value.equals("right")) + return ParaStyle.ALIGN_RIGHT; + if (value.equals("center")) + return ParaStyle.ALIGN_CENTER; + if (value.equals("justify")) + return ParaStyle.ALIGN_JUST; + if (value.equals("justified")) + return ParaStyle.ALIGN_JUST; + if (value.equals("start")) + return ParaStyle.ALIGN_LEFT; + if (value.equals("left")) + return ParaStyle.ALIGN_LEFT; + Debug.log(Debug.ERROR, "Unknown string (" + + value + ") in alignment.I()"); + return ParaStyle.ALIGN_LEFT; + } +} + + +/** + * <p>This class represents a paragraph <code>Style</code>.</p> + * + * <p><table border="1" cellpadding="1"><tr><td> + * Attribute </td><td>Value + * </td></tr><tr><td> + * MARGIN_LEFT </td><td>mm * 100 + * </td></tr><tr><td> + * MARGIN_RIGHT </td><td>mm * 100 + * </td></tr><tr><td> + * MARGIN_TOP </td><td>mm * 100 (space on top of paragraph) + * </td></tr><tr><td> + * MARGIN_BOTTOM </td><td>mm * 100 + * </td></tr><tr><td> + * TEXT_INDENT </td><td>mm * 100 (first line indent) + * </td></tr><tr><td> + * LINE_HEIGHT </td><td>mm * 100, unless or'ed with LH_PCT, in which + * case it is a percentage (e.g. 200% for double spacing) + * Can also be or'ed with LH_ATLEAST. Value is stored + * in bits indicated by LH_VALUEMASK. + * </td></tr><tr><td> + * TEXT_ALIGN </td><td>ALIGN_RIGHT, ALIGN_CENTER, ALIGN_JUST, ALIGN_LEFT + * </td></tr></table></p> + * + * @author David Proulx + */ +public class ParaStyle extends Style implements Cloneable { + + /** The left margin property. */ + public static final int MARGIN_LEFT = 0; + /** The right margin property. */ + public static final int MARGIN_RIGHT = 1; + /** The top margin property. */ + public static final int MARGIN_TOP = 2; + /** The bottom margin property. */ + public static final int MARGIN_BOTTOM = 3; + /** Indent left property. */ + public static final int TEXT_INDENT = 4; + /** Indent right property. */ + public static final int LINE_HEIGHT = 5; + /** Align text property. */ + public static final int TEXT_ALIGN = 6; + // This must always be one more than highest property + /** Total number of properties. */ + protected static final int NR_PROPERTIES = 7; + + /** + * Array of flags indicating which attributes are set for this + * paragraph <code>Style</code>. + */ + protected boolean isSet[] = new boolean[NR_PROPERTIES]; + /** Array of attribute values for this paragraph <code>tyle</code>. */ + protected int value[] = new int[NR_PROPERTIES]; + /** Array of attribute names for this paragraph <code>Style</code>. */ + protected String attrName[] = { + "fo:margin-left", + "fo:margin-right", + "fo:margin-top", + "fo:margin-bottom", + "fo:text-indent", + "fo:line-height", + "fo:text-align" + }; + + /** Array of attribute structures for this paragraph <code>Style</code>. */ + protected Class algor[] = { + horizSize.class, + horizSize.class, + horizSize.class, + horizSize.class, + horizSize.class, + lineHeight.class, + alignment.class + }; + + /** Align right. */ + public static final int ALIGN_RIGHT = 1; + /** Align center. */ + public static final int ALIGN_CENTER = 2; + /** Align justified. */ + public static final int ALIGN_JUST = 3; + /** Align left. */ + public static final int ALIGN_LEFT = 4; + + /** Line height percentage. */ + public static final int LH_PCT = 0x40000000; + /** Line height minimum value. */ + public static final int LH_ATLEAST = 0x20000000; + /** Line height mask. */ + public static final int LH_VALUEMASK = 0x00FFFFFF; + + /** Ignored tags. */ + private static String[] ignored = { + "style:font-name", "fo:font-size", "fo:font-weight", "fo:color", + "fo:language", "fo:country", "style:font-name-asian", + "style:font-size-asian", "style:language-asian", + "style:country-asian", "style:font-name-complex", + "style:font-size-complex", "style:language-complex", + "style:country-complex", "style:text-autospace", "style:punctuation-wrap", + "style:line-break", "fo:keep-with-next", "fo:font-style", + "text:number-lines", "text:line-number" + }; + + + /** + * Constructor for use when going from DOM to client device format. + * + * @param node A <i>style:style</i> <code>Node</code> which, which + * is assumed to have <i>family</i> attribute of + * <i>paragraph</i>. + * @param sc The <code>StyleCatalog</code>, which is used for + * looking up ancestor <code>Style</code> objects. + */ + public ParaStyle(Node node, StyleCatalog sc) { + + super(node, sc); + + // 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); + setAttribute(attr.getNodeName(), attr.getNodeValue()); + } + } + } + } + } + } + + + /** + * Constructor for use when going from client device format to DOM. + * + * @param name Name of the <code>Style</code>. Can be null. + * @param family Family of the <code>Style</code> - usually + * <i>paragraph</i>, <i>text</i>, etc. Can be null. + * @param parent Name of the parent <code>Style</code>, or null + * if none. + * @param attribs Array of attributes to set. + * @param values Array of values to set. + * @param sc The <code>StyleCatalog</code>, which is used for + * looking up ancestor <code>Style</code> objects. + */ + public ParaStyle(String name, String familyName, String parentName, + String attribs[], String values[], StyleCatalog sc) { + super(name, familyName, parentName, sc); + if (attribs != null) + for (int i = 0; i < attribs.length; i++) + setAttribute(attribs[i], values[i]); + } + + + /** + * Alternate constructor for use when going from client device + * format to DOM. + * + * @param name Name of the <code>Style</code>. Can be null. + * @param family Family of the <code>Style</code> - usually + * <i>paragraph</i>, <i>text</i>, etc. Can be null. + * @param parent Name of the parent <code>Style</code>, or + * null if none. + * @param attribs Array of attributes indices to set. + * @param values Array of values to set. + * @param sc The <code>StyleCatalog</code>, which is used for + * looking up ancestor <code>Style</code> objects. + */ + public ParaStyle(String name, String familyName, String parentName, + int attribs[], String values[], StyleCatalog lookup) { + super(name, familyName, parentName, lookup); + if (attribs != null) + for (int i = 0; i < attribs.length; i++) + setAttribute(attribs[i], values[i]); + } + + + /** + * This code checks whether an attribute is one that we + * intentionally ignore. + * + * @param attribute The attribute to check. + * + * @return true if attribute can be ignored, false otherwise. + */ + private boolean isIgnored(String attribute) { + for (int i = 0; i < ignored.length; i++) { + if (ignored[i].equals(attribute)) + return true; + } + return false; + } + + + /** + * Set an attribute for this paragraph <code>Style</code>. + * + * @param attr The attribute to set. + * @param value The attribute value to set. + */ + public void setAttribute(String attr, String value) { + for (int i = 0; i < NR_PROPERTIES; i++) { + if (attr.equals(attrName[i])) { + setAttribute(i, value); + return; + } + } + if (!isIgnored(attr)) + Debug.log(Debug.INFO, "ParaStyle Unhandled: " + attr + "=" + value); + } + + + /** + * Check whether an attribute is set in this <code>Style</code>. + * + * @param attrIndex The attribute index to check. + * + * @return true if the attribute at specified index is set, + * false otherwise. + */ + public boolean isAttributeSet(int attrIndex) { + return isSet[attrIndex]; + } + + + /** + * Get the value of an integer attribute. + * + * @param attrIndex Index of the attribute. + * + * @return Value of the attribute, 0 if not set. + */ + public int getAttribute(int attrIndex) { + if (isSet[attrIndex]) + return value[attrIndex]; + else return 0; + } + + + /** + * Set an attribute for this paragraph <code>Style</code>. + * + * @param attr The attribute index to set. + * @apram value The attribute value to set. + */ + public void setAttribute(int attr, String value) { + isSet[attr] = true; + try { + this.value[attr] = (((conversionAlgorithm)algor[attr].newInstance())).I(value); + } catch (Exception e) { + Debug.log(Debug.ERROR, "Instantiation error", e); + } + } + + + /** + * Return the <code>Style</code> in use. + * + * @return The fully-resolved copy of the <code>Style</code> in use. + */ + public Style getResolved() { + ParaStyle resolved = null; + try { + resolved = (ParaStyle)this.clone(); + } catch (Exception e) { + Debug.log(Debug.ERROR, "Can't clone", e); + } + + // Look up the parent style. (If there is no style catalog + // specified, we can't do any lookups). + ParaStyle parentStyle = null; + if (sc != null) { + if (parent != null) { + parentStyle = (ParaStyle)sc.lookup(parent, family, null, + this.getClass()); + if (parentStyle == null) + Debug.log(Debug.ERROR, "parent style lookup of " + + parent + " failed!"); + else + parentStyle = (ParaStyle)parentStyle.getResolved(); + } else if (!name.equals("DEFAULT_STYLE")) { + parentStyle = (ParaStyle)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 = (ParaStyle)parentStyle.getResolved(); + for (int i = 0; i < NR_PROPERTIES; i++) { + if (!isSet[i] && parentStyle.isSet[i]) { + resolved.isSet[i] = true; + resolved.value[i] = parentStyle.value[i]; + } + } + } + return resolved; + } + + + /** + * Private function to return the value as an element in + * a Comma Separated Value (CSV) format. + * + * @param value The value to format. + * + * @return The formatted value. + */ + private static String toCSV(String value) { + if (value != null) + return "\"" + value + "\","; + else + return "\"\","; + } + + + /** + * Private function to return the value as a last element in + * a Comma Separated Value (CSV) format. + * + * @param value The value to format. + * + * @return The formatted value. + */ + private static String toLastCSV(String value) { + if (value != null) + return "\"" + value + "\""; + else + return "\"\""; + } + + + /** + * Print a Comma Separated Value (CSV) header line for the + * spreadsheet dump. + */ + public static void dumpHdr() { + System.out.println(toCSV("Name") + toCSV("Family") + toCSV("parent") + + toCSV("left mgn") + toCSV("right mgn") + + toCSV("top mgn") + toCSV("bottom mgn") + toCSV("txt indent") + + toCSV("line height") + toLastCSV("txt align")); + } + + + /** + * Dump this <code>Style</code> as a Comma Separated Value (CSV) + * line. + */ + public void dumpCSV() { + String attributes = ""; + for (int index = 0; index <= 6; index++) { + if (isSet[index]) { + attributes += toCSV("" + value[index]); + } + else + attributes += toCSV(null); // unspecified + } + System.out.println(toCSV(name) + toCSV(family) + toCSV(parent) + + attributes + toLastCSV(null)); + } + + + /** + * Create the <code>Node</code> with the specified elements. + * + * @parentDoc Parent <code>Document</code> of the + * <code>Node</code> to create. + * @param name Name of the <code>Node</code>. + * + * @return The 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> is a subset of the + * <code>Style</code>. + * + * @param style <code>Style</code> to check. + * + * @return true if <code>style</code> is a subset, false + * otherwise. + */ + public boolean isSubset(Style style) { + + if (!super.isSubset(style)) + return false; + if (!this.getClass().isAssignableFrom(style.getClass())) + return false; + ParaStyle ps = (ParaStyle)style; + + for (int i = 0; i < NR_PROPERTIES; i++) { + if (ps.isSet[i]) { + // if (!isSet[i]) return false; + + if (i < NR_PROPERTIES - 1) { + // Compare the actual values. We allow a margin of error + // here because the conversion loses precision. + int diff; + if (value[i] > ps.value[i]) + diff = value[i] - ps.value[i]; + else + diff = ps.value[i] - value[i]; + if (diff > 32) + return false; + } else { + if (i == TEXT_ALIGN) + if ((value[i] == 0) && (ps.value[i] == 4)) + continue; + if (value[i] != ps.value[i]) + return false; + } + } + } + return true; + } + + + /** + * Add <code>Style</code> attributes to the given + * <code>Node</code>. This may involve writing child + * <code>Node</code> objects as well. + * + * @param node The <code>Node</code> to add <code>Style</code> + * attributes. + */ + public void writeAttributes(Element node) { + for (int i = 0; i <= TEXT_INDENT; i++) { + if (isSet[i]) { + double temp = value[i] / 100.0; + String stringVal = (new Double(temp)).toString() + "mm"; + node.setAttribute(attrName[i], stringVal); + } + } + + if (isSet[LINE_HEIGHT]) { + String stringVal; + if ((value[LINE_HEIGHT] & LH_PCT) != 0) + stringVal = (new Integer(value[LINE_HEIGHT] & LH_VALUEMASK)).toString() + "%"; + else { + double temp = (value[LINE_HEIGHT] & LH_VALUEMASK) / 100.0; + stringVal = (new Double(temp)).toString() + "mm"; + } + node.setAttribute(attrName[LINE_HEIGHT], stringVal); + } + + if (isSet[TEXT_ALIGN]) { + String val; + switch (value[TEXT_ALIGN]) { + case ALIGN_RIGHT: val = "end"; break; + case ALIGN_CENTER: val = "center"; break; + case ALIGN_JUST: val = "justify"; break; + case ALIGN_LEFT: val = "left"; break; + default: val = "unknown"; break; + } + node.setAttribute(attrName[TEXT_ALIGN], val); + } + } +} + diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/Style.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/Style.java new file mode 100644 index 000000000000..6d464671f1af --- /dev/null +++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/Style.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: Style.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; + +import org.w3c.dom.Node; +import org.w3c.dom.NamedNodeMap; + +/** + * An object of class <code>Style</code> represents a <i>style</i> + * in an OpenOffice document. In practice subclasses of this + * <code>Style</code>, such as <code>TextStyle</code>, + * <code>ParaStyle</code> are used. + * + * @author David Proulx + * @see <a href="TextStyle.html">TextStyle</a>, + * <a href="ParaStyle.html">ParaStyle</a> + */ +public class Style { + + /** Name of the <code>Style</code>. */ + protected String name = null; + /** Family of the <code>Style</code>. */ + protected String family = null; + /** Parent of the <code>Style</code>. */ + protected String parent = null; + + /** + * A reference to the <code>StyleCatalog</code> to be used for + * looking up ancestor <code>Style</code> objects. + */ + protected StyleCatalog sc; + + + /** + * Constructor for use when going from DOM to client device format. + * + * @param node A <i>style:style</i> or <i>style:default-style</i> + * <code>Node</code> from the document being parsed. + * No checking of <code>Node</code> is done, so if it + * is not of the proper type the results will be + * unpredictable. + * @param sc The <code>StyleCatalog</code>, which is used for + * looking up ancestor <code>Style</code> objects. + */ + public Style(Node node, StyleCatalog sc) { + + this.sc = sc; + + // Run through the attributes of this node, saving + // the ones we're interested in. + if (node.getNodeName().equals("style:default-style")) + name = "DEFAULT_STYLE"; + NamedNodeMap attrNodes = node.getAttributes(); + if (attrNodes != null) { + int len = attrNodes.getLength(); + for (int i = 0; i < len; i++) { + Node attr = attrNodes.item(i); + if (attr.getNodeName().equals("style:family")) + family = attr.getNodeValue(); + else if (attr.getNodeName().equals("style:name")) { + name = attr.getNodeValue(); + } else if (attr.getNodeName().equals("style:parent-style-name")) + parent = attr.getNodeValue(); + + } + } + } + + + /** + * Constructor for use when going from client device format to DOM. + * + * @param name Name of the <code>Style</code>. Can be null. + * @param family Family of the <code>Style</code> - usually + * <i>paragraph</i>, <i>text</i>, etc. Can be null. + * @param parent Name of the parent <code>Style</code>, or null if none. + * @param sc The <code>StyleCatalog</code>, which is used for + * looking up ancestor <code>Style</code> objects. + */ + public Style(String name, String family, String parent, StyleCatalog sc) { + this.sc = sc; + this.name = name; + this.family = family; + this.parent = parent; + } + + + /** + * Set the <code>StyleCatalog</code> to be used when looking up the + * <code>Style</code> parent. + * + * @param sc The <code>StyleCatalog</code>, which is used for + * looking up ancestor <code>Style</code> objects. + */ + public void setCatalog(StyleCatalog sc) { + this.sc = sc; + } + + + /** + * Returns the name of this <code>Style</code>. + * + * @return The name of this <code>Style</code>. + */ + public String getName() { + return name; + } + + + /** + * Sets the name of this <code>Style</code>. + * + * @param newName The new name of this <code>Style</code>. + */ + public void setName(String newName) { + name = newName; + } + + + /** + * Return the family of this <code>Style</code>. + * + * @return The family of this <code>Style</code>. + */ + public String getFamily() { + return family; + } + + /** + * Return the name of the parent of this <code>Style</code>. + * + * @return The parent of this <code>Style</code>. + */ + public String getParent() { + return parent; + } + + + /** + * 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 A resolved <code>Style</code> object in which to look up + * ancestors. + */ + public Style getResolved() { + return new Style(name, family, parent, sc); + } + + + /** + * Write a <code>Node</code> in <code>parentDoc</code> + * representing this <code>Style</code>. Note that the + * <code>Node</code> is returned unconnected. + * + * @param parentDoc Document to which new <code>Node</code> will + * belong. + * @param name Name to use for new <code>Node</code>. + */ + public Node createNode(org.w3c.dom.Document parentDoc, String name) { + // DJP: write this! Should call writeAttributes() + return null; + } + + + /** + * Write this <code>Style</code> object's attributes to the given + * <code>Node</code>. This may involve writing child + * <code>Node</code> objects as well. This is similar to the + * <code>writeNode</code> method, but the <code>Node</code> + * already exists, and this does <b>not</b> write the name, + * family, and parent attributes, which are assumed to already + * exist in the <code>Node</code>. + * + * @param node The <code>Node</code> to add style attributes. + */ + public void writeAttributes(Node node) { + } + + + /** + * Return true if <code>Style</code> is a subset of this one. Note + * that this will return true even if <code>Style</code> is less + * specific than this <code>Style</code>, so long as it does not + * contradict this <code>Style</code> in any way. + * + * This always returns true since only subclasses of + * <code>Style</code> contain any actual <code>Style</code> + * information. + * + * @param style The <code>Style</code> to check + * + * @return true if the <code>Style</code> is a subset, false otherwise. + */ + public boolean isSubset(Style style) { + return true; + } +} + diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/StyleCatalog.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/StyleCatalog.java new file mode 100644 index 000000000000..32732b65f4a3 --- /dev/null +++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/StyleCatalog.java @@ -0,0 +1,393 @@ +/************************************************************************* + * + * 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: StyleCatalog.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; + +import org.w3c.dom.NodeList; +import org.w3c.dom.Node; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Element; +import org.openoffice.xmerge.util.*; +import java.util.Vector; +import java.lang.reflect.Constructor; + + +/** + * A <code>StyleCatalog</code> holds a collection of <code>Style</code> + * objects. It is intended for use when parsing or building a DOM + * document. + * + * Each entry in the <code>StyleCatalog</code> represents a + * <code>Style</code>, and is an object which is a subclass of + * <code>Style</code>. + * + * @author David Proulx + * @see <a href="Style.html">Style</a> + */ +public class StyleCatalog { + + private Vector styles; // The actual styles + + /** + * Constructor + * + * @param initialEntries Expected number of entries to set + * for efficiency purposes. + */ + public StyleCatalog(int initialEntries) { + styles = new Vector(initialEntries); + } + + + /** + * <p>Parse the <code>Document</code> starting from <code>node</code> + * and working downward, and add all styles found, so long as their + * family name is listed in <code>families</code>. For each + * family name in <code>families</code> there must be a corresponding + * element in <code>classes</code>, which specifies the class type + * to use for that family. All of these classes must be + * subclasses of <code>Style</code>. There can be multiple + * classes specified for a particular family.</p> + * + * <p>If <code>defaultClass</code> is non-null, then all styles that + * are found will be added. Any <code>Style</code> whose family is + * not listed in <code>families</code> will be added using defaultClass, + * which, of course, must be a subclass of <code>Style</code>. + * If <code>alwaysCreateDefault</code> is true, then a class + * of type <code>defaultClass</code> will always be created, + * regardless of whether there was also a match in + * <code>families</code>.</p> + * + * <p>DJP Todo: make it recursive so that <code>node</code> can be + * higher up in the <code>Document</code> tree.</p> + * + * @param node The node to be searched for + * <code>Style</code> objects. + * @param families An array of <code>Style</code> families + * to add. + * @param classes An array of class types corresponding + * to the families array. + * @param defaultClass All <code>Style</code> objects that are + * found are added to this class. + * @param alwaysCreateDefault A class of type <code>defaultClass</code> + * will always be created, regardless of + * whether there is a match in the + * families array. + */ + public void add(Node node, String families[], Class classes[], + Class defaultClass, boolean alwaysCreateDefault) { + + if (node == null) + return; + + if (families == null) + families = new String[0]; + if (classes == null) + classes = new Class[0]; + if (node.hasChildNodes()) { + NodeList children = node.getChildNodes(); + int len = children.getLength(); + + for (int i = 0; i < len; i++) { + boolean found = false; + Node child = children.item(i); + String name = child.getNodeName(); + if (name.equals("style:default-style") || name.equals("style:style")) { + String familyName = getFamilyName(child); + if (familyName == null) { + Debug.log(Debug.ERROR, "familyName is null!"); + continue; + } + + for (int j = 0; j < families.length; j++) { + if (families[j].equals(familyName)) { + Class styleClass = classes[j]; + callConstructor(classes[j], child); + found = true; + } + } + if ((!found || alwaysCreateDefault) && (defaultClass != null)) + callConstructor(defaultClass, child); + } + } + } + } + + + /** + * Call the constructor of class <code>cls</code> with parameters + * <code>node</code>, and add the resulting <code>Style</code> to + * the catalog. + * + * @param cls The class whose constructor will be called. + * @param node The constructed class will be added to this node. + */ + private void callConstructor(Class cls, Node node) { + Class params[] = new Class[2]; + params[0] = Node.class; + params[1] = this.getClass(); + try { + Constructor c = cls.getConstructor(params); + Object p[] = new Object[2]; + p[0] = node; + p[1] = this; + styles.add(c.newInstance(p)); + } catch (Exception e) { + Debug.log(Debug.ERROR, "Exception when calling constructor", e); + } + } + + + /** + * Add a <code>Style</code> to the catalog. + * + * @param s The <code>Style</code> to add. + */ + public void add(Style s) { + styles.addElement(s); + } + + + /** + * Return the first <code>Style</code> matching the specified names. + * + * @param name Name to match, null is considered + * <i>always match</i>. + * @param family Family to match, null is considered + * <i>always match</i>. + * @param parent Parent to match, null is considered + * <i>always match</i>. + * @param styleClass styleClass to match, null is considered + * <i>always match</i>. + * + * @return <code>Style</code> value if all parameters match, + * null otherwise + */ + public Style lookup(String name, String family, String parent, + Class styleClass) { + int nStyles = styles.size(); + for (int i = 0; i < nStyles; i++) { + Style s = (Style)styles.elementAt(i); + if ((name != null) && (s.getName() != null) + && (!s.getName().equals(name))) + continue; + if ((family != null) && (s.getFamily() != null) + && (!s.getFamily().equals(family))) + continue; + if ((parent != null) && (s.getParent() != null) + && (!s.getParent().equals(parent))) + continue; + if ((styleClass != null) && (s.getClass() != styleClass)) + continue; + if (s.getName() == null) continue; // DJP: workaround for "null name" problem + return s; + } + return null; // none found + } + + + /** + * Given a <code>Style</code> <code>s<code> return all + * <code>Style</code> objects that match. + * + * @param s <code>Style</code> to match. + * + * @return An array of <code>Style</code> objects that match, an + * empty array if none match. + */ + public Style[] getMatching(Style s) { + + // Run through and count the matching styles so we know how big of + // an array to allocate. + int matchCount = 0; + int nStyles = styles.size(); + for (int j = 0; j < nStyles; j++) { + Style p = ((Style)styles.elementAt(j)).getResolved(); + if (p.isSubset(s)) matchCount++; + } + + // Now allocate the array, and run through again, populating it. + Style[] matchArray = new Style[matchCount]; + matchCount = 0; + for (int j = 0; j < nStyles; j++) { + Style p = ((Style)styles.elementAt(j)).getResolved(); + if (p.isSubset(s)) matchArray[matchCount++] = p; + } + return matchArray; + } + + + /** + * Given a <code>Style</code> <code>s</code>, return the + * <code>style</code> that is the closest match. Not currently + * implemented. + * + * @param s <code>Style</code> to match. + * + * @return The <code>Style</code> that most closely matches. + */ + public Style getBestMatch(Style s) { + // DJP: is this needed? + // DJP ToDo: implement this + return null; + } + + + /** + * <p>Create a <code>Node</code> named <code>name</code> in + * <code>Document</code> <code>parentDoc</code>, and write the + * entire <code>StyleCatalog</code> to it.</p> + * + * <p>Note that the resulting node is returned, but is not connected + * into the document. Placing the output node in the document is + * left to the caller.</p> + * + * @param parentDoc The <code>Document</code> to add the + * <code>Node</code>. + * @param name The name of the <code>Node</code> to add. + * + * @return The <code>Element</code> that was created. + */ + public Element writeNode(org.w3c.dom.Document parentDoc, String name) { + Element rootNode = parentDoc.createElement(name); + + int len = styles.size(); + for (int j = 0; j < len; j++) { + Style s = (Style)styles.get(j); + + Element styleNode = parentDoc.createElement("style:style"); + + if (s.getName() != null) + styleNode.setAttribute("style:name", s.getName()); + if (s.getParent() != null) + styleNode.setAttribute("style:parent-style-name", s.getParent()); + if (s.getFamily() != null) + styleNode.setAttribute("style:family", s.getFamily()); + + Element propertiesNode = (Element) s.createNode(parentDoc, "style:properties"); + // if (propertiesNode.getFirstChild() != null) + // DJP: only add node if has children OR attributes + if (propertiesNode != null) + styleNode.appendChild(propertiesNode); + + rootNode.appendChild(styleNode); + } + + return rootNode; + } + + + /** + * Dump the <code>Style</code> table in Comma Separated Value (CSV) + * format + * + * @param para If true, dump in paragraph <code>Style</code>, + * otherwise dump in text style. + */ + public void dumpCSV(boolean para) { + if (!para) { + TextStyle.dumpHdr(); + int nStyles = styles.size(); + for (int i = 0; i < nStyles; i++) { + Style s = (Style)styles.get(i); + if (s.getClass().equals(TextStyle.class)) + ((TextStyle)s).dumpCSV(); + } + } else { + ParaStyle.dumpHdr(); + int nStyles = styles.size(); + for (int i = 0; i < nStyles; i++) { + Style s = (Style)styles.get(i); + if (s.getClass().equals(ParaStyle.class)) + ((ParaStyle)s).dumpCSV(); + } + } + + } + + + /** + * Check whether a given node represents a <code>Style</code> + * that should be added to the catalog, and if so, return the + * class type for it. If <code>Style</code> should not be added, + * or if node is not a <code>Style</code>, return null. + * + * @param node The <code>Node</code> to be checked. + * @param families An array of <code>Style</code> families. + * @param classes An array of class types corresponding to the + * families array. + * @param defaultClass The default class. + * + * @return The class that is appropriate for this node. + */ + private Class getClass(Node node, String[] families, Class[] classes, + Class defaultClass) { + NamedNodeMap attributes = node.getAttributes(); + if (attributes != null) { + int len = attributes.getLength(); + for (int i = 0; i < len; i++) { + Node attr = attributes.item(i); + if (attr.getNodeName().equals("style:family")) { + String familyName = attr.getNodeValue(); + for (int j = 0; j < families.length; j++) { + if (families[j].equals(familyName)) + return classes[j]; + } + return defaultClass; + } + } + } + return null; + } + + + /** + * Find the family attribute of a <code>Style</code> <code>Node</code>. + * + * @param node The <code>Node</code> to check. + * + * @return The family attribute, or null if one does not + * exist. + */ + private String getFamilyName(Node node) { + NamedNodeMap attributes = node.getAttributes(); + if (attributes != null) { + int len = attributes.getLength(); + for (int i = 0; i < len; i++) { + Node attr = attributes.item(i); + if (attr.getNodeName().equals("style:family")) { + return attr.getNodeValue(); + } + } + } + return null; + } +} + diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/TextStyle.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/TextStyle.java new file mode 100644 index 000000000000..06f2bc778210 --- /dev/null +++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/TextStyle.java @@ -0,0 +1,682 @@ +/************************************************************************* + * + * 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: TextStyle.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. + * + ************************************************************************/ + +// DJP ToDo: need way of specifying fg/bg colors on ws->DOM + +package org.openoffice.xmerge.converter.xml; + +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 org.openoffice.xmerge.util.Debug; + +/** + * Represents a text <code>Style</code> in an OpenOffice document. + * + * @author David Proulx + */ +public class TextStyle extends Style implements Cloneable { + + final protected static int FIRST_ATTR = 0x01; + /** 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; + /** Indicates the last attribute. */ + final protected static int LAST_ATTR = 0x20; + + /** Values of text attributes. */ + protected int values = 0; + /** Bitwise mask of text attributes. */ + protected int mask = 0; + + /** Font size in points. */ + protected int sizeInPoints = 0; + /** Font name. */ + protected String fontName = null; + /** Font <code>Color</code>. */ + protected Color fontColor = null; + /** Background <code>Color</code>. */ + protected Color bgColor = null; + + /** + * 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 TextStyle(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 Bitwise mask of text attributes that this text + * <code>Style</code> will specify. Can be any + * combination of the following, or'ed together: + * {@link #BOLD}, {@link #ITALIC}, {@link #UNDERLINE}, + * {@link #STRIKETHRU}, {@link #SUPERSCRIPT}, + * {@link #SUBSCRIPT}. This parameter determines what + * attributes this <code>Style</code> will specify. + * When an attribute is specified in a + * <code>Style</code>, its value can be either + * <i>on</i> or <i>off</i>. The on/off value for + * each attribute is controlled by the + * <code>values</code> parameter. + * @param values Values of text attributes that this text + * <code>Style</code> will be setting. Any of the + * attributes ({@link #BOLD}, etc) listed for + * <code>mask</code> can be used for this. + * @param fontSize Font size in points. + * @param fontName Name of font. + * @param sc The <code>StyleCatalog</code>, which is used for + * looking up ancestor <code>Style</code> objects. + */ + public TextStyle(String name, String family, String parent, + int mask, int values, int fontSize, String fontName, StyleCatalog sc) { + super(name, family, parent, sc); + this.mask = mask; + this.values = values; + this.sizeInPoints = fontSize; + this.fontName = fontName; + } + + + /** + * 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); + } + + + /** + * 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")) { + if (value.equals("bold")) turnAttributesOn(BOLD); + else if (value.equals("normal")) turnAttributesOff(BOLD); + } + + else if (attr.equals("fo:font-style")) { + if (value.equals("italic")) turnAttributesOn(ITALIC); + else if (value.equals("oblique")) turnAttributesOn(ITALIC); + else if (value.equals("normal")) turnAttributesOff(ITALIC); + } + + else if (attr.equals("style:text-underline")) { + if (value.equals("none")) + turnAttributesOff(UNDERLINE); + else + turnAttributesOn(UNDERLINE); + } + + else if (attr.equals("style:text-crossing-out")) { + if (value.equals("none")) + turnAttributesOff(STRIKETHRU); + else + turnAttributesOn(STRIKETHRU); + } + + else if (attr.equals("style:text-position")) { + if (value.startsWith("super ")) + turnAttributesOn(SUPERSCRIPT); + else if (value.startsWith("sub ")) + turnAttributesOn(SUBSCRIPT); + else if (value.startsWith("0% ")) + turnAttributesOff(SUPERSCRIPT | SUBSCRIPT); + 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) turnAttributesOn(SUBSCRIPT); + else if (amount > 0) turnAttributesOn(SUPERSCRIPT); + } + } + } + + else if (attr.equals("fo:font-size")) { + if (value.endsWith("pt")) { + String num = value.substring(0, value.length() - 2); + sizeInPoints = Integer.parseInt(num); + } + } + + else if (attr.equals("style:font-name")) + fontName = value; + + else if (attr.equals("fo:color")) + fontColor = parseColorString(value); + + else if (attr.equals("style:text-background-color")) + bgColor = parseColorString(value); + + else if (isIgnored(attr)) {} + + else { + Debug.log(Debug.INFO, "TextStyle Unhandled: " + attr + "=" + value); + } + } + + + /** + * 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)); + } + + + /** + * 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 (!((values & attribute) == 0)); + } + + + /** + * Return the font size for this <code>Style</code>. + * + * @return The font size in points + */ + public int getFontSize() { + return sizeInPoints; + } + + + /** + * Return the name of the font for this <code>Style</code>. + * + * @return Name of font, or null if no font is specified by + * this <code>Style</code>. + */ + public String getFontName() { + return fontName; + } + + + /** + * Return the font <code>Color</code> for this <code>Style</code>. + * Can be null if none was specified. + * + * @return <code>Color</code> value for this <code>Style</code>. + * Can be null. + */ + public Color getFontColor() { + return fontColor; + } + + + /** + * Return the background <code>Color</code> for this + * <code>Style</code>. Can be null if none was specified. + * + * @return Background <code>Color</code> value for this + * <code>Style</code>. Can be null. + */ + public Color getBackgroundColor() { + return bgColor; + } + + + /** + * Set the font and/or background <code>Color</code> for this + * <code>Style</code>. + * + * @param fontColor The font <code>Color</code> to set. + * @param backgroundColor The background <code>Color</code> to set. + */ + public void setColors(Color fontColor, Color backgroundColor) { + if (fontColor != null) + this.fontColor = fontColor; + if (backgroundColor != null) + this.bgColor = backgroundColor; + } + + + /** + * 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. + TextStyle resolved = null; + try { + resolved = (TextStyle)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.) + TextStyle parentStyle = null; + if (sc != null) { + if (parent != null) { + parentStyle = (TextStyle)sc.lookup(parent, family, null, + this.getClass()); + if (parentStyle == null) + Debug.log(Debug.ERROR, "parent style lookup of " + + parent + " failed!"); + else + parentStyle = (TextStyle)parentStyle.getResolved(); + + } else if (!name.equals("DEFAULT_STYLE")) { + parentStyle = (TextStyle)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 = (TextStyle)parentStyle.getResolved(); + + if ((sizeInPoints == 0) && (parentStyle.sizeInPoints != 0)) + resolved.sizeInPoints = parentStyle.sizeInPoints; + if ((fontName == null) && (parentStyle.fontName != null)) + resolved.fontName = parentStyle.fontName; + if ((fontColor == null) && (parentStyle.fontColor != null)) + resolved.fontColor = parentStyle.fontColor; + if ((bgColor == null) && (parentStyle.bgColor != null)) + resolved.bgColor = parentStyle.bgColor; + for (int m = BOLD; m <= SUBSCRIPT; m = m << 1) { + if (((mask & m) == 0) && ((parentStyle.mask & m) != 0)) { + resolved.mask |= m; + resolved.values |= (parentStyle.mask & m); + } + } + + } + return resolved; + } + + + /** + * Set one or more text attributes to <i>on</i>. + * + * @param flags Flag values to set <i>on</i>. + */ + private void turnAttributesOn(int flags) { + mask |= flags; + values |= flags; + } + + + /** + * Set one or more text attributes to <i>off</i>. + * + * @param flags The flag values to set <i>off</i>. + */ + private void turnAttributesOff(int flags) { + mask |= flags; + values &= ~flags; + } + + + /** + * Private function to return the value as an element in + * a Comma Separated Value (CSV) format. + * + * @param The value to format. + * + * @return The formatted value. + */ + private static String toCSV(String value) { + if (value != null) + return "\"" + value + "\","; + else + return "\"\","; + } + + + /** + * Private function to return the value as a last element in + * a Comma Separated Value (CSV) format. + * + * @param value The value to format. + * + * @return The formatted value. + */ + private static String toLastCSV(String value) { + if (value != null) + return "\"" + value + "\""; + else + return "\"\""; + } + + + /** + * Print a Comma Separated Value (CSV) header line for the + * spreadsheet dump. + */ + public static void dumpHdr() { + System.out.println(toCSV("Name") + toCSV("Family") + toCSV("parent") + + toCSV("Font") + toCSV("Size") + + toCSV("Bold") + toCSV("Italic") + toCSV("Underline") + + toCSV("Strikethru") + toCSV("Superscript") + toLastCSV("Subscript")); + } + + + /** + * Dump this <code>Style</code> as a Comma Separated Value (CSV) line. + */ + public void dumpCSV() { + String attributes = ""; + for (int bitVal = 0x01; bitVal <= 0x20; bitVal = bitVal << 1) { + if ((bitVal & mask) != 0) { + attributes += toCSV(((bitVal & values) != 0) ? "yes" : "no"); + } else attributes += toCSV(null); // unspecified + } + System.out.println(toCSV(name) + toCSV(family) + toCSV(parent) + + toCSV(fontName) + toCSV("" + sizeInPoints) + attributes + toLastCSV(null)); + } + + + /** + * 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; + TextStyle tStyle = (TextStyle)style; + + if (tStyle.values != values) + return false; + + if (tStyle.sizeInPoints != 0) { + if (sizeInPoints != tStyle.sizeInPoints) + return false; + } + + if (tStyle.fontName != null) { + if (fontName == null) + return false; + if (!fontName.equals(tStyle.fontName)) + return false; + } + + if (tStyle.fontColor != null) { + if (fontColor == null) + return false; + if (!fontColor.equals(tStyle.fontColor)) + return false; + } + + if (tStyle.bgColor != null) { + if (bgColor == null) + return false; + if (!bgColor.equals(tStyle.bgColor)) + 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 ((mask & BOLD) != 0) + if ((values & BOLD) != 0) + node.setAttribute("fo:font-weight", "bold"); + + if ((mask & ITALIC) != 0) + if ((values & ITALIC) != 0) + node.setAttribute("fo:font-style", "italic"); + + if ((mask & UNDERLINE) != 0) + if ((values & UNDERLINE) != 0) + node.setAttribute("style:text-underline", "single"); + + if ((mask & STRIKETHRU) != 0) + if ((values & STRIKETHRU) != 0) + node.setAttribute("style:text-crossing-out", "single-line"); + + if ((mask & SUPERSCRIPT) != 0) + if ((values & SUPERSCRIPT) != 0) + node.setAttribute("style:text-position", "super 58%"); + + if ((mask & SUBSCRIPT) != 0) + if ((values & SUBSCRIPT) != 0) + node.setAttribute("style:text-position", "sub 58%"); + + if (sizeInPoints != 0) { + Integer fs = new Integer(sizeInPoints); + node.setAttribute("fo:font-size", fs.toString() + "pt"); + } + + if (fontName != null) + node.setAttribute("style:font-name", fontName); + + if (fontColor != null) + node.setAttribute("fo:color", buildColorString(fontColor)); + + if (bgColor != null) + node.setAttribute("style:text-background-color", + buildColorString(bgColor)); + } + + + /** + * 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/source/xmerge/java/org/openoffice/xmerge/converter/xml/package.html b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/package.html new file mode 100644 index 000000000000..e0336ce04643 --- /dev/null +++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/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.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. + + #************************************************************************* + --> +<html> +<head> +<title>org.openoffice.xmerge.util package</title> +</head> + +<body bgcolor="white"> + +<p><code>Document</code> and <code>PluginFactory</code> implementations +for XML based formats. + +</body> +</html> diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/BookSettings.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/BookSettings.java new file mode 100644 index 000000000000..b3d99fa0d5b6 --- /dev/null +++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/BookSettings.java @@ -0,0 +1,231 @@ +/************************************************************************* + * + * 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.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.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 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/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/CellStyle.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/CellStyle.java new file mode 100644 index 000000000000..131e8aa35613 --- /dev/null +++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/CellStyle.java @@ -0,0 +1,513 @@ +/************************************************************************* + * + * 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.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.awt.Color; + +import org.w3c.dom.NodeList; +import org.w3c.dom.Node; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Element; + +import org.openoffice.xmerge.converter.xml.Style; +import org.openoffice.xmerge.converter.xml.StyleCatalog; +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/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/ColumnRowInfo.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/ColumnRowInfo.java new file mode 100644 index 000000000000..038cb2b8950c --- /dev/null +++ b/xmerge/source/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/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/ColumnStyle.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/ColumnStyle.java new file mode 100644 index 000000000000..e7c1cb2ce354 --- /dev/null +++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/ColumnStyle.java @@ -0,0 +1,303 @@ +/************************************************************************* + * + * 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.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.NodeList; +import org.w3c.dom.Node; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Element; + +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/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/DocumentMergerImpl.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/DocumentMergerImpl.java new file mode 100644 index 000000000000..df190ed83396 --- /dev/null +++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/DocumentMergerImpl.java @@ -0,0 +1,201 @@ +/************************************************************************* + * + * 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.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.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.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/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/Format.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/Format.java new file mode 100644 index 000000000000..55690658de07 --- /dev/null +++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/Format.java @@ -0,0 +1,478 @@ +/************************************************************************* + * + * 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.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.awt.Color; + +/** + * 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/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/NameDefinition.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/NameDefinition.java new file mode 100644 index 000000000000..f5b485eeb636 --- /dev/null +++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/NameDefinition.java @@ -0,0 +1,218 @@ +/************************************************************************* + * + * 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.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.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/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/RowStyle.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/RowStyle.java new file mode 100644 index 000000000000..4f5ee01f91e3 --- /dev/null +++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/RowStyle.java @@ -0,0 +1,303 @@ +/************************************************************************* + * + * 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.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.NodeList; +import org.w3c.dom.Node; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Element; + +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/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/SheetSettings.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/SheetSettings.java new file mode 100644 index 000000000000..c0074e5a8bbd --- /dev/null +++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/SheetSettings.java @@ -0,0 +1,376 @@ +/************************************************************************* + * + * 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.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.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; + +/** + * 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/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/SpreadsheetDecoder.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/SpreadsheetDecoder.java new file mode 100644 index 000000000000..f01f40ff98e9 --- /dev/null +++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/SpreadsheetDecoder.java @@ -0,0 +1,183 @@ +/************************************************************************* + * + * 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.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.IOException; +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/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/SpreadsheetEncoder.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/SpreadsheetEncoder.java new file mode 100644 index 000000000000..01ce78322c09 --- /dev/null +++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/SpreadsheetEncoder.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: SpreadsheetEncoder.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.IOException; +import java.util.Vector; + +/** + * <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/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/SxcConstants.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/SxcConstants.java new file mode 100644 index 000000000000..ecf4bd26de9b --- /dev/null +++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/SxcConstants.java @@ -0,0 +1,52 @@ +/************************************************************************* + * + * 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/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/SxcDocument.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/SxcDocument.java new file mode 100644 index 000000000000..f5b2977839d8 --- /dev/null +++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/SxcDocument.java @@ -0,0 +1,95 @@ +/************************************************************************* + * + * 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.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/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/SxcDocumentDeserializer.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/SxcDocumentDeserializer.java new file mode 100644 index 000000000000..ab6a941a60f4 --- /dev/null +++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/SxcDocumentDeserializer.java @@ -0,0 +1,795 @@ +/************************************************************************* + * + * 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.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.NodeList; +import org.w3c.dom.Node; +import org.w3c.dom.Element; + +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.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/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/SxcDocumentSerializer.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/SxcDocumentSerializer.java new file mode 100644 index 000000000000..9fb29e5504a4 --- /dev/null +++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/SxcDocumentSerializer.java @@ -0,0 +1,996 @@ +/************************************************************************* + * + * 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.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 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.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) { + + 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. + */ + org.w3c.dom.Document contentDom = sxcDoc.getContentDOM(); + nl = contentDom.getElementsByTagName(TAG_OFFICE_AUTOMATIC_STYLES); + if (nl.getLength() != 0) { + styleCat.add(nl.item(0), families, classes, null, false); + } + + org.w3c.dom.Document stylesDom = sxcDoc.getStyleDOM(); + nl = stylesDom.getElementsByTagName(TAG_OFFICE_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/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/SxcPluginFactory.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/SxcPluginFactory.java new file mode 100644 index 000000000000..abd12130057b --- /dev/null +++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/SxcPluginFactory.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: 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.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/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/package.html b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/package.html new file mode 100644 index 000000000000..ee33c0c58794 --- /dev/null +++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/package.html @@ -0,0 +1,44 @@ +<!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.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. + + #************************************************************************* + --> +<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/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxw/SxwDocument.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxw/SxwDocument.java new file mode 100644 index 000000000000..c530868910d9 --- /dev/null +++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxw/SxwDocument.java @@ -0,0 +1,97 @@ +/************************************************************************* + * + * 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: SxwDocument.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.sxw; + +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 SXW format. + */ +public class SxwDocument extends OfficeDocument { + + + /** + * Constructor with arguments to set <code>name</code>. + * + * @param name The name of the <code>Document</code> + */ + public SxwDocument(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 namespaceAware flag. + * @param validating The value of the validating flag. + */ + public SxwDocument(String name, boolean namespaceAware, boolean validating) { + + super(name, namespaceAware, validating); + } + + + /** + * Returns the Office file extension for the SXW format. + * + * @return The Office file extension for the SXW format. + */ + protected String getFileExtension() { + return OfficeConstants.SXW_FILE_EXTENSION; + } + + + /** + * Returns the Office attribute for the SXW format. + * + * @return The Office attribute for the SXW format. + */ + protected String getOfficeClassAttribute() { + return OfficeConstants.SXW_TYPE; + } + + /** + * Method to return the MIME type of the document. + * + * @return String The document's MIME type. + */ + protected final String getDocumentMimeType() { + return OfficeConstants.SXW_MIME_TYPE; + } + +} + diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxw/SxwPluginFactory.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxw/SxwPluginFactory.java new file mode 100644 index 000000000000..9051769e04c8 --- /dev/null +++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxw/SxwPluginFactory.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: SxwPluginFactory.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.sxw; + +import java.io.InputStream; +import java.io.IOException; + +import org.openoffice.xmerge.PluginFactory; +import org.openoffice.xmerge.Document; +import org.openoffice.xmerge.util.registry.ConverterInfo; + +/** + * General implementation of the <code>PluginFactory</code> interface + * for SXW documents. + * + * @see org.openoffice.xmerge.DocumentDeserializer + * @see org.openoffice.xmerge.DocumentMerger + * @see org.openoffice.xmerge.DocumentSerializer + */ +public abstract class SxwPluginFactory extends PluginFactory { + + /** + * Constructor that caches the <code>ConvertInfo</code> that + * corresponds to the registry information for this plug-in. + * + * @param ci <code>ConvertInfo</code> object. + */ + public SxwPluginFactory (ConverterInfo ci) { + super(ci); + } + + + public Document createOfficeDocument(String name, InputStream is) + throws IOException { + + // read zipped XML stream + SxwDocument doc = new SxwDocument(name); + doc.read(is); + return doc; + } + + public Document createOfficeDocument(String name, InputStream is,boolean isZip) + throws IOException { + + // read XML stream + SxwDocument doc = new SxwDocument(name); + doc.read(is,isZip); + return doc; + } +} + diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxw/package.html b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxw/package.html new file mode 100644 index 000000000000..93ffa03dc0b4 --- /dev/null +++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxw/package.html @@ -0,0 +1,44 @@ +<!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.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. + + #************************************************************************* + --> +<html> +<head> +<title>org.openoffice.xmerge.converter.xml.sxw package</title> +</head> + +<body bgcolor="white"> +<p>Provides base implementation of StarWriter XML conversion to and from +different "Device" <code>Document</code> formats.</p> + +</body> +</html> diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/xslt/ConverterCapabilitiesImpl.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/xslt/ConverterCapabilitiesImpl.java new file mode 100644 index 000000000000..a4c238e4d625 --- /dev/null +++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/xslt/ConverterCapabilitiesImpl.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: ConverterCapabilitiesImpl.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.xslt; + +import org.openoffice.xmerge.ConverterCapabilities; +import org.openoffice.xmerge.converter.xml.OfficeConstants; + +/** + * <p>Xslt implementation of <code>ConverterCapabilities</code> for + * the {@link + * org.openoffice.xmerge.converter.xml.xslt.PluginFactoryImpl + * PluginFactoryImpl}.</p> + * + * <p>Used with StarWriter XML to/from XSLT supported formats 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_DOCUMENT.equals(tag)) + return true; + else if (OfficeConstants.TAG_OFFICE_DOCUMENT_CONTENT.equals(tag)) + return true; + else if (OfficeConstants.TAG_OFFICE_BODY.equals(tag)) + return true; + else if (OfficeConstants.TAG_PARAGRAPH.equals(tag)) + return true; + else if (OfficeConstants.TAG_HEADING.equals(tag)) + return true; + else if (OfficeConstants.TAG_ORDERED_LIST.equals(tag)) + return true; + else if (OfficeConstants.TAG_UNORDERED_LIST.equals(tag)) + return true; + else if (OfficeConstants.TAG_LIST_ITEM.equals(tag)) + return true; + else if (OfficeConstants.TAG_LIST_HEADER.equals(tag)) + return true; + else if (OfficeConstants.TAG_SPAN.equals(tag)) + return true; + else if (OfficeConstants.TAG_HYPERLINK.equals(tag)) + return true; + else if (OfficeConstants.TAG_LINE_BREAK.equals(tag)) + return true; + else if (OfficeConstants.TAG_SPACE.equals(tag)) + return true; + else if (OfficeConstants.TAG_TAB_STOP.equals(tag)) + return true; + + return false; + } + + public boolean canConvertAttribute(String tag, + String attribute) { + + if (OfficeConstants.TAG_SPACE.equals(tag)) { + + if (OfficeConstants.ATTRIBUTE_SPACE_COUNT.equals(attribute)) + return true; + } + + return false; + } +} + diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/xslt/DocumentDeserializerImpl.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/xslt/DocumentDeserializerImpl.java new file mode 100644 index 000000000000..cfdfe9ea1cdf --- /dev/null +++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/xslt/DocumentDeserializerImpl.java @@ -0,0 +1,253 @@ +/************************************************************************* + * + * 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: DocumentDeserializerImpl.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.xslt; + +import java.io.InputStream; +import java.io.IOException; +import java.util.Enumeration; +import java.io.ByteArrayOutputStream; +import java.io.ByteArrayInputStream; + + + + +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.dom.DOMDocument; +//import org.openoffice.xmerge.converter.xml.sxw.SxwDocument; +import org.openoffice.xmerge.converter.xml.xslt.GenericOfficeDocument; +import org.openoffice.xmerge.util.Debug; +import org.openoffice.xmerge.util.registry.ConverterInfo; + +// Imported TraX classes +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.Transformer; +import javax.xml.transform.dom.DOMSource; +//import javax.xml.transform.dom.DOMResult; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerConfigurationException; +import javax.xml.transform.stream.StreamResult; +import javax.xml.transform.stream.StreamSource; +import javax.xml.transform.URIResolver; +import javax.xml.transform.Source; + + +// +//import org.apache.xalan.serialize.Serializer; +//import org.apache.xalan.serialize.SerializerFactory; +//import org.apache.xalan.templates.OutputProperties; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; + +// Imported java classes +import java.io.FileNotFoundException; + + +/** + * <p>Xslt implementation of + * org.openoffice.xmerge.DocumentSerializer + * for the {@link + * org.openoffice.xmerge.converter.xml.xslt.PluginFactoryImpl + * PluginFactoryImpl}.</p> + * + * <p>The <code>serialize</code> method transforms the DOM + * document from the given <code>Document</code> object by + * means of a supplied Xsl Stylesheet.</p> + * + * @author Aidan Butler + */ +public final class DocumentDeserializerImpl + implements DocumentDeserializer,URIResolver { + + /** A <code>ConvertData</code> object assigned to this object. */ + private InputStream is = null; + private ConvertData cd = null; + private PluginFactoryImpl pluginFactory = null; + + /** + * Constructor that assigns the given <code>ConvertData</code> + * to this object. + * + * @param pf A <code>PluginFactoryImpl</code> object. + * + * @param cd A <code>ConvertData</code> object to read data for + * the conversion process by the <code>deserialize</code> + * method. + */ + public DocumentDeserializerImpl(PluginFactoryImpl pf,ConvertData cd) { + this.cd = cd; + pluginFactory = pf; + } + + + + /* + * This method performs the xslt transformation on the supplied <code> + * Document</code> and returns a <code>ByteArrayOutputStream</code> object. + * + * Xslt transformation code + * + * @return baos A <code>ByteArrayOutputStream</code> object containing + * the result of the Xslt transformation. + * @throws TransformerException,TransformerConfigurationException + * , FileNotFoundException,IOException + * + */ + public Document deserialize() throws ConvertException, IOException { + log("\nFound the XSLT deserializer"); + Enumeration enumerate = cd.getDocumentEnumeration(); + org.w3c.dom.Document domDoc=null; + DOMDocument docOut=null; + GenericOfficeDocument doc = null; + ByteArrayOutputStream baos =null; + GenericOfficeDocument sxwDoc = new GenericOfficeDocument("output"); + while (enumerate.hasMoreElements()) { + docOut = (DOMDocument) enumerate.nextElement(); + } + domDoc = docOut.getContentDOM(); + try{ + baos = transform(domDoc); + sxwDoc.initContentDOM(); + DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance(); + dFactory.setNamespaceAware(true); + DocumentBuilder dBuilder = dFactory.newDocumentBuilder(); + sxwDoc.setContentDOM(dBuilder.parse(new ByteArrayInputStream(baos.toByteArray()))); + + } + catch(Exception e){ + System.out.println("The following error occurred:"+e); + } + return sxwDoc; + } + + public Source resolve(String href,String base) + throws TransformerException{ + //System.out.println("\nhref "+href+"\nbase "+base); + if (href !=null){ + if(href.equals("javax.xml.transform.dom.DOMSource")|| href.equals("")) + return null; + try{ + ConverterInfo ci = pluginFactory.getConverterInfo(); + String newhRef ="jar:"+ci.getJarName()+"!/"+href; + //System.out.println("\n Looking For "+ newhRef); + StreamSource sheetFile= new StreamSource(newhRef); + return sheetFile; + } + catch (Exception e){ + System.out.println("\nException in Xslt Resolver " +e); + return null; + } + } + else + return null; + } + + /* + * This method performs the xslt transformation on the supplied Dom Tree. + * + * Xslt transformation code + * + * @throws TransformerException,TransformerConfigurationException + * , FileNotFoundException,IOException + * + */ + private ByteArrayOutputStream transform(org.w3c.dom.Document xmlDoc) + throws TransformerException,TransformerConfigurationException + , FileNotFoundException,IOException{ + + log("\nTransforming..."); + ConverterInfo ci = pluginFactory.getConverterInfo(); + ByteArrayOutputStream baos= new ByteArrayOutputStream(); + try{ + DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance(); + dFactory.setNamespaceAware(true); + DocumentBuilder dBuilder = dFactory.newDocumentBuilder(); + + String teststr = ci.getXsltDeserial(); + teststr= teststr.substring(0,6); + org.w3c.dom.Document xslDoc=null; + if ((teststr.equals("http:/"))||(teststr.equals("file:/")) + ||(teststr.equals("jar://"))){ + log(ci.getXsltDeserial()); + xslDoc= dBuilder.parse(ci.getXsltDeserial()); + + } + else{ + log(ci.getJarName()+"!/"+ci.getXsltDeserial()); + xslDoc = dBuilder.parse( + "jar:"+ci.getJarName()+"!/"+ci.getXsltDeserial()); + } + + + DOMSource xslDomSource = new DOMSource(xslDoc); + DOMSource xmlDomSource = new DOMSource(xmlDoc); + + //call the tranformer using the XSL, Source and Result dom. + TransformerFactory tFactory = TransformerFactory.newInstance(); + tFactory.setURIResolver(this); + Transformer transformer = tFactory.newTransformer(xslDomSource); + transformer.transform(xmlDomSource,new StreamResult(baos)); + /* + // Serialize for output to standard out + Serializer serializer = SerializerFactory.getSerializer + (OutputProperties.getDefaultMethodProperties("xml")); + serializer.setOutputStream(System.out); + serializer.asDOMSerializer().serialize(xmlDomResult.getNode()); + */ + + log("\n** Transform Complete ***"); + + } + catch (StackOverflowError sOE){ + System.out.println("\nERROR : Stack Overflow Error During Transformation\n Try increasing the stack size by passing the -Xss1m option to the JRE."); + throw sOE; + } + catch(Exception e){ + System.out.println("An error occured in the transformation : "+e); + } + return baos; + } + + /** + * Sends message to the log object. + * + * @param str Debug message. + */ + private void log(String str) { + + Debug.log(Debug.TRACE, str); + } + +} + diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/xslt/DocumentMergerImpl.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/xslt/DocumentMergerImpl.java new file mode 100644 index 000000000000..3d5d28737a68 --- /dev/null +++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/xslt/DocumentMergerImpl.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: DocumentMergerImpl.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.xslt; + +import org.w3c.dom.Document; + +import org.openoffice.xmerge.DocumentMerger; +import org.openoffice.xmerge.MergeException; +import org.openoffice.xmerge.ConverterCapabilities; +import org.openoffice.xmerge.converter.xml.xslt.GenericOfficeDocument; +import org.openoffice.xmerge.merger.DiffAlgorithm; +import org.openoffice.xmerge.merger.Difference; +import org.openoffice.xmerge.merger.NodeMergeAlgorithm; +import org.openoffice.xmerge.merger.Iterator; +import org.openoffice.xmerge.merger.diff.ParaNodeIterator; +import org.openoffice.xmerge.merger.diff.IteratorLCSAlgorithm; +import org.openoffice.xmerge.merger.merge.DocumentMerge; +import org.openoffice.xmerge.merger.merge.CharacterBaseParagraphMerge; +import org.openoffice.xmerge.util.Debug; + + +/** + * Xslt implementation of <code>DocumentMerger</code> + * for the {@link + * org.openoffice.xmerge.converter.xml.xslt.PluginFactoryImpl + * PluginFactoryImpl}.</p> + */ +public class DocumentMergerImpl implements DocumentMerger { + + private ConverterCapabilities cc_; + private org.openoffice.xmerge.Document orig = null; + + public DocumentMergerImpl(org.openoffice.xmerge.Document doc, ConverterCapabilities cc) { + cc_ = cc; + this.orig = doc; + } + + public void merge(org.openoffice.xmerge.Document modifiedDoc) throws MergeException { + + GenericOfficeDocument wdoc1 = (GenericOfficeDocument) orig; + GenericOfficeDocument wdoc2 = (GenericOfficeDocument) modifiedDoc; + + Document doc1 = wdoc1.getContentDOM(); + Document doc2 = wdoc2.getContentDOM(); + + Iterator i1 = new ParaNodeIterator(cc_, doc1.getDocumentElement()); + Iterator i2 = new ParaNodeIterator(cc_, doc2.getDocumentElement()); + + DiffAlgorithm diffAlgo = new IteratorLCSAlgorithm(); + + // find out the paragrah level diffs + Difference[] diffTable = diffAlgo.computeDiffs(i1, i2); + + if (Debug.isFlagSet(Debug.INFO)) { + Debug.log(Debug.INFO, "Diff Result: "); + + for (int i = 0; i < diffTable.length; i++) { + Debug.log(Debug.INFO, diffTable[i].debug()); + } + } + + // merge the paragraphs + NodeMergeAlgorithm charMerge = new CharacterBaseParagraphMerge(); + DocumentMerge docMerge = new DocumentMerge(cc_, charMerge); + + Iterator result = null; + + docMerge.applyDifference(i1, i2, diffTable); + } +} + + diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/xslt/DocumentSerializerImpl.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/xslt/DocumentSerializerImpl.java new file mode 100644 index 000000000000..2293cb0be74f --- /dev/null +++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/xslt/DocumentSerializerImpl.java @@ -0,0 +1,300 @@ +/************************************************************************* + * + * 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: DocumentSerializerImpl.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.xslt; + +import org.w3c.dom.NodeList; +import org.w3c.dom.Node; +import org.w3c.dom.Element; +import org.w3c.dom.*; + +import java.io.IOException; +import java.io.ByteArrayOutputStream; +import java.io.ByteArrayInputStream; + +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.xslt.GenericOfficeDocument; +import org.openoffice.xmerge.converter.dom.DOMDocument; +import org.openoffice.xmerge.util.registry.ConverterInfo; +import org.openoffice.xmerge.converter.xml.OfficeConstants; + +// Imported TraX classes +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.Transformer; +import javax.xml.transform.stream.StreamSource; +import javax.xml.transform.stream.StreamResult; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerConfigurationException; +import javax.xml.transform.URIResolver; +import javax.xml.transform.Source; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; + +// Imported java classes +import java.io.FileNotFoundException; + +/** + * <p>Xslt implementation of + * org.openoffice.xmerge.DocumentSerializer + * for the {@link + * org.openoffice.xmerge.converter.xml.xslt.PluginFactoryImpl + * PluginFactoryImpl}.</p> + * + * <p>The <code>serialize</code> method transforms the DOM + * document from the given <code>Document</code> object by + * means of a supplied Xsl Stylesheet.</p> + * + * @author Aidan Butler + */ + + +public final class DocumentSerializerImpl + implements DocumentSerializer,OfficeConstants,URIResolver { + + + /** SXW <code>Document</code> object that this converter processes. */ + private GenericOfficeDocument sxwDoc = null; + + private PluginFactoryImpl pluginFactory = null; + + /** + * Constructor. + * + * @param pf A <code>PluginFactoryImpl</code> + * @param doc A SXW <code>Document</code> to be converted. + */ + public DocumentSerializerImpl(PluginFactoryImpl pf,Document doc) { + pluginFactory=pf; + sxwDoc = (GenericOfficeDocument) doc; + } + + + /** + * Method to convert a <code>Document</code> with an xsl stylesheet. + * It creates a <code>Document</code> object, which is then transformed + * with the Xslt processer. A <code>ConvertData </code> object is + * constructed and returned. + * + * @return cd A <code>ConvertData</code> object. + * @throws ConvertException If any I/O error occurs. + * @throws IOException If any I/O error occurs. + */ + public ConvertData serialize() throws ConvertException, IOException { + String docName = sxwDoc.getName(); + org.w3c.dom.Document domDoc = sxwDoc.getContentDOM(); + org.w3c.dom.Document metaDoc = sxwDoc.getMetaDOM(); + org.w3c.dom.Document styleDoc = sxwDoc.getStyleDOM(); + ByteArrayOutputStream baos= new ByteArrayOutputStream(); + ConvertData cd = new ConvertData(); + Node offnode = (Node)domDoc.getDocumentElement(); + if (!(offnode.getNodeName()).equals("office:document")){ + try{ + DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance(); + DocumentBuilder builder= builderFactory.newDocumentBuilder(); + DOMImplementation domImpl = builder.getDOMImplementation(); + DocumentType docType =domImpl.createDocumentType("office:document","-//OpenOffice.org//DTD OfficeDocument 1.0//EN",null); + org.w3c.dom.Document newDoc = domImpl.createDocument("http://openoffice.org/2000/office","office:document",docType); + + + Element rootElement=newDoc.getDocumentElement(); + rootElement.setAttribute("xmlns:office","http://openoffice.org/2000/office"); + rootElement.setAttribute("xmlns:style","http://openoffice.org/2000/style" ); + rootElement.setAttribute("xmlns:text","http://openoffice.org/2000/text"); + rootElement.setAttribute("xmlns:table","http://openoffice.org/2000/table"); + + rootElement.setAttribute("xmlns:draw","http://openoffice.org/2000/drawing"); + rootElement.setAttribute("xmlns:fo","http://www.w3.org/1999/XSL/Format" ); + rootElement.setAttribute("xmlns:xlink","http://www.w3.org/1999/xlink" ); + rootElement.setAttribute("xmlns:dc","http://purl.org/dc/elements/1.1/" ); + rootElement.setAttribute("xmlns:meta","http://openoffice.org/2000/meta" ); + rootElement.setAttribute("xmlns:number","http://openoffice.org/2000/datastyle" ); + rootElement.setAttribute("xmlns:svg","http://www.w3.org/2000/svg" ); + rootElement.setAttribute("xmlns:chart","http://openoffice.org/2000/chart" ); + rootElement.setAttribute("xmlns:dr3d","http://openoffice.org/2000/dr3d" ); + rootElement.setAttribute("xmlns:math","http://www.w3.org/1998/Math/MathML" ); + rootElement.setAttribute("xmlns:form","http://openoffice.org/2000/form" ); + rootElement.setAttribute("xmlns:script","http://openoffice.org/2000/script" ); + rootElement.setAttribute("xmlns:config","http://openoffice.org/2001/config" ); + rootElement.setAttribute("office:class","text" ); + rootElement.setAttribute("office:version","1.0"); + + + NodeList nodeList; + Node tmpNode; + Node rootNode = (Node)rootElement; + if (metaDoc !=null){ + nodeList= metaDoc.getElementsByTagName(TAG_OFFICE_META); + if (nodeList.getLength()>0){ + tmpNode = newDoc.importNode(nodeList.item(0),true); + rootNode.appendChild(tmpNode); + } + } if (styleDoc !=null){ + nodeList= styleDoc.getElementsByTagName(TAG_OFFICE_STYLES); + if (nodeList.getLength()>0){ + tmpNode = newDoc.importNode(nodeList.item(0),true); + rootNode.appendChild(tmpNode); + } + }if (domDoc !=null){ + nodeList= domDoc.getElementsByTagName(TAG_OFFICE_AUTOMATIC_STYLES); + if (nodeList.getLength()>0){ + tmpNode = newDoc.importNode(nodeList.item(0),true); + rootNode.appendChild(tmpNode); + } + nodeList= domDoc.getElementsByTagName(TAG_OFFICE_BODY); + if (nodeList.getLength()>0){ + tmpNode = newDoc.importNode(nodeList.item(0),true); + rootNode.appendChild(tmpNode); + } + } + domDoc=newDoc; + }catch(Exception e){ + System.out.println("\nAn Exception occurred with Xslt Serializer"+e); + } + + } + + try{ + baos=transform(domDoc); + } + catch (Exception e){ + System.out.println("\n Error with Xslt\n"); + } + + String ext = pluginFactory.getDeviceFileExtension(); + DOMDocument resultDomDoc=(DOMDocument)pluginFactory.createDeviceDocument(docName,new ByteArrayInputStream(baos.toByteArray())); + cd.addDocument (resultDomDoc); + return cd; + } + + public Source resolve(String href,String base) + throws TransformerException{ + //System.out.println("\nhref "+href+"\nbase "+base); + if (href !=null){ + if(href.equals("javax.xml.transform.dom.DOMSource")|| href.equals("")) + return null; + try{ + ConverterInfo ci = pluginFactory.getConverterInfo(); + String newhRef ="jar:"+ci.getJarName()+"!/"+href; + //System.out.println("\n Looking For "+ newhRef); + StreamSource sheetFile= new StreamSource(newhRef); + return sheetFile; + } + catch (Exception e){ + System.out.println("\nException in Xslt Resolver " +e); + return null; + } + } + else + return null; + } + + + /* + * This method performs the sxl transformation on the supplied <code> + * Document</code> and returns a <code>DOMResult</code> object. + * + * Xslt transformation code + * + * @return baos A <code>ByteArrayOutputStream</code> object containing + * the result of the Xslt transformation. + * @throws TransformerException,TransformerConfigurationException + * , FileNotFoundException,IOException + * + */ + + + private ByteArrayOutputStream transform(org.w3c.dom.Document domDoc) + throws TransformerException,TransformerConfigurationException + , FileNotFoundException,IOException{ + //System.out.println("\nTransforming..."); + ConverterInfo ci = pluginFactory.getConverterInfo(); + //DOMResult xmlDomResult = new DOMResult(); + ByteArrayOutputStream baos= new ByteArrayOutputStream(); + try{ + + DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance(); + dFactory.setNamespaceAware(true); + + DocumentBuilder dBuilder = dFactory.newDocumentBuilder(); + String teststr = ci.getXsltSerial(); + + teststr= teststr.substring(0,6); + org.w3c.dom.Document xslDoc=null; + if ((teststr.equals("http:/"))||(teststr.equals("file:/")) + ||(teststr.equals("jar://"))){ + System.out.println(ci.getXsltSerial()); + xslDoc= dBuilder.parse(ci.getXsltSerial()); + + } + else{ + //System.out.println(ci.getJarName()+"!/"+ci.getXsltSerial()); + xslDoc = dBuilder.parse( + "jar:"+ci.getJarName()+"!/"+ci.getXsltSerial()); + } + + DOMSource xslDomSource = new DOMSource(xslDoc); + DOMSource xmlDomSource = new DOMSource(domDoc); + + //call the tranformer using the XSL, Source and Result. + TransformerFactory tFactory = TransformerFactory.newInstance(); + tFactory.setURIResolver(this); + Transformer transformer = tFactory.newTransformer(xslDomSource); + + transformer.transform(xmlDomSource, new StreamResult(baos)); + + /* + transformer.transform(xmlDomSource, xmlDomResult); //Removed this impl because the DocType was not being written out + + // Serialize for output to standard out + Serializer serializer = SerializerFactory.getSerializer + (OutputProperties.getDefaultMethodProperties("xml")); + //serializer.setOutputStream(System.out); + serializer.setOutputStream(baos); + serializer.asDOMSerializer().serialize(xmlDomResult.getNode()); + //serializer.asDOMSerializer().serialize(xmlDomSource.getNode()); + + + //System.out.println("\n** Transform Complete ***"); + */ + } + catch(Exception e){ + System.out.println("An error occured in the transformation : "+e); + } + return baos; + } + + +} + diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/xslt/GenericOfficeDocument.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/xslt/GenericOfficeDocument.java new file mode 100644 index 000000000000..a996bf7f07bf --- /dev/null +++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/xslt/GenericOfficeDocument.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: GenericOfficeDocument.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.xslt; + +import org.openoffice.xmerge.converter.xml.OfficeDocument; + +/** + * This class is an implementation of <code>OfficeDocument</code> for + * the generic office format. + */ +public class GenericOfficeDocument extends OfficeDocument { + + /** + * Constructor with arguments to set <code>name</code>. + * + * @param name The name of the <code>Document</code> + */ + public GenericOfficeDocument(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 GenericOfficeDocument(String name, boolean namespaceAware, boolean validating) { + + super(name, namespaceAware, validating); + } + + /** + * Returns the Office file extension for the generic format. + * + * @return The Office file extension for the generic format. + */ + protected String getFileExtension() { + return ""; + } + + /** + * Returns the Office attribute for the generic format. + * + * @return The Office attribute for the generic format. + */ + protected String getOfficeClassAttribute() { + + return ""; + } + + /** + * Method to return the MIME type of the document. + * + * @return String The document's MIME type. + */ + protected String getDocumentMimeType() { + /* TODO: Determine the MIME-type from the input. */ + return ""; + } + +} + diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/xslt/PluginFactoryImpl.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/xslt/PluginFactoryImpl.java new file mode 100644 index 000000000000..9f5d39d6ea37 --- /dev/null +++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/xslt/PluginFactoryImpl.java @@ -0,0 +1,205 @@ +/************************************************************************* + * + * 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.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.xslt; + +import org.openoffice.xmerge.Document; +import org.openoffice.xmerge.ConvertData; +import org.openoffice.xmerge.DocumentSerializer; +import org.openoffice.xmerge.DocumentSerializerFactory; +import org.openoffice.xmerge.DocumentDeserializer; +import org.openoffice.xmerge.DocumentDeserializerFactory; +import org.openoffice.xmerge.PluginFactory; +import org.openoffice.xmerge.converter.dom.DOMDocument; +//import org.openoffice.xmerge.converter.xml.sxw.SxwDocument; +//import org.openoffice.xmerge.converter.xml.OfficeDocument; +import org.openoffice.xmerge.converter.xml.xslt.GenericOfficeDocument; +import org.openoffice.xmerge.util.registry.ConverterInfo; +import org.openoffice.xmerge.DocumentMerger; +import org.openoffice.xmerge.DocumentMergerFactory; +import org.openoffice.xmerge.ConverterCapabilities; + +import java.io.InputStream; +import java.util.Enumeration; +import java.io.IOException; +import java.util.Properties; + +/** + * <p>Xslt implementation of the <code>PluginFactory</code>. + * This encapsulates conversion of StarWriter XML format to and from + * a supported format.</p> + * + * <p>The superclass produces a particular + * {@link org.openoffice.xmerge.Document Document} + * object, i.e. {@link + * org.openoffice.xmerge.converter.xml.sxw.SxwDocument + * SxwDocument} that the converters in this class work 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}</p> + * + * @author Aidan Butler + */ +public final class PluginFactoryImpl extends PluginFactory + implements DocumentDeserializerFactory, DocumentSerializerFactory, DocumentMergerFactory +{ + + public PluginFactoryImpl (ConverterInfo ci) { + super(ci); + } + + /** ConverterCapabilities object for this type of conversion. */ + private final static ConverterCapabilities converterCap = + new ConverterCapabilitiesImpl(); + + + /** + * Returns an instance of <code>DocumentSerializerImpl</code>, + * which is an implementation of the <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 DocumentSerializerImpl(this,doc); + } + + + /** + * Returns an instance of <code>DocumentDeserializerImpl</code>, + * which is an implementation of the <code>DocumentDeserializer</code> + * interface. + * + * @param cd <code>ConvertData</code> object. + * + * @return A DocumentDeserializerImpl object. + */ + public DocumentDeserializer createDocumentDeserializer(ConvertData cd) { + + return new DocumentDeserializerImpl(this,cd); + } + + public org.openoffice.xmerge.Document createDeviceDocument(java.lang.String str, java.io.InputStream inputStream) throws java.io.IOException { + String ext = this.getDeviceFileExtension(); + DOMDocument domDoc = new DOMDocument(str,ext); + domDoc.read(inputStream); + return domDoc; + } + + + public Document createOfficeDocument(String name, InputStream is) + throws IOException { + + // read zipped XML stream + GenericOfficeDocument doc = new GenericOfficeDocument(name); + doc.read(is); + return doc; + } + + public Document createOfficeDocument(String name, InputStream is,boolean isZip) + throws IOException { + + // read zipped XML stream + GenericOfficeDocument doc = new GenericOfficeDocument(name); + doc.read(is,isZip); + return doc; + } + + /** + * Returns a <code>String</code> containing the file extension of a + * <code>Document</code>. This method uses a properties file to determine + * a mapping from the device mime in the <code>ConverterInfo</code> to a + * particular file extension. If a mapping is not specified, the default + * is ".txt". + * + * @return <code>String</code>. + */ + + + public String getDeviceFileExtension(){ + Class c = this.getClass(); + InputStream is = c.getResourceAsStream("XsltPlugin.properties"); + Properties props = new Properties(); + String ext= ".txt"; + String mimeType = null; + ConverterInfo ci = this.getConverterInfo(); + Enumeration enumerate = ci.getDeviceMime(); + while (enumerate.hasMoreElements()) { + mimeType= (String) enumerate.nextElement(); + } + try { + props.load(is); + + String info = props.getProperty(mimeType); + if (info != null) { + ext = info; + } + } catch (Exception e) { + + // It is okay for the property file to not exist. + // + } + return ext; + } + + /** + * Returns an instance of <code>DocumentMergerImpl</code>, + * which is an implementation of the <code>DocumentMerger</code> + * interface. + * + * @param doc <code>Document</code> to merge. + * + * @return A DocumentMergerImpl object. + */ + public DocumentMerger createDocumentMerger(Document doc) { + ConverterCapabilities cc = converterCap; + DocumentMergerImpl merger = new DocumentMergerImpl(doc, cc); + return merger; + + } + +} + + + + + + + + + + + diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/xslt/XsltPlugin.properties b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/xslt/XsltPlugin.properties new file mode 100644 index 000000000000..013f457b8ec9 --- /dev/null +++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/xslt/XsltPlugin.properties @@ -0,0 +1,40 @@ +#************************************************************************* +# +# 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: XsltPlugin.properties,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. +# +#************************************************************************* + + +# +# XsltPlugin.properties +# + +#This file allows users to specify the mime-type to file extension mappings + +# e.g text/html=.html +text/html=.html diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/xslt/package.html b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/xslt/package.html new file mode 100644 index 000000000000..5f4de34878a0 --- /dev/null +++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/xslt/package.html @@ -0,0 +1,71 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//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.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. + + #************************************************************************* + --> +<HTML> +<HEAD> + <TITLE>org.openoffice.xmerge.converter.xml.xslt package</TITLE> +</HEAD> +<BODY> +<P>Provides the tools for doing the conversion of StarWriter XML to +and from supported formats, through the use of an XSLT +transformation.</P> +<P>It follows the {@link org.openoffice.xmerge} +framework for the conversion process.</P> +<P>This converter does not currently support merge.</P> +<P><FONT FACE="Times New Roman, serif"><FONT SIZE=5><B>XSLT +Transformation</B></FONT></FONT></P> +<p>The converter makes use +of one or more XSLT style sheets, which are used in the +DocumentSerializer and DocumentDeserializer, to perform the actual +translations. The location of these stylesheets is extracted from the {@link org.openoffice.xmerge.util.registry.ConverterInfo ConverterInfo} data structure, and are specified using the optional converter-xslt-serialize and converter-xsltdeserialize tags in a plugins converter.xml file. Please refer to the SDK document for more information about how to implement a Plugin Configuration XML File for a specific plugin. +A sample OpenOffice to Html stylesheet and Html to +Openffice stylesheet, has been provided as a sample implementation. +The converter also makes use of an XsltPlugin.properties file, which may be edited by the user to provide MIME-TYPE to file extension mappings. This file is used by the {@link org.openoffice.xmerge.converter.xml.xslt.PluginFactoryImpl getDeviceFileExtension} method. +</p> + +<H2>TODO list</H2> + +<p><ol> +<li>Expand XSLT style sheets to support more office/html + capabilities</li> +<li>Add support for certain character codes, such as &nbsp + which currently causes the transformer to break.</li> +<li>Change the DocumentDeserializer transformer, so that the DOMResult is serialized using the xalan serializer and create an SxwDocument from the result</li> +</ol></p> + +@see org.openoffice.xmerge.util.registry + +</BODY> +</HTML> + + |