/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 2008 jOpenDocument, by ILM Informatique. All rights reserved. * * The contents of this file are subject to the terms of the GNU * General Public License Version 3 only ("GPL"). * You may not use this file except in compliance with the License. * You can obtain a copy of the License at http://www.gnu.org/licenses/gpl-3.0.html * See the License for the specific language governing permissions and limitations under the License. * * When distributing the software, include this License Header Notice in each file. * */ package org.jopendocument.dom; import static java.util.Arrays.asList; import static java.util.Collections.singletonList; import org.jopendocument.dom.spreadsheet.Table; import org.jopendocument.dom.text.Heading; import org.jopendocument.dom.text.Paragraph; import org.jopendocument.dom.text.ParagraphStyle; import java.math.BigDecimal; import java.util.Arrays; import java.util.Calendar; import java.util.List; import java.util.TimeZone; import junit.framework.TestCase; import org.jdom.Element; import org.jdom.xpath.XPath; public class ODSingleXMLDocumentTest extends TestCase { protected void setUp() throws Exception { super.setUp(); } public void testCreate() throws Exception { final ODSingleXMLDocument single = new ODPackage(this.getClass().getResourceAsStream("test.odt")).toSingle(); assertNull(single.isValid()); assertNull(single.checkStyles()); } public void testAdd() throws Exception { final ODSingleXMLDocument single = new ODPackage(this.getClass().getResourceAsStream("empty.odt")).toSingle(); // really empty single.getBody().removeContent(); final ODSingleXMLDocument single2 = new ODPackage(this.getClass().getResourceAsStream("styles.odt")).toSingle(); single.add(single2); assertNull(single.isValid()); assertNull(single.checkStyles()); } public void testAddParagraph() throws Exception { final ODSingleXMLDocument single = new ODPackage(this.getClass().getResourceAsStream("styles.odt")).toSingle(); final Paragraph p = new Paragraph(); p.setStyle("testPragraph"); p.addContent("Hello"); p.addTab(); p.addStyledContent("World", "testChar"); // add at the end assertNull(p.getElement().getDocument()); p.setDocument(single); assertSame(single.getDocument(), p.getElement().getDocument()); final Heading h = new Heading(); h.setStyle("inexistantt"); h.addContent("Heading text"); try { h.setDocument(single); fail("should throw since style doesn't exist"); } catch (Exception e) { // ok } h.setStyle("testPragraph"); // add before p final Element pParent = p.getElement().getParentElement(); single.add(h, pParent, pParent.indexOf(p.getElement())); assertNull(single.isValid()); assertNull(single.checkStyles()); // rm p.setDocument(null); assertNull(p.getElement().getDocument()); } public void testTable() throws Exception { final ODSingleXMLDocument single = new ODPackage(this.getClass().getResourceAsStream("test.odt")).toSingle(); assertNull(single.getDescendantByName("draw:text-box", "inexistant")); final Element table = single.getDescendantByName("table:table", "JODTestTable"); assertNotNull(table); final Table<ODSingleXMLDocument> t = new Table<ODSingleXMLDocument>(single, table); assertEquals(1, t.getHeaderRowCount()); assertEquals(0, t.getHeaderColumnCount()); final Calendar c = Calendar.getInstance(); c.clear(); c.set(2005, 0, 12, 12, 35); assertEquals(c.getTime(), t.getValueAt(2, 1)); // 11.91cm assertEquals(119.06f, t.getWidth()); t.setColumnCount(6, -1, true); t.setValueAt(3.14, 5, 0); assertTableWidth(t, 119.06f); final float ratio = t.getColumn(0).getWidth() / t.getColumn(1).getWidth(); t.setColumnCount(2, 1, true); // ratio is kept assertEquals(ratio, t.getColumn(0).getWidth() / t.getColumn(1).getWidth()); assertTableWidth(t, 119.06f); // table changes width final float width = t.getColumn(0).getWidth(); t.setColumnCount(4, 0, false); assertTableWidth(t, 119.06f + 2 * width); t.setColumnCount(1, 123, false); assertEquals(1, t.getColumnCount()); assertTableWidth(t, width); assertNull(single.isValid()); assertNull(single.checkStyles()); t.detach(); assertNull(single.getDescendantByName("table:table", "JODTestTable")); } private void assertTableWidth(Table<?> t, float w) { assertEquals(w, t.getWidth()); float total = 0; for (int i = 0; i < t.getColumnCount(); i++) { total += t.getColumn(i).getWidth(); } assertEquals(round(w), round(total)); } private long round(float w) { return Math.round(w * 100.0) / 100; } public void testFrame() throws Exception { final ODPackage pkg = new ODPackage(this.getClass().getResourceAsStream("test.odt")); final Element frameElem = pkg.toSingle().getDescendantByName("draw:frame", "Cadre1"); final ODFrame<ODSingleXMLDocument> frame = new ODFrame<ODSingleXMLDocument>(pkg.toSingle(), frameElem); assertEquals(new BigDecimal("42.72"), frame.getWidth()); // height depends on the content assertNull(frame.getHeight()); assertEquals("right", frame.getStyle().getGraphicProperties().getHorizontalPosition()); assertEquals("paragraph", frame.getStyle().getGraphicProperties().getHorizontalRelation()); assertEquals(asList("position"), frame.getStyle().getGraphicProperties().getProtected()); assertTrue(frame.getStyle().getGraphicProperties().isContentPrinted()); } public void testStyle() throws Exception { { final ODPackage pkg = new ODPackage(this.getClass().getResourceAsStream("test.odt")); // test that multiple attributes may refer to paragraph styles final XPath ellipseXPath = pkg.getContent().getXPath("//draw:ellipse[@draw:name = 'Ellipse']"); final Element ellipse = (Element) ellipseXPath.selectSingleNode(pkg.getContent().getDocument()); final String drawTextStyleName = ellipse.getAttributeValue("text-style-name", ellipse.getNamespace("draw")); final ParagraphStyle ellipseTextStyle = ParagraphStyle.DESC.findStyle(pkg, pkg.getContent().getDocument(), drawTextStyleName); assertEquals(singletonList(ellipse), ellipseTextStyle.getReferences()); // P1 exists both in content and styles but doesn't denote the same style final ODXMLDocument styles = pkg.getXMLFile("styles.xml"); final XPath headerXPath = styles.getXPath("//text:p[string() = 'Header']"); final Element headerElem = (Element) headerXPath.selectSingleNode(styles.getDocument()); final String styleName = new Paragraph(headerElem).getStyleName(); final Element styleP1 = pkg.getStyle(pkg.getDocument("styles.xml"), "paragraph", styleName); final Element contentP1 = pkg.getStyle(pkg.getDocument("content.xml"), "paragraph", styleName); assertNotNull(styleP1); assertNotNull(contentP1); assertNotSame(styleP1, contentP1); // styleP1 can only be referenced from styles.xml assertEquals(singletonList(headerElem), StyleStyle.warp(pkg, styleP1).getReferences()); // contentP1 can only be referenced from content.xml assertFalse(StyleStyle.warp(pkg, contentP1).getReferences().contains(headerElem)); // when merging styles.xml pkg.toSingle(); // the content doesn't change assertSame(contentP1, pkg.getStyle(pkg.getDocument("content.xml"), "paragraph", styleName)); final String mergedStyleName = new Paragraph((Element) headerXPath.selectSingleNode(pkg.getContent().getDocument())).getStyleName(); assertNotNull(mergedStyleName); assertFalse(styleName.equals(mergedStyleName)); } final ODPackage pkg = new ODPackage(this.getClass().getResourceAsStream("test.odt")); final Element heading = (Element) pkg.getContent().getXPath("//text:h[string() = 'Titre 2']").selectSingleNode(pkg.getContent().getDocument()); final String styleName = heading.getAttributeValue("style-name", heading.getNamespace()); // common styles are not in content.xml assertNull(pkg.getContent().getStyle("paragraph", styleName)); // but in styles.xml testStyleElem(pkg.getXMLFile("styles.xml").getStyle("paragraph", styleName)); testStyleElem(pkg.getStyle("paragraph", styleName)); // except if we merge the two pkg.toSingle(); testStyleElem(pkg.getContent().getStyle("paragraph", styleName)); testStyleElem(pkg.getStyle("paragraph", styleName)); } private void testStyleElem(final Element styleElem) { assertNotNull(styleElem); assertEquals("2", styleElem.getAttributeValue("default-outline-level", styleElem.getNamespace())); assertEquals("Heading", styleElem.getAttributeValue("parent-style-name", styleElem.getNamespace())); } public void testMeta() throws Exception { final ODPackage pkg = new ODPackage(this.getClass().getResourceAsStream("test.odt")); final ODMeta meta = pkg.getMeta(); assertEquals("firstInfo", meta.getUserMeta("Info 1").getValue()); assertEquals("", meta.getUserMeta("secondName").getValue()); final List<String> expected = Arrays.asList("Info 1", "secondName", "Info 3", "Info 4"); assertEquals(expected, meta.getUserMetaNames()); // does not exist assertNull(meta.getUserMeta("toto")); // now it does assertNotNull(meta.getUserMeta("toto", true)); meta.removeUserMeta("toto"); // now it was removed assertNull(meta.getUserMeta("toto")); final ODUserDefinedMeta toto = meta.getUserMeta("toto", true); toto.setValue(3.5); assertEquals(ODValueType.FLOAT, toto.getValueType()); assertEquals(3.5f, ((BigDecimal) toto.getValue()).floatValue()); final TimeZone marquisesTZ = TimeZone.getTimeZone("Pacific/Marquesas"); final TimeZone pstTZ = TimeZone.getTimeZone("PST"); final Calendar cal = Calendar.getInstance(pstTZ); final int hour = cal.get(Calendar.HOUR_OF_DAY); final int minute = cal.get(Calendar.MINUTE); { toto.setValue(cal, ODValueType.TIME); Calendar actual = (Calendar) toto.getValue(); assertEquals(hour, actual.get(Calendar.HOUR_OF_DAY)); assertEquals(minute, actual.get(Calendar.MINUTE)); // for TIME the time zone is important, the same date has not the same value cal.setTimeZone(marquisesTZ); toto.setValue(cal, ODValueType.TIME); actual = (Calendar) toto.getValue(); // +1h30 assertFalse(hour == actual.get(Calendar.HOUR_OF_DAY)); assertFalse(minute == actual.get(Calendar.MINUTE)); } { cal.setTimeZone(pstTZ); toto.setValue(cal, ODValueType.DATE); assertEquals(cal.getTime(), toto.getValue()); // on the contrary for DATE the timezone is irrelevant cal.setTimeZone(marquisesTZ); toto.setValue(cal, ODValueType.DATE); assertEquals(cal.getTime(), toto.getValue()); } } }