This is an automated email from the ASF dual-hosted git repository.

olabusayo pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/daffodil.git


The following commit(s) were added to refs/heads/main by this push:
     new 01e5483d7 Update java api to declare thrown checked exceptions
01e5483d7 is described below

commit 01e5483d7640ab1bd73d384daa9f2f71dc94ba0b
Author: olabusayoT <[email protected]>
AuthorDate: Mon Jan 12 12:01:08 2026 -0500

    Update java api to declare thrown checked exceptions
    
    - add InvalidGeneratorException for errors during forLanguage as 
InvalidParserException is only for reload call
    - ensure DaffodilParseXMLReader's exception declarations match with 
XMLReader exception declarations
    - allow global OVC, replace thrown error for onPath, with stored error
    - ensure `SchemaSetRuntimeData` cannot have null parser/unparser.
    - Adjust `UnsuppressableException` to extend `RuntimeException` since our 
Assert macros like aborts are challenging to declare. They are typically issued 
during runtime anyway, so it's more fitting for them to be labeled as such
    - fix some broken javacode to use @code instead of @link
    
    Deprecation/Compatibility
    - Previously we didn't declare checked exceptions that could have been 
thrown by the API code. Now that we declare them, users of the API will need to 
explicitly catch these exceptions.
    - Unlike in previous versions of Daffodil, dfdl:outputValueCalc is now 
allowed on global/root elements
    
    DAFFODIL-2287
---
 .../org/apache/daffodil/api/CodeGenerator.java     |  3 +-
 .../daffodil/api/DaffodilParseXMLReader.java       | 22 ++++++-----
 .../api/DaffodilUnparseContentHandler.java         |  4 +-
 .../org/apache/daffodil/api/ProcessorFactory.java  |  3 +-
 .../api/exceptions/InvalidGeneratorException.java  | 43 ++++++++++++++++++++++
 .../apache/daffodil/core/compiler/Compiler.scala   |  5 ++-
 .../core/runtime1/SchemaSetRuntime1Mixin.scala     | 28 +++++++++-----
 .../apache/daffodil/lib/exceptions/Assert.scala    |  3 +-
 .../runtime1/processors/DataProcessor.scala        | 13 +++++++
 .../java/org/apache/daffodil/jexample/TestAPI.java |  4 +-
 .../org/apache/daffodil/sexample/TestAPI.scala     |  1 +
 11 files changed, 102 insertions(+), 27 deletions(-)

diff --git 
a/daffodil-core/src/main/java/org/apache/daffodil/api/CodeGenerator.java 
b/daffodil-core/src/main/java/org/apache/daffodil/api/CodeGenerator.java
index ab8b73f4c..1cad8ea24 100644
--- a/daffodil-core/src/main/java/org/apache/daffodil/api/CodeGenerator.java
+++ b/daffodil-core/src/main/java/org/apache/daffodil/api/CodeGenerator.java
@@ -17,6 +17,7 @@
 
 package org.apache.daffodil.api;
 
+import java.io.IOException;
 import java.nio.file.Path;
 
 /**
@@ -30,7 +31,7 @@ public interface CodeGenerator extends WithDiagnostics {
    * @param outputDir output directory in which to create code directory 
(codeDir)
    * @return path of newly created directory (codeDir) containing generated 
code
    */
-  Path generateCode(String outputDir);
+  Path generateCode(String outputDir) throws IOException;
 
   /**
    * Compiles the generated code in order to run it in a TDML test
diff --git 
a/daffodil-core/src/main/java/org/apache/daffodil/api/DaffodilParseXMLReader.java
 
b/daffodil-core/src/main/java/org/apache/daffodil/api/DaffodilParseXMLReader.java
index 31b92fe24..c0f1aa35e 100644
--- 
a/daffodil-core/src/main/java/org/apache/daffodil/api/DaffodilParseXMLReader.java
+++ 
b/daffodil-core/src/main/java/org/apache/daffodil/api/DaffodilParseXMLReader.java
@@ -23,8 +23,12 @@ import org.xml.sax.DTDHandler;
 import org.xml.sax.EntityResolver;
 import org.xml.sax.ErrorHandler;
 import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXNotRecognizedException;
+import org.xml.sax.SAXNotSupportedException;
 import org.xml.sax.XMLReader;
 
+import java.io.IOException;
 import java.io.InputStream;
 
 /**
@@ -61,7 +65,7 @@ public interface DaffodilParseXMLReader extends XMLReader {
    * @param name feature flag whose value is to be retrieved
    * @return value of the feature flag
    */
