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

git-site-role pushed a commit to branch asf-site
in repository https://gitbox.apache.org/repos/asf/plc4x-website.git


The following commit(s) were added to refs/heads/asf-site by this push:
     new 521b501  Site checkin for project PLC4X: Jenkins Tools
521b501 is described below

commit 521b5013bedab14426cc29a04f5d98522aaaaa7c
Author: jenkins <[email protected]>
AuthorDate: Sun Nov 28 14:08:08 2021 +0000

    Site checkin for project PLC4X: Jenkins Tools
---
 developers/code-gen/index.html               |  56 +++++++----
 developers/code-gen/language/freemarker.html | 138 +++++++++++++++++++++++----
 2 files changed, 157 insertions(+), 37 deletions(-)

diff --git a/developers/code-gen/index.html b/developers/code-gen/index.html
index bc21f45..c572c02 100644
--- a/developers/code-gen/index.html
+++ b/developers/code-gen/index.html
@@ -269,10 +269,10 @@
 <p>Concrete protocol spec parsers and templates that actually generate code 
are implemented in derived modules.</p>
 </div>
 <div class="paragraph">
-<p>Also didn&#8217;t we want to tie ourselves to only one way to specify 
protocols and to generate code.</p>
+<p>We didn&#8217;t want to tie ourselves to only one way to specify protocols 
and to generate code. Generally multiple types of formats for specifying 
drivers are thinkable and the same way multiple ways of generating code are 
possible. Currently however we only have one parser: <code>MSpec</code> and one 
generator: <code>Freemarker</code>.</p>
 </div>
 <div class="paragraph">
-<p>This adds another layer to the hierarchy.</p>
+<p>These add more layers to the hierarchy.</p>
 </div>
 <div class="paragraph">
 <p>So for example in case of generating a Siemens S7 Driver for Java this 
would look like this:</p>
@@ -300,7 +300,7 @@
 <p>Usually using them required a large amount of workarounds, which made the 
solution quite complicated.</p>
 </div>
 <div class="paragraph">
-<p>In the end only DFDL and the corresponding Apache project <a 
href="https://daffodil.apache.org";>Apache Daffodil (incubating)</a> seemed to 
provide what we were looking for.</p>
+<p>In the end only DFDL and the corresponding Apache project <a 
href="https://daffodil.apache.org";>Apache Daffodil</a> seemed to provide what 
we were looking for.</p>
 </div>
 <div class="paragraph">
 <p>With this we were able to provide first driver versions fully specified in 
XML.</p>
@@ -321,7 +321,20 @@
 <p>In general all you need to specify, is the <code>protocolName</code> and 
the <code>languageName</code>.</p>
 </div>
 <div class="paragraph">
-<p>There is also an additional parameter: <code>outputDir</code>, which 
defaults to <code>${project.build.directory}/generated-sources/plc4x/</code> 
and usually shouldn&#8217;t require being changed.</p>
+<p>An additional option <code>outputFlavor</code> allows generating multiple 
versions of a driver for a given language. This can come in handy if we want to 
be able to generate <code>read-only</code> or <code>passive mode</code> driver 
variants.</p>
+</div>
+<div class="paragraph">
+<p>Last, not least, we have a pretty generic <code>options</code> config 
option, which is a Map type.</p>
+</div>
+<div class="paragraph">
+<p>With options is it possible to pass generic options to the code-generation. 
So if a driver or language requires further customization, these options can be 
used.</p>
+</div>
+<div class="paragraph">
+<p>Currently, the <code>Java</code> module makes use of such an option for 
specifying the Java <code>package</code> the generated code uses.
+If no <code>package</code> option is provided, the default package 
<code>org.apache.plc4x.{language-name}.{protocol-name}.{output-flavor}</code> 
is used, but especially when generating custom drivers, which are not part of 
the Apache PLC4X project, different package names are better suited. So in 
these cases, the user can simply override the default package name.</p>
+</div>
+<div class="paragraph">
+<p>There is also an additional parameter: <code>outputDir</code>, which 
defaults to <code>${project.build.directory}/generated-sources/plc4x/</code> 
and usually shouldn&#8217;t require being changed in case of a 
<code>Java</code> project, but usually requires tweaking when generating code 
for other languages.</p>
 </div>
 <div class="paragraph">
 <p>Here&#8217;s an example of a driver pom for building a <code>S7</code> 
driver for <code>java</code>:</p>
@@ -375,6 +388,7 @@
             &lt;configuration&gt;
               &lt;protocolName&gt;s7&lt;/protocolName&gt;
               &lt;languageName&gt;java&lt;/languageName&gt;
