package org.docx4j; import static org.junit.Assert.*; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.math.BigInteger; import java.util.ArrayList; import java.util.List; import javax.xml.bind.JAXBElement; import javax.xml.bind.JAXBException; import junit.framework.Assert; import org.docx4j.TraversalUtil.CallbackImpl; import org.docx4j.finders.ClassFinder; import org.docx4j.openpackaging.exceptions.Docx4JException; import org.docx4j.openpackaging.exceptions.InvalidFormatException; import org.docx4j.openpackaging.packages.WordprocessingMLPackage; import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart; import org.docx4j.wml.Body; import org.docx4j.wml.CTBookmark; import org.docx4j.wml.CTMarkupRange; import org.docx4j.wml.Document; import org.docx4j.wml.FldChar; import org.docx4j.wml.P; import org.docx4j.wml.R; import org.docx4j.wml.Text; import org.junit.Test; import org.jvnet.jaxb2_commons.ppp.Child; /** * See also org.docx4j.wml.ParentTest * */ public class ParentFixTest { @Test public void testBodyCreated() throws InvalidFormatException { WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.createPackage(); MainDocumentPart mdp = wordMLPackage.getMainDocumentPart(); mdp.addParagraphOfText("hello world"); Document doc = mdp.getJaxbElement(); Body body = doc.getBody(); // Set explicitly in Document Assert.assertEquals(doc, body.getParent()); // Now, check that traversing doesn't change anything (new TestCallback()).walkJAXBElements(doc); // No change Assert.assertEquals(doc, body.getParent()); } @Test public void testBodyLoaded() throws Docx4JException, IOException { WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.createPackage(); MainDocumentPart mdp = wordMLPackage.getMainDocumentPart(); mdp.addParagraphOfText("hello world"); ByteArrayOutputStream baos = new ByteArrayOutputStream(); Docx4J.save(wordMLPackage, baos); byte[] bytes = baos.toByteArray(); // Now load it wordMLPackage = Docx4J.load( new ByteArrayInputStream(bytes)); mdp = wordMLPackage.getMainDocumentPart(); Document doc = mdp.getJaxbElement(); Body body = doc.getBody(); // Expect it to be set Assert.assertEquals(doc, body.getParent()); // Now, check that traversing doesn't change anything (new TestCallback()).walkJAXBElements(doc); // No change Assert.assertEquals(doc, body.getParent()); } @Test public void testBodyChildrenCreated() throws InvalidFormatException { WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.createPackage(); MainDocumentPart mdp = wordMLPackage.getMainDocumentPart(); mdp.addParagraphOfText("hello world"); Document doc = mdp.getJaxbElement(); Body body = doc.getBody(); P p = (P)body.getContent().get(0); Assert.assertEquals(body, p.getParent()); // Now, check that traversing doesn't change anything (new TestCallback()).walkJAXBElements(doc); Assert.assertEquals(body, p.getParent()); } @Test public void testBodyChildrenLoaded() throws Docx4JException, IOException { WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.createPackage(); MainDocumentPart mdp = wordMLPackage.getMainDocumentPart(); mdp.addParagraphOfText("hello world"); ByteArrayOutputStream baos = new ByteArrayOutputStream(); Docx4J.save(wordMLPackage, baos); byte[] bytes = baos.toByteArray(); // Now load it wordMLPackage = Docx4J.load( new ByteArrayInputStream(bytes)); mdp = wordMLPackage.getMainDocumentPart(); Document doc = mdp.getJaxbElement(); Body body = doc.getBody(); P p = (P)body.getContent().get(0); Assert.assertEquals(body, p.getParent()); // Now, check that traversing doesn't change anything (new TestCallback()).walkJAXBElements(doc); Assert.assertEquals(body, p.getParent()); } /** * Prior to 3.3.1, unmarshalling a String does set parent properly, except in the known case of JAXBElement, * which a traverse fixes * * With 3.3.1, ArrayListWml gets this right first time * * @throws JAXBException */ @Test public void testUnmarshalString() throws JAXBException { String openXML = "<w:p xmlns:w=\"http://schemas.openxmlformats.org/wordprocessingml/2006/main\" xmlns:xml=\"http://www.w3.org/XML/1998/namespace\">" + "<w:r>" + "<w:t>some text</w:t>" +"</w:r>" + "<w:bookmarkStart w:id=\"0\" w:name=\"mybm\"/>" + "<w:bookmarkEnd w:id=\"0\"/>" +"</w:p>"; P p = (P)XmlUtils.unmarshalString(openXML); R r = (R)p.getContent().get(0); // Parent of run is set correctly if (r.getParent()==null) { System.out.println("null parent"); Assert.fail("null parent"); } else { System.out.println(r.getParent().getClass()); Assert.assertEquals(P.class, r.getParent().getClass()); } // Parent of a JAXBElement? Child o2 = (Child)XmlUtils.unwrap(p.getContent().get(1)); if (o2.getParent()==null) { System.out.println("null parent"); Assert.fail("null parent"); } else if (o2.getParent() instanceof JAXBElement) { // 3.3.0: Sad but true // <= 3.3.0: org.docx4j.wml.CTBookmark has parent class javax.xml.bind.JAXBElement - NOT GOOD Assert.assertEquals(JAXBElement.class, o2.getParent().getClass() ); // Traverse to fix // org.docx4j.wml.CTBookmark has parent class org.docx4j.wml.P - GOOD List foo = new ArrayList(); foo.add(p); ClassFinder finder = new ClassFinder(FldChar.class); // whatever new TraversalUtil(foo, finder); o2 = (Child)XmlUtils.unwrap(p.getContent().get(1)); if (o2.getParent()==null) { System.out.println("null parent"); Assert.fail("null parent"); } else { System.out.println(o2.getClass().getName() + " has CORRECTED parent " + o2.getParent().getClass()); Assert.assertEquals(o2.getParent().getClass(), P.class); } } else /* since 3.3.1 */ { System.out.println(o2.getClass().getName() + " has parent " + o2.getParent().getClass()); Assert.assertEquals(P.class, o2.getParent().getClass() ); } } /** * Prior to 3.3.1, Objects added manually don't have their parent set automatically. * But traversal fixes this. * * With 3.3.1, ArrayListWml gets this right first time * */ @Test public void testCreatedViaFactory() { org.docx4j.wml.ObjectFactory wmlObjectFactory = new org.docx4j.wml.ObjectFactory(); P p = wmlObjectFactory.createP(); // Create object for r R r = wmlObjectFactory.createR(); p.getContent().add( r); // Create object for t (wrapped in JAXBElement) Text text = wmlObjectFactory.createText(); JAXBElement<org.docx4j.wml.Text> textWrapped = wmlObjectFactory.createRT(text); r.getContent().add( textWrapped); text.setValue( "some text"); // Create object for bookmarkStart (wrapped in JAXBElement) CTBookmark bookmark = wmlObjectFactory.createCTBookmark(); JAXBElement<org.docx4j.wml.CTBookmark> bookmarkWrapped = wmlObjectFactory.createPBookmarkStart(bookmark); p.getContent().add( bookmarkWrapped); bookmark.setName( "mybm"); bookmark.setId( BigInteger.valueOf( 0) ); // Create object for bookmarkEnd (wrapped in JAXBElement) CTMarkupRange markuprange = wmlObjectFactory.createCTMarkupRange(); JAXBElement<org.docx4j.wml.CTMarkupRange> markuprangeWrapped = wmlObjectFactory.createPBookmarkEnd(markuprange); p.getContent().add( markuprangeWrapped); markuprange.setId( BigInteger.valueOf( 0) ); // Its not set automatically // if (r.getParent()==null) { // System.out.println("null parent"); // Assert.fail("null parent"); // } else { System.out.println(r.getParent().getClass()); Assert.assertEquals(P.class, r.getParent().getClass()); } // Traverse to fix: uncomment for 3.3.0 // List foo = new ArrayList(); // foo.add(p); // ClassFinder finder = new ClassFinder(FldChar.class); // whatever // new TraversalUtil(foo, finder); // Test bookmark Child o2 = (Child)XmlUtils.unwrap(p.getContent().get(1)); if (o2.getParent()==null) { System.out.println("null parent"); Assert.fail("null parent"); } else { System.out.println(o2.getClass().getName() + " has parent " + o2.getParent().getClass()); Assert.assertEquals(o2.getParent().getClass(), P.class); } // Test run o2 = (Child)XmlUtils.unwrap(p.getContent().get(0)); // run if (o2.getParent()==null) { System.out.println("null parent"); Assert.fail("null parent"); } else { System.out.println(o2.getClass().getName() + " has parent " + o2.getParent().getClass()); Assert.assertEquals(o2.getParent().getClass(), P.class); } // Traverse: shouldn't change anything List foo = new ArrayList(); foo.add(p); ClassFinder finder = new ClassFinder(FldChar.class); // whatever new TraversalUtil(foo, finder); // Test bookmark o2 = (Child)XmlUtils.unwrap(p.getContent().get(1)); if (o2.getParent()==null) { System.out.println("null parent"); Assert.fail("null parent"); } else { System.out.println(o2.getClass().getName() + " has parent " + o2.getParent().getClass()); Assert.assertEquals(o2.getParent().getClass(), P.class); } // Test run o2 = (Child)XmlUtils.unwrap(p.getContent().get(0)); // run if (o2.getParent()==null) { System.out.println("null parent"); Assert.fail("null parent"); } else { System.out.println(o2.getClass().getName() + " has parent " + o2.getParent().getClass()); Assert.assertEquals(o2.getParent().getClass(), P.class); } } class TestCallback extends CallbackImpl { @Override public List<Object> apply(Object o) { // TODO Auto-generated method stub return null; } } }