All Downloads are FREE. Search and download functionalities are using the official Maven repository.

javax0.jamal.api.Processor Maven / Gradle / Ivy

There is a newer version: 2.8.2
Show newest version
package javax0.jamal.api;

import java.io.IOException;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Properties;

/**
 * The processor object that can be used to process an input to generate the Jamal output.
 * 

* Processor instances should not be used by multiple threads. They are not thread safe by design. *

* A processor is AutoClosable, and it has to be closed. *

* Creating a processor instance may be expensive consuming significant amount of CPU cycles. Create one in your code * when you are going to need it. */ public interface Processor extends AutoCloseable { /** * Process the input and result the string after processing all built-in and user defined macros. * * @param in the input the processor has to work on. * @return the string after the processing * @throws BadSyntax when the input contains something that cannot be processed. */ String process(final Input in) throws BadSyntax; /** * A convenience method that executes the Jamal process for a String. It may be handy when the processor is * used to process some input that is not a file. * * @param in the input string * @return the result of the processing * @throws BadSyntax in case the input contains something that cannot be processed. */ default String process(final String in) throws BadSyntax { throw new IllegalArgumentException("The method process(String) is not supported by this processor"); } /** * Get the macro register of this processor. See {@link MacroRegister} * * @return the register */ MacroRegister getRegister(); /** * Get the JShell engine that the processor has. *

* Note that the JShell engine may not be initialized. It initializes automatically the first time when the engine's * {@link JShellEngine#evaluate(String)} is invoked. * * @return the JShell engine */ JShellEngine getJShellEngine(); /** * Create a new user defined macro. The implementation of this method calls the constructor of the matching * implementation of the user defined macro. The existence of this method loosens the coupling of the user of the * API and the actual implementation. The code using the implementation may need only to initiate the engine that * implements this interface, but does not need to access directly the UserDefinedMacro or other interface * implementations. *

* NOTE: The invocation of this method creates a new object, but it DOES NOT register the created user defined macro * in the macro registry. The sole purpose of this method is to decouple the API usage and the implementation. * * @param id the identifier (name) of the macro * @param input the content of the macro * @param params the parameter names of the macro * @return the new user defined macro * @throws BadSyntax in case the parameter names contain each other */ UserDefinedMacro newUserDefinedMacro(String id, String input, String[] params) throws BadSyntax; /** * The same as {@link #newUserDefinedMacro(String, String, String[])} but it can also define when the macro is * verbatim. The default implementation ignores the verbatim flag. See the note of {@link * #newUserDefinedMacro(String, String, String[]) newUserDefinedMacro()} * * @param id see {@link #newUserDefinedMacro(String, String, String[])} * @param input see {@link #newUserDefinedMacro(String, String, String[])} * @param verbatim {@code true} if the result of the macro should not be evaluated * @param params see {@link #newUserDefinedMacro(String, String, String[])} * @return see {@link #newUserDefinedMacro(String, String, String[])} * @throws BadSyntax see {@link #newUserDefinedMacro(String, String, String[])} */ default UserDefinedMacro newUserDefinedMacro(String id, String input, boolean verbatim, String[] params) throws BadSyntax { return newUserDefinedMacro(id, input, params); } /** * Create a new user defined script. Read the important comments for {@link #newUserDefinedMacro(String, String, * String[])} * * @param id see {@link #newUserDefinedMacro(String, String, String[])} * @param scriptType see {@link #newUserDefinedMacro(String, String, String[])} * @param input see {@link #newUserDefinedMacro(String, String, String[])} * @param params see {@link #newUserDefinedMacro(String, String, String[])} * @return see {@link #newUserDefinedMacro(String, String, String[])} */ ScriptMacro newScriptMacro(String id, String scriptType, String input, String[] params) throws BadSyntax; /** * Register an AutoCloseable closer that has to be closed when the execution is finished. *

* Some user defined (Java implemented) or built-in macro may create resources that perform some actions * asynchronous. The typical example is when a macro that creates some external resource starts a separate thread to * execute the task. This task has to be joined at the end of the processing. The general model is that there is a * resource that has to be closed. The {@code closer} may be the resource itself or some object that will close the * resource. *

* Closing as an operation may be treated fairly liberal. Almost anything can be "closing". The macro * {@code xmlFormat}, for example, "closes" the operation replacing the final output of Jamal with the XML formatted * version. *

* The {@code closer} may also implement the interfaces {@link javax0.jamal.api.Closer.ProcessorAware} or {@link * javax0.jamal.api.Closer.OutputAware} or both. * In that case the * {@link javax0.jamal.api.Closer.ProcessorAware#set(Processor) set(Processor)} * and/or * {@link javax0.jamal.api.Closer.OutputAware#set(Input)} set(Input)} * methods will be called before calling {@link AutoCloseable#close() close()} passing the {@link Processor} * or the {@link Input} instance holding the final processed output as argument. *

* Since the call to {@link AutoCloseable#close() close()} comes before {@link Processor#process(Input)} returns the * output may be altered by the implemented {@link AutoCloseable#close() close()} method. That way a built-in macro * may implement post-processing logic that works on the whole output. *

* The sample test {@code javax0.jamal.engine.TestProcessor#testPostProcessor()} in the file {@code * src/test/java/javax0/jamal/engine/TestProcessor.java} shows an example that converts the whole result to * uppercase. *

* The closer objects {@link AutoCloseable#close() close()} method may invoke the injected processors * {@link Processor#process(Input) process(Input)} method. In this case, however, the processor is already in a * state closing resources and processing the whole input again will not recursively invoke the closers. After * the input is processed the invocation of the closers registered in the first round continues. Any closer * registered during the call to {@link Processor#process(Input) process(Input)} from a closer will be ignored. *

* Calling this method the macro can register an {@link AutoCloseable} object. The method {@link * AutoCloseable#close() close()} will be invoked when the method {@link Processor#process(Input)} finishes its top * level execution. When the method is called in recursive calls from a macro or from any other place the deferred * resources will not be closed upon return, only when the top level call is to be returned. *

* The processor implementation guarantees that the processor will invoke the closers in the order registered. * The processor will never register an already registered closer. In other words, every closer is invoked only * once. In the order of executions the first registering is relevant. A closer {@code c2} is treated as already * registered if there is a registered closer {@code c1} so that {@code c1.equals(c2)}. *

* Note that this method, or any other method of the processor MUST NOT be invoked from other than the main thread * of the Jamal processing. Even if a macro spawns a new thread the new thread must not do anything with the * processor. * * @param closer the autocloseable object to be closed at the end of the processing. * @return the registered closer. It may not be the same closer as the argument {@code closer}. If the closer * was already registered, then the first registered closer will be returned. More formally, if there was a {@code * closer2} already registered such that {@code closer2.equals(closer)} then {@code closer2} will be returned. */ AutoCloseable deferredClose(AutoCloseable closer); /** * Get the context object that the embedding application was setting. The context object is a general object and the * processor does not do anything with it, except that it provides this method for all macros to get access to the * object. *

* The implementation of the processor must return the same object during its lifetime. * * @return the context object the embedding application set or {@code null} in case the context object was not set. */ Context getContext(); /** * @param id the identifier of the user defined macro * @return {@code true} if the user defined macro is defined at the current contex and {@code false} otherwise. */ default boolean isDefined(String id) { return getRegister().getUserDefined(id).isPresent(); } /** * Define a new user defined macro on the global level. Technically anything can be defined that implements the * {@link Identified} interface. Usually {@link UserDefinedMacro} is registered using this method. * * @param macro the macro to be registered */ default void defineGlobal(Identified macro) { getRegister().global(macro); } /** * Define a new user defined macro on the current scope. Technically anything can be defined that implements the * {@link Identified} interface. Usually {@link UserDefinedMacro} is registered using this method. * * @param macro the macro to be registered */ default void define(Identified macro) { getRegister().define(macro); } /** * This is a convenience method with the default implementation calling to the {@link * MacroRegister#separators(String, String)} method. * * @param openMacro see {@link MacroRegister#separators(String, String)} * @param closeMacro see {@link MacroRegister#separators(String, String)} * @throws BadSyntax see {@link MacroRegister#separators(String, String)} */ default void separators(String openMacro, String closeMacro) throws BadSyntax { getRegister().separators(openMacro, closeMacro); } Deque EMPTY_DEQUEUE = new ArrayDeque<>(); /** * @return the current number of errors that were detected in the source file, but were not aborting the evaluation. */ default Deque errors() { return EMPTY_DEQUEUE; } /** * Throw the last exception that was deferred. */ default void throwUp() throws BadSyntax { } /** * Convert a Jamal version string to a {@link Runtime.Version}. *

* The method removes the trailing zero versions, because those are not allowed by the parsing of {@link * Runtime.Version}. * * @param version the version string, probably from the macro argument {@code require} * @return the parsed version, which can be compared to the current version */ static Runtime.Version jamalVersion(final String version) { return Runtime.Version.parse(version.replaceAll("(?:\\.0+){0,2}\\.0+(-|$)", "$1")); } /** * Load the version property from the properties file and store it into the properties variable {@code version}. The * properties will contain one property named {@code "version"}. * * @param version the properties that will hold the version property */ static void jamalVersion(Properties version) { try { version.load(Processor.class.getClassLoader().getResourceAsStream("version.properties")); } catch (IOException e) { throw new IllegalArgumentException("Version information of Jamal cannot be identified."); } } /** * @return the current Jamal version in the form of a {@link Runtime.Version} */ static Runtime.Version jamalVersion() { final var version = new Properties(); jamalVersion(version); return jamalVersion(version.getProperty("version")); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy