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

ahuber pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/isis.git


The following commit(s) were added to refs/heads/master by this push:
     new 519fb7b  ISIS-2426: Add isis/tooling ecosystem
519fb7b is described below

commit 519fb7bb9a483a60edc674175cd7024a057c72b3
Author: Andi Huber <[email protected]>
AuthorDate: Wed Sep 9 08:52:04 2020 +0200

    ISIS-2426: Add isis/tooling ecosystem
---
 tooling/asciidoc-model/pom.xml                     |  49 +++++
 .../isis/tooling/adocmodel/AsciiDocFactory.java    | 143 +++++++++++++
 .../isis/tooling/adocmodel/AsciiDocWriter.java     | 174 ++++++++++++++++
 .../tooling/adocmodel/ast/ContentNodeAbstract.java | 229 +++++++++++++++++++++
 .../isis/tooling/adocmodel/ast/SimpleCell.java     |  64 ++++++
 .../isis/tooling/adocmodel/ast/SimpleColumn.java   |  73 +++++++
 .../isis/tooling/adocmodel/ast/SimpleDocument.java |  63 ++++++
 .../isis/tooling/adocmodel/ast/SimpleRow.java      |  33 +++
 .../adocmodel/ast/SimpleStructuralNode.java        | 121 +++++++++++
 .../isis/tooling/adocmodel/ast/SimpleTable.java    |  46 +++++
 .../tooling/adocmodel/test/AsciiDocWriterTest.java | 179 ++++++++++++++++
 .../tooling/adocmodel/test/attributed-table.adoc   |   8 +
 .../isis/tooling/adocmodel/test/simple-table.adoc  |  12 ++
 tooling/commons/pom.xml                            |  38 ++++
 .../org/apache/isis/tooling/_infra/_Files.java     |  51 +++++
 .../org/apache/isis/tooling/_infra/_Strings.java   |  39 ++++
 tooling/pom.xml                                    | 218 ++++++++++++++++++++
 tooling/project-model/pom.xml                      |  38 ++++
 18 files changed, 1578 insertions(+)

diff --git a/tooling/asciidoc-model/pom.xml b/tooling/asciidoc-model/pom.xml
new file mode 100644
index 0000000..13b42c0
--- /dev/null
+++ b/tooling/asciidoc-model/pom.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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. -->
+<project xmlns="http://maven.apache.org/POM/4.0.0";
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+       xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/maven-v4_0_0.xsd";>
+       <modelVersion>4.0.0</modelVersion>
+
+       <parent>
+               <groupId>org.apache.isis.tooling</groupId>
+               <artifactId>isis-tooling</artifactId>
+               <version>2.0.0-SNAPSHOT</version>
+       </parent>
+
+       <artifactId>isis-tooling-asciidoc-model</artifactId>
+
+       <name>Apache Isis Tooling - AsciiDoc Model</name>
+       <description>
+        Library for programmatic AsciiDoc generation.
+    </description>
+
+       <properties>
+       </properties>
+
+       <dependencies>
+
+               <dependency>
+                       <groupId>org.apache.isis.tooling</groupId>
+                       <artifactId>isis-tooling-commons</artifactId>
+               </dependency>
+
+               <dependency>
+                       <groupId>org.asciidoctor</groupId>
+                       <artifactId>asciidoctorj</artifactId>
+               </dependency>
+
+       </dependencies>
+
+</project>
+
+
diff --git 
a/tooling/asciidoc-model/src/main/java/org/apache/isis/tooling/adocmodel/AsciiDocFactory.java
 
b/tooling/asciidoc-model/src/main/java/org/apache/isis/tooling/adocmodel/AsciiDocFactory.java
new file mode 100644
index 0000000..78832a2
--- /dev/null
+++ 
b/tooling/asciidoc-model/src/main/java/org/apache/isis/tooling/adocmodel/AsciiDocFactory.java
@@ -0,0 +1,143 @@
+/*
+ *  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.isis.tooling.adocmodel;
+
+import org.asciidoctor.ast.Cell;
+import org.asciidoctor.ast.Column;
+import org.asciidoctor.ast.Document;
+import org.asciidoctor.ast.Row;
+import org.asciidoctor.ast.StructuralNode;
+import org.asciidoctor.ast.Table;
+
+import org.apache.isis.tooling.adocmodel.ast.SimpleCell;
+import org.apache.isis.tooling.adocmodel.ast.SimpleColumn;
+import org.apache.isis.tooling.adocmodel.ast.SimpleDocument;
+import org.apache.isis.tooling.adocmodel.ast.SimpleRow;
+import org.apache.isis.tooling.adocmodel.ast.SimpleTable;
+
+import lombok.val;
+
+public class AsciiDocFactory {
+
+    /**
+     * @return a blank/empty document object
+     */
+    public static Document doc() {
+        return new SimpleDocument();     
+    }
+
+    public static Table table(StructuralNode parent) {
+        val table = new SimpleTable();
+        parent.getBlocks().add(table);
+        return table;
+    }
+    
+    public static Column col(Table table) {
+        val column = new SimpleColumn();
+        table.getColumns().add(column);
+        column.setParent(table);
+        return column;
+    }
+
+    public static Row row(Table table) {
+        val row = new SimpleRow();
+        table.getBody().add(row);
+        return row;
+    }
+    
+    public static Row headRow(Table table) {
+        val row = new SimpleRow();
+        table.getHeader().add(row);
+        return row;
+    }
+    
+    public static Row footRow(Table table) {
+        val row = new SimpleRow();
+        table.getFooter().add(row);
+        return row;
+    }
+    
+    public static Cell cell(Row row, Column column, String source) {
+        val cell = new SimpleCell();
+        row.getCells().add(cell);
+        cell.setParent(column);
+        cell.setSource(source);
+        return cell;
+    }
+
+    public static Cell cell(Table table, int rowIndex, int colIndex, String 
source) {
+        val row = getOrCreateRow(table, rowIndex);
+        val col = getOrCreateColumn(table, colIndex);
+        return cell(row, col, source);
+    }
+    
+    public static Cell headCell(Table table, int rowIndex, int colIndex, 
String source) {
+        val row = getOrCreateHeadRow(table, rowIndex);
+        val col = getOrCreateColumn(table, colIndex);
+        return cell(row, col, source);
+    }
+    
+    public static Cell footCell(Table table, int rowIndex, int colIndex, 
String source) {
+        val row = getOrCreateFootRow(table, rowIndex);
+        val col = getOrCreateColumn(table, colIndex);
+        return cell(row, col, source);
+    }
+    
+    
+    // -- HELPER
+    
+    private static Column getOrCreateColumn(Table table, int colIndex) {
+        int maxIndexAvailable = table.getColumns().size() - 1; 
+        int colsToBeCreated = colIndex - maxIndexAvailable;
+        for(int i=0; i<colsToBeCreated; ++i) {
+            col(table);
+        }
+        return table.getColumns().get(colIndex);
+    }
+    
+    private static Row getOrCreateRow(Table table, int rowIndex) {
+        int maxIndexAvailable = table.getBody().size() - 1; 
+        int rowsToBeCreated = rowIndex - maxIndexAvailable;
+        for(int i=0; i<rowsToBeCreated; ++i) {
+            row(table);
+        }
+        return table.getBody().get(rowIndex);
+    }
+    
+    private static Row getOrCreateHeadRow(Table table, int rowIndex) {
+        int maxIndexAvailable = table.getHeader().size() - 1; 
+        int rowsToBeCreated = rowIndex - maxIndexAvailable;
+        for(int i=0; i<rowsToBeCreated; ++i) {
+            headRow(table);
+        }
+        return table.getHeader().get(rowIndex);
+    }
+    
+    private static Row getOrCreateFootRow(Table table, int rowIndex) {
+        int maxIndexAvailable = table.getFooter().size() - 1; 
+        int rowsToBeCreated = rowIndex - maxIndexAvailable;
+        for(int i=0; i<rowsToBeCreated; ++i) {
+            footRow(table);
+        }
+        return table.getFooter().get(rowIndex);
+    }
+    
+
+    
+}
diff --git 
a/tooling/asciidoc-model/src/main/java/org/apache/isis/tooling/adocmodel/AsciiDocWriter.java
 
