LibreOffice Module xmerge (master) 1
CharacterBaseParagraphMerge.java
Go to the documentation of this file.
1/*
2 * This file is part of the LibreOffice project.
3 *
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 *
8 * This file incorporates work covered by the following license notice:
9 *
10 * Licensed to the Apache Software Foundation (ASF) under one or more
11 * contributor license agreements. See the NOTICE file distributed
12 * with this work for additional information regarding copyright
13 * ownership. The ASF licenses this file to you under the Apache
14 * License, Version 2.0 (the "License"); you may not use this file
15 * except in compliance with the License. You may obtain a copy of
16 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
17 */
18
19package org.openoffice.xmerge.merger.merge;
20
21import java.util.List;
22import org.w3c.dom.Node;
29
36public final class CharacterBaseParagraphMerge implements NodeMergeAlgorithm {
37
47 public void merge(Node orgPara, Node modPara) {
48 CharacterParser orgParser = new CharacterParser(orgPara);
49 CharacterParser modParser = new CharacterParser(modPara);
50
51 char[] orgCharArray = orgParser.getCharArray();
52 char[] modCharArray = modParser.getCharArray();
53
55
56 Difference[] diffResult = diffAlgo.computeDiffs(orgCharArray,
57 modCharArray);
58 // debug use
59 System.out.println("Diff Result: ");
60 for (int i = 0; i < diffResult.length; i++) {
61 Debug.log(Debug.INFO, diffResult[i].debug());
62 }
63
64 applyDifference(orgParser, modParser, diffResult);
65 }
66
67 private void applyDifference(CharacterParser orgParser,
68 CharacterParser modParser,
69 Difference[] diffs) {
70
71 List<TextNodeEntry> orgNodeList = orgParser.getNodeList();
72 int diffCount = 0;
73 int numNode = orgNodeList.size();
74
75 for (int i = 0; i < numNode; i++) {
76
77 int extraChar = 0;
78 int orgDiffCount = diffCount;
79 TextNodeEntry orgTextNode = orgNodeList.get(i);
80
81 Debug.log(Debug.INFO, "checking node " + (i + 1) + " of " + numNode);
82
83 // check any difference in this node and estimate the new char num
84 for (; diffCount < diffs.length; diffCount++) {
85
86 Debug.log(Debug.INFO, " checking diff " + (diffCount + 1) +
87 " of " + diffs.length);
88 Debug.log(Debug.INFO, " OrgPosision <" +
89 diffs[diffCount].getOrgPosition() + "> diffCount <" +
90 diffCount + "> orgDiffCount <" + orgDiffCount + ">");
91
92 // don't need to check and diffs beyond the current node text
93 // range except the last node
94 if (diffs[diffCount].getOrgPosition() > orgTextNode.endChar() &&
95 i < numNode - 1) {
96 Debug.log(Debug.INFO, " breaking!");
97 break;
98 }
99
100 if (diffs[diffCount].getOrgPosition()
101 >= orgTextNode.startChar()) {
102 if (diffs[diffCount].getOperation() == Difference.DELETE) {
103 extraChar--;
104 } else if (diffs[diffCount].getOperation()
105 == Difference.ADD) {
106 extraChar++;
107 }
108
109 }
110 }
111
112 Debug.log(Debug.INFO, " final diffCount <" + diffCount +
113 "> final orgDiffCount <" + orgDiffCount + ">");
114
115 // will only try to merge if there is a difference in this node
116 if (diffCount > orgDiffCount) {
117
118 Debug.log(Debug.INFO, " There is a difference, doing merge");
119 Debug.log(Debug.INFO, " TextNode name <" +
120 orgTextNode.node().getNodeName() + ">");
121 Debug.log(Debug.INFO, " TextNode value <" +
122 orgTextNode.node().getNodeValue() + ">");
123 Debug.log(Debug.INFO, " TextNode start char <" +
124 orgTextNode.startChar() + "> TextNode end char <" +
125 orgTextNode.endChar() + ">");
126 Debug.log(Debug.INFO, " extraChar value <" + extraChar + ">");
127
128 coreMerge(orgDiffCount, diffCount, diffs,
129 modParser, orgTextNode, extraChar);
130 }
131 }
132 }
133
134 private void coreMerge(int startDiffNum, int endDiffNum, Difference[] diffs,
135 CharacterParser modParser,
136 TextNodeEntry orgTextNode, int extraChar) {
137
138 Node orgNode = orgTextNode.node();
139 char[] modTextArray = modParser.getCharArray();
140 String tmpString;
141
142 // Handle situation where getNodeValue returns null
143 if (orgNode.getNodeValue() != null)
144 tmpString = orgNode.getNodeValue();
145 else
146 tmpString = "";
147
148 char[] orgNodeText = tmpString.toCharArray();
149 char[] newNodeText;
150
151 if (orgNodeText.length + extraChar > 0)
152 newNodeText = new char[orgNodeText.length + extraChar];
153 else
154 newNodeText = new char[0];
155
156 int orgTextPosition = orgTextNode.startChar(); // used for block copy
157 int newTextPosition = 0; // used for block copy
158 int unChangedTextLength = 0;
159
160 char[] cacheCharArray = new char[endDiffNum - startDiffNum];
161 int cacheLength = 0;
162 int lastDiffOperation = Difference.UNCHANGE;
163 int lastDiffPosition = -1;
164
165 // starting to diff
166 for (int j = startDiffNum; j < endDiffNum; j++) {
167
168 // copy any contents before the diff
169 if (diffs[j].getOrgPosition() > orgTextPosition) {
170 // need to flush first
171 if (cacheLength > 0) {
172 System.arraycopy(cacheCharArray, 0,
173 newNodeText, newTextPosition, cacheLength);
174 newTextPosition += cacheLength;
175
176 // reset the markers
177 lastDiffPosition = -1;
178 lastDiffOperation = Difference.UNCHANGE;
179 cacheLength = 0;
180 }
181
182 // find out the length how many characters are
183 // untouched by the diff
184 unChangedTextLength = diffs[j].getOrgPosition() -
185 orgTextPosition;
186 System.arraycopy(orgNodeText,
187 orgTextPosition - orgTextNode.startChar(),
188 newNodeText, newTextPosition,
189 unChangedTextLength);
190 orgTextPosition += unChangedTextLength;
191 newTextPosition += unChangedTextLength;
192 }
193
194 // for any deleted characters, just skip without copy
195 // but still need to take care the cached characters
196 if (diffs[j].getOperation() == Difference.DELETE) {
197 orgTextPosition++;
198
199 // flush out the cache and copy the content to new Text
200 if (cacheLength > 0) {
201 System.arraycopy(cacheCharArray, 0,
202 newNodeText, newTextPosition, cacheLength);
203 newTextPosition += cacheLength;
204
205 // reset the markers
206 lastDiffPosition = -1;
207 lastDiffOperation = Difference.UNCHANGE;
208 cacheLength = 0;
209 }
210
211 continue;
212
213 // check whether we should flush the cache.
214 // For changed diffs, only continuous changes can be cached
215 // For Add diffs, only same insertion point can be cached
216 // and for both changed/add diffs, need to have same operation
217 // as last cached diffs.
218
219 } else {
220 if (lastDiffOperation != diffs[j].getOperation() ||
221 (diffs[j].getOperation() == Difference.CHANGE &&
222 diffs[j].getOrgPosition() != lastDiffPosition + 1) ||
223 (diffs[j].getOperation() == Difference.ADD &&
224 diffs[j].getOrgPosition() != lastDiffPosition)) {
225
226 // flush the cache
227 if (cacheLength > 0) {
228 System.arraycopy(cacheCharArray, 0, newNodeText,
229 newTextPosition, cacheLength);
230 newTextPosition += cacheLength;
231
232 // reset the markers
233 cacheLength = 0;
234 }
235 }
236
237 // add the diffs to the cache, now the diffs will be either
238 // a new 'changed' char or is an adjacent following change of
239 // last difference
240 cacheCharArray[cacheLength] =
241 modTextArray[diffs[j].getModPosition()];
242 cacheLength++;
243 lastDiffOperation = diffs[j].getOperation();
244 lastDiffPosition = diffs[j].getOrgPosition();
245
246 // need to increment the original text position
247 // after we cached it
248 if (lastDiffOperation == Difference.CHANGE) {
249 orgTextPosition++;
250 }
251 }
252 }
253
254 // flush any contents remaining in the cache
255 if (cacheLength > 0) {
256 System.arraycopy(cacheCharArray, 0, newNodeText,
257 newTextPosition, cacheLength);
258 newTextPosition += cacheLength;
259 // no need to reset any cache-related info as this is a last flush
260 }
261
262 // copy any contents after all the diffs
263 int orgStartPosition = orgTextNode.startChar();
264 if (orgNodeText.length + orgStartPosition > orgTextPosition) {
265 unChangedTextLength = orgNodeText.length + orgStartPosition
266 - orgTextPosition;
267 System.arraycopy(orgNodeText, orgTextPosition - orgStartPosition,
268 newNodeText, newTextPosition,
269 unChangedTextLength);
270 }
271
272 // set the text to the original node if there are any diffs processed.
273 // can't use newNodeText.length to check as even it is empty, we may
274 // process a whole bunch of deletion already (i.e. the whole
275 // orgNodeText deleted).
276 if (endDiffNum > startDiffNum) {
277 String newString = new String(newNodeText);
278 orgNode.setNodeValue(newString);
279 }
280 }
281}
This is the Difference basic unit.
Definition: Difference.java:27
int getOrgPosition()
Get the original Iterator position.
String debug()
Display debug information.
static final int CHANGE
Change operation.
Definition: Difference.java:36
int getModPosition()
Get the modified Iterator position.
static final int DELETE
Delete operation.
Definition: Difference.java:33
static final int UNCHANGE
Unchange operation (i.e.
Definition: Difference.java:39
int getOperation()
Get the operation of the Difference.
static final int ADD
Add operation.
Definition: Difference.java:30
This is an implementations of DiffAlgorithm interface which will difference char arrays.
Difference[] computeDiffs(char[] orgSeq, char[] modSeq)
Return an Difference array.
This is a parser to return a character array for difference purpose.
char[] getCharArray()
Returns the character array representation of the text.
List< TextNodeEntry > getNodeList()
Returns the Node pointer with the given character position.
A small class to hold the start/end character position and the Node pointer in a text Node.
int startChar()
Returns the start character.
This is an implementation of the NodeMergeAlgorithm interface.
void merge(Node orgPara, Node modPara)
Merge two paragraphs Node by using Longest Common Subsequence (LCS) character algorithm defined in Ch...
void applyDifference(CharacterParser orgParser, CharacterParser modParser, Difference[] diffs)
void coreMerge(int startDiffNum, int endDiffNum, Difference[] diffs, CharacterParser modParser, TextNodeEntry orgTextNode, int extraChar)
This class is used for logging debug messages.
Definition: Debug.java:39
static final int INFO
Informational messages.
Definition: Debug.java:42
static void log(int flag, String msg)
Log message based on the flag type.
Definition: Debug.java:205
This is an interface for a MergeAlgorithm to merge two Node objects.
int i
Provides implementations for the Iterator interface and related support classes.
The DiffAlgorithm and MergeAlgorithm are used to provide the merge capabilities of this project.
Provides general purpose utilities.
Provides interfaces for converting between two Document formats, and supports a "merge" interface for...
Definition: Convert.java:19