Changeset 481
- Timestamp:
- 06/17/08 08:33:31 (4 years ago)
- Location:
- trunk/docx4j/src/main/java/org/docx4j/diff
- Files:
-
- 1 added
- 2 edited
-
ParagraphDifferencer.java (modified) (14 diffs)
-
diffx2html.xslt (added)
-
diffx2wml.xslt (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/docx4j/src/main/java/org/docx4j/diff/ParagraphDifferencer.java
r480 r481 39 39 import javax.xml.transform.stream.StreamSource; 40 40 41 import org.apache.log4j.Logger; 41 42 import org.docx4j.XmlUtils; 42 43 import org.docx4j.wml.P; 43 44 import org.eclipse.compare.internal.LCSSettings; 45 import org.eclipse.compare.rangedifferencer.IRangeComparator; 44 import org.docx4j.wml.R; 45 46 46 import org.eclipse.compare.rangedifferencer.RangeDifference; 47 47 import org.docx4j.diff.StringComparator; … … 63 63 * and add back in at end 64 64 * 65 * - encode real occurences of RUN_DELIMITER, and add back in at end65 * - write xml:space="preserve" to generate w:t as necessary 66 66 * 67 67 */ 68 68 69 public final static String RUN_DELIMITER = "|"; 69 protected static Logger log = Logger.getLogger(ParagraphDifferencer.class); 70 70 71 71 72 public static String author = "unknown"; … … 80 81 public static void main(String[] args) throws Exception { 81 82 82 83 /*String[] runtContents = "|".split("\\" + RUN_DELIMITER);84 System.out.println( "'|' " + runtContents.length );85 86 runtContents = " |".split("\\" + RUN_DELIMITER);87 System.out.println( "' |' " + runtContents.length );88 89 runtContents = "| ".split("\\" + RUN_DELIMITER);90 System.out.println( "'| ' " + runtContents.length );91 92 runtContents = " | ".split("\\" + RUN_DELIMITER);93 System.out.println( "' | ' " + runtContents.length );*/94 95 96 83 // Test setup 97 String paraL = ParagraphDifferencerTest.BASE_DIR + "t2R R";98 String paraR = ParagraphDifferencerTest.BASE_DIR + "t 3L";84 String paraL = ParagraphDifferencerTest.BASE_DIR + "t2R"; 85 String paraR = ParagraphDifferencerTest.BASE_DIR + "t4"; 99 86 P pl = loadParagraph(paraL); 100 87 P pr = loadParagraph(paraR); … … 141 128 * It will tell which of the w:t have been populated/deleted, and 142 129 * what formatting has changed on their w:r elements. 130 * 131 * In terms of actual performance (versus plain old diffx), the 132 * main case where the pre-processing helps: 133 * 134 * 1. t2R cf t3L 135 * 136 Left input 137 138 <w:p> 139 <w:r> 140 <w:t xml:space="preserve">The quick brown </w:t> 141 </w:r> 142 <w:r> 143 <w:rPr> 144 <w:b/> 145 <w:sz w:val="28"/> 146 <w:szCs w:val="28"/> 147 </w:rPr> 148 <w:t>fox</w:t> 149 </w:r> 150 <w:r> 151 <w:t xml:space="preserve"> jumped over the </w:t> 152 </w:r> 153 <w:r> 154 <w:rPr> 155 <w:u w:val="single"/> 156 </w:rPr> 157 <w:t>lazy</w:t> 158 </w:r> 159 <w:r> 160 <w:t xml:space="preserve"> dog.</w:t> 161 </w:r> 162 </w:p> 163 164 165 Right input 166 167 <w:p> 168 <w:r> 169 <w:t>The quick brown fox jumped high </w:t> 170 </w:r> 171 <w:r> 172 <w:t>high over the lazy dog.</w:t> 173 </w:r> 174 </w:p> 175 176 177 * 143 178 */ 144 179 145 String leftXmlOld = org.docx4j.XmlUtils.marshaltoString(pl, true); 146 String rightXmlOld = org.docx4j.XmlUtils.marshaltoString(pr, true); 147 148 System.out.println("\n\n Left input \n\n" ); 149 System.out.println(leftXmlOld) ; 150 151 // Get their string content 152 String pLeftText = collateRuns(pl); 153 System.out.println("\n\n" + pLeftText); 154 155 System.out.println("\n\n Right input \n\n" ); 156 System.out.println(rightXmlOld) ; 157 158 String pRightText = collateRuns(pr); 159 System.out.println("\n\n" + pRightText); 160 180 // debug only 181 String leftXmlOld = null; 182 String rightXmlOld = null; 183 if (log.isInfoEnabled() ) { 184 leftXmlOld = org.docx4j.XmlUtils.marshaltoString(pl, true, true); 185 rightXmlOld = org.docx4j.XmlUtils.marshaltoString(pr, true, true); 186 } 187 161 188 // Compute LCS 162 StringComparator left = new StringComparator(p LeftText);163 StringComparator right = new StringComparator(p RightText);189 StringComparator left = new StringComparator(pl.toString()); 190 StringComparator right = new StringComparator(pr.toString()); 164 191 org.eclipse.compare.internal.LCSSettings settings = new org.eclipse.compare.internal.LCSSettings(); 165 192 … … 167 194 168 195 // Debug Output 169 System.out.println("\n\n RangeDifferences \n\n"); 170 for (int x=0; x<rd.length; x++) { 171 System.out.println ( 172 toRangeString( left, rd[x].leftStart(), rd[x].leftLength(), true ) 173 + rd[x].kindString() 174 + toRangeString( right, rd[x].rightStart(), rd[x].rightLength(), true ) ); 175 } 196 if (log.isDebugEnabled()) { 197 log.debug("\n\n RangeDifferences \n\n"); 198 for (int x=0; x<rd.length; x++) { 199 log.debug ( 200 toRangeString( left, rd[x].leftStart(), rd[x].leftLength(), true ) 201 + rd[x].kindString() 202 + toRangeString( right, rd[x].rightStart(), rd[x].rightLength(), true ) ); 203 } 204 } 176 205 177 206 // Now build appropriate replacement paragraph content … … 179 208 List<Object> pRightReplacement = new ArrayList<Object>(); 180 209 181 // Which of the existingw:r we are up to210 // Which of the _existing_ w:r we are up to 182 211 int pLeftIndex = 0; 183 212 int pRightIndex = 0; 184 185 // We'll need to add this in certain places 186 org.docx4j.wml.R emptyStructure = wmlFactory.createR(); 187 org.docx4j.wml.Text newT = wmlFactory.createText(); 188 emptyStructure.getRunContent().add(newT); 189 213 214 int[] leftCounts = getParagraphRunTextWordCounts(pl); 215 216 // StringBuilder debug = new StringBuilder(); 217 // debug.append("{ "); 218 // for (int i=0; i < leftCounts.length; i++) { 219 // try { 220 // debug.append( leftCounts[i] + ", "); 221 // } catch (RuntimeException e) { 222 // } 223 // } 224 // System.out.println(debug); 225 226 int[] rightCounts = getParagraphRunTextWordCounts(pr); 227 228 int leftWordCounter = -1; 229 int rightWordCounter = -1; 190 230 191 231 for (int x=0; x<rd.length; x++) { … … 193 233 // The original runs are always longer than 194 234 // each rd 235 236 // We will definitely require a new run 237 // structure for each side 238 R currentLeftStructure = createRunStructure("", 239 pl, pLeftIndex ); 240 R currentRightStructure = createRunStructure("", 241 pr, pRightIndex ); 242 243 pLeftReplacement.add(currentLeftStructure); 244 pRightReplacement.add(currentRightStructure); 195 245 196 246 if (rd[x].kind() == RangeDifference.NOCHANGE) { 247 log.debug("NOCHANGE"); 197 248 // These are part of the string LCS, 198 249 // (though they might not be part of the 199 250 // XML LCS once we've added their rPr 200 // back in.) 251 // back in.) 252 // This is where we focus our efforts. 201 253 202 String[] runtContents = toRangeString( left, rd[x].leftStart(), rd[x].leftLength(), true ) 203 .split("\\" + RUN_DELIMITER); 204 205 206 for (int j=0; j<runtContents.length; j++) { 207 208 System.out.println("Processing '" + runtContents[j] + "'"); 209 210 if (j>0 ) { 211 /* 212 * '|' 0 <--- never happens 213 ' |' 1 <--- never happens 214 '| ' 2 215 ' | ' 2 216 * 217 * The 'true' above ensures that if RUN_DELIMITER is present, 218 * runtContents.length will always be 2 or greater, which 219 * in turn ensures these will get incremented 220 */ 221 222 // This is a boundary on both left and right objects 223 254 255 // Process the words in rd[x] one word at a time 256 for (int i=rd[x].leftStart(); // left and right are identical 257 i<(rd[x].leftStart()+rd[x].leftLength()); i++) { 258 259 // Our objective is to ensure that both the 260 // left and right paragraphs end up with 261 // matching w:r/w:t boundaries. 262 263 // So when either of the existing paragraphs 264 // contains a boundary, this need to be inserted 265 // in both results 266 267 String word = left.getLeaf(i); 268 269 leftWordCounter++; 270 rightWordCounter++; 271 272 // log.debug(word); 273 274 if ( leftWordCounter < sum(leftCounts, 0, pLeftIndex) 275 && rightWordCounter < sum(rightCounts, 0, pRightIndex) ) { 276 277 // it is ok to insert into current w:t 278 addWord(currentLeftStructure, word); 279 addWord(currentRightStructure, word); 280 281 } else { 282 283 // log.debug("Hit boundary"); 284 285 // which boundary have we hit? 286 if (leftWordCounter == sum(leftCounts, 0, pLeftIndex) 287 && rightWordCounter == sum(rightCounts, 0, pRightIndex) ) { 288 // Quite likely, for example, same formatting in each 289 290 // We're now on to each paragraph's next w:t 291 pLeftIndex++; 292 pRightIndex++; 293 294 } else if (leftWordCounter == sum(leftCounts, 0, pLeftIndex) ) { 295 296 // We're now on to the left paragraph's next w:t 297 pLeftIndex++; 298 299 } else { 300 301 // We're now on to the right paragraph's next w:t 302 pRightIndex++; 303 } 304 305 currentLeftStructure = createRunStructure(word, 306 pl, pLeftIndex ); 307 currentRightStructure = createRunStructure(word, 308 pr, pRightIndex ); 309 310 pLeftReplacement.add(currentLeftStructure); 311 pRightReplacement.add(currentRightStructure); 312 313 } 314 315 } 316 317 } else if (rd[x].kind() == RangeDifference.CHANGE) { 318 log.debug("CHANGE"); 319 // These aren't part of the string LCS, 320 // (so they shouldn't be part of 321 // the XML LCS) 322 323 // All we need to do is make sure that 324 // the input is round tripped. 325 326 // Left side: Process the words in rd[x] one word at a time 327 // NB, can't just copy existing runs into the output 328 log.debug(".. left side"); 329 for (int i=rd[x].leftStart(); 330 i<(rd[x].leftStart()+rd[x].leftLength()); i++) { 331 332 String word = left.getLeaf(i); 333 // log.debug(word); 334 leftWordCounter++; 335 336 if ( leftWordCounter < sum(leftCounts, 0, pLeftIndex) ) { 337 // it is ok to insert into left's current w:t 338 addWord(currentLeftStructure, word); 339 } else { 340 // boundary hit 224 341 // We're now on to the left paragraph's next w:t 225 342 pLeftIndex++; 226 343 currentLeftStructure = createRunStructure(word, 344 pl, pLeftIndex ); 345 pLeftReplacement.add(currentLeftStructure); 346 } 347 348 } 349 350 // Right side 351 log.debug(".. right side"); 352 for (int i=rd[x].rightStart(); 353 i<(rd[x].rightStart()+rd[x].rightLength()); i++) { 354 355 String word = right.getLeaf(i); 356 log.debug(word); 357 rightWordCounter++; 358 359 if ( rightWordCounter < sum(rightCounts, 0, pRightIndex) ) { 360 // it is ok to insert into right's current w:t 361 addWord(currentRightStructure, word); 362 } else { 363 // boundary hit 227 364 // We're now on to the right paragraph's next w:t 228 365 pRightIndex++; 229 230 } 231 232 if ("".equals(runtContents[j])) 233 continue; 234 235 // Normal case 236 org.docx4j.wml.R newLeftR = createRunStructure(runtContents[j], pl, pLeftIndex ); 237 pLeftReplacement.add( newLeftR ); 238 239 240 org.docx4j.wml.R newRightR = createRunStructure(runtContents[j], pr, pRightIndex ); 241 pRightReplacement.add( newRightR ); 242 243 } 244 245 } else if (rd[x].kind() == RangeDifference.CHANGE) { 246 // These aren't part of the string LCS, 247 // (so they are unlikely to be part of 248 // the XML LCS) 366 currentRightStructure = createRunStructure(word, 367 pr, pRightIndex ); 368 pRightReplacement.add(currentRightStructure); 369 } 370 } 249 371 250 /* Test case t2RR, t3L results in:251 *252 * parrot | jumped CHANGEhigh253 *254 */255 256 // Left hand side257 String[] runtContents = toRangeString( left, rd[x].leftStart(), rd[x].leftLength(), true )258 .split("\\" + RUN_DELIMITER);259 for (int j=0; j<runtContents.length; j++) {260 261 System.out.println("Processing '" + runtContents[j] + "'");262 263 if (j>0 ) {264 // We're now on to the left paragraph's next w:t265 pLeftIndex++;266 }267 268 if ("".equals(runtContents[j]))269 continue;270 271 // Normal case272 org.docx4j.wml.R newLeftR = createRunStructure(runtContents[j], pl, pLeftIndex );273 pLeftReplacement.add( newLeftR );274 275 }276 277 // Right hand side278 runtContents = toRangeString( right, rd[x].rightStart(), rd[x].rightLength(), true )279 .split("\\" + RUN_DELIMITER);280 for (int j=0; j<runtContents.length; j++) {281 282 System.out.println("Processing '" + runtContents[j] + "'");283 284 if (j>0 ) {285 // We're now on to the left paragraph's next w:t286 pRightIndex++;287 }288 289 if ("".equals(runtContents[j]))290 continue;291 292 // Normal case293 org.docx4j.wml.R newRightR = createRunStructure(runtContents[j], pr, pRightIndex );294 pRightReplacement.add( newRightR );295 296 }297 372 } 373 298 374 } 299 375 … … 307 383 newRightP.getParagraphContent().addAll(pRightReplacement); 308 384 309 System.out.println("\n\n New left side \n\n" ); 310 String leftXmlNew = org.docx4j.XmlUtils.marshaltoString(newLeftP, true); 311 System.out.println(leftXmlNew) ; 312 313 System.out.println("\n\n New right side \n\n" ); 314 String rightXmlNew = org.docx4j.XmlUtils.marshaltoString(newRightP, true); 315 System.out.println(rightXmlNew) ; 316 317 System.out.println("\n\n Difference \n\n" ); 385 log.debug("\n\n Left input \n\n" ); 386 log.debug(leftXmlOld) ; 387 388 log.debug("\n\n New left side \n\n" ); 389 String leftXmlNew = org.docx4j.XmlUtils.marshaltoString(newLeftP, true, true); 390 log.debug(leftXmlNew) ; 391 392 log.debug("\n\n Right input \n\n" ); 393 log.debug(rightXmlOld) ; 394 395 log.debug("\n\n New right side \n\n" ); 396 String rightXmlNew = org.docx4j.XmlUtils.marshaltoString(newRightP, true, true); 397 log.debug(rightXmlNew) ; 398 399 log.debug("\n\n Difference \n\n" ); 318 400 319 401 String diffx = getDiffxOutput(leftXmlNew, rightXmlNew); 320 402 //String diffx = getDiffxOutput(rightXmlNew, leftXmlNew); 321 System.out.println(diffx) ;403 log.debug(diffx) ; 322 404 323 405 // Debug purposes only! 324 System.out.println("\n\n Compare naive difference \n\n" );406 log.debug("\n\n Compare naive difference \n\n" ); 325 407 326 408 String naive = getDiffxOutput(leftXmlOld, rightXmlOld); 327 System.out.println(naive) ;328 329 330 System.out.println("\n\n WordML\n\n" );409 log.debug(naive) ; 410 411 412 log.info("\n\n <p> difference with pre-processing</p> \n\n" ); 331 413 try { 332 414 StreamSource src = new StreamSource(new StringReader(diffx)); 333 415 java.io.InputStream xslt = 334 org.docx4j.utils.ResourceUtils.getResource("org/docx4j/diff/diffx2wml.xslt"); 416 org.docx4j.utils.ResourceUtils.getResource("org/docx4j/diff/diffx2html.xslt"); 417 //org.docx4j.utils.ResourceUtils.getResource("org/docx4j/diff/diffx2wml.xslt"); 335 418 Map<String, Object> transformParameters = new java.util.HashMap<String, Object>(); 336 419 transformParameters.put("author", author); … … 340 423 exc.printStackTrace(); 341 424 } 342 343 System.out.println("\n\n Done!" ); 344 345 } 346 425 426 log.info("\n\n <p> difference without preprocessing </p> \n\n" ); 427 try { 428 StreamSource src = new StreamSource(new StringReader(naive)); 429 java.io.InputStream xslt = 430 org.docx4j.utils.ResourceUtils.getResource("org/docx4j/diff/diffx2html.xslt"); 431 //org.docx4j.utils.ResourceUtils.getResource("org/docx4j/diff/diffx2wml.xslt"); 432 Map<String, Object> transformParameters = new java.util.HashMap<String, Object>(); 433 transformParameters.put("author", author); 434 XmlUtils.transform(src, xslt, transformParameters, result); 435 436 } catch (Exception exc) { 437 exc.printStackTrace(); 438 } 439 440 log.debug("\n\n Done!" ); 441 442 } 443 444 private static int sum( int[] array, int idx1, int idx2) { 445 446 StringBuilder debug = new StringBuilder(); 447 448 debug.append("{ "); 449 450 int sum = 0; 451 452 for (int i=idx1; i <= idx2; i++) { 453 debug.append( array[i] + ", "); 454 455 sum+=array[i]; 456 } 457 debug.append("} = " + sum); 458 // System.out.println(debug); 459 return sum; 460 461 } 462 463 /** Add a word to a w:r's existing w:t */ 464 private static void addWord(R r, String word) { 465 466 List runContent = r.getRunContent(); 467 468 for (Object o2 : runContent ) { 469 470 /* TODO - model assumes each w:r contains 471 only 1 w:t 472 473 Check spec to see what the story is. 474 475 */ 476 477 boolean found = false; 478 479 if (o2 instanceof org.docx4j.wml.Text) { 480 481 if (found) { 482 log.debug("TODO: Handle multiple w:t in w:r!"); 483 } 484 485 found = true; 486 487 org.docx4j.wml.Text t = (org.docx4j.wml.Text)o2; 488 489 String existingVal = t.getValue(); 490 491 t.setValue(existingVal + " " + word); // TODO smarter handling of spaces 492 493 } else { 494 log.debug(o2.getClass().getName()); 495 } 496 } 497 498 499 500 } 501 347 502 348 503 private static org.docx4j.wml.R createRunStructure(String textVal, … … 361 516 362 517 private static String toRangeString(StringComparator sc, int start, int length, boolean space) { 518 519 // This method only exists for debug... 363 520 364 521 StringBuilder result = new StringBuilder(); … … 390 547 } 391 548 549 public static int[] getParagraphRunTextWordCounts(P p) { 550 551 List<Object> children = p.getParagraphContent(); 552 553 int i=0; 554 int[] result = new int[children.size()]; // one for each w:r 555 556 for (Object o : children) { 557 558 if ( o instanceof org.docx4j.wml.R ) { 559 560 org.docx4j.wml.R r = (org.docx4j.wml.R)o; 561 List runContent = r.getRunContent(); 562 563 result[i]=0; 564 565 for (Object o2 : runContent ) { 566 567 /* TODO - model assumes each w:r contains 568 only 1 w:t 569 570 Check spec to see what the story is. 571 572 */ 573 574 boolean found = false; 575 576 if (o2 instanceof javax.xml.bind.JAXBElement) { 577 578 if (((JAXBElement) o2).getDeclaredType().getName().equals( 579 "org.docx4j.wml.Text")) { 580 581 if (found) { 582 log.debug("TODO: Handle multiple w:t in w:r!"); 583 } 584 585 found = true; 586 587 // System.out.println("Found Text"); 588 org.docx4j.wml.Text t = (org.docx4j.wml.Text) ((JAXBElement) o2) 589 .getValue(); 590 591 result[i] = getWordCount( t.getValue() ); 592 593 } else { 594 log.debug(((JAXBElement) o2).getDeclaredType().getName()); 595 } 596 } else { 597 log.debug(o2.getClass().getName()); 598 } 599 } 600 601 i++; 602 603 } else { 604 log.debug("Encountered " + children.get(i).getClass().getName()); 605 return null; 606 607 } 608 } 609 610 return result; 611 612 } 613 614 615 private static int getWordCount(String sentence) { 616 617 /* 618 * Need to convert leading and trailing spaces 619 * in order to get correct count. 620 * 621 * 'a' 1 622 ' a' 2 623 'a ' 1 624 ' b ' 2 625 ' b c ' 3 626 'b c' 2 627 'b c' 3 <-- and also double spaces here 628 629 * 630 * trim takes care of leading and trailing. 631 */ 632 633 return sentence.trim().split("\\s").length; 634 635 // TODO - handle cases of 2 spaces in a row, within the sentence 636 // via an improved regex 637 638 } 639 640 392 641 public static String getRunString(org.docx4j.wml.P p, int i) { 393 642 … … 406 655 if (((JAXBElement) o2).getDeclaredType().getName().equals( 407 656 "org.docx4j.wml.Text")) { 408 // System.out.println("Found Text");657 // log.debug("Found Text"); 409 658 org.docx4j.wml.Text t = (org.docx4j.wml.Text) ((JAXBElement) o2) 410 659 .getValue(); 411 660 result.append(t.getValue()); 412 661 } else { 413 System.out.println(((JAXBElement) o2).getDeclaredType().getName());662 log.debug(((JAXBElement) o2).getDeclaredType().getName()); 414 663 } 415 664 } else { 416 System.out.println(o2.getClass().getName());665 log.debug(o2.getClass().getName()); 417 666 } 418 667 } 419 668 420 669 } else { 421 System.out.println("Encountered " + children.get(i).getClass().getName());670 log.debug("Encountered " + children.get(i).getClass().getName()); 422 671 return null; 423 672 … … 428 677 } 429 678 430 431 public static String collateRuns(org.docx4j.wml.P p) { 432 433 StringBuilder result = new StringBuilder(); 434 boolean resultIsEmpty = true; 435 436 List<Object> children = p.getParagraphContent(); 437 438 // System.out.println("p.toString"); 439 440 for (Object o : children ) { 441 // System.out.println(" " + o.getClass().getName() ); 442 if ( o instanceof org.docx4j.wml.R) { 443 // System.out.println("Hit R"); 444 org.docx4j.wml.R run = (org.docx4j.wml.R)o; 445 List runContent = run.getRunContent(); 446 for (Object o2 : runContent ) { 447 if ( o2 instanceof javax.xml.bind.JAXBElement) { 448 // TODO - unmarshall directly to Text. 449 if ( ((JAXBElement)o2).getDeclaredType().getName().equals("org.docx4j.wml.Text") ) { 450 // System.out.println("Found Text"); 451 org.docx4j.wml.Text t = (org.docx4j.wml.Text)((JAXBElement)o2).getValue(); 452 453 if (resultIsEmpty) { 454 result.append( t.getValue() ); 455 resultIsEmpty = false; 456 } else { 457 result.append( " " + RUN_DELIMITER + " " + t.getValue() ); 458 } 459 460 } 461 } else { 462 // System.out.println(o2.getClass().getName()); 463 } 464 } 465 } 466 } 467 return result.toString(); 468 469 } 470 679 471 680 private static String getDiffxOutput(String xml1, String xml2) { 472 681 Reader xmlr1 = new StringReader(xml1); … … 523 732 } 524 733 734 /*String[] runtContents = "a".trim().split("\\s"); 735 System.out.println( "'a' " + runtContents.length ); 736 737 runtContents = " a".trim().split("\\s"); 738 System.out.println( "' a' " + runtContents.length ); 739 740 runtContents = "a ".trim().split("\\s"); 741 System.out.println( "'a ' " + runtContents.length ); 742 743 runtContents = " b ".trim().split("\\s"); 744 System.out.println( "' b ' " + runtContents.length ); 745 746 runtContents = " b c ".trim().split("\\s"); 747 System.out.println( "' b c ' " + runtContents.length ); 748 749 runtContents = "b c".trim().split("\\s"); 750 System.out.println( "'b c' " + runtContents.length ); 751 752 runtContents = "b c".trim().split("\\s"); 753 System.out.println( "'b c' " + runtContents.length );*/ 754 525 755 } -
trunk/docx4j/src/main/java/org/docx4j/diff/diffx2wml.xslt
r473 r481 9 9 xmlns:java="http://xml.apache.org/xalan/java" 10 10 xmlns:xml="http://www.w3.org/XML/1998/namespace" 11 xmlns:xalan="http://xml.apache.org/xalan" 11 12 12 13 version="1.0" … … 34 35 --> 35 36 36 <xsl:output method="xml" encoding="utf-8" omit-xml-declaration="no" indent="yes" /> 37 <xsl:output method="xml" encoding="utf-8" omit-xml-declaration="no" 38 indent="yes" xalan:indent-amount="4" /> 37 39 38 40 <xsl:param name="author"/>
Note: See TracChangeset
for help on using the changeset viewer.