b/tooling/asciidoc-model/src/main/java/org/apache/isis/tooling/adocmodel/AsciiDocWriter.java
new file mode 100644
index 0000000..b1fc9a8
--- /dev/null
+++ 
b/tooling/asciidoc-model/src/main/java/org/apache/isis/tooling/adocmodel/AsciiDocWriter.java
@@ -0,0 +1,174 @@
+/*
+ *  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.isis.tooling.adocmodel;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import org.asciidoctor.Asciidoctor;
+import org.asciidoctor.ast.Document;
+import org.asciidoctor.ast.Table;
+
+import org.apache.isis.tooling._infra._Strings;
+
+import lombok.RequiredArgsConstructor;
+import lombok.val;
+import lombok.extern.log4j.Log4j2;
+
+@Log4j2
+public class AsciiDocWriter {
+
+    /**
+     * the inverse of {@link Asciidoctor#load(String, java.util.Map)}}
+     * @throws IOException 
+     */
+    public static String toString(Document doc) throws IOException {
+        if(doc==null) {
+            return "";
+        }
+        val adocWriter = new AsciiDocWriter();
+        val stringWriter = new StringWriter();
+        adocWriter.write(doc, stringWriter);
+        return stringWriter.toString();
+    }
+  
+    /**
+     * the inverse of {@link Asciidoctor#load(String, java.util.Map)}}
+     * @throws IOException 
+     */
+    public void write(Document doc, Writer writer) throws IOException {
+        
+        if(doc==null) {
+            return;
+        }
+        
+        val formatWriter = new FormatWriter(writer);
+        
+        formatWriter.ifNonEmpty("= %s\n", doc.getTitle());
+        
+        for(val block : doc.getBlocks()) {
+            if(block instanceof Table) {
+                write((Table)block, formatWriter);
+                continue;
+            }
+            //TODO handle other types of blocks as well
+            log.warn("unknown block type detected (possibly not implemented 
yet) {}", block.getClass().getName());
+        }
+        
+    }
+
+    // -- TABLE
+
+//  |===
+//  |Name of Column 1 |Name of Column 2 |Name of Column 3 
+//
+//  |Cell in column 1, row 1
+//  |Cell in column 2, row 1
+//  |Cell in column 3, row 1
+//
+//  |Cell in column 1, row 2
+//  |Cell in column 2, row 2
+//  |Cell in column 3, row 2
+//  |===
+    private void write(Table table, FormatWriter writer) throws IOException {
+        writer.ifNonEmpty(".%s\n", table.getTitle());
+        
+        writer.appendMap("[%s]\n", formatedAttrMap(table), "%s=\"%s\"");
+        
+        writer.append("|===\n");
+        
+        // table.getColumns() ... styles
+        
+        for(val headRow : table.getHeader()) {
+            for(val cell : headRow.getCells()) {
+                writer.append("|%s ", cell.getSource());    
+            }
+            writer.append("\n");
+        }
+        
+        for(val row : table.getBody()) {
+            writer.append("\n"); // empty line before each row
+            for(val cell : row.getCells()) {
+                writer.append("|%s\n", cell.getSource());    
+            }
+        }
+        
+        writer.append("|===\n");
+    }
+    
+    private static Map<String, String> formatedAttrMap(Table table) {
+        final Map<String, String> formatedAttrMap = new LinkedHashMap<>();
+        
+        if(table.hasAttribute("cols")) {
+            formatedAttrMap.put("cols", (String)table.getAttribute("cols"));
+        }
+        
+        val options = table.getAttributes().entrySet()
+                .stream()
+                .filter(entry->entry.getKey().endsWith("-option"))
+                .map(entry->entry.getKey().substring(0, 
entry.getKey().length()-7))
+                .collect(Collectors.joining(","));
+        
+        if(!options.isEmpty()) {
+            formatedAttrMap.put("options", options);
+        }
+        
+        return formatedAttrMap;
+    }
+    
+    
+    @RequiredArgsConstructor
+    private static class FormatWriter {
+        private final Writer writer;
+    
+        void append(String string) throws IOException {
+            writer.append(string);
+        }
+        
+        void append(String format, Object ...args) throws IOException {
+            writer.append(String.format(format, args));
+        }
+
+        void ifNonEmpty(String format, String string) throws IOException {
+            if(_Strings.isNullOrEmpty(string)) {
+                return;
+            }
+            writer.append(String.format(format, string));
+        }
+        
+        void appendMap(String mapFormat, Map<String, String> map, String 
entryFormat) throws IOException {
+            if(map.isEmpty()) {
+                return;
+            }
+            val elements = map.entrySet()
+            .stream()
+            .map(entry->String.format(entryFormat, entry.getKey(), 
entry.getValue()))
+            .collect(Collectors.joining(", "));
+            append(mapFormat, elements);
+        }
+        
+    }
+    
+    
+    
+}
diff --git 
a/tooling/asciidoc-model/src/main/java/org/apache/isis/tooling/adocmodel/ast/ContentNodeAbstract.java
 
