1 package org.daisy.pipeline.nlp.calabash.impl;
2
3 import java.util.HashSet;
4 import java.util.List;
5 import java.util.Locale;
6 import java.util.Map;
7 import java.util.Set;
8
9 import net.sf.saxon.om.NodeInfo;
10 import net.sf.saxon.s9api.Axis;
11 import net.sf.saxon.s9api.QName;
12 import net.sf.saxon.s9api.XdmNode;
13 import net.sf.saxon.s9api.XdmNodeKind;
14 import net.sf.saxon.s9api.XdmSequenceIterator;
15
16 import org.daisy.pipeline.nlp.LangDetector;
17 import org.daisy.pipeline.nlp.calabash.impl.StringComposer.SentencePointer;
18 import org.daisy.pipeline.nlp.calabash.impl.StringComposer.TextPointer;
19 import org.daisy.pipeline.nlp.lexing.LexService.LexerInitException;
20 import org.daisy.pipeline.nlp.lexing.LexService.LexerToken;
21 import org.daisy.pipeline.nlp.lexing.LexService.Sentence;
22
23 import com.xmlcalabash.util.TreeWriter;
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46 public class XmlBreakRebuilder implements InlineSectionProcessor {
47
48 private TreeWriter mTreeWriter;
49 private Map<Locale, LexerToken> mLexers;
50 private StringComposer mStringComposer;
51 private FormatSpecifications mSpecs;
52 private XdmNode mPreviousNode;
53 private int mPreviousLevel;
54 private DuplicationManager mDuplicationManager;
55 private String mCurrentLang;
56 private LangDetector mLangDetector;
57 private List<String> mParsingErrors;
58
59 public XdmNode rebuild(TreeWriterFactory treeWriterFactory,
60 Map<Locale, LexerToken> lexers, XdmNode doc, FormatSpecifications specs,
61 LangDetector langDetector, boolean forbidAnyDup, List<String> parsingErrors)
62 throws LexerInitException {
63 mLexers = lexers;
64 mSpecs = specs;
65 mLangDetector = langDetector;
66 mStringComposer = new StringComposer();
67 mPreviousNode = getRoot(doc);
68 mPreviousLevel = 0;
69 mParsingErrors = parsingErrors;
70
71 mDuplicationManager = new DuplicationManager(forbidAnyDup);
72 Set<NodeInfo> unsplittable = new HashSet<NodeInfo>();
73 List<NodeInfo> duplicated;
74
75 do {
76 mDuplicationManager.onNewDocument();
77 mTreeWriter = treeWriterFactory.newInstance();
78 mTreeWriter.startDocument(doc.getBaseURI());
79 XdmSequenceIterator iter = doc.axisIterator(Axis.CHILD);
80 while (iter.hasNext()) {
81 XdmNode n = (XdmNode) iter.next();
82 if (n.getNodeKind() == XdmNodeKind.ELEMENT) {
83
84 XdmNode root = n;
85 mPreviousNode = root;
86 mTreeWriter.addStartElement(root);
87 mTreeWriter.addAttributes(root);
88 if (!"".equals(mSpecs.wordTag.getPrefix()))
89 mTreeWriter.addNamespace(mSpecs.wordTag.getPrefix(),
90 mSpecs.wordTag.getNamespaceURI());
91 if (!"".equals(mSpecs.sentenceTag.getPrefix()))
92 mTreeWriter.addNamespace(mSpecs.sentenceTag.getPrefix(),
93 mSpecs.sentenceTag.getNamespaceURI());
94
95 new InlineSectionFinder().find(root, mPreviousLevel, specs, this,
96 unsplittable);
97 closeAllElementsUntil(root, 0);
98 break;
99
100 } else {
101 mTreeWriter.addSubtree(n);
102 }
103 }
104 mTreeWriter.endDocument();
105
106 duplicated = mDuplicationManager.getDuplicatedNodes();
107
108 unsplittable.addAll(duplicated);
109
110 } while (duplicated.size() > 0);
111
112 XdmNode result = mTreeWriter.getResult();
113
114 return result;
115 }
116
117 public static int getDuplicatedIDs(Map<String, Integer> ref, Map<String, Integer> actual,
118 Set<String> unsplittable) {
119 int res = 0;
120 for (Map.Entry<String, Integer> e : ref.entrySet()) {
121 if (e.getValue() == 1 && !(Integer.valueOf(1).equals(actual.get(e.getKey())))) {
122 unsplittable.add(e.getKey());
123 ++res;
124 }
125 }
126
127 return res;
128 }
129
130 private void addOneWord(TextPointer word, List<Leaf> leaves, List<String> text) {
131 Leaf wordParent = deepestAncestorOf(leaves, word.firstSegment, word.lastSegment);
132 if (wordParent.formatting == null)
133 return;
134
135 startLexingElement(wordParent, mSpecs.wordTag);
136 if (word.firstSegment == word.lastSegment) {
137 writeTree(leaves.get(word.firstSegment), text.get(word.firstSegment).substring(
138 word.firstIndex, word.lastIndex));
139 } else {
140 writeTree(leaves.get(word.firstSegment), text.get(word.firstSegment).substring(
141 word.firstIndex));
142 for (int i = word.firstSegment + 1; i < word.lastSegment; ++i)
143 writeTree(leaves.get(i), text.get(i));
144 writeTree(leaves.get(word.lastSegment), text.get(word.lastSegment).substring(0,
145 word.lastIndex));
146 }
147 closeAllElementsUntil(wordParent.formatting, 1);
148 }
149
150 @Override
151 public void onInlineSectionFound(List<Leaf> leaves, List<String> text, Locale lang)
152 throws LexerInitException {
153
154 Locale expectedLang = lang;
155 lang = mLangDetector.findLang(lang, text);
156 if (lang != null && expectedLang != null) {
157 if (expectedLang.getLanguage().equals(lang.getLanguage()))
158 mCurrentLang = null;
159 else
160 mCurrentLang = lang.getISO3Language();
161 } else
162 mCurrentLang = null;
163
164 LexerToken lexer = mLexers.get(lang);
165 if (lexer == null) {
166 lexer = mLexers.get(null);
167 }
168
169 String input = mStringComposer.concat(text);
170 List<Sentence> sentences = lexer.split(input, lang, mParsingErrors);
171
172 boolean[] isLexMark = new boolean[leaves.size()];
173 for (int k = 0; k < isLexMark.length; ++k)
174 isLexMark[k] = (leaves.get(k).formatting == null);
175 List<SentencePointer> pointers = mStringComposer.makePointers(sentences, text,
176 isLexMark);
177
178 mDuplicationManager.onNewSection();
179
180 int sSegBoundary = -1;
181 int sIndexBoundary = -1;
182 for (SentencePointer sp : pointers) {
183
184 fillGap(sSegBoundary, sIndexBoundary, sp.boundaries.firstSegment,
185 sp.boundaries.firstIndex, leaves, text);
186
187 sSegBoundary = sp.boundaries.lastSegment;
188 sIndexBoundary = sp.boundaries.lastIndex;
189 Leaf sentenceParent = deepestAncestorOf(leaves, sp.boundaries.firstSegment,
190 sp.boundaries.lastSegment);
191
192 if (sentenceParent.formatting == null) {
193
194
195 continue;
196 }
197
198 startLexingElement(sentenceParent, mSpecs.sentenceTag);
199 int wSegBoundary = sp.boundaries.firstSegment;
200 int wIndexBoundary = sp.boundaries.firstIndex;
201 if (sp.content != null)
202 for (TextPointer w : sp.content) {
203
204 fillGap(wSegBoundary, wIndexBoundary, w.firstSegment, w.firstIndex,
205 leaves, text);
206 wSegBoundary = w.lastSegment;
207 wIndexBoundary = w.lastIndex;
208 addOneWord(w, leaves, text);
209 }
210
211 fillGap(wSegBoundary, wIndexBoundary, sp.boundaries.lastSegment,
212 sp.boundaries.lastIndex, leaves, text);
213
214 closeAllElementsUntil(sentenceParent.formatting, 1);
215
216 }
217
218 fillGap(sSegBoundary, sIndexBoundary, -1, -1, leaves, text);
219 mCurrentLang = null;
220 }
221
222 @Override
223 public void onEmptySectionFound(List<Leaf> leaves) {
224 mDuplicationManager.onNewSection();
225 for (int i = 0; i < leaves.size(); ++i) {
226 writeTree(leaves.get(i), null);
227 }
228 }
229
230 private void addNode(XdmNode node, int level) {
231 switch (node.getNodeKind()) {
232 case ELEMENT:
233 mDuplicationManager.onNewNode(node, level);
234 mTreeWriter.addStartElement(node);
235 mTreeWriter.addAttributes(node);
236 mPreviousLevel = level;
237 mPreviousNode = node;
238 break;
239 case COMMENT:
240 mTreeWriter.addComment(node.getStringValue());
241 mPreviousLevel = level - 1;
242 mPreviousNode = node.getParent();
243 break;
244 case PROCESSING_INSTRUCTION:
245 mTreeWriter.addPI(node.getNodeName().getClarkName(), node.getStringValue());
246 mPreviousLevel = level - 1;
247 mPreviousNode = node.getParent();
248 break;
249 default:
250 mTreeWriter.addSubtree(node);
251 mPreviousLevel = level - 1;
252 mPreviousNode = node.getParent();
253 break;
254 }
255 }
256
257 private void startLexingElement(Leaf wordOrsentenceParent, QName elementToWrite) {
258 XdmNode[] path = new XdmNode[wordOrsentenceParent.level + 1];
259
260
261
262 XdmNode sentenceAncestor = wordOrsentenceParent.formatting;
263 XdmNode lastNodeAncestor = mPreviousNode;
264 int minLevel = Math.min(wordOrsentenceParent.level, mPreviousLevel);
265 int topLevel = wordOrsentenceParent.level;
266 for (; topLevel > minLevel; --topLevel) {
267 path[topLevel] = sentenceAncestor;
268 sentenceAncestor = sentenceAncestor.getParent();
269 }
270 for (int lastLevel = mPreviousLevel; lastLevel > minLevel; --lastLevel)
271 lastNodeAncestor = lastNodeAncestor.getParent();
272
273 for (; !same(sentenceAncestor, lastNodeAncestor); --topLevel) {
274 path[topLevel] = sentenceAncestor;
275 sentenceAncestor = sentenceAncestor.getParent();
276 lastNodeAncestor = lastNodeAncestor.getParent();
277 }
278
279
280
281 while (!same(mPreviousNode, lastNodeAncestor)) {
282 mTreeWriter.addEndElement();
283 mPreviousNode = mPreviousNode.getParent();
284 }
285
286
287 for (int l = topLevel + 1; l <= wordOrsentenceParent.level; ++l) {
288 XdmNode n = path[l];
289 addNode(n, l);
290 }
291
292
293 mTreeWriter.addStartElement(elementToWrite);
294 if (mCurrentLang != null) {
295
296
297 mTreeWriter.addAttribute(mSpecs.langAttr, mCurrentLang);
298 }
299 mPreviousLevel = wordOrsentenceParent.level;
300 }
301
302 private void writeTree(Leaf leaf, String text) {
303 if (text != null && text.isEmpty())
304 return;
305
306 if (leaf.formatting == null)
307 return;
308
309 XdmNode[] leafPath = new XdmNode[leaf.level + 1];
310
311
312
313 XdmNode leafAncestor = leaf.formatting;
314 XdmNode lastNodeAncestor = mPreviousNode;
315 int minLevel = Math.min(leaf.level, mPreviousLevel);
316 int topLevel = leaf.level;
317 for (; topLevel > minLevel; --topLevel) {
318 leafPath[topLevel] = leafAncestor;
319 leafAncestor = leafAncestor.getParent();
320 }
321 for (int lastLevel = mPreviousLevel; lastLevel > minLevel; --lastLevel)
322 lastNodeAncestor = lastNodeAncestor.getParent();
323
324 for (; !same(leafAncestor, lastNodeAncestor); --topLevel) {
325 leafPath[topLevel] = leafAncestor;
326 leafAncestor = leafAncestor.getParent();
327 lastNodeAncestor = lastNodeAncestor.getParent();
328 }
329
330
331
332 while (!same(mPreviousNode, lastNodeAncestor)) {
333 mTreeWriter.addEndElement();
334 mPreviousNode = mPreviousNode.getParent();
335 }
336
337
338 for (int l = topLevel + 1; l <= leaf.level; ++l) {
339 XdmNode n = leafPath[l];
340 addNode(n, l);
341 }
342
343 if (text != null) {
344 mTreeWriter.addText(text);
345 mPreviousLevel = leaf.level;
346 }
347
348 }
349
350
351
352
353
354 private void closeAllElementsUntil(XdmNode dest, int extra) {
355 while (!same(mPreviousNode, dest)) {
356 mTreeWriter.addEndElement();
357 mPreviousNode = mPreviousNode.getParent();
358 --mPreviousLevel;
359 }
360 for (; extra > 0; --extra) {
361 mTreeWriter.addEndElement();
362 }
363 }
364
365 private void fillGap(int leftSegment, int leftIndex, int rightSegment, int rightIndex,
366 List<Leaf> leaves, List<String> text) {
367 if (leftSegment == -1) {
368 for (leftSegment = 0; leftSegment < text.size() && text.get(leftSegment) == null; ++leftSegment) {
369 writeTree(leaves.get(leftSegment), null);
370 }
371 leftIndex = 0;
372 }
373
374 int formerRightSegment = rightSegment;
375 if (rightSegment == -1) {
376 rightSegment = text.size() - 1;
377 formerRightSegment = rightSegment;
378 for (; rightSegment >= 0 && text.get(rightSegment) == null; --rightSegment);
379 if (rightSegment >= 0) {
380 rightIndex = text.get(rightSegment).length();
381 }
382 }
383
384 if (leftSegment < rightSegment) {
385 writeTree(leaves.get(leftSegment), text.get(leftSegment).substring(leftIndex));
386
387 for (int k = leftSegment + 1; k < rightSegment; ++k)
388 writeTree(leaves.get(k), text.get(k));
389
390 writeTree(leaves.get(rightSegment), text.get(rightSegment)
391 .substring(0, rightIndex));
392 } else if (leftSegment == rightSegment && rightIndex > leftIndex) {
393 writeTree(leaves.get(leftSegment), text.get(leftSegment).substring(leftIndex,
394 rightIndex));
395 }
396
397 for (++rightSegment; rightSegment <= formerRightSegment; ++rightSegment) {
398 writeTree(leaves.get(rightSegment), null);
399 }
400 }
401
402
403 private Leaf deepestAncestorOf(List<Leaf> leaves, int from, int to) {
404 if (from == to) {
405 return leaves.get(from);
406 }
407
408 int minLevel = Integer.MAX_VALUE;
409 for (int k = from; k <= to; ++k)
410 if (leaves.get(k).level < minLevel && leaves.get(k).formatting != null)
411 minLevel = leaves.get(k).level;
412
413 XdmNode[] all = new XdmNode[leaves.size()];
414 int size = 0;
415 for (int i = from; i <= to; ++i) {
416 all[size] = leaves.get(i).formatting;
417 if (all[size] != null) {
418 for (int k = leaves.get(i).level - minLevel; k > 0; --k)
419 all[size] = all[size].getParent();
420 ++size;
421 }
422 }
423
424 while (true) {
425 int i = 1;
426 for (; i < size && same(all[i], all[0]); ++i);
427 if (i >= size) {
428
429 break;
430 }
431 for (i = 0; i < size; ++i)
432 all[i] = all[i].getParent();
433 --minLevel;
434 }
435
436 Leaf result = new Leaf();
437 result.formatting = all[0];
438 result.level = minLevel;
439
440 return result;
441 }
442
443 private static XdmNode getRoot(XdmNode node) {
444 XdmSequenceIterator iter = node.axisIterator(Axis.CHILD);
445 while (iter.hasNext()) {
446 XdmNode n = (XdmNode) iter.next();
447 if (n.getNodeKind() == XdmNodeKind.ELEMENT)
448 return n;
449 }
450 return null;
451 }
452
453 private static boolean same(XdmNode n1, XdmNode n2) {
454 return ((n1 == null && n2 == null) || (n1 != null && n2 != null && n1
455 .getUnderlyingNode().isSameNodeInfo(n2.getUnderlyingNode())));
456 }
457 }