summaryrefslogtreecommitdiffstats
path: root/xmerge/source/xmerge/java/org/openoffice/xmerge/util
diff options
context:
space:
mode:
Diffstat (limited to 'xmerge/source/xmerge/java/org/openoffice/xmerge/util')
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/util/ActiveSyncDriver.java143
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/util/ColourConverter.java421
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/util/Debug.java230
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/util/Debug.properties29
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/util/EndianConverter.java128
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/util/IntArrayList.java125
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/util/OfficeUtil.java123
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/util/Resources.java74
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/util/TwipsConverter.java84
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/util/XmlUtil.java171
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/util/package-info.java22
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/util/registry/ConverterInfo.java406
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/util/registry/ConverterInfoMgr.java477
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/util/registry/ConverterInfoReader.java245
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/util/registry/RegistryException.java38
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/util/registry/package-info.java59
-rw-r--r--xmerge/source/xmerge/java/org/openoffice/xmerge/util/resources.properties59
17 files changed, 2834 insertions, 0 deletions
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 000000000..829963283
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/util/ActiveSyncDriver.java
@@ -0,0 +1,143 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package org.openoffice.xmerge.util;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileInputStream;
+import java.io.IOException;
+
+import java.util.StringTokenizer;
+import java.util.NoSuchElementException;
+
+import org.openoffice.xmerge.Convert;
+import org.openoffice.xmerge.Document;
+import org.openoffice.xmerge.ConvertData;
+import org.openoffice.xmerge.ConvertException;
+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;
+ 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");
+ } else {
+ return false;
+ }
+ }
+
+ ConverterInfoReader cirPlugin = new ConverterInfoReader(pluginJar.toURI().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
+ boolean bOK = true;
+ FileInputStream fis = null;
+ FileOutputStream fos = null;
+ try {
+ fis = new FileInputStream(srcFile);
+ conv.addInputStream(srcFile, fis);
+ try {
+ fos = new FileOutputStream(dstFile);
+ ConvertData dataOut = conv.convert();
+
+ // Get the document and write it out.
+ Document doc = (Document)dataOut.getDocumentEnumeration().next();
+ doc.write(fos);
+ fos.flush();
+ } finally {
+ if (fos != null)
+ fos.close();
+ }
+ } catch (IOException e) {
+ bOK = false;
+ } catch (NullPointerException e) {
+ bOK = false;
+ } catch (ConvertException e) {
+ bOK = false;
+ } catch (NoSuchElementException e) {
+ bOK = false;
+ } finally {
+ if (fis != null)
+ fis.close();
+ }
+
+ return bOK;
+ }
+} \ No newline at end of file
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 000000000..aefb3f966
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/util/ColourConverter.java
@@ -0,0 +1,421 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package org.openoffice.xmerge.util;
+
+import java.awt.Color;
+
+/**
+ * Utility class mapping RGB colour specifications to the colour indices used
+ * in the Pocket PC.
+ *
+ * <p>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 default 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.</p>
+ *
+ * <p>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.</p>
+ *
+ * <p>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).</p>
+ */
+
+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.
+ *
+ * <p>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).</p>
+ *
+ * @param lookup a 16 bit array mapping the 16 colours to their values.
+ */
+ public ColourConverter(short lookup[]) {
+ tableLookup = lookup;
+ }
+
+ /**
+ * Uses the colour table if 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 if 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 colour The index into Pocket Word's colour table.
+ *
+ * @return A {@code 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.
+ *
+ * <p>Most of the supported colours have their components set to either 0,
+ * 128 or 255. The exception is 'Grey' which is {@literal 0xC0C0C0}.</p>
+ *
+ * @param colour {@code 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 colours to their base color rather than
+ // white so we modify the rgb values if the colour is sufficiently 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 appropriate 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);
+
+ return matchedRGB == 0xC0C0C0;
+ }
+} \ No newline at end of file
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 000000000..ed95031f8
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/util/Debug.java
@@ -0,0 +1,230 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package org.openoffice.xmerge.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.util.Properties;
+
+/**
+ * This class is used for logging debug messages.
+ *
+ * <p>Currently, there are three types of logging: {@link #INFO}, {@link #TRACE}
+ * {@literal &} {@link #ERROR}. Use the {@code Debug.properties} file to set or
+ * unset each type. Also use {@code Debug.properties} to set the writer to
+ * either {@code System.out}, {@code System.err}, or to a file.</p>
+ */
+public final class Debug {
+
+ /** Informational messages. */
+ public static final int INFO = 0x0001;
+ /** Error messages. */
+ public static final int ERROR = 0x0002;
+ /** Trace messages. */
+ public static final int TRACE = 0x0004;
+
+ /** To set a flag. */
+ private static final boolean SET = true;
+
+ private static int flags = 0;
+ private static PrintWriter writer = null;
+
+ static {
+
+ InputStream is = null;
+ try {
+ try {
+ is = Debug.class.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);
+
+ } finally {
+ if (is !=null)
+ is.close();
+ }
+ } catch (Throwable ex) {
+ ex.printStackTrace(System.err);
+ }
+ }
+
+ /**
+ * Private constructor so as not to allow any instances of this class.
+ *
+ * <p>This serves as a singleton class.</p>
+ */
+ private Debug() {
+ }
+
+ /**
+ * Set the output to the specified argument.
+ *
+ * <p>This method is only used internally to prevent invalid string
+ * parameters.</p>
+ *
+ * @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} object.
+ *
+ * @param stream {@code OutputStream} object.
+ */
+ private static void setOutput(OutputStream stream) {
+ setOutput(new OutputStreamWriter(stream));
+ }
+
+ /**
+ * Set the {@code Writer} object to manage the output.
+ *
+ * @param w {@code Writer} object to write out.
+ */
+ private static void setOutput(Writer w) {
+ if (writer != null) {
+ writer.close();
+ }
+ writer = new PrintWriter(new BufferedWriter(w), true);
+ }
+
+ /**
+ * This method sets the levels for debugging logs.
+ *
+ * <p>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 )}</pre></blockquote>
+ *
+ * @param f Debug flag
+ * @param set Use {@code Debug.SET} to set, and {@code Debug.UNSET} to
+ * unset the given flag.
+ */
+ private static void setFlags(int f, boolean set) {
+ if (set) {
+ flags |= f;
+ } else {
+ flags &= ~f;
+ }
+ }
+
+ /**
+ * Checks if flag is set.
+ *
+ * @return {@code true} if info logging is on, otherwise {@code false}.
+ */
+ public static boolean isFlagSet(int f) {
+ return ((flags & f) != 0);
+ }
+
+ /**
+ * Log message based on the flag type.
+ *
+ * <p>Example 1:</p>
+ *
+ * <blockquote><pre>{@code Debug.log(Debug.INFO, "info string here");}</pre>
+ * </blockquote>
+ *
+ * <p>This logs the message during runtime if {@code debug.info} 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");}</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 flag 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) && writer != null) {
+ writer.println(msg);
+ }
+ }
+
+ /**
+ * Log message based on flag type plus print out stack trace of the
+ * exception passed in.
+ *
+ * <p>Refer to the other log method for description.</p>
+ *
+ * @param flag 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) && writer != null) {
+ writer.println(msg);
+ if (e != null)
+ e.printStackTrace(writer);
+ }
+ }
+} \ No newline at end of file
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 000000000..5802cb86c
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/util/Debug.properties
@@ -0,0 +1,29 @@
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This file incorporates work covered by the following license notice:
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed
+# with this work for additional information regarding copyright
+# ownership. The ASF licenses this file to you under the Apache
+# License, Version 2.0 (the "License"); you may not use this file
+# except in compliance with the License. You may obtain a copy of
+# the License at http://www.apache.org/licenses/LICENSE-2.0 .
+#
+# 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 000000000..a796806f9
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/util/EndianConverter.java
@@ -0,0 +1,128 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package org.openoffice.xmerge.util;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * Helper class providing static methods to convert data to/from Network Byte
+ * Order (Big Endian).
+ *
+ * <p>With the introduction of {@code java.nio.ByteOrder} and
+ * {@code java.nio.ByteBuffer} in Java 1.4, it may no longer be necessary to use
+ * this class in the future.</p>
+ *
+ * @version 1.1
+ */
+public class EndianConverter {
+
+ /**
+ * Convert a {@code short} to a Little Endian representation.
+ *
+ * @param value The {@code short} to be converted.
+ *
+ * @return Two element {@code byte} array containing the converted value.
+ */
+ public static byte[] writeShort (short value) {
+ return ByteBuffer.allocate(2).order(ByteOrder.LITTLE_ENDIAN)
+ .putShort(value).array();
+ }
+
+ /**
+ * Convert an integer to a Little Endian representation.
+ *
+ * @param value The {@code int} to be converted.
+ *
+ * @return Four element {@code byte} array containing the converted value.
+ */
+ public static byte[] writeInt (int value) {
+ return ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN)
+ .putInt(value).array();
+ }
+
+ /**
+ * Converts a {@code double} 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} containing the value to be converted.
+ *
+ * @return {@code byte} array containing the LE representation of a
+ * IEEE-754 float.
+ */
+ public static byte[] writeDouble(double value) {
+ return ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN).putLong(
+ Double.doubleToLongBits(value)).array();
+ }
+
+ /**
+ * Convert a Little Endian representation of a short to a Java {@code short}
+ * (Network Byte Order).
+ *
+ * <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} array containing the LE representation of
+ * the value.
+ *
+ * @return {@code short} containing the converted value.
+ */
+ public static short readShort (byte[] value) {
+ return ByteBuffer.allocate(2).order(ByteOrder.LITTLE_ENDIAN)
+ .put(value).getShort(0);
+ }
+
+ /**
+ * Convert a Little Endian representation of an integer to a Java {@code int}
+ * (Network Byte Order).
+ *
+ * <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} array containing the LE representation of
+ * the value.
+ *
+ * @return {@code int} containing the converted value.
+ */
+ public static int readInt(byte[] value) {
+ return ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN)
+ .put(value).getInt(0);
+ }
+
+ /**
+ * Convert a Little Endian representation of a float in IEEE-754 Little
+ * Endian to a Java {@code double} (Network Byte Order).
+ *
+ * <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} array containing the LE representation of an
+ * IEEE-754 float.
+ *
+ * @return {@code double} containing the converted value.
+ */
+ public static double readDouble(byte[] value) {
+ return Double.longBitsToDouble(
+ ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN).put(value)
+ .getLong(0));
+ }
+} \ No newline at end of file
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 000000000..56b8212b6
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/util/IntArrayList.java
@@ -0,0 +1,125 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package org.openoffice.xmerge.util;
+
+import java.util.ArrayList;
+
+/**
+ * This is a convenience class used to create an {@code ArrayList} of integers.
+ */
+public class IntArrayList {
+
+ /** The list to hold our integers. */
+ private final ArrayList<Integer> list;
+
+ /**
+ * Constructor.
+ *
+ * <p>Creates the list with 0 length.</p>
+ */
+ public IntArrayList() {
+ list = new ArrayList<Integer>();
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param initialCapacity Initial capacity of the list.
+ */
+ public IntArrayList(int initialCapacity) {
+ list = new ArrayList<Integer>(initialCapacity);
+ }
+
+ /**
+ * This method ensures that the list is large enough for {@code minCapacity}
+ * 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}
+ * elements.
+ *
+ * <p>It also fills in the new slots in the list with the integer value
+ * input as {@code fillValue}.</p>
+ *
+ * @param minCapacity The minimum capacity of the list.
+ * @param fillValue This method adds in an integer for each slot that was
+ * added to ensure that the list meets the minimum
+ * capacity. {@code fillValue} 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(Integer.valueOf(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, Integer.valueOf(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(Integer.valueOf(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 list.get(index).intValue();
+ }
+
+ /**
+ * This method gets the size of the list.
+ *
+ * @return The size of the list.
+ */
+ public int size() {
+ return list.size();
+ }
+} \ No newline at end of file
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 000000000..305100772
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/util/OfficeUtil.java
@@ -0,0 +1,123 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package org.openoffice.xmerge.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.ArrayList;
+
+/**
+ * Class providing utility methods for OpenOffice plug-ins.
+ *
+ * @version 1.1
+ */
+public class OfficeUtil implements OfficeConstants {
+
+ /**
+ * Method to replace whitespace character within text with appropriate
+ * OpenOffice tags.
+ *
+ * @param text The text to parse for whitespace.
+ *
+ * @return {@code Node} array containing OpenOffice XML nodes representing
+ * the text.
+ */
+ public static Node[] parseText(String text, Document parentDoc) {
+ ArrayList<Node> nodeVec = new ArrayList<Node>();
+
+ /*
+ * 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.add(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,
+ Integer.toString(nrSpaces));
+ 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[] = nodeVec.toArray(new Node[nodeVec.size()]);
+ return nodes;
+ }
+}
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 000000000..aa7dd7c10
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/util/Resources.java
@@ -0,0 +1,74 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package org.openoffice.xmerge.util;
+
+import java.util.ResourceBundle;
+
+/**
+ * Provides a singleton resource class for converter messages.
+ *
+ * <p>By default, this class will search for a {@code ResourceBundle} 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}, which is
+ * a {@code RuntimeException}, thus I opted to not explicitly declare it. If it
+ * does throw {@code MissingResourceException}, it may be due to a packaging
+ * problem.</p>
+ */
+public final class Resources
+{
+ private final ResourceBundle rb;
+ private static Resources instance = null;
+
+ /**
+ * This method returns the singleton instance of this class.
+ *
+ * @return The singleton {@code Resources} instance.
+ */
+ public synchronized static Resources getInstance() {
+ if (instance == null) {
+ instance = new Resources();
+ }
+
+ return instance;
+ }
+
+ /**
+ * Default constructor is only accessible within this class.
+ *
+ * <p>Load the resource bundle that contains the resource {@code String}
+ * values.</p>
+ */
+ private Resources() {
+ rb = ResourceBundle.getBundle("org.openoffice.xmerge.util.resources");
+ }
+
+ /**
+ * This method returns the corresponding {@code String} given the key.
+ *
+ * @param key Key string for getting the message {@code String}.
+ * @return Message {@code String} corresponding to the key.
+ */
+ public String getString(String key) {
+ return rb.getString(key);
+ }
+} \ No newline at end of file
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 000000000..a8501f8b9
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/util/TwipsConverter.java
@@ -0,0 +1,84 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package org.openoffice.xmerge.util;
+
+/**
+ * Helper class providing static methods to convert data to/from twips.
+ */
+public class TwipsConverter {
+
+ /**
+ * Convert from twips to cm's.
+ *
+ * @param value The {@code short} to be converted.
+ *
+ * @return {@code float} containing the converted.
+ */
+ public static float twips2cm(int value) {
+ float inches = (float) value/1440;
+ float cm = inches*(float)2.54;
+ return cm;
+ }
+
+ /**
+ * Convert from cm's to twips.
+ *
+ * @param value {@code float} containing the representation of the value.
+ *
+ * @return {@code int} containing the converted value.
+ */
+ private static int cm2twips(float value) {
+ int twips = (int) ((value/2.54)*1440);
+ return twips;
+ }
+
+ /**
+ * Convert from cm's to twips.
+ *
+ * @param value {@code float} containing the representation of the value.
+ *
+ * @return {@code int} containing the converted value.
+ */
+ private static int inches2twips(float value) {
+ return (int) (value*1440);
+ }
+
+ /**
+ * Convert {@code String} to twips.
+ *
+ * @param value {@code String} in the form {@literal "1.234cm"} or
+ * {@literal "1.234inch"}.
+ * @param defaultValue the default value.
+ * @return the converted value if {@code value} is a well-formatted {@code
+ * String}, {@code defaultValue} otherwise.
+ */
+ public static int convert2twips(String value, int defaultValue) {
+ int posi;
+
+ if ((posi = value.indexOf("cm")) != -1) {
+ float cm = Float.parseFloat(value.substring(0,posi));
+ return cm2twips(cm);
+ } else if ((posi = value.indexOf("inch")) != -1) {
+ float inches = Float.parseFloat(value.substring(0,posi));
+ return inches2twips(inches);
+ }
+
+ return defaultValue;
+ }
+} \ No newline at end of file
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 000000000..fd39536b3
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/util/XmlUtil.java
@@ -0,0 +1,171 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package org.openoffice.xmerge.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 utility methods for handling XML trees.
+ */
+public final class XmlUtil {
+
+ /**
+ * Perform a deep clone of certain {@code Node} which will base on the
+ * document {@code Node} of the old {@code Node}.
+ *
+ * @param oldNode The {@code Document} of this {@code Node} is used to
+ * clone the {@code Node}.
+ * @param newNode The <code>Node</code> to clone.
+ *
+ * @return The cloned {@code Node}.
+ */
+ public static Node deepClone(Node oldNode, Node newNode) {
+ Document docNode = oldNode.getOwnerDocument();
+
+ // clone the starting node
+ Node clonedNode = cloneNode(docNode, newNode);
+
+ if (clonedNode != null) {
+ // then clone the sub-tree recursively
+ cloneTree(docNode, clonedNode, newNode);
+ }
+
+ return clonedNode;
+ }
+
+ /**
+ * Clone the sub-tree under certain given {@code Node}.
+ *
+ * @param docNode The {@code Document} used to clone the {@code Node}.
+ * @param oldNode The {@code Node} to clone.
+ * @param newNode The destination {@code Node}.
+ */
+ 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} (either text or element).
+ *
+ * @param docNode The {@code Document} used to clone the {@code Node}.
+ * @param newNode The {@code Node}to clone.
+ *
+ * @return The cloned {@code Node}.
+ */
+ 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}.
+ *
+ * @param node {@code Node} to query.
+ *
+ * @return Name and type of XML DOM {@code Node}.
+ */
+ 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-info.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/util/package-info.java
new file mode 100644
index 000000000..db76b1cc9
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/util/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+/**
+ * Provides general purpose utilities.
+ */
+package org.openoffice.xmerge.util;
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 000000000..2190ab27c
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/util/registry/ConverterInfo.java
@@ -0,0 +1,406 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package org.openoffice.xmerge.util.registry;
+
+import java.lang.reflect.Constructor;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import org.openoffice.xmerge.DocumentDeserializerFactory;
+import org.openoffice.xmerge.DocumentMergerFactory;
+import org.openoffice.xmerge.DocumentSerializerFactory;
+import org.openoffice.xmerge.PluginFactory;
+
+/**
+ * Class for storing the information about a converter plug-in.
+ */
+public class ConverterInfo {
+
+ /** Keep track of the valid Office mime types. */
+ private static final String[] validOfficeTypes = new String[] {
+ // This needs to be updated to reflect all valid office types.
+ "staroffice/sxw",
+ "staroffice/sxc"
+ };
+
+ private final String piJarName;
+ private final String piOfficeMime;
+ private final ArrayList<String> piDeviceMime;
+ private final String piDisplayName;
+ private final String piDescription;
+ private final String piVersion;
+ private final String piVendor;
+ private final String piClassImpl;
+ private String piXsltSerial;
+ private String piXsltDeserial;
+ private boolean piCanSerialize = false;
+ private boolean piCanDeserialize = false;
+ private boolean piCanMerge = false;
+ private final ClassLoader piClassLoader;
+ 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
+ * {@code PluginFactory}.
+ * @param xsltSerial The URL of the serializer XSL stylesheet
+ * @param xsltDeserial The URL of the deserializer XSL stylesheet
+ *
+ * @throws RegistryException If {@code ci} cannot be loaded.
+ */
+ public ConverterInfo(String jarName, String officeMime,
+ ArrayList<String> deviceMime, String displayName,
+ String description, String version, String vendor,
+ String impl,String xsltSerial, String xsltDeserial)
+ throws RegistryException {
+
+ if (!isValidOfficeType(officeMime.trim())) {
+ 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 {
+ final URL jarURL = new URL(jarName);
+ final URL[] urls = new URL[] { jarURL };
+ URLClassLoader loader = AccessController.doPrivileged(
+ new PrivilegedAction<URLClassLoader>() {
+ public URLClassLoader run() {
+ return new URLClassLoader(urls, 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) {
+ throw new RegistryException(
+ "Class implementation of the plug-in cannot be loaded.", e);
+ }
+ }
+
+ /**
+ * 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
+ * {@code PluginFactory}.
+ *
+ * @throws RegistryException If {@code ci} cannot be loaded.
+ */
+ public ConverterInfo(String jarName, String officeMime,
+ ArrayList<String> deviceMime, String displayName, String description,
+ String version, String vendor, String impl)
+ throws RegistryException {
+
+ if (officeMime == null || displayName == null || description == null ||
+ version == null || vendor == null || impl == null)
+ throw new IllegalArgumentException("arguments unexpected null");
+
+ if (!isValidOfficeType(officeMime.trim())) {
+ 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 {
+ final URL jarURL = new URL(jarName);
+ final URL[] urls = new URL[] { jarURL };
+ URLClassLoader loader = AccessController.doPrivileged(
+ new PrivilegedAction<URLClassLoader>() {
+ public URLClassLoader run() {
+ return new URLClassLoader(urls, 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) {
+ throw new RegistryException(
+ "Class implementation of the plug-in cannot be loaded.", e);
+ }
+ }
+
+ /**
+ * Returns an instance of the {@code DocumentDeserializerFactory} interface.
+ *
+ * @return instance of the {@code DocumentDeserializer} for this
+ * {@code ConverterInfo}.
+ */
+ public DocumentSerializerFactory getDocSerializerFactory() {
+ return (DocumentSerializerFactory)piPluginFactory;
+ }
+
+ /**
+ * Returns an instance of the {@code DocumentSerializerFactory} interface.
+ *
+ * @return instance of the {@code DocumentSerializer} for this
+ * {@code ConverterInfo}.
+ */
+ public DocumentDeserializerFactory getDocDeserializerFactory() {
+ return (DocumentDeserializerFactory)piPluginFactory;
+ }
+
+ /**
+ * Returns an instance of the DocumentMergerFactory interface.
+ *
+ * @return instance of the {@code DocumentMergerFactory} for this
+ * {@code ConverterInfo}.
+ */
+ public DocumentMergerFactory getDocMergerFactory() {
+ return (DocumentMergerFactory)piPluginFactory;
+ }
+
+ /**
+ * Returns the jar file name.
+ *
+ * @return The jar file name, {@code null} if none exists.
+ */
+ public String getJarName() {
+ return piJarName;
+ }
+
+ /**
+ * Returns the office mime-type.
+ *
+ * @return The office mime-type, {@code null} if none exists.
+ */
+ public String getOfficeMime() {
+ return piOfficeMime;
+ }
+
+ /**
+ * Returns an {@code Enumeration} of {@code String} objects indicating the
+ * device mime-type.
+ *
+ * @return An {@code Enumeration} of {@code String} objects indicating the
+ * device mime-type.
+ */
+ public Iterator<String> getDeviceMime() {
+ return piDeviceMime.iterator();
+ }
+
+ /**
+ * Returns the display name.
+ *
+ * @return The display name, {@code null} if none exists.
+ */
+ public String getDisplayName() {
+ return piDisplayName;
+ }
+
+ /**
+ * Returns the description.
+ *
+ * @return The description, {@code null} if none exists.
+ */
+ public String getDescription() {
+ return piDescription;
+ }
+
+ /**
+ * Returns the version.
+ *
+ * @return The version, {@code null} if none exists.
+ */
+ public String getVersion() {
+ return piVersion;
+ }
+
+ /**
+ * Returns the vendor name.
+ *
+ * @return The vendor name, {@code null} if none exists.
+ */
+ public String getVendor() {
+ return piVendor;
+ }
+
+ /**
+ * Returns the implementation class name of PluginFactory.
+ *
+ * @return The implementation class name of {@code PluginFactory},
+ * {@code null} if none exists.
+ */
+ public String getClassImpl() {
+ return piClassImpl;
+ }
+
+ /**
+ * Returns the {@code PluginFactory} instance for this plug-in.
+ *
+ * @return The {@code PluginFactory} instance for this plug-in.
+ */
+ public PluginFactory getPluginFactory() {
+ return piPluginFactory;
+ }
+
+ /**
+ * Returns {@code true} if this plug-in has a serializer, {@code false}
+ * otherwise.
+ *
+ * @return {@code true} if this plug-in has a serializer, {@code false}
+ * otherwise.
+ */
+ public boolean canSerialize() {
+ return piCanSerialize;
+ }
+
+ /**
+ * Returns {@code true} if this plug-in has a deserializer, {@code false}
+ * otherwise.
+ *
+ * @return {@code true} if this plug-in has a deserializer, {@code false}
+ * otherwise.
+ */
+ public boolean canDeserialize() {
+ return piCanDeserialize;
+ }
+
+ /**
+ * Returns {@code true} if this plug-in has a merger, {@code false}
+ * otherwise.
+ *
+ * @return {@code true} if this plug-in has a merger, {@code false}
+ * otherwise.
+ */
+ public boolean canMerge() {
+ return piCanMerge;
+ }
+
+ /**
+ * Returns {@code true} if the officeMime is a valid Office mime type.
+ *
+ * @return {@code true} if the officeMime is a valid Office mime type.
+ */
+ public static boolean isValidOfficeType(String officeMime) {
+
+ boolean rc = false;
+ for (String validOfficeType : validOfficeTypes) {
+ if (officeMime.equals(validOfficeType)) {
+ rc = true;
+ }
+ }
+
+ return rc;
+ }
+
+ /**
+ * Returns a {@code String} containing the Xslt stylesheet URL that is to be
+ * used by the Xslt Plug-in Serializer.
+ *
+ * @return {@code String}.
+ */
+ public String getXsltSerial() {
+ return piXsltSerial;
+ }
+
+ /**
+ * Returns a {@code String} containing the xslt stylesheet URL that is to be
+ * used by the Xslt Plug-in Deserializer.
+ *
+ * @return {@code String}.
+ */
+ 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 000000000..9cea2df38
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/util/registry/ConverterInfoMgr.java
@@ -0,0 +1,477 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package org.openoffice.xmerge.util.registry;
+
+import java.util.*;
+import java.io.*;
+
+/**
+ * Manages the converter plug-ins that are currently active.
+ *
+ * <p>This class allows plug-ins to be added or removed dynamically.</p>
+ *
+ * <p>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 sub-classed.</p>
+ */
+public final class ConverterInfoMgr {
+
+ private static final ArrayList<ConverterInfo> converterInfoList = new ArrayList<ConverterInfo>();
+
+ /**
+ * Adds a converter plug-in to the registry.
+ *
+ * <p>The {@code ConverterInfo} must have a unique DisplayName and must have
+ * non-null values for DisplayName, ClassImpl, OfficeMime, and DeviceMime.</p>
+ *
+ * @param ci A {@code ConverterInfo} object describing a plug-in.
+ *
+ * @throws RegistryException If the {@code ConverterInfo} is not valid.
+ */
+ private static void addPlugIn(ConverterInfo ci) throws RegistryException {
+
+ // 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().hasNext()) {
+ 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.
+ for (ConverterInfo converterInfo : converterInfoList) {
+ if (ci.getDisplayName().equals(converterInfo.getDisplayName())) {
+ RegistryException re = new RegistryException(
+ "Converter with specified display name already exists.");
+ throw re;
+ }
+ }
+
+ // Since this is adding to a static Vector, make sure this add method
+ // call is synchronized.
+ synchronized (converterInfoList) {
+ converterInfoList.add(ci);
+ }
+ }
+
+ /**
+ * Adds a list of converter plug-ins to the registry.
+ *
+ * <p>Each {@code ConverterInfo} in the list must have a unique DisplayName
+ * and must have non-null values for DisplayName, ClassImpl, OfficeMime, and
+ * DeviceMime.</p>
+ *
+ * @param jarEnum An {@code Enumeration} of {@code ConverterInfo} objects
+ * describing one or more plug-in(s).
+ *
+ * @throws RegistryException If a {@code ConverterInfo} in the
+ * {@code Vector} is not valid.
+ */
+ public static void addPlugIn(Iterator<ConverterInfo> jarEnum)
+ throws RegistryException {
+
+ while (jarEnum.hasNext()) {
+ ConverterInfo converterInfo = jarEnum.next();
+ addPlugIn(converterInfo);
+ }
+ }
+
+ /**
+ * Returns an {@code Enumeration} of registered {@code ConverterInfo} objects.
+ *
+ * @return An {@code Enumeration} containing the currently registered
+ * {@code ConverterInfo} objects, an empty {@code Vector} if none
+ * exist.
+ */
+ private static Iterator<ConverterInfo> getConverterInfoEnumeration() {
+ return converterInfoList.iterator();
+ }
+
+ /**
+ * Removes any {@code ConverterInfo} object from the registry that have the
+ * specified jar name value.
+ *
+ * @param jar The name of the jarfile.
+ *
+ * @return {@code true} if a {@code ConverterInfo} object was removed,
+ * {@code false} otherwise.
+ */
+ public static boolean removeByJar(String jar) {
+
+ boolean rc = false;
+
+ for (Iterator<ConverterInfo> it = converterInfoList.iterator(); it.hasNext();) {
+ ConverterInfo converterInfo = it.next();
+ if (jar.equals(converterInfo.getJarName())) {
+ it.remove();
+ rc = true;
+ }
+ }
+ return rc;
+ }
+
+ /**
+ * Removes any {@code ConverterInfo} object from the registry that have the
+ * specified display name value.
+ *
+ * @param name The display name.
+ *
+ * @return {@code true} if a {@code ConverterInfo} object was removed,
+ * {@code false} otherwise.
+ */
+ private static boolean removeByName(String name) {
+
+ boolean rc = false;
+
+ for (Iterator<ConverterInfo> it = converterInfoList.iterator(); it.hasNext();) {
+ ConverterInfo converterInfo = it.next();
+ if (name.equals(converterInfo.getDisplayName())) {
+ it.remove();
+ rc = true;
+ }
+ }
+ return rc;
+ }
+
+ /**
+ * Returns the {@code ConverterInfo} object that supports the specified
+ * device/office mime type conversion.
+ *
+ * <p>If there are multiple {@code ConverterInfo} objects registered that
+ * support this conversion, only the first is returned.</p>
+ *
+ * @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) {
+
+ if (deviceMime == null ||
+ !ConverterInfo.isValidOfficeType(officeMime)) {
+ return null;
+ }
+
+ // Loop over elements comparing with deviceFromMime
+ for (ConverterInfo converterInfo : converterInfoList) {
+ String toDeviceInfo = converterInfo.getOfficeMime();
+ Iterator<String> fromEnum = converterInfo.getDeviceMime();
+
+ // Loop over the deviceMime types.
+ while (fromEnum.hasNext()) {
+ String fromDeviceInfo = fromEnum.next();
+ if (deviceMime.trim().equals(fromDeviceInfo) &&
+ officeMime.trim().equals(toDeviceInfo)) {
+ return converterInfo;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns an array of two {@code ConverterInfo} objects that can be chained
+ * to perform the specified mime type conversion.
+ *
+ * <p>If there are multiple {@code ConverterInfo} objects that support this
+ * conversion, only the first is returned.</p>
+ *
+ * @param deviceFromMime The device from mime.
+ * @param deviceToMime The device to mime.
+ *
+ * @return An array of two {@code ConverterInfo} objects that can be chained
+ * to perform the specified conversion.
+ */
+ private 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
+ Iterator<ConverterInfo> cifEnum = converterInfoList.iterator();
+ while (cifEnum.hasNext()) {
+
+ converterInfo[0] = cifEnum.next();
+ String fromOfficeInfo = converterInfo[0].getOfficeMime();
+ Iterator<String> fromEnum = converterInfo[0].getDeviceMime();
+
+ // Loop over the deviceMime types looking for a deviceFromMime
+ // match.
+ while (fromEnum.hasNext()) {
+ String fromDeviceInfo = fromEnum.next();
+
+ if (deviceFromMime.trim().equals(fromDeviceInfo)) {
+
+ // Found a match for deviceFrom. Now loop over the
+ // elements comparing with deviceToMime
+ Iterator<ConverterInfo> citEnum = converterInfoList.iterator();
+ while (citEnum.hasNext()) {
+
+ converterInfo[1] = citEnum.next();
+ String toOfficeInfo = converterInfo[1].getOfficeMime();
+ Iterator<String> toEnum = converterInfo[1].getDeviceMime();
+
+ // Loop over deviceMime types looking for a
+ // deviceToMime match.
+ while (toEnum.hasNext()) {
+ String toDeviceInfo = toEnum.next();
+ if (deviceToMime.trim().equals(toDeviceInfo) &&
+ fromOfficeInfo.equals(toOfficeInfo)) {
+
+ // Found a match
+ return converterInfo;
+ }
+ }
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ static String readLine(BufferedReader br) throws IOException{
+ String ret = br.readLine();
+ if (ret == null) {
+ throw new IOException("short read");
+ }
+ return ret;
+ }
+
+ /**
+ * 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) {
+
+ 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 = readLine(br).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 = readLine(br).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) {
+ Iterator<ConverterInfo> 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){
+ 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 = readLine(br).trim();
+ boolean rc = false;
+
+ if (c == 'D') {
+ rc = ConverterInfoMgr.removeByName(name);
+ } else {
+ rc = ConverterInfoMgr.removeByJar(name);
+ }
+
+ if (rc) {
+ 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 = readLine(br).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 = readLine(br).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 != null && 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') {
+
+ Iterator<ConverterInfo> ciEnum = ConverterInfoMgr.getConverterInfoEnumeration();
+
+ int ciCnt = 0;
+ while (ciEnum.hasNext())
+ {
+ System.out.println("");
+ System.out.println(" Displaying converter number " + ciCnt);
+ ConverterInfo converterInfo = ciEnum.next();
+ 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());
+ Iterator<String> fromEnum = converterInfo.getDeviceMime();
+ int feCnt = 1;
+ while (fromEnum.hasNext())
+ {
+ System.out.println(" DeviceMime : (#" + feCnt + ") : " +
+ fromEnum.next());
+ 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 000000000..21831a691
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/util/registry/ConverterInfoReader.java
@@ -0,0 +1,245 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package org.openoffice.xmerge.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} pulls a {@code META-INF/converter.xml} file
+ * out of a jar file and parses it, providing access to this information in a
+ * {@code Vector} of {@code ConverterInfo} objects.
+ */
+public class ConverterInfoReader {
+
+ private static final String TAG_CONVERTER = "converter";
+ private static final String ATTRIB_OFFICE_TYPE = "type";
+ private static final String ATTRIB_VERSION = "version";
+ private static final String TAG_NAME = "converter-display-name";
+ private static final String TAG_DESC = "converter-description";
+ private static final String TAG_VENDOR = "converter-vendor";
+ private static final String TAG_CLASS_IMPL = "converter-class-impl";
+ private static final String TAG_TARGET = "converter-target";
+ private static final String ATTRIB_DEVICE_TYPE = "type";
+ private static final String TAG_XSLT_DESERIAL = "converter-xslt-deserialize";
+ private static final String TAG_XSLT_SERIAL = "converter-xslt-serialize";
+ private final String jarfilename;
+ private final Document document;
+ private final ArrayList<ConverterInfo> converterInfoList;
+
+ /**
+ * Constructor.
+ *
+ * <p>A jar file is passed in. The jar file is parsed and the {@code Vector}
+ * of {@code ConverterInfo} objects is built.</p>
+ *
+ * @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 {@code DocumentBuilder}
+ * can not be built.
+ * @throws org.xml.sax.SAXException If the converter.xml file can not
+ * be parsed.
+ * @throws RegistryException If the {@code ConverterFactory}
+ * implementation of a plug-in cannot
+ * be loaded.
+ */
+ public ConverterInfoReader(String jar,boolean shouldvalidate) throws IOException,
+ ParserConfigurationException, org.xml.sax.SAXException,
+ RegistryException {
+
+ converterInfoList = new ArrayList<ConverterInfo>();
+ jarfilename = jar;
+
+ // Get Jar via URL
+ URL url = new URL("jar:" + jar + "!/META-INF/converter.xml");
+ JarURLConnection jarConnection = (JarURLConnection)url.openConnection();
+ JarEntry jarentry = jarConnection.getJarEntry();
+ JarFile jarfile = jarConnection.getJarFile();
+
+ if (jarfile == null || jarentry == null) {
+ throw new IOException("Missing jar entry");
+ }
+
+ // Build the InputSource
+ InputStream istream = jarfile.getInputStream(jarentry);
+ InputSource isource = new InputSource(istream);
+
+ // Get the DOM builder and build the document.
+
+ DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
+
+ //DTD validation
+
+ if (shouldvalidate) {
+ System.out.println("Validating xml...");
+ builderFactory.setValidating(true);
+ }
+
+ DocumentBuilder builder = builderFactory.newDocumentBuilder();
+ document = builder.parse(isource);
+
+ // Parse the document.
+
+ parseDocument();
+ }
+
+ /**
+ * Loops over the <i>converter</i> {@code Node} in the converter.xml file
+ * and processes them.
+ *
+ * @throws RegistryException If the plug-in associated with a specific
+ * <i>converter</i> {@code Node} 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} and placing it in a {@code ConverterInfo} object, and adds
+ * that object to a {@code Vector} of {@code ConverterInfo} objects.
+ *
+ * @param e The {@code Element} 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;
+ ArrayList<String> deviceMime = new ArrayList<String>();
+ 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);
+ }
+ converterInfoList.add(converterInfo);
+ }
+
+ /**
+ * Helper function to get the text value of an {@code Element}.
+ *
+ * @param e The {@code Element} to process.
+ *
+ * @return The text value of the {@code Element}.
+ */
+ 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} of {@code ConverterInfo} objects.
+ *
+ * @return An {@code Enumeration} of {@code ConverterInfo} objects.
+ */
+ public Iterator<ConverterInfo> getConverterInfoEnumeration() {
+ return converterInfoList.iterator();
+ }
+}
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 000000000..6452256d2
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/util/registry/RegistryException.java
@@ -0,0 +1,38 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+package org.openoffice.xmerge.util.registry;
+
+/**
+ * This {@code Exception} 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}.
+ */
+ public RegistryException(String message) {
+ super(message);
+ }
+
+ public RegistryException(String message, Throwable cause) {
+ super(message, cause);
+ }
+} \ No newline at end of file
diff --git a/xmerge/source/xmerge/java/org/openoffice/xmerge/util/registry/package-info.java b/xmerge/source/xmerge/java/org/openoffice/xmerge/util/registry/package-info.java
new file mode 100644
index 000000000..8634ab0a9
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/util/registry/package-info.java
@@ -0,0 +1,59 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+/**
+ * Provides an interface for plug-in registration.
+ *
+ * <p>Each plug-in must have a corresponding Plug-in 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 Plug-in 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
+ * Plug-in Configuration XML File for a specific plug-in.</p>
+ *
+ * <p>All information in the Plug-in Configuration XML File is bundled into one
+ * or more {@code ConverterInfo} object. The {@code ConverterInfoReader} object
+ * is used to build a {@code Vector} of {@code ConverterInfo} objects from a
+ * jarfile.</p>
+ *
+ * <p>The {@code ConverterInfoMgr} manages the registry of {@code ConverterInfo}.
+ * 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}
+ * objects that correspond to the plug-ins that are to be used.</p>
+ *
+ * <h2>TODO/IDEAS list</h2>
+ * <ol>
+ * <li>The {@code ConverterInfo} object could contain
+ * {@code org.w3c.dom.Document} 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 Plug-in Configuration XML file (tags that are only used by its
+ * associated plug-in).</li>
+ * <li>{@code ConverterInfo} should allow the merge/serialize/deserialize logic
+ * to be included in separate plug-ins, if desired.</li>
+ * <li>{@code ConverterInfoMgr} could use the Java Activation Framework (JAF)
+ * to manage registration.</li>
+ * </ol>
+ */
+package org.openoffice.xmerge.util.registry;
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 000000000..95e10b9e6
--- /dev/null
+++ b/xmerge/source/xmerge/java/org/openoffice/xmerge/util/resources.properties
@@ -0,0 +1,59 @@
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This file incorporates work covered by the following license notice:
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed
+# with this work for additional information regarding copyright
+# ownership. The ASF licenses this file to you under the Apache
+# License, Version 2.0 (the "License"); you may not use this file
+# except in compliance with the License. You may obtain a copy of
+# the License at http://www.apache.org/licenses/LICENSE-2.0 .
+#
+# 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 error 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 an 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.