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 @@
+
+
+ * 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 extends StyleElement> 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
+ *
+ *
+ * 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 @@ + + +* 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("* 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. - * + *
+ *+ * 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("