blob: 97e90b72c0936f4418eb9b356e0df3eeaaf9c71d [file] [log] [blame]
/**************************************************************
*
* 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
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the 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());
}
}
}
}
}