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

org.eclipse.edc.plugins.autodoc.html.HtmlManifestRenderer Maven / Gradle / Ivy

/*
 *  Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
 *
 *  This program and the accompanying materials are made available under the
 *  terms of the Apache License, Version 2.0 which is available at
 *  https://www.apache.org/licenses/LICENSE-2.0
 *
 *  SPDX-License-Identifier: Apache-2.0
 *
 *  Contributors:
 *       Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation
 *
 */

package org.eclipse.edc.plugins.autodoc.html;

import j2html.TagCreator;
import j2html.tags.Text;
import j2html.tags.specialized.ArticleTag;
import j2html.tags.specialized.CodeTag;
import j2html.tags.specialized.HeadTag;
import j2html.tags.specialized.UlTag;
import org.eclipse.edc.plugins.autodoc.spi.ManifestConverterException;
import org.eclipse.edc.plugins.autodoc.spi.ManifestRenderer;
import org.eclipse.edc.runtime.metamodel.domain.ConfigurationSetting;
import org.eclipse.edc.runtime.metamodel.domain.ModuleType;
import org.eclipse.edc.runtime.metamodel.domain.Service;
import org.eclipse.edc.runtime.metamodel.domain.ServiceReference;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

import static j2html.TagCreator.a;
import static j2html.TagCreator.article;
import static j2html.TagCreator.b;
import static j2html.TagCreator.body;
import static j2html.TagCreator.code;
import static j2html.TagCreator.em;
import static j2html.TagCreator.h2;
import static j2html.TagCreator.h3;
import static j2html.TagCreator.h4;
import static j2html.TagCreator.head;
import static j2html.TagCreator.hr;
import static j2html.TagCreator.html;
import static j2html.TagCreator.li;
import static j2html.TagCreator.nav;
import static j2html.TagCreator.section;
import static j2html.TagCreator.span;
import static j2html.TagCreator.style;
import static j2html.TagCreator.table;
import static j2html.TagCreator.tbody;
import static j2html.TagCreator.td;
import static j2html.TagCreator.th;
import static j2html.TagCreator.thead;
import static j2html.TagCreator.tr;
import static j2html.TagCreator.ul;
import static java.util.stream.Collectors.joining;
import static java.util.stream.IntStream.range;


public class HtmlManifestRenderer implements ManifestRenderer {

    private final OutputStream outputStream;
    private final HeadTag head = head();
    private final UlTag menu = ul();
    private final ArticleTag content = article();

    public HtmlManifestRenderer(OutputStream outputStream) {
        this.outputStream = outputStream;
    }

    @Override
    public void renderDocumentHeader() {
        head.with(style("""
                * {
                  box-sizing: border-box;
                }
                nav {
                  float: left;
                  width: 20%;
                  padding: 20px;
                }
                article {
                  float: left;
                  padding: 20px;
                  width: 80%;
                }
                table {
                  margin: 1em 0;
                  border-collapse: collapse;
                  width: 100%;
                  overflow-x: auto;
                  display: block;
                  font-variant-numeric: lining-nums tabular-nums;
                }
                tbody {
                  margin-top: 0.5em;
                  border-top: 1px solid #1a1a1a;
                  border-bottom: 1px solid #1a1a1a;
                }
                td {
                    padding: 5px
                }
                .odd {
                  background-color: cccccc;
                }
                .even {
                  background-color: eeeeee;
                }
                """));
    }

    @Override
    public void renderModuleHeading(@Nullable String moduleName, @NotNull String modulePath, @NotNull String version) {
        var modulePublishedName = modulePath.split(":")[1];

        var ul = ul();
        if (moduleName != null) {
            ul.with(li(b("Name: "), new Text(moduleName)));
        }
        ul.with(
                li(b("Path: "), code(modulePath)),
                li(b("Version: "), code(version))
        );

        var moduleTitle = h2(modulePublishedName).withId(modulePublishedName);
        content.with(hr())
                .with(a(moduleTitle).withHref("#" + modulePublishedName))
                .with(ul);

        menu.with(li().with(a(modulePublishedName).withHref("#" + modulePublishedName)));
    }

