001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.juneau.console.views.markdown;
018
019import java.util.*;
020
021/**
022 * A pluggable markdown-to-HTML renderer: hand it a markdown string, get back an HTML fragment.
023 *
024 * <p>
025 * This is the extension point of the optional markdown module &mdash; the toolkit's answer to "the way
026 * {@code ViewDef} turns a list of beans into a themed table, hand me a markdown string and give me back HTML".
027 * The interface is deliberately library-neutral: a single {@link #toHtml(String)} method, so any parser can sit
028 * behind it.  The shipped default is {@link CommonmarkMarkdownRenderer} (commonmark-java with the GFM tables
029 * extension enabled), but a consumer can swap in their own parser without the toolkit knowing which library won.
030 *
031 * <h5 class='section'>Selecting an implementation:</h5>
032 * <p>
033 * {@link #resolve()} returns the active renderer via the standard {@link ServiceLoader} SPI mechanism: if a
034 * consumer registers their own provider (a {@code META-INF/services/}
035 * {@code org.apache.juneau.console.views.markdown.MarkdownRenderer} file naming their implementation class),
036 * that provider wins; otherwise the built-in commonmark-java default is used.  The default is intentionally
037 * <b>not</b> registered as a service, so a single consumer-supplied provider is always unambiguous.
038 *
039 * <h5 class='section'>Example:</h5>
040 * <p class='bjava'>
041 *    <jc>// Render markdown to an HTML fragment using whichever renderer is active.</jc>
042 *    MarkdownRenderer <jv>renderer</jv> = MarkdownRenderer.<jsm>resolve</jsm>();
043 *    String <jv>html</jv> = <jv>renderer</jv>.toHtml(<js>"# Runbook\n\nStep one."</js>);
044 * </p>
045 *
046 * <p>
047 * The rendered HTML is intended to be dropped inside a <c>.jc-prose</c> container so it picks up the console's
048 * prose typography.  Raw HTML in the source is escaped; {@code javascript:} / {@code data:} URLs are stripped.
049 * The row-detail runtime still allowlist-copies the fragment (never {@code innerHTML}) when
050 * {@code FieldFormat.MARKDOWN} is set.
051 *
052 * <h5 class='section'>See Also:</h5>
053 * <ul>
054 *    <li class='jc'>{@link CommonmarkMarkdownRenderer}
055 * </ul>
056 *
057 * @since 10.0.0
058 */
059public interface MarkdownRenderer {
060
061   /**
062    * Renders the specified markdown source to an HTML fragment.
063    *
064    * @param markdown The markdown source. Must not be <jk>null</jk> (an empty string renders to an empty fragment).
065    * @return The rendered HTML fragment.
066    * @throws IllegalArgumentException If <jv>markdown</jv> is <jk>null</jk>.
067    */
068   String toHtml(String markdown);
069
070   /**
071    * Resolves the active renderer: the first {@link ServiceLoader}-registered provider if one exists, otherwise the
072    * built-in commonmark-java default.
073    *
074    * @return The active renderer. Never <jk>null</jk>.
075    */
076   static MarkdownRenderer resolve() {
077      return resolve(ServiceLoader.load(MarkdownRenderer.class));
078   }
079
080   /**
081    * Resolution seam used by {@link #resolve()}, exposed package-private so both the provider-found and
082    * default-fallback branches can be exercised without touching the JVM-wide {@link ServiceLoader} registry.
083    *
084    * @param providers The candidate providers (a live {@link ServiceLoader} in production).
085    * @return The first provider if any, otherwise a new {@link CommonmarkMarkdownRenderer}.
086    */
087   static MarkdownRenderer resolve(Iterable<MarkdownRenderer> providers) {
088      var i = providers.iterator();
089      return i.hasNext() ? i.next() : new CommonmarkMarkdownRenderer();
090   }
091}