+              &lt;outputFlavor&gt;read-write&lt;/outputFlavor&gt;
             &lt;/configuration&gt;
           &lt;/execution&gt;
         &lt;/executions&gt;
@@ -410,7 +424,7 @@
 </div>
 </div>
 <div class="paragraph">
-<p>So the plugin configuration is pretty straight forward, all that is 
specified, is the <code>protocolName</code> and the 
<code>languageName</code>.</p>
+<p>So the plugin configuration is pretty straight forward, all that is 
specified, is the <code>protocolName</code>, <code>languageName</code> and the 
<code>output-flavor</code>.</p>
 </div>
 <div class="paragraph">
 <p>The dependency:</p>
@@ -428,7 +442,7 @@
 <p>For example contains all classes the generated code relies on.</p>
 </div>
 <div class="paragraph">
-<p>The definitions of both the <code>s7</code> protocol as well as the 
<code>java</code> language are provided by the two dependencies:</p>
+<p>The definitions of both the <code>s7</code> protocol and <code>java</code> 
language are provided by the two dependencies:</p>
 </div>
 <div class="literalblock">
 <div class="content">
@@ -456,7 +470,7 @@
 </div>
 </div>
 <div class="paragraph">
-<p>The reason for why the dependencies are added as code-dependencies and why 
the scope is set the way it is is described in the <a 
href="#why_are_the_protocol_and_language_dependencies_done_so_strangely">Why 
are the protocol and language dependencies done so strangely?</a> section.</p>
+<p>The reason for why the dependencies are added as code-dependencies and why 
the scope is set the way it is, is described in the <a 
href="#why_are_the_protocol_and_language_dependencies_done_so_strangely">Why 
are the protocol and language dependencies done so strangely?</a> section.</p>
 </div>
 </div>
 <div class="sect2">
@@ -496,7 +510,7 @@ public interface Protocol {
      * @return the Map of types that need to be generated.
      * @throws GenerationException if anything goes wrong parsing.
      */
-    Map&lt;String, ComplexTypeDefinition&gt; getTypeDefinitions() throws 
GenerationException;
+    Map&lt;String, TypeDefinition&gt; getTypeDefinitions() throws 
GenerationException;
 
 }</pre>
 </div>