    @Override
    public void renderCategories(List categories) {
        var categoriesString = categories.isEmpty()
                ? "none"
                : categories.stream().filter(Objects::nonNull).filter(it -> !it.isBlank()).collect(joining(", "));

        content.with(em("Categories: " + categoriesString));
    }

    @Override
    public void renderExtensionPoints(List extensionPoints) {
        content.with(h3("Extension Points"));

        if (extensionPoints.isEmpty()) {
            content.with(em("none"));
        } else {
            var list = ul();
            extensionPoints.stream().map(it -> li(em(it.getService()))).forEach(list::with);
            content.with(list);
        }
    }

    @Override
    public void renderExtensionHeading() {

    }

    @Override
    public void renderExtensionHeader(@NotNull String className, @Nullable String name, @Nullable String overview, ModuleType type) {
        var extensionDetail = ul()
                .with(li(b("Class: ")).with(code(className)))
                .with(li(b("Type: ")).with(span(type.getKey())));

        if (overview != null) {
            extensionDetail.with(li(b("Overview: ")).with(span(overview)));
        }

        content.with(h3("Extension " + name)).with(extensionDetail);
    }

    @Override
    public void renderConfigurations(List configuration) {
        content.with(h4("Configuration: "));
        if (configuration.isEmpty()) {
            content.with(em("none"));
        } else {
            var header = tr(th("Key"), th("Required"), th("Type"), th("Default"),
                    th("Pattern"), th("Min"), th("Max"), th("Description"));

            var tbody = tbody();

            range(0, configuration.size()).mapToObj(index -> {
                var setting = configuration.get(index);
                var clazz = index % 2 == 0 ? "even" : "odd";
                return tr(
                        td(code(setting.getKey())),
                        td(setting.isRequired() ? code("x") : null).attr("align", "center"),
                        td(code(setting.getType())).attr("align", "center"),
                        td(code(setting.getDefaultValue())),
                        td(codeOrNull(setting.getPattern())).attr("align", "center"),
                        td(codeOrNull(setting.getMinimum())).attr("align", "right"),
                        td(codeOrNull(setting.getMaximum())).attr("align", "right"),
                        td(setting.getDescription()).attr("width", "40%")
                ).withClass(clazz);
            }).forEach(tbody::with);

            content.with(table().with(thead(header)).with(tbody));
        }
    }

    @Override
    public void renderProvidedServices(List provides) {
        content.with(h4("Provided Services: "));

        if (provides.isEmpty()) {
            content.with(em("none"));
        } else {
            var ul = ul();
            provides.stream().map(Service::getService)
                    .map(TagCreator::code)
                    .map(TagCreator::li)
                    .forEach(ul::with);
            content.with(ul);
        }
    }

    @Override
    public void renderReferencedServices(List references) {
        content.with(h4(new Text("Referenced (injected) Services ("), em("emphasized if required"), new Text("): ")));

        if (references.isEmpty()) {
            content.with(em("none"));
        } else {
            var ul = ul();
            references.stream()
                    .map(service -> {
                        var code = code(service.getService());
                        return service.isRequired() ? em(code) : code;
                    })
                    .map(TagCreator::li)
                    .forEach(ul::with);
            content.with(ul);
        }
    }

    @Override
    public OutputStream finalizeRendering() {
        try {
            var html = html()
                    .with(head)
                    .with(body().with(section(nav().with(menu), content)));
            outputStream.write(html.render().getBytes(StandardCharsets.UTF_8));
        } catch (IOException e) {
            throw new ManifestConverterException(e);
        }
        return outputStream;
    }

    @Nullable
    private CodeTag codeOrNull(Long value) {
        return Optional.ofNullable(value).map(Object::toString).map(TagCreator::code).orElse(null);
    }

    @Nullable
    private CodeTag codeOrNull(String value) {
        return Optional.ofNullable(value).map(TagCreator::code).orElse(null);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy