diff options
author | Peter Hancock <phancock@apache.org> | 2012-02-10 16:51:08 +0000 |
---|---|---|
committer | Peter Hancock <phancock@apache.org> | 2012-02-10 16:51:08 +0000 |
commit | a21ecc5e13be09393036efe90f4ab63a7ee09fab (patch) | |
tree | 2ed0c7fd447b36aa00c17342f2074300bcb07399 /test/java | |
parent | 098ac1879ef71df08ee43eb263f5efac77d4e352 (diff) | |
parent | 65e12053cb81114db964534f7e974c338a8a467b (diff) | |
download | xmlgraphics-fop-a21ecc5e13be09393036efe90f4ab63a7ee09fab.tar.gz xmlgraphics-fop-a21ecc5e13be09393036efe90f4ab63a7ee09fab.zip |
Merged in Temp_ImproveAccessibility
revs 1187234, 1188205, 1205935, 1236718, 1238313, 1240963
git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1242848 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'test/java')
30 files changed, 2510 insertions, 153 deletions
diff --git a/test/java/org/apache/fop/StandardTestSuite.java b/test/java/org/apache/fop/StandardTestSuite.java index bb59c8605..808542306 100644 --- a/test/java/org/apache/fop/StandardTestSuite.java +++ b/test/java/org/apache/fop/StandardTestSuite.java @@ -76,7 +76,8 @@ import org.apache.fop.pdf.PDFLibraryTestSuite; MinOptMaxTestCase.class, AdobeStandardEncodingTestCase.class, AFMParserTestCase.class, - FontEventProcessingTestCase.class + FontEventProcessingTestCase.class, + org.apache.fop.render.intermediate.IFStructureTreeBuilderTestCase.class }) public class StandardTestSuite { } diff --git a/test/java/org/apache/fop/accessibility/fo/DOMResultUtil.java b/test/java/org/apache/fop/accessibility/fo/DOMResultUtil.java new file mode 100644 index 000000000..5b4e264f2 --- /dev/null +++ b/test/java/org/apache/fop/accessibility/fo/DOMResultUtil.java @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.accessibility.fo; + +import java.io.File; + +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMResult; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; + +/** + * Utility class to stream an instance of {@link DOMResult} into a file. May be + * useful for debugging. + */ +final class DOMResultUtil { + + private DOMResultUtil() { + } + + /** + * Streams the given result into a file of the given name. + * + * @param result the result of a transformation + * @param filename name of the file into which to stream the result + * @throws TransformerException if a problem occurs when streaming + */ + public static void streamToFile(DOMResult result, String filename) throws TransformerException { + DOMSource source = new DOMSource(result.getNode()); + TransformerFactory tFactory = TransformerFactory.newInstance(); + Transformer transformer = tFactory.newTransformer(); + transformer.transform(source, new StreamResult(new File(filename))); + } + +} diff --git a/test/java/org/apache/fop/accessibility/fo/FO2StructureTreeConverterTestCase.java b/test/java/org/apache/fop/accessibility/fo/FO2StructureTreeConverterTestCase.java new file mode 100644 index 000000000..9c53bdde3 --- /dev/null +++ b/test/java/org/apache/fop/accessibility/fo/FO2StructureTreeConverterTestCase.java @@ -0,0 +1,223 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.accessibility.fo; + +import static org.junit.Assert.assertTrue; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; + +import javax.xml.transform.Result; +import javax.xml.transform.Source; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerConfigurationException; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.TransformerFactoryConfigurationError; +import javax.xml.transform.dom.DOMResult; +import javax.xml.transform.sax.SAXTransformerFactory; +import javax.xml.transform.sax.TransformerHandler; +import javax.xml.transform.stream.StreamResult; +import javax.xml.transform.stream.StreamSource; + +import org.custommonkey.xmlunit.Diff; +import org.junit.Test; +import org.w3c.dom.Document; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.AttributesImpl; + +import org.apache.fop.accessibility.StructureTree2SAXEventAdapter; +import org.apache.fop.accessibility.StructureTreeEventHandler; +import org.apache.fop.apps.FOPException; +import org.apache.fop.apps.FOUserAgent; +import org.apache.fop.fo.FODocumentParser; +import org.apache.fop.fo.FODocumentParser.FOEventHandlerFactory; +import org.apache.fop.fo.FOEventHandler; +import org.apache.fop.fo.LoadingException; +import org.apache.fop.fotreetest.DummyFOEventHandler; + +public class FO2StructureTreeConverterTestCase { + + private interface FOLoader { + + InputStream getFoInputStream(); + } + + private static final String STRUCTURE_TREE_SEQUENCE_NAME = "structure-tree-sequence"; + + private FOLoader foLoader; + + @Test + public void testCompleteDocument() throws Exception { + foLoader = new FOLoader() { + public InputStream getFoInputStream() { + return getResource("/org/apache/fop/fo/complete_document.fo"); + } + }; + testConverter(); + } + + @Test + public void testTableFooters() throws Exception { + foLoader = new FOLoader() { + public InputStream getFoInputStream() { + return getResource("table-footers.fo"); + } + }; + testConverter(); + } + + @Test + public void testCompleteContentWrappedInTableFooter() throws Exception { + Source xslt = new StreamSource(getResource("wrapCompleteDocumentInTableFooter.xsl")); + Transformer transformer = createTransformer(xslt); + InputStream originalFO = getResource("/org/apache/fop/fo/complete_document.fo"); + ByteArrayOutputStream transformedFoOutput = new ByteArrayOutputStream(); + transformer.transform(new StreamSource(originalFO), new StreamResult(transformedFoOutput)); + final byte[] transformedFoOutputBytes = transformedFoOutput.toByteArray(); + foLoader = new FOLoader() { + public InputStream getFoInputStream() { + return new ByteArrayInputStream(transformedFoOutputBytes); + } + }; + testConverter(); + } + + private Transformer createTransformer(Source xslt) throws TransformerFactoryConfigurationError, + TransformerConfigurationException { + TransformerFactory transformerFactory = TransformerFactory.newInstance(); + return transformerFactory.newTransformer(xslt); + } + + private static InputStream getResource(String name) { + return FO2StructureTreeConverterTestCase.class.getResourceAsStream(name); + } + + private void testConverter() throws Exception { + DOMResult expectedStructureTree = loadExpectedStructureTree(); + DOMResult actualStructureTree = buildActualStructureTree(); + final Diff diff = createDiff(expectedStructureTree, actualStructureTree); + assertTrue(diff.toString(), diff.identical()); + } + + private DOMResult loadExpectedStructureTree() { + DOMResult expectedStructureTree = new DOMResult(); + InputStream xslt = getResource("fo2StructureTree.xsl"); + runXSLT(xslt, foLoader.getFoInputStream(), expectedStructureTree); + return expectedStructureTree; + } + + private static void runXSLT(InputStream xslt, InputStream doc, Result result) { + Source fo = new StreamSource(doc); + try { + Transformer transformer = TransformerFactory.newInstance() + .newTransformer(new StreamSource(xslt)); + transformer.transform(fo, result); + } catch (TransformerConfigurationException e) { + throw new RuntimeException(e); + } catch (TransformerException e) { + throw new RuntimeException(e); + } finally { + closeStream(xslt); + closeStream(doc); + } + } + + private static void closeStream(InputStream stream) { + try { + stream.close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private DOMResult buildActualStructureTree() throws Exception { + DOMResult actualStructureTree = new DOMResult(); + createStructureTreeFromDocument(foLoader.getFoInputStream(), actualStructureTree); + return actualStructureTree; + } + + private static void createStructureTreeFromDocument(InputStream foInputStream, + Result result) throws Exception { + TransformerHandler tHandler = createTransformerHandler(result); + startStructureTreeSequence(tHandler); + StructureTreeEventHandler structureTreeEventHandler + = StructureTree2SAXEventAdapter.newInstance(tHandler); + FODocumentParser documentParser = createDocumentParser(structureTreeEventHandler); + FOUserAgent userAgent = createFOUserAgent(documentParser); + parseDocument(foInputStream, documentParser, userAgent); + endStructureTreeSequence(tHandler); + } + + private static TransformerHandler createTransformerHandler(Result domResult) + throws TransformerConfigurationException, TransformerFactoryConfigurationError { + SAXTransformerFactory factory = (SAXTransformerFactory) SAXTransformerFactory.newInstance(); + TransformerHandler transformerHandler = factory.newTransformerHandler(); + transformerHandler.setResult(domResult); + return transformerHandler; + } + + private static void startStructureTreeSequence(TransformerHandler tHandler) throws SAXException { + tHandler.startDocument(); + tHandler.startElement("", STRUCTURE_TREE_SEQUENCE_NAME, STRUCTURE_TREE_SEQUENCE_NAME, + new AttributesImpl()); + } + + private static FODocumentParser createDocumentParser( + final StructureTreeEventHandler structureTreeEventHandler) { + return FODocumentParser.newInstance(new FOEventHandlerFactory() { + public FOEventHandler newFOEventHandler(FOUserAgent foUserAgent) { + return new FO2StructureTreeConverter(structureTreeEventHandler, + new DummyFOEventHandler(foUserAgent)); + } + }); + } + + private static FOUserAgent createFOUserAgent(FODocumentParser documentParser) { + FOUserAgent userAgent = documentParser.createFOUserAgent(); + userAgent.setAccessibility(true); + return userAgent; + } + + private static void parseDocument(InputStream foInputStream, FODocumentParser documentParser, + FOUserAgent userAgent) throws FOPException, LoadingException { + try { + documentParser.parse(foInputStream, userAgent); + } finally { + closeStream(foInputStream); + } + } + + private static void endStructureTreeSequence(TransformerHandler tHandler) throws SAXException { + tHandler.endElement("", STRUCTURE_TREE_SEQUENCE_NAME, STRUCTURE_TREE_SEQUENCE_NAME); + tHandler.endDocument(); + } + + private static Diff createDiff(DOMResult expected, DOMResult actual) { + Diff diff = new Diff(getDocument(expected), getDocument(actual)); + return diff; + } + + private static Document getDocument(DOMResult result) { + return (Document) result.getNode(); + } +} diff --git a/test/java/org/apache/fop/accessibility/fo/fo2StructureTree.xsl b/test/java/org/apache/fop/accessibility/fo/fo2StructureTree.xsl new file mode 100644 index 000000000..ce326f3b1 --- /dev/null +++ b/test/java/org/apache/fop/accessibility/fo/fo2StructureTree.xsl @@ -0,0 +1,135 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<!-- $Id$ --> +<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" + xmlns:fo="http://www.w3.org/1999/XSL/Format" + xmlns:fox="http://xmlgraphics.apache.org/fop/extensions" + xmlns:foi="http://xmlgraphics.apache.org/fop/internal"> + + <xsl:output method="xml" indent="no"/> + + <xsl:template name="copy"> + <xsl:copy> + <xsl:apply-templates select="@*|node()"/> + </xsl:copy> + </xsl:template> + + + <!-- Ignore fo:root --> + <xsl:template match="fo:root"> + <structure-tree-sequence> + <xsl:apply-templates/> + </structure-tree-sequence> + </xsl:template> + + <!-- fo:page-sequence maps to structure-tree --> + <xsl:template match="fo:page-sequence"> + <structure-tree xmlns="http://xmlgraphics.apache.org/fop/intermediate"> + <xsl:apply-templates/> + </structure-tree> + </xsl:template> + + + <!-- Declarations and Pagination and Layout Formatting Objects --> + <xsl:template match="fo:static-content|fo:flow"> + <xsl:call-template name="copy"/> + </xsl:template> + + <!-- Block-level Formatting Objects --> + <xsl:template match="fo:block|fo:block-container"> + <xsl:call-template name="copy"/> + </xsl:template> + + <!-- Inline-level Formatting Objects --> + <xsl:template match="fo:character|fo:inline|fo:inline-container"> + <xsl:call-template name="copy"/> + </xsl:template> + + <xsl:template match="fo:external-graphic|fo:instream-foreign-object"> + <xsl:call-template name="copy"/> + </xsl:template> + + <xsl:template match="fo:page-number|fo:page-number-citation|fo:page-number-citation-last"> + <xsl:call-template name="copy"/> + </xsl:template> + + <!-- Formatting Objects for Tables --> + <xsl:template match="fo:table-and-caption|fo:table-caption"> + <xsl:call-template name="copy"/> + </xsl:template> + + <xsl:template match="fo:table"> + <xsl:copy> + <xsl:apply-templates select="@*"/> + <xsl:apply-templates select="*[name() != 'fo:table-footer']"/> + <xsl:apply-templates select="fo:table-footer"/> + </xsl:copy> + </xsl:template> + + <xsl:template match="fo:table-header|fo:table-footer|fo:table-body|fo:table-row|fo:table-cell"> + <xsl:call-template name="copy"/> + </xsl:template> + + <!-- Formatting Objects for Lists --> + <xsl:template match="fo:list-block|fo:list-item|fo:list-item-label|fo:list-item-body"> + <xsl:call-template name="copy"/> + </xsl:template> + + <!-- Dynamic Effects: Link and Multi Formatting Objects --> + <xsl:template match="fo:basic-link"> + <xsl:call-template name="copy"/> + </xsl:template> + + <!-- Out-of-Line Formatting Objects --> + <xsl:template match="fo:float|fo:footnote|fo:footnote-body"> + <xsl:call-template name="copy"/> + </xsl:template> + + <!-- Other Formatting Objects --> + <xsl:template match="fo:wrapper|fo:marker"> + <xsl:call-template name="copy"/> + </xsl:template> + + + <!-- Discard descendants of fo:leader --> + <xsl:template match="fo:leader"/> + + + <!-- Keep fox:alt-text and role attributes, discard everything else --> + <xsl:template match="@fox:alt-text|@role"> + <xsl:copy-of select="."/> + </xsl:template> + + <xsl:template match="@*"/> + + + <!-- Discard text nodes... --> + <xsl:template match="text()"/> + + <!-- ...except those that will result into marked content --> + <xsl:template match="fo:title/text() + |fo:block/text() + |fo:bidi-override/text() + |fo:inline/text() + |fo:basic-link/text() + |fo:wrapper/text() + |fo:marker/text()"> + <marked-content xmlns="http://xmlgraphics.apache.org/fop/intermediate"/> + </xsl:template> + +</xsl:stylesheet> diff --git a/test/java/org/apache/fop/accessibility/fo/table-footers.fo b/test/java/org/apache/fop/accessibility/fo/table-footers.fo new file mode 100644 index 000000000..6dcb9b68d --- /dev/null +++ b/test/java/org/apache/fop/accessibility/fo/table-footers.fo @@ -0,0 +1,195 @@ +<?xml version="1.0" standalone="no"?> +<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format"> + <fo:layout-master-set> + <fo:simple-page-master master-name="page" + page-height="440pt" page-width="420pt" margin="10pt"> + <fo:region-body display-align="center"/> + </fo:simple-page-master> + </fo:layout-master-set> + <fo:page-sequence master-reference="page"> + <fo:flow flow-name="xsl-region-body" line-height="10pt" font-size="8pt"> + <fo:table width="100% - 6pt" table-layout="fixed" + border-collapse="separate" border="2pt solid black" border-separation="2pt" padding="1pt" + start-indent="3pt" end-indent="3pt" space-after="2pt"> + <fo:table-header start-indent="0" end-indent="0"> + <fo:table-cell background-color="#E0E0E0" padding="2pt"> + <fo:block>Start Outer Header</fo:block> + <fo:table width="100% - 6pt" table-layout="fixed" + border="2pt solid red" padding="1pt" + start-indent="3pt" end-indent="3pt" space-after="2pt"> + <fo:table-header start-indent="0" end-indent="0"> + <fo:table-cell background-color="#FFB0B0" padding="2pt"> + <fo:block>Inner Header 1.1</fo:block> + </fo:table-cell> + <fo:table-cell background-color="#FFB0B0" padding="2pt"> + <fo:block>Inner Header 1.2</fo:block> + </fo:table-cell> + </fo:table-header> + <fo:table-footer start-indent="0" end-indent="0"> + <fo:table-cell background-color="#FFB0B0" padding="2pt"> + <fo:block>Inner Footer 1.1</fo:block> + </fo:table-cell> + <fo:table-cell background-color="#FFB0B0" padding="2pt"> + <fo:block>Inner Footer 1.2</fo:block> + </fo:table-cell> + </fo:table-footer> + <fo:table-body start-indent="0" end-indent="0"> + <fo:table-row> + <fo:table-cell background-color="#FFB0B0" padding="2pt"> + <fo:block>Inner Body 1.1</fo:block> + </fo:table-cell> + <fo:table-cell background-color="#FFB0B0" padding="2pt"> + <fo:block>Inner Body 1.2</fo:block> + </fo:table-cell> + </fo:table-row> + <fo:table-row> + <fo:table-cell background-color="#FFB0B0" padding="2pt"> + <fo:block>Inner Body 2.1</fo:block> + </fo:table-cell> + <fo:table-cell background-color="#FFB0B0" padding="2pt"> + <fo:block>Inner Body 2.2</fo:block> + </fo:table-cell> + </fo:table-row> + </fo:table-body> + </fo:table> + <fo:block>End Outer Header</fo:block> + </fo:table-cell> + </fo:table-header> + <fo:table-footer start-indent="0" end-indent="0"> + <fo:table-cell background-color="#E0E0E0" padding="2pt"> + <fo:block>Start Outer Footer</fo:block> + <fo:table width="100% - 6pt" table-layout="fixed" + border="2pt solid green" padding="1pt" + start-indent="3pt" end-indent="3pt" space-after="2pt"> + <fo:table-header start-indent="0" end-indent="0"> + <fo:table-cell background-color="lightgreen" padding="2pt"> + <fo:block>Inner Header 1.1</fo:block> + </fo:table-cell> + <fo:table-cell background-color="lightgreen" padding="2pt"> + <fo:block>Inner Header 1.2</fo:block> + </fo:table-cell> + </fo:table-header> + <fo:table-footer start-indent="0" end-indent="0"> + <fo:table-cell background-color="lightgreen" padding="2pt"> + <fo:block>Start Inner Footer 1.1</fo:block> + <fo:table width="100% - 6pt" table-layout="fixed" + border="2pt solid yellow" padding="1pt" + start-indent="3pt" end-indent="3pt" space-after="2pt"> + <fo:table-header start-indent="0" end-indent="0"> + <fo:table-cell background-color="yellow" padding="2pt"> + <fo:block>Inner Inner Header 1.1</fo:block> + </fo:table-cell> + <fo:table-cell background-color="yellow" padding="2pt"> + <fo:block>Inner Inner Header 1.2</fo:block> + </fo:table-cell> + </fo:table-header> + <fo:table-footer start-indent="0" end-indent="0"> + <fo:table-cell background-color="yellow" padding="2pt"> + <fo:block>Inner Inner Footer 1.1</fo:block> + </fo:table-cell> + <fo:table-cell background-color="yellow" padding="2pt"> + <fo:block>Inner Inner Footer 1.2</fo:block> + </fo:table-cell> + </fo:table-footer> + <fo:table-body start-indent="0" end-indent="0"> + <fo:table-row> + <fo:table-cell background-color="yellow" padding="2pt"> + <fo:block>Inner Inner Body 1.1</fo:block> + </fo:table-cell> + <fo:table-cell background-color="yellow" padding="2pt"> + <fo:block>Inner Inner Body 1.2</fo:block> + </fo:table-cell> + </fo:table-row> + <fo:table-row> + <fo:table-cell background-color="yellow" padding="2pt"> + <fo:block>Inner Inner Body 2.1</fo:block> + </fo:table-cell> + <fo:table-cell background-color="yellow" padding="2pt"> + <fo:block>Inner Inner Body 2.2</fo:block> + </fo:table-cell> + </fo:table-row> + </fo:table-body> + </fo:table> + <fo:block>End Inner Footer 1.1</fo:block> + </fo:table-cell> + <fo:table-cell background-color="lightgreen" padding="2pt"> + <fo:block>Inner Footer 1.2</fo:block> + </fo:table-cell> + </fo:table-footer> + <fo:table-body start-indent="0" end-indent="0"> + <fo:table-row> + <fo:table-cell background-color="lightgreen" padding="2pt"> + <fo:block>Inner Body 1.1</fo:block> + </fo:table-cell> + <fo:table-cell background-color="lightgreen" padding="2pt"> + <fo:block>Inner Body 1.2</fo:block> + </fo:table-cell> + </fo:table-row> + <fo:table-row> + <fo:table-cell background-color="lightgreen" padding="2pt"> + <fo:block>Inner Body 2.1</fo:block> + </fo:table-cell> + <fo:table-cell background-color="lightgreen" padding="2pt"> + <fo:block>Inner Body 2.2</fo:block> + </fo:table-cell> + </fo:table-row> + </fo:table-body> + </fo:table> + <fo:block>End Outer Footer</fo:block> + </fo:table-cell> + </fo:table-footer> + <fo:table-body start-indent="0" end-indent="0"> + <fo:table-row> + <fo:table-cell background-color="#E0E0E0" padding="2pt"> + <fo:block>Outer Body Cell 1</fo:block> + </fo:table-cell> + </fo:table-row> + <fo:table-row> + <fo:table-cell background-color="#E0E0E0" padding="2pt"> + <fo:block>Start Outer Body Cell 2</fo:block> + <fo:table width="100% - 6pt" table-layout="fixed" + border="2pt solid blue" padding="1pt" + start-indent="3pt" end-indent="3pt" space-after="2pt"> + <fo:table-header start-indent="0" end-indent="0"> + <fo:table-cell background-color="lightblue" padding="2pt"> + <fo:block>Inner Footer 1.1</fo:block> + </fo:table-cell> + <fo:table-cell background-color="lightblue" padding="2pt"> + <fo:block>Inner Footer 1.2</fo:block> + </fo:table-cell> + </fo:table-header> + <fo:table-footer start-indent="0" end-indent="0"> + <fo:table-cell background-color="lightblue" padding="2pt"> + <fo:block>Inner Header 1.1</fo:block> + </fo:table-cell> + <fo:table-cell background-color="lightblue" padding="2pt"> + <fo:block>Inner Header 1.2</fo:block> + </fo:table-cell> + </fo:table-footer> + <fo:table-body start-indent="0" end-indent="0"> + <fo:table-row> + <fo:table-cell background-color="lightblue" padding="2pt"> + <fo:block>Inner Body 1.1</fo:block> + </fo:table-cell> + <fo:table-cell background-color="lightblue" padding="2pt"> + <fo:block>Inner Body 1.2</fo:block> + </fo:table-cell> + </fo:table-row> + <fo:table-row> + <fo:table-cell background-color="lightblue" padding="2pt"> + <fo:block>Inner Body 2.1</fo:block> + </fo:table-cell> + <fo:table-cell background-color="lightblue" padding="2pt"> + <fo:block>Inner Body 2.2</fo:block> + </fo:table-cell> + </fo:table-row> + </fo:table-body> + </fo:table> + <fo:block>End Outer Body Cell 2</fo:block> + </fo:table-cell> + </fo:table-row> + </fo:table-body> + </fo:table> + </fo:flow> + </fo:page-sequence> +</fo:root> diff --git a/test/java/org/apache/fop/accessibility/fo/wrapCompleteDocumentInTableFooter.xsl b/test/java/org/apache/fop/accessibility/fo/wrapCompleteDocumentInTableFooter.xsl new file mode 100644 index 000000000..9608b2fb9 --- /dev/null +++ b/test/java/org/apache/fop/accessibility/fo/wrapCompleteDocumentInTableFooter.xsl @@ -0,0 +1,66 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<!-- $Id$ --> +<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" + xmlns:fo="http://www.w3.org/1999/XSL/Format"> + + <xsl:template match="@*|node()" name="copy"> + <xsl:copy> + <xsl:apply-templates select="@*|node()"/> + </xsl:copy> + </xsl:template> + + + <xsl:template match="/"> + <fo:root> + <fo:layout-master-set> + <fo:simple-page-master master-name="page" + page-height="500pt" page-width="300pt" margin="20pt"> + <fo:region-body margin-top="20pt"/> + </fo:simple-page-master> + </fo:layout-master-set> + <xsl:apply-templates select="//fo:page-sequence"/> + </fo:root> + </xsl:template> + + <xsl:template match="fo:page-sequence"> + <fo:page-sequence master-reference="page"> + <xsl:apply-templates select="fo:flow"/> + </fo:page-sequence> + </xsl:template> + + <xsl:template match="fo:flow"> + <xsl:copy> + <xsl:apply-templates select="@*[not(starts-with(name(), 'space-before'))]"/> + <fo:table width="100%" table-layout="fixed"> + <fo:table-footer> + <fo:table-cell background-color="#F0F0F0"> + <xsl:apply-templates select="@*[starts-with(name(), 'space-before')]"/> + <xsl:apply-templates select="*"/> + </fo:table-cell> + </fo:table-footer> + <fo:table-body> + <fo:table-cell> + <fo:block>The content below is in the table footer.</fo:block> + </fo:table-cell> + </fo:table-body> + </fo:table> + </xsl:copy> + </xsl:template> + +</xsl:stylesheet> diff --git a/test/java/org/apache/fop/fo/DelegatingFOEventHandlerTestCase.java b/test/java/org/apache/fop/fo/DelegatingFOEventHandlerTestCase.java new file mode 100644 index 000000000..313379e02 --- /dev/null +++ b/test/java/org/apache/fop/fo/DelegatingFOEventHandlerTestCase.java @@ -0,0 +1,531 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.fo; + +import static org.junit.Assert.assertArrayEquals; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +import javax.xml.transform.Result; +import javax.xml.transform.Source; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerConfigurationException; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.sax.SAXResult; +import javax.xml.transform.stream.StreamSource; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; + +import org.apache.fop.apps.FOUserAgent; +import org.apache.fop.fo.FODocumentParser.FOEventHandlerFactory; +import org.apache.fop.fo.flow.BasicLink; +import org.apache.fop.fo.flow.Block; +import org.apache.fop.fo.flow.BlockContainer; +import org.apache.fop.fo.flow.Character; +import org.apache.fop.fo.flow.ExternalGraphic; +import org.apache.fop.fo.flow.Footnote; +import org.apache.fop.fo.flow.FootnoteBody; +import org.apache.fop.fo.flow.Inline; +import org.apache.fop.fo.flow.InstreamForeignObject; +import org.apache.fop.fo.flow.Leader; +import org.apache.fop.fo.flow.ListBlock; +import org.apache.fop.fo.flow.ListItem; +import org.apache.fop.fo.flow.ListItemBody; +import org.apache.fop.fo.flow.ListItemLabel; +import org.apache.fop.fo.flow.PageNumber; +import org.apache.fop.fo.flow.PageNumberCitation; +import org.apache.fop.fo.flow.PageNumberCitationLast; +import org.apache.fop.fo.flow.Wrapper; +import org.apache.fop.fo.flow.table.Table; +import org.apache.fop.fo.flow.table.TableBody; +import org.apache.fop.fo.flow.table.TableCell; +import org.apache.fop.fo.flow.table.TableColumn; +import org.apache.fop.fo.flow.table.TableFooter; +import org.apache.fop.fo.flow.table.TableHeader; +import org.apache.fop.fo.flow.table.TableRow; +import org.apache.fop.fo.pagination.Flow; +import org.apache.fop.fo.pagination.PageSequence; +import org.apache.fop.fo.pagination.Root; +import org.apache.fop.fo.pagination.StaticContent; + +/** + * Tests that {@link DelegatingFOEventHandler} does forward every event to its delegate + * event handler. + */ +public class DelegatingFOEventHandlerTestCase { + + private InputStream document; + + private List<String> expectedEvents; + + private List<String> actualEvents; + + private FODocumentParser documentParser; + + private class DelegatingFOEventHandlerTester extends FOEventHandler { + + DelegatingFOEventHandlerTester(FOUserAgent foUserAgent) { + super(foUserAgent); + } + + private final StringBuilder eventBuilder = new StringBuilder(); + + @Override + public void startDocument() throws SAXException { + actualEvents.add("start document"); + } + + @Override + public void endDocument() throws SAXException { + actualEvents.add("end document"); + } + + @Override + public void startRoot(Root root) { + startElement(root); + } + + @Override + public void endRoot(Root root) { + endElement(root); + } + + @Override + public void startPageSequence(PageSequence pageSeq) { + startElement(pageSeq); + } + + @Override + public void endPageSequence(PageSequence pageSeq) { + endElement(pageSeq); + } + + @Override + public void startPageNumber(PageNumber pagenum) { + startElement(pagenum); + } + + @Override + public void endPageNumber(PageNumber pagenum) { + endElement(pagenum); + } + + @Override + public void startPageNumberCitation(PageNumberCitation pageCite) { + startElement(pageCite); + } + + @Override + public void endPageNumberCitation(PageNumberCitation pageCite) { + endElement(pageCite); + } + + @Override + public void startPageNumberCitationLast(PageNumberCitationLast pageLast) { + startElement(pageLast); + } + + @Override + public void endPageNumberCitationLast(PageNumberCitationLast pageLast) { + endElement(pageLast); + } + + @Override + public void startFlow(Flow fl) { + startElement(fl); + } + + @Override + public void endFlow(Flow fl) { + endElement(fl); + } + + @Override + public void startBlock(Block bl) { + startElement(bl); + } + + @Override + public void endBlock(Block bl) { + endElement(bl); + } + + @Override + public void startBlockContainer(BlockContainer blc) { + startElement(blc); + } + + @Override + public void endBlockContainer(BlockContainer blc) { + endElement(blc); + } + + @Override + public void startInline(Inline inl) { + startElement(inl); + } + + @Override + public void endInline(Inline inl) { + endElement(inl); + } + + @Override + public void startTable(Table tbl) { + startElement(tbl); + } + + @Override + public void endTable(Table tbl) { + endElement(tbl); + } + + @Override + public void startColumn(TableColumn tc) { + startElement(tc); + } + + @Override + public void endColumn(TableColumn tc) { + endElement(tc); + } + + @Override + public void startHeader(TableHeader header) { + startElement(header); + } + + @Override + public void endHeader(TableHeader header) { + endElement(header); + } + + @Override + public void startFooter(TableFooter footer) { + startElement(footer); + } + + @Override + public void endFooter(TableFooter footer) { + endElement(footer); + } + + @Override + public void startBody(TableBody body) { + startElement(body); + } + + @Override + public void endBody(TableBody body) { + endElement(body); + } + + @Override + public void startRow(TableRow tr) { + startElement(tr); + } + + @Override + public void endRow(TableRow tr) { + endElement(tr); + } + + @Override + public void startCell(TableCell tc) { + startElement(tc); + } + + @Override + public void endCell(TableCell tc) { + endElement(tc); + } + + @Override + public void startList(ListBlock lb) { + startElement(lb); + } + + @Override + public void endList(ListBlock lb) { + endElement(lb); + } + + @Override + public void startListItem(ListItem li) { + startElement(li); + } + + @Override + public void endListItem(ListItem li) { + endElement(li); + } + + @Override + public void startListLabel(ListItemLabel listItemLabel) { + startElement(listItemLabel); + } + + @Override + public void endListLabel(ListItemLabel listItemLabel) { + endElement(listItemLabel); + } + + @Override + public void startListBody(ListItemBody listItemBody) { + startElement(listItemBody); + } + + @Override + public void endListBody(ListItemBody listItemBody) { + endElement(listItemBody); + } + + @Override + public void startStatic(StaticContent staticContent) { + startElement(staticContent); + } + + @Override + public void endStatic(StaticContent statisContent) { + endElement(statisContent); + } + + @Override + public void startLink(BasicLink basicLink) { + startElement(basicLink); + } + + @Override + public void endLink(BasicLink basicLink) { + endElement(basicLink); + } + + @Override + public void image(ExternalGraphic eg) { + startElement(eg); + endElement(eg); + } + + @Override + public void startInstreamForeignObject(InstreamForeignObject ifo) { + startElement(ifo); + } + + @Override + public void endInstreamForeignObject(InstreamForeignObject ifo) { + endElement(ifo); + } + + @Override + public void startFootnote(Footnote footnote) { + startElement(footnote); + } + + @Override + public void endFootnote(Footnote footnote) { + endElement(footnote); + } + + @Override + public void startFootnoteBody(FootnoteBody body) { + startElement(body); + } + + @Override + public void endFootnoteBody(FootnoteBody body) { + endElement(body); + } + + @Override + public void startLeader(Leader l) { + startElement(l); + } + + @Override + public void endLeader(Leader l) { + endElement(l); + } + + @Override + public void startWrapper(Wrapper wrapper) { + startElement(wrapper); + } + + @Override + public void endWrapper(Wrapper wrapper) { + endElement(wrapper); + } + + @Override + public void character(Character c) { + startElement(c); + endElement(c); + } + + private void startElement(FObj node) { + addEvent("start ", node); + } + + private void endElement(FObj node) { + addEvent("end ", node); + } + + private void addEvent(String event, FObj node) { + eventBuilder.append(event); + eventBuilder.append(node.getLocalName()); + addID(node); + actualEvents.add(eventBuilder.toString()); + eventBuilder.setLength(0); + } + + private void addID(FObj node) { + String id = node.getId(); + if (id != null && id.length() > 0) { + eventBuilder.append(" id=\""); + eventBuilder.append(id); + eventBuilder.append("\""); + } + } + } + + @Before + public void setUp() throws IOException { + setUpEvents(); + loadDocument(); + createDocumentParser(); + } + + private void setUpEvents() throws IOException { + loadDocument(); + loadExpectedEvents(); + actualEvents = new ArrayList<String>(expectedEvents.size()); + } + + private void loadDocument() { + document = getClass().getResourceAsStream("complete_document.fo"); + } + + private void loadExpectedEvents() throws IOException { + expectedEvents = new ArrayList<String>(); + InputStream xslt = getClass().getResourceAsStream("extract-events.xsl"); + try { + runXSLT(xslt); + } finally { + closeStream(xslt); + closeStream(document); + } + } + + private void runXSLT(InputStream xslt) { + Transformer transformer = createTransformer(xslt); + Source fo = new StreamSource(document); + Result result = createTransformOutputHandler(); + try { + transformer.transform(fo, result); + } catch (TransformerException e) { + throw new RuntimeException(e); + } + } + + private Transformer createTransformer(InputStream xslt) { + TransformerFactory transformerFactory = TransformerFactory.newInstance(); + try { + return transformerFactory.newTransformer(new StreamSource(xslt)); + } catch (TransformerConfigurationException e) { + throw new RuntimeException(e); + } + } + + private Result createTransformOutputHandler() { + return new SAXResult(new DefaultHandler() { + + private final StringBuilder event = new StringBuilder(); + + @Override + public void startElement(String uri, String localName, String qName, + Attributes attributes) throws SAXException { + event.setLength(0); + } + + @Override + public void characters(char[] ch, int start, int length) throws SAXException { + event.append(ch, start, length); + } + + @Override + public void endElement(String uri, String localName, String qName) throws SAXException { + expectedEvents.add(event.toString()); + } + + }); + } + + private void closeStream(InputStream stream) { + try { + stream.close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private void createDocumentParser() { + documentParser = FODocumentParser.newInstance(new FOEventHandlerFactory() { + + public FOEventHandler newFOEventHandler(FOUserAgent foUserAgent) { + return new DelegatingFOEventHandler( + new DelegatingFOEventHandlerTester(foUserAgent)) { + }; + } + }); + } + + @Test + public void testFOEventHandler() throws Exception { + documentParser.parse(document); + assertArrayEquals(expectedEvents.toArray(), actualEvents.toArray()); + } + + @After + public void unloadDocument() throws IOException { + document.close(); + } + + /** + * Prints the given list to {@code System.out}, each element on a new line. For + * debugging purpose. + * + * @param list a list + */ + public void printList(List<?> list) { + for (Object element : list) { + System.out.println(element); + } + } + +} diff --git a/test/java/org/apache/fop/fo/FODocumentParser.java b/test/java/org/apache/fop/fo/FODocumentParser.java new file mode 100644 index 000000000..a7574e49d --- /dev/null +++ b/test/java/org/apache/fop/fo/FODocumentParser.java @@ -0,0 +1,161 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.fo; + +import java.io.InputStream; + +import javax.xml.transform.Result; +import javax.xml.transform.Source; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerConfigurationException; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.sax.SAXResult; +import javax.xml.transform.stream.StreamSource; + +import org.apache.fop.apps.FOPException; +import org.apache.fop.apps.FOUserAgent; +import org.apache.fop.apps.Fop; +import org.apache.fop.apps.FopFactory; +import org.apache.fop.events.EventListener; + +/** + * Parse an FO document and run the corresponding FO events through a given + * {@link FOEventHandler} instance. That instance is created using the helper + * {@link FOEventHandlerFactory}. + * + * <p>An instance of this class may not be used in multiple threads concurrently.<p> + * + * <p>An instance of this class may be used multiple times if the given + * {@link FOEventHandler} implementation can be used multiple times. + */ +public final class FODocumentParser { + + private static final TransformerFactory TRANSFORMER_FACTORY = TransformerFactory.newInstance(); + + private static final FopFactory FOP_FACTORY = FopFactory.newInstance(); + + private final FOEventHandlerFactory foEventHandlerFactory; + + private Fop fop; + + private Transformer transformer; + + private EventListener eventListener; + + /** + * A factory to create custom instances of {@link FOEventHandler}. + */ + public static interface FOEventHandlerFactory { + + /** + * Creates a new {@code FOEventHandler} instance parameterized with the given FO user agent. + * + * @param foUserAgent an FO user agent + * @return a new {@code FOEventHandler} instance + */ + FOEventHandler newFOEventHandler(FOUserAgent foUserAgent); + } + + private FODocumentParser(FOEventHandlerFactory foeEventHandlerFactory) { + this.foEventHandlerFactory = foeEventHandlerFactory; + } + + /** + * Creates and returns a new FO document parser. The given factory will be used to + * customize the handler that will receive FO events, using the + * {@link FOUserAgent#setFOEventHandlerOverride(FOEventHandler)} method. + * + * @param foEventHandlerFactory the factory to be used to create {@code + * FOEventHandler} instances + * @return a new parser + */ + public static FODocumentParser newInstance(FOEventHandlerFactory foEventHandlerFactory) { + return new FODocumentParser(foEventHandlerFactory); + } + + /** + * Sets the event listener to be used if events occurs when parsing the document. + * + * @param eventListener an event listener + */ + public void setEventListener(EventListener eventListener) { + this.eventListener = eventListener; + } + + /** + * Runs FOP on the given document. + * + * @param document XSL-FO document to parse + * @throws FOPException if an error occurs when initializing FOP + * @throws LoadingException if an error occurs when parsing the document + */ + public void parse(InputStream document) throws FOPException, LoadingException { + parse(document, createFOUserAgent()); + } + + /** + * Runs FOP on the given document with the supplied {@link FOUserAgent}. + * + * @param document XSL-FO document to parse + * @param foUserAgent The user agent + * @throws FOPException if an error occurs when initializing FOP + * @throws LoadingException if an error occurs when parsing the document + */ + public void parse(InputStream document, FOUserAgent foUserAgent) + throws FOPException, LoadingException { + fop = FOP_FACTORY.newFop(foUserAgent); + createTransformer(); + runTransformer(document); + } + + /** + * Creates a new {@link FOUserAgent}. + * @return It + */ + public FOUserAgent createFOUserAgent() { + FOUserAgent userAgent = FOP_FACTORY.newFOUserAgent(); + FOEventHandler foEventHandler = foEventHandlerFactory.newFOEventHandler(userAgent); + userAgent.setFOEventHandlerOverride(foEventHandler); + if (eventListener != null) { + userAgent.getEventBroadcaster().addEventListener(eventListener); + } + return userAgent; + } + + private void createTransformer() { + try { + transformer = TRANSFORMER_FACTORY.newTransformer(); + } catch (TransformerConfigurationException e) { + throw new RuntimeException(e); + } + } + + private void runTransformer(InputStream input) throws LoadingException, FOPException { + Source source = new StreamSource(input); + Result result = new SAXResult(fop.getDefaultHandler()); + try { + transformer.transform(source, result); + } catch (TransformerException e) { + Throwable cause = e.getCause(); + throw new LoadingException(cause == null ? e : cause); + } + } +} diff --git a/test/java/org/apache/fop/fo/FONodeMocks.java b/test/java/org/apache/fop/fo/FONodeMocks.java new file mode 100644 index 000000000..1310d4a78 --- /dev/null +++ b/test/java/org/apache/fop/fo/FONodeMocks.java @@ -0,0 +1,88 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.fo; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.io.IOException; + +import org.apache.xmlgraphics.image.loader.ImageException; +import org.apache.xmlgraphics.image.loader.ImageManager; +import org.apache.xmlgraphics.image.loader.ImageSessionContext; + +import org.apache.fop.apps.FOUserAgent; +import org.apache.fop.apps.FopFactory; + +/** + * A helper class for creating mocks of {@link FONode} and its descendants. + */ +public final class FONodeMocks { + + private FONodeMocks() { } + + /** + * Creates and returns a mock {@link FONode} configured with a mock + * {@link FOEventHandler}. The FO event handler returns a mock {@link FOUserAgent}, + * which in turn returns a mock {@link FopFactory}, which returns a mock + * {@link ImageManager}. + * + * @return a mock FO node + */ + public static FONode mockFONode() { + FONode mockFONode = mock(FONode.class); + mockGetFOEventHandler(mockFONode); + return mockFONode; + } + + private static void mockGetFOEventHandler(FONode mockFONode) { + FOEventHandler mockFOEventHandler = mock(FOEventHandler.class); + mockGetUserAgent(mockFOEventHandler); + when(mockFONode.getFOEventHandler()).thenReturn(mockFOEventHandler); + } + + private static void mockGetUserAgent(FOEventHandler mockFOEventHandler) { + FOUserAgent mockFOUserAgent = mock(FOUserAgent.class); + mockGetFactory(mockFOUserAgent); + when(mockFOEventHandler.getUserAgent()).thenReturn(mockFOUserAgent); + } + + private static void mockGetFactory(FOUserAgent mockFOUserAgent) { + FopFactory mockFopFactory = mock(FopFactory.class); + mockGetImageManager(mockFopFactory); + when(mockFOUserAgent.getFactory()).thenReturn(mockFopFactory); + } + + private static void mockGetImageManager(FopFactory mockFopFactory) { + try { + ImageManager mockImageManager = mock(ImageManager.class); + when(mockImageManager.getImageInfo(anyString(), any(ImageSessionContext.class))) + .thenReturn(null); + when(mockFopFactory.getImageManager()).thenReturn(mockImageManager); + } catch (ImageException e) { + throw new RuntimeException(e); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/test/java/org/apache/fop/fo/LoadingException.java b/test/java/org/apache/fop/fo/LoadingException.java new file mode 100644 index 000000000..a5d509209 --- /dev/null +++ b/test/java/org/apache/fop/fo/LoadingException.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.fo; + +/** + * This class specifies an exceptional condition that occurred while an XSL-FO document + * was being parsed. + */ +public class LoadingException extends Exception { + + private static final long serialVersionUID = 7529029475875542916L; + + LoadingException(Throwable cause) { + super(cause); + } + +} diff --git a/test/java/org/apache/fop/fo/complete_document.fo b/test/java/org/apache/fop/fo/complete_document.fo new file mode 100644 index 000000000..5a34e9e9a --- /dev/null +++ b/test/java/org/apache/fop/fo/complete_document.fo @@ -0,0 +1,176 @@ +<?xml version="1.0" standalone="no"?> +<!-- + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<!-- $Id$ --> +<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" + xmlns:fox="http://xmlgraphics.apache.org/fop/extensions"> + + <fo:layout-master-set> + <fo:simple-page-master master-name="page" + page-height="400pt" page-width="300pt" margin="20pt" margin-top="10pt"> + <fo:region-body margin-top="20pt"/> + <fo:region-before extent="15pt"/> + </fo:simple-page-master> + </fo:layout-master-set> + + <fo:page-sequence master-reference="page"> + <fo:static-content flow-name="xsl-region-before"> + <fo:block id="1" font-size="7pt" text-align-last="justify" padding-bottom="2pt" + border-bottom="0.25pt solid black">This is the page header<fo:leader/>Page <fo:page-number + id="2"/></fo:block> + </fo:static-content> + <fo:static-content flow-name="xsl-footnote-separator"> + <fo:block id="3"><fo:leader leader-length="100pt" leader-pattern="rule"/></fo:block> + </fo:static-content> + <fo:flow flow-name="xsl-region-body"> + <fo:block id="4">This is a link to the <fo:wrapper id="5" color="blue"><fo:basic-link id="6" + internal-destination="second-start">next page-sequence</fo:basic-link></fo:wrapper> + (which starts on page <fo:page-number-citation id="7" ref-id="second-start"/> and ends on + page <fo:page-number-citation-last id="8" ref-id="second-end"/>).</fo:block> + <fo:block id="9" font-family="sans-serif" font-weight="bold" space-before="1em" + space-after="0.2em" role="H1"><fo:block id="10">A Title Block</fo:block></fo:block> + <fo:block id="11">This block of text contains a footnote<fo:footnote id="12"><fo:inline id="13" + baseline-shift="super" font-size="70%">1</fo:inline><fo:footnote-body id="14"><fo:block + id="15">A footnote with a link to the <fo:wrapper id="16" color="blue"><fo:basic-link + id="17" external-destination="http://xmlgraphics.apache.org/fop/">FOP + website</fo:basic-link></fo:wrapper></fo:block></fo:footnote-body></fo:footnote> + call.</fo:block> + <fo:table id="18" space-before="1em" width="100%" table-layout="fixed"> + <fo:table-column id="19" column-width="proportional-column-width(1)"/> + <fo:table-column id="20" column-width="proportional-column-width(2)"/> + <fo:table-header id="21"> + <fo:table-row id="22"> + <fo:table-cell id="23" border="2pt solid black" padding="2pt 2pt 0"> + <fo:block id="24">Header 1.1</fo:block> + </fo:table-cell> + <fo:table-cell id="25" border="2pt solid black" padding="2pt 2pt 0"> + <fo:block id="26">Header 1.2</fo:block> + </fo:table-cell> + </fo:table-row> + </fo:table-header> + <fo:table-footer id="27"> + <fo:table-row id="28"> + <fo:table-cell id="29" border="2pt solid black" padding="2pt 2pt 0"> + <fo:block id="30">Footer 1.1</fo:block> + </fo:table-cell> + <fo:table-cell id="31" border="2pt solid black" padding="2pt 2pt 0"> + <fo:block id="32">Footer 1.2</fo:block> + </fo:table-cell> + </fo:table-row> + </fo:table-footer> + <fo:table-body id="33"> + <fo:table-row id="34"> + <fo:table-cell id="35" border="1pt solid black" padding="2pt 2pt 0"> + <fo:block id="36">Cell 1.1</fo:block> + </fo:table-cell> + <fo:table-cell id="37" border="1pt solid black" padding="2pt 2pt 0"> + <fo:block id="38">Cell 1.2</fo:block> + </fo:table-cell> + </fo:table-row> + <fo:table-row id="39"> + <fo:table-cell id="40" border="1pt solid black" padding="2pt 2pt 0"> + <fo:block id="41">Cell 2.1</fo:block> + </fo:table-cell> + <fo:table-cell id="42" border="1pt solid black" padding="2pt 2pt 0"> + <fo:block id="43">Cell 2.2</fo:block> + </fo:table-cell> + </fo:table-row> + </fo:table-body> + </fo:table> + <fo:block-container id="44" space-before="1.2em"> + <fo:block-container id="45" absolute-position="absolute" top="6pt" right="2.5pt" + inline-progression-dimension="37%" padding="3pt 1pt 2pt 3pt" border="1.5pt solid + darkblue"> + <fo:block id="46" color="darkblue" font-size="80%">This is an absolutely positioned + block-container. Nullam interdum mattis ipsum sit amet molestie.</fo:block> + </fo:block-container> + <fo:block id="47" end-indent="37% + 15pt">Lorem ipsum dolor sit amet, consectetur adipiscing + elit. Integer vel lacinia diam. Etiam venenatis magna vel libero imperdiet + rhoncus.</fo:block> + </fo:block-container> + </fo:flow> + </fo:page-sequence> + + <fo:page-sequence master-reference="page"> + <fo:static-content id="48" flow-name="xsl-region-before"> + <fo:block id="49" font-size="7pt" text-align-last="justify" padding-bottom="2pt" + border-bottom="0.25pt solid black">This is the page header<fo:leader id="50"/>Page + <fo:page-number id="51"/></fo:block> + </fo:static-content> + <fo:flow flow-name="xsl-region-body" text-align="justify" space-before.minimum="8pt" + space-before.optimum="10pt" space-before.maximum="12pt"> + <fo:block id="second-start">Starting a new page-sequence.</fo:block> + <fo:block id="52" text-align="center">The <fo:external-graphic id="53" + src="test/resources/images/fop-logo-color-24bit.png" + inline-progression-dimension.maximum="50%" content-width="scale-to-fit" + alignment-adjust="-46%" alignment-baseline="middle" fox:alt-text="FOP Logo"/> + logo.</fo:block> + <fo:list-block id="54" provisional-distance-between-starts="15pt" + provisional-label-separation="0" space-before="inherit"> + <fo:list-item id="55"> + <fo:list-item-label id="56" end-indent="label-end()"> + <fo:block id="57">1.</fo:block> + </fo:list-item-label> + <fo:list-item-body id="58" start-indent="body-start()"> + <fo:block id="59">First item of a list</fo:block> + </fo:list-item-body> + </fo:list-item> + <fo:list-item id="60"> + <fo:list-item-label id="61" end-indent="label-end()"> + <fo:block id="62">2.</fo:block> + </fo:list-item-label> + <fo:list-item-body id="63" start-indent="body-start()"> + <fo:block id="64">Second item of a list</fo:block> + </fo:list-item-body> + </fo:list-item> + <fo:list-item id="65"> + <fo:list-item-label id="66" end-indent="label-end()"> + <fo:block id="67">3.</fo:block> + </fo:list-item-label> + <fo:list-item-body id="68" start-indent="body-start()"> + <fo:block id="69">Third item of a list</fo:block> + </fo:list-item-body> + </fo:list-item> + </fo:list-block> + <fo:block id="70" text-align="center"><fo:instream-foreign-object id="71" + inline-progression-dimension.maximum="50%" content-width="scale-to-fit" + fox:alt-text="An inline SVG"> + <svg xmlns="http://www.w3.org/2000/svg" width="319" height="286.6"> + <g style="fill-opacity:0.7; stroke:black; stroke-width:3" + transform="translate(0, 286.6) scale(1, -1) translate(100, 100)"> + <circle cx="50" cy="86.6" r="80" style="fill:red;"/> + <circle cx="0" cy="0" r="80" style="fill:green;"/> + <circle cx="100" cy="0" r="80" style="fill:blue;"/> + </g> + </svg> + </fo:instream-foreign-object></fo:block> + <fo:block id="72" space-before="inherit">A block containing an <fo:inline id="73" + border="0.5pt solid black" padding="2pt" padding-bottom="0">inline</fo:inline> + element.</fo:block> + <fo:block id="74" space-before="inherit">A block containing a fancy <fo:character id="75" + border="1pt solid black" padding="0 2pt 1pt 2pt" font-family="Symbol" character="♦"/> + character.</fo:block> + <fo:block id="76" space-before="inherit" text-align-last="justify">A leader with special + content: <fo:leader id="77" leader-pattern="use-content"><fo:inline id="78"><fo:character + id="79" character=" "/><fo:inline id="80" border="0.5pt solid black" + padding-left="2pt" padding-right="2pt"><fo:character id="81" baseline-shift="-10%" + character="•"/></fo:inline></fo:inline></fo:leader>.</fo:block> + <fo:block id="second-end" space-before="inherit">Ending the page-sequence.</fo:block> + </fo:flow> + </fo:page-sequence> + +</fo:root> diff --git a/test/java/org/apache/fop/fo/extract-events.xsl b/test/java/org/apache/fop/fo/extract-events.xsl new file mode 100644 index 000000000..6cf42c984 --- /dev/null +++ b/test/java/org/apache/fop/fo/extract-events.xsl @@ -0,0 +1,62 @@ +<?xml version="1.0" encoding="UTF-8"?> +<xsl:stylesheet version="1.0" + xmlns:xsl="http://www.w3.org/1999/XSL/Transform" + xmlns:fo="http://www.w3.org/1999/XSL/Format" + exclude-result-prefixes="fo"> + + <xsl:output indent="yes" omit-xml-declaration="yes"/> + + <xsl:template match="/"> + <event> + <xsl:text>start document</xsl:text> + </event> + <xsl:apply-templates/> + <event> + <xsl:text>end document</xsl:text> + </event> + </xsl:template> + + <xsl:template match="fo:root"> + <event>start root</event> + <xsl:apply-templates select="fo:page-sequence"/> + <event>end root</event> + </xsl:template> + + <xsl:template match="fo:*"> + <xsl:call-template name="process.node"> + <xsl:with-param name="id"> + <xsl:apply-templates select="@id"/> + </xsl:with-param> + </xsl:call-template> + </xsl:template> + + <!-- Those elements do not retrieve the id property. + This will have to be fixed at some point. --> + <xsl:template match="fo:footnote|fo:footnote-body"> + <xsl:call-template name="process.node"/> + </xsl:template> + + <xsl:template name="process.node"> + <xsl:param name="id" select="''"/> + <event> + <xsl:text>start </xsl:text> + <xsl:value-of select="local-name()"/> + <xsl:value-of select="$id"/> + </event> + <xsl:apply-templates/> + <event> + <xsl:text>end </xsl:text> + <xsl:value-of select="local-name()"/> + <xsl:value-of select="$id"/> + </event> + </xsl:template> + + <xsl:template match="@id"> + <xsl:text> id="</xsl:text> + <xsl:value-of select="."/> + <xsl:text>"</xsl:text> + </xsl:template> + + <xsl:template match="text()"/> + +</xsl:stylesheet> diff --git a/test/java/org/apache/fop/fo/flow/table/AbstractTableTest.java b/test/java/org/apache/fop/fo/flow/table/AbstractTableTest.java index 69bbc8d8c..fb6ec6a25 100644 --- a/test/java/org/apache/fop/fo/flow/table/AbstractTableTest.java +++ b/test/java/org/apache/fop/fo/flow/table/AbstractTableTest.java @@ -19,33 +19,37 @@ package org.apache.fop.fo.flow.table; +import java.io.FileInputStream; import java.util.Iterator; import org.apache.fop.apps.FOUserAgent; +import org.apache.fop.fo.FODocumentParser; import org.apache.fop.fo.FOEventHandler; -import org.apache.fop.fotreetest.FOTreeUnitTester; +import org.apache.fop.fo.FODocumentParser.FOEventHandlerFactory; +import org.apache.fop.util.ConsoleEventListenerForTests; /** * Superclass for testcases related to tables, factoring the common stuff. */ -abstract class AbstractTableTest extends FOTreeUnitTester { +abstract class AbstractTableTest { - private FOTreeUnitTester.FOEventHandlerFactory tableHandlerFactory; + private FODocumentParser documentParser; private TableHandler tableHandler; - public AbstractTableTest() throws Exception { - super(); - tableHandlerFactory = new FOEventHandlerFactory() { - public FOEventHandler createFOEventHandler(FOUserAgent foUserAgent) { + protected void setUp(String filename) throws Exception { + createDocumentParser(); + documentParser.setEventListener(new ConsoleEventListenerForTests(filename)); + documentParser.parse(new FileInputStream("test/fotree/unittests/" + filename)); + } + + private void createDocumentParser() { + documentParser = FODocumentParser.newInstance(new FOEventHandlerFactory() { + public FOEventHandler newFOEventHandler(FOUserAgent foUserAgent) { tableHandler = new TableHandler(foUserAgent); return tableHandler; } - }; - } - - protected void setUp(String filename) throws Exception { - setUp(filename, tableHandlerFactory); + }); } protected TableHandler getTableHandler() { diff --git a/test/java/org/apache/fop/fo/flow/table/CollapsedConditionalBorderTestCase.java b/test/java/org/apache/fop/fo/flow/table/CollapsedConditionalBorderTestCase.java index 0c567b976..7c0301ca7 100644 --- a/test/java/org/apache/fop/fo/flow/table/CollapsedConditionalBorderTestCase.java +++ b/test/java/org/apache/fop/fo/flow/table/CollapsedConditionalBorderTestCase.java @@ -25,10 +25,11 @@ import java.awt.Color; import java.util.Iterator; import java.util.List; +import org.junit.Test; + import org.apache.fop.fo.Constants; import org.apache.fop.fo.FONode.FONodeIterator; import org.apache.fop.fo.properties.CommonBorderPaddingBackground.BorderInfo; -import org.junit.Test; /** * A testcase for the resolution of collapsed borders in the FO tree, taking @@ -107,10 +108,6 @@ public class CollapsedConditionalBorderTestCase extends AbstractTableTest { {{border8pt, Color.black}, {border6pt, Color.blue}, {border8pt, Color.black}, {border6pt, Color.blue}, {border4pt, Color.black}, {border4pt, Color.black}, {border4pt, Color.red}, {border8pt, Color.black}, {border8pt, Color.black}, {border8pt, Color.black}, {border8pt, Color.black}, {border4pt, Color.blue}, {border4pt, Color.red}, {border6pt, Color.magenta}, {border6pt, Color.magenta}, {border6pt, Color.magenta}} }; - public CollapsedConditionalBorderTestCase() throws Exception { - super(); - } - private static GridUnit getGridUnit(TablePart part) { return (GridUnit) ((List) ((List) part.getRowGroups().get(0)).get(0)).get(0); } diff --git a/test/java/org/apache/fop/fo/flow/table/ErrorCheckTest.java b/test/java/org/apache/fop/fo/flow/table/ErrorCheckTest.java index 724259a75..b30c64c07 100644 --- a/test/java/org/apache/fop/fo/flow/table/ErrorCheckTest.java +++ b/test/java/org/apache/fop/fo/flow/table/ErrorCheckTest.java @@ -19,8 +19,10 @@ package org.apache.fop.fo.flow.table; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import org.apache.fop.fo.LoadingException; import org.apache.fop.fo.ValidationException; /** @@ -28,16 +30,13 @@ import org.apache.fop.fo.ValidationException; */ abstract class ErrorCheckTest extends AbstractTableTest { - public ErrorCheckTest() throws Exception { - super(); - } - protected void launchTest(String filename) throws Exception { try { setUp(filename); - fail(); - } catch (ValidationException e) { + fail("Expected ValidationException to be thrown"); + } catch (LoadingException e) { // TODO check location + assertTrue(e.getCause() instanceof ValidationException); } } diff --git a/test/java/org/apache/fop/fo/flow/table/IllegalRowSpanTestCase.java b/test/java/org/apache/fop/fo/flow/table/IllegalRowSpanTestCase.java index 024e15c2e..b2e7a2c9d 100644 --- a/test/java/org/apache/fop/fo/flow/table/IllegalRowSpanTestCase.java +++ b/test/java/org/apache/fop/fo/flow/table/IllegalRowSpanTestCase.java @@ -27,10 +27,6 @@ import org.junit.Test; */ public class IllegalRowSpanTestCase extends ErrorCheckTest { - public IllegalRowSpanTestCase() throws Exception { - super(); - } - @Test public void testBody1() throws Exception { launchTest("table/illegal-row-span_body_1.fo"); diff --git a/test/java/org/apache/fop/fo/flow/table/RowGroupBuilderTestCase.java b/test/java/org/apache/fop/fo/flow/table/RowGroupBuilderTestCase.java index 3df0338ab..361517a66 100644 --- a/test/java/org/apache/fop/fo/flow/table/RowGroupBuilderTestCase.java +++ b/test/java/org/apache/fop/fo/flow/table/RowGroupBuilderTestCase.java @@ -35,10 +35,6 @@ import org.junit.Test; */ public class RowGroupBuilderTestCase extends AbstractTableTest { - public RowGroupBuilderTestCase() throws Exception { - super(); - } - /** * Checks that the given table-body(header,footer) will return row groups as expected. * More precisely, checks that the number of row groups corresponds to the size of the diff --git a/test/java/org/apache/fop/fo/flow/table/TableColumnColumnNumberTestCase.java b/test/java/org/apache/fop/fo/flow/table/TableColumnColumnNumberTestCase.java index 4f7d9e054..a21806559 100644 --- a/test/java/org/apache/fop/fo/flow/table/TableColumnColumnNumberTestCase.java +++ b/test/java/org/apache/fop/fo/flow/table/TableColumnColumnNumberTestCase.java @@ -23,9 +23,10 @@ import static org.junit.Assert.assertEquals; import java.util.Iterator; +import org.junit.Test; + import org.apache.fop.datatypes.PercentBaseContext; import org.apache.fop.fo.FObj; -import org.junit.Test; public class TableColumnColumnNumberTestCase extends AbstractTableTest { @@ -50,10 +51,6 @@ public class TableColumnColumnNumberTestCase extends AbstractTableTest { private TablePercentBaseContext percentBaseContext = new TablePercentBaseContext(); - public TableColumnColumnNumberTestCase() throws Exception { - super(); - } - private void checkColumn(Table t, int number, boolean isImplicit, int spans, int repeated, int width) { TableColumn c = t.getColumn(number - 1); // TODO a repeated column has a correct number only for its first occurrence diff --git a/test/java/org/apache/fop/fo/flow/table/TooManyColumnsTestCase.java b/test/java/org/apache/fop/fo/flow/table/TooManyColumnsTestCase.java index 284fd1d2f..76a5d196d 100644 --- a/test/java/org/apache/fop/fo/flow/table/TooManyColumnsTestCase.java +++ b/test/java/org/apache/fop/fo/flow/table/TooManyColumnsTestCase.java @@ -23,10 +23,6 @@ import org.junit.Test; public class TooManyColumnsTestCase extends ErrorCheckTest { - public TooManyColumnsTestCase() throws Exception { - super(); - } - @Test public void testBody1() throws Exception { launchTest("table/too-many-columns_body_1.fo"); diff --git a/test/java/org/apache/fop/fo/flow/table/UnimplementedWarningNeutralizer.java b/test/java/org/apache/fop/fo/flow/table/UnimplementedWarningNeutralizer.java new file mode 100644 index 000000000..1a5e38291 --- /dev/null +++ b/test/java/org/apache/fop/fo/flow/table/UnimplementedWarningNeutralizer.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.fo.flow.table; + +/** + * This class aims at easing testing, by preventing the event notification system from + * getting in the way just to issue an Unimplemented Feature warning. + */ +public final class UnimplementedWarningNeutralizer { + + private UnimplementedWarningNeutralizer() { } + + /** + * Neutralizes Unimplemented Feature events from the {@link TableAndCaption} and + * {@link TableCaption} classes. + */ + public static void neutralizeUnimplementedWarning() { + TableAndCaption.notImplementedWarningGiven = true; + TableCaption.notImplementedWarningGiven = true; + } +} diff --git a/test/java/org/apache/fop/fo/properties/AltTextHolderTestCase.java b/test/java/org/apache/fop/fo/properties/AltTextHolderTestCase.java new file mode 100644 index 000000000..cd5d545ff --- /dev/null +++ b/test/java/org/apache/fop/fo/properties/AltTextHolderTestCase.java @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.fo.properties; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.junit.Test; + +import org.apache.fop.apps.FOPException; +import org.apache.fop.apps.FOUserAgent; +import org.apache.fop.fo.Constants; +import org.apache.fop.fo.FONode; +import org.apache.fop.fo.FONodeMocks; +import org.apache.fop.fo.PropertyList; +import org.apache.fop.fo.expr.PropertyException; +import org.apache.fop.fo.flow.AbstractGraphics; +import org.apache.fop.fo.flow.ExternalGraphic; +import org.apache.fop.fo.flow.InstreamForeignObject; + + +/** + * Tests that the fox:alt-text property is correctly set on objects that support it. + */ +public class AltTextHolderTestCase { + + private final String altText = "alternative text"; + + @Test + public void externalGraphicHasAltText() throws FOPException { + testAltTextGetter(new ExternalGraphic(mockFONode())); + } + + @Test + public void instreamForeignObjectHasAltText() throws FOPException { + testAltTextGetter(new InstreamForeignObject(mockFONode())); + } + + private FONode mockFONode() { + FONode mockFONode = FONodeMocks.mockFONode(); + FOUserAgent mockFOUserAgent = mockFONode.getFOEventHandler().getUserAgent(); + when(mockFOUserAgent.isAccessibilityEnabled()).thenReturn(true); + return mockFONode; + } + + private void testAltTextGetter(AbstractGraphics g) throws FOPException { + g.bind(mockPropertyList()); + assertEquals(altText, g.getAltText()); + } + + private PropertyList mockPropertyList() throws PropertyException { + PropertyList mockPropertyList = PropertyListMocks.mockPropertyList(); + Property mockAltText = mock(Property.class); + when(mockAltText.getString()).thenReturn(altText); + when(mockPropertyList.get(Constants.PR_X_ALT_TEXT)).thenReturn(mockAltText); + return mockPropertyList; + } + +} diff --git a/test/java/org/apache/fop/fo/properties/CommonAccessibilityHolderTestCase.java b/test/java/org/apache/fop/fo/properties/CommonAccessibilityHolderTestCase.java new file mode 100644 index 000000000..352a39713 --- /dev/null +++ b/test/java/org/apache/fop/fo/properties/CommonAccessibilityHolderTestCase.java @@ -0,0 +1,128 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.fo.properties; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.lang.reflect.Constructor; +import java.util.ArrayList; +import java.util.List; + +import org.junit.Test; + +import org.apache.fop.fo.Constants; +import org.apache.fop.fo.FONode; +import org.apache.fop.fo.FONodeMocks; +import org.apache.fop.fo.PropertyList; +import org.apache.fop.fo.expr.PropertyException; +import org.apache.fop.fo.flow.table.UnimplementedWarningNeutralizer; + +/** + * This tests that all the FONodes that implement CommonAccessibilityHolder correctly configure + * the CommonAccessibility property. + */ +public class CommonAccessibilityHolderTestCase { + + private static final List<Class<? extends CommonAccessibilityHolder>> IMPLEMENTATIONS + = new ArrayList<Class<? extends CommonAccessibilityHolder>>(); + + private final String role = "role"; + + private final String sourceDocument = "source document"; + + static { + /* This triggers 'unimplemented feature' FO validation events so that the event system is + * not triggered when testing, avoiding extra convoluted dependency stubbing. */ + UnimplementedWarningNeutralizer.neutralizeUnimplementedWarning(); + + IMPLEMENTATIONS.add(org.apache.fop.fo.flow.BasicLink.class); + IMPLEMENTATIONS.add(org.apache.fop.fo.flow.Block.class); + IMPLEMENTATIONS.add(org.apache.fop.fo.pagination.bookmarks.Bookmark.class); + IMPLEMENTATIONS.add(org.apache.fop.fo.pagination.bookmarks.BookmarkTitle.class); + IMPLEMENTATIONS.add(org.apache.fop.fo.flow.ExternalGraphic.class); + IMPLEMENTATIONS.add(org.apache.fop.fo.flow.Footnote.class); + IMPLEMENTATIONS.add(org.apache.fop.fo.flow.FootnoteBody.class); + IMPLEMENTATIONS.add(org.apache.fop.fo.flow.InitialPropertySet.class); + IMPLEMENTATIONS.add(org.apache.fop.fo.flow.Inline.class); + IMPLEMENTATIONS.add(org.apache.fop.fo.flow.InstreamForeignObject.class); + IMPLEMENTATIONS.add(org.apache.fop.fo.flow.Leader.class); + IMPLEMENTATIONS.add(org.apache.fop.fo.flow.ListBlock.class); + IMPLEMENTATIONS.add(org.apache.fop.fo.flow.ListItem.class); + IMPLEMENTATIONS.add(org.apache.fop.fo.flow.ListItemBody.class); + IMPLEMENTATIONS.add(org.apache.fop.fo.flow.ListItemLabel.class); + IMPLEMENTATIONS.add(org.apache.fop.fo.flow.PageNumber.class); + IMPLEMENTATIONS.add(org.apache.fop.fo.flow.PageNumberCitation.class); + IMPLEMENTATIONS.add(org.apache.fop.fo.flow.PageNumberCitationLast.class); + IMPLEMENTATIONS.add(org.apache.fop.fo.pagination.Root.class); + IMPLEMENTATIONS.add(org.apache.fop.fo.flow.table.Table.class); + IMPLEMENTATIONS.add(org.apache.fop.fo.flow.table.TableAndCaption.class); + IMPLEMENTATIONS.add(org.apache.fop.fo.flow.table.TableBody.class); + IMPLEMENTATIONS.add(org.apache.fop.fo.flow.table.TableCaption.class); + IMPLEMENTATIONS.add(org.apache.fop.fo.flow.table.TableCell.class); + IMPLEMENTATIONS.add(org.apache.fop.fo.flow.table.TableFooter.class); + IMPLEMENTATIONS.add(org.apache.fop.fo.flow.table.TableHeader.class); + IMPLEMENTATIONS.add(org.apache.fop.fo.flow.table.TableRow.class); + IMPLEMENTATIONS.add(org.apache.fop.fo.pagination.Title.class); + } + + /** + * Bind should be overridden to correctly configure the CommonAccessibility property + * @throws Exception - + */ + @Test + public void bindMustSetRoleAndSourceDoc() throws Exception { + final PropertyList mockPList = mockPropertyList(); + final FONode parent = FONodeMocks.mockFONode(); + for (Class<? extends CommonAccessibilityHolder> clazz : IMPLEMENTATIONS) { + Constructor<? extends CommonAccessibilityHolder> constructor + = clazz.getConstructor(FONode.class); + CommonAccessibilityHolder sut = constructor.newInstance(parent); + ((FONode)sut).bind(mockPList); + String errorMessage = "Test failed for " + clazz + ": "; + assertEquals(errorMessage, role, sut.getCommonAccessibility().getRole()); + assertEquals(errorMessage, sourceDocument, + sut.getCommonAccessibility().getSourceDocument()); + } + } + + private PropertyList mockPropertyList() throws PropertyException { + final PropertyList mockPList = PropertyListMocks.mockPropertyList(); + PropertyListMocks.mockTableProperties(mockPList); + PropertyListMocks.mockCommonBorderPaddingBackgroundProps(mockPList); + mockRoleProperty(mockPList); + mockSourceDocProperty(mockPList); + return mockPList; + } + + private void mockRoleProperty(PropertyList mockPList) throws PropertyException { + final Property mockRoleProperty = mock(Property.class); + when(mockRoleProperty.getString()).thenReturn(role); + when(mockPList.get(Constants.PR_ROLE)).thenReturn(mockRoleProperty); + } + + private void mockSourceDocProperty(PropertyList mockPList) throws PropertyException { + final Property mockSourceDocProperty = mock(Property.class); + when(mockSourceDocProperty.getString()).thenReturn(sourceDocument); + when(mockPList.get(Constants.PR_SOURCE_DOCUMENT)).thenReturn(mockSourceDocProperty); + } + +} diff --git a/test/java/org/apache/fop/fo/properties/PropertyListMocks.java b/test/java/org/apache/fop/fo/properties/PropertyListMocks.java new file mode 100644 index 000000000..380f6e5a8 --- /dev/null +++ b/test/java/org/apache/fop/fo/properties/PropertyListMocks.java @@ -0,0 +1,94 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.fo.properties; + +import static org.mockito.Matchers.anyInt; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.apache.fop.fo.Constants; +import org.apache.fop.fo.PropertyList; +import org.apache.fop.fo.expr.PropertyException; + +/** + * A helper class for mocking a property list. + */ +public final class PropertyListMocks { + + private PropertyListMocks() { } + + /** + * Creates and returns a mock property list returning a generic default for the + * {@link PropertyList#get(int)} method. + * + * @return a mock property list + */ + public static PropertyList mockPropertyList() { + try { + final PropertyList mockPList = mock(PropertyList.class); + final Property mockGenericProperty = PropertyMocks.mockGenericProperty(); + when(mockPList.get(anyInt())).thenReturn(mockGenericProperty); + return mockPList; + } catch (PropertyException e) { + throw new RuntimeException(e); + } + } + + /** + * Overrides with working mock properties the values returned by + * {@link PropertyList#get(int)} for {@link Constants#PR_COLUMN_NUMBER}, + * {@link Constants#PR_NUMBER_COLUMNS_SPANNED}, + * {@link Constants#PR_NUMBER_ROWS_SPANNED} and {@link Constants#PR_BORDER_COLLAPSE}. + * + * @param mockPList a mock property list + */ + public static void mockTableProperties(PropertyList mockPList) { + try { + final Property mockNumberProperty = PropertyMocks.mockNumberProperty(); + when(mockPList.get(Constants.PR_COLUMN_NUMBER)).thenReturn(mockNumberProperty); + when(mockPList.get(Constants.PR_NUMBER_COLUMNS_SPANNED)).thenReturn(mockNumberProperty); + when(mockPList.get(Constants.PR_NUMBER_ROWS_SPANNED)).thenReturn(mockNumberProperty); + + final Property borderCollapseProperty = mock(Property.class); + when(borderCollapseProperty.getEnum()).thenReturn(Constants.EN_SEPARATE); + when(mockPList.get(Constants.PR_BORDER_COLLAPSE)).thenReturn(borderCollapseProperty); + } catch (PropertyException e) { + throw new RuntimeException(e); + } + } + + /** + * Overrides with a working mock property the value returned by + * {@link PropertyList#getBorderPaddingBackgroundProps()}. + * + * @param mockPList a mock property list + */ + public static void mockCommonBorderPaddingBackgroundProps(PropertyList mockPList) { + try { + final CommonBorderPaddingBackground mockCommonBorderPaddingBackground + = mock(CommonBorderPaddingBackground.class); + when(mockPList.getBorderPaddingBackgroundProps()) + .thenReturn(mockCommonBorderPaddingBackground); + } catch (PropertyException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/test/java/org/apache/fop/fo/properties/PropertyMocks.java b/test/java/org/apache/fop/fo/properties/PropertyMocks.java new file mode 100644 index 000000000..40c923249 --- /dev/null +++ b/test/java/org/apache/fop/fo/properties/PropertyMocks.java @@ -0,0 +1,80 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.fo.properties; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.apache.fop.datatypes.Numeric; +import org.apache.fop.datatypes.PercentBaseContext; +import org.apache.fop.fo.Constants; + +/** + * Helper class to create mocks of various kinds of properties. + */ +public final class PropertyMocks { + + private PropertyMocks() { } + + /** + * Creates and returns a generic mock property returning decent defaults for the + * {@link Property#getString()}, {@link Property#getEnum()} and + * {@link Property#getLengthRange()} methods. + * + * @return a mock all-purpose property + */ + public static Property mockGenericProperty() { + final Property mockGenericProperty = mock(Property.class); + when(mockGenericProperty.getString()).thenReturn("A non-empty string"); + when(mockGenericProperty.getEnum()).thenReturn(Constants.EN_SPACE); + LengthRangeProperty lengthRangeProperty = mockLengthRangeProperty(); + when(mockGenericProperty.getLengthRange()).thenReturn(lengthRangeProperty); + return mockGenericProperty; + } + + private static LengthRangeProperty mockLengthRangeProperty() { + final LengthRangeProperty mockLengthRangeProperty = mock(LengthRangeProperty.class); + final Property optimum = mockOptimumProperty(); + when(mockLengthRangeProperty.getOptimum(any(PercentBaseContext.class))) + .thenReturn(optimum); + return mockLengthRangeProperty; + } + + /** + * Creates and returns a mock property returning a decent default for the + * {@link Property#getNumeric()} method. + * + * @return a mock number property + */ + public static Property mockNumberProperty() { + final Property mockNumberProperty = mock(Property.class); + final Numeric mockNumeric = mock(Numeric.class); + when(mockNumberProperty.getNumeric()).thenReturn(mockNumeric); + return mockNumberProperty; + } + + private static Property mockOptimumProperty() { + final Property optimum = mock(Property.class); + when(optimum.isAuto()).thenReturn(true); + return optimum; + } + +} diff --git a/test/java/org/apache/fop/fotreetest/FOTreeTestSuite.java b/test/java/org/apache/fop/fotreetest/FOTreeTestSuite.java index 4a77ca000..b74afefc5 100644 --- a/test/java/org/apache/fop/fotreetest/FOTreeTestSuite.java +++ b/test/java/org/apache/fop/fotreetest/FOTreeTestSuite.java @@ -29,7 +29,9 @@ import org.junit.runners.Suite; @Suite.SuiteClasses({ org.apache.fop.fo.flow.table.AllTests.class, org.apache.fop.fo.pagination.AllTests.class, - FOTreeTestCase.class }) - + org.apache.fop.fotreetest.FOTreeTestCase.class, + org.apache.fop.fo.properties.CommonAccessibilityHolderTestCase.class, + org.apache.fop.fo.DelegatingFOEventHandlerTestCase.class +}) public final class FOTreeTestSuite { } diff --git a/test/java/org/apache/fop/fotreetest/FOTreeUnitTester.java b/test/java/org/apache/fop/fotreetest/FOTreeUnitTester.java deleted file mode 100644 index 0906c266d..000000000 --- a/test/java/org/apache/fop/fotreetest/FOTreeUnitTester.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* $Id$ */ - -package org.apache.fop.fotreetest; - -import java.io.File; - -import javax.xml.parsers.SAXParser; -import javax.xml.parsers.SAXParserFactory; - -import org.apache.fop.apps.FOUserAgent; -import org.apache.fop.apps.Fop; -import org.apache.fop.apps.FopFactory; -import org.apache.fop.fo.FOEventHandler; -import org.apache.fop.util.ConsoleEventListenerForTests; -import org.xml.sax.XMLReader; - - -/** - * Base class for unit-testing the FO tree building code. It performs the necessary setup - * to parse an FO file and register a proper {@link FOEventHandler}. That handler will be - * the entry point to test classes from the FObj hierarchy. - */ -public abstract class FOTreeUnitTester { - - private XMLReader foReader; - - private FopFactory fopFactory; - - /** - * Should be implemented by children testcases for properly setting up the custom - * FOEventHandler needed to test FObj classes. - */ - public abstract static class FOEventHandlerFactory { - - /** - * This method is called by FOTreeUnitTester when creating a {@link Fop} instance. - * That lets pass to the custom FOEventHandler the proper user agent that will be - * used by this instance. - * - * @param foUserAgent the user agent needed by the Fop instance that will be used - * to create the FO tree - * @return the appropriate FOEventHandler for performing the tests - */ - public abstract FOEventHandler createFOEventHandler(FOUserAgent foUserAgent); - } - - public FOTreeUnitTester() throws Exception { - // Stuff that needs to be set up only once and will be re-used for each test - SAXParserFactory spf = SAXParserFactory.newInstance(); - spf.setNamespaceAware(true); - spf.setValidating(false); - SAXParser parser; - parser = spf.newSAXParser(); - foReader = parser.getXMLReader(); - fopFactory = FopFactory.newInstance(); - } - - /** - * Launches FOP on the given FO file. - * - * @param filename path to the test FO file - * @param factory to create the appropriate FOEventHandler for performing tests - */ - public void setUp(String filename, FOEventHandlerFactory factory) throws Exception { - FOUserAgent ua = fopFactory.newFOUserAgent(); - ua.setFOEventHandlerOverride(factory.createFOEventHandler(ua)); - ua.getEventBroadcaster().addEventListener( - new ConsoleEventListenerForTests(filename)); - - Fop fop = fopFactory.newFop(ua); - - foReader.setContentHandler(fop.getDefaultHandler()); - foReader.setDTDHandler(fop.getDefaultHandler()); - foReader.setErrorHandler(fop.getDefaultHandler()); - foReader.setEntityResolver(fop.getDefaultHandler()); - - foReader.parse(new File("test/fotree/unittests/" + filename).toURI().toURL().toExternalForm()); - } -} diff --git a/test/java/org/apache/fop/intermediate/IFParserTestCase.java b/test/java/org/apache/fop/intermediate/IFParserTestCase.java index c37897dc1..7d4fb7ad8 100644 --- a/test/java/org/apache/fop/intermediate/IFParserTestCase.java +++ b/test/java/org/apache/fop/intermediate/IFParserTestCase.java @@ -48,6 +48,9 @@ import org.apache.fop.render.intermediate.IFSerializer; @RunWith(Parameterized.class) public class IFParserTestCase extends AbstractIFTest { + /** Set this to true to get the correspondence between test number and test file. */ + private static final boolean DEBUG = false; + /** * Gets the parameters for this test * @@ -56,7 +59,19 @@ public class IFParserTestCase extends AbstractIFTest { */ @Parameters public static Collection<File[]> getParameters() throws IOException { - return LayoutEngineTestUtils.getLayoutTestFiles(); + Collection<File[]> testFiles = LayoutEngineTestUtils.getLayoutTestFiles(); + if (DEBUG) { + printFiles(testFiles); + } + return testFiles; + } + + private static void printFiles(Collection<File[]> files) { + int index = 0; + for (File[] file : files) { + assert file.length == 1; + System.out.println(String.format("%3d %s", index++, file[0])); + } } /** diff --git a/test/java/org/apache/fop/render/intermediate/IFStructureTreeBuilderTestCase.java b/test/java/org/apache/fop/render/intermediate/IFStructureTreeBuilderTestCase.java new file mode 100644 index 000000000..65c6b25a6 --- /dev/null +++ b/test/java/org/apache/fop/render/intermediate/IFStructureTreeBuilderTestCase.java @@ -0,0 +1,169 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.intermediate; + +import static org.junit.Assert.fail; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.argThat; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentMatcher; +import org.mockito.InOrder; +import org.xml.sax.Attributes; +import org.xml.sax.ContentHandler; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.AttributesImpl; + +import org.apache.fop.fo.FOElementMapping; +import org.apache.fop.fo.extensions.ExtensionElementMapping; +import org.apache.fop.fo.extensions.InternalElementMapping; +import org.apache.fop.util.XMLUtil; + +public class IFStructureTreeBuilderTestCase { + + private IFStructureTreeBuilder sut; + + @Before + public void setUp() { + sut = new IFStructureTreeBuilder(); + } + + @Test + public void startAndEndPageSequence() throws SAXException { + final ContentHandler handler = mock(ContentHandler.class); + + try { + sut.replayEventsForPageSequence(handler, 0); + fail("No page sequences created"); + } catch (IndexOutOfBoundsException e) { + // Expected + } + + sut.startPageSequence(null); + sut.endPageSequence(); + + sut.replayEventsForPageSequence(handler, 0); + + InOrder inOrder = inOrder(handler); + + inOrder.verify(handler).startPrefixMapping( + InternalElementMapping.STANDARD_PREFIX, InternalElementMapping.URI); + inOrder.verify(handler).startPrefixMapping( + ExtensionElementMapping.STANDARD_PREFIX, ExtensionElementMapping.URI); + inOrder.verify(handler).startElement(eq(IFConstants.NAMESPACE), + eq(IFConstants.EL_STRUCTURE_TREE), + eq(IFConstants.EL_STRUCTURE_TREE), + any(Attributes.class)); + inOrder.verify(handler).endElement(eq(IFConstants.NAMESPACE), + eq(IFConstants.EL_STRUCTURE_TREE), + eq(IFConstants.EL_STRUCTURE_TREE)); + inOrder.verify(handler).endPrefixMapping(ExtensionElementMapping.STANDARD_PREFIX); + inOrder.verify(handler).endPrefixMapping(InternalElementMapping.STANDARD_PREFIX); + } + + @Test + public void startNode() throws Exception { + final String[] attributes = {"struct-id", "1"}; + final String nodeName = "block"; + final ContentHandler handler = mock(ContentHandler.class); + + sut.startPageSequence(null); + sut.startNode(nodeName, createSimpleAttributes(attributes)); + sut.endPageSequence(); + + sut.replayEventsForPageSequence(handler, 0); + + verify(handler).startElement(eq(FOElementMapping.URI), eq(nodeName), + eq(FOElementMapping.STANDARD_PREFIX + ":" + nodeName), + AttributesMatcher.match(createSimpleAttributes(attributes))); + } + + @Test + public void endNode() throws Exception { + final String nodeName = "block"; + final ContentHandler handler = mock(ContentHandler.class); + + sut.startPageSequence(null); + sut.endNode(nodeName); + sut.endPageSequence(); + + sut.replayEventsForPageSequence(handler, 0); + + verify(handler).endElement(eq(FOElementMapping.URI), eq(nodeName), + eq(FOElementMapping.STANDARD_PREFIX + ":" + nodeName)); + } + + private static Attributes createSimpleAttributes(String... attributes) { + assert (attributes.length % 2 == 0); + final AttributesImpl atts = new AttributesImpl(); + for (int i = 0; i < attributes.length; i += 2) { + String key = attributes[i]; + String value = attributes[i + 1]; + atts.addAttribute("", key, key, XMLUtil.CDATA, value); + } + return atts; + } + + private static final class AttributesMatcher extends ArgumentMatcher<Attributes> { + + private final Attributes expected; + + private AttributesMatcher(Attributes expected) { + this.expected = expected; + } + + public static Attributes match(Attributes expected) { + return argThat(new AttributesMatcher(expected)); + } + + public boolean matches(Object attributes) { + return attributesEqual(expected, (Attributes) attributes); + } + + private static boolean attributesEqual(Attributes attributes1, Attributes attributes2) { + if (attributes1.getLength() != attributes2.getLength()) { + return false; + } + for (int i = 0; i < attributes1.getLength(); i++) { + if (attributes1.getLocalName(i) != attributes2.getLocalName(i)) { + return false; + } + if (attributes1.getQName(i) != attributes2.getQName(i)) { + return false; + } + if (attributes1.getType(i) != attributes2.getType(i)) { + return false; + } + if (attributes1.getURI(i) != attributes2.getURI(i)) { + return false; + } + if (attributes1.getValue(i) != attributes2.getValue(i)) { + return false; + } + } + return true; + } + } +} diff --git a/test/java/org/apache/fop/render/intermediate/SAXEventRecorderTestCase.java b/test/java/org/apache/fop/render/intermediate/SAXEventRecorderTestCase.java new file mode 100644 index 000000000..c5aad66d0 --- /dev/null +++ b/test/java/org/apache/fop/render/intermediate/SAXEventRecorderTestCase.java @@ -0,0 +1,131 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.render.intermediate; + + +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.InOrder; +import org.xml.sax.Attributes; +import org.xml.sax.ContentHandler; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.AttributesImpl; + +import org.apache.fop.render.intermediate.IFStructureTreeBuilder.SAXEventRecorder; +import org.apache.fop.util.XMLUtil; + +/** + * Tests {@link SAXEventRecorder}. + */ +public class SAXEventRecorderTestCase { + + private static final String URI = "http://www.example.com/"; + + private SAXEventRecorder sut; + + @Before + public void setUp() { + sut = new SAXEventRecorder(); + } + + @Test + public void testStartEvent() throws SAXException { + final String localName = "element"; + final String qName = "prefix:" + localName; + final Attributes attributes = new AttributesImpl(); + + sut.startElement(URI, localName, qName, attributes); + ContentHandler handler = mock(ContentHandler.class); + sut.replay(handler); + verify(handler).startElement(URI, localName, qName, attributes); + } + + @Test + public void testEndEvent() throws SAXException { + final String localName = "element"; + final String qName = "prefix:" + localName; + sut.endElement(URI, localName, qName); + ContentHandler handler = mock(ContentHandler.class); + sut.replay(handler); + verify(handler).endElement(URI, localName, qName); + } + + @Test + public void testStartPrefixMapping() throws SAXException { + final String prefix = "prefix"; + + sut.startPrefixMapping(URI, prefix); + ContentHandler handler = mock(ContentHandler.class); + sut.replay(handler); + verify(handler).startPrefixMapping(URI, prefix); + } + + @Test + public void testEndPrefixMapping() throws SAXException { + final String prefix = "prefix"; + + sut.endPrefixMapping(prefix); + ContentHandler handler = mock(ContentHandler.class); + sut.replay(handler); + verify(handler).endPrefixMapping(prefix); + } + + @Test + public void completeTest() throws SAXException { + final String localName1 = "element"; + final String qName1 = "prefix:" + localName1; + final Attributes attributes1 = createAttributes(URI, localName1, qName1, "value-1"); + final String localName2 = "element2"; + final String qName2 = "prefix:" + localName2; + final Attributes attributes2 = createAttributes(URI, localName2, qName2, "value-2"); + final ContentHandler handler = mock(ContentHandler.class); + final String extensionUrl = "http://www.example.com/extension"; + final String extensionPrefix = "ext"; + + sut.startPrefixMapping(extensionPrefix, extensionUrl); + sut.startElement(URI, localName1, qName1, attributes1); + sut.startElement(URI, localName2, qName2, attributes2); + sut.endElement(URI, localName2, qName2); + sut.endElement(URI, localName1, qName1); + sut.endPrefixMapping(extensionPrefix); + + sut.replay(handler); + + InOrder inOrder = inOrder(handler); + inOrder.verify(handler).startPrefixMapping(extensionPrefix, extensionUrl); + inOrder.verify(handler).startElement(URI, localName1, qName1, attributes1); + inOrder.verify(handler).startElement(URI, localName2, qName2, attributes2); + inOrder.verify(handler).endElement(URI, localName2, qName2); + inOrder.verify(handler).endElement(URI, localName1, qName1); + inOrder.verify(handler).endPrefixMapping(extensionPrefix); + } + + private static Attributes createAttributes(String uri, String localName, + String qName, String value) { + final AttributesImpl atts = new AttributesImpl(); + atts.addAttribute(uri, localName, qName, XMLUtil.CDATA, value); + return atts; + } + +} diff --git a/test/java/org/apache/fop/util/XMLUtilTestCase.java b/test/java/org/apache/fop/util/LanguageTagsTestCase.java index 4c1b999e1..f7383c720 100644 --- a/test/java/org/apache/fop/util/XMLUtilTestCase.java +++ b/test/java/org/apache/fop/util/LanguageTagsTestCase.java @@ -20,31 +20,39 @@ package org.apache.fop.util; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; import java.util.Locale; import org.junit.Test; /** - * Tests {@link XMLUtil}. + * Tests {@link LanguageTags}. */ -public class XMLUtilTestCase { +public class LanguageTagsTestCase { + + @Test(expected = NullPointerException.class) + public void toLanguageTagRejectsNull() { + LanguageTags.toLanguageTag(null); + } @Test - public void testLocaleToRFC3066() throws Exception { - assertNull(XMLUtil.toRFC3066(null)); - assertEquals("en", XMLUtil.toRFC3066(new Locale("en"))); - assertEquals("en-US", XMLUtil.toRFC3066(new Locale("en", "US"))); - assertEquals("en-US", XMLUtil.toRFC3066(new Locale("EN", "us"))); + public void testToLanguageTag() throws Exception { + assertEquals("", LanguageTags.toLanguageTag(new Locale(""))); + assertEquals("en", LanguageTags.toLanguageTag(new Locale("en"))); + assertEquals("en-US", LanguageTags.toLanguageTag(new Locale("en", "US"))); + assertEquals("en-US", LanguageTags.toLanguageTag(new Locale("EN", "us"))); + } + + @Test(expected = NullPointerException.class) + public void toLocaleRejectsNull() { + LanguageTags.toLocale(null); } @Test public void testRFC3066ToLocale() throws Exception { - assertNull(XMLUtil.convertRFC3066ToLocale(null)); - assertNull(XMLUtil.convertRFC3066ToLocale("")); - assertEquals(new Locale("en"), XMLUtil.convertRFC3066ToLocale("en")); - assertEquals(new Locale("en", "US"), XMLUtil.convertRFC3066ToLocale("en-US")); - assertEquals(new Locale("en", "US"), XMLUtil.convertRFC3066ToLocale("EN-us")); + assertEquals(new Locale(""), LanguageTags.toLocale("")); + assertEquals(new Locale("en"), LanguageTags.toLocale("en")); + assertEquals(new Locale("en", "US"), LanguageTags.toLocale("en-US")); + assertEquals(new Locale("en", "US"), LanguageTags.toLocale("EN-us")); } } |