0

I am using Apache POI (version 5.2.2) Java library to replace text placeholders in word documents. One of my task is to replace/edit text put in a rectangle shape. I am using the code below and it successfully works, the only issue that I am facing is that if I am to change or edit some other paragraph's text in the document then it is giving an exception. The codes and the exception is given below.

The code to change the text box content:

private void replacePlaceholdersWithInAShape(XWPFParagraph paragraph) throws XmlException {

XmlCursor cursor = paragraph.getCTP().newCursor();
            cursor.selectPath("declare namespace w='http://schemas.openxmlformats.org/wordprocessingml/2006/main' .//*/w:txbxContent/w:p/w:r");
            List<XmlObject> ctrsintxtbx = new ArrayList<XmlObject>();
            while(cursor.hasNextSelection()) {
                cursor.toNextSelection();
                XmlObject obj = cursor.getObject();
                ctrsintxtbx.add(obj);
            }
            for (XmlObject obj : ctrsintxtbx) {
                CTR ctr = CTR.Factory.parse(obj.xmlText());
                XWPFRun bufferrun = new XWPFRun(ctr, (IRunBody)paragraph);
                String text = bufferrun.getText(0);
                if (text != null && !text.equalsIgnoreCase("")) {
                    if(text.contains("[{project_number}]")) {    
                        System.out.println("contains project number");
                        text = text.replace("[{project_number}]", "test");
                        bufferrun.setText("test", 0);
                    } else {                    
                        bufferrun.setText(text, 0);
                    }
                }
                obj.set(bufferrun.getCTR());
            }
}

The code use to change the paragraph text after successfully editing the text in the text box/shape

protected void replaceParagraphPlaceholders(String paraText, XWPFParagraph paragraph, String key, String value, Map<String, Object> fields) {

    //paragraph text's empty and null check.
    String paragraphText = paragraph.getText();
    if(paragraphText!=""&&paragraphText!=null) {
        if(paragraph.getRuns().size()<=1) {
            String runText = "Sample text";
            paragraph.getRuns().get(0).setText(runText, 0);
        }
    }
}

The exception coming from the above code while changing the paragraph text is as below.

java.util.ConcurrentModificationException: Document changed during select
    at org.apache.xmlbeans.impl.xpath.xmlbeans.XmlbeansXPathEngine.next(XmlbeansXPathEngine.java:100)
    at org.apache.xmlbeans.impl.store.Cursor._toSelection(Cursor.java:749)
    at org.apache.xmlbeans.impl.store.Cursor._getSelectionCount(Cursor.java:764)
    at org.apache.xmlbeans.impl.store.Cursor.notifyChange(Cursor.java:697)
    at org.apache.xmlbeans.impl.store.Locale.notifyChange(Locale.java:995)
    at org.apache.xmlbeans.impl.store.Cur.moveChars(Cur.java:1772)
    at org.apache.xmlbeans.impl.store.Cur.moveNodeContents(Cur.java:2016)
    at org.apache.xmlbeans.impl.store.Cur.moveNodeContents(Cur.java:1986)
    at org.apache.xmlbeans.impl.store.Xobj.store_text(Xobj.java:1833)
    at org.apache.xmlbeans.impl.values.XmlObjectBase.set_String(XmlObjectBase.java:1106)
    at org.apache.xmlbeans.impl.values.XmlObjectBase.setStringValue(XmlObjectBase.java:1774)
    at org.apache.poi.xwpf.usermodel.XWPFRun.setText(XWPFRun.java:363)
    at net.sharesuite.formletter.builder.poibuilder.docx.PoiDocxBuilder.replaceParagraphPlaceholders(PoiDocxBuilder.java:596)
    at net.sharesuite.formletter.builder.poibuilder.docx.PoiDocxBuilder.lambda$1(PoiDocxBuilder.java:205)
    at java.base/java.util.ArrayList$Itr.forEachRemaining(ArrayList.java:1003)
    at java.base/java.util.Collections$UnmodifiableCollection$1.forEachRemaining(Collections.java:1061)
    at net.sharesuite.formletter.builder.poibuilder.docx.PoiDocxBuilder.createFormLetterFile(PoiDocxBuilder.java:175)
    at net.sharesuite.formletter.service.impl.FormLetterServiceImpl.createFormLetter(FormLetterServiceImpl.java:42)
    at net.sharesuite.test.formletter.DocumentTest.testNLBLFormletter(DocumentTest.java:646)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:93)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:40)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:529)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:756)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:452)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:210)

Once the document's text is changed using the XMLObject, other changes in the document's paragraph etc. is causing the above exception.

Olaf Kock
  • 46,930
  • 8
  • 59
  • 90
  • XMLCursors are really hard to use properly. And you probably don't need to use them. The XWPF classes provide high level read and edit capabilities that allow you to avoid using XMLCursors. https://poi.apache.org/apidocs/dev/org/apache/poi/xwpf/usermodel/XWPFParagraph.html - see the methods that involve XWPFRun - XWPFRun is the class that wraps CTR class. – PJ Fanning May 03 '23 at 11:45
  • @PJFanning Many thanks for the guidance, actually I couldn't find any high level functions in the CTP or CTR class. Rather there are XMLCursor class that provides the appropriate XML which means I am able to read the text inside the shape. But the issue is there are no functions that on a high level change the paragraph's text inside the shape. There is a method in XMLObject class set(CTR ctr) that directly updates the XML, and in this way If I am to update some normal paragraph's text it is giving an exception i.e., java.util.ConcurrentModificationException mentioned in the question above. – Furqan Ahmed May 05 '23 at 17:15
  • https://poi.apache.org/apidocs/dev/org/apache/poi/xwpf/usermodel/XWPFRun.html#setText-java.lang.String- can be used to change the text (and this updates the CTR) – PJ Fanning May 06 '23 at 11:32

0 Answers0