@@ -506,7 +520,7 @@ public interface Protocol {
 They could even be hard coded.</p>
 </div>
 <div class="paragraph">
-<p>However we have currently implemented utilities for universally providing 
input:</p>
+<p>However, we have currently implemented utilities for universally providing 
input:</p>
 </div>
 <div class="ulist">
 <ul>
@@ -543,8 +557,18 @@ public interface LanguageOutput {
      */
     String getName();
 
-    void generate(File outputDir, String packageName, Map&lt;String, 
ComplexTypeDefinition&gt; types)
-        throws GenerationException;
+    List&lt;String&gt; supportedOutputFlavors();
+
+    /**
+     * An additional method which allows generator to have a hint which 
options are supported by it.
+     * This method might be used to improve user experience and warn, if set 
options are ones generator does not support.
+     *
+     * @return Set containing names of options this language output can accept.
+     */
+    Set&lt;String&gt; supportedOptions();
+
+    void generate(File outputDir, String languageName, String protocolName, 
String outputFlavor,
+        Map&lt;String, TypeDefinition&gt; types, Map&lt;String, String&gt; 
options) throws GenerationException;
 
 }</pre>
 </div>
@@ -553,7 +577,7 @@ public interface LanguageOutput {
 <p>The file for registering Language modules is located at: 
<code>META-INF/services/org.apache.plc4x.plugins.codegenerator.language.LanguageOutput</code></p>
 </div>
 <div class="paragraph">
-<p>Same as with the protocol modules, the language modules could too be 
implemented in any thinkable way, however we have already implemented some 
helpers for using:</p>
+<p>Same as with the protocol modules, the language modules could also be 
implemented in any thinkable way, however we have already implemented some 
helpers for using:</p>
 </div>
 <div class="ulist">
 <ul>
@@ -575,7 +599,7 @@ public interface LanguageOutput {
 <p>This is due to some restrictions in Maven, which result from the way Maven 
generally works.</p>
 </div>
 <div class="paragraph">
-<p>The main problem is that when starting a build, in the 
<code>validate</code> phase, Maven goes through the configuration, downloads 
the plugins and configures these.
+<p>The main problem is that when starting a build, in the 
<code>validate</code>-phase, Maven goes through the configuration, downloads 
the plugins and configures these.
 This means that Maven also tries to download the dependencies of the plugins 
too.</p>
 </div>
 <div class="paragraph">
@@ -589,7 +613,7 @@ In this case the build will fail because there is no Plugin 
with that version to
 In this case the only option would be to manually build and install the plugin 
in the release version and to re-start the release (Which is not a nice thing 
for the release manager).</p>
 </div>
 <div class="paragraph">
-<p>For this reason we have stripped down the plugin and it&#8217;s 
dependencies to an absolute minimum and have released (or will release) that 
separately from the rest, hoping due to the minimality of the dependencies that 
we will not have to do it very often.</p>
+<p>For this reason we have stripped down the plugin and its dependencies to an 
absolute minimum and have released (or will release) that separately from the 
rest, hoping due to the minimality of the dependencies that we will not have to 
do it very often.</p>
 </div>
 <div class="paragraph">
 <p>As soon as the tooling is released, the version is updated in the PLC4X 
build and the release version is used without any complications.</p>
@@ -608,7 +632,7 @@ So during a release the new versions of the modules 
wouldn&#8217;t exist, this w
 <p>We could release the protocol- and the language modules separately too, but 
we want the language and protocol modules to be part of the project, to not 
over-complicate things - especially during a release.</p>
 </div>
 <div class="paragraph">
-<p>So the Maven plugin is built in a way, that it uses the modules 
dependencies and creates it&#8217;s own Classloader to contain all of these 
modules at runtime.</p>
+<p>So the Maven plugin is built in a way, that it uses the modules 
dependencies and creates its own Classloader to contain all of these modules at 
runtime.</p>
 </div>
 <div class="paragraph">
 <p>This brings the benefit of being able to utilize Maven&#8217;s capability 
of determining the build order and dynamically creating the modules build 
classpath.</p>
@@ -625,7 +649,7 @@ Here the vendor of a Servlet engine is expected to provide 
an implementation of
 It is forbidden for an application to bring this along, but it is required to 
build the application.</p>
 </div>
 <div class="paragraph">
-<p>For this the Maven scope <code>provided</code>, which tells Maven to 
provide it during the build, but to exclude it from any applications it builds 
because it will be provided by the system running the application.</p>
+<p>For this the Maven scope <code>provided</code>, which tells Maven to 
provide it during the build, but to exclude it from any applications it builds, 
because it will be provided by the system running the application.</p>
 </div>
 <div class="paragraph">
 <p>This is not quite true, but it does the trick.</p>
diff --git a/developers/code-gen/language/freemarker.html 
b/developers/code-gen/language/freemarker.html
index 9531d33..872be88 100644
--- a/developers/code-gen/language/freemarker.html
+++ b/developers/code-gen/language/freemarker.html
@@ -252,10 +252,32 @@
 </ul>
 </div>
 <div class="paragraph">
-<p>A Freemarker-based output module, has to provide a list of 
<code>Template</code> instances as well as provide a 
<code>FreemarkerLanguageTemplateHelper</code> instance.</p>
+<p>A Freemarker-based output module, has to provide a set of 
<code>Template</code> instances as well as provide a 
<code>FreemarkerLanguageTemplateHelper</code> instance.</p>
 </div>
 <div class="paragraph">
-<p>What the <code>FreemarkerLanguageOutput</code> then does, is iterate over 
all complex types provided by the protocol module, and then iterates over all 
templates the current language defines.</p>
+<p>In general, we distinguish between these types of templates:</p>
+</div>
+<div class="ulist">
+<ul>
+<li>
+<p><code>Spec Templates</code> (Global output generated once per driver in 
total)</p>
+</li>
+<li>
+<p><code>Complex Type Templates</code> (Generates output for a complex 
type)</p>
+</li>
+<li>
+<p><code>Enum Templates</code> (Generates output for enum types)</p>
+</li>
+<li>
+<p><code>DataIO Templates</code> (Generates output for reading and writing 
PlcValues, which are our PLC4X form of presenting input and output data to our 
users)</p>
+</li>
+</ul>
+</div>
+<div class="paragraph">
+<p>For each of these, the developer can provide a list of templates, which 
then can generate multiple files per type (Which is important for languages 
such as <code>C</code> where for every type we need to generate a <code>Header 
file (.h)</code> and an <code>Implementation (.c)</code>)</p>
+</div>
+<div class="paragraph">
+<p>What the <code>FreemarkerLanguageOutput</code> then does, is iterate over 
all types provided by the protocol module, and then iterate over all templates 
the current language defines.</p>
 </div>
 <div class="paragraph">
 <p>The only convention used in this utility, is that the first line of output 
a template generates will be treated as the path relative to the base output 
directory.</p>
@@ -264,7 +286,7 @@
 <p>It will automatically create all needed intermediate directories and 
generate the rest of the input to the file specified by the first line.</p>
 </div>
 <div class="paragraph">
-<p>If this line is empty, the output is skipped for this instance.</p>
+<p>If this line is empty, the output is skipped for this type.</p>
 </div>
 <div class="sect2">
 <h3 id="example_java_output">Example <code>Java</code> output</h3>
@@ -272,50 +294,120 @@
 <div class="content">
 <pre>package org.apache.plc4x.language.java;
 
-import freemarker.template.*;
+import com.google.googlejavaformat.java.Formatter;
+import com.google.googlejavaformat.java.FormatterException;
+import freemarker.template.Configuration;
+import freemarker.template.Template;
+import org.apache.commons.io.FileUtils;
 import 
org.apache.plc4x.plugins.codegenerator.protocol.freemarker.FreemarkerLanguageOutput;
 import 
org.apache.plc4x.plugins.codegenerator.protocol.freemarker.FreemarkerLanguageTemplateHelper;
-import 
org.apache.plc4x.plugins.codegenerator.types.definitions.ComplexTypeDefinition;
+import org.apache.plc4x.plugins.codegenerator.types.definitions.TypeDefinition;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
-import java.io.*;
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
 import java.util.*;
 
 public class JavaLanguageOutput extends FreemarkerLanguageOutput {
 
+    private static final Logger LOGGER = 
LoggerFactory.getLogger(JavaLanguageOutput.class);
+
+    private final Formatter formatter = new Formatter();
+
     @Override
     public String getName() {
         return "Java";
     }
 
-    protected List&lt;Template&gt; getTemplates(Configuration 
freemarkerConfiguration) throws IOException {
+    @Override
+    public Set&lt;String&gt; supportedOptions() {
+        return Collections.singleton("package");
+    }
+
+    @Override
+    public List&lt;String&gt; supportedOutputFlavors() {
+        return Arrays.asList("read-write", "read-only", "passive");
+    }
+
+    @Override
+    protected List&lt;Template&gt; getSpecTemplates(Configuration 
freemarkerConfiguration) {
+        return Collections.emptyList();
+    }
+
+    @Override
+    protected List&lt;Template&gt; getComplexTypeTemplates(Configuration 
freemarkerConfiguration) throws IOException {
         return Arrays.asList(
-            
freemarkerConfiguration.getTemplate("templates/java/pojo-template.ftlh"),
-            
freemarkerConfiguration.getTemplate("templates/java/io-template.ftlh"));
+            
freemarkerConfiguration.getTemplate("templates/java/pojo-template.java.ftlh"),
+            
freemarkerConfiguration.getTemplate("templates/java/io-template.java.ftlh"));
     }
 
-    protected FreemarkerLanguageTemplateHelper getHelper(Map&lt;String, 
ComplexTypeDefinition&gt; types) {
-        return new JavaLanguageTemplateHelper(types);
+    @Override
+    protected List&lt;Template&gt; getEnumTypeTemplates(Configuration 
freemarkerConfiguration) throws IOException {
+        return Collections.singletonList(
+            
freemarkerConfiguration.getTemplate("templates/java/enum-template.java.ftlh"));
     }
 
+    @Override
+    protected List&lt;Template&gt; getDataIoTemplates(Configuration 
freemarkerConfiguration) throws IOException {
+        return Collections.singletonList(
+            
freemarkerConfiguration.getTemplate("templates/java/data-io-template.java.ftlh"));
+    }
+
+    @Override
+    protected FreemarkerLanguageTemplateHelper getHelper(TypeDefinition 
thisType, String protocolName, String flavorName, Map&lt;String, 
TypeDefinition&gt; types,
+                                                         Map&lt;String, 
String&gt; options) {
+        return new JavaLanguageTemplateHelper(thisType, protocolName, 
flavorName, types, options);
+    }
+
+    @Override
+    protected void postProcessTemplateOutput(File outputFile) {
+        try {
+            FileUtils.writeStringToFile(
+                outputFile,
+                formatter.formatSourceAndFixImports(
+                    FileUtils.readFileToString(outputFile, 
StandardCharsets.UTF_8)
+                ),
+                StandardCharsets.UTF_8
+            );
+        } catch (IOException | FormatterException e) {
+            LOGGER.error("Error formatting {}", outputFile, e);
+        }
+    }
 }</pre>
 </div>
 </div>
 <div class="paragraph">
-<p>As you can see, this output generates up to two files for every complex 
type.</p>
+<p>The <code>getName</code> method returns <code>Java</code>, this is what 
needs to be defined in the <code>plc4x-maven-plugin</code> configuration in the 
<code>language</code> option in order to select this output format.</p>
 </div>
 <div class="paragraph">
-<p>This is one file, providing the code for the POJO itself.
-The second one generates the IO-component, which contains all the code for 
parsing and serializing the corresponding POJO.</p>
+<p><code>supportedOptions</code> tells the plugin which <code>option</code> 
tags this code-generation output supports. In case of the <code>Java</code> 
output, this is only the <code>package</code> option, which defines the package 
name of the generated output.</p>
 </div>
 <div class="paragraph">
-<p>In other languages, for example <code>C++</code> it&#8217;s possible to use 
a third one to generate Header files or for <code>Python</code> perhaps only 
one in total.</p>
+<p>With <code>supportedOutputFlavors</code> we tell the user, that in general 
we support the three options: <code>read-write</code>, <code>read-only</code> 
and <code>passive</code> as valid inputs for the <code>outputFlavor</code> 
config option of the code-generation plugin.</p>
+</div>
+<div class="paragraph">
+<p>In this case Java doesn&#8217;t require any global files being generated 
for java, so we simply return an empty collection.</p>
+</div>
+<div class="paragraph">
+<p>For complex types, we currently use two templates (however this will soon 
be reduced to one). So for every complex type in a protocol definition, the 
templates: <code>templates/java/pojo-template.java.ftlh</code> and 
<code>templates/java/io-template.java.ftlh</code> will be executed.</p>
+</div>
+<div class="paragraph">
+<p>In case of enum types, only one template is being used.</p>
+</div>
+<div class="paragraph">
+<p>Same as for data-io.</p>
+</div>
+<div class="paragraph">
+<p>The next important method is the <code>getHelper</code> method, which 
returns an object, that is passed to the templates with the name 
<code>helper</code>. As mentioned before, a lot of operations would be too 
complex to implement in pure Freemarker code, so with these helpers every 
language can provide a helper utility for handling the complex operations.</p>
 </div>
 <div class="paragraph">
 <p>Here an example for a part of a template for generating Java POJOs:</p>
 </div>
 <div class="literalblock">
 <div class="content">
-<pre>${packageName?replace(".", "/")}/${typeName}.java
+<pre>${helper.packageName(protocolName, languageName, 
outputFlavor)?replace(".", "/")}/${type.name}.java
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
@@ -334,11 +426,13 @@ The second one generates the IO-component, which contains 
all the code for parsi
  * specific language governing permissions and limitations
  * under the License.
  */
-package ${packageName};
+package ${helper.packageName(protocolName, languageName, outputFlavor)};
 
-import org.apache.plc4x.java.utils.SizeAware;
+... imports ...
 
-public&lt;#if type.abstract&gt; abstract&lt;/#if&gt; class ${typeName}&lt;#if 
type.parentType??&gt; extends ${type.parentType.name}&lt;/#if&gt; implements 
SizeAware {
+// Code generated by code-generation. DO NOT EDIT.
+
+public&lt;#if type.isDiscriminatedParentTypeDefinition()&gt; 
abstract&lt;/#if&gt; class ${type.name}&lt;#if type.parentType??&gt; extends 
${type.parentType.name}&lt;/#if&gt; implements Message {
 
     ... SNIP ...
 
@@ -346,8 +440,10 @@ public&lt;#if type.abstract&gt; abstract&lt;/#if&gt; class 
${typeName}&lt;#if ty
 </div>
 </div>
 <div class="paragraph">
-<p>So as you can see, the first line will generate the file-path of the to be 
generated output.
-Which is followed</p>
+<p>So as you can see, the first line will generate the file-path of the to be 
generated output.</p>
+</div>
+<div class="paragraph">
+<p>As when creating more and more outputs for different languages, we have 
realized, that a lot of the code needed in the <code>Helper</code> utility 
repeats, we therefore introduced a so-called 
<code>BaseFreemarkerLanguageTemplateHelper</code> which contains a lot of 
stuff, that is important when generating new language output.</p>
 </div>
 </div>
 </div>

Reply via email to