You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by vy...@apache.org on 2024/02/21 14:21:42 UTC

(logging-log4j-tools) 22/29: Enhance `DocumentationGenerator` to provide source artifact information

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

vy pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/logging-log4j-tools.git

commit 23e055c0a08f557a7f54f6170dff48829534b3a2
Author: Volkan Yazıcı <vo...@yazi.ci>
AuthorDate: Mon Feb 19 10:26:27 2024 +0100

    Enhance `DocumentationGenerator` to provide source artifact information
---
 .../docgen/maven/DocumentationTemplateMojo.java    |  24 ++++-
 .../docgen/generator/ArtifactSourcedType.java      |  69 ++++++++++++
 .../docgen/generator/DocumentationGenerator.java   |  45 +++++---
 .../log4j/docgen/generator/SchemaGenerator.java    |   9 +-
 .../logging/log4j/docgen/generator/TypeLookup.java | 115 +++++++++++++++-----
 .../log4j/docgen/generator/base-java-types.xml     |   2 +-
 .../generator/DocumentationGeneratorTest.java      |   4 +-
 .../org.apache.logging.log4j.core.Appender.adoc    |   5 +
 .../org.apache.logging.log4j.core.Filter.adoc      |   5 +
 .../org.apache.logging.log4j.core.Layout.adoc      |   5 +
 ...ogging.log4j.core.appender.ConsoleAppender.adoc |  25 +++--
 ...ache.logging.log4j.core.config.AppenderRef.adoc |  15 ++-
 ....logging.log4j.core.config.AppendersPlugin.adoc |   8 +-
 ...he.logging.log4j.core.config.Configuration.adoc |  25 +++--
 ...ogging.log4j.core.config.CustomLevelConfig.adoc |  10 +-
 ...che.logging.log4j.core.config.CustomLevels.adoc |   8 +-
 ....log4j.core.config.LoggerConfig.RootLogger.adoc |  15 ++-
 ...che.logging.log4j.core.config.LoggerConfig.adoc |  18 +++-
 ...he.logging.log4j.core.config.LoggersPlugin.adoc |   8 +-
 ...logging.log4j.core.config.PropertiesPlugin.adoc |   8 +-
 ....apache.logging.log4j.core.config.Property.adoc |  10 +-
 ...ache.logging.log4j.core.filter.BurstFilter.adoc |  16 ++-
 ...he.logging.log4j.core.layout.PatternLayout.adoc |   8 +-
 .../plugins-to-freemarker-output/scalars.adoc      | 116 +++++++++++++--------
 .../test/resources/templates/interface.adoc.ftl    |  16 ++-
 .../src/test/resources/templates/plugin.adoc.ftl   |  35 ++++---
 .../src/test/resources/templates/scalars.adoc.ftl  |  23 ++--
 27 files changed, 484 insertions(+), 163 deletions(-)