b/tooling/asciidoc-model/src/main/java/org/apache/isis/tooling/adocmodel/ast/ContentNodeAbstract.java
new file mode 100644
index 0000000..8412347
--- /dev/null
+++ 
b/tooling/asciidoc-model/src/main/java/org/apache/isis/tooling/adocmodel/ast/ContentNodeAbstract.java
@@ -0,0 +1,229 @@
+/*
+ *  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.isis.tooling.adocmodel.ast;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.asciidoctor.ast.ContentNode;
+import org.asciidoctor.ast.Document;
+
+import org.apache.isis.tooling._infra._Strings;
+
+import lombok.Getter;
+import lombok.Setter;
+import lombok.val;
+
+public abstract class ContentNodeAbstract implements ContentNode {
+
+    @Getter @Setter private String id;
+    @Getter @Setter private String context;
+    @Getter @Setter private ContentNode parent;
+    @Getter @Setter private Document document;
+    @Getter @Setter private String nodeName;
+    @Getter @Setter private boolean inline;
+    @Getter @Setter private boolean block;
+    @Getter private final Map<String, Object> attributes = new HashMap<>();
+    @Getter @Setter private String role;
+    @Getter private final List<String> roles = new ArrayList<>(); 
+    @Getter @Setter private String reftext;
+    
+    @Override
+    public String id() {
+        return getId();
+    }
+    
+    @Override
+    public String context() {
+        return getContext();
+    }
+
+    @Override
+    public ContentNode parent() {
+        return getParent();
+    }
+
+    @Override
+    public Document document() {
+        return getDocument();
+    }
+
+    @Override
+    @Deprecated
+    public Object getAttr(Object name, Object defaultValue, boolean inherit) {
+        return getAttribute(name, defaultValue, inherit);
+    }
+
+    @Override
+    @Deprecated
+    public Object getAttr(Object name, Object defaultValue) {
+        return getAttribute(name, defaultValue);
+    }
+
+    @Override
+    @Deprecated
+    public Object getAttr(Object name) {
+        return getAttribute(name);
+    }
+
+    @Override
+    public Object getAttribute(Object name, Object defaultValue, boolean 
inherit) {
+        return attributes.getOrDefault(name, defaultValue);
+    }
+
+    @Override
+    public Object getAttribute(Object name, Object defaultValue) {
+        return attributes.getOrDefault(name, defaultValue);
+    }
+
+    @Override
+    public Object getAttribute(Object name) {
+        return attributes.get(name);
+    }
+
+    @Override
+    @Deprecated
+    public boolean isAttr(Object name, Object expected, boolean inherit) {
+        return isAttribute(name, expected, inherit);
+    }
+
+    @Override
+    @Deprecated
+    public boolean isAttr(Object name, Object expected) {
+        return isAttribute(name, expected);
+    }
+
+    @Override
+    public boolean isAttribute(Object name, Object expected, boolean inherit) {
+        return Boolean.TRUE == getAttribute(name, expected, inherit);
+    }
+
+    @Override
+    public boolean isAttribute(Object name, Object expected) {
+        return Boolean.TRUE == getAttribute(name, expected);
+    }
+
+    @Override
+    @Deprecated
+    public boolean hasAttr(Object name) {
+        return hasAttribute(name);
+    }
+
+    @Override
+    @Deprecated
+    public boolean hasAttr(Object name, boolean inherited) {
+        return hasAttribute(name, inherited);
+    }
+
+    @Override
+    public boolean hasAttribute(Object name) {
+        return getAttribute(name)!=null;
+    }
+
+    @Override
+    public boolean hasAttribute(Object name, boolean inherited) {
+        return getAttribute(name, inherited)!=null;
+    }
+
+    @Override
+    @Deprecated
+    public boolean setAttr(Object name, Object value, boolean overwrite) {
+        return setAttribute(name, value, overwrite);
+    }
+
+    @Override
+    public boolean setAttribute(Object name, Object value, boolean overwrite) {
+        val key = (String)name;
+        return overwrite 
+                ? attributes.put(key, value)==null
+                : attributes.get(key)!=null
+                    ? false
+                    : attributes.put(key, value)==null;
+    }
+
+    @Override
+    public boolean isOption(Object name) {
+        return false; // FIXME
+    }
+
+    @Override
+    public boolean isRole() {
+        return !_Strings.isNullOrEmpty(getRole());
+    }
+
+    @Override
+    @Deprecated
+    public String role() {
+        return getRole();
+    }
+
+    @Override
+    public boolean hasRole(String role) {
+        return roles.contains(role);
+    }
+
+    @Override
+    public void addRole(String role) {
+        roles.add(role);
+    }
+
+    @Override
+    public void removeRole(String role) {
+        roles.remove(role);
+    }
+
+    @Override
+    public boolean isReftext() {
+        return !_Strings.isNullOrEmpty(getReftext());
+    }
+
+    @Override
+    public String iconUri(String name) {
+        return String.format("[icon_uri %s]", name);
+    }
+
+    @Override
+    public String mediaUri(String target) {
+        return String.format("[media_uri %s]", target);
+    }
+
+    @Override
+    public String imageUri(String targetImage) {
+        return String.format("[image_uri %s]", targetImage);
+    }
+
+    @Override
+    public String imageUri(String targetImage, String assetDirKey) {
+        return String.format("[media_uri %s %s]", targetImage, assetDirKey);
+    }
+
+    @Override
+    public String readAsset(String path, Map<Object, Object> opts) {
+        return String.format("[read_asset %s %s]", path, opts);
+    }
+
+    @Override
+    public String normalizeWebPath(String path, String start, boolean 
preserveUriTarget) {
+        return String.format("[normalize_web_path %s %s %b]", path, start, 
preserveUriTarget);
+    }
+
+    
+}
diff --git 
a/tooling/asciidoc-model/src/main/java/org/apache/isis/tooling/adocmodel/ast/SimpleCell.java
 
b/tooling/asciidoc-model/src/main/java/org/apache/isis/tooling/adocmodel/ast/SimpleCell.java
new file mode 100644
index 0000000..96c35e9
--- /dev/null
+++ 
b/tooling/asciidoc-model/src/main/java/org/apache/isis/tooling/adocmodel/ast/SimpleCell.java
@@ -0,0 +1,64 @@
+/*
+ *  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.isis.tooling.adocmodel.ast;
+
+import org.asciidoctor.ast.Cell;
+import org.asciidoctor.ast.Column;
+import org.asciidoctor.ast.Document;
+import org.asciidoctor.ast.Table;
+
+import lombok.Getter;
+import lombok.Setter;
+
+public class SimpleCell extends ContentNodeAbstract implements Cell {
+
+    @Getter @Setter private String style;
+    @Getter @Setter private String source;
+    @Getter @Setter private String text;
+    @Getter @Setter private Object content;
+    @Getter @Setter private int rowspan;
+    @Getter @Setter private int colspan;
+    @Getter @Setter private Document innerDocument;
+    
+    @Override
+    public Column getColumn() {
+        return (Column) getParent();
+    }
+
+    @Override
+    public Table.HorizontalAlignment getHorizontalAlignment() {
+        return Table.HorizontalAlignment.valueOf(((String) 
getAttribute("halign", "left")).toUpperCase());
+    }
+
+    @Override
+    public void setHorizontalAlignment(Table.HorizontalAlignment halign) {
+        setAttribute("halign", halign.name().toLowerCase(), true);
+    }
+
+    @Override
+    public Table.VerticalAlignment getVerticalAlignment() {
+        return Table.VerticalAlignment.valueOf(((String) 
getAttribute("valign", "top")).toUpperCase());
+    }
+
+    @Override
+    public void setVerticalAlignment(Table.VerticalAlignment valign) {
+        setAttribute("valign", valign.name().toLowerCase(), true);
+    }
+    
+}
diff --git 
a/tooling/asciidoc-model/src/main/java/org/apache/isis/tooling/adocmodel/ast/SimpleColumn.java
 
b/tooling/asciidoc-model/src/main/java/org/apache/isis/tooling/adocmodel/ast/SimpleColumn.java
new file mode 100644
index 0000000..b424c24
--- /dev/null
+++ 
b/tooling/asciidoc-model/src/main/java/org/apache/isis/tooling/adocmodel/ast/SimpleColumn.java
@@ -0,0 +1,73 @@
+/*
+ *  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.isis.tooling.adocmodel.ast;
+
+import org.asciidoctor.ast.Column;
+import org.asciidoctor.ast.Table;
+
+import lombok.Getter;
+import lombok.Setter;
+
+public class SimpleColumn extends ContentNodeAbstract implements Column {
+
+    @Getter @Setter private String style;
+    
+    @Override
+    public Table getTable() {
+        return (Table) getParent();
+    }
+
+    @Override
+    public int getColumnNumber() {
+        Number columnNumber = (Number) getAttribute("colnumber");
+        return columnNumber == null ? -1 : columnNumber.intValue();
+    }
+
+    @Override
+    public int getWidth() {
+        Number width = (Number) getAttribute("width");
+        return width == null ? 0 : width.intValue();
+    }
+
+    @Override
+    public void setWidth(int width) {
+        setAttribute("width", width, true);
+    }
+
+    @Override
+    public Table.HorizontalAlignment getHorizontalAlignment() {
+        return Table.HorizontalAlignment.valueOf(((String) 
getAttribute("halign", "left")).toUpperCase());
+    }
+
+    @Override
+    public void setHorizontalAlignment(Table.HorizontalAlignment halign) {
+        setAttribute("halign", halign.name().toLowerCase(), true);
+    }
+
+    @Override
+    public Table.VerticalAlignment getVerticalAlignment() {
+        return Table.VerticalAlignment.valueOf(((String) 
getAttribute("valign", "top")).toUpperCase());
+    }
+
+    @Override
+    public void setVerticalAlignment(Table.VerticalAlignment valign) {
+        setAttribute("valign", valign.name().toLowerCase(), true);
+    }
+
+}
diff --git 
a/tooling/asciidoc-model/src/main/java/org/apache/isis/tooling/adocmodel/ast/SimpleDocument.java
 
b/tooling/asciidoc-model/src/main/java/org/apache/isis/tooling/adocmodel/ast/SimpleDocument.java
new file mode 100644
index 0000000..72f18f9
--- /dev/null
+++ 
b/tooling/asciidoc-model/src/main/java/org/apache/isis/tooling/adocmodel/ast/SimpleDocument.java
@@ -0,0 +1,63 @@
+/*
+ *  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.isis.tooling.adocmodel.ast;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.asciidoctor.ast.Document;
+import org.asciidoctor.ast.Title;
+
+import lombok.Getter;
+import lombok.Setter;
+
+public class SimpleDocument extends SimpleStructuralNode implements Document {
+
+    @Getter @Setter private Title structuredDoctitle;
+    @Getter @Setter private String doctitle;
+    @Getter private final Map<Object, Object> options = new HashMap<>();
+    @Getter @Setter private boolean sourcemap;
+    
+    @Override
+    public String doctitle() {
+        return getDoctitle();
+    }
+
+    @Override
+    public boolean isBasebackend(String backend) {
+        return false;
+    }
+
+    @Override
+    public boolean basebackend(String backend) {
+        return false;
+    }
+
+    @Override
+    public int getAndIncrementCounter(String name) {
+        return 0;
+    }
+
+    @Override
+    public int getAndIncrementCounter(String name, int initialValue) {
+        return 0;
+    }
+
+
+}
diff --git 
a/tooling/asciidoc-model/src/main/java/org/apache/isis/tooling/adocmodel/ast/SimpleRow.java
 
b/tooling/asciidoc-model/src/main/java/org/apache/isis/tooling/adocmodel/ast/SimpleRow.java
new file mode 100644
index 0000000..6bda645
--- /dev/null
+++ 
b/tooling/asciidoc-model/src/main/java/org/apache/isis/tooling/adocmodel/ast/SimpleRow.java
@@ -0,0 +1,33 @@
+/*
+ *  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.isis.tooling.adocmodel.ast;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.asciidoctor.ast.Cell;
+import org.asciidoctor.ast.Row;
+
+import lombok.Getter;
+
+public class SimpleRow implements Row {
+
+    @Getter private final List<Cell> cells = new ArrayList<>();
+
+}
diff --git 
a/tooling/asciidoc-model/src/main/java/org/apache/isis/tooling/adocmodel/ast/SimpleStructuralNode.java
 
b/tooling/asciidoc-model/src/main/java/org/apache/isis/tooling/adocmodel/ast/SimpleStructuralNode.java
new file mode 100644
index 0000000..52d27e7
--- /dev/null
+++ 
b/tooling/asciidoc-model/src/main/java/org/apache/isis/tooling/adocmodel/ast/SimpleStructuralNode.java
@@ -0,0 +1,121 @@
+/*
+ *  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.isis.tooling.adocmodel.ast;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import org.asciidoctor.ast.Cursor;
+import org.asciidoctor.ast.StructuralNode;
+
+import lombok.Getter;
+import lombok.Setter;
+
+public class SimpleStructuralNode extends ContentNodeAbstract implements 
StructuralNode {
+
+    @Getter @Setter private String title;
+    @Getter @Setter private String caption;
+    @Getter @Setter private String style;
+    @Getter private final List<StructuralNode> blocks = new ArrayList<>();
+    @Getter @Setter private Object content;
+    @Getter @Setter private int level;
+    @Getter @Setter private String convert;
+    
+    @Override
+    @Deprecated
+    public String title() {
+        return getTitle();
+    }
+
+    @Override
+    @Deprecated
+    public String style() {
+        return getStyle();
+    }
+
+    @Override
+    @Deprecated
+    public List<StructuralNode> blocks() {
+        return getBlocks();
+    }
+
+    @Override
+    public void append(StructuralNode block) {
+        getBlocks().add(block);
+    }
+
+    @Override
+    @Deprecated
+    public Object content() {
+        return getContent();
+    }
+
+    @Override
+    public String convert() {
+        return getConvert();
+    }
+
+    @Override
+    public Cursor getSourceLocation() {
+        return null;
+    }
+
+    @Override
+    public String getContentModel() {
+        return null;
+    }
+
+    @Override
+    public List<String> getSubstitutions() {
+        return Collections.emptyList();
+    }
+
+    @Override
+    public boolean isSubstitutionEnabled(String substitution) {
+        return false;
+    }
+
+    @Override
+    public void removeSubstitution(String substitution) {
+        
+    }
+
+    @Override
+    public void addSubstitution(String substitution) {
+        
+    }
+
+    @Override
+    public void prependSubstitution(String substitution) {
+        
+    }
+
+    @Override
+    public void setSubstitutions(String... substitutions) {
+    }
+
+    @Override
+    public List<StructuralNode> findBy(Map<Object, Object> selector) {
+        return Collections.emptyList();
+    }
+
+    
+}
diff --git 
a/tooling/asciidoc-model/src/main/java/org/apache/isis/tooling/adocmodel/ast/SimpleTable.java
 
b/tooling/asciidoc-model/src/main/java/org/apache/isis/tooling/adocmodel/ast/SimpleTable.java
new file mode 100644
index 0000000..3044b2e
--- /dev/null
+++ 
b/tooling/asciidoc-model/src/main/java/org/apache/isis/tooling/adocmodel/ast/SimpleTable.java
@@ -0,0 +1,46 @@
+/*
+ *  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.isis.tooling.adocmodel.ast;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.asciidoctor.ast.Column;
+import org.asciidoctor.ast.Row;
+import org.asciidoctor.ast.Table;
+
+import lombok.Getter;
+import lombok.Setter;
+
+public class SimpleTable extends SimpleStructuralNode implements Table {
+
+    @Getter @Setter private String frame;
+    @Getter @Setter private String grid;
+    @Getter private final List<Column> columns = new ArrayList<>();
+    @Getter private final List<Row> header = new ArrayList<>();
+    @Getter private final List<Row> body = new ArrayList<>();
+    @Getter private final List<Row> footer = new ArrayList<>();
+    
+    
+    @Override
+    public boolean hasHeaderOption() {
+        return false;
+    }
+
+}
diff --git 
a/tooling/asciidoc-model/src/test/java/org/apache/isis/tooling/adocmodel/test/AsciiDocWriterTest.java
 
b/tooling/asciidoc-model/src/test/java/org/apache/isis/tooling/adocmodel/test/AsciiDocWriterTest.java
new file mode 100644
index 0000000..8120b13
--- /dev/null
+++ 
b/tooling/asciidoc-model/src/test/java/org/apache/isis/tooling/adocmodel/test/AsciiDocWriterTest.java
@@ -0,0 +1,179 @@
+/*
+ *  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.isis.tooling.adocmodel.test;
+
+import java.io.IOException;
+import java.util.HashMap;
+
+import org.asciidoctor.Asciidoctor;
+import org.asciidoctor.ast.Document;
+import org.asciidoctor.ast.Table;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.apache.isis.tooling._infra._Strings;
+import org.apache.isis.tooling.adocmodel.AsciiDocWriter;
+
+import static org.apache.isis.tooling.adocmodel.AsciiDocFactory.*;
+
+import lombok.val;
+
+class AsciiDocWriterTest {
+
+    private Document doc;
+
+    @BeforeEach
+    void setUp() throws Exception {
+        doc = doc();
+    }
+
+    @AfterEach
+    void tearDown() throws Exception {
+    }
+
+    @Test
+    void testDocTitle() throws IOException {
+        
+        doc.setTitle("Hello World");
+        
+        String actualAdoc = AsciiDocWriter.toString(doc); 
+        String expectedAdoc = "= Hello World\n";
+        
+        System.out.println(actualAdoc);
+        
+        assertEquals(expectedAdoc, actualAdoc);
+    }
+    
+    @Test
+    void testSimpleTable() throws IOException {
+        
+        val adocRef = _Strings.readResource(this, "simple-table.adoc");
+        
+        val table = table(doc);
+        table.setTitle("Table");
+        
+        headCell(table, 0, 0, "Col-1");
+        headCell(table, 0, 1, "Col-2");
+        headCell(table, 0, 2, "Col-3");
+        
+        cell(table, 0, 0, "1-1");
+        cell(table, 0, 1, "1-2");
+        cell(table, 0, 2, "1-3");
+        
+        cell(table, 1, 0, "2-1");
+        cell(table, 1, 1, "2-2");
+        cell(table, 1, 2, "2-3");
+        
+        String actualAdoc = AsciiDocWriter.toString(doc); 
+        
+        System.out.println(actualAdoc);
+        
+        assertEquals(adocRef, actualAdoc);
+        
+    }
+    
+    @Test
+    void testAttributedTable() throws IOException {
+        
+        val adocRef = _Strings.readResource(this, "attributed-table.adoc");
+        
+        val table = table(doc);
+        table.setTitle("Some table");
+        table.setAttribute("cols", "3m,2a", true);
+        table.setAttribute("header-option", "", true);
+        
+        headCell(table, 0, 0, "Col-1");
+        headCell(table, 0, 1, "Col-2");
+        
+        cell(table, 0, 0, "1-1");
+        cell(table, 0, 1, "1-2");
+        
+        String actualAdoc = AsciiDocWriter.toString(doc); 
+        
+        System.out.println(actualAdoc);
+        
+        assertEquals(adocRef, actualAdoc);
+        
+    }
+    
+    @Test @Disabled
+    void testSimpleTableModel() throws IOException {
+    
+        val adocRef = _Strings.readResource(this, "simple-table.adoc");
+        val asciidoctor = Asciidoctor.Factory.create();
+        val refDoc = asciidoctor.load(adocRef, new HashMap<String, Object>());
+        
+        String actualAdoc = AsciiDocWriter.toString(refDoc);
+        
+        debug(refDoc);
+        
+        System.out.println(actualAdoc);
+        
+        assertEquals(adocRef, actualAdoc);
+    }
+    
+    
+    @Test
+    void testAttributedTableModel() throws IOException {
+    
+        val adocRef = _Strings.readResource(this, "attributed-table.adoc");
+        val asciidoctor = Asciidoctor.Factory.create();
+        val refDoc = asciidoctor.load(adocRef, new HashMap<String, Object>());
+        
+        String actualAdoc = AsciiDocWriter.toString(refDoc);
+        
+        debug(refDoc);
+        
+        System.out.println(actualAdoc);
+        
+        assertEquals(adocRef, actualAdoc);
+    }
+    
+    private static void debug(Document doc) {
+        val refTable = (Table)doc.getBlocks().get(0);
+        val refCol = refTable.getColumns().get(0);
+        val refRow = refTable.getBody().get(0);
+        val refCell = refRow.getCells().get(0);
+        
+        val refHead = refTable.getHeader().get(0);
+        
+        
+        System.out.println("tab attrib: " + refTable.getAttributes());
+        System.out.println("tab caption: " + refTable.getCaption());
+        System.out.println("tab title: " + refTable.getTitle());
+        
+        System.out.println("col attrib: " + refCol.getAttributes());
+        System.out.println("col context: " + refCol.getContext());
+        System.out.println("col id: " + refCol.getId());
+        System.out.println("col reftex: " + refCol.getReftext());
+        System.out.println("col nodeName: " + refCol.getNodeName());
+        System.out.println("col role: " + refCol.getRole());
+        
+        
+        System.out.println("cell source: " + refCell.getSource());
+        
+        System.out.println("head source: " + 
refHead.getCells().get(0).getSource());
+    }
+    
+
+}
diff --git 
a/tooling/asciidoc-model/src/test/java/org/apache/isis/tooling/adocmodel/test/attributed-table.adoc
 
b/tooling/asciidoc-model/src/test/java/org/apache/isis/tooling/adocmodel/test/attributed-table.adoc
new file mode 100644
index 0000000..2193b79
--- /dev/null
+++ 
b/tooling/asciidoc-model/src/test/java/org/apache/isis/tooling/adocmodel/test/attributed-table.adoc
@@ -0,0 +1,8 @@
+.Some table
+[cols="3m,2a", options="header"]
+|===
+|Col-1 |Col-2 
+
+|1-1
+|1-2
+|===
diff --git 
a/tooling/asciidoc-model/src/test/java/org/apache/isis/tooling/adocmodel/test/simple-table.adoc
 
b/tooling/asciidoc-model/src/test/java/org/apache/isis/tooling/adocmodel/test/simple-table.adoc
new file mode 100644
index 0000000..72a5b95
--- /dev/null
+++ 
b/tooling/asciidoc-model/src/test/java/org/apache/isis/tooling/adocmodel/test/simple-table.adoc
@@ -0,0 +1,12 @@
+.Table
+|===
+|Col-1 |Col-2 |Col-3 
+
+|1-1
+|1-2
+|1-3
+
+|2-1
+|2-2
+|2-3
+|===
diff --git a/tooling/commons/pom.xml b/tooling/commons/pom.xml
new file mode 100644
index 0000000..ba4434c
--- /dev/null
+++ b/tooling/commons/pom.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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. -->
+<project xmlns="http://maven.apache.org/POM/4.0.0";
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+       xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/maven-v4_0_0.xsd";>
+       <modelVersion>4.0.0</modelVersion>
+
+       <parent>
+               <groupId>org.apache.isis.tooling</groupId>
+               <artifactId>isis-tooling</artifactId>
+               <version>2.0.0-SNAPSHOT</version>
+       </parent>
+
+       <artifactId>isis-tooling-commons</artifactId>
+
+       <name>Apache Isis Tooling - Commons</name>
+       <description>
+        Shared tooling classes.
+    </description>
+
+       <properties>
+       </properties>
+
+       <dependencies>
+       </dependencies>
+
+</project>
+
+
diff --git 
a/tooling/commons/src/main/java/org/apache/isis/tooling/_infra/_Files.java 
b/tooling/commons/src/main/java/org/apache/isis/tooling/_infra/_Files.java
new file mode 100644
index 0000000..c41a80d
--- /dev/null
+++ b/tooling/commons/src/main/java/org/apache/isis/tooling/_infra/_Files.java
@@ -0,0 +1,51 @@
+package org.apache.isis.tooling._infra;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.DirectoryStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+
+import lombok.val;
+
+public class _Files {
+
+    public static Set<File> searchFiles(
+            final File dir, 
+            final Predicate<File> dirFilter, 
+            final Predicate<File> fileFilter) throws IOException {
+        
+        final Set<File> fileList = new HashSet<>();
+        searchFiles(dir, dirFilter, fileFilter, fileList::add);
+        return fileList;
+    }
+    
+    /** recursive file search */
+    public static void searchFiles(
+            final File dir, 
+            final Predicate<File> dirFilter, 
+            final Predicate<File> fileFilter,
+            final Consumer<File> onFileFound) throws IOException {
+        
+        try (DirectoryStream<Path> stream = 
Files.newDirectoryStream(dir.toPath())) {
+            for (Path path : stream) {
+                val file = path.toFile();
+                if (Files.isDirectory(path)) {
+                    if(dirFilter.test(file)) {
+                        // go deeper
+                        searchFiles(file, dirFilter, fileFilter, onFileFound);
+                    }
+                } else {
+                    if(fileFilter.test(file)) {
+                        onFileFound.accept(file);
+                    }
+                }
+            }
+        }
+    }
+    
+}
diff --git 
a/tooling/commons/src/main/java/org/apache/isis/tooling/_infra/_Strings.java 
b/tooling/commons/src/main/java/org/apache/isis/tooling/_infra/_Strings.java
new file mode 100644
index 0000000..7989b74
--- /dev/null
+++ b/tooling/commons/src/main/java/org/apache/isis/tooling/_infra/_Strings.java
@@ -0,0 +1,39 @@
+package org.apache.isis.tooling._infra;
+
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.Scanner;
+
+import javax.annotation.Nullable;
+
+public final class _Strings {
+
+    public static String read(final InputStream input) {
+        if(input==null) {
+            return "";
+        }
+        // see 
https://stackoverflow.com/questions/309424/how-to-read-convert-an-inputstream-into-a-string-in-java
+        try(Scanner scanner = new Scanner(input, 
StandardCharsets.UTF_8.name())){
+            scanner.useDelimiter("\\A");
+            return scanner.hasNext() ? scanner.next().replace("\r", "") : "";
+        }
+    }
+    
+    public static String readResource(Class<?> location, String name) {
+        return read(location.getResourceAsStream(name));  
+    }
+    
+    public static String readResource(Object location, String name) {
+        return readResource(location.getClass(), name);  
+    }
+    
+    /**
+     * Same as {@link #isEmpty(CharSequence)}
+     * @param x
+     * @return true only if string is of zero length or null.
+     */
+    public static boolean isNullOrEmpty(@Nullable final CharSequence x){
+        return x==null || x.length()==0;
+    }
+    
+}
diff --git a/tooling/pom.xml b/tooling/pom.xml
new file mode 100644
index 0000000..35de159
--- /dev/null
+++ b/tooling/pom.xml
@@ -0,0 +1,218 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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. -->
+<project xmlns="http://maven.apache.org/POM/4.0.0";
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+       xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/maven-v4_0_0.xsd";>
+       <modelVersion>4.0.0</modelVersion>
+
+       <parent>
+               <groupId>org.springframework.boot</groupId>
+               <artifactId>spring-boot-starter-parent</artifactId>
+               <version>2.3.2.RELEASE</version>
+               <relativePath /> <!-- lookup parent from repository -->
+       </parent>
+
+       <groupId>org.apache.isis.tooling</groupId>
+       <artifactId>isis-tooling</artifactId>
+       <version>2.0.0-SNAPSHOT</version>
+
+       <name>Apache Isis Tooling</name>
+       <description>
+        Libraries and tools not depending on the _Apache Isis Core_ ecosystem, 
+        eg. code mining, automated documentation.
+    </description>
+
+       <packaging>pom</packaging>
+
+       <properties>
+
+               
<jar-plugin.automaticModuleName>org.apache.isis.tooling</jar-plugin.automaticModuleName>
+               
<git-plugin.propertiesDir>org/apache/isis/tooling</git-plugin.propertiesDir>
+
+               <java.version>11</java.version>
+
+               <asciidoctorj.version>2.1.0</asciidoctorj.version>
+               <gradle-tooling.version>6.5.1</gradle-tooling.version>
+               <lombok.version>1.18.12</lombok.version>
+               <maven-model-builder.version>3.6.3</maven-model-builder.version>
+               <structurizr.version>1.5.0</structurizr.version>
+
+       </properties>
+
+       <repositories>
+               <repository>
+                       <id>gradle-repo</id>
+                       
<url>https://repo.gradle.org/gradle/libs-releases-local/</url>
+                       <releases>
+                               <enabled>true</enabled>
+                       </releases>
+                       <snapshots>
+                               <enabled>false</enabled>
+                       </snapshots>
+               </repository>
+       </repositories>
+
+       <build>
+               <resources>
+                       <resource>
+                               <filtering>true</filtering>
+                               <directory>src/main/resources</directory>
+                               <includes>
+                                       <include>**</include>
+                               </includes>
+                       </resource>
+                       <resource>
+                               <filtering>false</filtering>
+                               <directory>src/main/java</directory>
+                               <includes>
+                                       <include>**</include>
+                               </includes>
+                               <excludes>
+                                       <exclude>**/*.java</exclude>
+                               </excludes>
+                       </resource>
+               </resources>
+               <plugins>
+                       <plugin>
+                               <artifactId>maven-compiler-plugin</artifactId>
+                               <configuration>
+                                       <release>11</release>
+                               </configuration>
+                       </plugin>
+               </plugins>
+       </build>
+
+       <dependencyManagement>
+               <dependencies>
+               
+                       <!-- INTERNAL -->
+               
+                       <dependency>
+                               <groupId>org.apache.isis.tooling</groupId>
+                               <artifactId>isis-tooling-commons</artifactId>
+                               <version>${project.version}</version>
+                       </dependency>
+               
+                       <!-- EXTERNAL -->
+
+                       <dependency>
+                               <groupId>org.asciidoctor</groupId>
+                               <artifactId>asciidoctorj</artifactId>
+                               <version>${asciidoctorj.version}</version>
+                       </dependency>
+
+                       <dependency>
+                               <groupId>org.gradle</groupId>
+                               <artifactId>gradle-tooling-api</artifactId>
+                               <version>${gradle-tooling.version}</version>
+                       </dependency>
+
+                       <dependency>
+                               <groupId>org.apache.maven</groupId>
+                               <artifactId>maven-model-builder</artifactId>
+                               
<version>${maven-model-builder.version}</version>
+                       </dependency>
+
+                       <dependency>
+                               <groupId>org.projectlombok</groupId>
+                               <artifactId>lombok</artifactId>
+                               <version>${lombok.version}</version>
+                       </dependency>
+
+                       <dependency>
+                               <groupId>com.structurizr</groupId>
+                               <artifactId>structurizr-core</artifactId>
+                               <version>${structurizr.version}</version>
+                       </dependency>
+
+               </dependencies>
+       </dependencyManagement>
+
+       <dependencies>
+       
+               <!-- LOMBOK -->
+       
+               <dependency>
+                       <groupId>org.projectlombok</groupId>
+                       <artifactId>lombok</artifactId>
+                       <scope>provided</scope>
+               </dependency>
+               
+               <!-- fixes Module lombok does not read a module that exports 
org.mapstruct.ap.spi -->
+               <dependency>
+                       <groupId>org.mapstruct</groupId>
+                       <artifactId>mapstruct-processor</artifactId>
+                       <version>1.3.1.Final</version>
+               </dependency>
+               
+               <!-- @Nullable -->
+               
+               <dependency>
+                       <groupId>com.google.code.findbugs</groupId>
+                       <artifactId>annotations</artifactId>
+                       <version>2.0.1</version>
+               </dependency>
+               
+               <!-- LOGGING -->
+               
+               <!-- Add Log4j2 Dependency, as a replacement for Spring's 
default 'logback'. 
+                       This requires for the 'spring-boot-starter' to exclude 
the default logging 
+                       artifact 
'org.springframework.boot:spring-boot-starter-logging' see 
https://www.callicoder.com/spring-boot-log4j-2-example/ -->
+               <dependency>
+                       <groupId>org.springframework.boot</groupId>
+                       <artifactId>spring-boot-starter-log4j2</artifactId>
+                       <exclusions>
+                               <exclusion>
+                                       <!-- convergence issues from 
spring-boot-starter-log4j2 -->
+                                       <groupId>org.slf4j</groupId>
+                                       <artifactId>slf4j-api</artifactId>
+                               </exclusion>
+                       </exclusions>
+               </dependency>
+               <dependency>
+                       <groupId>org.slf4j</groupId>
+                       <artifactId>slf4j-api</artifactId>
+               </dependency>
+               
+               <!-- TESTING -->
+               
+               <dependency>
+                       <groupId>org.junit.jupiter</groupId>
+                       <artifactId>junit-jupiter-api</artifactId>
+                       <scope>test</scope>
+               </dependency>
+
+               <dependency>
+                       <groupId>org.junit.jupiter</groupId>
+                       <artifactId>junit-jupiter-engine</artifactId>
+                       <scope>test</scope>
+               </dependency>
+
+               <dependency>
+                       <groupId>org.junit.vintage</groupId>
+                       <artifactId>junit-vintage-engine</artifactId>
+                       <scope>test</scope>
+               </dependency>
+               
+       </dependencies>
+
+       <modules>
+               <module>commons</module>
+               <module>asciidoc-model</module>
+               <module>project-model</module>
+       </modules>
+
+       <profiles>
+       </profiles>
+</project>
+
+
diff --git a/tooling/project-model/pom.xml b/tooling/project-model/pom.xml
new file mode 100644
index 0000000..f08923e
--- /dev/null
+++ b/tooling/project-model/pom.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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. -->
+<project xmlns="http://maven.apache.org/POM/4.0.0";
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+       xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/maven-v4_0_0.xsd";>
+       <modelVersion>4.0.0</modelVersion>
+
+       <parent>
+               <groupId>org.apache.isis.tooling</groupId>
+               <artifactId>isis-tooling</artifactId>
+               <version>2.0.0-SNAPSHOT</version>
+       </parent>
+       
+       <artifactId>isis-tooling-project-model</artifactId>
+
+       <name>Apache Isis Tooling - Project Model</name>
+       <description>
+        Code mining library for Gradle/Maven project module tree introspection.
+    </description>
+
+       <properties>
+       </properties>
+
+       <dependencies>
+       </dependencies>
+
+</project>
+
+

Reply via email to