summaryrefslogtreecommitdiff
path: root/xmerge/source/xmerge/java/org/openoffice
diff options
context:
space:
mode:
Diffstat (limited to 'xmerge/source/xmerge/java/org/openoffice')
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/Convert.java327
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/ConvertData.java117
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/ConvertException.java45
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/ConverterCapabilities.java67
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/ConverterFactory.java124
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/Document.java99
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/DocumentDeserializer.java65
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/DocumentDeserializer2.java71
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/DocumentDeserializerFactory.java65
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/DocumentMerger.java93
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/DocumentMergerFactory.java63
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/DocumentSerializer.java67
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/DocumentSerializer2.java73
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/DocumentSerializerFactory.java63
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/MergeException.java45
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/PluginFactory.java193
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/Version.java88
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/converter/dom/DOMDocument.java412
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/converter/dom/package.html55
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/converter/palm/PalmDB.java469
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/converter/palm/PalmDocument.java177
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/converter/palm/PdbDecoder.java233
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/converter/palm/PdbEncoder.java196
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/converter/palm/PdbHeader.java162
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/converter/palm/PdbUtil.java105
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/converter/palm/Record.java216
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/converter/palm/package.html142
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/EmbeddedBinaryObject.java127
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/EmbeddedObject.java116
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/EmbeddedXMLObject.java295
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/OfficeConstants.java439
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/OfficeDocument.java1265
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/OfficeDocumentException.java130
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/OfficeZip.java458
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/ParaStyle.java601
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/Style.java227
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/StyleCatalog.java390
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/TextStyle.java679
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/package.html41
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/BookSettings.java228
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/CellStyle.java510
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/ColumnRowInfo.java195
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/ColumnStyle.java300
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/DocumentMergerImpl.java198
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/Format.java475
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/NameDefinition.java215
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/RowStyle.java300
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/SheetSettings.java373
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/SpreadsheetDecoder.java180
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/SpreadsheetEncoder.java129
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/SxcConstants.java49
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/SxcDocument.java92
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/SxcDocumentDeserializer.java792
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/SxcDocumentSerializer.java993
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/SxcPluginFactory.java82
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/package.html40
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxw/SxwDocument.java94
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxw/SxwPluginFactory.java76
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxw/package.html40
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/xslt/ConverterCapabilitiesImpl.java93
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/xslt/DocumentDeserializerImpl.java250
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/xslt/DocumentMergerImpl.java97
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/xslt/DocumentSerializerImpl.java297
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/xslt/GenericOfficeDocument.java93
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/xslt/PluginFactoryImpl.java202
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/xslt/XsltPlugin.properties37
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/xslt/package.html67
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/merger/DiffAlgorithm.java51
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/merger/Difference.java242
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/merger/Iterator.java123
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/merger/MergeAlgorithm.java61
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/merger/NodeMergeAlgorithm.java52
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/merger/diff/CellNodeIterator.java114
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/merger/diff/CharArrayLCSAlgorithm.java234
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/merger/diff/CharacterParser.java142
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/merger/diff/IteratorLCSAlgorithm.java236
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/merger/diff/IteratorRowCompare.java243
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/merger/diff/NodeIterator.java384
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/merger/diff/ObjectArrayIterator.java210
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/merger/diff/ParaNodeIterator.java90
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/merger/diff/RowIterator.java81
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/merger/diff/TextNodeEntry.java88
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/merger/diff/TextNodeIterator.java84
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/merger/diff/package.html43
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/merger/merge/CharacterBaseParagraphMerge.java310
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/merger/merge/DocumentMerge.java247
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/merger/merge/PositionBaseRowMerge.java260
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/merger/merge/SheetMerge.java89
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/merger/merge/SheetUtil.java107
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/merger/merge/package.html43
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/merger/package.html75
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/package.html113
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/test/ConverterInfoList.java105
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/test/ConverterInfoList.properties37
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/test/Driver.java318
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/util/ActiveSyncDriver.java144
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/util/ColourConverter.java446
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/util/Debug.java339
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/util/Debug.properties39
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/util/EndianConverter.java178
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/util/IntArrayList.java147
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/util/OfficeUtil.java135
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/util/Resources.java99
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/util/TwipsConverter.java99
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/util/XmlUtil.java189
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/util/package.html39
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/util/registry/ConverterInfo.java436
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/util/registry/ConverterInfoMgr.java536
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/util/registry/ConverterInfoReader.java279
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/util/registry/RegistryException.java47
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/util/registry/build.xml135
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/util/registry/package.html82
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/util/resources.properties69
113 files changed, 22717 insertions, 0 deletions
diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/Convert.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/Convert.java
new file mode 100644
index 000000000000..59b6db6aa84c
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/Convert.java
@@ -0,0 +1,327 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.util.Enumeration;
+
+import org.openoffice.xmerge.util.registry.ConverterInfo;
+
+/**
+ * The <code>Convert</code> class manages a conversion from one
+ * mime-type to another. The <code>ConvertFactory</code> is
+ * responsible for returning the appropriate <code>Convert</code>
+ * class for a specified conversion. This class is responsible
+ * for all interactions with the <code>PluginFactory</code>
+ * implementation.
+ *
+ * @see ConverterFactory
+ * @see PluginFactory
+ * @see org.openoffice.xmerge.util.registry.ConverterInfo
+ *
+ * @author Martin Maher
+ */
+public class Convert implements Cloneable {
+
+ /**
+ * ConvertInfo that corresponds to the from-mime/to-mime
+ * conversion.
+ */
+ private ConverterInfo ci;
+
+ /**
+ * true if converting to the Office format, false if converting
+ * to the device format.
+ */
+ private boolean toOffice;
+
+ /**
+ * Holds the convert input data.
+ */
+ private ConvertData inputCD = new ConvertData();
+
+
+ /**
+ * Construct a Convert class with specified <code>ConvertInfo</code>
+ * registry information.
+ *
+ * @param ci A <code>ConvertInfo</code> object containing
+ * registry information corresponding to a
+ * specific plug-in.
+ * @param toOffice true if converting to the Office format,
+ * false if converting to the device format.
+ */
+ public Convert(ConverterInfo ci, boolean toOffice) {
+ this.ci = ci;
+ this.toOffice = toOffice;
+ }
+
+
+ /**
+ * Adds an <code>InputStream</code> to be used as input by the
+ * <code>Convert</code> class. It is possible that many files
+ * need to be converted into a single output <code>Documetn</code>,
+ * so this function may be called more than one time. It is the
+ * plug-in's responsibility to know how to handle the input.
+ *
+ * @param name The name corresponding to the <code>InputStream</code>.
+ * @param is <code>InputStream</code> to be used as input.
+ *
+ * @throws IOException If any I/O error occurs.
+ */
+ public void addInputStream(String name, InputStream is)
+ throws IOException {
+
+ Document inputDoc;
+
+ if (toOffice == true) {
+ inputDoc = ci.getPluginFactory().createDeviceDocument(name, is);
+ } else {
+ inputDoc = ci.getPluginFactory().createOfficeDocument(name, is);
+ }
+ inputCD.addDocument(inputDoc);
+ }
+
+ /**
+ * Adds an <code>InputStream</code> to be used as input by the
+ * <code>Convert</code> class. It is possible that many files
+ * need to be converted into a single output <code>Documetn</code>,
+ * so this function may be called more than one time. It is the
+ * plug-in's responsibility to know how to handle the input.
+ *
+ * @param name The name corresponding to the <code>InputStream</code>.
+ * @param is <code>InputStream</code> to be used as input.
+ * @param isZip <code>boolean</code> to identify that incoming stream is * zipped
+ *
+ * @throws IOException If any I/O error occurs.
+ */
+ public void addInputStream(String name, InputStream is,boolean isZip)
+ throws IOException {
+
+ Document inputDoc;
+
+ if (toOffice == true) {
+ inputDoc = ci.getPluginFactory().createDeviceDocument(name, is);
+ } else {
+ inputDoc = ci.getPluginFactory().createOfficeDocument(name, is, isZip);
+ }
+ inputCD.addDocument(inputDoc);
+ }
+
+
+ /**
+ * Returns a <code>DocumentMerger</code> for the given <code>Document</code>.
+ *
+ * @param origDoc The <code>Document</code> were later changes will be merged to
+ *
+ * @return The <code>DocumentMerger</code> object for the given document.
+ *
+ * @throws IOException If any I/O error occurs.
+ */
+ public DocumentMerger getDocumentMerger(Document origDoc)
+ throws IOException {
+
+ DocumentMergerFactory myDocMergerFactory = ci.getDocMergerFactory();
+ DocumentMerger merger = myDocMergerFactory.createDocumentMerger(origDoc);
+ return merger;
+ }
+
+ /**
+ * Resets the input queue, so that the user can use this class to
+ * perform another conversion. This causes the
+ * <code>addInputStream</code> method to accept input for the next
+ * conversion.
+ */
+ public void reset() {
+ inputCD.reset();
+ }
+
+
+ /**
+ * Clones a Convert object so another Convert object can
+ * do the same conversion. <code>InputStream<code> objects passed
+ * in via calls to the <code>addInputStream</code> method are not
+ * copied.
+ *
+ * @return The cloned <code>Convert</code> object.
+ */
+ public Object clone() {
+
+ Convert aClone = null;
+
+ try {
+ aClone = (Convert) super.clone();
+ aClone.reset();
+ }
+ catch (CloneNotSupportedException e) {
+ System.out.println("Convert clone could not be created");
+ }
+ return aClone;
+ }
+
+
+ /**
+ * Convert the input specified in calls to the <code>addInputStream</code>
+ * method to the output format specified by this <code>Convert</code>
+ * class.
+ *
+ * @return The output data.
+ *
+ * @throws ConvertException If any conversion error occurs.
+ * @throws IOException If any I/O error occurs.
+ */
+ public ConvertData convert() throws ConvertException, IOException {
+
+ ConvertData dataOut = new ConvertData();
+
+ if (toOffice) {
+
+ // From device format to Office format
+ //
+ DocumentDeserializerFactory myDocDeserializerFactory =
+ ci.getDocDeserializerFactory();
+ DocumentDeserializer deser =
+ myDocDeserializerFactory.createDocumentDeserializer(inputCD);
+ Document deviceDoc = deser.deserialize();
+
+
+ dataOut.addDocument(deviceDoc);
+ return dataOut;
+
+ } else {
+
+ // From Office format to device format
+ //
+ DocumentSerializerFactory myDocSerializerFactory =
+ ci.getDocSerializerFactory();
+
+ Enumeration e = inputCD.getDocumentEnumeration();
+
+ Document doc = (Document) e.nextElement();
+ DocumentSerializer ser = myDocSerializerFactory.createDocumentSerializer(doc);
+ dataOut = ser.serialize();
+
+ return dataOut;
+ }
+ }
+
+ /**
+ * NEW (HJ):
+ * Convert the input specified in calls to the <code>addInputStream</code>
+ * method to the output format specified by this <code>Convert</code>
+ * class.
+ * The (de)serializer may use the URLs to resolve links and choose name(s)
+ * for destination document(s).
+ *
+ * @return The output data.
+ *
+ * @param sFromURL URL of the source document (may be null if unknown)
+ * @param sToURL URL of the destination document (may be null if unknown)
+ *
+ * @throws ConvertException If any conversion error occurs.
+ * @throws IOException If any I/O error occurs.
+ */
+ public ConvertData convert(String sFromURL, String sToURL) throws
+ ConvertException, IOException {
+
+ ConvertData dataOut = new ConvertData();
+
+ if (toOffice) {
+
+ // From device format to Office format
+ //
+ DocumentDeserializerFactory myDocDeserializerFactory =
+ ci.getDocDeserializerFactory();
+ DocumentDeserializer deser =
+ myDocDeserializerFactory.createDocumentDeserializer(inputCD);
+ Document officeDoc = deser instanceof DocumentSerializer2 ?
+ ((DocumentDeserializer2) deser).deserialize(sFromURL,sToURL) :
+ deser.deserialize();
+
+
+ dataOut.addDocument(officeDoc);
+ return dataOut;
+
+ } else {
+
+ // From Office format to device format
+ //
+ DocumentSerializerFactory myDocSerializerFactory =
+ ci.getDocSerializerFactory();
+
+ Enumeration e = inputCD.getDocumentEnumeration();
+
+ Document doc = (Document) e.nextElement();
+ DocumentSerializer ser = myDocSerializerFactory.createDocumentSerializer(doc);
+ dataOut = ser instanceof DocumentSerializer2 ?
+ ((DocumentSerializer2) ser).serialize(sFromURL,sToURL) :
+ ser.serialize();
+
+ return dataOut;
+ }
+ }
+
+ /**
+ * Returns the appropriate &quot;Office&quot; <code>Document</code>
+ * object for this plug-in.
+ *
+ * @param name The name of the <code>Document</code> to create.
+ * @param is The <code>InputStream</code> corresponding to the
+ * <code>Document</code> to create.
+ *
+ * @return The appropriate &quot;Office&quot; <code>Document</code>
+ * object for this plug-in.
+ *
+ * @throws IOException If any I/O error occurs.
+ */
+ public Document getOfficeDocument(String name, InputStream is)
+ throws IOException {
+ return(ci.getPluginFactory().createOfficeDocument(name, is));
+ }
+
+
+ /**
+ * Returns the appropriate &quot;Device&quot; <code>Document</code>
+ * object for this plug-in.
+ *
+ * @param name The name of the <code>Document</code> to create.
+ * @param is The <code>InputStream</code> corresponding to the
+ * <code>Document</code> to create.
+ *
+ * @return The appropriate &quot;Device&quot; <code>Document</code>
+ * object for this plug-in.
+ *
+ * @throws IOException If any I/O error occurs.
+ */
+ public Document getDeviceDocument(String name, InputStream is)
+ throws IOException {
+ return(ci.getPluginFactory().createDeviceDocument(name, is));
+ }
+}
+
diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/ConvertData.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/ConvertData.java
new file mode 100644
index 000000000000..b2da0e3f3e4c
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/ConvertData.java
@@ -0,0 +1,117 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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;
+
+import java.util.Vector;
+import java.util.Enumeration;
+
+/**
+ * <p><code>ConvertData</code> is used as a container for passing
+ * <code>Document</code> objects in and out of the <code>Convert</code>
+ * class. The <code>ConvertData</code> contains a <code>String</code>
+ * name and a <code>Vector</code> of <code>Document</code> objects.</p>
+ *
+ * @author Martin Maher
+ */
+public class ConvertData {
+
+ /**
+ * Vector of <code>Document</code> objects.
+ */
+ private Vector v = new Vector();
+
+ /**
+ * Name of the <code>ConvertData</code> object.
+ */
+ private String name;
+
+
+ /**
+ * Resets ConvertData. This empties all <code>Document</code>
+ * objects from this class. This allows reuse of a
+ * <code>ConvertData</code>.
+ */
+ public void reset() {
+ name = null;
+ v.removeAllElements();
+ }
+
+ /**
+ * Returns the <code>Document</code> name.
+ *
+ * @return The <code>Document</code> name.
+ */
+ public String getName() {
+ return name;
+ }
+
+
+ /**
+ * Sets the <code>Document</code> name.
+ *
+ * @param docName The name of the <code>Document</code>.
+ */
+ public void setName(String docName) {
+ name = docName;
+ }
+
+
+ /**
+ * Adds a <code>Document</code> to the vector.
+ *
+ * @param doc The <code>Document</code> to add.
+ */
+ public void addDocument(Document doc) {
+ v.add(doc);
+ }
+
+
+ /**
+ * Gets an <code>Enumeration</code> to access the <code>Vector</code>
+ * of <code>Document</code> objects.
+ *
+ * @return The <code>Enumeration</code> to access the
+ * <code>Vector</code> of <code>Document</code> objects.
+ */
+ public Enumeration getDocumentEnumeration() {
+ Enumeration enumerate = v.elements();
+ return (enumerate);
+ }
+
+
+ /**
+ * Gets the number of <code>Document</code> objects currently stored
+ *
+ * @return The number of <code>Document</code> objects currently
+ * stored.
+ */
+ public int getNumDocuments() {
+ return (v.size());
+ }
+}
+
diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/ConvertException.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/ConvertException.java
new file mode 100644
index 000000000000..5065ca20ca10
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/ConvertException.java
@@ -0,0 +1,45 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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;
+
+/**
+ * This <code>Exception</code> is thrown by convert algorithms.
+ */
+public class ConvertException extends Exception {
+
+ /**
+ * Exception thrown by convert algorithms.
+ *
+ * @param message Message to be included in the
+ * <code>Exception</code>.
+ */
+ public ConvertException(String message) {
+ super(message);
+ }
+}
+
diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/ConverterCapabilities.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/ConverterCapabilities.java
new file mode 100644
index 000000000000..b26e0d5559b8
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/ConverterCapabilities.java
@@ -0,0 +1,67 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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;
+
+/**
+ * <p>A <code>ConverterCapabilities</code> object is used by
+ * <code>DocumentMerger</code> implementations. The
+ * <code>ConverterCapabilities</code> indicates which
+ * &quot;Office&quot; XML tags are supported by the
+ * &quot;Device&quot; format.</p>
+ *
+ * @see org.openoffice.xmerge.PluginFactory
+ * @see org.openoffice.xmerge.DocumentMerger
+ */
+public interface ConverterCapabilities {
+
+
+ /**
+ * Test to see if the device document format supports the
+ * tag in question.
+ *
+ * @param tag The tag to check.
+ *
+ * @return true if the device format supports the
+ * tag, false otherwise.
+ */
+ boolean canConvertTag(String tag);
+
+
+ /**
+ * Test to see if the device document format supports the
+ * tag attribute in question.
+ *
+ * @param tag The tag to check.
+ * @param attribute The tag attribute to check.
+ *
+ * @return true if the device format supports the
+ * attribute, false otherwise.
+ */
+ boolean canConvertAttribute(String tag, String attribute);
+}
+
diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/ConverterFactory.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/ConverterFactory.java
new file mode 100644
index 000000000000..b6afe0d42bfa
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/ConverterFactory.java
@@ -0,0 +1,124 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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;
+
+import org.openoffice.xmerge.util.registry.ConverterInfo;
+import org.openoffice.xmerge.util.registry.ConverterInfoMgr;
+
+/**
+ * Factory that provides access to <code>Convert</code> objects, which
+ * are used to do a conversion. The <code>ConvertFactory</code> does
+ * this via the <code>ConvertInfoMgr</code> which maintains a list of
+ * which <code>Convert</code> objects are available and their
+ * capabilities.
+ *
+ * @see Convert
+ * @see org.openoffice.xmerge.util.registry.ConverterInfoMgr
+ *
+ * @author Martin Maher
+ */
+public class ConverterFactory {
+
+ /**
+ * Confirms whether or not a particular conversion can be done
+ * based on the Mime types of the files to be converted to and
+ * from.
+ *
+ * @param mimeTypeIn The mime input type.
+ * @param mimeTypeOut The mime output type.
+ *
+ * @return true if the conversion is possible, false otherwise.
+ */
+ public boolean canConvert(String mimeTypeIn, String mimeTypeOut) {
+
+ ConverterInfo foundInfo = null;
+
+ // findConverterInfo expects the second paramenter to be the
+ // destination MimeType
+ if (foundInfo.isValidOfficeType(mimeTypeOut))
+ foundInfo = ConverterInfoMgr.findConverterInfo(mimeTypeIn, mimeTypeOut);
+ else
+ foundInfo = ConverterInfoMgr.findConverterInfo(mimeTypeOut, mimeTypeIn);
+
+ if (foundInfo != null)
+ return true;
+ else
+ return false;
+ }
+
+
+ /**
+ * Returns the <code>Convert</code> object that converts
+ * the specified device/office mime type conversion. If there
+ * are multiple <code>Converter</code> objects registered
+ * that support this conversion, only the first is returned.
+ *
+ * @param mimeTypeIn The mime input type.
+ * @param mimeTypeOut The mime output type.
+ *
+ * @return The first <code>Convert</code> object that supports
+ * the specified conversion.
+ */
+ public Convert getConverter(String mimeTypeIn, String mimeTypeOut) {
+
+ ConverterInfo foundInfo = null;
+ boolean toOffice;
+
+ toOffice = foundInfo.isValidOfficeType(mimeTypeOut);
+
+ // findConverterInfo expects the second paramenter to be the
+ // destination MimeType
+ if (toOffice)
+ foundInfo = ConverterInfoMgr.findConverterInfo(mimeTypeIn, mimeTypeOut);
+ else
+ foundInfo = ConverterInfoMgr.findConverterInfo(mimeTypeOut, mimeTypeIn);
+
+ if (foundInfo != null)
+ return getConverter(foundInfo, toOffice);
+ else
+ return null;
+ }
+
+
+ /**
+ * Returns the <code>Convert</code> object that is described
+ * by the <code>ConverterInfo</code> parameter.
+ *
+ * @param ci The <code>ConverterInfo</code> describing the converter.
+ *
+ * @param toOffice true to convert to office, false to convert to device.
+ *
+ * @return The <code>Convert</code> object
+ */
+ public Convert getConverter(ConverterInfo ci, boolean toOffice) {
+
+ Convert myConvert = new Convert(ci, toOffice);
+ return myConvert;
+ }
+}
+
diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/Document.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/Document.java
new file mode 100644
index 000000000000..786050a42a0e
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/Document.java
@@ -0,0 +1,99 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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;
+
+import java.io.OutputStream;
+import java.io.InputStream;
+import java.io.IOException;
+
+/**
+ * <p>A <code>Document</code> represents any <code>Document</code>
+ * to be converted and the resulting <code>Document</code> from any
+ * conversion.</p>
+ *
+ * <p>It is created by the <code>PluginFactory</code> object's {@link
+ * org.openoffice.xmerge.PluginFactory#createOfficeDocument
+ * createOfficeDocument} method or the {@link
+ * org.openoffice.xmerge.PluginFactory#createDeviceDocument
+ * createDeviceDocument} method.</p>
+ *
+ * @author Herbie Ong
+ * @see org.openoffice.xmerge.PluginFactory
+ */
+public interface Document {
+
+ /**
+ * <p>Writes out the <code>Document</code> content to the specified
+ * <code>OutputStream</code>.</p>
+ *
+ * <p>This method may not be thread-safe.
+ * Implementations may or may not synchronize this
+ * method. User code (i.e. caller) must make sure that
+ * calls to this method are thread-safe.</p>
+ *
+ * @param os <code>OutputStream</code> to write out the
+ * <code>Document</code> content.
+ *
+ * @throws IOException If any I/O error occurs.
+ */
+ public void write(OutputStream os) throws IOException;
+
+
+ /**
+ * <p>Reads the content from the <code>InputStream</code> into
+ * the <code>Document</code>.</p>
+ *
+ * <p>This method may not be thread-safe.
+ * Implementations may or may not synchronize this
+ * method. User code (i.e. caller) must make sure that
+ * calls to this method are thread-safe.</p>
+ *
+ * @param is <code>InputStream</code> to read in the
+ * <code>Document</code> content.
+ *
+ * @throws IOException If any I/O error occurs.
+ */
+ public void read(InputStream is) throws IOException;
+
+
+ /**
+ * Returns the <code>Document</code> name with no file extension.
+ *
+ * @return The <code>Document</code> name with no file extension.
+ */
+ public String getName();
+
+
+ /**
+ * Returns the <code>Document</code> name with file extension.
+ *
+ * @return The <code>Document</code> name with file extension.
+ */
+ public String getFileName();
+}
+
diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/DocumentDeserializer.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/DocumentDeserializer.java
new file mode 100644
index 000000000000..4c26453a982b
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/DocumentDeserializer.java
@@ -0,0 +1,65 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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;
+
+import java.io.IOException;
+
+/**
+ * <p>A <code>DocumentDeserializer</code> represents a converter that
+ * converts &quot;Device&quot; <code>Document</code> objects into the
+ * &quot;Office&quot; <code>Document</code> format.</p>
+ *
+ * <p>The <code>DocumentDeserializer</code> object is created by
+ * the </code>PluginFactory</code> {@link
+ * org.openoffice.xmerge.DocumentDeserializerFactory#createDocumentDeserializer
+ * createDocumentDeserializer} method. When it is constructed, a
+ * <code>ConvertData</code> object is passed in to be used as input.</p>
+ *
+ * @author Herbie Ong
+ * @see org.openoffice.xmerge.PluginFactory
+ * @see org.openoffice.xmerge.DocumentDeserializerFactory
+ */
+public interface DocumentDeserializer {
+
+ /**
+ * <p>Convert the data passed into the <code>DocumentDeserializer</code>
+ * constructor into the &quot;Office&quot; <code>Document</code>
+ * format.</p>
+ *
+ * <p>This method may or may not be thread-safe. It is expected
+ * that the user code does not call this method in more than one
+ * thread. And for most cases, this method is only done once.</p>
+ *
+ * @return The resulting <code>Document</code> object from conversion.
+ *
+ * @throws ConvertException If any Convert error occurs.
+ * @throws IOException If any I/O error occurs.
+ */
+ public Document deserialize() throws ConvertException, IOException;
+}
+
diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/DocumentDeserializer2.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/DocumentDeserializer2.java
new file mode 100644
index 000000000000..417144b7ac49
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/DocumentDeserializer2.java
@@ -0,0 +1,71 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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;
+
+import java.io.IOException;
+
+/**
+ * <p>A <code>DocumentDeserializer</code> represents a converter that
+ * converts &quot;Device&quot; <code>Document</code> objects into the
+ * &quot;Office&quot; <code>Document</code> format.</p>
+ *
+ * <p>The <code>PluginFactory</code> {@link
+ * org.openoffice.xmerge.DocumentDeserializerFactory#createDocumentDeserializer
+ * createDocumentDeserializer} method creates a <code>DocumentDeserializer</code>,
+ * which may or may not implement <code>DocumentDeserializer2</code>.
+ * When it is constructed, a
+ * <code>ConvertData</code> object is passed in to be used as input.</p>
+ *
+ * @author Henrik Just
+ * @see org.openoffice.xmerge.PluginFactory
+ * @see org.openoffice.xmerge.DocumentDeserializerFactory
+ */
+public interface DocumentDeserializer2 extends DocumentSerializer {
+
+ /**
+ * <p>Convert the data passed into the <code>DocumentDeserializer2</code>
+ * constructor into the &quot;Office&quot; <code>Document</code>
+ * format. The URL's passed may be used to resolve links and to choose the
+ * name of the output office document.</p>
+ *
+ * <p>This method may or may not be thread-safe. It is expected
+ * that the user code does not call this method in more than one
+ * thread. And for most cases, this method is only done once.</p>
+ *
+ * @return The resulting <code>Document</code> object from conversion.
+ *
+ * @param deviceURL URL of the device document (may be null if unknown)
+ * @param officeURL URL of the office document (may be null if unknown)
+ *
+ * @throws ConvertException If any Convert error occurs.
+ * @throws IOException If any I/O error occurs.
+ */
+ public Document deserialize(String deviceURL, String officeURL) throws
+ ConvertException, IOException;
+}
+
diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/DocumentDeserializerFactory.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/DocumentDeserializerFactory.java
new file mode 100644
index 000000000000..315ebc0cf051
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/DocumentDeserializerFactory.java
@@ -0,0 +1,65 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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;
+
+/**
+ * <p>A <code>DocumentDeserializer</code> object is used to convert
+ * from the &quot;Device&quot; <code>Documetn</code> format to the
+ * &quot;Office&quot; <code>Document</code> format.</p>
+ *
+ * <p>All plug-in implementations of the <code>PluginFactory</code>
+ * interface that also support deserialization must also
+ * implement this interface.</p>
+ *
+ * @see PluginFactory
+ * @see DocumentDeserializer
+ */
+public interface DocumentDeserializerFactory {
+
+ /**
+ * The <code>DocumentDeserializer</code> is used to convert
+ * from the &quot;Device&quot; <code>Document</code> format to
+ * the &quot;Office&quot; <code>Document</code> format.</p>
+ *
+ * The <code>ConvertData</code> object is passed along to the
+ * created <code>DocumentDeserializer</code> via its constructor.
+ * The <code>ConvertData</code> is read and converted when the
+ * the <code>DocumentDeserializer</code> object's
+ * <code>deserialize</code> method is called.
+ * </p>
+ *
+ * @param cd <code>ConvertData</code> object that the created
+ * <code>DocumentDeserializer</code> object uses as
+ * input.
+ *
+ * @return A <code>DocumentDeserializer</code> object.
+ */
+
+ public DocumentDeserializer createDocumentDeserializer(ConvertData cd);
+}
+
diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/DocumentMerger.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/DocumentMerger.java
new file mode 100644
index 000000000000..3f29b2dad7f2
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/DocumentMerger.java
@@ -0,0 +1,93 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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;
+
+/**
+ * <p>A <code>DocumentMerger</code> can merge changes from a modified
+ * &quot;Device&quot; <code>Document</code> to the assigned original
+ * &quot;Office&quot; <code>Document</code>.</p>
+ *
+ * <p>Merge is useful when an <code>OfficeDocument</code>
+ * is converted to a &quot;Device&quot; <code>Document</code> format,
+ * and the &quot;Device&quot; <code>Document</code> version is modified.
+ * Those changes can be merged back into the original
+ * <code>OfficeDocument</code> with the merger. The merger is capable
+ * of doing this even if the &quot;Device&quot; format is lossy in
+ * comparison to the <code>OfficeDocument</code> format.</p>
+ *
+ * <p>The <code>ConverterCapabilities</code> object is what the
+ * DocumentMerger utilizes to know how the &quot;Office&quot;
+ * <code>Document</code> tags are supported in the &quot;Device&quot;
+ * format.</p>
+ *
+ * <p>The <code>DocumentMerger</code> object is created by a
+ * the <code>DocumentMergerFactory</code> {@link
+ * org.openoffice.xmerge.DocumentMergerFactory#createDocumentMerger
+ * createDocumenMerger} method. When it is constructed, the
+ * &quot;Original Office&quot; <code>Document</code> object is
+ * passed in to be used as input.</p>
+ *
+ * @author Herbie Ong
+ * @see org.openoffice.xmerge.PluginFactory
+ * @see org.openoffice.xmerge.DocumentMergerFactory
+ * @see org.openoffice.xmerge.ConverterCapabilities
+ */
+public interface DocumentMerger {
+
+ /**
+ * <p>This method will find the changes that had happened
+ * in the <code>modifiedDoc</code> <code>Document</code>
+ * object given the designated original <code>Document</code>.</p>
+ *
+ * <p>Note that this process may need the knowledge of the
+ * conversion process since some conversion process are lossy.
+ * Items/Data that are lost during the conversion process are not
+ * classified as changes. The main target of this method
+ * is to apply the changes done in <code>modifiedDoc</code>
+ * into the assigned original <code>Document</code> object, thus
+ * it also will try to preserve items that were originally in
+ * the original <code>Document</code>, but never got transferred
+ * during the
+ * {@link org.openoffice.xmerge.DocumentSerializer#serialize
+ * serialize} process/method call. After this method call, the
+ * original <code>Document</code> object will contain the changes
+ * applied.</p>
+ *
+ * <p>This method may or may not be thread-safe.
+ * Also, it is expected that the user uses only one instance
+ * of a <code>DocumentMerger</code> object per merge process.
+ * Create another <code>DocumentMerger</code> object for another
+ * merge process.</p>
+ *
+ * @param modifiedDoc device <code>Document</code> object.
+ *
+ * @throws MergeException If any merge error occurs.
+ */
+ public void merge(Document modifiedDoc) throws MergeException;
+}
+
diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/DocumentMergerFactory.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/DocumentMergerFactory.java
new file mode 100644
index 000000000000..3daa41db4b99
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/DocumentMergerFactory.java
@@ -0,0 +1,63 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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;
+
+/**
+ * <p>All plug-in implementations of the <code>PluginFactory</code>
+ * interface that also support merging must also implement
+ * this interface.</p>
+ *
+ * <p>Merge is useful when an <code>OfficeDocument</code>
+ * is converted to a &quot;Device&quot; <code>Document</code> format,
+ * and the &quot;Device&quot; <code>Document</code> version is modified.
+ * Those changes can be merged back into the original
+ * <code>OfficeDocument</code> with the merger. The merger is capable
+ * of doing this even if the &quot;Device&quot; format is lossy in
+ * comparison to the <code>OfficeDocument</code> format.</p>
+ *
+ * @see PluginFactory
+ * @see DocumentMerger
+ * @see ConverterCapabilities
+ *
+ */
+public interface DocumentMergerFactory {
+
+ /**
+ * <p>Create a <code>DocumentMerger</code> object given a
+ * <code>Document</code> object.</p>
+ *
+ * @param doc <code>Document</code> object that the created
+ * <code>DocumentMerger</code> object uses as a base
+ * <code>Document</code> for merging changes into.
+ *
+ * @return A <code>DocumentMerger</code> object or null if none
+ * exists.
+ */
+ public DocumentMerger createDocumentMerger(Document doc);
+}
+
diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/DocumentSerializer.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/DocumentSerializer.java
new file mode 100644
index 000000000000..d46e5baca765
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/DocumentSerializer.java
@@ -0,0 +1,67 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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;
+
+import java.io.IOException;
+
+/**
+ * <p>A <code>DocumentSerializer</code> represents a converter that
+ * converts a &quot;Office&quot; <code>Document</code> to a
+ * &quot;Device&quot; <code>Document</code> format.</p>
+ *
+ * <p>The <code>DocumentSerializer</code> object is created by a
+ * the <code>PluginFactory</code> {@link
+ * org.openoffice.xmerge.DocumentSerializerFactory#createDocumentSerializer
+ * createDocumentSerializer} method. When it is constructed, a
+ * &quot;Office&quot; <code>Document</code> object is passed in to
+ * be used as input.</p>
+ *
+ * @author Herbie Ong
+ * @see org.openoffice.xmerge.PluginFactory
+ * @see org.openoffice.xmerge.DocumentSerializerFactory
+ */
+public interface DocumentSerializer {
+
+ /**
+ * <p>Convert the data passed into the <code>DocumentSerializer</code>
+ * constructor into the &quot;Device&quot; <code>Document</code>
+ * format.</p>
+ *
+ * <p>This method may or may not be thread-safe. It is expected
+ * that the user code does not call this method in more than one
+ * thread. And for most cases, this method is only done once.</p>
+ *
+ * @return <code>ConvertData</code> object to pass back the
+ * converted data.
+ *
+ * @throws ConvertException If any conversion error occurs.
+ * @throws IOException If any I/O error occurs.
+ */
+ public ConvertData serialize() throws ConvertException, IOException;
+}
+
diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/DocumentSerializer2.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/DocumentSerializer2.java
new file mode 100644
index 000000000000..dd3ee25460a6
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/DocumentSerializer2.java
@@ -0,0 +1,73 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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;
+
+import java.io.IOException;
+
+/**
+ * <p>A <code>DocumentSerializer2</code> represents a converter that
+ * converts a &quot;Office&quot; <code>Document</code> to a
+ * &quot;Device&quot; <code>Document</code> format.</p>
+ *
+ * <p>The <code>PluginFactory</code> {@link
+ * org.openoffice.xmerge.DocumentSerializerFactory#createDocumentSerializer
+ * createDocumentSerializer} method creates a <code>DocumentSerializer</code>,
+ * which may or may not implement <code>DocumentSerializer2</code>.
+ * When it is constructed, a
+ * &quot;Office&quot; <code>Document</code> object is passed in to
+ * be used as input.</p>
+ *
+ * @author Henrik Just
+ * @see org.openoffice.xmerge.PluginFactory
+ * @see org.openoffice.xmerge.DocumentSerializerFactory
+ */
+public interface DocumentSerializer2 extends DocumentSerializer {
+
+ /**
+ * <p>Convert the data passed into the <code>DocumentSerializer2</code>
+ * constructor into the &quot;Device&quot; <code>Document</code>
+ * format. The URL's passed may be used to resolve links and to name
+ * the output device document(s).</p>
+ *
+ * <p>This method may or may not be thread-safe. It is expected
+ * that the user code does not call this method in more than one
+ * thread. And for most cases, this method is only done once.</p>
+ *
+ * @return <code>ConvertData</code> object to pass back the
+ * converted data.
+ *
+ * @param officeURL URL of the office document (may be null if unknown)
+ * @param deviceURL URL of the device document (may be null if unknown)
+ *
+ * @throws ConvertException If any conversion error occurs.
+ * @throws IOException If any I/O error occurs.
+ */
+ public ConvertData serialize(String officeURL, String deviceURL) throws
+ ConvertException, IOException;
+}
+
diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/DocumentSerializerFactory.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/DocumentSerializerFactory.java
new file mode 100644
index 000000000000..6d6f47078b5e
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/DocumentSerializerFactory.java
@@ -0,0 +1,63 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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;
+
+/**
+ * <p>A <code>DocumentSerializer</code> object is used to convert
+ * from the &quot;Office&quot; <code>Document</code> format to the
+ * &quot;Device&quot; <code>Document</code> format.</p>
+ *
+ * <p>All plug-in implementations of the <code>PluginFactory</code>
+ * interface that also support serialization must also
+ * implement this interface.</p>
+ *
+ * @see PluginFactory
+ * @see DocumentSerializer
+ */
+public interface DocumentSerializerFactory {
+
+ /**
+ * <p>The <code>DocumentSerializer</code> is used to convert
+ * from the &quot;Office&quot; <code>Document</code> format
+ * to the &quot;Device&quot; <code>Document</code> format.</p>
+ *
+ * The <code>ConvertData</code> object is passed along to the
+ * created <code>DocumentSerializer</code> via its constructor.
+ * The <code>ConvertData</code> is read and converted when the
+ * the <code>DocumentSerializer</code> object's
+ * <code>serialize</code> method is called.
+ *
+ * @param doc <code>Document</code> object that the created
+ * <code>DocumentSerializer</code> object uses
+ * as input.
+ *
+ * @return A <code>DocumentSerializer</code> object.
+ */
+ public DocumentSerializer createDocumentSerializer(Document doc);
+}
+
diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/MergeException.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/MergeException.java
new file mode 100644
index 000000000000..8c649c8fa6cf
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/MergeException.java
@@ -0,0 +1,45 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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;
+
+/**
+ * This <code>Exception</code> is thrown by merge algorithms.
+ */
+public class MergeException extends Exception {
+
+ /**
+ * Exception thrown by merge algorithms.
+ *
+ * @param message Message to be included in the
+ * <code>Exception</code>.
+ */
+ public MergeException(String message) {
+ super(message);
+ }
+}
+
diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/PluginFactory.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/PluginFactory.java
new file mode 100644
index 000000000000..de7baba4683f
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/PluginFactory.java
@@ -0,0 +1,193 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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;
+
+import org.openoffice.xmerge.util.registry.ConverterInfo;
+import java.io.InputStream;
+import java.io.IOException;
+
+/**
+ * <p>A <code>PluginFactory</code> encapsulates the
+ * conversions from one <code>Document</code> format to another.
+ * It provides conversions in both directions. Refer to the
+ * <a href="package-summary.html#package_description">
+ * package description</a> for its usage.</p>
+ *
+ * <p>Conversion from the &quot;Office&quot; <code>Document</code>
+ * format to a &quot;Device&quot; <code>Document</code> format may
+ * be lossy, i.e. some information may be lost. If a plug-in
+ * implements the <code>DocumentMergerFactory</code> interface,
+ * then there is the possibility for merging the changes done on the
+ * &quot;Device&quot; <code>Document</code> back to the original
+ * &quot;Office&quot; <code>Document</code> via the
+ * <code>DocumentMerger</code> interface.</p>
+ *
+ * <p>Plug-ins that convert from the &quot;Device&quot;
+ * <code>Document</code> format to the &quot;Office&quot;
+ * <code>Document</code> format must implement the
+ * <code>DocumentDeserializerFactory</code> interface. Plug-ins
+ * that convert from the &quot;Office&quot; <code>Document</code>
+ * format to the &quot;Device&quot; format must implement the
+ * <code>DocumentSerializerFactory</code> interface.
+ *
+ * <p>All plug-ins should have an associated Plugin Configuration XML
+ * File which describes the capabilities of the plug-in. If the
+ * plug-in is bundled in a jarfile, then this XML file is also bundled
+ * with the jarfile. The data in the XML file is managed by the
+ * <code>ConverterInfo</code> object. The <code>ConverterInfoMgr</code>
+ * manages a registry of all <code>ConverterInfo</code> objects. For
+ * more information about this XML file, refer to
+ * <a href="converter/xml/sxc/package-summary.html">
+ * org.openoffice.xmerge.util.registry</a>.</p>
+ *
+ * @author Herbie Ong
+ * @see Document
+ * @see DocumentSerializer
+ * @see DocumentSerializerFactory
+ * @see DocumentDeserializer
+ * @see DocumentDeserializerFactory
+ * @see DocumentMerger
+ * @see DocumentMergerFactory
+ * @see ConverterInfo
+ * @see org.openoffice.xmerge.util.registry.ConverterInfoMgr
+ */
+
+public abstract class PluginFactory {
+
+ /**
+ * Cached <code>ConvertInfo</code> object.
+ */
+ private ConverterInfo ciCache;
+
+
+ /**
+ * Constructor that caches the <code>ConvertInfo</code> that
+ * corresponds to the registry information for this plug-in.
+ *
+ * @param ci <code>ConvertInfo</code> object.
+ */
+ public PluginFactory(ConverterInfo ci) {
+ ciCache=ci;
+ }
+
+
+ /**
+ * Returns the <code>ConvertInfo</code> that corresponds to this
+ * plug-in.
+ *
+ * @return The <code>ConvertInfo</code> that corresponds to this
+ * plug-in.
+ */
+ public ConverterInfo getConverterInfo () {
+ return ciCache;
+ }
+
+
+ /**
+ * <p>Create a <code>Document</code> object that corresponds to
+ * the Office data passed in via the <code>InputStream</code>
+ * object. This abstract method must be implemented for each
+ * plug-in.</p>
+ *
+ * <p>This method will read from the given <code>InputStream</code>
+ * object. The returned <code>Document</code> object will contain
+ * the necessary data for the other objects created by the
+ * <code>PluginFactory</code> to process, like a
+ * <code>DocumentSerializer</code> object and a
+ * <code>DocumentMerger</code> object.</p>
+ *
+ * @param name The <code>Document</code> name.
+ * @param is <code>InputStream</code> object corresponding
+ * to the <code>Document</code>.
+ *
+ * @return A <code>Document</code> object representing the
+ * particular <code>Document</code> format for the
+ * <code>PluginFactory</code>.
+ *
+ * @throws IOException If any I/O error occurs.
+ */
+ public abstract Document createOfficeDocument(String name, InputStream is)
+ throws IOException;
+
+
+ /**
+ * <p>Create a <code>Document</code> object that corresponds to
+ * the Office data passed in via the <code>InputStream</code>
+ * object. This abstract method must be implemented for each
+ * plug-in.</p>
+ *
+ * <p>This method will read from the given <code>InputStream</code>
+ * object. The returned <code>Document</code> object will contain
+ * the necessary data for the other objects created by the
+ * <code>PluginFactory</code> to process, like a
+ * <code>DocumentSerializer</code> object and a
+ * <code>DocumentMerger</code> object.</p>
+ *
+ * @param name The <code>Document</code> name.
+ * @param is <code>InputStream</code> object corresponding
+ * to the <code>Document</code>.
+ * @param isZip <code>boolean</code> to show that the created office
+ * document is to be zipped.
+ *
+ * @return A <code>Document</code> object representing the
+ * particular <code>Document</code> format for the
+ * <code>PluginFactory</code>.
+ *
+ * @throws IOException If any I/O error occurs.
+ */
+ public abstract Document createOfficeDocument(String name, InputStream is,boolean isZip)
+ throws IOException;
+
+
+ /**
+ * <p>Create a <code>Document</code> object that corresponds to
+ * the device data passed in via the <code>InputStream</code>
+ * object. This abstract method must be implemented for each
+ * plug-in.</p>
+ *
+ * <p>This method will read from the given <code>InputStream</code>
+ * object. The returned <code>Document</code> object will contain
+ * the necessary data for the other objects created by the
+ * <code>PluginFactory</code> to process, like a
+ * <code>DocumentSerializer</code> object and a
+ * <code>DocumentMerger</code> object.</p>
+ *
+ * @param name The <code>Document</code> name.
+ * @param is <code>InputStream</code> object corresponding
+ * to the <code>Document</code>.
+ *
+ * @return A <code>Document</code> object representing the
+ * particular <code>Document</code> format for the
+ * <code>PluginFactory</code>.
+ *
+ * @throws IOException If any I/O error occurs.
+ */
+ public abstract Document createDeviceDocument(String name, InputStream is)
+ throws IOException;
+}
+
diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/Version.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/Version.java
new file mode 100644
index 000000000000..b639d95a8116
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/Version.java
@@ -0,0 +1,88 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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;
+
+/**
+ * This class provides a quick utility to check the version of the
+ * jar file. It has a main method that prints out the version
+ * info. It also provides two static methods for runtime classes
+ * to query.
+ *
+ * @author Herbie Ong
+ */
+public final class Version {
+
+ private static Package pkg;
+
+ private static Version version;
+
+ static {
+
+ version = new Version();
+ pkg = version.getClass().getPackage();
+ }
+
+ /**
+ * Private constructor to provide a singleton instance.
+ */
+ private Version() {
+ }
+
+ /**
+ * Returns specification version.
+ *
+ * @return The specification version.
+ */
+ public static String getSpecificationVersion() {
+ return pkg.getSpecificationVersion();
+ }
+
+ /**
+ * Returns implementation version.
+ *
+ * @return The implementation version.
+ */
+
+ public static String getImplementationVersion() {
+ return pkg.getImplementationVersion();
+ }
+
+ /**
+ * Main method for printing out version info.
+ *
+ * @param args Array of arguments, not used.
+ */
+ public static void main(String args[]) {
+
+ System.out.println("Specification-Title: " + pkg.getSpecificationTitle());
+ System.out.println("Specification-Vendor: " + pkg.getSpecificationVendor());
+ System.out.println("Specification-Version: " + pkg.getSpecificationVersion());
+ System.out.println("Implementation-Version: " + pkg.getImplementationVersion());
+ }
+}
+
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..f7df881b1061
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/dom/DOMDocument.java
@@ -0,0 +1,412 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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..0198fdd4576f
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/dom/package.html
@@ -0,0 +1,55 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+ <!--
+ #*************************************************************************
+ #
+ DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+
+ Copyright 2000, 2010 Oracle and/or its affiliates.
+
+ OpenOffice.org - a multi-platform office productivity suite
+
+ 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..11286ee824ce
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/palm/PalmDB.java
@@ -0,0 +1,469 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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..668bfdb7f155
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/palm/PalmDocument.java
@@ -0,0 +1,177 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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..68e7c66beb11
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/palm/PdbDecoder.java
@@ -0,0 +1,233 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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..46433e88ec2e
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/palm/PdbEncoder.java
@@ -0,0 +1,196 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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..2f9d389e80cc
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/palm/PdbHeader.java
@@ -0,0 +1,162 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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..d0714d677960
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/palm/PdbUtil.java
@@ -0,0 +1,105 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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..23690792bdbf
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/palm/Record.java
@@ -0,0 +1,216 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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..f2628bb0b235
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/palm/package.html
@@ -0,0 +1,142 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<!--
+ #*************************************************************************
+ #
+ DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+
+ Copyright 2000, 2010 Oracle and/or its affiliates.
+
+ OpenOffice.org - a multi-platform office productivity suite
+
+ 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..ac82675b229d
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/EmbeddedBinaryObject.java
@@ -0,0 +1,127 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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..11240b4bceac
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/EmbeddedObject.java
@@ -0,0 +1,116 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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..6e5f123ded53
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/EmbeddedXMLObject.java
@@ -0,0 +1,295 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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..afa889cb9e3f
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/OfficeConstants.java
@@ -0,0 +1,439 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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..d3372be5a757
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/OfficeDocument.java
@@ -0,0 +1,1265 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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..2c8ae71f2af3
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/OfficeDocumentException.java
@@ -0,0 +1,130 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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..9752650533a1
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/OfficeZip.java
@@ -0,0 +1,458 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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..662a542d0e7b
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/ParaStyle.java
@@ -0,0 +1,601 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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..cc1e1d3d530c
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/Style.java
@@ -0,0 +1,227 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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..c783d366a68b
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/StyleCatalog.java
@@ -0,0 +1,390 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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..91926310776c
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/TextStyle.java
@@ -0,0 +1,679 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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..9d4d0f5bd5cf
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/package.html
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<!--
+ #*************************************************************************
+ #
+ DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+
+ Copyright 2000, 2010 Oracle and/or its affiliates.
+
+ OpenOffice.org - a multi-platform office productivity suite
+
+ 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..9c1ae2768524
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/BookSettings.java
@@ -0,0 +1,228 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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..2e3e34b6d620
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/CellStyle.java
@@ -0,0 +1,510 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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..14c1dbfc8e77
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/ColumnRowInfo.java
@@ -0,0 +1,195 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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..76c742b57496
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/ColumnStyle.java
@@ -0,0 +1,300 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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..2fa5f22303b2
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/DocumentMergerImpl.java
@@ -0,0 +1,198 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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 &quot;Office&quot; <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..2bb836a7303d
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/Format.java
@@ -0,0 +1,475 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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..de83543fd86e
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/NameDefinition.java
@@ -0,0 +1,215 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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..2b4ce04a13f0
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/RowStyle.java
@@ -0,0 +1,300 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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..4258e1a5e035
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/SheetSettings.java
@@ -0,0 +1,373 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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..169f54a65845
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/SpreadsheetDecoder.java
@@ -0,0 +1,180 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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 &quot;Device&quot;
+ * <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..fc965bcd88b1
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/SpreadsheetEncoder.java
@@ -0,0 +1,129 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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..32fd85fa071a
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/SxcConstants.java
@@ -0,0 +1,49 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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..5561ef3c9d9c
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/SxcDocument.java
@@ -0,0 +1,92 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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..2eb507d09b7b
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/SxcDocumentDeserializer.java
@@ -0,0 +1,792 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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 &quot;Device&quot;
+ * <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", "&apos;Courier New&apos;", "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..b0cfc09893bc
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/SxcDocumentSerializer.java
@@ -0,0 +1,993 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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
+ * &quot;Device&quot; <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 &quot;Device&quot;
+ * <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..401bdff1f533
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/SxcPluginFactory.java
@@ -0,0 +1,82 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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..d350ea262303
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxc/package.html
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<!--
+ #*************************************************************************
+ #
+ DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+
+ Copyright 2000, 2010 Oracle and/or its affiliates.
+
+ OpenOffice.org - a multi-platform office productivity suite
+
+ 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 &quot;Device&quot; <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..0485c4a435c0
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxw/SxwDocument.java
@@ -0,0 +1,94 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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..a1f53bfd9ede
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxw/SxwPluginFactory.java
@@ -0,0 +1,76 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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..a9ced174ff5a
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/sxw/package.html
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<!--
+ #*************************************************************************
+ #
+ DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+
+ Copyright 2000, 2010 Oracle and/or its affiliates.
+
+ OpenOffice.org - a multi-platform office productivity suite
+
+ 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 &quot;Device&quot; <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..5f6e4ac38ce2
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/xslt/ConverterCapabilitiesImpl.java
@@ -0,0 +1,93 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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 &quot;Office&quot;
+ * <code>Document</code> tags and attributes are supported on the
+ * &quot;Device&quot; <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..e118cc690ea0
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/xslt/DocumentDeserializerImpl.java
@@ -0,0 +1,250 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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..6416d0d531a0
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/xslt/DocumentMergerImpl.java
@@ -0,0 +1,97 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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..5bf8d4983da1
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/xslt/DocumentSerializerImpl.java
@@ -0,0 +1,297 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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..d0ecb2c943ac
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/xslt/GenericOfficeDocument.java
@@ -0,0 +1,93 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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..36872751e999
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/xslt/PluginFactoryImpl.java
@@ -0,0 +1,202 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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..aeb6fdaba443
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/xslt/XsltPlugin.properties
@@ -0,0 +1,37 @@
+#*************************************************************************
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# Copyright 2000, 2010 Oracle and/or its affiliates.
+#
+# OpenOffice.org - a multi-platform office productivity suite
+#
+# 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.
+#
+#*************************************************************************
+# x-no-translate
+
+
+#
+# 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..30c71b15655f
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/converter/xml/xslt/package.html
@@ -0,0 +1,67 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<!--
+ #*************************************************************************
+ #
+ DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+
+ Copyright 2000, 2010 Oracle and/or its affiliates.
+
+ OpenOffice.org - a multi-platform office productivity suite
+
+ 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 &amp;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>
+
+
diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/DiffAlgorithm.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/DiffAlgorithm.java
new file mode 100644
index 000000000000..76158a7053c0
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/DiffAlgorithm.java
@@ -0,0 +1,51 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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.merger;
+
+/**
+ * This is the difference algorithm interface. It is an interface so
+ * that different algorithms may be plugged-in to actually compute
+ * the differences.
+ *
+ * NOTE: this code may not be thread safe.
+ */
+public interface DiffAlgorithm {
+
+ /**
+ * Returns a <code>Difference</code> array. This method finds out
+ * the difference between two sequences.
+ *
+ * @param orgSeq The original sequence of object.
+ * @param modSeq The modified (or changed) sequence to
+ * compare against with the origial.
+ *
+ * @return A <code>Difference</code> array.
+ */
+ public Difference[] computeDiffs(Iterator orgSeq, Iterator modSeq);
+}
+
diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/Difference.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/Difference.java
new file mode 100644
index 000000000000..31977b36f377
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/Difference.java
@@ -0,0 +1,242 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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.merger;
+
+
+/**
+ * This is the <code>Difference</code> basic unit. Used by the
+ * <code>DiffAlgorithm</code> as a set of difference between two
+ * <code>Iterators</code> (the original and modified
+ * <code>Iterators</code>).
+ *
+ * @author smak
+ */
+public final class Difference {
+
+ /**
+ * Add operation.
+ */
+ public static final int ADD = 1;
+
+ /**
+ * Delete operation.
+ */
+ public static final int DELETE = 2;
+
+ /**
+ * Change operation.
+ */
+ public static final int CHANGE = 3;
+
+ /**
+ * Unchange operation (i.e. no change).
+ */
+ public static final int UNCHANGE = 4;
+
+ /**
+ * The action of the diff - either {@link #ADD} or {@link #DELETE}.
+ */
+ private int operation;
+
+ /**
+ * <p>The position of the content that should be operated on (original
+ * iterator).</p>
+ *
+ * <p>For ADD, the orgPosition is the position of the original sequence
+ * where the diff will insert (the element count is starting from 0, and
+ * always insert before the element). The modPosition is the position
+ * of the diff in the modified sequence (also starting from 0).</p>
+ *
+ * <blockquote><pre>
+ * example:
+ *
+ * diff - &lt;B D&gt;and &lt;A B C D E F&gt;
+ * note: &lt;B D&gt;is original sequence and &lt;A B C D E F&gt;
+ * is the modified one.
+ *
+ * and here is the position of those sequence:
+ * &lt;B D&gt; &lt;A B C D E F&gt;
+ * 0 1 0 1 2 3 4 5
+ *
+ * result:
+ * &lt;diff orgPos=0 modPos=0 operation=ADD&gt; &lt;-- element A
+ * &lt;diff orgPos=1 modPos=2 operation=ADD&gt; &lt;-- element C
+ * &lt;diff orgPos=2 modPos=4 operation=ADD&gt; &lt;-- element E
+ * &lt;diff orgPos=2 modPos=5 operation=ADD&gt; &lt;-- element F
+ *
+ * </pre> </blockquote>
+ * <p>One can notice the add operation is inserted before the position.
+ * Hence, in order to append an element, we will have a position of
+ * original sequence length + 1 to denote an append.</p>
+ *
+ * <p>For DELETE, orgPosition is the position that the element
+ * will be deleted (starting from 0) and modPosition is the position
+ * where the deleted element should be (consider as an ADD).</p>
+ *
+ * <p>The modPosition is less useful and it is difficult to understand
+ * how the position is calculated. One can just skip this piece of
+ * information. It is useful if one wants to reverse the role
+ * of original sequence and modified sequence and find out the diff
+ * easily (just change add to delete and delete to add for operation
+ * and swap the orgPosition and modPosition).</p>
+ *
+ * <blockquote><pre>
+ * example:
+ *
+ * diff - &lt;A B C D E F&gt; and &lt; B D&gt;
+ * note: &lt;A B C D E F&gt; is original sequence and &lt;B D&gt;
+ * is the modified one.
+ *
+ * and here is the position of those sequence:
+ * &lt;A B C D E F&gt; &lt;B D&gt;
+ * 0 1 2 3 4 5 0 1
+ *
+ * result:
+ * &lt;diff orgPos=0 modPos=0 operation=DELETE&gt; &lt;-- element A
+ * &lt;diff orgPos=2 modPos=1 operation=DELETE&gt; &lt;-- element C
+ * &lt;diff orgPos=4 modPos=2 operation=DELETE&gt; &lt;-- element E
+ * &lt;diff orgPos=5 modPos=2 operation=DELETE&gt; &lt;-- element F
+ * </pre></blockquote>
+ */
+ private int orgPosition;
+
+ /**
+ * The position of the content that should be operated (modified iterator).
+ * For explanation and examples, see {@link #orgPosition}.
+ */
+ private int modPosition;
+
+
+ /**
+ * Constructor. This is the standard way to create a
+ * <code>Difference</code> object.
+ *
+ * @param operation Either {@link #ADD} or {@link #DELETE}.
+ * @param orgPosition The position in the original (first)
+ * <code>Iterator</code>.
+ * @param modPosition The position in the modified (second)
+ * <code>Iterator</code>.
+ */
+ public Difference(int operation, int orgPosition,
+ int modPosition) {
+ this.operation = operation;
+ this.orgPosition = orgPosition;
+ this.modPosition = modPosition;
+ }
+
+
+ /**
+ * Get the operation of the <code>Difference</code>.
+ *
+ * @return the operation of the <code>Difference</code>,
+ * either {@link #ADD} or {@link #DELETE}
+ */
+ public int getOperation() {
+ return operation;
+ }
+
+ /**
+ * Get the original <code>Iterator</code> position.
+ *
+ * @return The position in the original (first) <code>Iterator</code>
+ */
+ public int getOrgPosition() {
+ return orgPosition;
+ }
+
+ /**
+ * Get the modified <code>Iterator</code> position.
+ *
+ * @return The position in the modified (second) <code>Iterator</code>
+ */
+ public int getModPosition() {
+ return modPosition;
+ }
+
+
+ /**
+ * Two <code>Difference</code> objects will equal if and only if
+ * all operation, orgPosition, modPosition and content are equal.
+ *
+ * @param obj Object to compare.
+ *
+ * @return true if equal, false otherwise.
+ */
+ public boolean equals(Object obj) {
+ if (obj instanceof Difference) {
+ Difference diff = (Difference) obj;
+ if ((operation == diff.operation) &&
+ (orgPosition == diff.orgPosition) &&
+ (modPosition == diff.modPosition)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Display debug information.
+ *
+ * @return Debug string.
+ */
+ public String debug() {
+
+ String opStr = "";
+
+ switch (operation) {
+ case ADD:
+ opStr = "add";
+ break;
+ case DELETE:
+ opStr = "del";
+ break;
+ case CHANGE:
+ opStr = "chg";
+ break;
+ case UNCHANGE:
+ opStr = "uch";
+ break;
+ default:
+ break;
+ }
+ return "<diff orgPos=" + orgPosition + " modPos=" + modPosition +
+ " op=" + opStr + ">";
+ }
+
+ /**
+ * Returns position and operation values as a single string.
+ *
+ * @return orgPosition, modPosition and operation as a single string.
+ */
+ public String toString() {
+
+ return orgPosition + " " + modPosition + " " + operation;
+ }
+}
+
diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/Iterator.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/Iterator.java
new file mode 100644
index 000000000000..5ebfa7b2f499
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/Iterator.java
@@ -0,0 +1,123 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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.merger;
+
+/**
+ * This is an interface used by the {@link
+ * org.openoffice.xmerge.merger.DiffAlgorithm
+ * DiffAlgorithm} and {@link
+ * org.openoffice.xmerge.merger.MergeAlgorithm
+ * MergeAlgorithm} to access a <code>Document</code>.
+ *
+ * @author smak
+ */
+public interface Iterator {
+
+
+ /**
+ * Move to next element in the sequence.
+ *
+ * @return The <code>Object</code> of the next element in the sequence.
+ * If there is no next element, then return null.
+ */
+ public Object next();
+
+
+ /**
+ * Move to previous element in the sequence.
+ *
+ * @return The <code>Object</code> of the previous element in the sequence.
+ * If there is no previous element, then return null.
+ */
+ public Object previous();
+
+
+ /**
+ * Move to the beginning of the sequence.
+ *
+ * @return The <code>Object</code> of the first element in the sequence.
+ * If it is empty, then return null.
+ */
+ public Object start();
+
+
+ /**
+ * Move to the end of the sequence.
+ *
+ * @return The <code>Object</code> of the last element in the sequence.
+ * If it is empty, then return null.
+ */
+ public Object end();
+
+
+ /**
+ * Return the current element <code>Object</code> content.
+ *
+ * @return The <code>Object</code> at current position.
+ */
+ public Object currentElement();
+
+
+ /**
+ * Return the total element count in the sequence.
+ *
+ * @return The total element count.
+ */
+ public int elementCount();
+
+
+ /**
+ * A method to allow the difference algorithm to test whether the
+ * <code>obj1</code> and <code>obj2</code> in the
+ * <code>Iterator</code> are considered equal. As not every
+ * <code>Object</code> in the <code>Iterator</code> can implement its
+ * own equal method, with this equivalent method, we can allow
+ * flexibility for the <code>Iterator</code> to choose a custom way
+ * to compare two objects. Two objects can even be compared based on
+ * the position in the <code>Iterator</code> rather than by
+ * the content via this option.
+ *
+ * @param obj1 The first <code>Object</code>.
+ * @param obj2 The second <code>Object</code>.
+ *
+ * @return true if equal, false otherwise.
+ */
+ public boolean equivalent(Object obj1, Object obj2);
+
+
+ /**
+ * <p>A method to force the <code>Iterator</code> to transverse the tree
+ * again to refresh the content.</p>
+ *
+ * <p>It is used mainly for <code>Iterator</code> objects which take a snap
+ * shot instead of dynamically transversing the tree. The current
+ * position will be set to the beginning.</p>
+ */
+ public void refresh();
+}
+
diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/MergeAlgorithm.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/MergeAlgorithm.java
new file mode 100644
index 000000000000..b1d4d5b510fd
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/MergeAlgorithm.java
@@ -0,0 +1,61 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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.merger;
+
+import org.openoffice.xmerge.MergeException;
+import org.openoffice.xmerge.merger.Iterator;
+import org.openoffice.xmerge.merger.Difference;
+
+/**
+ * This is the <code>MergeAlgorithm</code> interface. It is an
+ * interface so that different merge algorithms may be plugged-in
+ * to actually merge the diffs back to an original document.
+ *
+ * @author smak
+ */
+public interface MergeAlgorithm {
+
+ /**
+ * This method is to merge the difference to an <code>Iterator</code>.
+ * The original <code>Iterator</code> will be modified after the call.
+ *
+ * @param objSeq The original sequence which the difference
+ * will be applied. It will be modified.
+ * @param modSeq The modified sequence where the difference
+ * content will be extracted.
+ * @param differences The <code>Difference</code> array.
+ *
+ * @return An <code>Iterator</code> which is the modified original
+ * <code>Iterator</code> Sequence. Same as the first parameter.
+ *
+ * @throws MergeException If an error occurs during the merge.
+ */
+ public void applyDifference(Iterator orgSeq, Iterator modSeq,
+ Difference[] differences) throws MergeException;
+}
+
diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/NodeMergeAlgorithm.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/NodeMergeAlgorithm.java
new file mode 100644
index 000000000000..9337816c64ab
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/NodeMergeAlgorithm.java
@@ -0,0 +1,52 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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.merger;
+
+import org.w3c.dom.Node;
+
+/**
+ * This is an interface for a {@link
+ * org.openoffice.xmerge.merger.MergeAlgorithm
+ * MergeAlgorithm} to merge two <code>Node</code> objects. It is an
+ * interface so that different merge algorithms may be plugged-in.
+ *
+ * @author smak
+ */
+public interface NodeMergeAlgorithm {
+
+ /**
+ * This method is used to merge two given <code>Node</code>
+ * objects. Note: the original <code>Node</code> may be modified.
+ *
+ * @param originalNode The original <code>Node</code>.
+ * @param modifyNode The <code>Node</code> to be merged. It may
+ * be modified.
+ */
+ public void merge(Node orginialNode, Node modifyNode);
+}
+
diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/diff/CellNodeIterator.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/diff/CellNodeIterator.java
new file mode 100644
index 000000000000..95fddea14cb9
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/diff/CellNodeIterator.java
@@ -0,0 +1,114 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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.merger.diff;
+
+import org.w3c.dom.Node;
+import org.w3c.dom.Element;
+
+import org.openoffice.xmerge.ConverterCapabilities;
+import org.openoffice.xmerge.converter.xml.OfficeConstants;
+import org.openoffice.xmerge.util.Resources;
+
+
+/**
+ * <p>This is an implementations of the <code>Iterator</code> interface.
+ * It will traverse the tree and find cell <code>Node</code> sequences.</p>
+ *
+ * <p>Note: Once the XML Tree is parsed, then the <code>Iterator</code>
+ * will be a snap shot of that tree. That means even the tree is
+ * modified later, than the cached paragraph <code>Node</code> list will
+ * not be updated accordingly. For this reason and for performance reasons
+ * this <code>Iterator</code> does not support any operation methods such
+ * as insert, remove or replace. The main purpose of this
+ * <code>Iterator</code> is to be used with difference, not with merge.</p>
+ *
+ * @author smak
+ */
+public final class CellNodeIterator extends NodeIterator {
+
+ private Resources res = Resources.getInstance();
+
+ // can be expanded to an array in the future, not necessary right now
+ private static final String SUPPORTED_TAG1 = OfficeConstants.TAG_TABLE_CELL;
+
+ /**
+ * The standard constructor.
+ *
+ * @param cc The <code>ConverterCapabilities</code>.
+ * @param node The initial root <code>Node</code>.
+ */
+ public CellNodeIterator(ConverterCapabilities cc, Node node) {
+ super(cc, node);
+ }
+
+
+ /**
+ * Overwrite the parent <code>nodeSupported</code> method. Only cell
+ * <code>Node</code> objects are supported.
+ *
+ * @param node The <code>Node</code> to check.
+ *
+ * @return true if the <code>Node</code> is supported, false otherwise.
+ */
+ protected boolean nodeSupported(Node node) {
+
+ // can use an array later to check all possible tags for
+ // future expansion
+ if (node.getNodeType() == Node.ELEMENT_NODE &&
+ node.getNodeName().equals(SUPPORTED_TAG1)) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+
+ protected boolean childrenEqual(Node node1, Node node2) {
+
+ boolean equal = false;
+
+ if (node1.hasChildNodes() && node2.hasChildNodes()) {
+ Element cell1 = (Element)node1;
+ Element cell2 = (Element)node2;
+
+ // only need compare the first <text:p> children node, don't want
+ // to compare any non-supported features
+ // TODO: need to confirm whether all the text string is the
+ // first <text:p>, though I checked with the openoffice 619 build
+ Node paraNode1 = cell1.getElementsByTagName(
+ OfficeConstants.TAG_PARAGRAPH).item(0);
+ Node paraNode2 = cell2.getElementsByTagName(
+ OfficeConstants.TAG_PARAGRAPH).item(0);
+
+ equal = super.compareNode(paraNode1, paraNode2);
+ }
+
+ return equal;
+ }
+}
+
diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/diff/CharArrayLCSAlgorithm.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/diff/CharArrayLCSAlgorithm.java
new file mode 100644
index 000000000000..9026303ca3d3
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/diff/CharArrayLCSAlgorithm.java
@@ -0,0 +1,234 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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.merger.diff;
+
+import java.util.Vector;
+import org.openoffice.xmerge.merger.Difference;
+
+/**
+ * <p>This is an implementations of <code>DiffAlgorithm</code> interface
+ * which will difference char arrays.</p>
+ *
+ * <p>It also use Longest Common Subsequence (LCS). The algorithm is based
+ * on the book "Introduction to Algorithms" by Thomas H.Cormen,
+ * Charles E.Leiserson, and Ronald L.Riverst (MIT Press 1990) page 314.</p>
+ *
+ * @author smak
+ */
+public class CharArrayLCSAlgorithm {
+
+ /**
+ * Return an <code>Difference</code> array. This method finds out
+ * the difference between two sequences.
+ *
+ * @param orgSeq The original sequence.
+ * @param modSeq The modified (or changed) sequence to
+ * compare against the origial.
+ *
+ * @return A <code>Difference</code> array.
+ */
+ public Difference[] computeDiffs(char[] orgSeq, char[] modSeq) {
+
+ int orgSeqlen = orgSeq.length;
+ int modSeqlen = modSeq.length;
+
+ int[][] diffTable;
+
+ // Diff table is used to keep track which element is the same or not
+ // in those 2 sequences
+ diffTable = createDiffTable(orgSeq, modSeq);
+
+ // debug purpose...
+ // printDiffTable(diffTable);
+
+ Vector diffResult = new Vector();
+
+ generateResult(diffTable, orgSeqlen, modSeqlen, diffResult);
+
+ // don't need anymore if Difference do not contain content information
+ /* fillInDiffContent(diffResult, orgSeq, modSeq); */
+
+ Difference[] diffArray = new Difference[0];
+
+ // convert the vector to array, it has to do in here as
+ // generateResult is called recursively
+ if (diffResult.size() > 0) {
+ diffArray = new Difference[diffResult.size()];
+ diffResult.copyInto(diffArray);
+ }
+
+ diffTable = null;
+ diffResult = null;
+
+ return diffArray;
+ }
+
+
+ /**
+ * Debug function Used to print out the nicely formatted
+ * difference table.
+ *
+ * @param diffTable The difference table to display.
+ */
+ private void printDiffTable(int[][] diffTable) {
+
+ for (int i = 0; i < diffTable.length; i++) {
+ for (int j = 0; j < diffTable[i].length; j++) {
+ System.out.print(" " + diffTable[i][j] + " ");
+ }
+ System.out.println();
+ }
+ }
+
+
+ /**
+ * Create the difference table.
+ * The difference table is used internal to keep track what
+ * elements are common or different in the two sequences.
+ *
+ * @param orgSeq The original sequence to be used as a base.
+ * @param modSeq The modified sequence to compare.
+ *
+ * @return A difference table as a two-dimensional array of
+ * integers.
+ */
+ private int[][] createDiffTable(char[] orgSeq, char[] modSeq) {
+ int orgSeqlen = orgSeq.length + 1;
+ int modSeqlen = modSeq.length + 1;
+ int[][] diffTable;
+
+ // initialize the diffTable (it need to be 1 row/col bigger
+ // than the original str)
+ diffTable = new int[orgSeqlen][];
+ for (int i = 0; i < orgSeqlen; i++) {
+ diffTable[i] = new int[modSeqlen];
+ }
+
+ // compute the diff Table using LCS algorithm, refer to the book
+ // mentioned at the top of the program
+ for (int i = 1; i < orgSeqlen; i++) {
+ for (int j = 1; j < modSeqlen; j++) {
+
+ if (orgSeq[i-1] == modSeq[j-1]) {
+ diffTable[i][j] = diffTable[i-1][j-1]+1;
+ } else {
+ if (diffTable[i-1][j] >= diffTable[i][j-1]) {
+ diffTable[i][j] = diffTable[i-1][j];
+ } else {
+ diffTable[i][j] = diffTable[i][j-1];
+ }
+ }
+ }
+ }
+
+ return diffTable;
+ }
+
+
+ /**
+ * Generate the <code>Difference</code> result vector.
+ * This method will be called recursively to backtrack the difference
+ * table to get the difference result (and also the LCS).
+ *
+ * @param diffTable The difference table containing the
+ * <code>Difference</code> result.
+ * @param i The nth element in original sequence to
+ * compare. This method is called recursively
+ * with i and j decreased until 0.
+ * @param j The nth element in modified sequence to
+ * compare.
+ * @param diffVector A vector to output the <code>Difference</code>
+ * result. Can not use a return variable as it
+ * is a recursive method. The vector will contain
+ * <code>Difference</code> objects with operation
+ * and positions filled in.
+ */
+ private void generateResult(int[][] diffTable,
+ int i, int j, Vector diffVector) {
+
+ // handle the first element
+ if (i == 0 || j == 0) {
+ if (i == 0 && j == 0) {
+ // return
+ } else if (j == 0) {
+ for (int cnt = 0; cnt < i; cnt++) {
+ Difference diff =
+ new Difference(Difference.DELETE, cnt, j);
+ diffVector.add(diff);
+ }
+ } else {
+ for (int cnt = 0; cnt < j; cnt++) {
+ Difference diff =
+ new Difference(Difference.ADD, i, cnt);
+ diffVector.add(diff);
+ }
+ }
+ return;
+ }
+
+ // for the detail of this algorithm, refer to the book mentioned on
+ // the top and page 317 and 318.
+ if ((diffTable[i-1][j-1] == diffTable[i][j] -1) &&
+ (diffTable[i-1][j-1] == diffTable[i-1][j]) &&
+ (diffTable[i-1][j-1] == diffTable[i][j-1])) {
+
+ // the element of ith and jth in org and mod sequence is the same
+ generateResult(diffTable, i-1, j-1, diffVector);
+ } else {
+ if (diffTable[i-1][j] > diffTable[i][j-1]) {
+
+ // recursively call first, then add the result so that
+ // the beginning of the diffs will be stored first
+ generateResult(diffTable, i-1, j, diffVector);
+
+ Difference diff =
+ new Difference(Difference.DELETE, i-1, j);
+ diffVector.add(diff);
+ } else if (diffTable[i-1][j] < diffTable[i][j-1]) {
+
+ // recursively call first, then add the result so that
+ // the beginning of the diffs will be stored first
+ generateResult(diffTable, i, j-1, diffVector);
+
+ Difference diff =
+ new Difference(Difference.ADD, i, j-1);
+ diffVector.add(diff);
+ } else { // diffTable[i-1][j] == diffTable[i][j-1]
+ // recursively call first, then add the result so that
+ // the beginning of the diffs will be stored first
+ generateResult(diffTable, i-1, j-1, diffVector);
+
+ Difference diff =
+ new Difference(Difference.CHANGE, i-1, j-1);
+ diffVector.add(diff);
+
+ }
+ }
+ }
+}
+
diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/diff/CharacterParser.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/diff/CharacterParser.java
new file mode 100644
index 000000000000..1a047d130a03
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/diff/CharacterParser.java
@@ -0,0 +1,142 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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.merger.diff;
+
+import org.w3c.dom.Node;
+
+import org.openoffice.xmerge.converter.xml.OfficeConstants;
+
+import java.util.Vector;
+import java.util.List;
+
+
+/**
+ * <p>This is a parser to return a character array for difference purpose.
+ * It will use depth first search to traverse all the characters inside the
+ * text <code>Node</code> under a given <code>Node</code> (most likely to be
+ * a paragraph <code>Node</code>).</p>
+ *
+ * <p>Note: Once the XML Tree is parsed, then the <code>Iterator</code> will be
+ * a snap shot of that tree. That means even the tree is modified later, than
+ * the cached paragraph <code>Node</code> list will not be updated accordingly.
+ * For this reason and for performance reasons this <code>Iterator</code> does
+ * not support any operation methods such as insert, remove or replace. The
+ * main purpose of this <code>Iterator</code> is to be used with difference,
+ * not with merge.</p>
+ *
+ * @author smak
+ */
+public class CharacterParser {
+
+ private TextNodeIterator textNodes;
+ private int currentPosition = 0;
+ private List nodeList_ = null;
+ private char[] charArray;
+
+
+ /**
+ * Standard constructor.
+ *
+ * @param node The initial root <code>Node</code>.
+ */
+ public CharacterParser(Node node) {
+ textNodes = new TextNodeIterator(node);
+ nodeList_ = new Vector();
+
+ parseNodes();
+ }
+
+
+ /**
+ * Returns the <code>Node</code> pointer with the given character position.
+ *
+ * @return The <code>Node</code> pointer with the given character position.
+ */
+ public List getNodeList() {
+ // will go through the nodeList to find the corresponding node
+ return nodeList_;
+ }
+
+ /**
+ * Returns the character array representation of the text.
+ *
+ * @return The character array representation of the text.
+ */
+ public char[] getCharArray() {
+ return charArray;
+ }
+
+ private void parseNodes() {
+
+ StringBuffer strBuf = new StringBuffer();
+
+ /* create the character array by iterate the textnode iterator */
+ Node currentNode = (Node)(textNodes.start());
+ for (;
+ currentNode != null;
+ currentNode = (Node)(textNodes.next())) {
+
+ // add the text value into the array
+ String textValue = null;
+ String nodeName = currentNode.getNodeName();
+
+ // TODO: Space node have a count attribute which is not handled!
+ if (currentNode.getNodeType() == Node.TEXT_NODE) {
+ textValue = currentNode.getNodeValue();
+ } else if (nodeName.equals(OfficeConstants.TAG_SPACE)) {
+ textValue = " ";
+ } else if (nodeName.equals(OfficeConstants.TAG_TAB_STOP)) {
+ textValue = "\t";
+ }
+
+ if (textValue != null) {
+ strBuf.append(textValue);
+ addNewNodeEntry(textValue.length(), currentNode);
+ }
+ }
+
+ charArray = strBuf.toString().toCharArray();
+ }
+
+
+ /**
+ * Adds a new <code>Node</code> entry.
+ *
+ * @param textLen The text length.
+ * @param node The <code>Node</code>.
+ */
+ private void addNewNodeEntry(int textLen, Node node) {
+
+ TextNodeEntry nodeEntry = new TextNodeEntry(currentPosition,
+ currentPosition + textLen - 1, node);
+ currentPosition = currentPosition + textLen;
+
+ nodeList_.add(nodeEntry);
+ }
+}
+
diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/diff/IteratorLCSAlgorithm.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/diff/IteratorLCSAlgorithm.java
new file mode 100644
index 000000000000..ae90274bfaf1
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/diff/IteratorLCSAlgorithm.java
@@ -0,0 +1,236 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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.merger.diff;
+
+import java.util.Vector;
+import org.openoffice.xmerge.merger.DiffAlgorithm;
+import org.openoffice.xmerge.merger.Difference;
+import org.openoffice.xmerge.merger.Iterator;
+import org.openoffice.xmerge.util.Debug;
+
+/**
+ * This is one of the implementations of <code>DiffAlgorithm</code> interface.
+ * Using Longest Common Subsequence (LCS). The algorithm here is based
+ * on the book "Introduction to Algorithms" by Thomas H.Cormen,
+ * Charles E.Leiserson and Ronald L.Riverst (MIT Press 1990) page 314.
+ *
+ * @author smak
+ */
+public class IteratorLCSAlgorithm implements DiffAlgorithm {
+
+ public Difference[] computeDiffs(Iterator orgSeq, Iterator modSeq) {
+
+ int orgSeqlen = orgSeq.elementCount();
+ int modSeqlen = modSeq.elementCount();
+
+ int[][] diffTable;
+
+ // Diff table is used to keep track which element is the same or not
+ // in those 2 sequences
+ diffTable = createDiffTable(orgSeq, modSeq);
+
+ // debug purpose...
+ if (Debug.isFlagSet(Debug.INFO)) {
+ printDiffTable(diffTable);
+ }
+
+ Vector diffResult = new Vector();
+
+ generateResult(diffTable, orgSeqlen, modSeqlen, diffResult);
+
+ Difference[] diffArray = new Difference[0];
+
+ // convert the vector to array, it has to do in here as
+ // generateResult is called recursively
+ if (diffResult.size() > 0) {
+ diffArray = new Difference[diffResult.size()];
+ diffResult.copyInto(diffArray);
+ }
+
+ diffTable = null;
+ diffResult = null;
+
+ return diffArray;
+ }
+
+
+ /**
+ * Debug function used to print out the nicely formatted
+ * difference table.
+ *
+ * @param diffTable The difference table to display.
+ */
+ private void printDiffTable(int[][] diffTable) {
+
+ String tmpString = "";
+
+ for (int i = 0; i < diffTable.length; i++) {
+ for (int j = 0; j < diffTable[i].length; j++) {
+ tmpString = tmpString + " " + diffTable[i][j] + " ";
+ }
+ Debug.log(Debug.INFO, tmpString);
+ tmpString = "";
+ }
+ }
+
+ /**
+ * Create the difference table.
+ * The difference table is used internal to keep track what
+ * elements are common or different in the two sequences.
+ *
+ * @param orgSeq The original sequence to be used as a base.
+ * @param modSeq The modified sequence to compare.
+ *
+ * @return A difference table as a two-dimensional array of
+ * integers.
+ */
+ private int[][] createDiffTable(Iterator orgSeq, Iterator modSeq) {
+ int orgSeqlen = orgSeq.elementCount() + 1;
+ int modSeqlen = modSeq.elementCount() + 1;
+ int[][] diffTable;
+
+ // initialize the diffTable
+ diffTable = new int[orgSeqlen][];
+ for (int i = 0; i < orgSeqlen; i++) {
+ diffTable[i] = new int[modSeqlen];
+ }
+
+ // compute the diff Table using LCS algorithm, refer to the book
+ // mentioned at the top of the program
+
+ int i, j;
+
+ Object orgSeqObject, modSeqObject;
+
+ for (orgSeqObject = orgSeq.start(), i = 1;
+ orgSeqObject != null;
+ orgSeqObject = orgSeq.next(), i++) {
+
+ for (modSeqObject = modSeq.start(), j = 1;
+ modSeqObject != null;
+ modSeqObject = modSeq.next(), j++) {
+
+ if (orgSeq.equivalent(orgSeqObject, modSeqObject)) {
+ diffTable[i][j] = diffTable[i-1][j-1]+1;
+ } else {
+ if (diffTable[i-1][j] >= diffTable[i][j-1]) {
+ diffTable[i][j] = diffTable[i-1][j];
+ } else {
+ diffTable[i][j] = diffTable[i][j-1];
+ }
+ }
+ }
+ }
+
+ return diffTable;
+ }
+
+
+ /**
+ * Generate the <code>Difference</code> object result vector.
+ * This method will be called recursively to backtrack the difference
+ * table to get the difference result (and also the LCS).
+ *
+ * @param diffTable The difference table containing the
+ * <code>Difference</code> result.
+ * @param i The nth element in original sequence to
+ * compare. This method is called recursively
+ * with i and j decreased until 0.
+ * @param j The nth element in modified sequence to
+ * compare.
+ * @param diffVector A vector to output the <code>Difference</code>
+ * result. Can not use a return variable as it
+ * is a recursive method. The vector will contain
+ * <code>Difference</code> objects with operation
+ * and positions fill in.
+ */
+ private void generateResult(int[][] diffTable,
+ int i, int j, Vector diffVector) {
+
+ // handle the first element
+ if (i == 0 && j == 0) {
+ return;
+
+ } else if (j == 0) {
+ for (int cnt = 0; cnt < i; cnt++) {
+ Difference diff =
+ new Difference(Difference.DELETE, cnt, j);
+ diffVector.add(diff);
+ }
+ return;
+
+ } else if (i == 0) {
+ for (int cnt = 0; cnt < j; cnt++) {
+ Difference diff =
+ new Difference(Difference.ADD, i, cnt);
+ diffVector.add(diff);
+ }
+ return;
+ }
+
+ // for the detail of this algorithm, refer to the book mentioned on
+ // the top and page 317 and 318.
+ if ((diffTable[i-1][j-1] == diffTable[i][j] -1) &&
+ (diffTable[i-1][j-1] == diffTable[i-1][j]) &&
+ (diffTable[i-1][j-1] == diffTable[i][j-1])) {
+
+ // the element of ith and jth in org and mod sequence is the same
+ generateResult(diffTable, i-1, j-1, diffVector);
+ } else {
+ if (diffTable[i-1][j] > diffTable[i][j-1]) {
+
+ // recursively call first, then add the result so that
+ // the beginning of the diffs will be stored first
+ generateResult(diffTable, i-1, j, diffVector);
+
+ Difference diff =
+ new Difference(Difference.DELETE, i-1, j);
+ diffVector.add(diff);
+ } else if (diffTable[i-1][j] < diffTable[i][j-1]) {
+
+ // recursively call first, then add the result so that
+ // the beginning of the diffs will be stored first
+ generateResult(diffTable, i, j-1, diffVector);
+
+ Difference diff =
+ new Difference(Difference.ADD, i, j-1);
+ diffVector.add(diff);
+ } else { // diffTable[i-1][j] == diffTable[i][j-1]
+ // recursively call first, then add the result so that
+ // the beginning of the diffs will be stored first
+ generateResult(diffTable, i-1, j-1, diffVector);
+
+ Difference diff =
+ new Difference(Difference.CHANGE, i-1, j-1);
+ diffVector.add(diff);
+
+ }
+ }
+ }
+}
+
diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/diff/IteratorRowCompare.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/diff/IteratorRowCompare.java
new file mode 100644
index 000000000000..fa6642432838
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/diff/IteratorRowCompare.java
@@ -0,0 +1,243 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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.merger.diff;
+
+import org.w3c.dom.Node;
+import org.w3c.dom.Element;
+
+import java.util.Vector;
+import org.openoffice.xmerge.merger.DiffAlgorithm;
+import org.openoffice.xmerge.merger.Difference;
+import org.openoffice.xmerge.merger.Iterator;
+import org.openoffice.xmerge.converter.xml.OfficeConstants;
+
+/**
+ * <p>A very simple and direct difference algorithm for row
+ * <code>Node</code> objects in a spreadsheet. Basically, it will
+ * compare objects in sequence and does not look ahead (unlike LCS).</p>
+ *
+ * <p><ol><li>
+ * If two objects are the same, skip to next one.
+ * </li><li>
+ * Otherwise check whether the row repeated attribute is the same.
+ * </li><li>
+ * If the row repeated attribute is the same, then compare two rows
+ * and mark it as <i>change</i> if those rows are different.
+ * </li><li>
+ * If the row repeated attribute is different, then split the rows and
+ * continue to compare.
+ * </li><li>
+ * If there are more objects in the modseq than the original sequence,
+ * then all of the extra ones in the modified sequence are marked as add.
+ * </li><li>
+ * If there are more objects in the original sequence than the modified
+ * sequence, then all the extra one in the modified sequence are marked
+ * as delete.
+ * </li></ol></p>
+ *
+ * <p>NOTE: The algorithm will have potential side effect to split rows.</p>
+ *
+ * @author smak
+ */
+
+public class IteratorRowCompare implements DiffAlgorithm {
+
+ /**
+ * Compute the differences of the given two sequences.
+ * Refer to the class description.
+ *
+ * Return an array of <code>Difference</code> objects. This method finds
+ * out the difference between two sequences.
+ *
+ * @param orgSeq The original sequence.
+ * @param modSeq The modified (or changed) sequence to
+ * compare against with the origial.
+ *
+ * @return An array of Difference objects.
+ */
+ public Difference[] computeDiffs(Iterator orgSeq, Iterator modSeq) {
+
+ int orgSeqlen = orgSeq.elementCount();
+ int modSeqlen = modSeq.elementCount();
+
+ Vector diffVector = new Vector();
+
+ // i and j are counters to keep track the current position in the
+ // iterator
+ int i = 0;
+ int j = 0;
+ Object orgSeqObject = orgSeq.start();
+ Object modSeqObject = modSeq.start();
+ Element orgRow, modRow;
+ boolean different = false;
+ boolean orgSplited = false;
+ boolean modSplited = false;
+
+ while (orgSeqObject != null) {
+
+ different = true;
+
+ if (modSeqObject == null) {
+ // no more modsequence, all the remaining org sequence objs
+ // should consider as a delete.
+ Difference diff = new Difference(Difference.DELETE, i, j);
+ diffVector.add(diff);
+ orgSeqObject = orgSeq.next();
+
+ } else {
+ if (!orgSeq.equivalent(orgSeqObject, modSeqObject)) {
+
+ orgRow = (Element)orgSeqObject;
+ modRow = (Element)modSeqObject;
+
+ // check whether the original Row with multiple row
+ // if so, need to split one out for merge
+ String orgRowRepeated = orgRow.getAttribute(
+ OfficeConstants.ATTRIBUTE_TABLE_NUM_ROWS_REPEATED);
+ String modRowRepeated = modRow.getAttribute(
+ OfficeConstants.ATTRIBUTE_TABLE_NUM_ROWS_REPEATED);
+
+
+ int orgRowNum = 1;
+ int modRowNum = 1;
+
+ if (orgRowRepeated.length() > 0) {
+ orgRowNum =
+ Integer.valueOf(orgRowRepeated).intValue();
+ }
+ if (modRowRepeated.length() > 0) {
+ modRowNum =
+ Integer.valueOf(modRowRepeated).intValue();
+ }
+
+ // try to find out the common number of repeated Rows
+ if (orgRowNum == modRowNum) {
+ orgSeqObject = orgSeq.next();
+ modSeqObject = modSeq.next();
+
+ // cut the original row into two halves, first half
+ // have the repeated attribute = modify row attr
+ } else if (orgRowNum > modRowNum) {
+ Element orgSplitRow = splitRepeatedRow(
+ orgRow, modRowNum,
+ orgRowNum - modRowNum);
+ // it may equal after the split!
+ if (orgSeq.equivalent(orgSplitRow, modRow)) {
+ different = false;
+ }
+ orgSplited = true;
+ modSeqObject = modSeq.next();
+
+ // cut the modified Row into two halves, first half
+ // have the repeated attribute = original Row attr
+ } else {
+ Element modSplitRow = splitRepeatedRow(
+ modRow, orgRowNum,
+ modRowNum - orgRowNum);
+
+ // check whether rows are equal after the split
+ if (modSeq.equivalent(orgRow, modSplitRow)) {
+ different = false;
+ }
+ modSplited = true;
+ orgSeqObject = orgSeq.next();
+ }
+
+ if (different) {
+ Difference diff = new Difference(Difference.CHANGE,
+ i, j);
+ diffVector.add(diff);
+ }
+
+ } else {
+ // Rows are equivalent, move on to next one.
+ orgSeqObject = orgSeq.next();
+ modSeqObject = modSeq.next();
+ } // end if-else
+ j++;
+ } // end if-else
+ i++;
+ } // end while loop
+
+ // any extra objects in modify sequence should consider as an add
+ // to the original sequence
+ for (; modSeqObject != null; modSeqObject = modSeq.next(), j++) {
+ Difference diff = new Difference(Difference.ADD, i, j);
+ diffVector.add(diff);
+ }
+
+ // need to refresh the iterator if we split the rows
+ if (orgSplited) {
+ orgSeq.refresh();
+ }
+
+ if (modSplited) {
+ modSeq.refresh();
+ }
+
+
+ // convert the vector to array
+ Difference[] diffArray = new Difference[diffVector.size()];
+ diffVector.copyInto(diffArray);
+
+ return diffArray;
+ }
+
+
+ private Element splitRepeatedRow(Element orgRow, int splitNum, int orgNum) {
+ // NOTE: should we really want to do deep clone?
+ // in most the case, it is an empty Row, but the
+ // specification didn't forbid any node to use multiple
+ // column attributes. i.e. the node can contain text
+ // nodes or other things under it.
+ Element splitRow = (Element)(orgRow.cloneNode(true));
+
+ if (splitNum > 1) {
+ splitRow.setAttribute(
+ OfficeConstants.ATTRIBUTE_TABLE_NUM_ROWS_REPEATED,
+ String.valueOf(splitNum));
+ } else if (splitNum == 1) {
+ splitRow.removeAttribute(
+ OfficeConstants.ATTRIBUTE_TABLE_NUM_ROWS_REPEATED);
+ }
+ if (orgNum > 1) {
+ orgRow.setAttribute(
+ OfficeConstants.ATTRIBUTE_TABLE_NUM_ROWS_REPEATED,
+ String.valueOf(orgNum));
+ } else if (orgNum == 1) {
+ orgRow.removeAttribute(
+ OfficeConstants.ATTRIBUTE_TABLE_NUM_ROWS_REPEATED);
+ }
+
+ Node parentNode = orgRow.getParentNode();
+ parentNode.insertBefore(splitRow, orgRow);
+
+ return splitRow;
+ }
+}
+
diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/diff/NodeIterator.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/diff/NodeIterator.java
new file mode 100644
index 000000000000..308affeb130a
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/diff/NodeIterator.java
@@ -0,0 +1,384 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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.merger.diff;
+
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.NamedNodeMap;
+
+import org.openoffice.xmerge.ConverterCapabilities;
+import org.openoffice.xmerge.merger.Iterator;
+import org.openoffice.xmerge.util.Debug;
+import org.openoffice.xmerge.util.Resources;
+
+import java.util.Vector;
+import java.util.List;
+
+
+/**
+ * <p>This is an implementation of the <code>Iterator</code> interface.
+ * It will traverse the tree and find <code>Node</code> sequences.</p>
+ *
+ * <p>Note: Once the XML Tree is parsed, then the <code>Iterator</code> will
+ * be a snap shot of that tree. That means even the tree is modified later,
+ * than the cached paragraph <code>Node</code> list will not be updated
+ * accordingly. For this reason and for performance reasons this
+ * <code>Iterator</code> does not support any operation methods such as
+ * insert, remove or replace. The main purpose of this
+ * <code>Iterator</code> is to be used with difference, not with merge.</p>
+ *
+ * @author smak
+ */
+public abstract class NodeIterator implements Iterator {
+
+ private List nodeList = null;
+ private int currentPosition = 0;
+ private Node root;
+ private ConverterCapabilities cc_ = null;
+
+
+ /**
+ * Standard constructor.
+ *
+ * @param cc The <code>ConverterCapabilities</code>.
+ * @param node The initial root <code>Node</code>.
+ */
+ public NodeIterator(ConverterCapabilities cc, Node node) {
+ cc_ = cc;
+ nodeList = new Vector();
+ root = node;
+ markTree(node);
+ }
+
+
+ public Object next() {
+ if (currentPosition < nodeList.size() - 1) {
+ currentPosition++;
+ return currentElement();
+ } else {
+ return null;
+ }
+ }
+
+
+ public Object previous() {
+ if (currentPosition > 0) {
+ currentPosition--;
+ return currentElement();
+ } else {
+ return null;
+ }
+ }
+
+
+ public Object start() {
+ currentPosition = 0;
+ return currentElement();
+ }
+
+
+ public Object end() {
+ int size = nodeList.size();
+
+ if (size > 0) {
+ currentPosition = size - 1;
+ return currentElement();
+ } else {
+ return null;
+ }
+ }
+
+
+ public Object currentElement() {
+
+ if (currentPosition < 0 || currentPosition >= nodeList.size()) {
+ return null;
+ }
+
+ return nodeList.get(currentPosition);
+ }
+
+
+ public int elementCount() {
+ return nodeList.size();
+ }
+
+
+ public boolean equivalent(Object obj1, Object obj2) {
+ boolean equal = false;
+ String errMsg = null;
+ if (!(obj1 instanceof Node && obj2 instanceof Node)) {
+ errMsg = Resources.getInstance().getString("NOT_NODE_ERROR");
+ Debug.log(Debug.ERROR, errMsg);
+ } else {
+ Node node1 = (Node)obj1;
+ Node node2 = (Node)obj2;
+
+ equal = compareNode(node1, node2);
+ }
+ return equal;
+ }
+
+
+ public void refresh() {
+ nodeList = new Vector();
+ markTree(root);
+ currentPosition = 0;
+ }
+
+
+ /**
+ * Used to compare two <code>Node</code> objects (type/name/value)
+ * and all their children <code>Node</code> objects.
+ *
+ * @param node1 The first <code>Node</code> to compare.
+ * @param node2 The second <code>Node</code> to compare.
+ *
+ * @return true if <code>Node</code> is equal, false otherwise.
+ */
+ protected boolean compareNode(Node node1, Node node2) {
+ boolean equal = false;
+
+ nodeCheck: {
+
+ if (node1 == null || node2 == null) {
+ break nodeCheck;
+ }
+
+ // nodevalue is short
+ if (node1.getNodeType() != node2.getNodeType()) {
+ break nodeCheck;
+ }
+
+ // nodeName will not be null
+ if (!node1.getNodeName().equals(node2.getNodeName())) {
+ break nodeCheck;
+ }
+
+ // nodeValue can be null for a lot of type of cells
+ if (node1.getNodeValue() == null && node2.getNodeValue() == null) {
+ // empty
+ } else if (node1.getNodeValue() == null ||
+ node2.getNodeValue() == null) {
+ break nodeCheck;
+ } else if (!node1.getNodeValue().equals(node2.getNodeValue())) {
+ break nodeCheck;
+ }
+
+ // try to compare attributes
+ if (!attributesEqual(node1, node2)) {
+ break nodeCheck;
+ }
+
+ // don't need to compare if both node do not have children
+ if (!node1.hasChildNodes() && !node2.hasChildNodes()) {
+ equal = true;
+ break nodeCheck;
+ // don't need to compare if one node has children but not the other
+ } else if (!node1.hasChildNodes() || !node2.hasChildNodes()) {
+ equal = false;
+ break nodeCheck;
+ // need to compare if both node has children
+ } else if (!childrenEqual(node1, node2)) {
+ break nodeCheck;
+ }
+
+ equal = true;
+ }
+
+ return equal;
+ }
+
+
+ /**
+ * Compare the children of two <code>Node</code> objects. This
+ * method can be intentionally overridden by any class that
+ * extend from <code>NodeIterator</code> so that it can have
+ * its own children comparison if necessary.
+ *
+ * @param node1 The first <code>Node</code> to compare.
+ * @param node2 The second <code>Node</code> to compare.
+ *
+ * @return true if children are equal, false otherwise.
+ */
+ protected boolean childrenEqual(Node node1, Node node2) {
+
+ boolean equal = false;
+
+ childrenCheck: {
+ NodeList node1Children = node1.getChildNodes();
+ NodeList node2Children = node2.getChildNodes();
+
+ if (node1Children == null || node2Children == null) {
+ break childrenCheck;
+ }
+
+ if (node1Children.getLength() != node2Children.getLength()) {
+ break childrenCheck;
+ }
+
+ // compare all the childrens
+ equal = true;
+
+ for (int i = 0; i < node1Children.getLength(); i++) {
+ if (!compareNode(node1Children.item(i),
+ node2Children.item(i))) {
+ equal = false;
+ break childrenCheck;
+ }
+ }
+ }
+
+ return equal;
+ }
+
+
+ /**
+ * Compare attributes of two <code>Node</code> objects. This
+ * method can be intentionally overridden by any class that
+ * extends from <code>NodeIterator</code> so that it can have
+ * its own attribute comparison.
+ *
+ * @param node1 The first <code>Node</code> to compare.
+ * @param node2 The second <code>Node</code> to compare.
+ *
+ * @return true if attributes are equal, false otherwise.
+ */
+ protected boolean attributesEqual(Node node1, Node node2) {
+
+ boolean equal = false;
+ String nodeName = node1.getNodeName();
+ NamedNodeMap attrNode[] = new NamedNodeMap[2];
+ attrNode[0] = node1.getAttributes();
+ attrNode[1] = node2.getAttributes();
+
+ // attribute node will be null if node is not an element node
+ // and attribute nodes are equal if both are not element node
+ if (attrNode[0] == null || attrNode[1] == null) {
+ if (attrNode[0] == null && attrNode[1] == null) {
+ equal = true;
+ }
+ return equal;
+ }
+
+ // compare the attributes from node1 vs node2 and node2 vs node1
+ // though it's a little inefficient for the duplication of comparison
+ // as the number of attributes is not so many, it should not be
+ // a big problem.
+ int len [] = new int[2];
+ int src, dst;
+
+ attrCheck: {
+ for (int i = 0; i < 2; i++) {
+
+ if (i == 0) {
+ src = 0;
+ dst = 1;
+ } else {
+ src = 1;
+ dst = 0;
+ }
+
+ len[src] = attrNode[src].getLength();
+
+ for (int j = 0; j < len[src]; j++) {
+ Node srcAttr = attrNode[src].item(j);
+ String srcAttrName = srcAttr.getNodeName();
+
+ // copy the supported attrs
+ if (cc_ == null ||
+ cc_.canConvertAttribute(nodeName, srcAttrName)) {
+
+ // check whether the attribute exist in dst node
+ Node dstAttr = attrNode[dst].getNamedItem(srcAttrName);
+
+ if (dstAttr == null) {
+ Debug.log(Debug.INFO,
+ "[NodeIterator] Attr not exist in dst - "
+ + srcAttrName);
+ break attrCheck;
+ }
+
+ // then compare the attribute values
+ if (!srcAttr.getNodeValue().equals(
+ dstAttr.getNodeValue())) {
+ Debug.log(Debug.INFO,
+ "[NodeIterator] Attr diff src: " +
+ srcAttr.getNodeValue() + " dst: "+
+ dstAttr.getNodeValue());
+ break attrCheck;
+ }
+ } // end if cc_ loop
+ } // end for j loop
+ } // end for i loop
+
+ // the whole checking is done smoothly and all attributes are equal
+ equal = true;
+ }
+
+ return equal;
+ }
+
+
+ /**
+ * Check whether a <code>Node</code> is supported. This method
+ * can be intentionally overridden by any class that extends from
+ * <code>NodeIterator</code> so that it can specify which
+ * <code>Node</code> to support.
+ *
+ * @param node <code>Node</code> to check.
+ *
+ * @return true if <code>Node</code> is supported, false otherwise.
+ */
+ protected abstract boolean nodeSupported(Node node);
+
+ // doing a depth first search for the tree and mark all supported nodes
+ private void markTree(Node node) {
+
+ // if this is a supported node, then we add it to our cache table
+ if (nodeSupported(node)) {
+ nodeList.add(node);
+ } else {
+ // or we go through all children nodes recursively
+ // (can be optimized in future)
+ String nodeName = node.getNodeName();
+ if ( cc_ == null || cc_.canConvertTag(nodeName)) {
+ NodeList nodeList = node.getChildNodes();
+ int nodeListLength = nodeList.getLength();
+ for (int i = 0; i < nodeListLength; i++) {
+ markTree(nodeList.item(i));
+ }
+ }
+ else {
+ Debug.log(Debug.INFO, " [NodeIterator::markTree] Skipping node "
+ + nodeName);
+ }
+ }
+ }
+}
+
diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/diff/ObjectArrayIterator.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/diff/ObjectArrayIterator.java
new file mode 100644
index 000000000000..d52b53fd78a7
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/diff/ObjectArrayIterator.java
@@ -0,0 +1,210 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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.merger.diff;
+
+import org.openoffice.xmerge.merger.Iterator;
+
+/**
+ * <p>This is an implementation of the <code>Iterator</code> interface.
+ * It is based upon a simple <code>Object</code> array.</p>
+ *
+ * <p>Note: this class is not thread safe for performance reasons.</p>
+ *
+ * @author smak
+ */
+public final class ObjectArrayIterator implements Iterator {
+
+
+ /**
+ * The <code>Object</code> array.
+ */
+ private Object [] objArray;
+ private int currentPosition;
+
+
+ /**
+ * Private default constructor.
+ */
+ private ObjectArrayIterator() {
+ // do not allow user new a ObjectArrayIterator without argument
+ }
+
+
+ /**
+ * Standard constructor.
+ *
+ * @param objArray The <code>Object</code> array.
+ */
+ public ObjectArrayIterator(Object [] objArray) {
+ if (objArray != null) {
+ this.objArray = new Object[objArray.length];
+ System.arraycopy(objArray, 0, this.objArray, 0, objArray.length);
+ currentPosition = 0;
+ } else {
+ this.objArray = new Object[0];
+ }
+ }
+
+
+ public Object next() {
+ if (currentPosition < objArray.length - 1) {
+ currentPosition++;
+ return currentElement();
+ } else {
+ return null;
+ }
+
+ }
+
+
+ public Object previous() {
+ if (currentPosition > 0) {
+ currentPosition--;
+ return currentElement();
+ } else {
+ return null;
+ }
+ }
+
+
+ public Object start() {
+ currentPosition = 0;
+ return currentElement();
+ }
+
+
+ public Object end() {
+ if (objArray.length > 0) {
+ currentPosition = objArray.length - 1;
+ }
+ return currentElement();
+ }
+
+
+ public Object currentElement() {
+ if (objArray.length > 0) {
+ return objArray[currentPosition];
+ } else {
+ return null;
+ }
+ }
+
+
+ /**
+ * Replace current <code>Object</code>.
+ *
+ * @param object <code>Object</code> to replace.
+ */
+ public void replace(Object object) {
+ objArray[currentPosition] = object;
+ }
+
+
+ /**
+ * Insert <code>Object</code> after current <code>Object</code>.
+ *
+ * @param object <code>Object</code> to insert.
+ */
+ public void insert(Object object) {
+ Object [] objArray2 = new Object[objArray.length+1];
+
+ // copy the array content up before the currentposition
+ if (currentPosition > 0) {
+ System.arraycopy(objArray, 0, objArray2, 0, currentPosition);
+ }
+
+ objArray2[currentPosition] = object;
+
+ // copy the array content up after the currentposition
+ System.arraycopy(objArray, currentPosition, objArray2,
+ currentPosition + 1, objArray.length - currentPosition);
+
+ objArray = objArray2;
+ currentPosition++;
+ }
+
+ /**
+ * Append <code>Object</code> after current <code>Object</code>.
+ *
+ * @param object <code>Object</code> to append.
+ */
+ public void append(Object object) {
+ Object [] objArray2 = new Object[objArray.length + 1];
+
+ int newPosition = currentPosition + 1;
+
+ // copy the array content up to the currentposition
+ System.arraycopy(objArray, 0, objArray2, 0, newPosition);
+
+ objArray2[newPosition] = object;
+
+ // copy the array content up after the currentposition
+ if (currentPosition < objArray.length - 1) {
+ System.arraycopy(objArray, newPosition, objArray2,
+ newPosition + 1, objArray.length - newPosition);
+ }
+
+ objArray = objArray2;
+ }
+
+ /**
+ * Remove current <code>Object</code>.
+ */
+ public void remove() {
+ Object [] objArray2 = new Object[objArray.length - 1];
+
+ // copy the array content up before the currentposition
+ if (currentPosition > 0) {
+ System.arraycopy(objArray, 0, objArray2, 0, currentPosition);
+ }
+
+ // copy the array content up after the currentposition
+ if (currentPosition < objArray.length - 1) {
+ System.arraycopy(objArray, currentPosition + 1, objArray2,
+ currentPosition, objArray.length - currentPosition - 1);
+ }
+
+ objArray = objArray2;
+
+ if (currentPosition == objArray.length)
+ currentPosition--;
+ }
+
+ public int elementCount() {
+ return objArray.length;
+ }
+
+ public boolean equivalent(Object obj1, Object obj2) {
+ return obj1.equals(obj2);
+ }
+
+ public void refresh() {
+ // do nothing
+ }
+}
+
diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/diff/ParaNodeIterator.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/diff/ParaNodeIterator.java
new file mode 100644
index 000000000000..2e16275e0e44
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/diff/ParaNodeIterator.java
@@ -0,0 +1,90 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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.merger.diff;
+
+import org.w3c.dom.Node;
+
+import org.openoffice.xmerge.ConverterCapabilities;
+import org.openoffice.xmerge.converter.xml.OfficeConstants;
+
+
+/**
+ * <p>This is an implementation of the <code>Iterator</code> interface.
+ * It will traverse the tree and find the Paragraph/Heading <code>Node</code>
+ * sequences.</p>
+ *
+ * <p>Note: Once the XML Tree is parsed, then the <code>Iterator</code> will
+ * be a snap shot of that tree. That means even the tree is modified later,
+ * than the cached paragraph <code>Node</code> list will not be updated
+ * accordingly. For this reason and for performance reasons this
+ * <code>Iterator</code> does not support any operation methods such as
+ * insert, remove or replace. The main purpose of this
+ * <code>Iterator</code> is to be used with difference, not with merge.</p>
+ *
+ * @author smak
+ */
+public final class ParaNodeIterator extends NodeIterator {
+
+ // can be expanded to an array in the future, not necessary right now
+ private static final String SUPPORTED_TAG1 = OfficeConstants.TAG_PARAGRAPH;
+ private static final String SUPPORTED_TAG2 = OfficeConstants.TAG_HEADING;
+
+ /**
+ * Standard constructor.
+ *
+ * @param cc The <code>ConverterCapabilities</code>.
+ * @param node The initial root <code>Node</code>.
+ */
+ public ParaNodeIterator(ConverterCapabilities cc, Node node) {
+ // not using convertercapabilities unless it's needed in future.
+ super(cc, node);
+ }
+
+
+ /**
+ * Overwrite the parent <code>nodeSupported</code> method.
+ *
+ * @param node <code>Node</code> to check.
+ *
+ * @return true if the <code>Node</code> is supported, false
+ * otherwise.
+ */
+ protected boolean nodeSupported(Node node) {
+
+ // can use an array later to check all possible tags for
+ // future expansion
+ if (node.getNodeType() == Node.ELEMENT_NODE &&
+ (node.getNodeName().equals(SUPPORTED_TAG1) ||
+ node.getNodeName().equals(SUPPORTED_TAG2))) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+}
+
diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/diff/RowIterator.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/diff/RowIterator.java
new file mode 100644
index 000000000000..0e514209e2f0
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/diff/RowIterator.java
@@ -0,0 +1,81 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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.merger.diff;
+
+import org.w3c.dom.Node;
+
+import org.openoffice.xmerge.ConverterCapabilities;
+import org.openoffice.xmerge.converter.xml.OfficeConstants;
+import org.openoffice.xmerge.util.Resources;
+
+
+/**
+ * This is an implementation of the <code>Iterator</code> interface and extends
+ * <code>NodeIterator</code>. It will traverse the tree and find row sequences.
+ *
+ * @author smak
+ */
+public final class RowIterator extends NodeIterator {
+
+ private Resources res = Resources.getInstance();
+
+ // TODO: should compare the ConverterCapabilities supported feature only!
+ // otherwise even though one with a chart, one without, will still be
+ // considered to be not equivalent.
+
+ /**
+ * Standard constructor.
+ *
+ * @param cc The <code>ConverterCapabilities</code>.
+ * @param node The initial root <code>Node</code>.
+ */
+ public RowIterator(ConverterCapabilities cc, Node node) {
+ super(cc, node);
+ }
+
+ /**
+ * Overwrite the parent <code>nodeSupported</code> method. Only
+ * row <code>Node</code> objects are supported.
+ *
+ * @param node <code>Node</code> to check.
+ *
+ * @return true if the <code>Node</code> is supported, false otherwise.
+ */
+ protected boolean nodeSupported(Node node) {
+
+ // can use an array later to check all possible tags for
+ // future expansion
+ if (node.getNodeType() == Node.ELEMENT_NODE &&
+ node.getNodeName().equals(OfficeConstants.TAG_TABLE_ROW)) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+}
+
diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/diff/TextNodeEntry.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/diff/TextNodeEntry.java
new file mode 100644
index 000000000000..8840585b2b92
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/diff/TextNodeEntry.java
@@ -0,0 +1,88 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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.merger.diff;
+
+import org.w3c.dom.Node;
+
+/**
+ * A small class to hold the start/end character position and the
+ * <code>Node</code> pointer in a text <code>Node</code>. It is
+ * mainly used for character parser to make a list of text
+ * <code>Node</code> cache entries.
+ *
+ * @author smak
+ */
+public class TextNodeEntry {
+
+ private int startChar_;
+ private int endChar_;
+ private Node node_;
+
+ /**
+ * Constructor
+ *
+ * @param startChar The start character position.
+ * @param endChar The end character position.
+ * @param node The text <code>Node</code>.
+ */
+ public TextNodeEntry(int startChar, int endChar, Node node) {
+ startChar_ = startChar;
+ endChar_ = endChar;
+ node_ = node;
+ }
+
+ /**
+ * Returns the start character.
+ *
+ * @return The start character.
+ */
+ public int startChar() {
+ return startChar_;
+ }
+
+
+ /**
+ * Returns the end character.
+ *
+ * @return The end character.
+ */
+ public int endChar() {
+ return endChar_;
+ }
+
+
+ /**
+ * Returns the <code>Node</code>.
+ *
+ * @return The <code>Node</code>.
+ */
+ public Node node() {
+ return node_;
+ }
+}
+
diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/diff/TextNodeIterator.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/diff/TextNodeIterator.java
new file mode 100644
index 000000000000..b1da7bcdcdaf
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/diff/TextNodeIterator.java
@@ -0,0 +1,84 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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.merger.diff;
+
+import org.w3c.dom.Node;
+
+import org.openoffice.xmerge.converter.xml.OfficeConstants;
+
+
+/**
+ * <p>This is an implementation of the <code>Iterator</code> interface.
+ * It will traverse the tree and find text/space/tab <code>Node</code>
+ * sequences.</p>
+ *
+ * <p>Note: Once the XML Tree is parsed, then the <code>Iterator</code>
+ * will be a snap shot of that tree. That means even the tree is modified
+ * later, than the cached paragraph <code>Node</code> list will not be
+ * updated accordingly. For this reason and for performance reasons
+ * this <code>Iterator</code> does not support any operation methods
+ * such as insert, remove or replace. The main purpose of this
+ * <code>Iterator</code> is to be used with difference, not with merge.</p>
+ *
+ * @author smak
+ */
+public final class TextNodeIterator extends NodeIterator {
+
+ /**
+ * Standard constructor.
+ *
+ * @param initial The initial root <code>Node</code>.
+ */
+ public TextNodeIterator(Node node) {
+ super(null, node);
+ }
+
+ /**
+ * Overwrite the parent <code>nodeSupported</code> method. Only text
+ * <code>Node</code> objects are supported.
+ *
+ * @param node <code>Node</code> to check.
+ *
+ * @return true if the <code>Node</code> is supported, false
+ * otherwise.
+ */
+ protected boolean nodeSupported(Node node) {
+
+ // can use an array later to check all possible tags for
+ // future expansion
+ if (node.getNodeType() == Node.TEXT_NODE ||
+ node.getNodeName().equals(OfficeConstants.TAG_SPACE) ||
+ node.getNodeName().equals(OfficeConstants.TAG_TAB_STOP) ||
+ node.getNodeName().equals(OfficeConstants.TAG_LINE_BREAK)) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+}
+
diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/diff/package.html b/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/diff/package.html
new file mode 100644
index 000000000000..f9d1b12a5f39
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/diff/package.html
@@ -0,0 +1,43 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<!--
+ #*************************************************************************
+ #
+ DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+
+ Copyright 2000, 2010 Oracle and/or its affiliates.
+
+ OpenOffice.org - a multi-platform office productivity suite
+
+ 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.merger.diff package</title>
+</head>
+
+<body bgcolor="white">
+<p>Provides implementations for the {@link
+org.openoffice.xmerge.merger.Iterator Iterator}
+interface and related support classes. These are used by the {@link
+org.openoffice.xmerge.merger.DiffAlgorithm
+DiffAlgorithm} interface.</p>
+
+</body>
+</html>
diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/merge/CharacterBaseParagraphMerge.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/merge/CharacterBaseParagraphMerge.java
new file mode 100644
index 000000000000..adcd483dd44a
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/merge/CharacterBaseParagraphMerge.java
@@ -0,0 +1,310 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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.merger.merge;
+
+import java.util.List;
+import org.w3c.dom.Node;
+import org.openoffice.xmerge.merger.Difference;
+import org.openoffice.xmerge.merger.NodeMergeAlgorithm;
+import org.openoffice.xmerge.merger.diff.CharacterParser;
+import org.openoffice.xmerge.merger.diff.CharArrayLCSAlgorithm;
+import org.openoffice.xmerge.merger.diff.TextNodeEntry;
+import org.openoffice.xmerge.util.Debug;
+
+/**
+ * This is an implementation of the <code>NodeMergeAlgorithm</code>
+ * interface. It is used to merge two paragraph <code>Node</code>
+ * objects based on character comparisons.
+ *
+ * @author smak
+ */
+public final class CharacterBaseParagraphMerge
+ implements NodeMergeAlgorithm {
+
+
+ private class cacheCharArray {
+ public cacheCharArray(int cacheSize) {
+ }
+ }
+
+
+ /**
+ * Merge two paragraph <code>Node</code> by using Longest Common
+ * Subsequence (LCS) character algorithm defined in {@link
+ * org.openoffice.xmerge.merger.diff.CharArrayLCSAlgorithm
+ * CharArrayLCSAlgorithm}
+ *
+ * @param orgPara The original paragraph <code>Node</code>.
+ * @param modPara The modified paragraph <code>Node</code>.
+ */
+ public void merge(Node orgPara, Node modPara) {
+ CharacterParser orgParser = new CharacterParser(orgPara);
+ CharacterParser modParser = new CharacterParser(modPara);
+
+ char[] orgCharArray = orgParser.getCharArray();
+ char[] modCharArray = modParser.getCharArray();
+
+ CharArrayLCSAlgorithm diffAlgo = new CharArrayLCSAlgorithm();
+
+ Difference[] diffResult = diffAlgo.computeDiffs(orgCharArray,
+ modCharArray);
+ // debug use
+ System.out.println("Diff Result: ");
+ for (int i = 0; i < diffResult.length; i++) {
+ Debug.log(Debug.INFO, diffResult[i].debug());
+ }
+
+ applyDifference(orgParser, modParser, diffResult);
+ }
+
+
+ private void applyDifference(CharacterParser orgParser,
+ CharacterParser modParser,
+ Difference[] diffs) {
+
+ List orgNodeList = orgParser.getNodeList();
+ List modNodeList = modParser.getNodeList();
+ int diffCount = 0;
+ int modNodeListCnt = 0;
+ int numNode = orgNodeList.size();
+
+ for (int i = 0; i < numNode; i++) {
+
+ int extraChar = 0;
+ int orgDiffCount = diffCount;
+ TextNodeEntry orgTextNode = (TextNodeEntry)(orgNodeList.get(i));
+
+ Debug.log(Debug.INFO, "checking node " + (i + 1) + " of " + numNode);
+
+ // check any difference in this node and estimate the new char num
+ for (; diffCount < diffs.length; diffCount++) {
+
+ Debug.log(Debug.INFO, " checking diff " + (diffCount + 1) +
+ " of " + diffs.length);
+ Debug.log(Debug.INFO, " OrgPosision <" +
+ diffs[diffCount].getOrgPosition() + "> diffCount <" +
+ diffCount + "> orgDiffCount <" + orgDiffCount + ">");
+
+ // don't need to check and diffs beyond the current node text
+ // range except the last node
+ if (diffs[diffCount].getOrgPosition() > orgTextNode.endChar() &&
+ i < numNode - 1) {
+ Debug.log(Debug.INFO, " breaking!");
+ break;
+ }
+
+ if (diffs[diffCount].getOrgPosition()
+ >= orgTextNode.startChar()) {
+ if (diffs[diffCount].getOperation() == Difference.DELETE) {
+ extraChar--;
+ } else if (diffs[diffCount].getOperation()
+ == Difference.ADD) {
+ extraChar++;
+ }
+
+ }
+ }
+
+ Debug.log(Debug.INFO, " final diffCount <" + diffCount +
+ "> final orgDiffCount <" + orgDiffCount + ">");
+
+ // will only try to merge if there is a difference in this node
+ if (diffCount > orgDiffCount) {
+
+ Debug.log(Debug.INFO, " There is a difference, doing merge");
+ Debug.log(Debug.INFO, " TextNode name <" +
+ orgTextNode.node().getNodeName() + ">");
+ Debug.log(Debug.INFO, " TextNode value <" +
+ orgTextNode.node().getNodeValue() + ">");
+ Debug.log(Debug.INFO, " TextNode start char <" +
+ orgTextNode.startChar() + "> TextNode end char <" +
+ orgTextNode.endChar() + ">");
+ Debug.log(Debug.INFO, " extraChar value <" + extraChar + ">");
+
+ coreMerge(orgDiffCount, diffCount, diffs, orgParser,
+ modParser, orgTextNode, extraChar);
+ }
+ }
+ }
+
+ private void coreMerge(int startDiffNum, int endDiffNum, Difference[] diffs,
+ CharacterParser orgParser, CharacterParser modParser,
+ TextNodeEntry orgTextNode, int extraChar) {
+
+ Node orgNode = orgTextNode.node();
+ char[] modTextArray = modParser.getCharArray();
+ String tmpString;
+
+ // Handle situation where getNodeValue returns null
+ //
+ if (orgNode.getNodeValue() != null)
+ tmpString = orgNode.getNodeValue();
+ else
+ tmpString = "";
+
+ char[] orgNodeText = tmpString.toCharArray();
+ char[] newNodeText;
+
+ if (orgNodeText.length + extraChar > 0)
+ newNodeText = new char[orgNodeText.length + extraChar];
+ else
+ newNodeText = new char[0];
+
+ int orgTextPosition = orgTextNode.startChar(); // used for block copy
+ int newTextPosition = 0; // used for block copy
+ int unChangedTextLength = 0;
+
+ char[] cacheCharArray = new char[endDiffNum - startDiffNum];
+ int cacheLength = 0;
+ int lastDiffOperation = Difference.UNCHANGE;
+ int lastDiffPosition = -1;
+
+ // starting to diff
+ //
+ for (int j = startDiffNum; j < endDiffNum; j++) {
+
+ // copy any contents before the diff
+ //
+ if (diffs[j].getOrgPosition() > orgTextPosition) {
+ // need to flush first
+ if (cacheLength > 0) {
+ System.arraycopy(cacheCharArray, 0,
+ newNodeText, newTextPosition, cacheLength);
+ newTextPosition += cacheLength;
+
+ // reset the markers
+ lastDiffPosition = -1;
+ lastDiffOperation = Difference.UNCHANGE;
+ cacheLength = 0;
+ }
+
+ // find out the length how many characters are
+ // untouched by the diff
+ unChangedTextLength = diffs[j].getOrgPosition() -
+ orgTextPosition;
+ System.arraycopy(orgNodeText,
+ orgTextPosition - orgTextNode.startChar(),
+ newNodeText, newTextPosition,
+ unChangedTextLength);
+ orgTextPosition += unChangedTextLength;
+ newTextPosition += unChangedTextLength;
+ }
+
+ // for any deleted characters, just skip without copy
+ // but still need to take care the cached characters
+ //
+ if (diffs[j].getOperation() == Difference.DELETE) {
+ orgTextPosition++;
+
+ // flush out the cache and copy the content to new Text
+ if (cacheLength > 0) {
+ System.arraycopy(cacheCharArray, 0,
+ newNodeText, newTextPosition, cacheLength);
+ newTextPosition += cacheLength;
+
+ // reset the markers
+ lastDiffPosition = -1;
+ lastDiffOperation = Difference.UNCHANGE;
+ cacheLength = 0;
+ }
+
+ continue;
+
+
+ // check whether we should flush the cache.
+ // For changed diffs, only continuous changes can be cached
+ // For Add diffs, only same insertion point can be cached
+ // and for both changed/add diffs, need to have same operation
+ // as last cached diffs.
+
+ } else {
+ if (lastDiffOperation != diffs[j].getOperation() ||
+ (diffs[j].getOperation() == Difference.CHANGE &&
+ diffs[j].getOrgPosition() != lastDiffPosition + 1) ||
+ (diffs[j].getOperation() == Difference.ADD &&
+ diffs[j].getOrgPosition() != lastDiffPosition)) {
+
+ // flush the cache
+ if (cacheLength > 0) {
+ System.arraycopy(cacheCharArray, 0, newNodeText,
+ newTextPosition, cacheLength);
+ newTextPosition += cacheLength;
+
+ // reset the markers
+ lastDiffPosition = -1;
+ lastDiffOperation = Difference.UNCHANGE;
+ cacheLength = 0;
+ }
+ }
+
+ // add the diffs to the cache, now the diffs will be either
+ // a new 'changed' char or is an adjacent following change of
+ // last difference
+ cacheCharArray[cacheLength] =
+ modTextArray[diffs[j].getModPosition()];
+ cacheLength++;
+ lastDiffOperation = diffs[j].getOperation();
+ lastDiffPosition = diffs[j].getOrgPosition();
+
+ // need to increment the original text position
+ // after we cached it
+ if (lastDiffOperation == Difference.CHANGE) {
+ orgTextPosition++;
+ }
+ }
+ }
+
+ // flush any contents remaining in the cache
+ if (cacheLength > 0) {
+ System.arraycopy(cacheCharArray, 0, newNodeText,
+ newTextPosition, cacheLength);
+ newTextPosition += cacheLength;
+ // no need to reset any cache-related info as this is a last flush
+ }
+
+ // copy any contents after all the diffs
+ int orgStartPosition = orgTextNode.startChar();
+ if (orgNodeText.length + orgStartPosition > orgTextPosition) {
+ unChangedTextLength = orgNodeText.length + orgStartPosition
+ - orgTextPosition;
+ System.arraycopy(orgNodeText, orgTextPosition - orgStartPosition,
+ newNodeText, newTextPosition,
+ unChangedTextLength);
+ }
+
+ // set the text to the original node if there are any diffs processed.
+ // can't use newNodeText.length to check as even it is empty, we may
+ // process a whole bunch of deletion already (i.e. the whole
+ // orgNodeText deleted).
+ if (endDiffNum > startDiffNum) {
+ String newString = new String(newNodeText);
+ orgNode.setNodeValue(newString);
+ }
+ }
+}
+
diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/merge/DocumentMerge.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/merge/DocumentMerge.java
new file mode 100644
index 000000000000..5c293000a964
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/merge/DocumentMerge.java
@@ -0,0 +1,247 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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.merger.merge;
+
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+import org.openoffice.xmerge.ConverterCapabilities;
+import org.openoffice.xmerge.MergeException;
+import org.openoffice.xmerge.merger.MergeAlgorithm;
+import org.openoffice.xmerge.merger.NodeMergeAlgorithm;
+import org.openoffice.xmerge.merger.Iterator;
+import org.openoffice.xmerge.merger.Difference;
+import org.openoffice.xmerge.util.XmlUtil;
+
+/**
+ * This is an implementation of the <code>MergeAlgorithm</code> interface.
+ * This class will merge two <code>Document</code> classes. It utilizes the
+ * appropriate class which implements {@link
+ * org.openoffice.xmerge.merger.NodeMergeAlgorithm
+ * NodeMergeAlgorithm} to perform the merge.
+ *
+ * @author smak
+ */
+public class DocumentMerge implements MergeAlgorithm {
+
+ private NodeMergeAlgorithm subDocumentMerge = null;
+
+ /** The capabilities of this converter. */
+ protected ConverterCapabilities cc_;
+
+
+ /**
+ * Constructor
+ *
+ * @param cc The <code>ConverterCapabilities</code>.
+ * @param merge The <code>NodeMergeAlgorithm</code>.
+ */
+ public DocumentMerge(ConverterCapabilities cc, NodeMergeAlgorithm merge) {
+ cc_ = cc;
+ subDocumentMerge = merge;
+ }
+
+
+ public void applyDifference(Iterator orgSeq, Iterator modSeq,
+ Difference[] differences) throws MergeException {
+
+
+ // a quick test whether the differences array is in ascending order
+ int currentPosition = -1;
+ boolean haveDeleteOperation = false;
+
+ for (int i = 0; i < differences.length; i++) {
+ if (differences[i].getOrgPosition() > currentPosition) {
+ currentPosition = differences[i].getOrgPosition();
+ if (differences[i].getOperation() == Difference.DELETE) {
+ haveDeleteOperation = true;
+ } else {
+ haveDeleteOperation = false;
+ }
+ } else if (differences[i].getOrgPosition() == currentPosition) {
+ if (differences[i].getOperation() == Difference.DELETE) {
+ haveDeleteOperation = true;
+ } else if (differences[i].getOperation() == Difference.ADD &&
+ haveDeleteOperation == true) {
+ throw new MergeException(
+ "Differences array is not sorted. Delete before Add");
+ }
+ } else {
+ throw new MergeException("Differences array need to be sorted.");
+ }
+ }
+
+ // reset sequence counters
+ orgSeq.start();
+ int orgSeqCounter = 0;
+
+ modSeq.start();
+ int modSeqCounter = 0;
+
+ // check for each diff unit in the diff array to apply the diff
+ for (int i = 0; i < differences.length; i++) {
+
+ Difference currentDiff = differences[i];
+
+ int operation = currentDiff.getOperation();
+
+ Object currentElement;
+
+ switch (operation) {
+
+ case Difference.DELETE:
+ // loop through the original sequence up to the expected
+ // position. note that we use delta (see above comment)
+ // also. we will just continue the counter without reset it.
+ for (;
+ orgSeqCounter < currentDiff.getOrgPosition();
+ orgSeqCounter++, orgSeq.next()) {
+ // empty
+ }
+
+ // remove the Node. note that it will NOT affect the
+ // iterator sequence as ParaNodeIterator is a static one.
+ removeNode((Node)(orgSeq.currentElement()));
+
+ break;
+
+ // if it's an add operation, then get content from original seq
+ case Difference.ADD:
+ // loop through the modified sequence up to the expected
+ // position to get the content. As we don't need to modify
+ // the sequence. we don't need to use delta to do adjustment.
+ for (;
+ modSeqCounter < currentDiff.getModPosition();
+ modSeqCounter++, modSeq.next()) {
+ // empty
+ }
+
+ currentElement = orgSeq.currentElement();
+
+ for (;
+ orgSeqCounter < currentDiff.getOrgPosition();
+ orgSeqCounter++, currentElement = orgSeq.next()) {
+ // empty
+ }
+
+ if (orgSeqCounter > orgSeq.elementCount() - 1) {
+ // append the element to the end of the original sequence
+ appendNode((Node)(orgSeq.currentElement()),
+ (Node)(modSeq.currentElement()));
+ } else {
+ // insert the element BEFORE the current element
+ insertNode((Node)(orgSeq.currentElement()),
+ (Node)(modSeq.currentElement()));
+ }
+
+ break;
+
+ case Difference.CHANGE:
+ for (;
+ modSeqCounter < currentDiff.getModPosition();
+ modSeqCounter++, modSeq.next()) {
+ // empty
+ }
+
+ currentElement = orgSeq.currentElement();
+
+ for (;
+ orgSeqCounter < currentDiff.getOrgPosition();
+ orgSeqCounter++, currentElement = orgSeq.next()) {
+ // empty
+ }
+
+ if (subDocumentMerge == null) {
+ // use a simple replace if no row merge algorithm supply
+ replaceElement((Element)orgSeq.currentElement(),
+ (Element)modSeq.currentElement());
+ } else {
+ subDocumentMerge.merge((Element)orgSeq.currentElement(),
+ (Element)modSeq.currentElement());
+
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+
+ /**
+ * Removes the specified <code>Node</code>.
+ *
+ * @param node <code>Node</code> to remove.
+ */
+ protected void removeNode(Node node) {
+
+ Node parent = node.getParentNode();
+ parent.removeChild(node);
+ }
+
+ /**
+ * Appends <code>Node</code> after the specified <code>Node</code>.
+ *
+ * @param oldNode <code>Node</code> to append after.
+ * @param newNode <code>Node</code> to append.
+ */
+ protected void appendNode(Node oldNode, Node newNode) {
+ Node clonedNode = XmlUtil.deepClone(oldNode, newNode);
+ Node parent = oldNode.getParentNode();
+ parent.appendChild(clonedNode);
+ }
+
+
+ /**
+ * Insert <code>Node</code> before the specified <code>Node</code>.
+ *
+ * @param oldNode <code>Node</code> to insert before.
+ * @param newNode <code>Node</code> to insert.
+ */
+ protected void insertNode(Node oldNode, Node newNode) {
+ Node clonedNode = XmlUtil.deepClone(oldNode, newNode);
+ Node parent = oldNode.getParentNode();
+ parent.insertBefore(clonedNode, oldNode);
+ }
+
+
+ /**
+ * Replace <code>Element</code>.
+ *
+ * @param currElem <code>Element</code> to be replaced.
+ * @param newElem <code>Element</code> to replace.
+ */
+ protected void replaceElement(Element currElem, Element newElem) {
+
+ Node clonedNode = XmlUtil.deepClone(currElem, newElem);
+ Node parent = currElem.getParentNode();
+ parent.replaceChild(clonedNode, currElem);
+ }
+}
+
diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/merge/PositionBaseRowMerge.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/merge/PositionBaseRowMerge.java
new file mode 100644
index 000000000000..4d912d1c8a56
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/merge/PositionBaseRowMerge.java
@@ -0,0 +1,260 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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.merger.merge;
+
+import org.w3c.dom.Node;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.NamedNodeMap;
+
+import org.openoffice.xmerge.ConverterCapabilities;
+import org.openoffice.xmerge.merger.Iterator;
+import org.openoffice.xmerge.merger.NodeMergeAlgorithm;
+import org.openoffice.xmerge.merger.diff.CellNodeIterator;
+import org.openoffice.xmerge.converter.xml.OfficeConstants;
+import org.openoffice.xmerge.util.XmlUtil;
+
+
+/**
+ * This is an implementation of the <code>NodeMergeAlgorithm</code>
+ * interface. It is used to merge two rows using a positional
+ * comparison base method.
+ */
+public final class PositionBaseRowMerge implements NodeMergeAlgorithm {
+
+ /** The capabilities of this converter. */
+ private ConverterCapabilities cc_;
+
+
+ /**
+ * Constructor.
+ *
+ * @param cc The <code>ConverterCapabilities</code>.
+ */
+ public PositionBaseRowMerge(ConverterCapabilities cc) {
+ cc_ = cc;
+ }
+
+
+ public void merge(Node orgRow, Node modRow) {
+
+ Iterator orgCells = new CellNodeIterator(cc_, orgRow);
+ Iterator modCells = new CellNodeIterator(cc_, modRow);
+
+ mergeCellSequences(orgCells, modCells);
+ }
+
+
+ // used to compare the cell 1 by 1
+ private void mergeCellSequences(Iterator orgSeq, Iterator modSeq) {
+
+ boolean needMerge = true;
+ Element orgCell, modCell;
+
+ Object orgSeqObject = orgSeq.start();
+ Object modSeqObject = modSeq.start();
+
+ while (orgSeqObject != null) {
+
+
+ needMerge = true;
+
+ if (modSeqObject == null) {
+ // no corresponding cell in the target, empty out the cell
+ SheetUtil.emptyCell(cc_, (Node)orgSeqObject);
+ orgSeqObject = orgSeq.next();
+
+ } else {
+
+ // compare the cell directly
+ if (!orgSeq.equivalent(orgSeqObject, modSeqObject)) {
+
+ orgCell = (Element)orgSeqObject;
+ modCell = (Element)modSeqObject;
+
+ // check whether the original cell with multiple column
+ // if so, need to split one out for merge
+ String orgColRepeated = orgCell.getAttribute(
+ OfficeConstants.ATTRIBUTE_TABLE_NUM_COLUMNS_REPEATED);
+ String modColRepeated = modCell.getAttribute(
+ OfficeConstants.ATTRIBUTE_TABLE_NUM_COLUMNS_REPEATED);
+
+ int orgColNum = 1;
+ int modColNum = 1;
+
+ if (orgColRepeated.length() > 0) {
+ orgColNum =
+ Integer.valueOf(orgColRepeated).intValue();
+ }
+ if (modColRepeated.length() > 0) {
+ modColNum =
+ Integer.valueOf(modColRepeated).intValue();
+ }
+
+ // try to find out the common number of repeated cols
+ if (orgColNum == modColNum) {
+ orgSeqObject = orgSeq.next();
+ modSeqObject = modSeq.next();
+
+ // cut the original cell into 2 half, first half
+ // have the repeated attribute = modify cell attr
+ } else if (orgColNum > modColNum) {
+ Element orgSplitCell = splitColRepeatedCell(
+ orgCell, modColNum,
+ orgColNum - modColNum);
+ // it may equal after the split!
+ if (orgSeq.equivalent(orgSplitCell, modCell)) {
+ needMerge = false;
+ }
+ orgCell = orgSplitCell;
+ modSeqObject = modSeq.next();
+
+ // cut the modified cell into 2 half, first half
+ // have the repeated attribute = original cell attr
+ } else {
+ Element modSplitCell = splitColRepeatedCell(
+ modCell, orgColNum,
+ modColNum - orgColNum);
+ // it may equal after the split!
+ if (modSeq.equivalent(orgCell, modSplitCell)) {
+ needMerge = false;
+ }
+ modCell = modSplitCell;
+ orgSeqObject = orgSeq.next();
+ }
+
+ if (needMerge) {
+ mergeCells(orgCell, modCell);
+ }
+
+ } else {
+ // cells are equivalent, move on to next one.
+ orgSeqObject = orgSeq.next();
+ modSeqObject = modSeq.next();
+ } // end if-else
+ } // end if-else
+ } // end while loop
+
+ // get the one of the original cell, so that the cloned node
+ // can base it to find the document node
+ orgCell = (Element)orgSeq.start();
+
+ // add any extra cells to the original cell sequence.
+ for (; modSeqObject != null; modSeqObject = modSeq.next()) {
+ Node clonedNode = XmlUtil.deepClone(orgCell, (Node)modSeqObject);
+ Node parent = orgCell.getParentNode();
+ parent.appendChild(clonedNode);
+ }
+ }
+
+
+ private Element splitColRepeatedCell(Element orgCell,
+ int splitNum, int orgNum) {
+ // NOTE: should we really want to do deep clone?
+ // in most the case, it is an empty cell, but the
+ // specification didn't forbid any node to use multiple
+ // column attributes. i.e. the node can contain text
+ // nodes or other things under it.
+ Element splitCell = (Element)(orgCell.cloneNode(true));
+
+ if (splitNum > 1) {
+ splitCell.setAttribute(
+ OfficeConstants.ATTRIBUTE_TABLE_NUM_COLUMNS_REPEATED,
+ String.valueOf(splitNum));
+ } else if (splitNum == 1) {
+ splitCell.removeAttribute(
+ OfficeConstants.ATTRIBUTE_TABLE_NUM_COLUMNS_REPEATED);
+ }
+ if (orgNum > 1) {
+ orgCell.setAttribute(
+ OfficeConstants.ATTRIBUTE_TABLE_NUM_COLUMNS_REPEATED,
+ String.valueOf(orgNum));
+ } else if (orgNum == 1) {
+ orgCell.removeAttribute(
+ OfficeConstants.ATTRIBUTE_TABLE_NUM_COLUMNS_REPEATED);
+ }
+
+ Node parentNode = orgCell.getParentNode();
+ parentNode.insertBefore(splitCell, orgCell);
+
+ return splitCell;
+ }
+
+
+ private void mergeCells(Element orgCell, Element modCell) {
+
+ // remove all the supported attributes and possible text child for
+ // string cells
+ SheetUtil.emptyCell(cc_, orgCell);
+
+ // copy all the supported attributes and possible text child from
+ // the modified cell
+ NamedNodeMap attrNodes = modCell.getAttributes();
+
+ if (attrNodes != null) {
+
+ // copy the first text:p node. As it's not necessary only string
+ // type cell can have a text:p section.
+ NodeList paraNodes =
+ modCell.getElementsByTagName(OfficeConstants.TAG_PARAGRAPH);
+
+ Node firstParaNode = paraNodes.item(0);
+
+ // try to clone the node
+ if (firstParaNode != null) {
+
+ Node clonedNode = XmlUtil.deepClone(orgCell, firstParaNode);
+
+ // insert as the first child of the original cell
+ Node firstChild = orgCell.getFirstChild();
+ if (firstChild != null) {
+ orgCell.insertBefore(clonedNode, firstChild);
+ } else {
+ orgCell.appendChild(clonedNode);
+ }
+ }
+
+ // check all the attributes and copy those we supported in
+ // converter
+ // NOTE: for attribute list, refer to section 4.7.2 in specification
+ int len = attrNodes.getLength();
+
+ for (int i = 0; i < len; i++) {
+ Node attr = attrNodes.item(i);
+
+ // copy the supported attrs
+ if (cc_.canConvertAttribute(OfficeConstants.TAG_TABLE_CELL,
+ attr.getNodeName())) {
+ orgCell.setAttribute(attr.getNodeName(),
+ attr.getNodeValue());
+ }
+ }
+ }
+ }
+}
+
diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/merge/SheetMerge.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/merge/SheetMerge.java
new file mode 100644
index 000000000000..edb95cfd9cb2
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/merge/SheetMerge.java
@@ -0,0 +1,89 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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.merger.merge;
+
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import org.openoffice.xmerge.ConverterCapabilities;
+import org.openoffice.xmerge.merger.NodeMergeAlgorithm;
+
+/**
+ * This class extends the <code>DocumentMerge</code> class.
+ * This class will merge two spreadsheet documents.
+ * The main difference between this implementation and
+ * <code>DocumentMerge</code>
+ * is that this merge will try to maintain unsupported features by
+ * examing the cell <code>node</code> objects one by one when it
+ * removes a node from the original <code>Iterator</code>.
+ *
+ * @author smak
+ */
+public final class SheetMerge extends DocumentMerge {
+
+ /**
+ * Constructor.
+ *
+ * @param cc The <code>ConverterCapabilities</code>.
+ * @param merge The <code>NodeMergeAlgorithm</code>.
+ */
+ public SheetMerge(ConverterCapabilities cc, NodeMergeAlgorithm merge) {
+ super(cc, merge);
+ }
+
+
+ /**
+ * Remove specified <code>Node</code>.
+ *
+ * @param node <code>Node</code> to remove.
+ */
+ protected void removeNode(Node node) {
+
+ clearRow(node);
+ }
+
+
+ /**
+ * Clear the row corresponding to the <code>Node</code>
+ *
+ * @param node <code>Node</code> containing the row to clear.
+ */
+ private void clearRow(Node node) {
+ NodeList children = node.getChildNodes();
+ int numOfChildren = children.getLength();
+
+ // clear all the cells under the row node but maintain any unsupported
+ // features
+ // TODO: we can actually check anything left after the clear up.
+ // if there is nothing left, then we can even delete the cell nodes
+ for (int i = 0; i < numOfChildren; i++) {
+ SheetUtil.emptyCell(cc_, children.item(i));
+ }
+ }
+}
+
diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/merge/SheetUtil.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/merge/SheetUtil.java
new file mode 100644
index 000000000000..0329434105b8
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/merge/SheetUtil.java
@@ -0,0 +1,107 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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.merger.merge;
+
+import org.w3c.dom.Node;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.NamedNodeMap;
+
+import org.openoffice.xmerge.ConverterCapabilities;
+import org.openoffice.xmerge.converter.xml.OfficeConstants;
+
+
+/**
+ * Utility methods to handle sheet XML tree.
+ */
+public class SheetUtil {
+
+ /**
+ * <p>Empty the content of a cell value. This includes the following:
+ * </p>
+ *
+ * <p><ul><li>
+ * Remove all of the supported attributes.
+ * </li><li>
+ * Remove the first <i>text:p</i> <code>Node</code> for most of the cells.
+ * </li></ul></p>
+ *
+ * @param cc The <code>ConverterCapabilities</code>.
+ * @param node The <code>Node</code>.
+ */
+ public static void emptyCell(ConverterCapabilities cc, Node node) {
+
+ NamedNodeMap attrNodes = node.getAttributes();
+
+ if (attrNodes != null) {
+
+ // empty the first text:p node.
+ // Note: it's not necessary only string type cell contain text:p
+ // basically, all different type of cell will contain one
+ Element cell = (Element)node;
+
+ // get the paragraph node list
+ NodeList paraNodes =
+ cell.getElementsByTagName(OfficeConstants.TAG_PARAGRAPH);
+
+ Node firstParaNode = paraNodes.item(0);
+
+ // remove the first paragraph element node
+ if (firstParaNode != null) {
+ Node parent = firstParaNode.getParentNode();
+ parent.removeChild(firstParaNode);
+ }
+
+ // check all the attributes and remove those we supported in
+ // converter
+ // NOTE: for attribute list, refer to section 4.7.2 in specification
+ int len = attrNodes.getLength();
+
+ for (int i = 0; i < len; ) {
+ Node attr = attrNodes.item(i);
+
+ // when we hit the end of the attribute nodes, return
+ // it may happen sooner as we keep on removing nodes
+ if (attr == null) {
+ break;
+ }
+ // remove the supported attr except columns repeated attribute
+ if (cc.canConvertAttribute(OfficeConstants.TAG_TABLE_CELL,
+ attr.getNodeName()) &&
+ !attr.getNodeName().equals(
+ OfficeConstants.ATTRIBUTE_TABLE_NUM_COLUMNS_REPEATED)) {
+
+ attrNodes.removeNamedItem(attr.getNodeName());
+ } else {
+ i++;
+ }
+ }
+ }
+ }
+}
+
diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/merge/package.html b/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/merge/package.html
new file mode 100644
index 000000000000..d54b7c37a0ef
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/merge/package.html
@@ -0,0 +1,43 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<!--
+ #*************************************************************************
+ #
+ DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+
+ Copyright 2000, 2010 Oracle and/or its affiliates.
+
+ OpenOffice.org - a multi-platform office productivity suite
+
+ 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.merger.diff package</title>
+</head>
+
+<body bgcolor="white">
+<p>Provides implementations for the {@link
+org.openoffice.xmerge.merger.MergeAlgorithm
+MergeAlgorithm} interface, the {@link
+org.openoffice.xmerge.merger.NodeMergeAlgorithm
+NodeMergeAlgorithm} interface, and related support classes.</p>
+
+</body>
+</html>
diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/package.html b/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/package.html
new file mode 100644
index 000000000000..e5cd65c3f357
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/package.html
@@ -0,0 +1,75 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<!--
+ #*************************************************************************
+ #
+ DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+
+ Copyright 2000, 2010 Oracle and/or its affiliates.
+
+ OpenOffice.org - a multi-platform office productivity suite
+
+ 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.merger package</title>
+</head>
+
+<body bgcolor="white">
+<p>The <code>DiffAlgorithm</code> and <code>MergeAlgorithm</code>
+are used to provide the merge capabilities of this project.</p>
+
+<p>Merge is useful when an <code>OfficeDocument</code>
+is converted to a &quot;Device&quot; <code>Document</code> format,
+and the &quot;Device&quot; <code>Document</code> version is modified.
+Those changes can be merged back into the original
+<code>OfficeDocument</code> with the merger. The merger is capable
+of doing this even if the &quot;Device&quot; format is lossy in
+comparison to the <code>OfficeDocument</code> format.</p>
+
+<p>The <code>DiffAlgorithm</code> generates a list of
+<code>Difference</code> objects that represent the
+differences between two <code>OfficeDocument</code> objects.
+It is assumed that one is the original <code>OfficeDocument</code>
+object and the other is a &quot;lossy&quot; version of the same
+<code>Document</code> with edits to be merged. Typically the
+&quot;lossy&quot; version is created by converting a &quot;Device&quot
+<code>Document</code> back into an <code>OfficeDocument</code>.
+
+<p>The <code>MergeAlgorithm</code> takes the <code>Difference</code>
+objects as input, and creates a merged <code>OfficeDocument</code>.
+A merged <code>OfficeDocument</code> has the following features:</p>
+
+<p><ul>
+<li>Tags in the <code>OfficeDocument</code> that are not
+ supported in the device format are not altered or removed.
+<li>Changes made to the device format are merged back into
+ the <code>OfficeDocument</code> in the location determined by
+ the <code>DiffAlgorithm</code>.
+</ul></p>
+
+<p>Each converter provides an implementation of the
+{@link org.openoffice.xmerge.ConverterCapabilities
+ConverterCapabilities} which specifies which
+<code>OfficeDocument</code> tags are supported for the
+device format.</p>
+
+</body>
+</html>
diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/package.html b/xmerge/source/xmerge/java/org/openoffice/xmerge/package.html
new file mode 100644
index 000000000000..ea56e3b6c118
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/package.html
@@ -0,0 +1,113 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<!--
+ #*************************************************************************
+ #
+ DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+
+ Copyright 2000, 2010 Oracle and/or its affiliates.
+
+ OpenOffice.org - a multi-platform office productivity suite
+
+ 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 package</title>
+</head>
+
+<body bgcolor="white">
+
+<p>Provides interfaces for converting between two <code>Document</code>
+formats, and supports a &quot;merge&quot; interface for merging back
+changes from a &quot;lossy&quot; format back into a rich format.</p>
+
+<p>The {@link org.openoffice.xmerge.Convert
+Convert} object encapsulates the conversion of one format to/from another
+format. The user requests a <code>Convert</code> object via the
+<code>ConverterFactory</code>.</p>
+
+<p>The <code>Convert</code> class encapsulates a specific plug-in.
+A plug-in can support deserialization (convert from &quot;Device&quot;
+to &quot;Office&quot;) and/or serialization (convert from
+&quot;Office&quot; to &quot;Device&quot;). If a plug-in supports
+both deserialization and serialization, then it can also support
+&quot;merge&quot;.</p>
+
+<p>To support conversions where a single input <code>Document</code> can
+create multiple output <code>Document</code> objects, data is passed in
+and out of the conversion functions via a <code>ConvertData</code> object.
+This <code>ConvertData</code> can contain one or more <code>Document</code>
+objects. It is assumed that the client will know when to pass multiple
+files into a specific plug-in, and that the plug-in will know how to
+handle the multiple files.</p>
+
+<p>Merging is useful when converting from a rich <code>Document</code>
+format to a more lossy format. Then the user may modify the
+<code>Document</code> in the lossy format, and &quot;merge&quot; those
+changes back into the original &quot;rich&quot; <code>Document</code>.
+Each merge implementation provides a <code>ConverterCapabilities</code>
+implementation so that the merge logic knows what changes from the
+&quot;lossy&quot; format to merge into the original &quot;rich&quot;
+<code>Document</code>.</p>
+
+<p>Each plug-in must be registed via the singleton ConverterInfoMgr
+object via its {@link
+org.openoffice.xmerge.util.registry.ConverterInfoMgr#addPlugIn
+addPlugIn} method.</p>
+
+<h2>Providing implementations</h2>
+
+<p>The plug-in implementation must include the <code>getDeviceDocument</code>
+and <code>getOfficeDocument</code> methods. These functions need to return
+the appropriate type of <code>Document</code> for the plug-in. It may be
+necessary to create a new implementation of the <code>Document</code>
+interface if one does not exist that meets the needs of the plug-in.</p>
+
+<p>Currently, base implementations for working with StarWriter XML
+<code>Document</code> objects are available via the
+<a href="converter/xml/sxc/package-summary.html#package_description">
+org.openoffice.xmerge.xml.sxw</a>
+package, and StarCalc XML <code>Document</code> objects via the
+<a href="converter/xml/sxw/package-summary.html#package_description">
+org.openoffice.xmerge.xml.sxc</a>
+package.</p>
+
+<h2>TODO/IDEAS list</h2>
+
+<p><ol>
+<li>An idea is to combine the <code>ConvertData</code> and the
+ <code>Convert</code> classes, so that a <code>ConvertData</code>
+ knows what it can convert into and whether or not it can merge.
+ Then a user would call convert/merge methods on the
+ <code>ConvertData</code> class, which returns a
+ <code>ConvertData</code> object that likewise knows what it can
+ convert/merge into.</li>
+<li><code>DocumentSerialize</code> constructors and the
+ <code>DocumentDeserializer.deserializer</code> method could pass
+ in a <code>ConvertData</code> object rather than assuming
+ a single <code>Document</code> will represent a &quot;rich&quot;
+ <code>Document</code>.</li>
+<li>May need to add a <code>PluginFactory.setProperties</code>
+ method for adding properties specific to each converter.</li>
+</ol></p>
+
+</body>
+</html>
+
diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/test/ConverterInfoList.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/test/ConverterInfoList.java
new file mode 100644
index 000000000000..f9742c48a743
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/test/ConverterInfoList.java
@@ -0,0 +1,105 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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.test;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.BufferedInputStream;
+import java.util.Properties;
+import java.util.Vector;
+import java.util.Enumeration;
+
+/**
+ * Loads a properties file so that registry knows which plug-ins
+ * it needs to load.
+ *
+ * @author: Martin Maher
+ */
+public class ConverterInfoList {
+
+ private static String defaultPropsFile = "ConverterInfoList.properties";
+
+ private Vector jars;
+ private Properties props = null;
+
+
+ /**
+ * This constructor loads and reads the default properties file.
+ * The default property file name is:
+ * &quot;ConverterInfoList.properties&quot;.
+ *
+ * @throws IOException If any I/O error occurs.
+ */
+ public ConverterInfoList() throws IOException {
+ this(defaultPropsFile);
+ }
+
+ /**
+ * This constructor loads and reads the properties file.
+ *
+ * @param propsFile The properties file to load.
+ *
+ * @throws IOException If any I/O error occurs.
+ */
+ public ConverterInfoList(String propsFile) throws IOException {
+
+ Class c = this.getClass();
+ InputStream is = c.getResourceAsStream(propsFile);
+ BufferedInputStream bis = new BufferedInputStream(is);
+ props = new Properties();
+ props.load(bis);
+ bis.close();
+
+ int i = 1;
+ String jarFileName = new String();
+ jars = new Vector();
+
+ while ((jarFileName = props.getProperty("jarname" + i)) != null) {
+ jars.add(jarFileName);
+ i++;
+ }
+ }
+
+
+ /**
+ * Returns a <code>Vector</code> containing a list of
+ * <code>String</code> objects. Each <code>String</code>
+ * describes a plug-in to be loaded into the registry.
+ *
+ *
+ * @return A <code>Vector</code> containing a list of
+ * <code>String</code> objects. Each
+ * <code>String</code> describes a plug-in to be
+ * loaded into the registry.
+ */
+ public Enumeration getJarFileEnum() {
+
+ return jars.elements();
+ }
+}
+
diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/test/ConverterInfoList.properties b/xmerge/source/xmerge/java/org/openoffice/xmerge/test/ConverterInfoList.properties
new file mode 100644
index 000000000000..a342273c67b9
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/test/ConverterInfoList.properties
@@ -0,0 +1,37 @@
+#*************************************************************************
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# Copyright 2000, 2010 Oracle and/or its affiliates.
+#
+# OpenOffice.org - a multi-platform office productivity suite
+#
+# 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.
+#
+#*************************************************************************
+# x-no-translate
+
+#
+# Jarfiles to be loaded
+#
+jarname1=file:///jarDirectory/htmlsoff.jar
+
+#
+# Pocket Word plugin
+#
+jarname2=file:///jarDirectory/pocketword.jar
diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/test/Driver.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/test/Driver.java
new file mode 100644
index 000000000000..5855727364ef
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/test/Driver.java
@@ -0,0 +1,318 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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.test;
+
+import java.util.Vector;
+import java.util.Enumeration;
+import java.io.FileOutputStream;
+import java.io.FileInputStream;
+import java.io.File;
+
+import org.openoffice.xmerge.Convert;
+import org.openoffice.xmerge.Document;
+import org.openoffice.xmerge.ConvertData;
+import org.openoffice.xmerge.ConverterFactory;
+import org.openoffice.xmerge.test.ConverterInfoList;
+import org.openoffice.xmerge.util.registry.ConverterInfoMgr;
+import org.openoffice.xmerge.util.registry.ConverterInfoReader;
+import org.openoffice.xmerge.DocumentMerger;
+
+/**
+ * This class is a command-line driver for the converter framework.
+ * It is expected that this code will be later called by the device
+ * server. It does some basic validation of the command-line
+ * parameters.
+ */
+public final class Driver {
+
+ /** Command-line parameter. */
+ private String fromMime = null;
+
+ /** Command-line parameter. */
+ private String toMime = null;
+
+ /** mergeFile name. */
+ private String mergeFile = null;
+
+ /** Command-line parmeter. */
+ private Vector deviceFiles = new Vector();
+
+ /** Command-line parmeter shortcuts. */
+ private String mimeTypes[] = {
+ "sxc", "staroffice/sxc",
+ "sxw","staroffice/sxw"
+ };
+
+
+ /**
+ * Main.
+ *
+ * @param args The argument passed on the command line.
+ */
+ public static void main(String args[]) {
+
+ // Register jarfiles
+ //
+ String propFile = "ConverterInfoList.properties";
+ ConverterInfoList cil = null;
+ try {
+ cil = new ConverterInfoList(propFile);
+ } catch (Exception e) {
+ System.out.println("\nCannot not load " + propFile +
+ " property file");
+ }
+
+ Enumeration jarInfoEnumeration;
+ ConverterInfoReader cir;
+
+ Enumeration jarFileEnum = cil.getJarFileEnum();
+ while (jarFileEnum.hasMoreElements()) {
+ String jarName = (String) jarFileEnum.nextElement();
+ try {
+ cir = new ConverterInfoReader(jarName, false);
+ jarInfoEnumeration = cir.getConverterInfoEnumeration();
+ ConverterInfoMgr.addPlugIn(jarInfoEnumeration);
+ } catch (Exception e) {
+ System.out.println("\nCannot not load <" + jarName +
+ "> from the <" + propFile + "> property file");
+ }
+ }
+
+ try {
+
+ Driver app = new Driver();
+ app.parseCommandLine(args);
+ app.doConversion();
+ } catch (IllegalArgumentException ex) {
+
+ String msg = ex.getMessage();
+ if (msg != null) System.out.println("\n" + msg);
+ showUsage();
+
+ } catch (Exception ex) {
+
+ String msg = ex.getMessage();
+ if (msg != null) System.out.println("\n" + msg);
+ ex.printStackTrace();
+ }
+ }
+
+
+ /**
+ * Gets a <code>Convert</code> object using the
+ * <code>ConverterFactory</code> and does the conversion using
+ * this object.
+ *
+ * @throws IllegalArgumentException If an argument is invalid.
+ */
+ private void doConversion() throws IllegalArgumentException {
+
+ ConverterFactory cf = new ConverterFactory();
+ Convert myConvert = cf.getConverter(fromMime, toMime);
+ String processFile = null;
+
+ if (myConvert == null) {
+ System.out.println("\nNo plug-in exists to convert from <" +
+ fromMime + "> to <" + toMime + ">");
+ throw new IllegalArgumentException();
+ }
+
+ try {
+ Enumeration dfEnum = deviceFiles.elements();
+ while (dfEnum.hasMoreElements()) {
+ processFile = (String)dfEnum.nextElement();
+ File f = new File(processFile);
+
+ // Make sure the input file actually exists before using it
+ if (!f.exists()) {
+ System.out.println(processFile + " does not exist!");
+ System.exit(0);
+ }
+ FileInputStream fis = new FileInputStream(f);
+ myConvert.addInputStream(f.getName(), fis);
+ }
+ } catch (Exception addExcept) {
+ System.out.println("\nFile <" + processFile + "> is not in <" +
+ fromMime + "> format");
+ throw new IllegalArgumentException();
+ }
+
+ ConvertData dataOut = null;
+
+ try {
+ dataOut = myConvert.convert();
+ } catch (Exception convertExcept) {
+ System.out.println("\nThere was an error in the conversion");
+ convertExcept.printStackTrace();
+ }
+
+ if (dataOut != null ) {
+
+ if (mergeFile == null) {
+ Enumeration docEnum = dataOut.getDocumentEnumeration();
+ while (docEnum.hasMoreElements()) {
+ Document docOut = (Document)docEnum.nextElement();
+ String fileName = docOut.getFileName();
+ try {
+ FileOutputStream fos = new FileOutputStream(fileName);
+ docOut.write(fos);
+ fos.flush();
+ fos.close();
+ } catch (Exception writeExcept) {
+ System.out.println("\nThere was an writing out file <" +
+ fileName + ">");
+ writeExcept.printStackTrace();
+ }
+ }
+ } else {
+ try {
+ FileInputStream mergeIS = new FileInputStream(mergeFile);
+ Document mergeDoc = myConvert.getOfficeDocument(mergeFile, mergeIS);
+ DocumentMerger merger = myConvert.getDocumentMerger(mergeDoc);
+ Enumeration mergeDocEnum = dataOut.getDocumentEnumeration();
+ Document convertedFile = (Document)mergeDocEnum.nextElement();
+
+ merger.merge(convertedFile);
+ mergeIS.close();
+
+ FileOutputStream fos = new FileOutputStream(mergeFile);
+ mergeDoc.write(fos);
+ fos.flush();
+ fos.close();
+ } catch (Exception mergeExcept) {
+ System.out.println("\nThere was an error in the merge");
+ mergeExcept.printStackTrace();
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Display usage.
+ */
+ private static void showUsage() {
+
+ System.out.println("\nUsage:");
+ System.out.println("\n java org.openoffice.xmerge.test.Driver <args>");
+ System.out.println("\n where <args> is as follows:");
+ System.out.println(" -from <MIMETYPE> -to <MIMETYPE> [ -merge <OrigDoc ] <document>\n");
+ }
+
+
+ /**
+ * Parse command-line arguments.
+ *
+ * @param args Array of command line arguments.
+ *
+ * @throws IllegalArgumentException If an argument is invalid.
+ */
+ private void parseCommandLine(String args[])
+ throws IllegalArgumentException {
+
+ if (args.length == 0) {
+ throw new IllegalArgumentException();
+ }
+
+ for (int i = 0; i < args.length; i++) {
+ String arg = args[i];
+
+ if ("-to".equals(arg)) {
+ toMime = extractArg(i, args);
+ for (int j = 0; j < mimeTypes.length; j+=2) {
+ if(mimeTypes[j].equals(extractArg(i, args)))
+ toMime = mimeTypes[j+1];
+ }
+ i++;
+ } else if ("-from".equals(arg)) {
+ fromMime = extractArg(i, args);
+ for (int j = 0; j < mimeTypes.length; j+=2) {
+ if(mimeTypes[j].equals(extractArg(i, args)))
+ fromMime = mimeTypes[j+1];
+ }
+ i++;
+ } else if ("-merge".equals(arg)) {
+ mergeFile = extractArg(i, args);
+ if (!isZip(mergeFile)) {
+ throw new
+ IllegalArgumentException("Arg " + i +
+ ": expected zip, got " +
+ mergeFile);
+ }
+ i++;
+ } else {
+ deviceFiles.add(arg);
+ }
+ }
+
+ System.out.println("\nConverting from " + fromMime + " to " + toMime +
+ ((mergeFile != null) ? " with merge " : " "));
+ }
+
+
+ /**
+ * Extract the next argument from the array, while checking to see
+ * that the array size is not exceeded. Throw a friendly error
+ * message in case the arg is missing.
+ *
+ * @param i Argument index.
+ * @param args Array of command line arguments.
+ *
+ * @return The argument with the specified index.
+ *
+ * @throws IllegalArgumentException If an argument is invalid.
+ */
+ private String extractArg(int i, String args[])
+ throws IllegalArgumentException {
+
+ if (i+1 < args.length)
+ return args[i+1];
+ else throw new
+ IllegalArgumentException("Arg " + i +
+ ": expected arg for " + args[i]);
+ }
+
+
+ /**
+ * Simple validation for Office ZIP files.
+ *
+ * @param zipName The name of the ZIP file.
+ *
+ * @return true if zipName is valid, false otherwise.
+ */
+ private boolean isZip(String zipName) {
+
+ String str = zipName.toLowerCase();
+ if (str.endsWith("sxw") || zipName.endsWith("sxc")) {
+ return true;
+ }
+
+ return false;
+ }
+}
+
diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/util/ActiveSyncDriver.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/util/ActiveSyncDriver.java
new file mode 100644
index 000000000000..fb7941a15ae0
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/util/ActiveSyncDriver.java
@@ -0,0 +1,144 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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.util;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileInputStream;
+import java.util.StringTokenizer;
+
+import org.openoffice.xmerge.Convert;
+import org.openoffice.xmerge.Document;
+import org.openoffice.xmerge.ConvertData;
+import org.openoffice.xmerge.ConverterFactory;
+import org.openoffice.xmerge.util.registry.ConverterInfoMgr;
+import org.openoffice.xmerge.util.registry.ConverterInfoReader;
+
+public class ActiveSyncDriver {
+ public static void main(String[] args) {
+ if (args.length != 4) {
+ return;
+ }
+
+ ActiveSyncDriver asd = new ActiveSyncDriver();
+
+ try {
+ // At the moment can't really signal back to the calling DLL
+ asd.Convert(args[0], args[1], args[2], args[3]);
+ }
+ catch (Exception e) {
+ return;
+ }
+ }
+
+
+ private boolean Convert(String srcMime, String dstMime, String srcFile, String dstFile) throws Exception {
+ /*
+ * The classpath passed in by XMergeSync.dll should contain all of the
+ * jar files, but at the least it will contain xmerge.jar, so strip off
+ * the xmerge.jar portion and use the remainder to provide a root for
+ * the Pocket Word and Pocket Excel plugins.
+ */
+ String ooClassDir = null;
+ String strClassPath = System.getProperty("java.class.path");
+
+ StringTokenizer st = new StringTokenizer(strClassPath, ";");
+
+ // There should be at least one element, but just in case
+ while (st.hasMoreTokens()) {
+ String s = st.nextToken();
+
+ if (s.endsWith("xmerge.jar")) {
+ ooClassDir = s.substring(0, s.indexOf("xmerge.jar"));
+ }
+ }
+
+ if (ooClassDir == null) {
+ return true;
+ }
+
+
+ /*
+ * The XMergeSync.dll should will have checked for the presence of the
+ * jars at the same location already.
+ *
+ * Because they can be installed separately, though, the MIME types need
+ * to be check to see which one to load.
+ */
+ File pluginJar = null;
+ if (srcMime.equals("staroffice/sxw") || srcMime.equals("application/x-pocket-word"))
+ {
+ pluginJar = new File(ooClassDir + "pocketWord.jar");
+ }
+ else if (srcMime.equals("staroffice/sxc") || srcMime.equals("application/x-pocket-excel"))
+ {
+ pluginJar = new File(ooClassDir + "pexcel.jar");
+ }
+
+ ConverterInfoReader cirPlugin = new ConverterInfoReader(pluginJar.toURL().toString(), false);
+
+ ConverterInfoMgr.addPlugIn(cirPlugin.getConverterInfoEnumeration());
+
+ ConverterFactory cf = new ConverterFactory();
+ Convert conv = cf.getConverter(srcMime, dstMime);
+
+ if (conv == null) {
+ return false;
+ }
+
+ // Everything is registered so do the conversion
+ FileInputStream fis = new FileInputStream(srcFile);
+ FileOutputStream fos = new FileOutputStream(dstFile);
+
+ conv.addInputStream(srcFile, fis);
+
+ ConvertData dataOut;
+ try {
+ dataOut = conv.convert();
+ }
+ catch (Exception e) {
+ return false;
+ }
+
+ if (dataOut == null) {
+ return false;
+ }
+
+ // Get the document and write it out.
+ Document doc = (Document)dataOut.getDocumentEnumeration().nextElement();
+ if (doc == null) {
+ return false;
+ }
+
+ doc.write(fos);
+ fos.flush();
+ fos.close();
+
+ return true;
+ }
+}
diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/util/ColourConverter.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/util/ColourConverter.java
new file mode 100644
index 000000000000..33ef294d4ae8
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/util/ColourConverter.java
@@ -0,0 +1,446 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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.util;
+
+
+import java.awt.Color;
+
+/**
+ * Utility class mapping RGB colour specifications to the colour indices used
+ * in the Pocket PC. The original converter was written for use with Pocket
+ * Word it was later put into the utils so Pocket excel could use this code
+ * also. For this reason the defualt values are those used by Pocket Word but
+ * a colour table can be passed in through the constructor to map the 16
+ * values to a colour table.
+ *
+ * These colour indices are based on the Windows VGA 16 colour palette, which
+ * later was used as the basis for the named colours in the HTML 3.2
+ * specification.
+ *
+ * In Pocket Word's case, the match to the VGA 16 palette is not exact as it
+ * swaps Grey and Silver, with Silver being the darker colour (i.e. having the
+ * lower RGB value).
+ */
+
+public class ColourConverter {
+
+ /** Colour table index for Black */
+ private static final short BLACK = 0;
+
+ /** Colour table index for Silver */
+ private static final short SILVER = 1;
+
+ /** Colour table index for Grey */
+ private static final short GREY = 2;
+
+ /** Colour table index for White */
+ private static final short WHITE = 3;
+
+ /** Colour table index for Red */
+ private static final short RED = 4;
+
+ /** Colour table index for Lime */
+ private static final short LIME = 5;
+
+ /** Colour table index for Blue */
+ private static final short BLUE = 6;
+
+ /** Colour table index for Aqua */
+ private static final short AQUA = 7;
+
+ /** Colour table index for Fuchsia */
+ private static final short FUCHSIA = 8;
+
+ /** Colour table index for Yellow */
+ private static final short YELLOW = 9;
+
+ /** Colour table index for Maroon */
+ private static final short MAROON = 10;
+
+ /** Colour table index for Green */
+ private static final short GREEN = 11;
+
+ /** Colour table index for Navy */
+ private static final short NAVY = 12;
+
+ /** Colour table index for Teal */
+ private static final short TEAL = 13;
+
+ /** Colour table index for Purple */
+ private static final short PURPLE = 14;
+
+ /** Colour table index for Olive */
+ public static final short OLIVE = 15;
+
+ private short tableLookup[] = null;
+
+ /**
+ * Default constructor used in the case where a lookup table is not
+ * required
+ */
+ public ColourConverter() {
+
+ }
+
+ /**
+ * Constructor that passes in the colour lookup table. This is required in
+ * cases where the 16 colour values are something other than there default
+ * values (e.g. in the case of pocket Excel)
+ *
+ * @param short[] a 16 bit array mapping the 16 colours to there values
+ */
+ public ColourConverter(short lookup[]) {
+
+ tableLookup = lookup;
+ }
+
+ /**
+ * Uses the colour table it it exists to translate default values to
+ * values in the colorTable
+ */
+ private short colourLookup(short colour) {
+
+ if(tableLookup!=null) {
+ return tableLookup[colour];
+ } else {
+ return colour;
+ }
+ }
+
+ /**
+ * Uses the colour table it it exists to translate default values to
+ * values in the colorTable
+ */
+ private short indexLookup(short index) {
+
+ short result = 0;
+
+ if(tableLookup!=null) {
+ for(short i = 0;i < tableLookup.length;i++) {
+ if(tableLookup[i]==index)
+ result = i;
+ }
+ } else {
+ result = index;
+ }
+
+ return result;
+ }
+ /**
+ * This method maps a Pocket Word colour index value to an RGB value as
+ * used by OpenOffice.
+ *
+ * @param index The index into Pocket Word's colour table.
+ *
+ * @return A Color object representing the RGB value of the Pocket Word
+ * colour.
+ */
+ public Color convertToRGB (short colour) {
+
+ short index = indexLookup(colour);
+
+ int r = 0;
+ int g = 0;
+ int b = 0;
+
+ switch (index) {
+ case SILVER:
+ r = g = b = 128;
+ break;
+
+ case GREY:
+ r = g = b = 192;
+ break;
+
+ case WHITE:
+ r = g = b = 255;
+ break;
+
+ case RED:
+ r = 255;
+ break;
+
+ case LIME:
+ g = 255;
+ break;
+
+ case BLUE:
+ b = 255;
+ break;
+
+ case AQUA:
+ g = b = 255;
+ break;
+
+ case FUCHSIA:
+ r = b = 255;
+ break;
+
+ case YELLOW:
+ r = g = 255;
+ break;
+
+ case MAROON:
+ r = 128;
+ break;
+
+ case GREEN:
+ g = 128;
+ break;
+
+ case NAVY:
+ b = 128;
+ break;
+
+ case TEAL:
+ b = g = 128;
+ break;
+
+ case PURPLE:
+ r = b = 128;
+ break;
+
+ case OLIVE:
+ r = g = 128;
+ break;
+
+ case BLACK:
+ default:
+ r = g = b = 0;
+ break;
+ }
+
+ return new Color(r, g, b);
+ }
+
+
+ /**
+ * This method approximates an RGB value (as used by Writer) to one of the
+ * 16 available colours
+ *
+ * Most of the supported colours have their components set to either 0, 128
+ * or 255. The exception is 'Grey' which is 0xC0C0C0.
+ *
+ * @param colour Color object representing the RGB value of the colour.
+ *
+ * @return Index into the Pocket Word colour table which represents the
+ * closest match to the specified colour.
+ */
+ public short convertFromRGB (Color colour) {
+ int matchedRGB = 0;
+ short indexColour = 0;
+ int reducedMap[] = new int[] { 0, 0, 128 };
+
+ int red = colour.getRed();
+ int green = colour.getGreen();
+ int blue = colour.getBlue();
+
+ // We need to convert the pale colors to their base color rather than
+ // white so we modify the rgb values if the colour is sufficently
+ // white
+ if(red>0xC0 && green>0xC0 && blue>0xC0) {
+
+ if(red!=0xFF)
+ red = getClosest(red, reducedMap);
+ if(green!=0xFF)
+ green = getClosest(green, reducedMap);
+ if(blue!=0xFF)
+ blue = getClosest(blue, reducedMap);
+ }
+
+ /*
+ * Need to derive an RGB value that has been rounded to match the ones
+ * Pocket Word knows about.
+ */
+ matchedRGB += getClosest(red) << 16;
+ matchedRGB += getClosest(green) << 8;
+ matchedRGB += getClosest(blue);
+
+ /*
+ * The colour map used by Pocket Word doesn't have any combinations of
+ * values beyond 0 and any other value. A value of 255 in any RGB
+ * code indicates a dominant colour. Other colours are only modifiers
+ * to the principal colour(s). Thus, for this conversion, modifiers
+ * can be dropped.
+ */
+ if ((matchedRGB & 0xFF0000) == 0xFF0000 || (matchedRGB & 0xFF00) == 0xFF00
+ || (matchedRGB & 0xFF) == 0xFF) {
+ if ((matchedRGB & 0xFF0000) == 0x800000) {
+ matchedRGB ^= 0x800000;
+ }
+ if ((matchedRGB & 0xFF00) == 0x8000) {
+ matchedRGB ^= 0x8000;
+ }
+ if ((matchedRGB & 0xFF) == 0x80) {
+ matchedRGB ^= 0x80;
+ }
+ }
+
+
+ /*
+ * And now for the actual matching ...
+ *
+ * Colours are based on the Windows VGA 16 palette. One difference
+ * though is that Pocket Word seems to switch the RGB codes for Grey
+ * and Silver. In Pocket Word Silver is the darker colour leaving Grey
+ * is closest to White.
+ *
+ * Shades of grey will be converted to either Silver or White, where
+ * Grey may be a more appropraite colour. This is handled specially
+ * only for Silver and White matches.
+ */
+ switch (matchedRGB) {
+ case 0x000000:
+ indexColour = BLACK;
+ break;
+
+ case 0x808080:
+ if (!isGrey(colour)) {
+ indexColour = SILVER;
+ }
+ else {
+ indexColour = GREY;
+ }
+ break;
+
+ case 0xFFFFFF:
+ if (!isGrey(colour)) {
+ indexColour = WHITE;
+ }
+ else {
+ indexColour = GREY;
+ }
+ break;
+
+ case 0xFF0000:
+ indexColour = RED;
+ break;
+
+ case 0x00FF00:
+ indexColour = LIME;
+ break;
+
+ case 0x0000FF:
+ indexColour = BLUE;
+ break;
+
+ case 0x00FFFF:
+ indexColour = AQUA;
+ break;
+
+ case 0xFF00FF:
+ indexColour = FUCHSIA;
+ break;
+
+ case 0xFFFF00:
+ indexColour = YELLOW;
+ break;
+
+ case 0x800000:
+ indexColour = MAROON;
+ break;
+
+ case 0x008000:
+ indexColour = GREEN;
+ break;
+
+ case 0x000080:
+ indexColour = NAVY;
+ break;
+
+ case 0x008080:
+ indexColour = TEAL;
+ break;
+
+ case 0x800080:
+ indexColour = PURPLE;
+ break;
+
+ case 0x808000:
+ indexColour = OLIVE;
+ break;
+
+ default: // Just in case!
+ indexColour = BLACK;
+ break;
+ }
+
+ return colourLookup(indexColour);
+ }
+
+
+ /*
+ * Default implementation, checks for the closest of value to 0, 128 or 255.
+ */
+ private int getClosest(int value) {
+ int points[] = new int[] { 0, 128, 255 };
+
+ return getClosest(value, points);
+ }
+
+
+ /*
+ * Utility method that returns the closest of the three points to the value
+ * supplied.
+ */
+ private int getClosest(int value, int[] points) {
+
+ if (value == points[0] || value == points[1] || value == points[2]) {
+ return value;
+ }
+
+ if (value < points[1]) {
+ int x = value - points[0];
+ return (Math.round((float)x / (points[1] - points[0])) == 1 ? points[1] : points[0]);
+ }
+ else {
+ int x = value - points[1];
+ return (Math.round((float)x / (points[2] - points[1])) >= 1 ? points[2] : points[1]);
+ }
+ }
+
+
+ /*
+ * Checks to see if the supplied colour can be considered to be grey.
+ */
+ private boolean isGrey(Color c) {
+ int matchedRGB = 0;
+ int points[] = new int[] { 128, 192, 255 };
+
+ matchedRGB += getClosest(c.getRed(), points) << 16;
+ matchedRGB += getClosest(c.getGreen(), points) << 8;
+ matchedRGB += getClosest(c.getBlue(), points);
+
+ if (matchedRGB == 0xC0C0C0) {
+ return true;
+ }
+
+ return false;
+ }
+}
+
diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/util/Debug.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/util/Debug.java
new file mode 100644
index 000000000000..8b9f06eb4e86
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/util/Debug.java
@@ -0,0 +1,339 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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.util;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.FileWriter;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.text.DateFormat;
+import java.util.Date;
+import java.util.Calendar;
+import java.util.Properties;
+
+/**
+ * This class is used for logging debug messages.
+ * Currently, there are three types of logging: {@link #INFO},
+ * {@link #TRACE} & {@link #ERROR}. Use the Debug.properties
+ * file to set or unset each type. Also use Debug.properties
+ * to set the writer to either <code>System.out</code>,
+ * <code>System.err</code>, or to a file.
+ *
+ * @author Herbie Ong
+ */
+public final class Debug {
+
+ /** Informational messages. */
+ public final static int INFO = 0x0001;
+ /** Error messages. */
+ public final static int ERROR = 0x0002;
+ /** Trace messages. */
+ public final static int TRACE = 0x0004;
+
+ /** To set a flag. */
+ public final static boolean SET = true;
+ /** To unset a flag. */
+ public final static boolean UNSET = false;
+
+ private static int flags = 0;
+ private static PrintWriter writer = null;
+
+ static {
+
+ try {
+
+ Class c = new Debug().getClass();
+ InputStream is = c.getResourceAsStream("Debug.properties");
+ Properties props = new Properties();
+ props.load(is);
+
+ String info = props.getProperty("debug.info", "false");
+ info = info.toLowerCase();
+
+ if (info.equals("true")) {
+ setFlags(Debug.INFO, Debug.SET);
+ }
+
+ String trace = props.getProperty("debug.trace", "false");
+ trace = trace.toLowerCase();
+
+ if (trace.equals("true")) {
+ setFlags(Debug.TRACE, Debug.SET);
+ }
+
+ String error = props.getProperty("debug.error", "false");
+ error = error.toLowerCase();
+
+ if (error.equals("true")) {
+ setFlags(Debug.ERROR, Debug.SET);
+ }
+
+ String w = props.getProperty("debug.output", "System.out");
+ setOutput(w);
+
+ } catch (Throwable ex) {
+
+ ex.printStackTrace(System.err);
+ }
+ }
+
+
+ /**
+ * Private constructor so as not to allow any instances
+ * of this class. This serves as a singleton class.
+ */
+ private Debug() {
+ }
+
+
+ /**
+ * Set the output to the specified argument.
+ * This method is only used internally to prevent
+ * invalid string parameters.
+ *
+ * @param str Output specifier.
+ */
+ private static void setOutput(String str) {
+
+ if (writer == null) {
+
+ if (str.equals("System.out")) {
+
+ setOutput(System.out);
+
+ } else if (str.equals("System.err")) {
+
+ setOutput(System.err);
+
+ } else {
+
+ try {
+
+ setOutput(new FileWriter(str));
+
+ } catch (IOException e) {
+
+ e.printStackTrace(System.err);
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Set the output to an <code>OutputStream</code> object.
+ *
+ * @param stream OutputStream object.
+ */
+ private static void setOutput(OutputStream stream) {
+
+ setOutput(new OutputStreamWriter(stream));
+ }
+
+
+ /**
+ * Set the <code>Writer</code> object to manage the output.
+ *
+ * @param w <code>Writer</code> object to write out.
+ */
+ private static void setOutput(Writer w) {
+
+ if (writer != null) {
+
+ writer.close();
+ }
+
+ writer = new PrintWriter(new BufferedWriter(w), true);
+ }
+
+
+ /**
+ * <p>
+ * This method sets the levels for debugging logs.
+ * Example calls:
+ * </p>
+ *
+ * <blockquote><pre><code>
+ * Debug.setFlags( Debug.INFO, Debug.SET )
+ * Debug.setFlags( Debug.TRACE, Debug.SET )
+ * Debug.setFlags( Debug.INFO | Debug.TRACE, Debug.SET )
+ * Debug.setFlags( Debug.ERROR, Debug.UNSET )
+ * </code></pre></blockquote>
+ *
+ * @param f Debug flag
+ * @param set Use Debug.SET to set, and Debug.UNSET to unset
+ * the given flag.
+ */
+ private static void setFlags(int f, boolean set) {
+
+ if (set) {
+ flags |= f;
+ } else {
+ flags &= ~f;
+ }
+ }
+
+
+ /**
+ * Prints out information regarding platform.
+ */
+ public static void logSystemInfo() {
+
+ if (writer != null) {
+
+ writer.println();
+ writer.println("Platform Information:");
+ writer.println("OS : " + System.getProperty("os.name"));
+ writer.println("Version : " + System.getProperty("os.version"));
+ writer.println("Platform : " + System.getProperty("os.arch"));
+ writer.println("JDK Version : " + System.getProperty("java.version"));
+ writer.println("JDK Vendor : " + System.getProperty("java.vendor"));
+ writer.println();
+ }
+ }
+
+
+ /**
+ * Prints out timestamp.
+ */
+ public static void logTime() {
+
+ if (writer != null) {
+
+ Date time = Calendar.getInstance().getTime();
+ DateFormat dt = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL);
+ writer.println(dt.format(time));
+ }
+ }
+
+
+ /**
+ * Checks if flag is set.
+ *
+ * @return true if info logging is on, otherwise false
+ */
+ public static boolean isFlagSet(int f) {
+
+ return ((flags & f) != 0);
+ }
+
+
+ /**
+ * <p>Log message based on the flag type.</p>
+ *
+ * <p>Example 1:</p>
+ *
+ * <blockquote><pre><code>
+ * Debug.log(Debug.INFO, "info string here");
+ * </code></pre></blockquote>
+ *
+ * <p>This logs the message during runtime if
+ * <code>debug.info</code> in the properties file is
+ * set to true.</p>
+ *
+ * <p>Example 2:</p>
+ *
+ * <blockquote><pre><code>
+ * Debug.log(Debug.INFO | Debug.TRACE, "info string here");
+ * </code></pre></blockquote>
+ *
+ * <p>This logs the message during runtime if debug.info or debug.trace
+ * in the properties file is set to true.</p>
+ *
+ * @param int Log type, one of the Debug constants
+ * {@link #INFO}, {@link #TRACE}, {@link #ERROR}
+ * or a combination of which or'ed together.
+ * @param msg The message.
+ */
+ public static void log(int flag, String msg) {
+
+ if (isFlagSet(flag)) {
+
+ if (writer != null) {
+
+ writer.println(msg);
+ }
+ }
+ }
+
+
+ /**
+ * Log message based on flag type plus print out stack trace
+ * of the exception passed in. Refer to the other log method
+ * for description.
+ *
+ * @param int Log type, one of the Debug constants
+ * {@link #INFO}, {@link #TRACE}, {@link #ERROR}
+ * or a combination of which or'ed together.
+ * @param msg The message.
+ * @param e Throwable object.
+ */
+ public static void log(int flag, String msg, Throwable e) {
+
+ if (isFlagSet(flag)) {
+
+ if (writer != null) {
+
+ writer.println(msg);
+ if (e != null)
+ e.printStackTrace(writer);
+ }
+ }
+ }
+
+
+ /**
+ * Converts the given bytes to a <code>String</code> of
+ * Hex digits.
+ *
+ * @param bytes <code>byte</code> array.
+ *
+ * @return Hex representation in a <code>String</code>.
+ */
+ public static String byteArrayToHexString(byte bytes[]) {
+
+ StringBuffer buff = new StringBuffer();
+
+ for (int i = 0; i < bytes.length; i++) {
+
+ int ch = ((int) bytes[i] & 0xff);
+ String str = Integer.toHexString(ch);
+ if (str.length() < 2)
+ buff.append('0');
+ buff.append(str);
+ buff.append(' ');
+ }
+
+ return buff.toString();
+ }
+}
+
diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/util/Debug.properties b/xmerge/source/xmerge/java/org/openoffice/xmerge/util/Debug.properties
new file mode 100644
index 000000000000..925392b86cdd
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/util/Debug.properties
@@ -0,0 +1,39 @@
+#*************************************************************************
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# Copyright 2000, 2010 Oracle and/or its affiliates.
+#
+# OpenOffice.org - a multi-platform office productivity suite
+#
+# 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.
+#
+#*************************************************************************
+# x-no-translate
+
+
+#
+# debug logging information and settings.
+#
+
+debug.info=false
+debug.trace=false
+debug.error=false
+debug.output=System.err
+
+
diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/util/EndianConverter.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/util/EndianConverter.java
new file mode 100644
index 000000000000..c41a428dd0c2
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/util/EndianConverter.java
@@ -0,0 +1,178 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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.util;
+
+
+/**
+ * <p>Helper class providing static methods to convert data to/from
+ * Network Byte Order (Big Endian).</p>
+ *
+ * <p>With the introduction of <code>java.nio.ByteOrder</code> and
+ * <code>java.nio.ByteBuffer</code> in Java 1.4, it may no longer be
+ * necessary to use this class in the future.</p>
+ *
+ * @author Mark Murnane
+ * @version 1.1
+ */
+public class EndianConverter {
+
+ /**
+ * <p>Convert a <code>short</code> to a Little Endian representation.</p>
+ *
+ * @param value The <code>short</code> to be converted.
+ *
+ * @return Two element <code>byte</code> array containing the converted
+ * value.
+ */
+ public static byte[] writeShort (short value) {
+ byte[] leShort = new byte[2];
+
+ leShort[0] = (byte) value;
+ leShort[1] = (byte) (value >>> 8);
+
+ return leShort;
+ }
+
+
+ /**
+ * <p>Convert an integer to a Little Endian representation.</p>
+ *
+ * @param value The <code>int</code> to be converted.
+ *
+ * @return Four element <code>byte</code> array containing the converted
+ * value.
+ */
+ public static byte[] writeInt (int value) {
+ byte[] leInt = new byte[4];
+
+ leInt[0] = (byte) value;
+ leInt[1] = (byte) (value >>> 8);
+ leInt[2] = (byte) (value >>> 16);
+ leInt[3] = (byte) (value >>> 24);
+
+ return leInt;
+ }
+
+ /**
+ * <p>Converts a <code>double</code> to a Little Endian representation
+ * of a float in IEEE-754 format.
+ *
+ * <p>An array with more than eight elements can be used, but only the first
+ * eight elements will be read.</p>
+ *
+ * @param value <code>double</code> containing the value to be converted
+ *
+ * @return <code>byte</code> array containing the LE representation of a IEEE-754 float
+ */
+ public static byte[] writeDouble(double value) {
+
+ long myDouble = Double.doubleToLongBits(value);
+ byte[] leDouble = new byte[8];
+
+ leDouble[0] = (byte) (myDouble >>> 0);
+ leDouble[1] = (byte) (myDouble >>> 8);
+ leDouble[2] = (byte) (myDouble >>> 16);
+ leDouble[3] = (byte) (myDouble >>> 24);
+ leDouble[4] = (byte) (myDouble >>> 32);
+ leDouble[5] = (byte) (myDouble >>> 40);
+ leDouble[6] = (byte) (myDouble >>> 48);
+ leDouble[7] = (byte) (myDouble >>> 56);
+
+ return leDouble;
+ }
+
+ /**
+ * <p>Convert a Little Endian representation of a short to a Java
+ * <code>short</code> (Network Byte Order).</p>
+ *
+ * <p>An array with more than two elements can be used, but only the first
+ * two elements will be read.</p>
+ *
+ * @param value <code>byte</code> array containing the LE representation
+ * of the value.
+ *
+ * @return <code>short</code> containing the converted value.
+ */
+ public static short readShort (byte[] value) {
+ int high, low;
+
+ high = value[1] & 0xFF;
+ low = value[0] & 0xFF;
+
+ return (short)(high << 8 | low);
+ }
+
+
+ /**
+ * <p>Convert a Little Endian representation of an integer to a Java
+ * <code>int</code> (Network Byte Order).</p>
+ *
+ * <p>An array with more than four elements can be used, but only the first
+ * four elements will be read.</p>
+ *
+ * @param value <code>byte</code> array containing the LE representation
+ * of the value.
+ *
+ * @return <code>int</code> containing the converted value.
+ */
+ public static int readInt(byte[] value) {
+ int number = 0;
+
+ for (int i = 0; i < 4; i++) {
+ number |= (value[i] & 0xFF) << ( i * 8);
+ }
+
+ return number;
+ }
+
+ /**
+ * <p>Convert a Little Endian representation of a float in IEEE-754 Little
+ * Endian to a Java <code>double</code> (Network Byte Order).</p>
+ *
+ * <p>An array with more than eight elements can be used, but only the first
+ * eight elements will be read.</p>
+ *
+ * @param value <code>byte</code> array containing the LE representation
+ * of a IEEE-754 float.
+ *
+ * @return <code>double</code> containing the converted value.
+ */
+ public static double readDouble(byte[] value) {
+
+ long lvalue = ( ((long)(value[7]) << 56) +
+ ((long)(value[6]&0xFF) << 48) +
+ ((long)(value[5]&0xFF) << 40) +
+ ((long)(value[4]&0xFF) << 32) +
+ ((long)(value[3]&0xFF) << 24) +
+ ((long)(value[2]&0xFF) << 16) +
+ ((long)(value[1]&0xFF) << 8) +
+ (long)(value[0]&0xFF));
+
+ return Double.longBitsToDouble(lvalue);
+ }
+}
diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/util/IntArrayList.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/util/IntArrayList.java
new file mode 100644
index 000000000000..a317fa2997e1
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/util/IntArrayList.java
@@ -0,0 +1,147 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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.util;
+
+import java.util.ArrayList;
+import java.lang.Integer;
+
+/**
+ * This is a convenience class used to create an
+ * <code>ArrayList</code> of integers.
+ */
+public class IntArrayList {
+
+ /** The list to hold our integers. */
+ private ArrayList list;
+
+ /**
+ * Constructor.
+ * Creates the list with 0 length.
+ */
+ public IntArrayList() {
+ list = new ArrayList();
+ }
+
+
+ /**
+ * Constructor.
+ *
+ * @param initialCapacity Initial capacity of the list.
+ */
+ public IntArrayList(int initialCapacity) {
+ list = new ArrayList(initialCapacity);
+ }
+
+
+ /**
+ * This method ensures that the list is large enough for
+ * <code>minCapacity</code> elements.
+ *
+ * @param minCapacity The minimum capacity of the list.
+ */
+ public void ensureCapacity(int minCapacity) {
+
+ list.ensureCapacity(minCapacity);
+ }
+
+
+ /**
+ * This method ensures that the list is large enough for
+ * <code>minCapacity</code> elements. It also fills in the
+ * new slots in the list with the integer value input as
+ * <code>fillValue</code>.
+ *
+ * @param minCapacity The minimum capacity of the list.
+ * @param fillValue This method adds in a integer for each
+ * slot that was added to ensure that the
+ * list meets the minimum capacity.
+ * <code>fillValue</code> is the value
+ * used as the initial value of these
+ * added elements.
+ */
+ public void ensureCapacityAndFill(int minCapacity, int fillValue) {
+
+ list.ensureCapacity(minCapacity);
+
+ int needToAdd = minCapacity - list.size();
+ if (needToAdd > 0) {
+ for (int i = 0; i < needToAdd; i++) {
+ list.add(new Integer(fillValue));
+ }
+ }
+ }
+
+
+ /**
+ * This method sets an element of the list to the input
+ * integer value.
+ *
+ * @param index The index in the list of the element
+ * we wish to set.
+ * @param value The integer value that we assign to the
+ * selected element of the list.
+ */
+ public void set(int index, int value) {
+ list.set(index, new Integer(value));
+ }
+
+
+ /**
+ * This method appends an element to the list.
+ *
+ * @param value The integer value that we assign to the
+ * element that we are appending to the list.
+ */
+ public void add(int value) {
+ list.add(new Integer(value));
+ }
+
+
+ /**
+ * This method gets the integer value stored in element index.
+ *
+ * @param index The index in the list of the element
+ * we wish to get the value from.
+ *
+ * @return The value of the data stored in element index.
+ */
+ public int get(int index) {
+ return ((Integer)list.get(index)).intValue();
+ }
+
+
+ /**
+ * This method gets the size of the list.
+ *
+ * @return The size of the list.
+ */
+ public int size() {
+ return list.size();
+ }
+}
+
diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/util/OfficeUtil.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/util/OfficeUtil.java
new file mode 100644
index 000000000000..64a37037c406
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/util/OfficeUtil.java
@@ -0,0 +1,135 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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.util;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.Text;
+
+import org.openoffice.xmerge.converter.xml.OfficeConstants;
+
+import java.util.Vector;
+
+
+/**
+ * Class providing utility methods for OpenOffice plugins.
+ *
+ * @author Mark Murnane
+ * @version 1.1
+ */
+public class OfficeUtil implements OfficeConstants {
+
+ /**
+ * <p>Method to replace whitespace character within text with appropriate
+ * OpenOffice tags.</p>
+ *
+ * @param text The text to parse for whitespace.
+ *
+ * @return <code>Node</code> array containing OpenOffice XML nodes
+ * representing the text.
+ */
+ public static Node[] parseText(String text, Document parentDoc) {
+ Vector nodeVec = new Vector();
+
+ /*
+ * Break up the text from the text run into Open
+ * Office text runs. There may be more runs in OO because
+ * runs of 2 or more spaces map to nodes.
+ */
+ while ((text.indexOf(" ") != -1) || (text.indexOf("\t") != 1)) {
+
+ /*
+ * Find the indices of tabs and multiple spaces, and
+ * figure out which of them occurs first in the string.
+ */
+ int spaceIndex = text.indexOf(" ");
+ int tabIndex = text.indexOf("\t");
+ if ((spaceIndex == -1) && (tabIndex == -1))
+ break; // DJP This should not be necessary. What is wrong
+ // with the while() stmt up above?
+ int closerIndex; // Index of the first of these
+ if (spaceIndex == -1)
+ closerIndex = tabIndex;
+ else if (tabIndex == -1)
+ closerIndex = spaceIndex;
+ else
+ closerIndex = (spaceIndex > tabIndex) ? tabIndex : spaceIndex;
+
+ /*
+ * If there is any text prior to the first occurrence of a
+ * tab or spaces, create a text node from it, then chop it
+ * off the string we're working with.
+ */
+ if (closerIndex > 0) {
+ String beginningText = text.substring(0, closerIndex);
+ Text textNode = parentDoc.createTextNode(beginningText);
+ nodeVec.addElement(textNode);
+ }
+ text = text.substring(closerIndex);
+
+ /*
+ * Handle either tab character or space sequence by creating
+ * an element for it, and then chopping out the text that
+ * represented it in "text".
+ */
+ if (closerIndex == tabIndex) {
+ Element tabNode = parentDoc.createElement(TAG_TAB_STOP);
+ nodeVec.add(tabNode);
+ text = text.substring(1); // tab is always a single character
+ } else {
+ // Compute length of space sequence.
+ int nrSpaces = 2;
+ while ((nrSpaces < text.length())
+ && text.substring(nrSpaces, nrSpaces + 1).equals(" "))
+ nrSpaces++;
+
+ Element spaceNode = parentDoc.createElement(TAG_SPACE);
+ spaceNode.setAttribute(ATTRIBUTE_SPACE_COUNT,
+ new Integer(nrSpaces).toString());
+ nodeVec.add(spaceNode);
+ text = text.substring(nrSpaces);
+ }
+ }
+
+ /*
+ * No more tabs or space sequences. If there's any remaining
+ * text create a text node for it.
+ */
+ if (text.length() > 0) {
+ Text textNode = parentDoc.createTextNode(text);
+ nodeVec.add(textNode);
+ }
+
+ // Now create and populate an array to return the nodes in.
+ Node nodes[] = new Node[nodeVec.size()];
+ for (int i = 0; i < nodeVec.size(); i++)
+ nodes[i] = (Node)nodeVec.elementAt(i);
+ return nodes;
+ }
+} \ No newline at end of file
diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/util/Resources.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/util/Resources.java
new file mode 100644
index 000000000000..44db518b3389
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/util/Resources.java
@@ -0,0 +1,99 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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.util;
+
+import java.util.ResourceBundle;
+
+/**
+ * <p>Provides a singleton resource class for converter messages.</p>
+ *
+ * <p>By default, this class will search for a
+ * <code>ResourceBundle</code> class file or properties file based
+ * on the default locale.</p>
+ *
+ * <p>A properties file resources.properties will be provided.</p>
+ *
+ * <p>Note that if the resource bundle object is not loaded, the
+ * construction of the singleton object will throw a
+ * <code>MissingResourceException</code>, which is a
+ * <code>RuntimeException</code>, thus I opted to not explicitly
+ * declare it. If it does throw <code>MissingResourceException</code>,
+ * it may be due to a packaging problem.</p>
+ *
+ * @author Herbie Ong
+ */
+public final class Resources
+{
+ private ResourceBundle rb = null;
+
+ private static Resources instance = null;
+
+
+ /**
+ * This method returns the singleton instance
+ * of this class.
+ *
+ * @return The singleton <code>Resources</code>
+ * instance.
+ */
+ public synchronized static Resources getInstance()
+ {
+ if (instance == null)
+ {
+ instance = new Resources();
+ }
+
+ return instance;
+ }
+
+
+ /**
+ * Default constructor is only accessible within this class.
+ * Load the resource bundle that contains the resource
+ * <code>String</code> values.
+ */
+ private Resources()
+ {
+ rb = ResourceBundle.getBundle("org.openoffice.xmerge.util.resources");
+ }
+
+
+ /**
+ * This method returns the corresponding <code>String</code> given
+ * the key.
+ *
+ * @param key Key string for getting the message
+ * <code>String</code>.
+ * @return Message <code>String</code> corresponding to the key.
+ */
+ public String getString(String key)
+ {
+ return rb.getString(key);
+ }
+}
+
diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/util/TwipsConverter.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/util/TwipsConverter.java
new file mode 100644
index 000000000000..fe3931a5aadf
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/util/TwipsConverter.java
@@ -0,0 +1,99 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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.util;
+
+
+/**
+ * <p>Helper class providing static methods to convert data to/from
+ * twips</p>
+ *
+ * @author Martin Maher
+ */
+public class TwipsConverter {
+
+ /**
+ * <p>Convert from twips to cm's</p>
+ *
+ * @param value The <code>short</code> to be converted.
+ *
+ * @return float containing the converted
+ */
+ public static float twips2cm(int value) {
+
+ float inches = (float) value/1440;
+ float cm = (float) inches*(float)2.54;
+
+ return cm;
+ }
+
+
+
+ /**
+ * <p>Convert from cm's to twips</p>
+ *
+ * @param value <code>byte</code> array containing the LE representation
+ * of the value.
+ *
+ * @return int containing the converted value.
+ */
+ public static int cm2twips(float value) {
+
+ int twips = (int) ((value/2.54)*1440);
+
+ return twips;
+ }
+
+ /**
+ * <p>Convert from twips to cm's</p>
+ *
+ * @param value The <code>short</code> to be converted.
+ *
+ * @return float containing the converted
+ */
+ public static float twips2inches(int value) {
+
+ return (float) value/1440;
+ }
+
+
+
+ /**
+ * <p>Convert from cm's to twips</p>
+ *
+ * @param value <code>byte</code> array containing the LE representation
+ * of the value.
+ *
+ * @return int containing the converted value.
+ */
+ public static int inches2twips(float value) {
+
+ return (int) (value*1440);
+ }
+
+
+}
diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/util/XmlUtil.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/util/XmlUtil.java
new file mode 100644
index 000000000000..9b3ec402b2e8
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/util/XmlUtil.java
@@ -0,0 +1,189 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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.util;
+
+import org.w3c.dom.Node;
+import org.w3c.dom.Document;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+
+/**
+ * Class containing static util methods for handling XML trees.
+ *
+ * @author smak
+ */
+public final class XmlUtil {
+
+
+ /**
+ * Perform a deep clone of certain <code>Node</code> which
+ * will base on the document <code>Node</code> of the old
+ * <code>Node</code>.
+ *
+ * @param oldNode The <code>Document</code> of this
+ * <code>Node</code> is used to clone
+ * the <code>Node</code>
+ * @param newNode The <code>Node</code> to clone.
+ *
+ * @return The cloned <code>Node</code>.
+ */
+ public static Node deepClone(Node oldNode, Node newNode) {
+ Document docNode = oldNode.getOwnerDocument();
+
+ // clone the starting node
+ Node clonedNode = cloneNode(docNode, newNode);
+
+ // then clone the sub-tree recursively
+ cloneTree(docNode, clonedNode, newNode);
+
+ return clonedNode;
+ }
+
+
+ /**
+ * Clone the sub-tree under certain given <code>Node</code>
+ *
+ * @param docNode The <code>Document</code> used to clone
+ * the <code>Node</code>.
+ * @param oldNode The <code>Node</code> to clone.
+ * @param newNode The destination <code>Node</code>.
+ */
+ private static void cloneTree(Document docNode, Node oldNode, Node newNode) {
+
+ NodeList nodeList = newNode.getChildNodes();
+ int nodeListLen = nodeList.getLength();
+
+ for (int i = 0; i < nodeListLen; i++) {
+ Node newClonedChild = cloneNode(docNode, nodeList.item(i));
+ if (newClonedChild != null) {
+ oldNode.appendChild(newClonedChild);
+ cloneTree(docNode, newClonedChild, nodeList.item(i));
+ }
+ }
+ }
+
+
+ /**
+ * Clone a <code>Node</code> (either text or element).
+ *
+ * @param docNode The <code>Document</code> used to
+ * clone the <code>Node</code>.
+ * @param newNode The <code>Node</code> to clone.
+ *
+ * @return The cloned <code>Node</code>.
+ */
+ private static Node cloneNode(Document docNode, Node newNode) {
+
+ Node clonedNode = null;
+
+ // only support text node and element node (will copy the attributes)
+ switch (newNode.getNodeType()) {
+ case Node.TEXT_NODE:
+ String textStr = newNode.getNodeValue();
+ clonedNode = docNode.createTextNode(textStr);
+ break;
+ case Node.ELEMENT_NODE:
+ Element oldElem = (Element)newNode;
+ String tagName = newNode.getNodeName();
+ Element newElem = (docNode.createElement(tagName));
+
+ // copy the attributes
+ NamedNodeMap attrs = oldElem.getAttributes();
+
+ for (int i = 0; i < attrs.getLength(); i++) {
+ newElem.setAttribute(attrs.item(i).getNodeName(),
+ attrs.item(i).getNodeValue());
+ }
+ clonedNode = newElem;
+ break;
+ }
+ return clonedNode;
+ }
+
+
+ /**
+ * Returns the name and type of an XML DOM <code>Node</code>.
+ *
+ * @param node <code>Node</code> to query.
+ *
+ * @return Name and type of XML DOM <code>Node</code>.
+ */
+ public static String getNodeInfo(Node node) {
+
+ String str = null;
+ switch (node.getNodeType()) {
+
+ case Node.ELEMENT_NODE:
+ str = "ELEMENT";
+ break;
+ case Node.ATTRIBUTE_NODE:
+ str = "ATTRIBUTE";
+ break;
+ case Node.TEXT_NODE:
+ str = "TEXT";
+ break;
+ case Node.CDATA_SECTION_NODE:
+ str = "CDATA_SECTION";
+ break;
+ case Node.ENTITY_REFERENCE_NODE:
+ str = "ENTITY_REFERENCE";
+ break;
+ case Node.ENTITY_NODE:
+ str = "ENTITY";
+ break;
+ case Node.PROCESSING_INSTRUCTION_NODE:
+ str = "PROCESSING_INSTRUCTION";
+ break;
+ case Node.COMMENT_NODE:
+ str = "COMMENT";
+ break;
+ case Node.DOCUMENT_NODE:
+ str = "DOCUMENT";
+ break;
+ case Node.DOCUMENT_TYPE_NODE:
+ str = "DOCUMENT_TYPE";
+ break;
+ case Node.DOCUMENT_FRAGMENT_NODE:
+ str = "DOCUMENT_FRAGMENT";
+ break;
+ case Node.NOTATION_NODE:
+ str = "NOTATION";
+ break;
+ }
+
+ StringBuffer buffer = new StringBuffer("name=\"");
+ buffer.append(node.getNodeName());
+ buffer.append("\" type=\"");
+ buffer.append(str);
+ buffer.append("\"");
+
+ return buffer.toString();
+ }
+}
+
diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/util/package.html b/xmerge/source/xmerge/java/org/openoffice/xmerge/util/package.html
new file mode 100644
index 000000000000..1caffdbe41d4
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/util/package.html
@@ -0,0 +1,39 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<!--
+ #*************************************************************************
+ #
+ DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+
+ Copyright 2000, 2010 Oracle and/or its affiliates.
+
+ OpenOffice.org - a multi-platform office productivity suite
+
+ 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>Provides general purpose utilities.</p>
+
+</body>
+</html>
diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/util/registry/ConverterInfo.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/util/registry/ConverterInfo.java
new file mode 100644
index 000000000000..359bcbfeb222
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/util/registry/ConverterInfo.java
@@ -0,0 +1,436 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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.util.registry;
+
+import java.util.Vector;
+import java.util.Enumeration;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.lang.reflect.Constructor;
+import org.openoffice.xmerge.PluginFactory;
+import org.openoffice.xmerge.DocumentSerializerFactory;
+import org.openoffice.xmerge.DocumentDeserializerFactory;
+import org.openoffice.xmerge.DocumentMergerFactory;
+
+/**
+ * Class for storing the information about a converter plugin.
+ *
+ * @author Brian Cameron
+ */
+public class ConverterInfo {
+
+ /**
+ * Keep track of the valid Office mime types
+ */
+ private static String[] validOfficeTypes;
+ public static String SxwType = "staroffice/sxw";
+ public static String SxcType = "staroffice/sxc";
+
+
+ static {
+ // This needs to be updated to reflect all valid office types.
+ //
+ validOfficeTypes = new String[2];
+ validOfficeTypes[0] = SxwType;
+ validOfficeTypes[1] = SxcType;
+ }
+
+ private String piJarName;
+ private String piOfficeMime;
+ private Vector piDeviceMime;
+ private String piDisplayName;
+ private String piDescription;
+ private String piVersion;
+ private String piVendor;
+ private String piClassImpl;
+ private String piXsltSerial;
+ private String piXsltDeserial;
+ private boolean piCanSerialize = false;
+ private boolean piCanDeserialize = false;
+ private boolean piCanMerge = false;
+ private ClassLoader piClassLoader = null;
+ private PluginFactory piPluginFactory;
+
+
+ /**
+ * The constructor builds a ConverterInfo structure.
+ *
+ * @param jarName The URL of the jarfile.
+ * @param officeMime The office mime-type.
+ * @param deviceMime The device mime-type.
+ * @param displayName The display name.
+ * @param description The description.
+ * @param version The version.
+ * @param vendor The vendor name.
+ * @param impl The implementation class name of
+ * PluginFactory.
+ * @param xsltSerial The url of the serializer xsl stylesheet
+ * @param xsltDeserial The url of the deserializer xsl stylesheet
+ *
+ * @throws RegistryException If <code>ci</code> cannot
+ * be loaded.
+ */
+ public ConverterInfo(String jarName, String officeMime,
+ Vector deviceMime, String displayName, String description,
+ String version, String vendor, String impl,String xsltSerial,
+ String xsltDeserial)
+ throws RegistryException {
+
+ if (isValidOfficeType(officeMime.trim()) == false) {
+ RegistryException re = new RegistryException(
+ "Invalid office type");
+ throw re;
+ }
+
+ piJarName = jarName.trim();
+ piOfficeMime = officeMime.trim();
+ piDeviceMime = deviceMime;
+ piDisplayName = displayName.trim();
+ piDescription = description.trim();
+ piVersion = version.trim();
+ piVendor = vendor.trim();
+ piXsltSerial = xsltSerial.trim();
+ piXsltDeserial= xsltDeserial.trim();
+ piClassImpl = impl.trim();
+ piClassLoader = this.getClass().getClassLoader();
+
+ // Get instance of the PluginFactory.
+ //
+ try {
+ URL jarURL = new URL(jarName);
+ URLClassLoader loader = new URLClassLoader(new URL[] { jarURL },
+ piClassLoader);
+ Class clas = loader.loadClass(piClassImpl);
+ Class[] argumentTypes = { org.openoffice.xmerge.util.registry.ConverterInfo.class };
+ Constructor construct = clas.getConstructor(argumentTypes);
+
+ Object[] arguments = { this };
+ piPluginFactory = ( PluginFactory ) construct.newInstance(arguments);
+
+ // See which interfaces the plug-in PluginFactory supports.
+ //
+ Class[] cl = piPluginFactory.getClass().getInterfaces();
+ for (int i=0; i < cl.length; i++) {
+
+ if (cl[i].getName().equals("org.openoffice.xmerge.DocumentSerializerFactory")) {
+ piCanSerialize = true;
+ }
+ if (cl[i].getName().equals("org.openoffice.xmerge.DocumentDeserializerFactory")) {
+ piCanDeserialize = true;
+ }
+ if (cl[i].getName().equals("org.openoffice.xmerge.DocumentMergerFactory")) {
+ piCanMerge = true;
+ }
+ }
+
+ } catch (Exception e) {
+ RegistryException re = new RegistryException(
+ "Class implementation of the plug-in cannot be loaded.");
+ throw re;
+ }
+ }
+
+ /**
+ * The constructor builds a ConverterInfo structure.
+ *
+ * @param jarName The URL of the jarfile.
+ * @param officeMime The office mime-type.
+ * @param deviceMime The device mime-type.
+ * @param displayName The display name.
+ * @param description The description.
+ * @param version The version.
+ * @param vendor The vendor name.
+ * @param impl The implementation class name of
+ * PluginFactory.
+ *
+ * @throws RegistryException If <code>ci</code> cannot
+ * be loaded.
+ */
+
+
+ public ConverterInfo(String jarName, String officeMime,
+ Vector deviceMime, String displayName, String description,
+ String version, String vendor, String impl)
+ throws RegistryException {
+
+ if (isValidOfficeType(officeMime.trim()) == false) {
+ RegistryException re = new RegistryException(
+ "Invalid office type");
+ throw re;
+ }
+
+ piJarName = jarName.trim();
+ piOfficeMime = officeMime.trim();
+ piDeviceMime = deviceMime;
+ piDisplayName = displayName.trim();
+ piDescription = description.trim();
+ piVersion = version.trim();
+ piVendor = vendor.trim();
+ piClassImpl = impl.trim();
+ piClassLoader = this.getClass().getClassLoader();
+
+ // Get instance of the PluginFactory.
+ //
+ try {
+ URL jarURL = new URL(jarName);
+ URLClassLoader loader = new URLClassLoader(new URL[] { jarURL },
+ piClassLoader);
+ Class clas = loader.loadClass(piClassImpl);
+ Class[] argumentTypes = { org.openoffice.xmerge.util.registry.ConverterInfo.class };
+ Constructor construct = clas.getConstructor(argumentTypes);
+
+ Object[] arguments = { this };
+ piPluginFactory = ( PluginFactory ) construct.newInstance(arguments);
+
+ // See which interfaces the plug-in PluginFactory supports.
+ //
+ Class[] cl = piPluginFactory.getClass().getInterfaces();
+ for (int i=0; i < cl.length; i++) {
+
+ if (cl[i].getName().equals("org.openoffice.xmerge.DocumentSerializerFactory")) {
+ piCanSerialize = true;
+ }
+ if (cl[i].getName().equals("org.openoffice.xmerge.DocumentDeserializerFactory")) {
+ piCanDeserialize = true;
+ }
+ if (cl[i].getName().equals("org.openoffice.xmerge.DocumentMergerFactory")) {
+ piCanMerge = true;
+ }
+ }
+
+ } catch (Exception e) {
+ RegistryException re = new RegistryException(
+ "Class implementation of the plug-in cannot be loaded.");
+ throw re;
+ }
+ }
+
+
+
+
+ /**
+ * Create a default constructor so we can use isValidOfficeType
+ * without having to actually have a valid ConverterInfo.
+ */
+ private ConverterInfo() {
+ }
+
+
+ /**
+ * Returns an instance of the DocumentDeserializerFactory interface.
+ *
+ * @return instance of the DocumentDeserializer for this ConverterInfo.
+ */
+ public DocumentSerializerFactory getDocSerializerFactory() {
+ return (DocumentSerializerFactory)piPluginFactory;
+ }
+
+
+ /**
+ * Returns an instance of the DocumentSerializerFactory interface.
+ *
+ * @return instance of the DocumentSerializer for this ConverterInfo.
+ */
+ public DocumentDeserializerFactory getDocDeserializerFactory() {
+ return (DocumentDeserializerFactory)piPluginFactory;
+ }
+
+
+ /**
+ * Returns an instance of the DocumentMergerFactory interface.
+ *
+ * @return instance of the DocumentMergerFactory for this ConverterInfo.
+ */
+ public DocumentMergerFactory getDocMergerFactory() {
+ return (DocumentMergerFactory)piPluginFactory;
+ }
+
+
+ /**
+ * Returns the jar file name.
+ *
+ * @return The jar file name, null if none exists.
+ */
+ public String getJarName() {
+ return piJarName;
+ }
+
+
+ /**
+ * Returns the office mime-type.
+ *
+ * @return The office mime-type, null if none exists.
+ */
+ public String getOfficeMime() {
+ return piOfficeMime;
+ }
+
+
+ /**
+ * Returns an <code>Enumeration</code> of <code>String</code>
+ * objects indicating the device mime-type.
+ *
+ * @return An <code>Enumeration</code> of <code>String</code>
+ * objects indicating the device mime-type.
+ */
+ public Enumeration getDeviceMime() {
+ return(piDeviceMime.elements());
+ }
+
+
+ /**
+ * Returns the display name.
+ *
+ * @return The display name, null if none exists.
+ */
+ public String getDisplayName() {
+ return piDisplayName;
+ }
+
+
+ /**
+ * Returns the description.
+ *
+ * @return The description, null if none exists.
+ */
+ public String getDescription() {
+ return piDescription;
+ }
+
+
+ /**
+ * Returns the version.
+ *
+ * @return The version, null if none exists.
+ */
+ public String getVersion() {
+ return piVersion;
+ }
+
+
+ /**
+ * Returns the vendor name.
+ *
+ * @return The vendor name, null if none exists.
+ */
+ public String getVendor() {
+ return piVendor;
+ }
+
+
+ /**
+ * Returns the implementation class name of PluginFactory.
+ *
+ * @return The implementation class name of PluginFactory,
+ * null if none exists.
+ */
+ public String getClassImpl() {
+ return piClassImpl;
+ }
+
+
+ /**
+ * Returns the PluginFactory instance for this plug-in.
+ *
+ * @return The PluginFactory instance for this plug-in.
+ */
+ public PluginFactory getPluginFactory() {
+ return piPluginFactory;
+ }
+
+
+ /**
+ * Returns true if this plug-in has a serializier, false otherwise.
+ *
+ * @return true if this plug-in has a serializier, false otherwise.
+ */
+ public boolean canSerialize() {
+ return piCanSerialize;
+ }
+
+
+ /**
+ * Returns true if this plug-in has a deserializier, false otherwise.
+ *
+ * @return true if this plug-in has a deserializier, false otherwise.
+ */
+ public boolean canDeserialize() {
+ return piCanDeserialize;
+ }
+
+
+ /**
+ * Returns true if this plug-in has a merger, false otherwise.
+ *
+ * @return true if this plug-in has a merger, false otherwise.
+ */
+ public boolean canMerge() {
+ return piCanMerge;
+ }
+
+
+ /**
+ * Returns true if the officeMime is a valid Office mime type.
+ *
+ * @return true if the officeMime is a valid Office mime type.
+ */
+ public static boolean isValidOfficeType(String officeMime) {
+
+ boolean rc = false;
+ for (int i=0; i < validOfficeTypes.length; i++) {
+ if (officeMime.equals(validOfficeTypes[i])) {
+ rc = true;
+ }
+ }
+
+ return rc;
+ }
+
+ /**
+ * Returns a <code>String</code> containing the Xslt stylesheet url that
+ * is to be used by the Xslt Plugin Serializer.
+ *
+ * @return <code>String</code>
+ */
+
+ public String getXsltSerial() {
+ return piXsltSerial;
+ }
+
+ /**
+ * Returns a <code>String</code> containing the xslt stylesheet url that
+ * is to be used by the Xslt Plugin Deserializer.
+ *
+ * @return <code>String</code>
+ */
+
+ public String getXsltDeserial() {
+ return piXsltDeserial;
+ }
+}
+
diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/util/registry/ConverterInfoMgr.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/util/registry/ConverterInfoMgr.java
new file mode 100644
index 000000000000..0993924c129f
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/util/registry/ConverterInfoMgr.java
@@ -0,0 +1,536 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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.util.registry;
+
+import java.util.*;
+import java.io.*;
+
+/**
+ * Manages the converter plug-ins that are currently active.
+ * This class allows plug-ins to be added or removed dynamically.
+ * This class is a singleton (static) class, so that only one
+ * manager can exist at a time. It is final, so it may not be
+ * subclassed.
+ *
+ * @author: Brian Cameron
+ */
+public final class ConverterInfoMgr {
+
+ private static Vector converterInfoList;
+
+ /**
+ * Constructor
+ */
+ static {
+ converterInfoList = new Vector();
+ }
+
+
+ /**
+ * Adds a converter plug-in to the registry. The
+ * <code>ConverterInfo</code> must have a unique DisplayName
+ * and must have non-null values for DisplayName, ClassImpl,
+ * OfficeMime, and DeviceMime.
+ *
+ * @param ConverterInfo A <code>ConverterInfo</code> object
+ * describing a plug-in.
+ *
+ * @throws RegistryException If the <code>ConverterInfo</code>
+ * is not valid.
+ */
+ public static void addPlugIn(ConverterInfo ci) throws RegistryException {
+
+ ConverterInfo converterInfo;
+
+ // Validate
+ //
+ if (ci.getDisplayName() == null) {
+ RegistryException re = new RegistryException(
+ "Converter must have valid name.");
+ throw re;
+ }
+ if (ci.getClassImpl() == null) {
+ RegistryException re = new RegistryException(
+ "Converter must have valid class implementation specified.");
+ throw re;
+ }
+ if (ci.getOfficeMime() == null) {
+ RegistryException re = new RegistryException(
+ "Converter must have valid office mime specified.");
+ throw re;
+ }
+ if (! ci.getDeviceMime().hasMoreElements()) {
+ RegistryException re = new RegistryException(
+ "Converter must have valid device mime specified.");
+ throw re;
+ }
+
+ // Verify there is no converter with the same Display Name in
+ // the registry.
+ //
+ Enumeration ciEnum = converterInfoList.elements();
+ while (ciEnum.hasMoreElements()) {
+ converterInfo = (ConverterInfo)ciEnum.nextElement();
+ if (ci.getDisplayName().equals(converterInfo.getDisplayName())) {
+ RegistryException re = new RegistryException(
+ "Converter with specified display name already exists.");
+ throw re;
+ }
+ }
+
+ // Since this is a adding to a static Vector, make sure this
+ // add method call is synchronized.
+ //
+ synchronized (converterInfoList) {
+ converterInfoList.add(ci);
+ }
+ }
+
+
+ /**
+ * Adds a <code>Vector</code> of converter plug-ins to the registry.
+ * Each <code>ConverterInfo</code> in the <code>Vector</code> must have
+ * a unique DisplayName and must have non-null values for DisplayName,
+ * ClassImpl, OfficeMime, and DeviceMime.
+ *
+ * @param ciVectory A <code>Vector</code> of <code>ConverterInfo</code>
+ * objects describing one or more plug-in(s).
+ *
+ * @throws RegistryException If a <code>ConverterInfo</code> in the
+ * <code>Vector</code> is not valid.
+ */
+ public static void addPlugIn(Enumeration jarEnum) throws RegistryException {
+
+ while (jarEnum.hasMoreElements()) {
+ ConverterInfo converterInfo = (ConverterInfo)jarEnum.nextElement();
+ addPlugIn(converterInfo);
+ }
+ }
+
+
+ /**
+ * Returns an <code>Enumeration</code> of registered
+ * <code>ConverterInfo</code> objects.
+ *
+ * @return An <code>Enumeration</code> containing the currently registered
+ * <code>ConverterInfo</code> objects, an empty
+ * <code>Vector</code> if none exist.
+ */
+ public static Enumeration getConverterInfoEnumeration() {
+ return (converterInfoList.elements());
+ }
+
+
+ /**
+ * Removes any <code>ConverterInfo</code> object from the registry
+ * that have the specified jar name value.
+ *
+ * @param jar The name of the jarfile.
+ *
+ * @return True if a <code>ConverterInfo</code> object was
+ * removed, false otherwise.
+ */
+ public static boolean removeByJar(String jar) {
+
+ ConverterInfo converterInfo;
+ boolean rc = false;
+
+ // FIX (HJ): Has to use an iterator, since we are removing items
+ /*Enumeration ciEnum = converterInfoList.elements();
+ while (ciEnum.hasMoreElements())
+ {
+ converterInfo = (ConverterInfo)ciEnum.nextElement();
+ if (jar.equals(converterInfo.getJarName())) {
+ converterInfoList.remove(converterInfo);
+ rc = true;
+ }
+ }*/
+
+ Iterator ciIter = converterInfoList.iterator();
+ while (ciIter.hasNext())
+ {
+ converterInfo = (ConverterInfo)ciIter.next();
+ if (jar.equals(converterInfo.getJarName())) {
+ ciIter.remove();
+ rc = true;
+ }
+ }
+ return rc;
+ }
+
+
+ /**
+ * Removes any <code>ConverterInfo</code> object from the registry
+ * that have the specified display name value.
+ *
+ * @param name The display name.
+ *
+ * @return True if a <code>ConverterInfo</code> object was
+ * removed, false otherwise.
+ */
+ public static boolean removeByName(String name) {
+
+ ConverterInfo converterInfo;
+ boolean rc = false;
+
+ Enumeration ciEnum = converterInfoList.elements();
+ while (ciEnum.hasMoreElements())
+ {
+ converterInfo = (ConverterInfo)ciEnum.nextElement();
+ if (name.equals(converterInfo.getDisplayName())) {
+ converterInfoList.remove(converterInfo);
+ rc = true;
+ }
+ }
+ return rc;
+ }
+
+
+ /**
+ * Returns the <code>ConverterInfo</code> object that supports
+ * the specified device/office mime type conversion. If there
+ * are multiple <code>ConverterInfo</code> objects registered
+ * that support this conversion, only the first is returned.
+ *
+ * @param deviceMime The device mime.
+ * @param officeMime The office mime.
+ *
+ * @return The first plug-in that supports the specified
+ * conversion.
+ */
+ public static ConverterInfo findConverterInfo(String deviceMime, String officeMime) {
+
+ ConverterInfo converterInfo;
+
+ if (deviceMime == null ||
+ ConverterInfo.isValidOfficeType(officeMime) == false) {
+ return null;
+ }
+
+ // Loop over elements comparing with deviceFromMime
+ //
+ Enumeration ciEnum = converterInfoList.elements();
+ while (ciEnum.hasMoreElements()) {
+
+ converterInfo = (ConverterInfo)ciEnum.nextElement();
+ String toDeviceInfo = (String)converterInfo.getOfficeMime();
+ Enumeration fromEnum = converterInfo.getDeviceMime();
+
+ // Loop over the deviceMime types.
+ //
+ while (fromEnum.hasMoreElements()) {
+ String fromDeviceInfo = (String)fromEnum.nextElement();
+ if (deviceMime.trim().equals(fromDeviceInfo) &&
+ officeMime.trim().equals(toDeviceInfo)) {
+ return (converterInfo);
+ }
+ }
+ }
+ return null;
+ }
+
+
+ /**
+ * Returns an array of two <code>ConverterInfo</code> objects that
+ * can be chained to perform the specified mime type conversion.
+ * If there are multiple <code>ConverterInfo</code> objects that
+ * support this conversion, only the first is returned.
+ *
+ * @param deviceMimeFrom The device from mime.
+ * @param deviceMimeTo The device to mime.
+ *
+ * @return An array of two <code>ConverterInfo</code> objects
+ * that can be chained to perform the specified
+ * conversion.
+ */
+ public static ConverterInfo[] findConverterInfoChain(String deviceFromMime, String deviceToMime) {
+
+ if (deviceFromMime == null || deviceToMime == null) {
+ return null;
+ }
+
+ ConverterInfo[] converterInfo = new ConverterInfo[2];
+
+ // Loop over elements comparing with deviceFromMime
+ //
+ Enumeration cifEnum = converterInfoList.elements();
+ while (cifEnum.hasMoreElements()) {
+
+ converterInfo[0] = (ConverterInfo)cifEnum.nextElement();
+ String fromOfficeInfo = converterInfo[0].getOfficeMime();
+ Enumeration fromEnum = converterInfo[0].getDeviceMime();
+
+ // Loop over the deviceMime types looking for a deviceFromMime
+ // match.
+ //
+ while (fromEnum.hasMoreElements()) {
+ String fromDeviceInfo = (String)fromEnum.nextElement();
+
+ if (deviceFromMime.trim().equals(fromDeviceInfo)) {
+
+ // Found a a match for deviceFrom. Now loop over the
+ // elements comparing with deviceToMime
+ //
+ Enumeration citEnum = converterInfoList.elements();
+ while (citEnum.hasMoreElements()) {
+
+ converterInfo[1] = (ConverterInfo)citEnum.nextElement();
+ String toOfficeInfo = converterInfo[1].getOfficeMime();
+ Enumeration toEnum = converterInfo[1].getDeviceMime();
+
+ // Loop over deviceMime types looking for a
+ // deviceToMime match.
+ //
+ while (toEnum.hasMoreElements()) {
+ String toDeviceInfo = (String)toEnum.nextElement();
+ if (deviceToMime.trim().equals(toDeviceInfo) &&
+ fromOfficeInfo.equals(toOfficeInfo)) {
+
+ // Found a match
+ //
+ return (converterInfo);
+ }
+ }
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+
+ /**
+ * main to let the user specify what plug-ins to register from
+ * jarfiles and to display the currently registered plug-ins.
+ *
+ * @param args Not used.
+ */
+ public static void main(String args[]) {
+
+ ConverterInfoReader cir = null;
+ boolean validate = false;
+ InputStreamReader isr = new InputStreamReader(System.in);
+ BufferedReader br = new BufferedReader(isr);
+ char c = ' ';
+
+ boolean exitFlag = false;
+ while (exitFlag == false) {
+
+ System.out.println("\nMenu:");
+ System.out.println("(L)oad plug-ins from a jar file");
+ System.out.println("(D)isplay name unload");
+ System.out.println("(J)ar name unload");
+ System.out.println("(F)ind ConverterInfo");
+ System.out.println("(C)ind ConverterInfo chain");
+ System.out.println("(V)iew plug-ins");
+ System.out.println("(T)oggle Validation");
+ System.out.println("(Q)uit\n");
+
+ try {
+ c = br.readLine().toUpperCase().trim().charAt(0);
+ } catch(Exception e) {
+ System.out.println("Invalid entry");
+ System.out.println("Error msg: " + e.getMessage());
+ continue;
+ }
+
+ System.out.println("");
+
+ // Quit
+ //
+ if (c == 'Q') {
+ exitFlag = true;
+
+ // Load by Jarfile
+ //
+ } else if (c == 'L') {
+
+ System.out.println("Enter path to jarfile: ");
+ try {
+ String jarname = br.readLine().trim();
+ cir = new ConverterInfoReader(jarname,validate);
+ } catch (RegistryException e) {
+ System.out.println("Cannot load plug-in ConverterFactory implementation.");
+ System.out.println("Error msg: " + e.getMessage());
+ } catch (Exception e) {
+ System.out.println("Error adding data to registry");
+ System.out.println("Error msg: " + e.getMessage());
+ }
+
+ if (cir != null) {
+ Enumeration jarInfoEnum = cir.getConverterInfoEnumeration();
+ try {
+ ConverterInfoMgr.addPlugIn(jarInfoEnum);
+ } catch (Exception e) {
+ System.out.println("Error adding data to registry");
+ System.out.println("Error msg: " + e.getMessage());
+ }
+ }
+
+ // Unload by Display Name or Jarfile
+ //
+ } else if (c == 'T') {
+ if (validate== true){
+ System.out.println("Validation switched off");
+ validate=false;
+ }else{
+ System.out.println("Validation switched on");
+ validate=true;
+ }
+ } else if (c == 'D' || c == 'J') {
+
+ if (c == 'D') {
+ System.out.println("Enter display name: ");
+ } else {
+ System.out.println("Enter path to jarfile: ");
+ }
+
+ try
+ {
+ String name = br.readLine().trim();
+ boolean rc = false;
+
+ if (c == 'D') {
+ rc = ConverterInfoMgr.removeByName(name);
+ } else {
+ rc = ConverterInfoMgr.removeByJar(name);
+ }
+
+ if (rc == true) {
+ System.out.println("Remove successful.");
+ } else {
+ System.out.println("Remove failed.");
+ }
+
+ } catch (Exception e) {
+ System.out.println("Error removing value from registry");
+ System.out.println("Error msg: " + e.getMessage());
+ }
+
+ // Find Office Mime
+ //
+ } else if (c == 'F' || c == 'C') {
+
+ String findMimeOne = null;
+ String findMimeTwo = null;
+
+ if (c == 'F') {
+ System.out.println("Enter device mime: ");
+ } else {
+ System.out.println("Enter device from mime: ");
+ }
+
+ try {
+ findMimeOne = br.readLine().trim();
+ } catch (Exception e) {
+ System.out.println("Error adding data to registry");
+ System.out.println("Error msg: " + e.getMessage());
+ }
+
+ if (c == 'F') {
+ System.out.println("Enter office mime: ");
+ } else {
+ System.out.println("Enter device to mime: ");
+ }
+
+ try {
+ findMimeTwo = br.readLine().trim();
+ } catch (Exception e) {
+ System.out.println("Error adding data to registry");
+ System.out.println("Error msg: " + e.getMessage());
+ }
+
+ if (c == 'F') {
+ ConverterInfo foundInfo = ConverterInfoMgr.findConverterInfo(findMimeOne, findMimeTwo);
+ if (foundInfo != null) {
+ System.out.println(" Found ConverterInfo");
+ System.out.println(" DisplayName : " + foundInfo.getDisplayName());
+ } else {
+ System.out.println(" Did not find ConverterInfo");
+ }
+ } else {
+ ConverterInfo[] foundInfo = ConverterInfoMgr.findConverterInfoChain(findMimeOne,
+ findMimeTwo);
+ if (foundInfo[0] != null && foundInfo[1] != null ) {
+ System.out.println(" Found ConverterInfo Chain");
+ System.out.println(" DisplayName : " + foundInfo[0].getDisplayName());
+ System.out.println(" DisplayName : " + foundInfo[1].getDisplayName());
+ } else {
+ System.out.println(" Did not find ConverterInfo");
+ }
+ }
+
+ // View
+ //
+ } else if (c == 'V') {
+
+ Enumeration ciEnum = ConverterInfoMgr.getConverterInfoEnumeration();
+
+ int ciCnt = 0;
+ while (ciEnum.hasMoreElements())
+ {
+ System.out.println("");
+ System.out.println(" Displaying converter number " + ciCnt);
+ ConverterInfo converterInfo = (ConverterInfo)ciEnum.nextElement();
+ System.out.println(" DisplayName : " + converterInfo.getDisplayName());
+ System.out.println(" JarFile : " + converterInfo.getJarName());
+ System.out.println(" Description : " + converterInfo.getDescription());
+ System.out.println(" Version : " + converterInfo.getVersion());
+ System.out.println(" OfficeMime : " + converterInfo.getOfficeMime());
+ Enumeration fromEnum = converterInfo.getDeviceMime();
+ int feCnt = 1;
+ while (fromEnum.hasMoreElements())
+ {
+ System.out.println(" DeviceMime : (#" + feCnt + ") : " +
+ (String)fromEnum.nextElement());
+ feCnt++;
+ }
+ if (feCnt == 1) {
+ System.out.println(" DeviceMime : None specified");
+ }
+
+ System.out.println(" Vendor : " + converterInfo.getVendor());
+ System.out.println(" ClassImpl : " + converterInfo.getClassImpl());
+ System.out.println(" XsltSerial : " + converterInfo.getXsltSerial());
+ System.out.println(" XsltDeserial : " + converterInfo.getXsltDeserial());
+ System.out.println(" Serialize : " + converterInfo.canSerialize());
+ System.out.println(" Deserialize : " + converterInfo.canDeserialize());
+ System.out.println(" Merge : " + converterInfo.canMerge());
+ ciCnt++;
+ }
+
+ if (ciCnt == 0) {
+ System.out.println("No converters registered");
+ }
+ } else {
+ System.out.println("Invalid input");
+ }
+ }
+ }
+}
+
diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/util/registry/ConverterInfoReader.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/util/registry/ConverterInfoReader.java
new file mode 100644
index 000000000000..238fd59f9608
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/util/registry/ConverterInfoReader.java
@@ -0,0 +1,279 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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.util.registry;
+
+import java.io.*;
+import java.util.*;
+import java.util.jar.*;
+import org.xml.sax.*;
+import org.w3c.dom.*;
+import javax.xml.parsers.*;
+import java.net.URL;
+import java.net.JarURLConnection;
+
+/**
+ * The <code>ConverterInfoReader</code> pulls a META-INF/converter.xml
+ * file out of a jar file and parses it, providing access to this
+ * information in a <code>Vector</code> of <code>ConverterInfo</code>
+ * objects.
+ *
+ * @author Brian Cameron
+ */
+public class ConverterInfoReader {
+
+ private final static String TAG_CONVERTER = "converter";
+ private final static String ATTRIB_OFFICE_TYPE = "type";
+ private final static String ATTRIB_VERSION = "version";
+ private final static String TAG_NAME = "converter-display-name";
+ private final static String TAG_DESC = "converter-description";
+ private final static String TAG_VENDOR = "converter-vendor";
+ private final static String TAG_CLASS_IMPL = "converter-class-impl";
+ private final static String TAG_TARGET = "converter-target";
+ private final static String ATTRIB_DEVICE_TYPE = "type";
+ private final static String TAG_XSLT_DESERIAL = "converter-xslt-deserialize";
+ private final static String TAG_XSLT_SERIAL = "converter-xslt-serialize";
+ private String jarfilename;
+ private Document document;
+ private Vector converterInfoList;
+
+
+ /**
+ * Constructor. A jar file is passed in. The jar file is
+ * parsed and the <code>Vector</code> of <code>ConverterInfo</code>
+ * objects is built.
+ *
+ * @param jar The URL of the jar file to process.
+ * @param shouldvalidate Boolean to enable or disable xml validation.
+ *
+ * @throws IOException If the jar file cannot
+ * be read or if the
+ * META-INF/converter.xml
+ * can not be read in the
+ * jar file.
+ * @throws ParserConfigurationException If the DocumentBuilder
+ * can not be built.
+ * @throws org.xml.sax.SAXException If the converter.xml
+ * file can not be parsed.
+ * @throws RegistryException If the ConverterFactory
+ * implementation of a
+ * plug-in cannot be loaded.
+ */
+ public ConverterInfoReader(String jar,boolean shouldvalidate) throws IOException,
+ ParserConfigurationException, org.xml.sax.SAXException,
+ RegistryException {
+
+ InputStream istream;
+ InputSource isource;
+ DocumentBuilderFactory builderFactory;
+ DocumentBuilder builder;
+ JarURLConnection jarConnection;
+ JarEntry jarentry;
+ JarFile jarfile;
+ URL url;
+
+ converterInfoList = new Vector();
+ jarfilename = jar;
+
+ // Get Jar via URL
+ //
+ url = new URL("jar:" + jar + "!/META-INF/converter.xml");
+ jarConnection = (JarURLConnection)url.openConnection();
+ jarentry = jarConnection.getJarEntry();
+ jarfile = jarConnection.getJarFile();
+
+ // Build the InputSource
+ //
+ istream = jarfile.getInputStream(jarentry);
+ isource = new InputSource(istream);
+
+ // Get the DOM builder and build the document.
+ //
+ builderFactory = DocumentBuilderFactory.newInstance();
+
+ //DTD validation
+ if (shouldvalidate){
+ System.out.println("Validating xml...");
+ builderFactory.setValidating(true);
+ }
+ //
+ builder = builderFactory.newDocumentBuilder();
+ document = builder.parse(isource);
+
+ // Parse the document.
+ //
+ parseDocument();
+ }
+
+
+ /**
+ * Loops over the <i>converter</i> <code>Node</code> in the converter.xml
+ * file and processes them.
+ *
+ * @throws RegistryException If the plug-in associated with a
+ * specific <i>converter</i> <code>Node</code>
+ * cannot be loaded.
+ */
+ private void parseDocument() throws RegistryException {
+
+ Node converterNode;
+ NodeList converterNodes = document.getElementsByTagName(TAG_CONVERTER);
+
+ for (int i=0; i < converterNodes.getLength(); i++) {
+ converterNode = converterNodes.item(i);
+ if (converterNode.getNodeType() == Node.ELEMENT_NODE) {
+ parseConverterNode((Element)converterNode);
+ }
+ }
+ }
+
+
+ /**
+ * Parses a <i>converter</i> node, pulling the information out of
+ * the <code>Node</code> and placing it in a <code>ConverterInfo</code>
+ * object, and adds that object to a <code>Vector</code> of
+ * <code>ConverterInfo</code> objects.
+ *
+ * @param e The <code>Element</code> corresponding to the
+ * <i>converter</i> XML tag.
+ *
+ *
+ * @throws RegistryException If the plug-in cannot be loaded.
+ */
+ private void parseConverterNode(Element e) throws RegistryException {
+
+ Element detailElement;
+ Node detailNode;
+ String elementTagName;
+ String officeMime = null;
+ Vector deviceMime = new Vector();
+ String name = null;
+ String desc = null;
+ String version = null;
+ String vendor = null;
+ String classImpl = null;
+ String xsltSerial = null;
+ String xsltDeserial= null;
+ String temp;
+
+ temp = e.getAttribute(ATTRIB_OFFICE_TYPE);
+ if (temp.length() != 0) {
+ officeMime = temp;
+ }
+
+ temp = e.getAttribute(ATTRIB_VERSION);
+ if (temp.length() != 0) {
+ version = temp;
+ }
+
+ NodeList detailNodes = e.getChildNodes();
+ for (int i=0; i < detailNodes.getLength(); i++) {
+
+ detailNode = detailNodes.item(i);
+ if (detailNode.getNodeType() == Node.ELEMENT_NODE) {
+
+ detailElement = (Element)detailNode;
+ elementTagName = detailElement.getTagName();
+
+ if (TAG_NAME.equalsIgnoreCase(elementTagName)) {
+ name = getTextValue(detailElement);
+ } else if (TAG_DESC.equalsIgnoreCase(elementTagName)) {
+ desc = getTextValue(detailElement);
+ } else if (TAG_VENDOR.equalsIgnoreCase(elementTagName)) {
+ vendor = getTextValue(detailElement);
+ } else if (TAG_XSLT_SERIAL.equalsIgnoreCase(elementTagName)) {
+ xsltSerial = getTextValue(detailElement);
+ } else if (TAG_XSLT_DESERIAL.equalsIgnoreCase(elementTagName)) {
+ xsltDeserial = getTextValue(detailElement);
+ } else if (TAG_CLASS_IMPL.equalsIgnoreCase(elementTagName)) {
+ classImpl = getTextValue(detailElement);
+ } else if (TAG_TARGET.equalsIgnoreCase(elementTagName)) {
+
+ temp = detailElement.getAttribute(ATTRIB_DEVICE_TYPE);
+ if (temp.length() != 0) {
+ deviceMime.add(temp);
+ }
+ }
+ }
+ }
+ ConverterInfo converterInfo;
+ if ((xsltSerial==null) || (xsltDeserial==null)){
+ converterInfo = new ConverterInfo(jarfilename,
+ officeMime, deviceMime, name,
+ desc, version, vendor,classImpl);
+ }
+ else{
+ converterInfo = new ConverterInfo(jarfilename,
+ officeMime, deviceMime, name,
+ desc, version, vendor,classImpl,
+ xsltSerial,xsltDeserial);
+ }
+ /*ConverterInfo converterInfo = new ConverterInfo(jarfilename,
+ officeMime, deviceMime, name, desc, version, vendor,
+ classImpl);*/
+ converterInfoList.add(converterInfo);
+ }
+
+
+ /**
+ * Helper function to get the text value of an
+ * <code>Element</code>.
+ *
+ * @param e The <code>Element</code> to process.
+ *
+ * @return The text value of the <code>Element</code>.
+ */
+ private String getTextValue(Element e) {
+
+ NodeList tempNodes = e.getChildNodes();
+ String text = null;
+ Node tempNode;
+
+ for (int j=0; j < tempNodes.getLength(); j++) {
+ tempNode = tempNodes.item(j);
+ if (tempNode.getNodeType() == Node.TEXT_NODE) {
+ text = tempNode.getNodeValue().trim();
+ break;
+ }
+ }
+
+ return text;
+ }
+
+
+ /**
+ * Returns an <code>Enumeration</code> of <code>ConverterInfo</code>
+ * objects.
+ *
+ * @return An <code>Enumeration</code> of <code>ConverterInfo</code>
+ * objects.
+ */
+ public Enumeration getConverterInfoEnumeration() {
+ return (converterInfoList.elements());
+ }
+}
+
diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/util/registry/RegistryException.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/util/registry/RegistryException.java
new file mode 100644
index 000000000000..ea370d386c1c
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/util/registry/RegistryException.java
@@ -0,0 +1,47 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * 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.util.registry;
+
+/**
+ * This <code>Exception</code> is thrown by converter registry
+ * algorithms.
+ */
+public class RegistryException extends Exception {
+
+
+ /**
+ * Exception thrown by merge algorithms.
+ *
+ * @param message Message to be included in the
+ * <code>Exception</code>.
+ */
+ public RegistryException(String message) {
+ super(message);
+ }
+}
+
diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/util/registry/build.xml b/xmerge/source/xmerge/java/org/openoffice/xmerge/util/registry/build.xml
new file mode 100644
index 000000000000..525546d2f7fc
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/util/registry/build.xml
@@ -0,0 +1,135 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ #*************************************************************************
+ #
+ DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+
+ Copyright 2000, 2010 Oracle and/or its affiliates.
+
+ OpenOffice.org - a multi-platform office productivity suite
+
+ This file is part of OpenOffice.org.
+
+ OpenOffice.org is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License version 3
+ only, as published by the Free Software Foundation.
+
+ OpenOffice.org is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License version 3 for more details
+ (a copy is included in the LICENSE file that accompanied this code).
+
+ You should have received a copy of the GNU Lesser General Public License
+ version 3 along with OpenOffice.org. If not, see
+ <http://www.openoffice.org/license.html>
+ for a copy of the LGPLv3 License.
+
+ #*************************************************************************
+ -->
+<project name="xmrg_jooxu_registry" default="main" basedir=".">
+
+ <!-- ================================================================= -->
+ <!-- settings -->
+ <!-- ================================================================= -->
+
+ <!-- project prefix, used for targets and build.lst -->
+ <property name="prj.prefix" value="xmrg"/>
+
+ <!-- name of this sub target used in recursive builds -->
+ <property name="target" value="xmrg_jooxu_registry"/>
+
+ <!-- relative path to project directory -->
+ <property name="prj" value="../../../../../.."/>
+
+ <!-- start of java source code package structure -->
+ <property name="java.dir" value="${prj}/java"/>
+
+ <!-- path component for current java package -->
+ <property name="package" value="org/openoffice/xmerge/util/registry"/>
+
+ <!-- define how to handle CLASSPATH environment -->
+ <property name="build.sysclasspath" value="ignore"/>
+
+ <property environment="env"/>
+ <property name="env.XML_APIS_JAR" value="${solar.jar}/xml-apis.jar"/>
+ <property name="env.XERCES_JAR" value="${solar.jar}/xercesImpl.jar"/>
+
+ <!-- classpath settings for javac tasks -->
+ <path id="classpath">
+ <pathelement location="${build.class}"/>
+ <pathelement location="${env.XML_APIS_JAR}"/>
+ <pathelement location="${env.XERCES_JAR}"/>
+ </path>
+
+ <!-- set whether we want to compile with or without deprecation -->
+ <property name="deprecation" value="on"/>
+
+ <!-- ================================================================= -->
+ <!-- solar build environment targets -->
+ <!-- ================================================================= -->
+
+ <target name="build_dir" unless="build.dir">
+ <property name="build.dir" value="${out}"/>
+ </target>
+
+ <target name="solar" depends="build_dir" if="solar.update">
+ <property name="solar.properties"
+ value="${solar.bin}/solar.properties"/>
+ </target>
+
+ <target name="init" depends="solar">
+ <property name="build.compiler" value="classic"/>
+ <property file="${solar.properties}"/>
+ <property file="${build.dir}/class/solar.properties"/>
+ </target>
+
+ <target name="info">
+ <echo message="--------------------"/>
+ <echo message="${target}"/>
+ <echo message="--------------------"/>
+ </target>
+
+
+ <!-- ================================================================= -->
+ <!-- custom targets -->
+ <!-- ================================================================= -->
+
+ <!-- the main target, called in recursive builds -->
+ <target name="main" depends="info,prepare,compile"/>
+
+ <!-- prepare output directories -->
+ <target name="prepare" depends="init" if="build.class">
+ <mkdir dir="${build.dir}"/>
+ <mkdir dir="${build.class}"/>
+ </target>
+
+ <!-- compile java sources in ${package} -->
+ <target name="compile" depends="prepare" if="build.class">
+ <javac srcdir="${java.dir}"
+ destdir="${build.class}"
+ debug="${debug}"
+ deprecation="${deprecation}"
+ optimize="${optimize}">
+ <classpath refid="classpath"/>
+ <include name="${package}/ConverterInfo.java"/>
+ <include name="${package}/ConverterInfoMgr.java"/>
+ <include name="${package}/ConverterInfoReader.java"/>
+ <include name="${package}/RegistryException.java"/>
+ <include name="${package}/UnoBridgeConverterInfo.java"/>
+ </javac>
+ </target>
+
+ <!-- clean up -->
+ <target name="clean" depends="prepare">
+ <delete includeEmptyDirs="true">
+ <fileset dir="${build.class}">
+ <patternset>
+ <include name="${package}/*.class"/>
+ </patternset>
+ </fileset>
+ </delete>
+ </target>
+
+</project>
+
diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/util/registry/package.html b/xmerge/source/xmerge/java/org/openoffice/xmerge/util/registry/package.html
new file mode 100644
index 000000000000..b6af6262cc5c
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/util/registry/package.html
@@ -0,0 +1,82 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<!--
+ #*************************************************************************
+ #
+ DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+
+ Copyright 2000, 2010 Oracle and/or its affiliates.
+
+ OpenOffice.org - a multi-platform office productivity suite
+
+ 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.registry package</title>
+</head>
+
+<body bgcolor="white">
+
+<p>Provides an interface for plug-in registration. Each plug-in must
+have a corresponding Plugin Configuration XML File which is named
+converter.xml. If the plug-in is stored in a jarfile, this
+converter.xml file is typically stored in the following location in
+the jarfile:</p>
+
+<blockquote>
+ META-INF/converter.xml
+</blockquote>
+
+<p>The Plugin Configuration XML File must validate against the
+converter.dtd file provided with this package. Since a jarfile
+can contain multiple plug-ins, this DTD supports specifying multiple
+plug-ins per jarfile. Please refer to the SDK document for more
+information about how to implement a Plugin Configuration XML File
+for a specific plugin.</p>
+
+<p>All information in the Plugin Configuratino XML File is bundled
+into one or more <code>ConverterInfo</code> object. The
+<code>ConverterInfoReader</code> object is used to build a
+<code>Vector</code> of <code>ConverterInfo</code> objects from a
+jarfile.</p>
+
+<p>The <code>ConverterInfoMgr</code> manages the registry of
+<code>ConverterInfo</code>. It is a singleton class, so that only one
+registry manager will ever exist. It is the client program's
+responsibility to register <code>ConverterInfo</code> objects that
+correspond to the plug-ins that are to be used.</p>
+
+<h2>TODO/IDEAS list</h2>
+
+<p><ol>
+<li>The <code>ConverterInfo</code> object could contain
+ <code>org.w3c.dom.Document</code> fragments that are accessed in a
+ generic fashion rather than get/set methods for each item in the DTD.
+ This would provide a more flexible approach, especially for adding
+ custom tags to a specific Plugin Configuration XML file (tags that
+ are only used by its associated plug-in).
+<li><code>ConverterInfo</code> should allow the merge/serialize/deserialize
+ logic to be included in separate plug-ins, if desired.</li>
+<li><code>ConverterInfoMgr</code> could use the Java Activation
+ Framework (JAF) to manage registration.</li>
+</ol></p>
+
+</body>
+</html>
diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/util/resources.properties b/xmerge/source/xmerge/java/org/openoffice/xmerge/util/resources.properties
new file mode 100644
index 000000000000..99e6ccae2645
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/util/resources.properties
@@ -0,0 +1,69 @@
+#*************************************************************************
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# Copyright 2000, 2010 Oracle and/or its affiliates.
+#
+# OpenOffice.org - a multi-platform office productivity suite
+#
+# 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.
+#
+#*************************************************************************
+# x-no-translate
+
+
+#
+# resources.properties
+#
+# resources for org.openoffice.xmerge.converter package.
+#
+NULL_MIME_EXCEPTION=Cannot specify null MIME types
+EMPTY_MIME_EXCEPTION=Cannot specify empty MIME types
+CANNOT_LOAD_CLASS=Unable to load class
+CANNOT_INST_CLASS=Unable to instantiate class
+NOT_AN_INSTANCE= is not an instance of
+CANNOT_FIND_REGISTERED=Cannot find registered class
+PARSE_ERROR=Parse Error
+LINE=Line
+COLUMN=Column
+PUBLIC_ID=PublicId
+SYSTEM_ID=SystemId
+INVALID_LOG_LEVEL=Invalid log level specified
+OPERATION_NOT_SUPPORTED=Operation not supported
+TEMPLATE_FILE_LOAD_ERROR=Error in loading template file -
+
+#
+# diff/merge algorithm erro messages
+#
+EMPTY_NODE_EXCEPTION=Current Node is empty
+NOT_LEAFNODE_EXCEPTION=Current Node is not a leaf node
+ROOTNODE_EXCEPTION=Cannot perform insert/append/remove on root node
+NOT_TEXTNODE_EXCEPTION=The target Node is not a TEXT_NODE, it is -
+NULL_NODE_EXCEPTION=The initial Xmldocument node is null
+CELL_NODE_EXCEPTION1=Cell node do not have only 1 child <text:p> nodes, will skip the merge of this node.Num of PARA child nodes:
+CELL_NODE_EXCEPTION2=Cell node have a non Element child nodes -
+CELL_NODE_EXCEPTION2=There is a child node under an expected empty cell node.
+NOT_ELEM_NODE_ERROR=The compared nodes are not a Element Node
+NOT_PARA_NODE_ERROR=The compared nodes are not a Paragraph or Heading node -
+NOT_NODE_ERROR=The compared nodes are not a Node
+#
+# SXW to/from DOC conversion error messages.
+#
+UNKNOWN_DOC_VERSION=Unknown DOC version.
+DOC_TEXT_LENGTH_EXCEEDED=DOC text length exceeds maximum value.
+DOC_TEXT_RECORD_SIZE_EXCEEDED=DOC text record exceeds size limit.