1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 package org.openoffice.xmerge.merger.merge;
25 
26 import org.w3c.dom.Node;
27 import org.w3c.dom.Element;
28 import org.w3c.dom.NodeList;
29 import org.w3c.dom.NamedNodeMap;
30 
31 import org.openoffice.xmerge.ConverterCapabilities;
32 import org.openoffice.xmerge.merger.Iterator;
33 import org.openoffice.xmerge.merger.NodeMergeAlgorithm;
34 import org.openoffice.xmerge.merger.diff.CellNodeIterator;
35 import org.openoffice.xmerge.converter.xml.OfficeConstants;
36 import org.openoffice.xmerge.util.XmlUtil;
37 
38 
39 /**
40  *  This is an implementation of the <code>NodeMergeAlgorithm</code>
41  *  interface.  It is used to merge two rows using a positional
42  *  comparison base method.
43  */
44 public final class PositionBaseRowMerge implements NodeMergeAlgorithm {
45 
46     /**  The capabilities of this converter. */
47     private ConverterCapabilities cc_;
48 
49 
50     /**
51      *  Constructor.
52      *
53      *  @param  cc  The <code>ConverterCapabilities</code>.
54      */
PositionBaseRowMerge(ConverterCapabilities cc)55     public PositionBaseRowMerge(ConverterCapabilities cc) {
56         cc_ = cc;
57     }
58 
59 
merge(Node orgRow, Node modRow)60     public void merge(Node orgRow, Node modRow) {
61 
62         Iterator orgCells = new CellNodeIterator(cc_, orgRow);
63         Iterator modCells = new CellNodeIterator(cc_, modRow);
64 
65         mergeCellSequences(orgCells, modCells);
66     }
67 
68 
69     // used to compare the cell 1 by 1
mergeCellSequences(Iterator orgSeq, Iterator modSeq)70     private void mergeCellSequences(Iterator orgSeq, Iterator modSeq) {
71 
72         boolean needMerge = true;
73         Element orgCell, modCell;
74 
75         Object orgSeqObject = orgSeq.start();
76         Object modSeqObject = modSeq.start();
77 
78         while (orgSeqObject != null) {
79 
80 
81             needMerge = true;
82 
83             if (modSeqObject ==  null)  {
84                 // no corresponding cell in the target, empty out the cell
85                 SheetUtil.emptyCell(cc_, (Node)orgSeqObject);
86                 orgSeqObject = orgSeq.next();
87 
88             } else {
89 
90                 // compare the cell directly
91                 if (!orgSeq.equivalent(orgSeqObject, modSeqObject)) {
92 
93                     orgCell = (Element)orgSeqObject;
94                     modCell = (Element)modSeqObject;
95 
96                     // check whether the original cell with multiple column
97                     // if so, need to split one out for merge
98                     String orgColRepeated = orgCell.getAttribute(
99                         OfficeConstants.ATTRIBUTE_TABLE_NUM_COLUMNS_REPEATED);
100                     String modColRepeated = modCell.getAttribute(
101                         OfficeConstants.ATTRIBUTE_TABLE_NUM_COLUMNS_REPEATED);
102 
103                     int orgColNum = 1;
104                     int modColNum = 1;
105 
106                     if (orgColRepeated.length() > 0) {
107                         orgColNum =
108                             Integer.valueOf(orgColRepeated).intValue();
109                     }
110                     if (modColRepeated.length() > 0) {
111                         modColNum =
112                             Integer.valueOf(modColRepeated).intValue();
113                     }
114 
115                     // try to find out the common number of repeated cols
116                     if (orgColNum == modColNum) {
117                         orgSeqObject = orgSeq.next();
118                         modSeqObject = modSeq.next();
119 
120                     // cut the original cell into 2 half, first half
121                     // have the repeated attribute = modify cell attr
122                     } else if (orgColNum > modColNum) {
123                         Element orgSplitCell = splitColRepeatedCell(
124                                     orgCell, modColNum,
125                                     orgColNum - modColNum);
126                         // it may equal after the split!
127                         if (orgSeq.equivalent(orgSplitCell, modCell)) {
128                             needMerge = false;
129                         }
130                         orgCell = orgSplitCell;
131                         modSeqObject = modSeq.next();
132 
133                     // cut the modified cell into 2 half, first half
134                     // have the repeated attribute = original cell attr
135                     } else {
136                         Element modSplitCell = splitColRepeatedCell(
137                                     modCell, orgColNum,
138                                     modColNum - orgColNum);
139                         // it may equal after the split!
140                         if (modSeq.equivalent(orgCell, modSplitCell)) {
141                             needMerge = false;
142                         }
143                         modCell = modSplitCell;
144                         orgSeqObject = orgSeq.next();
145                     }
146 
147                     if (needMerge) {
148                         mergeCells(orgCell, modCell);
149                     }
150 
151                 } else {
152                     // cells are equivalent, move on to next one.
153                     orgSeqObject = orgSeq.next();
154                     modSeqObject = modSeq.next();
155                 } // end if-else
156             } // end if-else
157         } // end while loop
158 
159         // get the one of the original cell, so that the cloned node
160         // can base it to find the document node
161         orgCell = (Element)orgSeq.start();
162 
163         // add any extra cells to the original cell sequence.
164         for (; modSeqObject != null; modSeqObject = modSeq.next()) {
165             Node clonedNode = XmlUtil.deepClone(orgCell, (Node)modSeqObject);
166             Node parent = orgCell.getParentNode();
167             parent.appendChild(clonedNode);
168         }
169     }
170 
171 
splitColRepeatedCell(Element orgCell, int splitNum, int orgNum)172     private Element splitColRepeatedCell(Element orgCell,
173                                          int splitNum, int orgNum) {
174         // NOTE: should we really want to do deep clone?
175         // in most the case, it is an empty cell, but the
176         // specification didn't forbid any node to use multiple
177         // column attributes. i.e. the node can contain text
178         // nodes or other things under it.
179         Element splitCell = (Element)(orgCell.cloneNode(true));
180 
181         if (splitNum > 1) {
182             splitCell.setAttribute(
183               OfficeConstants.ATTRIBUTE_TABLE_NUM_COLUMNS_REPEATED,
184               String.valueOf(splitNum));
185         } else if (splitNum == 1) {
186             splitCell.removeAttribute(
187               OfficeConstants.ATTRIBUTE_TABLE_NUM_COLUMNS_REPEATED);
188         }
189         if (orgNum > 1) {
190             orgCell.setAttribute(
191               OfficeConstants.ATTRIBUTE_TABLE_NUM_COLUMNS_REPEATED,
192               String.valueOf(orgNum));
193         } else if (orgNum == 1) {
194             orgCell.removeAttribute(
195               OfficeConstants.ATTRIBUTE_TABLE_NUM_COLUMNS_REPEATED);
196         }
197 
198         Node parentNode = orgCell.getParentNode();
199         parentNode.insertBefore(splitCell, orgCell);
200 
201         return splitCell;
202     }
203 
204 
mergeCells(Element orgCell, Element modCell)205     private void mergeCells(Element orgCell, Element modCell) {
206 
207         // remove all the supported attributes and possible text child for
208         // string cells
209         SheetUtil.emptyCell(cc_, orgCell);
210 
211         // copy all the supported attributes and possible text child from
212         // the modified cell
213         NamedNodeMap attrNodes = modCell.getAttributes();
214 
215         if (attrNodes != null) {
216 
217             // copy the first text:p node. As it's not necessary only string
218             // type cell can have a text:p section.
219             NodeList paraNodes =
220                 modCell.getElementsByTagName(OfficeConstants.TAG_PARAGRAPH);
221 
222             Node firstParaNode = paraNodes.item(0);
223 
224             // try to clone the node
225             if (firstParaNode != null) {
226 
227                 Node clonedNode = XmlUtil.deepClone(orgCell, firstParaNode);
228 
229                 // insert as the first child of the original cell
230                 Node firstChild = orgCell.getFirstChild();
231                 if (firstChild != null) {
232                     orgCell.insertBefore(clonedNode, firstChild);
233                 } else {
234                     orgCell.appendChild(clonedNode);
235                 }
236             }
237 
238             // check all the attributes and copy those we supported in
239             // converter
240             // NOTE: for attribute list, refer to section 4.7.2 in specification
241             int len = attrNodes.getLength();
242 
243             for (int i = 0; i < len; i++) {
244                 Node attr = attrNodes.item(i);
245 
246                 // copy the supported attrs
247                 if (cc_.canConvertAttribute(OfficeConstants.TAG_TABLE_CELL,
248                                            attr.getNodeName())) {
249                     orgCell.setAttribute(attr.getNodeName(),
250                                          attr.getNodeValue());
251                 }
252             }
253         }
254     }
255 }
256 
257