-  boolean getFeature(String name);
+  boolean getFeature(String name) throws SAXNotRecognizedException, 
SAXNotSupportedException;;
 
   /**
    * Set the value of the feature flag
@@ -69,7 +73,7 @@ public interface DaffodilParseXMLReader extends XMLReader {
    * @param name  feature flag to be set
    * @param value value to be assigned to feature flag
    */
-  void setFeature(String name, boolean value);
+  void setFeature(String name, boolean value) throws 
SAXNotRecognizedException, SAXNotSupportedException;;
 
   /**
    * Get the value of the property
@@ -77,7 +81,7 @@ public interface DaffodilParseXMLReader extends XMLReader {
    * @param name property whose value is to be retrieved
    * @return value of the property
    */
-  Object getProperty(String name);
+  Object getProperty(String name) throws SAXNotRecognizedException, 
SAXNotSupportedException;;
 
   /**
    * Set the value of the property
@@ -85,7 +89,7 @@ public interface DaffodilParseXMLReader extends XMLReader {
    * @param name  property whose value is to be set
    * @param value value to be assigned to the property
    */
-  void setProperty(String name, Object value);
+  void setProperty(String name, Object value) throws 
SAXNotRecognizedException, SAXNotSupportedException;
 
   /**
    * Register an entity resolver
@@ -149,14 +153,14 @@ public interface DaffodilParseXMLReader extends XMLReader 
{
    *
    * @param input data to be parsed
    */
-  void parse(InputSource input);
+  void parse(InputSource input) throws IOException, SAXException;
 
   /**
    * Parse data from a system identifier/URI. This method is not supported by 
the API.
    *
    * @param systemId URI for data to be parsed
    */
-  void parse(String systemId);
+  void parse(String systemId) throws IOException, SAXException;
 
   /**
    * Parse input data from an InputSourceDataInputStream. Infoset can 
retrieved via the registered
@@ -164,7 +168,7 @@ public interface DaffodilParseXMLReader extends XMLReader {
    *
    * @param isdis data to be parsed
    */
-  void parse(InputSourceDataInputStream isdis);
+  void parse(InputSourceDataInputStream isdis) throws IOException, 
SAXException;
 
   /**
    * Parse input data from an InputStream. Infoset can retrieved via the 
registered contentHandler
@@ -172,7 +176,7 @@ public interface DaffodilParseXMLReader extends XMLReader {
    *
    * @param stream data to be parsed
    */
-  void parse(InputStream stream);
+  void parse(InputStream stream) throws IOException, SAXException;
 
   /**
    * Parse input data from an array of bytes. Infoset can be retrieved via the 
registered
@@ -180,5 +184,5 @@ public interface DaffodilParseXMLReader extends XMLReader {
    *
    * @param arr data to be parsed
    */
-  void parse(byte[] arr);
+  void parse(byte[] arr) throws IOException, SAXException;
 }
diff --git 
a/daffodil-core/src/main/java/org/apache/daffodil/api/DaffodilUnparseContentHandler.java
 
