diff options
Diffstat (limited to 'xmerge/source/xmerge/java/org/openoffice/xmerge/merger/merge')
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> |