@@ -148,7 +148,7 @@ default UserDefinedMacro newUserDefinedMacro(String id, String input, boolean ve
148148 /**
149149 * Register an AutoCloseable closer that has to be closed when the execution is finished.
150150 * <p>
151- * Some user defined (Java implemented) or built-in macro may create resources that perform some actions
151+ * Some user- defined (Java implemented) or built-in macro may create resources that perform some actions
152152 * asynchronous. The typical example is when a macro that creates some external resource starts a separate thread to
153153 * execute the task. This task has to be joined at the end of the processing. The general model is that there is a
154154 * resource that has to be closed. The {@code closer} may be the resource itself or some object that will close the
@@ -181,7 +181,7 @@ default UserDefinedMacro newUserDefinedMacro(String id, String input, boolean ve
181181 * the input is processed, the invocation of the closers registered in the first round continues. Any closer
182182 * registered during the call to {@link Processor#process(Input) process(Input)} from a closer will be ignored.
183183 * <p>
184- * Calling this method the macro can register an {@link AutoCloseable} object. The method {@link
184+ * Calling this method, the macro can register an {@link AutoCloseable} object. The method {@link
185185 * AutoCloseable#close() close()} will be invoked when the method {@link Processor#process(Input)} finishes its top
186186 * level execution. When the method is called in recursive calls from a macro or from any other place the deferred
187187 * resources will not be closed upon return, only when the top level call is to be returned.
@@ -191,6 +191,9 @@ default UserDefinedMacro newUserDefinedMacro(String id, String input, boolean ve
191191 * once. In the order of executions, the first registering is relevant. A closer {@code c2} is treated as already
192192 * registered if there is a registered closer {@code c1} so that {@code c1.equals(c2)}.
193193 * <p>
194+ * It is important that the closer implemented {@link Object#equals(Object) equals(Object other)} never returns
195+ * {@code true} if the {@code other} object is not an instance of the same class.
196+ * <p>
194197 * It also means that any call to this method must use the return value of the method to reference the closer and
195198 * not the object passed as argument, unless they are the same object. If you pass an object {@code c2} that is
196199 * {@code c1.equals(c2)} but are not the exact same, then the method will return {@code c1} and the object {@code
@@ -199,19 +202,56 @@ default UserDefinedMacro newUserDefinedMacro(String id, String input, boolean ve
199202 * This approach was created to allow the macros to create cheap closer objects and call them from the macro
200203 * evaluation. In this case, the macro can register the closer when it knows that a closer is needed, and it does
201204 * not need to maintain a state and remember if there was already a closer registered. Also, the closer is not
202- * directly associated with the macro making it possible to register multiple closers assuming they are not
205+ * directly associated with the macro, making it possible to register multiple closers assuming they are not
203206 * equal to each other.
204207 * <p>
205208 * Note that this method, or any other method of the processor MUST NOT be invoked from other than the main thread
206- * of the Jamal processing. Even if a macro spawns a new thread the new thread must not do anything with the
209+ * of the Jamal processing. Even if a macro spawns a new thread, the new thread must not do anything with the
207210 * processor.
211+ * <p>
212+ * The closer has to implement {@link AutoCloseable} and not {@link java.io.Closeable} because
213+ * {@link java.io.Closeable} does not allow
214+ * checked exceptions, like BadSyntax from the method {@link AutoCloseable#close() close()}.
208215 *
209- * @param closer the autocloseable object to be closed at the end of the processing.
216+ * @param closer the AutoCloseable object to be closed at the end of the processing.
210217 * @return the registered closer. It may not be the same closer as the argument {@code closer}. If the closer
211218 * was already registered, then the first registered closer will be returned. More formally, if there was a {@code
212219 * closer2} already registered such that {@code closer2.equals(closer)} then {@code closer2} will be returned.
213220 */
214- AutoCloseable deferredClose (AutoCloseable closer );
221+ <T extends AutoCloseable > T deferredClose (T closer );
222+
223+ /**
224+ * Create a bad syntax exception and add it to the list of exceptions to be thrown at the end of the processing.
225+ * <p>
226+ * This is a convenience method proxying {@link #deferredThrow(BadSyntax)}.
227+ *
228+ * @param errorMessage is the error message used in String format
229+ * @param parameters the parameters to create the error message
230+ */
231+ void deferredThrow (final String errorMessage , Object ... parameters );
232+
233+ /**
234+ * Add the BadSyntax exception to the list of exceptions to be thrown at the end of the processing.
235+ * <p>
236+ * Call this method instead of throwing a BadSyntax exception whenever further processing of the input is
237+ * possible and can be advantageous to discover further syntax errors. That way the user can fix multiple
238+ * syntax errors without having to rerun the program with the fixed first error, then the second and so on.
239+ * <p>
240+ * It is recommended to call {@link #newUserDefinedMacro(String, String, String...)} unless there is a special
241+ * need to create the exception on the caller side, for example, adding a cause to the exception.
242+ *
243+ *
244+ * @param bs the BadSyntax to be thrown
245+ */
246+ void deferredThrow (final BadSyntax bs );
247+
248+ default void deferBadSyntax (BadSyntax .ThrowingRunnable runner ){
249+ try {
250+ runner .run ();
251+ }catch (BadSyntax bs ){
252+ deferredThrow (bs );
253+ }
254+ }
215255
216256 /**
217257 * Get the context object that the embedding application was setting. The context object is a general object and the
@@ -369,6 +409,7 @@ interface FileWriter {
369409 * @return the structure containing the result, which is nothing, or final name
370410 */
371411 IOHookResult write (final String fileName , final String content );
412+
372413 IOHookResult write (final String fileName , final byte [] content );
373414 }
374415
0 commit comments