b/daffodil-core/src/main/java/org/apache/daffodil/api/DaffodilUnparseContentHandler.java
index 41b585b3e..ab2dee26b 100644
--- 
a/daffodil-core/src/main/java/org/apache/daffodil/api/DaffodilUnparseContentHandler.java
+++ 
b/daffodil-core/src/main/java/org/apache/daffodil/api/DaffodilUnparseContentHandler.java
@@ -27,7 +27,7 @@ import org.xml.sax.Locator;
  */
 public interface DaffodilUnparseContentHandler extends ContentHandler {
   /**
-   * Returns the result of the SAX unparse containing diagnostic information. 
The {@link finish()}
+   * Returns the result of the SAX unparse containing diagnostic information. 
The {@code finish()}
    * method should be called prior to calling this function.
    *
    * If the XMLReader parse method throws a DaffodilUnhandledSAXException, 
which generally indicates
@@ -38,7 +38,7 @@ public interface DaffodilUnparseContentHandler extends 
ContentHandler {
   UnparseResult getUnparseResult();
 
   /**
-   * Ensure calls to {@link getUnparseResult()} return a value and clean up 
internal state. This
+   * Ensure calls to {@code getUnparseResult()} return a value and clean up 
internal state. This
    * should be called after XMLReader parsing has ended, even if the XMLReader 
throws an exception.
    */
   void finish();
diff --git 
a/daffodil-core/src/main/java/org/apache/daffodil/api/ProcessorFactory.java 
b/daffodil-core/src/main/java/org/apache/daffodil/api/ProcessorFactory.java
index 55ef436d7..2e687bf40 100644
--- a/daffodil-core/src/main/java/org/apache/daffodil/api/ProcessorFactory.java
+++ b/daffodil-core/src/main/java/org/apache/daffodil/api/ProcessorFactory.java
@@ -17,6 +17,7 @@
 
 package org.apache.daffodil.api;
 
+import org.apache.daffodil.api.exceptions.InvalidGeneratorException;
 /**
  * Factory to create {@link DataProcessor}s, used for parsing data
  */
@@ -46,5 +47,5 @@ public interface ProcessorFactory extends WithDiagnostics {
    * @return a {@link CodeGenerator} to generate code from a DFDL schema to 
parse or unparse data
    */
   @Experimental
-  CodeGenerator forLanguage(String language);
+  CodeGenerator forLanguage(String language) throws InvalidGeneratorException;
 }
diff --git 
a/daffodil-core/src/main/java/org/apache/daffodil/api/exceptions/InvalidGeneratorException.java
 
b/daffodil-core/src/main/java/org/apache/daffodil/api/exceptions/InvalidGeneratorException.java
new file mode 100644
index 000000000..4b6ce7f0b
--- /dev/null
+++ 
b/daffodil-core/src/main/java/org/apache/daffodil/api/exceptions/InvalidGeneratorException.java
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+
+package org.apache.daffodil.api.exceptions;
+
+/**
+ * This exception will be thrown as a result of attempting to use
+ * a generator for an invalid language
+ */
+public class InvalidGeneratorException extends Exception {
+  /**
+   * constructor for error message only
+   *
+   * @param msg error message
+   */
+  public InvalidGeneratorException(String msg) {
+    super(msg);
+  }
+
+  /**
+   * constructor for error message and cause
+   *
+   * @param msg   error message
+   * @param cause error cause
+   */
+  public InvalidGeneratorException(String msg, Throwable cause) {
+    super(msg, cause);
+  }
+}
diff --git 
a/daffodil-core/src/main/scala/org/apache/daffodil/core/compiler/Compiler.scala 
b/daffodil-core/src/main/scala/org/apache/daffodil/core/compiler/Compiler.scala
index 4a8cd3008..8e75d68ab 100644
--- 
a/daffodil-core/src/main/scala/org/apache/daffodil/core/compiler/Compiler.scala
+++ 
b/daffodil-core/src/main/scala/org/apache/daffodil/core/compiler/Compiler.scala
@@ -33,6 +33,7 @@ import scala.util.Try
 import scala.xml.Node
 
 import org.apache.daffodil.api
+import org.apache.daffodil.api.exceptions.InvalidGeneratorException
 import org.apache.daffodil.api.exceptions.InvalidParserException
 import org.apache.daffodil.core.dsom.SchemaSet
 import org.apache.daffodil.core.dsom.walker.RootView
@@ -129,7 +130,7 @@ final class ProcessorFactory private (
     val className = language match {
       case "c" => "org.apache.daffodil.codegen.c.DaffodilCCodeGenerator"
       case _ =>
-        throw new InvalidParserException(
+        throw new InvalidGeneratorException(
           s"code generator; source language $language is not supported"
         )
     }
@@ -140,7 +141,7 @@ final class ProcessorFactory private (
       _.newInstance(sset.root).asInstanceOf[api.CodeGenerator]
     }
     val codeGenerator = tryInstance.recover { case ex =>
-      throw new InvalidParserException(s"Error creating $className", ex)
+      throw new InvalidGeneratorException(s"Error creating $className", ex)
     }.get
 
     codeGenerator
diff --git 
a/daffodil-core/src/main/scala/org/apache/daffodil/core/runtime1/SchemaSetRuntime1Mixin.scala
 
b/daffodil-core/src/main/scala/org/apache/daffodil/core/runtime1/SchemaSetRuntime1Mixin.scala
index 9e3c0efe1..3e9c5634f 100644
--- 
a/daffodil-core/src/main/scala/org/apache/daffodil/core/runtime1/SchemaSetRuntime1Mixin.scala
+++ 
b/daffodil-core/src/main/scala/org/apache/daffodil/core/runtime1/SchemaSetRuntime1Mixin.scala
@@ -19,6 +19,7 @@ package org.apache.daffodil.core.runtime1
 
 import org.apache.daffodil.core.dsom.SchemaSet
 import org.apache.daffodil.core.dsom.SequenceTermBase
+import org.apache.daffodil.lib.exceptions.Assert
 import org.apache.daffodil.lib.util.Logger
 import org.apache.daffodil.runtime1.iapi.DFDL
 import org.apache.daffodil.runtime1.layers.LayerRuntimeCompiler
@@ -74,17 +75,24 @@ trait SchemaSetRuntime1Mixin {
 
   def onPath(xpath: String): DFDL.DataProcessor = {
     checkNotError()
-    if (xpath != "/")
-      root.notYetImplemented("""Path must be "/". Other path support is not 
yet implemented.""")
-    val rootERD = root.elementRuntimeData
-    root.schemaDefinitionUnless(
-      !rootERD.dpathElementCompileInfo.isOutputValueCalc,
-      "The root element cannot have the dfdl:outputValueCalc property."
-    )
-    val p = if (!root.isError) parser else null
-    val u = if (!root.isError) unparser else null
+    if (xpath != "/") {
+      root.notYetImplemented(
+        """Path must be "/". Other path support is not yet implemented."""
+      )
+    }
+    // we want to ensure that it's impossible for SchemaSetRuntimeData to get a
+    // null parser/unparser, and that it's impossible for a DataProcessor
+    // to have an error
+    Assert.invariant(!root.isError)
     val ssrd =
-      new SchemaSetRuntimeData(p, u, rootERD, variableMap, allLayers, 
layerRuntimeCompiler)
+      new SchemaSetRuntimeData(
+        parser,
+        unparser,
+        root.elementRuntimeData,
+        variableMap,
+        allLayers,
+        layerRuntimeCompiler
+      )
     if (root.numComponents > root.numUniqueComponents)
       Logger.log.debug(
         s"Compiler: component counts: unique ${root.numUniqueComponents}, 
actual ${root.numComponents}."
diff --git 
a/daffodil-core/src/main/scala/org/apache/daffodil/lib/exceptions/Assert.scala 
b/daffodil-core/src/main/scala/org/apache/daffodil/lib/exceptions/Assert.scala
index da8b82493..b3150ebbf 100644
--- 
a/daffodil-core/src/main/scala/org/apache/daffodil/lib/exceptions/Assert.scala
+++ 
b/daffodil-core/src/main/scala/org/apache/daffodil/lib/exceptions/Assert.scala
@@ -85,7 +85,8 @@ class MultiException[T <: Throwable] private (causes: 
java.util.List[T], msg: St
 }
 
 // $COVERAGE-OFF$ These exception objects should never be created by tests.
-abstract class UnsuppressableException(m: String, th: Throwable) extends 
Exception(m, th) {
+abstract class UnsuppressableException(m: String, th: Throwable)
+  extends RuntimeException(m, th) {
   def this(msg: String) = this(msg, null)
   def this(th: Throwable) = this(null, th)
 }
diff --git 
a/daffodil-core/src/main/scala/org/apache/daffodil/runtime1/processors/DataProcessor.scala
 
b/daffodil-core/src/main/scala/org/apache/daffodil/runtime1/processors/DataProcessor.scala
index fa1a7243b..cf88c42a4 100644
--- 
a/daffodil-core/src/main/scala/org/apache/daffodil/runtime1/processors/DataProcessor.scala
+++ 
b/daffodil-core/src/main/scala/org/apache/daffodil/runtime1/processors/DataProcessor.scala
@@ -190,6 +190,19 @@ class DataProcessor(
     copy(variableMap = newVariableMap)
   }
 
+  /**
+   * It is impossible for this to ever return true, because the 
ProcessorFactory
+   * compiles everything and any failures there will be captured on the 
ProcessorFactory.
+   * 
+   * If ProcessorFactory.isError == true, then calling 
ProcessorFactory.onPath() should 
+   * throw n usage exception, and we should never get a DataProcessor.
+   *
+   * If ProcessorFactory.isError == false, then it's virtually impossible to 
+   * create a DataProcessor since onPath doesn't do any new logic that could
+   * lead to any errors. The requiredEvaluates at the top of 
SchemaSetRuntime1Mixin 
+   * ensures everything (e.g. parser, unparser) is evaluated when the SchemaSet
+   * is created in the ProcessorFactory.compile()
+   */
   override def isError = false
 
   override def getDiagnostics: java.util.List[api.Diagnostic] =
diff --git 
a/daffodil-core/src/test/java/org/apache/daffodil/jexample/TestAPI.java 
b/daffodil-core/src/test/java/org/apache/daffodil/jexample/TestAPI.java
index 58082e470..7131c3c2c 100644
--- a/daffodil-core/src/test/java/org/apache/daffodil/jexample/TestAPI.java
+++ b/daffodil-core/src/test/java/org/apache/daffodil/jexample/TestAPI.java
@@ -21,6 +21,7 @@ import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
@@ -67,6 +68,7 @@ import org.apache.commons.io.FileUtils;
 import org.jdom2.output.Format;
 import org.junit.Test;
 
+import org.xml.sax.SAXException;
 import org.xml.sax.SAXNotRecognizedException;
 import org.xml.sax.SAXNotSupportedException;
 import org.xml.sax.XMLReader;
@@ -968,7 +970,7 @@ public class TestAPI {
   }
 
   @Test
-  public void testAPI21() throws IOException, ClassNotFoundException {
+  public void testAPI21() throws IOException, ClassNotFoundException, 
SAXException {
     // Test SAX parsing with errors
     org.apache.daffodil.api.Compiler c = Daffodil.compiler();
     java.io.File schemaFile = getResource("/test/api/mySchema1.dfdl.xsd");
diff --git 
a/daffodil-core/src/test/scala/org/apache/daffodil/sexample/TestAPI.scala 
b/daffodil-core/src/test/scala/org/apache/daffodil/sexample/TestAPI.scala
index 6ebc42253..cd9623c49 100644
--- a/daffodil-core/src/test/scala/org/apache/daffodil/sexample/TestAPI.scala
+++ b/daffodil-core/src/test/scala/org/apache/daffodil/sexample/TestAPI.scala
@@ -226,6 +226,7 @@ class TestAPI {
       assertEquals("42", bos.toString())
     }
   }
+
   @Test
   def testAPI2(): Unit = {
     val c = Daffodil.compiler()

Reply via email to