diff --git a/.editorconfig b/.editorconfig index 3aac7e0a..6022cb1d 100644 --- a/.editorconfig +++ b/.editorconfig @@ -8,6 +8,10 @@ charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true +# note that Saxon license should not be added to git but you may need it locally for testing +[saxon-license.lic] +charset = latin1 + [*.{yml,yaml}] indent_size = 2 diff --git a/.gitignore b/.gitignore index 60294d51..2d884817 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,7 @@ build/ *.iml # Ignore the output of the Ant build -barcode4j/dist/ \ No newline at end of file +barcode4j/dist/ + +# If a Saxon license is used for testing make sure it's not added +saxon-license.lic diff --git a/barcode4j-saxon/pom.xml b/barcode4j-saxon/pom.xml new file mode 100644 index 00000000..34e95bde --- /dev/null +++ b/barcode4j-saxon/pom.xml @@ -0,0 +1,82 @@ + + + + com.singingbush + barcode4j-parent + 2.3.1-SNAPSHOT + + 4.0.0 + + barcode4j-saxon + Barcode4J Saxon (PE & EE 10 and above) + Support for Saxon PE and EE (should work with Saxon 10.6.* to 12.*) + + + 8 + 8 + + + + + + 10.6 + + + + + + + + + com.singingbush + barcode4j + ${project.parent.version} + + + + com.saxonica + Saxon-EE + ${saxon.version} + provided + + + + org.junit.jupiter + junit-jupiter-api + test + + + org.junit.jupiter + junit-jupiter-params + test + + + org.junit.jupiter + junit-jupiter-engine + test + + + + + + + org.apache.maven.plugins + maven-deploy-plugin + + + true + + + + + + + + saxonica + Saxonica + https://dev.saxonica.com/maven/ + + + diff --git a/barcode4j-saxon/src/main/java/org/krysalis/barcode4j/saxon/BarcodeExtensionElementFactory.java b/barcode4j-saxon/src/main/java/org/krysalis/barcode4j/saxon/BarcodeExtensionElementFactory.java new file mode 100644 index 00000000..ec004dcb --- /dev/null +++ b/barcode4j-saxon/src/main/java/org/krysalis/barcode4j/saxon/BarcodeExtensionElementFactory.java @@ -0,0 +1,50 @@ +/* + * Copyright 2003-2012 Jeremias Maerki. + * Copyright 2020-2024 Samael Bate (singingbush) + * + * Licensed 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. + */ +package org.krysalis.barcode4j.saxon; + +import com.saxonica.xsltextn.ExtensionElementFactory; +import net.sf.saxon.style.StyleElement; + +/** + * This class represents the element factory for the barcode extension for Saxon. + *

+ * Later releases of Saxon are split into 3 versions: + *

+ * + *

+ * Saxonica only provide support for element extensibility in the EE and PE releases. + * From version 10 onward, instead of implementing "net.sf.saxon.style.ExtensionElementFactory" + * use "com.saxonica.xsltextn.ExtensionElementFactory" + *