diff --git a/log4j-docgen-maven-plugin/src/main/java/org/apache/logging/log4j/docgen/maven/DocumentationTemplateMojo.java b/log4j-docgen-maven-plugin/src/main/java/org/apache/logging/log4j/docgen/maven/DocumentationTemplateMojo.java
index f93be5c..1459d8b 100644
--- a/log4j-docgen-maven-plugin/src/main/java/org/apache/logging/log4j/docgen/maven/DocumentationTemplateMojo.java
+++ b/log4j-docgen-maven-plugin/src/main/java/org/apache/logging/log4j/docgen/maven/DocumentationTemplateMojo.java
@@ -21,13 +21,33 @@ import org.apache.maven.plugins.annotations.Parameter;
 public final class DocumentationTemplateMojo {
 
     /**
-     * The name of the template (e.g., {@code plugin.adoc.ftl}, {@code scalars.adoc.ftl})
+     * The name of the FreeMarker template.
+     * <p>
+     * This corresponds to the file path (relative to {@link DocumentationGeneratorMojo#templateDirectory the template directory}) of the FreeMarker template.
+     * For instance, {@code scalars.adoc.ftl}, {@code plugin.adoc.ftl}, and {@code interface.adoc.ftl} for scalar values, plugins, and interfaces, respectively.
+     * </p>
      */
     @Parameter(required = true)
     String name;
 
     /**
-     * The target file path rendered content will be written to (e.g., {@code %g/%a/%c.adoc})
+     * The target file path rendered content will be written to.
+     * <p>
+     * Certain directives are subject to substitution:
+     * </p>
+     * <ul>
+     * <li>%a – artifact ID, if the type is provided with an artifact origin; {@code unknown-artifactId}, otherwise</li>
+     * <li>%c – class name</li>
+     * <li>%g – group ID, if the type is provided with an artifact origin; {@code unknown-groupId}, otherwise</li>
+     * <li>%v – version, if the type is provided with an artifact origin; {@code unknown-version}, otherwise</li>
+     * </ul>
+     * <p>
+     * For instance,
+     * </p>
+     * <ul>
+     * <li>{@code scalars.adoc} for scalar values</li>
+     * <li>{@code %g/%a/%c.adoc} for interfaces and plugins</li>
+     * </ul>
      */
     @Parameter(required = true)
     String targetPath;
diff --git a/log4j-docgen/src/main/java/org/apache/logging/log4j/docgen/generator/ArtifactSourcedType.java b/log4j-docgen/src/main/java/org/apache/logging/log4j/docgen/generator/ArtifactSourcedType.java
new file mode 100644
index 0000000..e35f233
--- /dev/null
+++ b/log4j-docgen/src/main/java/org/apache/logging/log4j/docgen/generator/ArtifactSourcedType.java
@@ -0,0 +1,69 @@
+/*
+ * 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.logging.log4j.docgen.generator;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.Comparator;
+import org.apache.logging.log4j.docgen.PluginSet;
+import org.apache.logging.log4j.docgen.Type;
+import org.jspecify.annotations.Nullable;
+
+public final class ArtifactSourcedType implements Comparable<ArtifactSourcedType> {
+
+    private static final Comparator<ArtifactSourcedType> COMPARATOR = Comparator.<ArtifactSourcedType, String>comparing(
+                    sourcedType -> sourcedType.type.getClassName())
+            .thenComparing(sourcedType -> na(sourcedType.groupId))
+            .thenComparing(sourcedType -> na(sourcedType.artifactId))
+            .thenComparing(sourcedType -> na(sourcedType.version));
+
+    private static String na(@Nullable final String value) {
+        return value != null ? value : "N/A";
+    }
+
+    @Nullable
+    public final String groupId;
+
+    @Nullable
+    public final String artifactId;
+
+    @Nullable
+    public final String version;
+
+    public final Type type;
+
+    private ArtifactSourcedType(
+            @Nullable final String groupId,
+            @Nullable final String artifactId,
+            @Nullable final String version,
+            final Type type) {
+        this.groupId = groupId;
+        this.artifactId = artifactId;
+        this.version = version;
+        this.type = requireNonNull(type, "type");
+    }
+
+    static ArtifactSourcedType ofPluginSet(final PluginSet pluginSet, final Type type) {
+        return new ArtifactSourcedType(pluginSet.getGroupId(), pluginSet.getArtifactId(), pluginSet.getVersion(), type);
+    }
+
+    @Override
+    @SuppressWarnings("NullableProblems")
+    public int compareTo(final ArtifactSourcedType sourcedType) {
+        return COMPARATOR.compare(this, sourcedType);
+    }
+}
diff --git a/log4j-docgen/src/main/java/org/apache/logging/log4j/docgen/generator/DocumentationGenerator.java b/log4j-docgen/src/main/java/org/apache/logging/log4j/docgen/generator/DocumentationGenerator.java
index 38a1237..c2a6871 100644
--- a/log4j-docgen/src/main/java/org/apache/logging/log4j/docgen/generator/DocumentationGenerator.java
+++ b/log4j-docgen/src/main/java/org/apache/logging/log4j/docgen/generator/DocumentationGenerator.java
@@ -21,7 +21,6 @@ import static org.apache.logging.log4j.tools.internal.freemarker.util.FreeMarker
 
 import java.nio.file.Path;
 import java.util.Collection;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.TreeSet;
@@ -31,6 +30,8 @@ import org.apache.logging.log4j.docgen.AbstractType;
 import org.apache.logging.log4j.docgen.PluginSet;
 import org.apache.logging.log4j.docgen.PluginType;
 import org.apache.logging.log4j.docgen.ScalarType;
+import org.apache.logging.log4j.docgen.Type;
+import org.jspecify.annotations.Nullable;
 
 public final class DocumentationGenerator {
 
@@ -41,14 +42,15 @@ public final class DocumentationGenerator {
         final List<PluginSet> extendedSets = Stream.concat(BaseTypes.PLUGIN_SETS.stream(), args.pluginSets.stream())
                 .collect(Collectors.toList());
         final TypeLookup lookup = TypeLookup.of(extendedSets);
-        final Collection<ScalarType> scalarTypes = new TreeSet<>();
-        lookup.forEach((typeKey, type) -> {
+        final Collection<ArtifactSourcedType> scalarTypes = new TreeSet<>();
+        lookup.forEach((className, sourcedType) -> {
+            final Type type = sourcedType.type;
             if (type instanceof PluginType) {
-                documentAbstractType((AbstractType) type, lookup, args.templateDirectory, args.pluginTemplate);
+                documentAbstractType(sourcedType, lookup, args.templateDirectory, args.pluginTemplate);
             } else if (type instanceof AbstractType) {
-                documentAbstractType((AbstractType) type, lookup, args.templateDirectory, args.interfaceTemplate);
+                documentAbstractType(sourcedType, lookup, args.templateDirectory, args.interfaceTemplate);
             } else if (type instanceof ScalarType) {
-                scalarTypes.add((ScalarType) type);
+                scalarTypes.add(sourcedType);
             } else {
                 throw new RuntimeException("unknown type: `" + type + "`");
             }
@@ -57,28 +59,39 @@ public final class DocumentationGenerator {
     }
 
     private static void documentAbstractType(
-            final AbstractType abstractType,
+            final ArtifactSourcedType sourcedType,
             final TypeLookup lookup,
             final Path templateDirectory,
             final DocumentationTemplate template) {
-        final Map<String, Object> templateData = new HashMap<>();
-        templateData.put("type", abstractType);
-        templateData.put("lookup", lookup);
-        final Path targetPath = createAbstractTypeTargetPath(abstractType, template.targetPath);
+        final Map<String, Object> templateData = Map.of(
+                "sourcedType", sourcedType,
+                "lookup", lookup);
+        final Path targetPath = createAbstractTypeTargetPath(sourcedType, template.targetPath);
         render(templateDirectory, template.name, templateData, targetPath);
     }
 
-    private static Path createAbstractTypeTargetPath(final AbstractType abstractType, final String targetPathPattern) {
-        final String targetPath = targetPathPattern.replaceAll("%c", abstractType.getClassName());
+    private static Path createAbstractTypeTargetPath(
+            final ArtifactSourcedType sourcedType, final String targetPathPattern) {
+        final String groupId = or(sourcedType.groupId, "unknown-groupId");
+        final String artifactId = or(sourcedType.artifactId, "unknown-artifactId");
+        final String version = or(sourcedType.version, "unknown-version");
+        final String targetPath = targetPathPattern
+                .replaceAll("%g", groupId)
+                .replaceAll("%a", artifactId)
+                .replaceAll("%v", version)
+                .replaceAll("%c", sourcedType.type.getClassName());
         return Path.of(targetPath);
     }
 
+    private static String or(@Nullable final String value, final String fallback) {
+        return value != null ? value : fallback;
+    }
+
     private static void documentScalarTypes(
-            final Collection<ScalarType> scalarTypes,
+            final Collection<ArtifactSourcedType> scalarTypes,
             final Path templateDirectory,
             final DocumentationTemplate template) {
-        final Map<String, Object> templateData = new HashMap<>();
-        templateData.put("scalars", scalarTypes);
+        final Map<String, Object> templateData = Map.of("sourcedTypes", scalarTypes);
         render(templateDirectory, template.name, templateData, Path.of(template.targetPath));
     }
 }
diff --git a/log4j-docgen/src/main/java/org/apache/logging/log4j/docgen/generator/SchemaGenerator.java b/log4j-docgen/src/main/java/org/apache/logging/log4j/docgen/generator/SchemaGenerator.java
index 652086c..ab13a87 100644
--- a/log4j-docgen/src/main/java/org/apache/logging/log4j/docgen/generator/SchemaGenerator.java
+++ b/log4j-docgen/src/main/java/org/apache/logging/log4j/docgen/generator/SchemaGenerator.java
@@ -101,7 +101,8 @@ public final class SchemaGenerator {
     }
 
     private static void writeTypes(final TypeLookup lookup, final XMLStreamWriter writer) throws XMLStreamException {
-        for (final Type type : lookup.values()) {
+        for (final ArtifactSourcedType sourcedType : lookup.values()) {
+            final Type type = sourcedType.type;
             if (isBuiltinXmlType(type.getClassName())) {
                 continue;
             }
@@ -199,7 +200,7 @@ public final class SchemaGenerator {
             implementations.add(abstractType.getClassName());
         }
         for (final String implementation : implementations) {
-            final PluginType pluginType = (PluginType) lookup.get(implementation);
+            final PluginType pluginType = (PluginType) lookup.get(implementation).type;
             for (final String key : getKeyAndAliases(pluginType)) {
                 writer.writeEmptyElement(XSD_NAMESPACE, "element");
                 writer.writeAttribute("name", key);
@@ -235,7 +236,7 @@ public final class SchemaGenerator {
             throws XMLStreamException {
         final String type = element.getType();
         final String xmlType = getXmlType(lookup, type);
-        final AbstractType abstractType = (AbstractType) lookup.get(type);
+        final AbstractType abstractType = (AbstractType) lookup.get(type).type;
         final PluginType pluginType = abstractType instanceof PluginType ? (PluginType) abstractType : null;
         /*
          * If a plugin extends another plugin or has multiple aliases
@@ -287,7 +288,7 @@ public final class SchemaGenerator {
             case "java.lang.String":
                 return "string";
         }
-        final Type type = lookup.get(className);
+        final ArtifactSourcedType type = lookup.get(className);
         if (type != null) {
             return LOG4J_PREFIX + ":" + className;
         }
diff --git a/log4j-docgen/src/main/java/org/apache/logging/log4j/docgen/generator/TypeLookup.java b/log4j-docgen/src/main/java/org/apache/logging/log4j/docgen/generator/TypeLookup.java
index bde06ff..7aceacd 100644
--- a/log4j-docgen/src/main/java/org/apache/logging/log4j/docgen/generator/TypeLookup.java
+++ b/log4j-docgen/src/main/java/org/apache/logging/log4j/docgen/generator/TypeLookup.java
@@ -16,16 +16,19 @@
  */
 package org.apache.logging.log4j.docgen.generator;
 
-import java.util.HashSet;
+import java.util.List;
 import java.util.Set;
 import java.util.TreeMap;
-import java.util.function.Predicate;
+import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
 import org.apache.logging.log4j.docgen.AbstractType;
+import org.apache.logging.log4j.docgen.PluginElement;
 import org.apache.logging.log4j.docgen.PluginSet;
 import org.apache.logging.log4j.docgen.PluginType;
 import org.apache.logging.log4j.docgen.Type;
+import org.jspecify.annotations.Nullable;
 
-final class TypeLookup extends TreeMap<String, Type> {
+final class TypeLookup extends TreeMap<String, ArtifactSourcedType> {
 
     private static final long serialVersionUID = 1L;
 
@@ -33,30 +36,92 @@ final class TypeLookup extends TreeMap<String, Type> {
         return new TypeLookup(pluginSets);
     }
 
-    private TypeLookup(final Iterable<? extends PluginSet> sets) {
-        final Predicate<PluginType> hasCoreNamespace = p -> "Core".equals(p.getNamespace());
-        // Round 1: Merge all the information from the sets
-        sets.forEach(set -> {
-            set.getScalars().forEach(scalar -> put(scalar.getClassName(), scalar));
-            set.getAbstractTypes().forEach(abstractType -> put(abstractType.getClassName(), abstractType));
-            set.getPlugins().stream().filter(hasCoreNamespace).forEach(plugin -> put(plugin.getClassName(), plugin));
+    private TypeLookup(final Iterable<? extends PluginSet> pluginSets) {
+        mergeDescriptors(pluginSets);
+        populateTypeHierarchy(pluginSets);
+        removeUnusedTypes(pluginSets);
+    }
+
+    private void mergeDescriptors(Iterable<? extends PluginSet> pluginSets) {
+        pluginSets.forEach(pluginSet -> {
+            pluginSet.getScalars().forEach(scalar -> {
+                final ArtifactSourcedType sourcedType = ArtifactSourcedType.ofPluginSet(pluginSet, scalar);
+                put(scalar.getClassName(), sourcedType);
+            });
+            pluginSet.getAbstractTypes().forEach(abstractType -> {
+                final ArtifactSourcedType sourcedType = ArtifactSourcedType.ofPluginSet(pluginSet, abstractType);
+                put(abstractType.getClassName(), sourcedType);
+            });
+            pluginSet.getPlugins().forEach(pluginType -> {
+                final ArtifactSourcedType sourcedType = ArtifactSourcedType.ofPluginSet(pluginSet, pluginType);
+                put(pluginType.getClassName(), sourcedType);
+            });
+        });
+    }
+
+    private void populateTypeHierarchy(Iterable<? extends PluginSet> pluginSets) {
+        pluginSets.forEach(pluginSet -> {
+            final Set<PluginType> pluginTypes = pluginSet.getPlugins();
+            pluginTypes.forEach(plugin -> {
+                final Set<String> superTypeClassNames = plugin.getSupertypes();
+                superTypeClassNames.forEach(superTypeClassName -> {
+                    final AbstractType sourcedSuperType = getOrPutAbstractType(superTypeClassName, pluginSet);
+                    sourcedSuperType.addImplementation(plugin.getClassName());
+                });
+            });
         });
-        // Round 2: fill in the set of abstract types used in elements and the list of their possible implementations
-        final Set<String> requiredAbstractTypes = new HashSet<>();
-        sets.forEach(set -> set.getPlugins().stream().filter(hasCoreNamespace).forEach(plugin -> {
-            plugin.getSupertypes()
-                    .forEach(supertype -> ((AbstractType) computeIfAbsent(supertype, TypeLookup::createAbstractType))
-                            .addImplementation(plugin.getClassName()));
-            plugin.getElements().forEach(element -> requiredAbstractTypes.add(element.getType()));
-        }));
-        // Round 3: remove the types that are not required and do not have a description
-        values().removeIf(
-                        type -> !requiredAbstractTypes.contains(type.getClassName()) && type.getDescription() == null);
     }
 
-    private static AbstractType createAbstractType(final String className) {
-        final AbstractType abstractType = new AbstractType();
-        abstractType.setClassName(className);
-        return abstractType;
+    /**
+     * Gets the {@link AbstractType} associated with the provided class name, otherwise puts a new one sourced using the provided {@link PluginSet}.
+     * <h1>Invocation order matters!</h1>
+     * <p>
+     * Consider we are processing {@code plugins.xml}s from following artifacts:
+     * </p>
+     * <ol>
+     * <li>{@code log4j-core} providing {@code Layout}</li>
+     * <li>{@code log4j-layout-template-json} providing {@code JsonTemplateLayout} extending from {@code Layout}
+     * </ol>
+     * <p>
+     * If {@code plugins.xml} of {@code log4j-layout-template-json} gets processed first, {@code Layout} type will be <strong>incorrectly</strong> associated with {@code log4j-layout-template-json}.
+     * Hence, {@code log4j-core} processing needs to happen before the {@code log4j-layout-template-json} processing.
+     * </p>
+     *
+     * @param className a class name
+     * @param pluginSet the descriptor sourcing the type
+     * @return either the existing {@link AbstractType} associated with the provided class name, or a new one created
+     */
+    @Nullable
+    private AbstractType getOrPutAbstractType(final String className, final PluginSet pluginSet) {
+
+        // If there is already an entry with the same class name, return that
+        @Nullable final ArtifactSourcedType foundSourcedType = get(className);
+        if (foundSourcedType != null) {
+            return (AbstractType) foundSourcedType.type;
+        }
+
+        // Otherwise, create a new one, and return that
+        else {
+            final AbstractType type = new AbstractType();
+            type.setClassName(className);
+            final ArtifactSourcedType sourcedType = ArtifactSourcedType.ofPluginSet(pluginSet, type);
+            put(className, sourcedType);
+            return type;
+        }
+    }
+
+    private void removeUnusedTypes(Iterable<? extends PluginSet> pluginSets) {
+        final Set<String> requiredAbstractTypes = StreamSupport.stream(pluginSets.spliterator(), false)
+                .map(PluginSet::getPlugins)
+                .flatMap(Set::stream)
+                .map(PluginType::getElements)
+                .flatMap(List::stream)
+                .map(PluginElement::getType)
+                .collect(Collectors.toSet());
+        values().removeIf(sourcedType -> {
+            final Type type = sourcedType.type;
+            final boolean typeRequired = !requiredAbstractTypes.contains(type.getClassName());
+            return typeRequired && type.getDescription() == null;
+        });
     }
 }
diff --git a/log4j-docgen/src/main/resources/org/apache/logging/log4j/docgen/generator/base-java-types.xml b/log4j-docgen/src/main/resources/org/apache/logging/log4j/docgen/generator/base-java-types.xml
index bc2a8f1..0dd93bc 100644
--- a/log4j-docgen/src/main/resources/org/apache/logging/log4j/docgen/generator/base-java-types.xml
+++ b/log4j-docgen/src/main/resources/org/apache/logging/log4j/docgen/generator/base-java-types.xml
@@ -22,7 +22,7 @@
   <scalars>
 
     <scalar className="boolean">
-      <description>A boolean value.</description>
+      <description>A boolean value</description>
       <values>
         <value name="false">
           <description>false</description>
diff --git a/log4j-docgen/src/test/java/org/apache/logging/log4j/docgen/generator/DocumentationGeneratorTest.java b/log4j-docgen/src/test/java/org/apache/logging/log4j/docgen/generator/DocumentationGeneratorTest.java
index 8c068ec..6b89139 100644
--- a/log4j-docgen/src/test/java/org/apache/logging/log4j/docgen/generator/DocumentationGeneratorTest.java
+++ b/log4j-docgen/src/test/java/org/apache/logging/log4j/docgen/generator/DocumentationGeneratorTest.java
@@ -44,9 +44,9 @@ public class DocumentationGeneratorTest {
                 new DocumentationTemplate(
                         "scalars.adoc.ftl", outputDir.resolve("scalars.adoc").toString()),
                 new DocumentationTemplate(
-                        "plugin.adoc.ftl", outputDir.resolve("Core/%c.adoc").toString()),
+                        "plugin.adoc.ftl", outputDir.resolve("%g/%a/%c.adoc").toString()),
                 new DocumentationTemplate(
-                        "interface.adoc.ftl", outputDir.resolve("Core/%c.adoc").toString()));
+                        "interface.adoc.ftl", outputDir.resolve("%g/%a/%c.adoc").toString()));
         DocumentationGenerator.generateDocumentation(generatorArgs);
 
         // Verify the output
diff --git a/log4j-docgen/src/test/resources/plugins-to-freemarker-output/Core/org.apache.logging.log4j.core.Appender.adoc b/log4j-docgen/src/test/resources/plugins-to-freemarker-output/org.apache.logging.log4j/log4j-core/org.apache.logging.log4j.core.Appender.adoc
similarity index 90%
rename from log4j-docgen/src/test/resources/plugins-to-freemarker-output/Core/org.apache.logging.log4j.core.Appender.adoc
rename to log4j-docgen/src/test/resources/plugins-to-freemarker-output/org.apache.logging.log4j/log4j-core/org.apache.logging.log4j.core.Appender.adoc
index 28c148a..a44c188 100644
--- a/log4j-docgen/src/test/resources/plugins-to-freemarker-output/Core/org.apache.logging.log4j.core.Appender.adoc
+++ b/log4j-docgen/src/test/resources/plugins-to-freemarker-output/org.apache.logging.log4j/log4j-core/org.apache.logging.log4j.core.Appender.adoc
@@ -14,8 +14,12 @@ 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.
 ////
+[#org_apache_logging_log4j_core_Appender]
 = Appender
 
+Class:: `org.apache.logging.log4j.core.Appender`
+Provider:: `org.apache.logging.log4j:log4j-core`
+
 Appends log events.
 An Appender can contain a `Layout` if applicable as well as an `ErrorHandler`.
 Typical Appender implementations coordinate with an implementation of `AbstractManager` to handle external resources such as streams, connections, and other shared state.
@@ -27,6 +31,7 @@ For instance, many Appenders can take advantage of the `@OutputStreamManager` cl
 It is recommended that Appenders don't do any heavy lifting since there can be many instances of the class being used at any given time.
 When resources require locking (e.g., through `@FileLock`), it is important to isolate synchronized code to prevent concurrency issues.
 
+[#org_apache_logging_log4j_core_Appender-implementations]
 == Known implementations
 
 * xref:org.apache.logging.log4j.core.appender.ConsoleAppender.adoc[ConsoleAppender]
diff --git a/log4j-docgen/src/test/resources/plugins-to-freemarker-output/Core/org.apache.logging.log4j.core.Filter.adoc b/log4j-docgen/src/test/resources/plugins-to-freemarker-output/org.apache.logging.log4j/log4j-core/org.apache.logging.log4j.core.Filter.adoc
similarity index 87%
rename from log4j-docgen/src/test/resources/plugins-to-freemarker-output/Core/org.apache.logging.log4j.core.Filter.adoc
rename to log4j-docgen/src/test/resources/plugins-to-freemarker-output/org.apache.logging.log4j/log4j-core/org.apache.logging.log4j.core.Filter.adoc
index 0c852c0..8fb358a 100644
--- a/log4j-docgen/src/test/resources/plugins-to-freemarker-output/Core/org.apache.logging.log4j.core.Filter.adoc
+++ b/log4j-docgen/src/test/resources/plugins-to-freemarker-output/org.apache.logging.log4j/log4j-core/org.apache.logging.log4j.core.Filter.adoc
@@ -14,14 +14,19 @@ 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.
 ////
+[#org_apache_logging_log4j_core_Filter]
 = Filter
 
+Class:: `org.apache.logging.log4j.core.Filter`
+Provider:: `org.apache.logging.log4j:log4j-core`
+
 Interface that must be implemented to allow custom event filtering.
 It is highly recommended that applications make use of the filters provided with this Log4j Core before creating their own.
 
 This interface supports "global" filters (i.e. - all events must pass through them first), attached to specific loggers and associated with Appenders.
 It is recommended that, where possible, `Filter` implementations create a generic filtering method that can be called from any of the filter methods.
 
+[#org_apache_logging_log4j_core_Filter-implementations]
 == Known implementations
 
 * xref:org.apache.logging.log4j.core.filter.BurstFilter.adoc[BurstFilter]
diff --git a/log4j-docgen/src/test/resources/plugins-to-freemarker-output/Core/org.apache.logging.log4j.core.Layout.adoc b/log4j-docgen/src/test/resources/plugins-to-freemarker-output/org.apache.logging.log4j/log4j-core/org.apache.logging.log4j.core.Layout.adoc
similarity index 83%
rename from log4j-docgen/src/test/resources/plugins-to-freemarker-output/Core/org.apache.logging.log4j.core.Layout.adoc
rename to log4j-docgen/src/test/resources/plugins-to-freemarker-output/org.apache.logging.log4j/log4j-core/org.apache.logging.log4j.core.Layout.adoc
index 78a1f6c..fcab5e5 100644
--- a/log4j-docgen/src/test/resources/plugins-to-freemarker-output/Core/org.apache.logging.log4j.core.Layout.adoc
+++ b/log4j-docgen/src/test/resources/plugins-to-freemarker-output/org.apache.logging.log4j/log4j-core/org.apache.logging.log4j.core.Layout.adoc
@@ -14,10 +14,15 @@ 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.
 ////
+[#org_apache_logging_log4j_core_Layout]
 = Layout
 
+Class:: `org.apache.logging.log4j.core.Layout`
+Provider:: `org.apache.logging.log4j:log4j-core`
+
 A layout is responsible for formatting a `LogEvent` as a string.
 
+[#org_apache_logging_log4j_core_Layout-implementations]
 == Known implementations
 
 * xref:org.apache.logging.log4j.core.layout.PatternLayout.adoc[PatternLayout]
diff --git a/log4j-docgen/src/test/resources/plugins-to-freemarker-output/Core/org.apache.logging.log4j.core.appender.ConsoleAppender.adoc b/log4j-docgen/src/test/resources/plugins-to-freemarker-output/org.apache.logging.log4j/log4j-core/org.apache.logging.log4j.core.appender.ConsoleAppender.adoc
similarity index 73%
rename from log4j-docgen/src/test/resources/plugins-to-freemarker-output/Core/org.apache.logging.log4j.core.appender.ConsoleAppender.adoc
rename to log4j-docgen/src/test/resources/plugins-to-freemarker-output/org.apache.logging.log4j/log4j-core/org.apache.logging.log4j.core.appender.ConsoleAppender.adoc
index 59cd3ec..ac5bed4 100644
--- a/log4j-docgen/src/test/resources/plugins-to-freemarker-output/Core/org.apache.logging.log4j.core.appender.ConsoleAppender.adoc
+++ b/log4j-docgen/src/test/resources/plugins-to-freemarker-output/org.apache.logging.log4j/log4j-core/org.apache.logging.log4j.core.appender.ConsoleAppender.adoc
@@ -14,10 +14,15 @@ 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.
 ////
+[#org_apache_logging_log4j_core_appender_ConsoleAppender]
 = Console
 
+Class:: `org.apache.logging.log4j.core.appender.ConsoleAppender`
+Provider:: `org.apache.logging.log4j:log4j-core`
+
 Write log events to the console.
 
+[#org_apache_logging_log4j_core_appender_ConsoleAppender-XML-snippet]
 == XML snippet
 [source, xml]
 ----
@@ -27,11 +32,12 @@ Write log events to the console.
          immediateFlush="true"
          name=""
          target="SYSTEM_OUT">
-    ... one Filter element ...
-    ... one Layout element ...
+    <a-Filter-implementation/>
+    <a-Layout-implementation/>
 </Console>
 ----
 
+[#org_apache_logging_log4j_core_appender_ConsoleAppender-attributes]
 == Attributes
 
 Required attributes are in **bold face**.
@@ -41,41 +47,42 @@ Required attributes are in **bold face**.
 |Name|Type|Default|Description
 
 |bufferedIo
-|xref:../scalars.adoc#boolean[boolean]
+|xref:../../scalars.adoc#boolean[boolean]
 |true
 a|If set to `true` (default) the appender will buffer messages before sending them.
 This attribute is ignored if `immediateFlush` is set to `true`.
 
 |bufferSize
-|xref:../scalars.adoc#int[int]
+|xref:../../scalars.adoc#int[int]
 |
 a|Size in bytes of the appender's buffer.
 
 |ignoreExceptions
-|xref:../scalars.adoc#boolean[boolean]
+|xref:../../scalars.adoc#boolean[boolean]
 |true
 a|If set to `false` logging exceptions will be forwarded to the caller.
 
 |immediateFlush
-|xref:../scalars.adoc#boolean[boolean]
+|xref:../../scalars.adoc#boolean[boolean]
 |true
 a|If set to `true`, the appender flushes the output stream at each message and
 buffering is disabled regardless of the value of `bufferedIo`.
 
 |**name**
-|xref:../scalars.adoc#java.lang.String[String]
+|xref:../../scalars.adoc#java_lang_String[String]
 |
 a|The name of the appender used in appender references.
 Must be unique.
 
 |target
-|xref:../scalars.adoc#org.apache.logging.log4j.core.appender.ConsoleAppender.Target[Target]
+|xref:../../scalars.adoc#org_apache_logging_log4j_core_appender_ConsoleAppender_Target[Target]
 |SYSTEM_OUT
 a|Specifies the target of the appender.
 
 |===
 
-== Nested Components
+[#org_apache_logging_log4j_core_appender_ConsoleAppender-components]
+== Nested components
 
 Required components are in **bold face**.
 
diff --git a/log4j-docgen/src/test/resources/plugins-to-freemarker-output/Core/org.apache.logging.log4j.core.config.AppenderRef.adoc b/log4j-docgen/src/test/resources/plugins-to-freemarker-output/org.apache.logging.log4j/log4j-core/org.apache.logging.log4j.core.config.AppenderRef.adoc
similarity index 71%
rename from log4j-docgen/src/test/resources/plugins-to-freemarker-output/Core/org.apache.logging.log4j.core.config.AppenderRef.adoc
rename to log4j-docgen/src/test/resources/plugins-to-freemarker-output/org.apache.logging.log4j/log4j-core/org.apache.logging.log4j.core.config.AppenderRef.adoc
index 652be60..2aa1f96 100644
--- a/log4j-docgen/src/test/resources/plugins-to-freemarker-output/Core/org.apache.logging.log4j.core.config.AppenderRef.adoc
+++ b/log4j-docgen/src/test/resources/plugins-to-freemarker-output/org.apache.logging.log4j/log4j-core/org.apache.logging.log4j.core.config.AppenderRef.adoc
@@ -14,19 +14,25 @@ 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.
 ////
+[#org_apache_logging_log4j_core_config_AppenderRef]
 = AppenderRef
 
+Class:: `org.apache.logging.log4j.core.config.AppenderRef`
+Provider:: `org.apache.logging.log4j:log4j-core`
+
 Reference to an appender defined in the `Appenders` section.
 
+[#org_apache_logging_log4j_core_config_AppenderRef-XML-snippet]
 == XML snippet
 [source, xml]
 ----
 <AppenderRef level=""
              ref="">
-    ... one Filter element ...
+    <a-Filter-implementation/>
 </AppenderRef>
 ----
 
+[#org_apache_logging_log4j_core_config_AppenderRef-attributes]
 == Attributes
 
 Required attributes are in **bold face**.
@@ -36,18 +42,19 @@ Required attributes are in **bold face**.
 |Name|Type|Default|Description
 
 |level
-|xref:../scalars.adoc#org.apache.logging.log4j.Level[Level]
+|xref:../../scalars.adoc#org_apache_logging_log4j_Level[Level]
 |
 a|The level to filter against.
 
 |**ref**
-|xref:../scalars.adoc#java.lang.String[String]
+|xref:../../scalars.adoc#java_lang_String[String]
 |
 a|The name of an appender.
 
 |===
 
-== Nested Components
+[#org_apache_logging_log4j_core_config_AppenderRef-components]
+== Nested components
 
 Required components are in **bold face**.
 
diff --git a/log4j-docgen/src/test/resources/plugins-to-freemarker-output/Core/org.apache.logging.log4j.core.config.AppendersPlugin.adoc b/log4j-docgen/src/test/resources/plugins-to-freemarker-output/org.apache.logging.log4j/log4j-core/org.apache.logging.log4j.core.config.AppendersPlugin.adoc
similarity index 76%
rename from log4j-docgen/src/test/resources/plugins-to-freemarker-output/Core/org.apache.logging.log4j.core.config.AppendersPlugin.adoc
rename to log4j-docgen/src/test/resources/plugins-to-freemarker-output/org.apache.logging.log4j/log4j-core/org.apache.logging.log4j.core.config.AppendersPlugin.adoc
index c8f6334..b4e1e33 100644
--- a/log4j-docgen/src/test/resources/plugins-to-freemarker-output/Core/org.apache.logging.log4j.core.config.AppendersPlugin.adoc
+++ b/log4j-docgen/src/test/resources/plugins-to-freemarker-output/org.apache.logging.log4j/log4j-core/org.apache.logging.log4j.core.config.AppendersPlugin.adoc
@@ -14,17 +14,23 @@ 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.
 ////
+[#org_apache_logging_log4j_core_config_AppendersPlugin]
 = Appenders
 
+Class:: `org.apache.logging.log4j.core.config.AppendersPlugin`
+Provider:: `org.apache.logging.log4j:log4j-core`
+
 A wrapper for a list of appenders.
 
+[#org_apache_logging_log4j_core_config_AppendersPlugin-XML-snippet]
 == XML snippet
 [source, xml]
 ----
 <Appenders/>
 ----
 
-== Nested Components
+[#org_apache_logging_log4j_core_config_AppendersPlugin-components]
+== Nested components
 
 Required components are in **bold face**.
 
diff --git a/log4j-docgen/src/test/resources/plugins-to-freemarker-output/Core/org.apache.logging.log4j.core.config.Configuration.adoc b/log4j-docgen/src/test/resources/plugins-to-freemarker-output/org.apache.logging.log4j/log4j-core/org.apache.logging.log4j.core.config.Configuration.adoc
similarity index 80%
rename from log4j-docgen/src/test/resources/plugins-to-freemarker-output/Core/org.apache.logging.log4j.core.config.Configuration.adoc
rename to log4j-docgen/src/test/resources/plugins-to-freemarker-output/org.apache.logging.log4j/log4j-core/org.apache.logging.log4j.core.config.Configuration.adoc
index f7a675e..48742fd 100644
--- a/log4j-docgen/src/test/resources/plugins-to-freemarker-output/Core/org.apache.logging.log4j.core.config.Configuration.adoc
+++ b/log4j-docgen/src/test/resources/plugins-to-freemarker-output/org.apache.logging.log4j/log4j-core/org.apache.logging.log4j.core.config.Configuration.adoc
@@ -14,10 +14,15 @@ 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.
 ////
+[#org_apache_logging_log4j_core_config_Configuration]
 = Configuration
 
+Class:: `org.apache.logging.log4j.core.config.Configuration`
+Provider:: `org.apache.logging.log4j:log4j-core`
+
 A Log4j configuration contains many components of which two are required: `Appenders` and `Loggers`.
 
+[#org_apache_logging_log4j_core_config_Configuration-XML-snippet]
 == XML snippet
 [source, xml]
 ----
@@ -36,6 +41,7 @@ A Log4j configuration contains many components of which two are required: `Appen
 </Configuration>
 ----
 
+[#org_apache_logging_log4j_core_config_Configuration-attributes]
 == Attributes
 
 Required attributes are in **bold face**.
@@ -45,7 +51,7 @@ Required attributes are in **bold face**.
 |Name|Type|Default|Description
 
 |dest
-|xref:../scalars.adoc#java.lang.String[String]
+|xref:../../scalars.adoc#java_lang_String[String]
 |err
 a|Specifies the destination for status logger events.
 The possible values are:
@@ -57,45 +63,46 @@ The possible values are:
 If the provided value is invalid, then the default destination of standard out will be used.
 
 |monitorInterval
-|xref:../scalars.adoc#int[int]
+|xref:../../scalars.adoc#int[int]
 |0
 a|Number of seconds between polls for configuration changes
 
 |**name**
-|xref:../scalars.adoc#java.lang.String[String]
+|xref:../../scalars.adoc#java_lang_String[String]
 |
 a|Name of the configuration
 
 |schema
-|xref:../scalars.adoc#java.lang.String[String]
+|xref:../../scalars.adoc#java_lang_String[String]
 |
 a|The name of a classpath resource to use to validate the configuration
 
 |shutdownHook
-|xref:../scalars.adoc#java.lang.String[String]
+|xref:../../scalars.adoc#java_lang_String[String]
 |
 a|Indicates if Log4j should automatically shut down when the JVM shuts down.
 Possible values are `enable` and `disable`.
 The shutdown hook is enabled by default, unless Log4j detects the presence of the Servlet API.
 
 |shutdownTimeout
-|xref:../scalars.adoc#int[int]
+|xref:../../scalars.adoc#int[int]
 |
 a|Timeout in milliseconds of the logger context shut down
 
 |status
-|xref:../scalars.adoc#org.apache.logging.log4j.Level[Level]
+|xref:../../scalars.adoc#org_apache_logging_log4j_Level[Level]
 |ERROR
 a|Sets the level of the status logger
 
 |strict
-|xref:../scalars.adoc#boolean[boolean]
+|xref:../../scalars.adoc#boolean[boolean]
 |false
 a|If set to `true` the configuration file will be validated using an XML schema.
 
 |===
 
-== Nested Components
+[#org_apache_logging_log4j_core_config_Configuration-components]
+== Nested components
 
 Required components are in **bold face**.
 
diff --git a/log4j-docgen/src/test/resources/plugins-to-freemarker-output/Core/org.apache.logging.log4j.core.config.CustomLevelConfig.adoc b/log4j-docgen/src/test/resources/plugins-to-freemarker-output/org.apache.logging.log4j/log4j-core/org.apache.logging.log4j.core.config.CustomLevelConfig.adoc
similarity index 74%
rename from log4j-docgen/src/test/resources/plugins-to-freemarker-output/Core/org.apache.logging.log4j.core.config.CustomLevelConfig.adoc
rename to log4j-docgen/src/test/resources/plugins-to-freemarker-output/org.apache.logging.log4j/log4j-core/org.apache.logging.log4j.core.config.CustomLevelConfig.adoc
index 411f5ab..eac1689 100644
--- a/log4j-docgen/src/test/resources/plugins-to-freemarker-output/Core/org.apache.logging.log4j.core.config.CustomLevelConfig.adoc
+++ b/log4j-docgen/src/test/resources/plugins-to-freemarker-output/org.apache.logging.log4j/log4j-core/org.apache.logging.log4j.core.config.CustomLevelConfig.adoc
@@ -14,10 +14,15 @@ 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.
 ////
+[#org_apache_logging_log4j_core_config_CustomLevelConfig]
 = CustomLevel
 
+Class:: `org.apache.logging.log4j.core.config.CustomLevelConfig`
+Provider:: `org.apache.logging.log4j:log4j-core`
+
 Configures a custom level.
 
+[#org_apache_logging_log4j_core_config_CustomLevelConfig-XML-snippet]
 == XML snippet
 [source, xml]
 ----
@@ -25,6 +30,7 @@ Configures a custom level.
              name=""/>
 ----
 
+[#org_apache_logging_log4j_core_config_CustomLevelConfig-attributes]
 == Attributes
 
 Required attributes are in **bold face**.
@@ -34,12 +40,12 @@ Required attributes are in **bold face**.
 |Name|Type|Default|Description
 
 |**intLevel**
-|xref:../scalars.adoc#int[int]
+|xref:../../scalars.adoc#int[int]
 |
 a|An integer determines the strength of the custom level relative to the built-in levels.
 
 |**name**
-|xref:../scalars.adoc#java.lang.String[String]
+|xref:../../scalars.adoc#java_lang_String[String]
 |
 a|The name of the level.
 
diff --git a/log4j-docgen/src/test/resources/plugins-to-freemarker-output/Core/org.apache.logging.log4j.core.config.CustomLevels.adoc b/log4j-docgen/src/test/resources/plugins-to-freemarker-output/org.apache.logging.log4j/log4j-core/org.apache.logging.log4j.core.config.CustomLevels.adoc
similarity index 78%
rename from log4j-docgen/src/test/resources/plugins-to-freemarker-output/Core/org.apache.logging.log4j.core.config.CustomLevels.adoc
rename to log4j-docgen/src/test/resources/plugins-to-freemarker-output/org.apache.logging.log4j/log4j-core/org.apache.logging.log4j.core.config.CustomLevels.adoc
index afc46e5..c10c19b 100644
--- a/log4j-docgen/src/test/resources/plugins-to-freemarker-output/Core/org.apache.logging.log4j.core.config.CustomLevels.adoc
+++ b/log4j-docgen/src/test/resources/plugins-to-freemarker-output/org.apache.logging.log4j/log4j-core/org.apache.logging.log4j.core.config.CustomLevels.adoc
@@ -14,17 +14,23 @@ 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.
 ////
+[#org_apache_logging_log4j_core_config_CustomLevels]
 = CustomLevels
 
+Class:: `org.apache.logging.log4j.core.config.CustomLevels`
+Provider:: `org.apache.logging.log4j:log4j-core`
+
 A wrapper for a list of custom level configurations.
 
+[#org_apache_logging_log4j_core_config_CustomLevels-XML-snippet]
 == XML snippet
 [source, xml]
 ----
 <CustomLevels/>
 ----
 
-== Nested Components
+[#org_apache_logging_log4j_core_config_CustomLevels-components]
+== Nested components
 
 Required components are in **bold face**.
 
diff --git a/log4j-docgen/src/test/resources/plugins-to-freemarker-output/Core/org.apache.logging.log4j.core.config.LoggerConfig.RootLogger.adoc b/log4j-docgen/src/test/resources/plugins-to-freemarker-output/org.apache.logging.log4j/log4j-core/org.apache.logging.log4j.core.config.LoggerConfig.RootLogger.adoc
similarity index 74%
rename from log4j-docgen/src/test/resources/plugins-to-freemarker-output/Core/org.apache.logging.log4j.core.config.LoggerConfig.RootLogger.adoc
rename to log4j-docgen/src/test/resources/plugins-to-freemarker-output/org.apache.logging.log4j/log4j-core/org.apache.logging.log4j.core.config.LoggerConfig.RootLogger.adoc
index cdee35f..0f5ea48 100644
--- a/log4j-docgen/src/test/resources/plugins-to-freemarker-output/Core/org.apache.logging.log4j.core.config.LoggerConfig.RootLogger.adoc
+++ b/log4j-docgen/src/test/resources/plugins-to-freemarker-output/org.apache.logging.log4j/log4j-core/org.apache.logging.log4j.core.config.LoggerConfig.RootLogger.adoc
@@ -14,20 +14,26 @@ 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.
 ////
+[#org_apache_logging_log4j_core_config_LoggerConfig_RootLogger]
 = Root
 
+Class:: `org.apache.logging.log4j.core.config.LoggerConfig.RootLogger`
+Provider:: `org.apache.logging.log4j:log4j-core`
+
 Configures the root logger
 
+[#org_apache_logging_log4j_core_config_LoggerConfig_RootLogger-XML-snippet]
 == XML snippet
 [source, xml]
 ----
 <Root includeLocation=""
       level="ERROR">
     <AppenderRef/>
-    ... one Filter element ...
+    <a-Filter-implementation/>
 </Root>
 ----
 
+[#org_apache_logging_log4j_core_config_LoggerConfig_RootLogger-attributes]
 == Attributes
 
 Required attributes are in **bold face**.
@@ -37,20 +43,21 @@ Required attributes are in **bold face**.
 |Name|Type|Default|Description
 
 |includeLocation
-|xref:../scalars.adoc#boolean[boolean]
+|xref:../../scalars.adoc#boolean[boolean]
 |
 a|When set to `false` location information will **not** be computed.
 
 The default value depends on the logger context implementation: it is `false` for `AsyncLoggerContext` and `true` for `LoggerContext`.
 
 |level
-|xref:../scalars.adoc#org.apache.logging.log4j.Level[Level]
+|xref:../../scalars.adoc#org_apache_logging_log4j_Level[Level]
 |ERROR
 a|The level of the logger.
 
 |===
 
-== Nested Components
+[#org_apache_logging_log4j_core_config_LoggerConfig_RootLogger-components]
+== Nested components
 
 Required components are in **bold face**.
 
diff --git a/log4j-docgen/src/test/resources/plugins-to-freemarker-output/Core/org.apache.logging.log4j.core.config.LoggerConfig.adoc b/log4j-docgen/src/test/resources/plugins-to-freemarker-output/org.apache.logging.log4j/log4j-core/org.apache.logging.log4j.core.config.LoggerConfig.adoc
similarity index 74%
rename from log4j-docgen/src/test/resources/plugins-to-freemarker-output/Core/org.apache.logging.log4j.core.config.LoggerConfig.adoc
rename to log4j-docgen/src/test/resources/plugins-to-freemarker-output/org.apache.logging.log4j/log4j-core/org.apache.logging.log4j.core.config.LoggerConfig.adoc
index 60af05b..b427d1e 100644
--- a/log4j-docgen/src/test/resources/plugins-to-freemarker-output/Core/org.apache.logging.log4j.core.config.LoggerConfig.adoc
+++ b/log4j-docgen/src/test/resources/plugins-to-freemarker-output/org.apache.logging.log4j/log4j-core/org.apache.logging.log4j.core.config.LoggerConfig.adoc
@@ -14,10 +14,15 @@ 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.
 ////
+[#org_apache_logging_log4j_core_config_LoggerConfig]
 = Logger
 
+Class:: `org.apache.logging.log4j.core.config.LoggerConfig`
+Provider:: `org.apache.logging.log4j:log4j-core`
+
 Configures a particular logger
 
+[#org_apache_logging_log4j_core_config_LoggerConfig-XML-snippet]
 == XML snippet
 [source, xml]
 ----
@@ -25,10 +30,11 @@ Configures a particular logger
         level=""
         name="">
     <AppenderRef/>
-    ... one Filter element ...
+    <a-Filter-implementation/>
 </Logger>
 ----
 
+[#org_apache_logging_log4j_core_config_LoggerConfig-attributes]
 == Attributes
 
 Required attributes are in **bold face**.
@@ -38,25 +44,26 @@ Required attributes are in **bold face**.
 |Name|Type|Default|Description
 
 |includeLocation
-|xref:../scalars.adoc#boolean[boolean]
+|xref:../../scalars.adoc#boolean[boolean]
 |
 a|When set to `false` location information will **not** be computed.
 
 The default value depends on the logger context implementation: it is `false` for `AsyncLoggerContext` and `true` for `LoggerContext`.
 
 |level
-|xref:../scalars.adoc#org.apache.logging.log4j.Level[Level]
+|xref:../../scalars.adoc#org_apache_logging_log4j_Level[Level]
 |
 a|The level of the logger.
 
 |**name**
-|xref:../scalars.adoc#java.lang.String[String]
+|xref:../../scalars.adoc#java_lang_String[String]
 |
 a|The name of the logger.
 
 |===
 
-== Nested Components
+[#org_apache_logging_log4j_core_config_LoggerConfig-components]
+== Nested components
 
 Required components are in **bold face**.
 
@@ -74,6 +81,7 @@ a|A filter to filter events, before calling the appenders.
 
 |===
 
+[#org_apache_logging_log4j_core_config_LoggerConfig-implementations]
 == Known implementations
 
 * xref:org.apache.logging.log4j.core.config.LoggerConfig.RootLogger.adoc[RootLogger]
diff --git a/log4j-docgen/src/test/resources/plugins-to-freemarker-output/Core/org.apache.logging.log4j.core.config.LoggersPlugin.adoc b/log4j-docgen/src/test/resources/plugins-to-freemarker-output/org.apache.logging.log4j/log4j-core/org.apache.logging.log4j.core.config.LoggersPlugin.adoc
similarity index 77%
rename from log4j-docgen/src/test/resources/plugins-to-freemarker-output/Core/org.apache.logging.log4j.core.config.LoggersPlugin.adoc
rename to log4j-docgen/src/test/resources/plugins-to-freemarker-output/org.apache.logging.log4j/log4j-core/org.apache.logging.log4j.core.config.LoggersPlugin.adoc
index 46063f1..e5aeecf 100644
--- a/log4j-docgen/src/test/resources/plugins-to-freemarker-output/Core/org.apache.logging.log4j.core.config.LoggersPlugin.adoc
+++ b/log4j-docgen/src/test/resources/plugins-to-freemarker-output/org.apache.logging.log4j/log4j-core/org.apache.logging.log4j.core.config.LoggersPlugin.adoc
@@ -14,17 +14,23 @@ 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.
 ////
+[#org_apache_logging_log4j_core_config_LoggersPlugin]
 = Loggers
 
+Class:: `org.apache.logging.log4j.core.config.LoggersPlugin`
+Provider:: `org.apache.logging.log4j:log4j-core`
+
 A wrapper for a list of logger configurations.
 
+[#org_apache_logging_log4j_core_config_LoggersPlugin-XML-snippet]
 == XML snippet
 [source, xml]
 ----
 <Loggers/>
 ----
 
-== Nested Components
+[#org_apache_logging_log4j_core_config_LoggersPlugin-components]
+== Nested components
 
 Required components are in **bold face**.
 
diff --git a/log4j-docgen/src/test/resources/plugins-to-freemarker-output/Core/org.apache.logging.log4j.core.config.PropertiesPlugin.adoc b/log4j-docgen/src/test/resources/plugins-to-freemarker-output/org.apache.logging.log4j/log4j-core/org.apache.logging.log4j.core.config.PropertiesPlugin.adoc
similarity index 76%
rename from log4j-docgen/src/test/resources/plugins-to-freemarker-output/Core/org.apache.logging.log4j.core.config.PropertiesPlugin.adoc
rename to log4j-docgen/src/test/resources/plugins-to-freemarker-output/org.apache.logging.log4j/log4j-core/org.apache.logging.log4j.core.config.PropertiesPlugin.adoc
index ff5613c..4e79408 100644
--- a/log4j-docgen/src/test/resources/plugins-to-freemarker-output/Core/org.apache.logging.log4j.core.config.PropertiesPlugin.adoc
+++ b/log4j-docgen/src/test/resources/plugins-to-freemarker-output/org.apache.logging.log4j/log4j-core/org.apache.logging.log4j.core.config.PropertiesPlugin.adoc
@@ -14,17 +14,23 @@ 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.
 ////
+[#org_apache_logging_log4j_core_config_PropertiesPlugin]
 = Properties
 
+Class:: `org.apache.logging.log4j.core.config.PropertiesPlugin`
+Provider:: `org.apache.logging.log4j:log4j-core`
+
 A wrapper for a list of properties.
 
+[#org_apache_logging_log4j_core_config_PropertiesPlugin-XML-snippet]
 == XML snippet
 [source, xml]
 ----
 <Properties/>
 ----
 
-== Nested Components
+[#org_apache_logging_log4j_core_config_PropertiesPlugin-components]
+== Nested components
 
 Required components are in **bold face**.
 
diff --git a/log4j-docgen/src/test/resources/plugins-to-freemarker-output/Core/org.apache.logging.log4j.core.config.Property.adoc b/log4j-docgen/src/test/resources/plugins-to-freemarker-output/org.apache.logging.log4j/log4j-core/org.apache.logging.log4j.core.config.Property.adoc
similarity index 74%
rename from log4j-docgen/src/test/resources/plugins-to-freemarker-output/Core/org.apache.logging.log4j.core.config.Property.adoc
rename to log4j-docgen/src/test/resources/plugins-to-freemarker-output/org.apache.logging.log4j/log4j-core/org.apache.logging.log4j.core.config.Property.adoc
index d7c55ac..1b01968 100644
--- a/log4j-docgen/src/test/resources/plugins-to-freemarker-output/Core/org.apache.logging.log4j.core.config.Property.adoc
+++ b/log4j-docgen/src/test/resources/plugins-to-freemarker-output/org.apache.logging.log4j/log4j-core/org.apache.logging.log4j.core.config.Property.adoc
@@ -14,10 +14,15 @@ 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.
 ////
+[#org_apache_logging_log4j_core_config_Property]
 = Property
 
+Class:: `org.apache.logging.log4j.core.config.Property`
+Provider:: `org.apache.logging.log4j:log4j-core`
+
 Represents a key/value pair in the configuration.
 
+[#org_apache_logging_log4j_core_config_Property-XML-snippet]
 == XML snippet
 [source, xml]
 ----
@@ -25,6 +30,7 @@ Represents a key/value pair in the configuration.
           value=""/>
 ----
 
+[#org_apache_logging_log4j_core_config_Property-attributes]
 == Attributes
 
 Required attributes are in **bold face**.
@@ -34,12 +40,12 @@ Required attributes are in **bold face**.
 |Name|Type|Default|Description
 
 |**name**
-|xref:../scalars.adoc#java.lang.String[String]
+|xref:../../scalars.adoc#java_lang_String[String]
 |
 a|Name of the property.
 
 |value
-|xref:../scalars.adoc#java.lang.String[String]
+|xref:../../scalars.adoc#java_lang_String[String]
 |
 a|Value of the property.
 
diff --git a/log4j-docgen/src/test/resources/plugins-to-freemarker-output/Core/org.apache.logging.log4j.core.filter.BurstFilter.adoc b/log4j-docgen/src/test/resources/plugins-to-freemarker-output/org.apache.logging.log4j/log4j-core/org.apache.logging.log4j.core.filter.BurstFilter.adoc
similarity index 74%
rename from log4j-docgen/src/test/resources/plugins-to-freemarker-output/Core/org.apache.logging.log4j.core.filter.BurstFilter.adoc
rename to log4j-docgen/src/test/resources/plugins-to-freemarker-output/org.apache.logging.log4j/log4j-core/org.apache.logging.log4j.core.filter.BurstFilter.adoc
index 21d1ab3..a2ba2ad 100644
--- a/log4j-docgen/src/test/resources/plugins-to-freemarker-output/Core/org.apache.logging.log4j.core.filter.BurstFilter.adoc
+++ b/log4j-docgen/src/test/resources/plugins-to-freemarker-output/org.apache.logging.log4j/log4j-core/org.apache.logging.log4j.core.filter.BurstFilter.adoc
@@ -14,11 +14,16 @@ 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.
 ////
+[#org_apache_logging_log4j_core_filter_BurstFilter]
 = BurstFilter
 
+Class:: `org.apache.logging.log4j.core.filter.BurstFilter`
+Provider:: `org.apache.logging.log4j:log4j-core`
+
 The BurstFilter is a logging filter that regulates logging traffic.
 Use this filter when you want to control the mean rate and maximum burst of log statements that can be sent to an appender.
 
+[#org_apache_logging_log4j_core_filter_BurstFilter-XML-snippet]
 == XML snippet
 [source, xml]
 ----
@@ -29,6 +34,7 @@ Use this filter when you want to control the mean rate and maximum burst of log
              rate="10"/>
 ----
 
+[#org_apache_logging_log4j_core_filter_BurstFilter-attributes]
 == Attributes
 
 Required attributes are in **bold face**.
@@ -38,27 +44,27 @@ Required attributes are in **bold face**.
 |Name|Type|Default|Description
 
 |level
-|xref:../scalars.adoc#org.apache.logging.log4j.Level[Level]
+|xref:../../scalars.adoc#org_apache_logging_log4j_Level[Level]
 |WARN
 a|Log events less specific than this level are filtered, while events with level equal or more specific always match.
 
 |maxBurst
-|xref:../scalars.adoc#long[long]
+|xref:../../scalars.adoc#long[long]
 |
 a|Sets the maximum number of events that can occur before events are filtered for exceeding the average rate.
 
 |onMatch
-|xref:../scalars.adoc#org.apache.logging.log4j.core.Filter.Result[Result]
+|xref:../../scalars.adoc#org_apache_logging_log4j_core_Filter_Result[Result]
 |NEUTRAL
 a|Action to perform if the filter matches.
 
 |onMismatch
-|xref:../scalars.adoc#org.apache.logging.log4j.core.Filter.Result[Result]
+|xref:../../scalars.adoc#org_apache_logging_log4j_core_Filter_Result[Result]
 |DENY
 a|Action to perform if the filter does not match.
 
 |rate
-|xref:../scalars.adoc#float[float]
+|xref:../../scalars.adoc#float[float]
 |10
 a|Sets the average number of events per second to allow.
 
diff --git a/log4j-docgen/src/test/resources/plugins-to-freemarker-output/Core/org.apache.logging.log4j.core.layout.PatternLayout.adoc b/log4j-docgen/src/test/resources/plugins-to-freemarker-output/org.apache.logging.log4j/log4j-core/org.apache.logging.log4j.core.layout.PatternLayout.adoc
similarity index 81%
rename from log4j-docgen/src/test/resources/plugins-to-freemarker-output/Core/org.apache.logging.log4j.core.layout.PatternLayout.adoc
rename to log4j-docgen/src/test/resources/plugins-to-freemarker-output/org.apache.logging.log4j/log4j-core/org.apache.logging.log4j.core.layout.PatternLayout.adoc
index 23e2180..a567c45 100644
--- a/log4j-docgen/src/test/resources/plugins-to-freemarker-output/Core/org.apache.logging.log4j.core.layout.PatternLayout.adoc
+++ b/log4j-docgen/src/test/resources/plugins-to-freemarker-output/org.apache.logging.log4j/log4j-core/org.apache.logging.log4j.core.layout.PatternLayout.adoc
@@ -14,8 +14,12 @@ 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.
 ////
+[#org_apache_logging_log4j_core_layout_PatternLayout]
 = PatternLayout
 
+Class:: `org.apache.logging.log4j.core.layout.PatternLayout`
+Provider:: `org.apache.logging.log4j:log4j-core`
+
 A flexible layout configurable with pattern string.
 
 The goal of this class is to {@link org.apache.logging.log4j.core.Layout#toByteArray format} a {@link LogEvent} and return the results.
@@ -24,12 +28,14 @@ The format of the result depends on the _conversion pattern_.
 The conversion pattern is closely related to the conversion pattern of the printf function in C.
 A conversion pattern is composed of literal text and format control expressions called _conversion specifiers_.
 
+[#org_apache_logging_log4j_core_layout_PatternLayout-XML-snippet]
 == XML snippet
 [source, xml]
 ----
 <PatternLayout pattern="%m%n"/>
 ----
 
+[#org_apache_logging_log4j_core_layout_PatternLayout-attributes]
 == Attributes
 
 Required attributes are in **bold face**.
@@ -39,7 +45,7 @@ Required attributes are in **bold face**.
 |Name|Type|Default|Description
 
 |pattern
-|xref:../scalars.adoc#java.lang.String[String]
+|xref:../../scalars.adoc#java_lang_String[String]
 |%m%n
 a|The pattern to use to format log events.
 
diff --git a/log4j-docgen/src/test/resources/plugins-to-freemarker-output/scalars.adoc b/log4j-docgen/src/test/resources/plugins-to-freemarker-output/scalars.adoc
index 3562590..d147a94 100644
--- a/log4j-docgen/src/test/resources/plugins-to-freemarker-output/scalars.adoc
+++ b/log4j-docgen/src/test/resources/plugins-to-freemarker-output/scalars.adoc
@@ -14,48 +14,69 @@ 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.
 ////
+[#type-converters]
 = Type converters
 
 Type converter plugins are used to convert simple `String` values into other types.
 
-== Available converters
+[#boolean]
+== `boolean`
 
-[cols="1m,2"]
-|===
-|Type|Description
 
-|[[boolean]]
-boolean
-a|A boolean value.
+A boolean value
 
-Possible values:
+[#boolean-values]
+=== Possible values
 
 `false`:: false
 `true`:: true
-|[[byte]]
-byte
-a|An 8-bit signed integer from `-128` to `127`
-|[[double]]
-double
-a|A double precision floating number from `4.94065645841246544e-324` to `1.79769313486231570e+308`
-|[[float]]
-float
-a|A single precision floating number from `1.40239846e-45` to `3.40282347e+38`
-|[[int]]
-int
-a|A 32-bit signed integer from `-2147483648` to `2147483647`
-|[[java.lang.String]]
-java.lang.String
-a|A string of characters
-|[[long]]
-long
-a|A 64-bit signed integer from `-9223372036854775808` to `9223372036854775807`
-|[[org.apache.logging.log4j.Level]]
-o.a.l.l.Level
-a|Represents a logging level.
+
+[#byte]
+== `byte`
+
+
+An 8-bit signed integer from `-128` to `127`
+
+[#double]
+== `double`
+
+
+A double precision floating number from `4.94065645841246544e-324` to `1.79769313486231570e+308`
+
+[#float]
+== `float`
+
+
+A single precision floating number from `1.40239846e-45` to `3.40282347e+38`
+
+[#int]
+== `int`
+
+
+A 32-bit signed integer from `-2147483648` to `2147483647`
+
+[#java_lang_String]
+== `java.lang.String`
+
+
+A string of characters
+
+[#long]
+== `long`
+
+
+A 64-bit signed integer from `-9223372036854775808` to `9223372036854775807`
+
+[#org_apache_logging_log4j_Level]
+== `org.apache.logging.log4j.Level`
+
+Provider:: `org.apache.logging.log4j:log4j-core`
+
+Represents a logging level.
 NOTE: The Log4j API supports custom levels, the following list contains only the standard ones.
 
-Possible values:
+[#org_apache_logging_log4j_Level-values]
+=== Possible values
 
 `OFF`:: Special level that disables logging.
 No events should be logged at this level.
@@ -66,23 +87,36 @@ No events should be logged at this level.
 `DEBUG`:: A general debugging event
 `TRACE`:: A fine-grained debug message, typically capturing the flow through the application
 `ALL`:: Special level indicating all events should be logged
-|[[org.apache.logging.log4j.core.Filter.Result]]
-o.a.l.l.core.Filter.Result
-a|The result that can returned from a filter method call.
 
-Possible values:
+[#org_apache_logging_log4j_core_Filter_Result]
+== `org.apache.logging.log4j.core.Filter.Result`
+
+Provider:: `org.apache.logging.log4j:log4j-core`
+
+The result that can returned from a filter method call.
+
+[#org_apache_logging_log4j_core_Filter_Result-values]
+=== Possible values
 
 `ACCEPT`:: The event will be processed without further filtering based on the log Level.
 `NEUTRAL`:: No decision could be made, further filtering should occur.
 `DENY`:: The event should not be processed.
-|[[org.apache.logging.log4j.core.appender.ConsoleAppender.Target]]
-o.a.l.l.core.appender.ConsoleAppender.Target
-a|Specifies the target of a console appender.
 
-Possible values:
+[#org_apache_logging_log4j_core_appender_ConsoleAppender_Target]
+== `org.apache.logging.log4j.core.appender.ConsoleAppender.Target`
+
+Provider:: `org.apache.logging.log4j:log4j-core`
+
+Specifies the target of a console appender.
+
+[#org_apache_logging_log4j_core_appender_ConsoleAppender_Target-values]
+=== Possible values
 
 `SYSTEM_OUT`:: Logs to the standard output.
 `SYSTEM_ERR`:: Logs to the standard error.
-|[[short]]
-short
-a|A 16-bit signed integer from `-32768` to `32767`
+
+[#short]
+== `short`
+
+
+A 16-bit signed integer from `-32768` to `32767`
diff --git a/log4j-docgen/src/test/resources/templates/interface.adoc.ftl b/log4j-docgen/src/test/resources/templates/interface.adoc.ftl
index 415107c..5302097 100644
--- a/log4j-docgen/src/test/resources/templates/interface.adoc.ftl
+++ b/log4j-docgen/src/test/resources/templates/interface.adoc.ftl
@@ -15,19 +15,25 @@
   See the License for the specific language governing permissions and
   limitations under the License.
 -->
+<#-- @ftlvariable name="sourcedType" type="org.apache.logging.log4j.docgen.model.ArtifactSourcedType" -->
+<#assign type = sourcedType.type/>
 <#-- @ftlvariable name="type" type="org.apache.logging.log4j.docgen.model.AbstractType" -->
 <#-- @ftlvariable name="lookup" type="org.apache.logging.log4j.docgen.generator.TypeLookup" -->
 <#include "license.adoc.ftl">
+[#${type.className?replace('.', '_')}]
 = ${type.className?keep_after_last('.')}
-<#if type.description??>
 
+Class:: `${type.className}`
+<#if sourcedType.groupId?has_content && sourcedType.artifactId?has_content>
+Provider:: `${sourcedType.groupId}:${sourcedType.artifactId}`
+
+</#if><#if type.description??>
 ${type.description.text}
 </#if>
 
+[#${type.className?replace('.', '_')}-implementations]
 == Known implementations
-<#if type.implementations??>
 
-    <#list type.implementations as impl>
+<#list type.implementations as impl>
 * xref:${impl}.adoc[${impl?contains('.')?then(impl?keep_after_last('.'), impl)}]
-    </#list>
-</#if>
+</#list>
diff --git a/log4j-docgen/src/test/resources/templates/plugin.adoc.ftl b/log4j-docgen/src/test/resources/templates/plugin.adoc.ftl
index ab4b72b..f219996 100644
--- a/log4j-docgen/src/test/resources/templates/plugin.adoc.ftl
+++ b/log4j-docgen/src/test/resources/templates/plugin.adoc.ftl
@@ -15,20 +15,28 @@
   See the License for the specific language governing permissions and
   limitations under the License.
 -->
+<#-- @ftlvariable name="sourcedType" type="org.apache.logging.log4j.docgen.model.ArtifactSourcedType" -->
+<#assign type = sourcedType.type/>
 <#-- @ftlvariable name="type" type="org.apache.logging.log4j.docgen.PluginType" -->
 <#-- @ftlvariable name="lookup" type="org.apache.logging.log4j.docgen.generator.TypeLookup" -->
 <#include "license.adoc.ftl">
+[#${type.className?replace('.', '_')}]
 = ${type.name}
 
-${type.description.text}
+Class:: `${type.className}`
+<#if sourcedType.groupId?has_content && sourcedType.artifactId?has_content>
+Provider:: `${sourcedType.groupId}:${sourcedType.artifactId}`
 
+</#if>${type.description.text}
+
+[#${type.className?replace('.', '_')}-XML-snippet]
 == XML snippet
 [source, xml]
 ----
 <#assign tag><${type.name} </#assign>
 <#assign indent = tag?replace('.', ' ', 'r')/>
 <#assign has_elements = type.elements?size != 0/>
-<#if type.attributes?size == 0>
+<#if !type.attributes?has_content>
 <${type.name}/>
     <#else>
         <#list type.attributes?sort_by('name') as attr>
@@ -40,21 +48,22 @@ ${indent}${attr.name}="${attr.defaultValue!}"${attr?is_last?then(has_elements?th
         </#list>
         <#if has_elements>
             <#list type.elements as element>
-                <#assign element_type = lookup[element.type]/>
+                <#assign element_type = lookup[element.type].type/>
 <#-- @ftlvariable name="element_type" type="org.apache.logging.log4j.docgen.model.AbstractType" -->
-                <#if element_type.name?? && element_type.implementations?size == 0>
+                <#if element_type.name?? && !element_type.implementations?has_content>
 <#-- @ftlvariable name="element_type" type="org.apache.logging.log4j.docgen.model.PluginType" -->
     <${element_type.name}/>
-                    <#else>
-    ... ${(element.multiplicity == '*')?then('multiple ' + element.type?keep_after_last('.') + ' elements', 'one ' + element.type?keep_after_last('.') + ' element')} ...
+                <#else>
+    <a-${element.type?keep_after_last('.')}-implementation/><#if element.multiplicity == '*'><!-- multiple occurrences allowed --></#if>
                 </#if>
             </#list>
 </${type.name}>
         </#if>
 </#if>
 ----
-<#if type.attributes?size != 0>
+<#if type.attributes?has_content>
 
+[#${type.className?replace('.', '_')}-attributes]
 == Attributes
 
 Required attributes are in **bold face**.
@@ -65,16 +74,17 @@ Required attributes are in **bold face**.
 
     <#list type.attributes?sort_by('name') as attr>
 |${attr.required?then('**', '')}${attr.name}${attr.required?then('**', '')}
-|xref:../scalars.adoc#${attr.type}[${attr.type?contains('.')?then(attr.type?keep_after_last('.'), attr.type)}]
+|xref:../../scalars.adoc#${attr.type?replace('.', '_')}[${attr.type?contains('.')?then(attr.type?keep_after_last('.'), attr.type)}]
 |${attr.defaultValue!}
 a|${attr.description.text}
 
     </#list>
 |===
 </#if>
-<#if type.elements?size != 0>
+<#if has_elements>
 
-== Nested Components
+[#${type.className?replace('.', '_')}-components]
+== Nested components
 
 Required components are in **bold face**.
 
@@ -83,15 +93,16 @@ Required components are in **bold face**.
 |Tag|Type|Description
 
     <#list type.elements?sort_by('type') as element>
-|${element.required?then('**', '') + (lookup[element.type].name!'N/A') + element.required?then('**', '')}
+|${element.required?then('**', '') + (lookup[element.type].type.name!'N/A') + element.required?then('**', '')}
 |xref:${element.type}.adoc[${element.type?contains('.')?then(element.type?keep_after_last('.'), element.type)}]
 a|${element.description.text}
 
     </#list>
 |===
 </#if>
-<#if type.implementations?size != 0>
+<#if type.implementations?has_content>
 
+[#${type.className?replace('.', '_')}-implementations]
 == Known implementations
 
     <#list type.implementations as impl>
diff --git a/log4j-docgen/src/test/resources/templates/scalars.adoc.ftl b/log4j-docgen/src/test/resources/templates/scalars.adoc.ftl
index 6e646c6..5dde191 100644
--- a/log4j-docgen/src/test/resources/templates/scalars.adoc.ftl
+++ b/log4j-docgen/src/test/resources/templates/scalars.adoc.ftl
@@ -15,25 +15,28 @@ 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.
 -->
-<#-- @ftlvariable name="scalars" type="org.apache.logging.log4j.docgen.model.ScalarType[]" -->
+<#-- @ftlvariable name="sourcedTypes" type="org.apache.logging.log4j.docgen.generator.ArtifactSourcedType[]" -->
 <#include "license.adoc.ftl">
+[#type-converters]
 = Type converters
 
 Type converter plugins are used to convert simple `String` values into other types.
+<#list sourcedTypes as sourcedType>
+<#assign scalar = sourcedType.type/>
+<#-- @ftlvariable name="scalar" type="org.apache.logging.log4j.docgen.model.ScalarType" -->
 
-== Available converters
+[#${scalar.className?replace('.', '_')}]
+== `${scalar.className}`
 
-[cols="1m,2"]
-|===
-|Type|Description
+    <#if sourcedType.groupId?has_content && sourcedType.artifactId?has_content>
+Provider:: `${sourcedType.groupId}:${sourcedType.artifactId}`
+    </#if>
 
-<#list scalars as scalar>
-|[[${scalar.className}]]
-${scalar.className?replace('org.apache.logging.log4j', 'o.a.l.l')}
-a|${scalar.description.text}
+${scalar.description.text}
     <#if scalar.values?size != 0>
 
-Possible values:
+[#${scalar.className?replace('.', '_')}-values]
+=== Possible values
 
         <#list scalar.values as value>
 `${value.name}`:: ${value.description.text}