summaryrefslogtreecommitdiff
path: root/xmerge/source/xmerge/java/org/openoffice/xmerge/merger/merge
diff options
context:
space:
mode:
Diffstat (limited to 'xmerge/source/xmerge/java/org/openoffice/xmerge/merger/merge')
-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
6 files changed, 1056 insertions, 0 deletions
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>