+ * @author Jeremias Maerki & Samael Bate (singingbush) + * @see com.saxonica.xsltextn.ExtensionElementFactory + */ +public class BarcodeExtensionElementFactory implements ExtensionElementFactory { + + /** + * @see com.saxonica.xsltextn.ExtensionElementFactory#getExtensionClass(java.lang.String) + */ + @Override + public Class getExtensionClass(String localName) { + return localName.equals("barcode") ? BarcodeStyleElement.class : BarcodeNonRootStyleElement.class; + } + +} diff --git a/barcode4j-saxon/src/main/java/org/krysalis/barcode4j/saxon/BarcodeNonRootStyleElement.java b/barcode4j-saxon/src/main/java/org/krysalis/barcode4j/saxon/BarcodeNonRootStyleElement.java new file mode 100644 index 00000000..c60bc387 --- /dev/null +++ b/barcode4j-saxon/src/main/java/org/krysalis/barcode4j/saxon/BarcodeNonRootStyleElement.java @@ -0,0 +1,53 @@ +/* + * Copyright 2003-2012 Jeremias Maerki. + * Copyright 2020-2024 Samael Bate (singingbush) + * + * Licensed 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. + */ +package org.krysalis.barcode4j.saxon; + +import net.sf.saxon.expr.Expression; +import net.sf.saxon.style.Compilation; +import net.sf.saxon.style.ComponentDeclaration; +import net.sf.saxon.style.StyleElement; +import net.sf.saxon.trans.XPathException; + +/** + * Non-root barcode elements. + * + * @author Jeremias Maerki & Samael Bate (singingbush) + */ +public class BarcodeNonRootStyleElement extends StyleElement { + + /** + * @see StyleElement#prepareAttributes() + */ + @Override + public void prepareAttributes() { + //nop + } + + @Override + public Expression compile(Compilation compilation, ComponentDeclaration decl) throws XPathException { + return null; + } + + /* + * @see net.sf.saxon.style.StyleElement#compile(net.sf.saxon.expr.instruct.Executable) + */ +// @Override +// public Expression compile(Executable exec) throws XPathException { +// return null; +// } + +} diff --git a/barcode4j-saxon/src/main/java/org/krysalis/barcode4j/saxon/BarcodeStyleElement.java b/barcode4j-saxon/src/main/java/org/krysalis/barcode4j/saxon/BarcodeStyleElement.java new file mode 100644 index 00000000..ffa595fd --- /dev/null +++ b/barcode4j-saxon/src/main/java/org/krysalis/barcode4j/saxon/BarcodeStyleElement.java @@ -0,0 +1,375 @@ +/* + * Copyright 2003-2012 Jeremias Maerki. + * Copyright 2020-2024 Samael Bate (singingbush) + * + * Licensed 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. + */ +package org.krysalis.barcode4j.saxon; + +import com.saxonica.xqj.SaxonXQPreparedExpression; +import net.sf.saxon.dom.DOMNodeWrapper; +import net.sf.saxon.dom.DocumentWrapper; +import net.sf.saxon.dom.NodeOverNodeInfo; +import net.sf.saxon.event.Outputter; +import net.sf.saxon.event.Receiver; +import net.sf.saxon.event.SequenceReceiver; +import net.sf.saxon.expr.Expression; +import net.sf.saxon.expr.Literal; +import net.sf.saxon.expr.SimpleExpression; +import net.sf.saxon.expr.XPathContext; +import net.sf.saxon.expr.instruct.SimpleNodeConstructor; +import net.sf.saxon.expr.parser.ContextItemStaticInfo; +import net.sf.saxon.expr.parser.ExpressionVisitor; +import net.sf.saxon.expr.parser.RebindingMap; +import net.sf.saxon.om.*; +import net.sf.saxon.option.dom4j.DOM4JNodeWrapper; +import net.sf.saxon.s9api.Axis; +import net.sf.saxon.style.Compilation; +import net.sf.saxon.style.ComponentDeclaration; +import net.sf.saxon.style.ExtensionInstruction; +import net.sf.saxon.style.StyleElement; +import net.sf.saxon.sxpath.XPathExpression; +import net.sf.saxon.trace.ExpressionPresenter; +import net.sf.saxon.trans.XPathException; +import net.sf.saxon.tree.iter.ArrayIterator; +import net.sf.saxon.type.SchemaType; +import net.sf.saxon.type.ValidationException; + +import org.krysalis.barcode4j.configuration.Configuration; +import org.krysalis.barcode4j.configuration.ConfigurationException; +import org.krysalis.barcode4j.BarcodeDimension; +import org.krysalis.barcode4j.BarcodeException; +import org.krysalis.barcode4j.BarcodeGenerator; +import org.krysalis.barcode4j.BarcodeUtil; +import org.krysalis.barcode4j.output.svg.SVGCanvasProvider; +import org.krysalis.barcode4j.tools.ConfigurationUtil; +import org.w3c.dom.Node; + +/** + * This represents the main barcode element. + * + * @author Jeremias Maerki & Samael Bate (singingbush) + */ +public class BarcodeStyleElement extends ExtensionInstruction { + + private static final int STATIC_CONTEXT = -1; + + private Expression message; + private Expression orientation; + +// public BarcodeStyleElement() { +// super(); +// this.defaultXPathNamespace = "barcode4j"; +// //this.defaultCollationName = null; +// } + +// /** +// * @see StyleElement#isInstruction() +// */ +// @Override +// public boolean isInstruction() { +// return true; // todo: evaluate extending ExtensionInstruction which already does this +// } + + /** + * Determine whether this type of element is allowed to contain a template-body + * + * @return true: yes, it may contain a template-body (this is done only so that + * it can contain xsl:fallback) + */ + @Override + public boolean mayContainSequenceConstructor() { + return true; + } + + /** + * @see StyleElement#prepareAttributes() + */ + @Override + public void prepareAttributes() { + // Get mandatory message attribute + // final String msgAtt = super.getAttributeValue("message"); + final String msgAtt = super.getAttributeValue("", "message"); + if (msgAtt == null) { + reportAbsence("message"); + } + + //final AttributeInfo attributeInfo = new AttributeInfo(NodeName); + + this.message = super.makeAttributeValueTemplate(msgAtt, null); + + // final String orientationAtt = super.getAttributeValue("orientation"); + final String orientationAtt = super.getAttributeValue("", "orientation"); + this.orientation = orientationAtt != null ? super.makeAttributeValueTemplate(orientationAtt, null) : null; + } + + /** + * @see StyleElement#validate(ComponentDeclaration) + */ + @Override + public void validate(ComponentDeclaration decl) throws XPathException { + super.validate(decl); + //super.checkWithinTemplate(); + message = typeCheck("message", message); + if (orientation != null) { + orientation = typeCheck("orientation", orientation); + } + } + + @Override + public void postValidate() throws XPathException { + if(!this.hasChildNodes()) { + this.compileError("barcode should have child element"); + } + // todo: enumerate child nodes and make sure that a BarcodeNonRootStyleElement exists + } + + /** + * @see StyleElement#compile(Compilation, ComponentDeclaration) + */ + @Override + public Expression compile(Compilation exec, ComponentDeclaration decl) throws XPathException { + if (this.isTopLevel()) { + return null; + } + final NodeOverNodeInfo node = NodeOverNodeInfo.wrap(this); + final Configuration cfg = ConfigurationUtil.buildConfiguration(node); + + return new BarcodeExpression(message, orientation, cfg); + } + + /** + * @see StyleElement#isPermittedChild(StyleElement) + */ + @Override + protected boolean isPermittedChild(StyleElement styleElement) { + // I am allowing anything right now + // todo: Consider doing something like: + // return styleElement instanceof BarcodeNonRootStyleElement; + return true; + } + + private static class BarcodeExpression extends SimpleExpression { + + private final Expression message; + private final Expression orientation; + private final Configuration cfg; + + public BarcodeExpression(final Expression message, final Expression orientation, final Configuration cfg) { + this.message = message; + this.orientation = orientation; + this.cfg = cfg; + } + + /** + * @see Expression#getImplementationMethod() + */ + @Override + public int getImplementationMethod() { + return Expression.PROCESS_METHOD; + } + +// @Override +// public ItemType getItemType(TypeHierarchy typeHierarchy) { +// return Type.ITEM_TYPE; // that's what SimpleExpression would do +// } + + /* + * Diagnostic print of expression structure. The abstract expression tree is written to the supplied output destination. + * @param destination the expression presenter used to display the structure + */ +// @Override +// public void explain(ExpressionPresenter destination) { +// +// } + + /* + * Determine the static cardinality of the expression. This implementation returns "zero or more", which can be overridden in a subclass. + * @return the computed cardinality, as one of the values StaticProperty.ALLOWS_ZERO_OR_ONE, StaticProperty.EXACTLY_ONE, StaticProperty.ALLOWS_ONE_OR_MORE, StaticProperty.ALLOWS_ZERO_OR_MORE + */ +// @Override +// protected int computeCardinality() { +// return 0; +// } + + /* + * Copy an expression. This makes a deep copy. + * @return A deep copy of the expression + */ +// @Override +// public Expression copy(RebindingMap rebindings) { +// throw new UnsupportedOperationException("copy"); +// } + +// @Override +// public CharSequence evaluateAsString(XPathContext context) throws XPathException { +// return super.evaluateAsString(context); +// } + + /** + * This is the replacement of what used to be "void process(XPathContext context) throws XPathException" in Saxon 8 + * + * Generally it is advisable, if calling iterate() to process a supplied sequence, to call it only once; if the value is required more than once, it should first be converted to a GroundedValue by calling the utility method SequenceTool.toGroundedValue(). + * + * If the expected value is a single item, the item should be obtained by calling Sequence.head(): it cannot be assumed that the item will be passed as an instance of Item or AtomicValue. + * + * It is the caller's responsibility to perform any type conversions required to convert arguments to the type expected by the callee. An exception is where this Callable is explicitly an argument-converting wrapper around the original Callable. + * + * @param context the dynamic evaluation context + * @param arguments the values of the arguments, supplied as Sequences. + *

Generally it is advisable, if calling iterate() to process a supplied sequence, to + * call it only once; if the value is required more than once, it should first be converted + * to a {@link GroundedValue} by calling the utility method + * SequenceTool.toGroundedValue().

+ *

If the expected value is a single item, the item should be obtained by calling + * Sequence.head(): it cannot be assumed that the item will be passed as an instance of + * {@link Item} or {@link net.sf.saxon.value.AtomicValue}.

+ *

It is the caller's responsibility to perform any type conversions required + * to convert arguments to the type expected by the callee. An exception is where + * this Callable is explicitly an argument-converting wrapper around the original + * Callable.

+ * @return the result of the evaluation, in the form of a Sequence. It is the responsibility + * of the callee to ensure that the type of result conforms to the expected result type. + * @throws XPathException if a dynamic error occurs during the evaluation of the expression + * @see net.sf.saxon.expr.Callable#call(XPathContext, Sequence[]) + * + * This method signature will change in a later release of Saxon 9 + */ + @Override + public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException { + final String effMessage = (message.evaluateAsString(context)).toString(); + int effOrientation = 0; + + if (orientation != null) { + final String degrees = orientation.evaluateAsString(context).toString(); + try { + effOrientation = BarcodeDimension.normalizeOrientation(Integer.parseInt(degrees)); + } catch (final NumberFormatException e) { + throw new ValidationException(e); + } catch (final IllegalArgumentException e) { + throw new ValidationException(e); + } + } + + try { + final Receiver out = context.getController().getPrincipalResult(); + //final SequenceReceiver out = context.getController().getPrincipalResult(); + + //Acquire BarcodeGenerator + final BarcodeGenerator gen = BarcodeUtil.getInstance().createBarcodeGenerator(cfg); + + //Setup Canvas + final SVGCanvasProvider svg = cfg.getAttributeAsBoolean("useNamespace", true) ? + new SVGCanvasProvider(cfg.getAttribute("prefix", "svg"), effOrientation) : + new SVGCanvasProvider(false, effOrientation); + + //Generate barcode + gen.generateBarcode(svg, effMessage); + + final DocumentWrapper wrapper = new DocumentWrapper( + svg.getDOM(), + SVGCanvasProvider.SVG_NAMESPACE, + context.getConfiguration() + ); + + final Item item = new DocumentNodeWrapper(svg.getDOM(), wrapper); + //final Item item = new DOMNodeWrapper(svg.getDOM(), wrapper, null, -1); + + out.append(item, this.getLocation(), 1); + + return null; // todo: fix this!! + + // todo: fix this code + // in later Saxon use something like this: + // final GroundedValue val1 = SequenceTool.toGroundedValue(new ArrayIterator(arguments[0].head())); + // final Item val2 = arguments[0].head(); + + //return arguments[0]; // todo: this is probably not right + //return evaluateItem(context); + //return item.makeRepeatable(); + //return wrapper.getRootNode(); + + } catch (final ConfigurationException e) { + throw new XPathException("(Barcode4J) " + e.getMessage()); + } catch (final BarcodeException e) { + throw new XPathException("(Barcode4J) " + e.getMessage()); + } + } + + static class DocumentNodeWrapper extends DOMNodeWrapper { + protected DocumentNodeWrapper(Node node, DocumentWrapper docWrapper) { + super(node, docWrapper, null, -1); + } + } + + /* + * Diagnostic print of expression structure. The abstract expression tree is written to the supplied output destination. + */ +// @Override +// public void export(ExpressionPresenter destination) throws XPathException { +// super.export(destination); +// } + + /* + * Process the instruction, without returning any tail calls + * @param context The dynamic context, giving access to the current node, the current variables, etc. + * @throws XPathException + */ + //@Override +// public void process(XPathContext context) throws XPathException { +// String effMessage = (message.evaluateAsString(context)).toString(); +// int effOrientation = 0; +// if (orientation != null) { +// String s = (orientation.evaluateAsString(context)).toString(); +// try { +// effOrientation = Integer.parseInt(s); +// effOrientation = BarcodeDimension +// .normalizeOrientation(effOrientation); +// } catch (NumberFormatException nfe) { +// throw new ValidationException(nfe); +// } catch (IllegalArgumentException iae) { +// throw new ValidationException(iae); +// } +// } +// +// try { +// SequenceReceiver out = context.getReceiver(); +// +// // Acquire BarcodeGenerator +// final BarcodeGenerator gen = BarcodeUtil.getInstance() +// .createBarcodeGenerator(cfg); +// +// // Setup Canvas +// final SVGCanvasProvider svg; +// if (cfg.getAttributeAsBoolean("useNamespace", true)) { +// svg = new SVGCanvasProvider(cfg.getAttribute("prefix", "svg"), +// effOrientation); +// } else { +// svg = new SVGCanvasProvider(false, effOrientation); +// } +// // Generate barcode +// gen.generateBarcode(svg, effMessage); +// +// DocumentWrapper wrapper = new DocumentWrapper(svg.getDOM(), +// SVGCanvasProvider.SVG_NAMESPACE, context.getConfiguration()); +// out.append(wrapper, this.getLocationId(), 1); +// +// } catch (ConfigurationException ce) { +// throw new XPathException("(Barcode4J) " + ce.getMessage()); +// } catch (BarcodeException be) { +// throw new XPathException("(Barcode4J) " + be.getMessage()); +// } +// } + + } +} diff --git a/barcode4j-saxon/src/test/java/org/krysalis/barcode4j/saxon/BarcodeStyleElementTest.java b/barcode4j-saxon/src/test/java/org/krysalis/barcode4j/saxon/BarcodeStyleElementTest.java new file mode 100644 index 00000000..6e6306d4 --- /dev/null +++ b/barcode4j-saxon/src/test/java/org/krysalis/barcode4j/saxon/BarcodeStyleElementTest.java @@ -0,0 +1,77 @@ +package org.krysalis.barcode4j.saxon; + +import net.sf.saxon.om.NodeInfo; +import net.sf.saxon.style.StyleElement; +import net.sf.saxon.style.XSLDocument; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * @author Samael Bate (singingbush) + * created on 27/11/2023 + */ +public class BarcodeStyleElementTest { + + private static final String BARCODE_ELEMENT_NAME = "barcode"; + + private BarcodeStyleElement element; + + @BeforeEach + void setUp() { + final StyleElement parent = new XSLDocument(); + + this.element = new BarcodeStyleElement(); + assertNull(element.getParent()); + + parent.insertChildren(new NodeInfo[]{ this.element }, true, false); + assertNotNull(element.getParent()); + } + + @Test + @DisplayName("should be an instruction element") + void testIsInstruction() { + assertTrue(element.isInstruction()); + } + + @Test + void testMayContainSequenceConstructor() { + assertTrue(element.mayContainSequenceConstructor()); + } + + /* + * Generally a BarcodeStyleElement will have BarcodeNonRootStyleElement as children + * + * + * + * 15mm + * 0.21mm + * ABC + * 10mw + * + * + * + */ + @Test + @DisplayName("Should permit nor root barcode child elements") + void isPermittedChild() { + assertTrue(element.isPermittedChild(new BarcodeNonRootStyleElement())); + + // it should probably allow child elements for doing logic + assertTrue(element.isPermittedChild(new net.sf.saxon.style.XSLIf())); + assertTrue(element.isPermittedChild(new net.sf.saxon.style.XSLLocalVariable())); + assertTrue(element.isPermittedChild(new net.sf.saxon.style.XSLLocalParam())); + + // this is also the reason mayContainSequenceConstructor() returns true + assertTrue(element.isPermittedChild(new net.sf.saxon.style.XSLFallback())); + + // todo: Consider disallowing other element types + //assertFalse(element.isPermittedChild(null)); + //assertFalse(element.isPermittedChild(new net.sf.saxon.style.XSLElement())); + //assertFalse(element.isPermittedChild(new net.sf.saxon.style.XSLDocument())); + } +} diff --git a/barcode4j-saxon/src/test/java/org/krysalis/barcode4j/saxon/SaxonExtTest.java b/barcode4j-saxon/src/test/java/org/krysalis/barcode4j/saxon/SaxonExtTest.java new file mode 100644 index 00000000..75d8d177 --- /dev/null +++ b/barcode4j-saxon/src/test/java/org/krysalis/barcode4j/saxon/SaxonExtTest.java @@ -0,0 +1,139 @@ +/* + * Copyright 2003-2004 Jeremias Maerki. + * + * Licensed 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. + */ +package org.krysalis.barcode4j.saxon; + +import com.saxonica.config.EnterpriseConfiguration; +import com.saxonica.config.EnterpriseTransformerFactory; +import net.sf.saxon.Configuration; +import net.sf.saxon.jaxp.SaxonTransformerFactory; +import net.sf.saxon.lib.FeatureKeys; +import net.sf.saxon.trans.LicenseException; +import net.sf.saxon.trans.XPathException; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.condition.EnabledIf; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import javax.xml.transform.Result; +import javax.xml.transform.Source; +import javax.xml.transform.Transformer; +import javax.xml.transform.stream.StreamResult; +import javax.xml.transform.stream.StreamSource; +import java.io.File; +import java.io.StringWriter; +import java.net.URISyntaxException; +import java.nio.file.Paths; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +/** + * Test class for the Saxon PE/EE extension. + *

+ * Saxon-PE: com.saxonica.config.ProfessionalTransformerFactory + * Saxon-EE: com.saxonica.config.EnterpriseTransformerFactory + *

+ * See {@linkplain 'https://www.saxonica.com/documentation12/index.html#!extensibility/extension-functions'} + * + * @author Jeremias Maerki & Samael Bate + */ +@EnabledIf( + value = "validSaxonLicenseFound", + disabledReason = "A valid saxon license needs to be on the classpath for this test to run" +) +public class SaxonExtTest { + + private static EnterpriseConfiguration configuration; + + @BeforeAll + static void beforeAll() { + configuration = new EnterpriseConfiguration(); + } + + static boolean validSaxonLicenseFound() { + // final Processor processor = new Processor(true); + // processor.registerExtensionFunction(new ExtensionFunction()); + + // can use either ProfessionalConfiguration or EnterpriseConfiguration + final Configuration configuration = EnterpriseConfiguration.newConfiguration(); + + try { + System.out.println("Using Saxon " + configuration.getEditionCode() + " configuration"); + //return configuration.isLicensedFeature(Feature.ALLOW_SYNTAX_EXTENSIONS.code); + return configuration.isLicensedFeature(Configuration.LicenseFeature.ENTERPRISE_XSLT); + } catch (final LicenseException e) { + System.err.println(e.getMessage()); + return false; + } + } + + @DisplayName("Do XML transform using Saxon's implementation of TransformerFactory") + @ParameterizedTest + @ValueSource(strings = { "xml/saxon-xml-transform.xsl", "xml/saxon-html-transform.xsl" }) + void testSaxonExt(final String xsltTemplate) throws Exception { + givenBarcodeExtensionRegistered(); + + final EnterpriseConfiguration config = new EnterpriseConfiguration(); + + // PE or EE only: + config.setExtensionElementNamespace("http://barcode4j.krysalis.org/ns", BarcodeExtensionElementFactory.class.getName()); + + //config.setAllNodesUntyped(true); // because we have no license when testing (cannot be Schema Aware) + //config.setExtensionElementNamespace(); + final SaxonTransformerFactory factory = new EnterpriseTransformerFactory(config); // required for EE!! + + factory.setAttribute(FeatureKeys.ALLOW_SYNTAX_EXTENSIONS, true); + + // FeatureKeys.ALLOW_SYNTAX_EXTENSIONS + + // todo: Don't use 'net.sf.saxon.jaxp.TransformerImpl', it should be using a PE or EE + // equivalent. new com.saxonica.config.ProfessionalTransformerFactory() + final Transformer trans = factory.newTransformer( + new StreamSource(loadTestResourceFile(xsltTemplate)) + ); + final Source src = new StreamSource(loadTestResourceFile("xml/barcodes.xml")); + + final StringWriter writer = new StringWriter(); + Result res = new StreamResult(writer); + + trans.transform(src, res); + String output = writer.getBuffer().toString(); + assertTrue(output.indexOf("svg") >= 0); + //System.out.println(writer.getBuffer()); + } + + private void givenBarcodeExtensionRegistered() throws XPathException { + //configuration.registerExtensionInstruction(BarcodeStyleElement.class); + + // PE or EE only: + configuration.setExtensionElementNamespace("java:/org.krysalis.barcode4j.saxon.BarcodeExtensionElementFactory", BarcodeExtensionElementFactory.class.getName()); + configuration.setExtensionElementNamespace("http://barcode4j.krysalis.org/org.krysalis.barcode4j.saxon.BarcodeExtensionElementFactory", BarcodeExtensionElementFactory.class.getName()); + + //configuration.setExtensionElementNamespace("http://barcode4j.krysalis.org/ns", BarcodeExtensionElementFactory.class.getName()); + //configuration.setExtensionElementNamespace("classpath://org.krysalis.barcode4j.saxon9.BarcodeExtensionElementFactory", BarcodeExtensionElementFactory.class.getName()); + } + + private File loadTestResourceFile(final String resource) { + try { + return Paths.get(this.getClass().getClassLoader().getResource(resource).toURI()).toFile(); + } catch (final URISyntaxException e) { + fail("Could no load resource : " + resource); + } + return null; + } + +} diff --git a/barcode4j-saxon/src/test/resources/xml/barcodes.xml b/barcode4j-saxon/src/test/resources/xml/barcodes.xml new file mode 100644 index 00000000..5d4c9d77 --- /dev/null +++ b/barcode4j-saxon/src/test/resources/xml/barcodes.xml @@ -0,0 +1,25 @@ + + + + + upc-A + 012345678905 + + + ean-13 + 0012345678905 + + + code128 + 1234567890 + + + pdf417 + Here is some text encoded in a 2D barcode + + + datamatrix + Here is some text encoded in a datamatrix + + + diff --git a/barcode4j-saxon/src/test/resources/xml/saxon-html-transform.xsl b/barcode4j-saxon/src/test/resources/xml/saxon-html-transform.xsl new file mode 100644 index 00000000..333a4e2a --- /dev/null +++ b/barcode4j-saxon/src/test/resources/xml/saxon-html-transform.xsl @@ -0,0 +1,111 @@ + + + + + + +
+ +
+
+ + + +
+ Barcode of type + + + + : + + + + + 15mm + 0.33mm + 10mw + auto + + + + + + + + 15mm + 0.33mm + 10mw + auto + + + + + + + + 15mm + 0.21mm + ABC + 10mw + + + + + + + + 0.705554mm + 3mw + 2 + 2 + 2 + 3 + 90 + 0 + 2mw + + 3.0 + + + + + + + + 1.411108mm + 1mw + force-none + 22 + 26 + + + + +
+
+ + + + + + + XML to HTML + + + +
+ +
+ + +
+
diff --git a/barcode4j-saxon/src/test/resources/xml/saxon-test.xsl b/barcode4j-saxon/src/test/resources/xml/saxon-test.xsl new file mode 100644 index 00000000..dfe05af6 --- /dev/null +++ b/barcode4j-saxon/src/test/resources/xml/saxon-test.xsl @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + diff --git a/barcode4j-saxon/src/test/resources/xml/saxon-xml-transform.xsl b/barcode4j-saxon/src/test/resources/xml/saxon-xml-transform.xsl new file mode 100644 index 00000000..96c73491 --- /dev/null +++ b/barcode4j-saxon/src/test/resources/xml/saxon-xml-transform.xsl @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + 15mm + 0.33mm + 10mw + auto + + + + + + + + 15mm + 0.33mm + 10mw + auto + + + + + + + + 15mm + 0.21mm + ABC + 10mw + + + + + + + + 0.705554mm + 3mw + 2 + 2 + 2 + 3 + 90 + 0 + 2mw + + 3.0 + + + + + + + + 1.411108mm + 1mw + force-none + 22 + 26 + + + + + diff --git a/barcode4j-saxon/src/test/resources/xml/xslt-test.xml b/barcode4j-saxon/src/test/resources/xml/xslt-test.xml new file mode 100644 index 00000000..d3336064 --- /dev/null +++ b/barcode4j-saxon/src/test/resources/xml/xslt-test.xml @@ -0,0 +1,6 @@ + + + + Hello World! + + diff --git a/barcode4j-saxon8/pom.xml b/barcode4j-saxon8/pom.xml index c43bfc85..b3d8bd98 100644 --- a/barcode4j-saxon8/pom.xml +++ b/barcode4j-saxon8/pom.xml @@ -13,7 +13,11 @@ barcode4j-saxon8 Barcode4J Saxon 8 - Support for Saxon 8. Current saxon version ${net.sf.saxon.8.version} + Support for Saxon 8. Current saxon version ${saxon.version} + + + 8.7 + @@ -25,12 +29,12 @@ net.sf.saxon saxon - ${net.sf.saxon.8.version} + ${saxon.version} net.sf.saxon saxon-dom - ${net.sf.saxon.8.version} + ${saxon.version} @@ -49,6 +53,11 @@ junit-jupiter-engine test + + org.mockito + mockito-core + test + diff --git a/barcode4j-saxon8/src/main/java/org/krysalis/barcode4j/saxon8/BarcodeExtensionElementFactory.java b/barcode4j-saxon8/src/main/java/org/krysalis/barcode4j/saxon8/BarcodeExtensionElementFactory.java index 4dfe4cff..7e248a52 100644 --- a/barcode4j-saxon8/src/main/java/org/krysalis/barcode4j/saxon8/BarcodeExtensionElementFactory.java +++ b/barcode4j-saxon8/src/main/java/org/krysalis/barcode4j/saxon8/BarcodeExtensionElementFactory.java @@ -19,15 +19,19 @@ /** * This class represents the element factory for the barcode extension for Saxon. - * + *

* Later releases of Saxon are split into 3 versions: - * Saxon-HE (Home Edition) available on maven central - * Saxon-PE (Professional Edition) manual download & requires license - * Saxon-EE (Enterprise Edition) manual download & requires license - * + *

+ * + *

* The net.sf.saxon.style.ExtensionElementFactory class is not available in Saxon-HE as Saxonica * only provide support for element extensibility in the EE and PE releases. - * + *

+ * @see net.sf.saxon.style.ExtensionElementFactory * @author Jeremias Maerki * @version $Id: BarcodeExtensionElementFactory.java,v 1.2 2004-09-04 20:25:55 jmaerki Exp $ */ @@ -37,8 +41,8 @@ public class BarcodeExtensionElementFactory implements ExtensionElementFactory { * @see net.sf.saxon.style.ExtensionElementFactory#getExtensionClass(java.lang.String) */ @Override - public Class getExtensionClass(String localname) { - return localname.equals("barcode") ? BarcodeStyleElement.class : BarcodeNonRootStyleElement.class; + public Class getExtensionClass(String localName) { + return localName.equals("barcode") ? BarcodeStyleElement.class : BarcodeNonRootStyleElement.class; } } diff --git a/barcode4j-saxon8/src/main/java/org/krysalis/barcode4j/saxon8/BarcodeStyleElement.java b/barcode4j-saxon8/src/main/java/org/krysalis/barcode4j/saxon8/BarcodeStyleElement.java index 4fe8d592..9cd4836d 100644 --- a/barcode4j-saxon8/src/main/java/org/krysalis/barcode4j/saxon8/BarcodeStyleElement.java +++ b/barcode4j-saxon8/src/main/java/org/krysalis/barcode4j/saxon8/BarcodeStyleElement.java @@ -33,6 +33,7 @@ import net.sf.saxon.expr.XPathContext; import net.sf.saxon.instruct.Executable; import net.sf.saxon.style.StyleElement; +import net.sf.saxon.style.ExtensionInstruction; import net.sf.saxon.trans.DynamicError; import net.sf.saxon.trans.XPathException; import net.sf.saxon.type.ValidationException; @@ -43,19 +44,11 @@ * @author Jeremias Maerki * @version $Id: BarcodeStyleElement.java,v 1.4 2007-01-15 11:12:33 jmaerki Exp $ */ -public class BarcodeStyleElement extends StyleElement { +public class BarcodeStyleElement extends ExtensionInstruction { private Expression message; private Expression orientation; - /** - * @see net.sf.saxon.style.StyleElement#isInstruction() - */ - @Override - public boolean isInstruction() { - return true; - } - /** * Determine whether this type of element is allowed to contain a template-body * @@ -68,6 +61,9 @@ public boolean mayContainSequenceConstructor() { } /** + * This is called while the stylesheet tree is still being built, so it should not attempt to navigate the tree. + * Its task is to validate the attributes of the stylesheet element and perform any preprocessing necessary. + * For example, if the attribute is an attribute value template, this includes creating an Expression that can subsequently be evaluated to get the AVT's value. * @see net.sf.saxon.style.StyleElement#prepareAttributes() */ @Override @@ -96,14 +92,29 @@ public void validate() throws XPathException { } } + @Override + public void postValidate() throws XPathException { + if(!this.hasChildNodes()) { + this.compileError("barcode should have child element"); + } + // todo: enumerate child nodes and make sure that a BarcodeNonRootStyleElement exists + } + /** + * This is called to create an Expression object which is added to the expression tree. + * @param exec a non-nullable saxon executable + * @return a new BarcodeExpression instance using the message & orientation expressions + * @throws XPathException not thrown by this implementation * @see net.sf.saxon.style.StyleElement#compile(net.sf.saxon.instruct.Executable) */ + //@Nullable & arg is non-null @Override public Expression compile(Executable exec) throws XPathException { final NodeOverNodeInfo node = NodeOverNodeInfo.wrap(this); final Configuration cfg = ConfigurationUtil.buildConfiguration(node); return new BarcodeExpression(message, orientation, cfg); + + // this.compileError("Invalid element name: " + var3, "XTDE0820"); } /** @@ -157,10 +168,11 @@ public void process(XPathContext context) throws XPathException { //Acquire BarcodeGenerator final BarcodeGenerator gen = BarcodeUtil.getInstance().createBarcodeGenerator(cfg); - //Setup Canvas - final SVGCanvasProvider svg = cfg.getAttributeAsBoolean("useNamespace", true) ? - new SVGCanvasProvider(cfg.getAttribute("prefix", "svg"), effOrientation) : - new SVGCanvasProvider(false, effOrientation); + // Setup Canvas (need to not set "svg:" namespace if we're outputting HTML) + final SVGCanvasProvider svg = new SVGCanvasProvider(false, effOrientation); + //final SVGCanvasProvider svg = cfg.getAttributeAsBoolean("useNamespace", true) ? + // new SVGCanvasProvider(cfg.getAttribute("prefix", "svg"), effOrientation) : + // new SVGCanvasProvider(false, effOrientation); //Generate barcode gen.generateBarcode(svg, effMessage); diff --git a/barcode4j-saxon8/src/test/java/org/krysalis/barcode4j/saxon8/SaxonExtTest.java b/barcode4j-saxon8/src/test/java/org/krysalis/barcode4j/saxon8/SaxonExtTest.java index 1c9b6c08..23c6d130 100644 --- a/barcode4j-saxon8/src/test/java/org/krysalis/barcode4j/saxon8/SaxonExtTest.java +++ b/barcode4j-saxon8/src/test/java/org/krysalis/barcode4j/saxon8/SaxonExtTest.java @@ -58,16 +58,17 @@ void testSaxon8Ext_UsingJAXP(final String xsltTemplate) throws Exception { final Source xmlDataSource = new StreamSource(loadTestResourceFile("xml/xslt-test.xml")); - final StringWriter writer = new StringWriter(); - final Result result = new StreamResult(writer); + final StreamResult result = new StreamResult(new StringWriter()); trans.transform(xmlDataSource, result); - final String output = writer.getBuffer().toString(); - assertTrue(output.contains(" +
+ Barcode of type + + + + : + @@ -78,6 +85,8 @@ + +
@@ -86,6 +95,13 @@ XML to HTML +
diff --git a/barcode4j-saxon8/src/test/resources/xml/saxon8-xml-transform.xsl b/barcode4j-saxon8/src/test/resources/xml/saxon8-xml-transform.xsl index c9dda592..79904603 100644 --- a/barcode4j-saxon8/src/test/resources/xml/saxon8-xml-transform.xsl +++ b/barcode4j-saxon8/src/test/resources/xml/saxon8-xml-transform.xsl @@ -1,5 +1,6 @@ - diff --git a/barcode4j-saxon91/pom.xml b/barcode4j-saxon91/pom.xml index d2337cc3..363f38fa 100644 --- a/barcode4j-saxon91/pom.xml +++ b/barcode4j-saxon91/pom.xml @@ -13,7 +13,11 @@ barcode4j-saxon91 Barcode4J Saxon 9.1 - Support for Saxon 9.1 + Support for Saxon ${saxon.version} + + + 9.1.0.8 + @@ -21,27 +25,53 @@ barcode4j ${project.parent.version} + net.sf.saxon saxon9 - ${net.sf.saxon.9.version} + ${saxon.version} + provided net.sf.saxon saxon9-dom - ${net.sf.saxon.9.version} + ${saxon.version} + provided + + + javax.xml.bind + jaxb-api + 2.3.1 + provided + + + org.glassfish.jaxb + jaxb-runtime + 2.3.7 + test + org.junit.jupiter junit-jupiter-api test + + org.junit.jupiter + junit-jupiter-params + test + org.junit.jupiter junit-jupiter-engine test + + org.mockito + mockito-core + test + @@ -61,6 +91,22 @@ true + + org.apache.maven.plugins + maven-enforcer-plugin + + + true + + + + org.codehaus.mojo + license-maven-plugin + + + true + + diff --git a/barcode4j-saxon91/src/main/java/org/krysalis/barcode4j/saxon9/BarcodeExtensionElementFactory.java b/barcode4j-saxon91/src/main/java/org/krysalis/barcode4j/saxon9/BarcodeExtensionElementFactory.java index 460b3d71..09c7696e 100644 --- a/barcode4j-saxon91/src/main/java/org/krysalis/barcode4j/saxon9/BarcodeExtensionElementFactory.java +++ b/barcode4j-saxon91/src/main/java/org/krysalis/barcode4j/saxon9/BarcodeExtensionElementFactory.java @@ -1,6 +1,4 @@ /* - * Copyright 2003-2004 Jeremias Maerki. - * * Licensed 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 @@ -19,17 +17,21 @@ /** * This class represents the element factory for the barcode extension for Saxon. - * + *

* Later releases of Saxon are split into 3 versions: - * Saxon-HE (Home Edition) available on maven central - * Saxon-PE (Professional Edition) manual download & requires license - * Saxon-EE (Enterprise Edition) manual download & requires license - * - * The net.sf.saxon.style.ExtensionElementFactory class is not available in Saxon-HE as Saxonica - * only provide support for element extensibility in the EE and PE releases. - * + *

+ *
    + *
  • Saxon-HE (Home Edition) available on maven central
  • + *
  • Saxon-PE (Professional Edition) available from Saxonica maven repo & requires license
  • + *
  • Saxon-EE (Enterprise Edition) available from Saxonica maven repo & requires license
  • + *
+ *

+ * Saxonica only provide support for element extensibility in the EE and PE releases. + * From version 10 onward, instead of implementing "net.sf.saxon.style.ExtensionElementFactory" + * use "com.saxonica.xsltextn.ExtensionElementFactory" + *

* @author Jeremias Maerki - * @version $Id: BarcodeExtensionElementFactory.java,v 1.2 2004-09-04 20:25:55 jmaerki Exp $ + * @see net.sf.saxon.style.ExtensionElementFactory */ public class BarcodeExtensionElementFactory implements ExtensionElementFactory { @@ -37,8 +39,8 @@ public class BarcodeExtensionElementFactory implements ExtensionElementFactory { * @see net.sf.saxon.style.ExtensionElementFactory#getExtensionClass(java.lang.String) */ @Override - public Class getExtensionClass(String name) { - return name.equals("barcode") ? BarcodeStyleElement.class : BarcodeNonRootStyleElement.class; + public Class getExtensionClass(String localName) { + return localName.equals("barcode") ? BarcodeStyleElement.class : BarcodeNonRootStyleElement.class; } } diff --git a/barcode4j-saxon91/src/main/java/org/krysalis/barcode4j/saxon9/BarcodeNonRootStyleElement.java b/barcode4j-saxon91/src/main/java/org/krysalis/barcode4j/saxon9/BarcodeNonRootStyleElement.java index a53461a9..6b64bcbe 100644 --- a/barcode4j-saxon91/src/main/java/org/krysalis/barcode4j/saxon9/BarcodeNonRootStyleElement.java +++ b/barcode4j-saxon91/src/main/java/org/krysalis/barcode4j/saxon9/BarcodeNonRootStyleElement.java @@ -1,12 +1,12 @@ /* * Copyright 2003-2004,2007 Jeremias Maerki. - * + * * Licensed 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. @@ -22,7 +22,7 @@ /** * Non-root barcode elements. - * + * * @author Jeremias Maerki * @version $Id: BarcodeNonRootStyleElement.java,v 1.3 2007-01-15 11:12:33 jmaerki Exp $ */ diff --git a/barcode4j-saxon91/src/main/java/org/krysalis/barcode4j/saxon9/BarcodeStyleElement.java b/barcode4j-saxon91/src/main/java/org/krysalis/barcode4j/saxon9/BarcodeStyleElement.java index 5d28b3d5..437d20d9 100644 --- a/barcode4j-saxon91/src/main/java/org/krysalis/barcode4j/saxon9/BarcodeStyleElement.java +++ b/barcode4j-saxon91/src/main/java/org/krysalis/barcode4j/saxon9/BarcodeStyleElement.java @@ -32,6 +32,7 @@ import net.sf.saxon.expr.SimpleExpression; import net.sf.saxon.expr.XPathContext; import net.sf.saxon.instruct.Executable; +import net.sf.saxon.style.ExtensionInstruction; import net.sf.saxon.style.StyleElement; import net.sf.saxon.trans.XPathException; import net.sf.saxon.type.ValidationException; @@ -41,20 +42,14 @@ * * @author Jeremias Maerki * @version $Id: BarcodeStyleElement.java,v 1.4 2007-01-15 11:12:33 jmaerki Exp $ + * + * Adapted for Saxon 9.1 by Samael Bate (singingbush) */ -public class BarcodeStyleElement extends StyleElement { +public class BarcodeStyleElement extends ExtensionInstruction { private Expression message; private Expression orientation; - /** - * @see net.sf.saxon.style.StyleElement#isInstruction() - */ - @Override - public boolean isInstruction() { - return true; - } - /** * Determine whether this type of element is allowed to contain a template-body * @@ -95,6 +90,19 @@ public void validate() throws XPathException { } } + /** + * Allows additional validation immediately after child nodes validated. + * This should be used to verify that barcode has a child that specifies type. Potentially same could be done for those types + * @see net.sf.saxon.style.StyleElement#postValidate() + */ + @Override + public void postValidate() throws XPathException { + if(!this.hasChildNodes()) { + this.compileError("barcode should have child element"); + } + // todo: enumerate child nodes and make sure that a BarcodeNonRootStyleElement exists + } + /** * @see net.sf.saxon.style.StyleElement#compile(net.sf.saxon.instruct.Executable) */ @@ -102,6 +110,7 @@ public void validate() throws XPathException { public Expression compile(Executable exec) throws XPathException { final NodeOverNodeInfo node = NodeOverNodeInfo.wrap(this); final Configuration cfg = ConfigurationUtil.buildConfiguration(node); + return new BarcodeExpression(message, orientation, cfg); } @@ -156,10 +165,11 @@ public void process(XPathContext context) throws XPathException { //Acquire BarcodeGenerator final BarcodeGenerator gen = BarcodeUtil.getInstance().createBarcodeGenerator(cfg); - //Setup Canvas - final SVGCanvasProvider svg = cfg.getAttributeAsBoolean("useNamespace", true) ? - new SVGCanvasProvider(cfg.getAttribute("prefix", "svg"), effOrientation) : - new SVGCanvasProvider(false, effOrientation); + // Setup Canvas (need to not set "svg:" namespace if we're outputting HTML) + final SVGCanvasProvider svg = new SVGCanvasProvider(false, effOrientation); + //final SVGCanvasProvider svg = cfg.getAttributeAsBoolean("useNamespace", true) ? + // new SVGCanvasProvider(cfg.getAttribute("prefix", "svg"), effOrientation) : + // new SVGCanvasProvider(false, effOrientation); //Generate barcode gen.generateBarcode(svg, effMessage); diff --git a/barcode4j-saxon91/src/test/java/org/krysalis/barcode4j/saxon9/SaxonExtTest.java b/barcode4j-saxon91/src/test/java/org/krysalis/barcode4j/saxon9/SaxonExtTest.java index 456b1c86..9e2921c5 100644 --- a/barcode4j-saxon91/src/test/java/org/krysalis/barcode4j/saxon9/SaxonExtTest.java +++ b/barcode4j-saxon91/src/test/java/org/krysalis/barcode4j/saxon9/SaxonExtTest.java @@ -18,49 +18,118 @@ import java.io.File; import java.io.StringWriter; import java.net.URISyntaxException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.Paths; +import java.util.Collections; +import java.util.List; -import javax.xml.transform.Result; +import javax.xml.bind.JAXBContext; +import javax.xml.bind.annotation.*; +import javax.xml.bind.util.JAXBSource; import javax.xml.transform.Source; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; - +import net.sf.saxon.Configuration; +import net.sf.saxon.Transform; import net.sf.saxon.TransformerFactoryImpl; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; /** - * Test class for the Saxon 9.x extension. + * Test class for the Saxon 9.1 extension. * - * @author Jeremias Maerki - * @version $Id: SaxonExtTest.java,v 1.2 2004-09-04 20:26:15 jmaerki Exp $ + * @author Samael Bate (singingbush) */ public class SaxonExtTest { - @Test - void testSaxon9Ext() throws Exception { - final TransformerFactory factory = new TransformerFactoryImpl(); + private static Configuration configuration; + + @BeforeAll + static void beforeAll() { + configuration = new Configuration(); + } + + @ParameterizedTest + @DisplayName("Do XML transform using Saxon's implementation of TransformerFactory (StreamSource)") + @ValueSource(strings = { "xml/saxon9-xml-transform.xsl", "xml/saxon9-html-transform.xsl" }) + void testSaxon9Ext_UsingSaxonTransformerFactoryWithStreamSource(final String xsltTemplate) throws Exception { + final TransformerFactory factory = new TransformerFactoryImpl(configuration); // use Saxon implementation of TransformerFactory! + final Transformer trans = factory.newTransformer( + new StreamSource(loadTestResourceFile(xsltTemplate)) + ); + + final Source xmlDataSource = new StreamSource(loadTestResourceFile("xml/xslt-test.xml")); // barcodes + + final StreamResult result = new StreamResult(new StringWriter()); + + trans.transform(xmlDataSource, result); + + final String output = result.getWriter().toString(); + + assertTrue(output.contains(" barcodes; + + public Barcodes() {} + + public Barcodes(List barcodes) { + this.barcodes = barcodes; + } + } + + @XmlRootElement(name = "barcode") + @XmlType(propOrder = { "type", "message" }) + private static class Barcode { + private String type; + private String message; + + public Barcode() {} + + public Barcode(String type, String message) { + this.type = type; + this.message = message; + } + + public String getType() { + return type; + } + + @XmlElement(name = "type") + public void setType(String type) { + this.type = type; + } + + public String getMessage() { + return message; + } + + @XmlElement(name = "message") + public void setMessage(String message) { + this.message = message; + } + } } diff --git a/barcode4j-saxon91/src/test/resources/xml/saxon9-html-transform.xsl b/barcode4j-saxon91/src/test/resources/xml/saxon9-html-transform.xsl new file mode 100644 index 00000000..333a4e2a --- /dev/null +++ b/barcode4j-saxon91/src/test/resources/xml/saxon9-html-transform.xsl @@ -0,0 +1,111 @@ + + + + + + +
+ +
+ + + + +
+ Barcode of type + + + + : + + + + + 15mm + 0.33mm + 10mw + auto + + + + + + + + 15mm + 0.33mm + 10mw + auto + + + + + + + + 15mm + 0.21mm + ABC + 10mw + + + + + + + + 0.705554mm + 3mw + 2 + 2 + 2 + 3 + 90 + 0 + 2mw + + 3.0 + + + + + + + + 1.411108mm + 1mw + force-none + 22 + 26 + + + + +
+
+ + + + + + + XML to HTML + + + +
+ +
+ + +
+
diff --git a/barcode4j-saxon91/src/test/resources/xml/saxon9-test.xsl b/barcode4j-saxon91/src/test/resources/xml/saxon9-test.xsl deleted file mode 100644 index e9989ba3..00000000 --- a/barcode4j-saxon91/src/test/resources/xml/saxon9-test.xsl +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/barcode4j-saxon91/src/test/resources/xml/saxon9-xml-transform.xsl b/barcode4j-saxon91/src/test/resources/xml/saxon9-xml-transform.xsl new file mode 100644 index 00000000..96c73491 --- /dev/null +++ b/barcode4j-saxon91/src/test/resources/xml/saxon9-xml-transform.xsl @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + 15mm + 0.33mm + 10mw + auto + + + + + + + + 15mm + 0.33mm + 10mw + auto + + + + + + + + 15mm + 0.21mm + ABC + 10mw + + + + + + + + 0.705554mm + 3mw + 2 + 2 + 2 + 3 + 90 + 0 + 2mw + + 3.0 + + + + + + + + 1.411108mm + 1mw + force-none + 22 + 26 + + + + + diff --git a/barcode4j-saxon91/src/test/resources/xml/xslt-test.xml b/barcode4j-saxon91/src/test/resources/xml/xslt-test.xml index d3336064..ee3dc4a2 100644 --- a/barcode4j-saxon91/src/test/resources/xml/xslt-test.xml +++ b/barcode4j-saxon91/src/test/resources/xml/xslt-test.xml @@ -1,6 +1,23 @@ - Hello World! + upc-A + 012345678905 + + + ean-13 + 0012345678905 + + + code128 + 1234567890 + + + pdf417 + Here is some text encoded in a 2D barcode + + + datamatrix + Here is some text encoded in a datamatrix diff --git a/barcode4j/src/main/java/org/krysalis/barcode4j/tools/ConfigurationUtil.java b/barcode4j/src/main/java/org/krysalis/barcode4j/tools/ConfigurationUtil.java index de0a399a..43297c3b 100644 --- a/barcode4j/src/main/java/org/krysalis/barcode4j/tools/ConfigurationUtil.java +++ b/barcode4j/src/main/java/org/krysalis/barcode4j/tools/ConfigurationUtil.java @@ -51,12 +51,11 @@ protected ConfigurationUtil() { * @param node the DOM node * @return the Configuration object */ - public static Configuration buildConfiguration(Node node) { + public static @Nullable Configuration buildConfiguration(@NotNull final Node node) { return processNode(node); } - @Nullable - private static Element findDocumentElement(@NotNull final Document document) { + private static @Nullable Element findDocumentElement(@NotNull final Document document) { try { return document.getDocumentElement(); //Xalan-bug, doesn't work (2.4.1) } catch (Exception e) { @@ -71,28 +70,25 @@ private static Element findDocumentElement(@NotNull final Document document) { } } - @Nullable - private static DefaultConfiguration processNode(@NotNull Node node) { + private static @Nullable DefaultConfiguration processNode(@NotNull Node node) { if (node.getNodeType() == Node.ELEMENT_NODE) { return processElement((Element)node); } else if (node.getNodeType() == Node.DOCUMENT_NODE) { return processElement(Objects.requireNonNull(findDocumentElement((Document) node))); } else if (node.getNodeType() == Node.DOCUMENT_FRAGMENT_NODE) { - DocumentFragment df = (DocumentFragment)node; + final DocumentFragment df = (DocumentFragment)node; return processNode(df.getFirstChild()); } else { return null; } } - @NotNull - private static DefaultConfiguration processElement(@NotNull Element el) { - String name = el.getLocalName(); // element can be null - if (name == null) { - name = el.getTagName(); - } + private static @NotNull DefaultConfiguration processElement(@NotNull Element el) { + final String name = el.getLocalName() != null ? el.getLocalName() : el.getTagName(); + final DefaultConfiguration cfg = new DefaultConfiguration(name); final NamedNodeMap attrs = el.getAttributes(); + for (int i = 0; i < attrs.getLength(); i++) { final Attr attr = (Attr)attrs.item(i); cfg.setAttribute(attr.getName(), attr.getValue()); @@ -118,8 +114,7 @@ private static DefaultConfiguration processElement(@NotNull Element el) { * @return the message or null * @throws ConfigurationException if an error occurs retrieving values from the configuration */ - @Nullable - public static String getMessage(@NotNull final Configuration cfg) throws ConfigurationException { + public static @Nullable String getMessage(@NotNull final Configuration cfg) throws ConfigurationException { String msg; try { msg = cfg.getAttribute("message"); diff --git a/pom.xml b/pom.xml index d163e094..7f0af8ae 100644 --- a/pom.xml +++ b/pom.xml @@ -19,6 +19,7 @@ barcode4j-fop-ext barcode4j-ant barcode4j-servlet + barcode4j-saxon barcode4j-saxon8 barcode4j-saxon91 barcode4j-examples @@ -457,7 +458,7 @@ false GPLv2|GPLv3 - xalan|org.jdom|javax.servlet|net.sf.saxon + xalan|org.jdom|javax.servlet|net.sf.saxon|com.saxonica true GPLv2 + Classpath Exception| @@ -478,6 +479,7 @@ LGPLv3|LGPLv3.0|LGPL-3.0| GNU Lesser General Public License v3.0 Apache License, Version 2.0| + Apache License version 2.0| The Apache Software License, Version 2.0|Apache2| Apache-2.0|Apache 2|APL2|Apache 2.0| The Apache License, Version 2.0|Apache Software License - Version 2.0|