/*
|
* The contents of this file are subject to the terms of the Common Development and
|
* Distribution License (the License). You may not use this file except in compliance with the
|
* License.
|
*
|
* You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
|
* specific language governing permission and limitations under the License.
|
*
|
* When distributing Covered Software, include this CDDL Header Notice in each file and include
|
* the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
|
* Header, with the fields enclosed by brackets [] replaced by your own identifying
|
* information: "Portions Copyright [year] [name of copyright owner]".
|
*
|
* Copyright 2015 ForgeRock AS.
|
* Portions Copyright 2026 3A Systems, LLC.
|
*/
|
package com.forgerock.opendj.cli;
|
|
import freemarker.template.Configuration;
|
import freemarker.template.Template;
|
import freemarker.template.TemplateExceptionHandler;
|
|
import java.io.ByteArrayOutputStream;
|
import java.io.OutputStreamWriter;
|
import java.io.Writer;
|
import java.util.Map;
|
import java.util.regex.Pattern;
|
|
/**
|
* This class provides utility functions to help generate reference documentation.
|
*/
|
public final class DocGenerationHelper {
|
|
/** Prevent instantiation. */
|
private DocGenerationHelper() {
|
// Do nothing.
|
}
|
|
/** FreeMarker template configuration. */
|
private static Configuration configuration;
|
|
/**
|
* An AsciiDoc attribute reference, such as {@code {name}}, that no backslash escapes.
|
* Word characters are Unicode ones, as in Asciidoctor.
|
*/
|
private static final Pattern ATTRIBUTE_REFERENCE =
|
Pattern.compile("(?<!\\\\)(\\{\\w[\\w-]*\\})", Pattern.UNICODE_CHARACTER_CLASS);
|
|
/**
|
* Gets a FreeMarker configuration for applying templates.
|
*
|
* @return A FreeMarker configuration.
|
*/
|
private static Configuration getConfiguration() {
|
if (configuration == null) {
|
configuration = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
|
configuration.setClassForTemplateLoading(DocGenerationHelper.class, "/templates");
|
configuration.setDefaultEncoding("UTF-8");
|
configuration.setTemplateExceptionHandler(TemplateExceptionHandler.DEBUG_HANDLER);
|
}
|
return configuration;
|
}
|
|
/**
|
* Appends the String result from applying a FreeMarker template.
|
*
|
* @param builder Append the result to this.
|
* @param template The name of a template file found in {@code resources/templates/}.
|
* @param map The map holding the data to use in the template.
|
*/
|
public static void applyTemplate(StringBuilder builder, final String template, final Map<String, Object> map) {
|
// FixMe: This method is public so it can be used by the SubCommandUsageHandler
|
// in org.forgerock.opendj.config.dsconfig.DSConfig.
|
|
// FreeMarker requires a configuration to find the template.
|
configuration = getConfiguration();
|
|
// FreeMarker takes the data and a Writer to process the template.
|
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
Writer writer = new OutputStreamWriter(outputStream)) {
|
Template configurationTemplate = configuration.getTemplate(template);
|
configurationTemplate.process(map, writer);
|
builder.append(escapeAttributeReferences(outputStream.toString()));
|
} catch (Exception e) {
|
throw new RuntimeException(e.getMessage(), e);
|
}
|
}
|
|
/**
|
* Escapes the AsciiDoc attribute references in generated reference text.
|
*
|
* <br>
|
*
|
* The generated reference refers to no AsciiDoc attribute: a {@code {name}} in it is the placeholder
|
* of an option value, written as is in the messages, which AsciiDoc would read as a reference
|
* to a missing attribute. A reference that is already escaped is left as is,
|
* so the result of a template can go through another template that includes it.
|
*
|
* @param text The generated AsciiDoc text.
|
* @return The text with each attribute reference escaped by a backslash.
|
*/
|
static String escapeAttributeReferences(final String text) {
|
return ATTRIBUTE_REFERENCE.matcher(text).replaceAll("\\\\$1");
|
}
|
|
/**
|
* Returns an option synopsis.
|
*
|
* <br>
|
*
|
* Note: The synopsis might contain characters that must be escaped in XML.
|
*
|
* @param argument The argument option.
|
* @return A synopsis.
|
*/
|
static String getOptionSynopsis(final Argument argument) {
|
StringBuilder builder = new StringBuilder();
|
|
final Character shortID = argument.getShortIdentifier();
|
if (shortID != null) {
|
builder.append("-").append(shortID.charValue());
|
}
|
final String longID = argument.getLongIdentifier();
|
if (shortID != null && longID != null) {
|
builder.append(" | ");
|
}
|
if (longID != null) {
|
builder.append("--").append(longID);
|
}
|
if (argument.needsValue()) {
|
builder.append(" ").append(argument.getValuePlaceholder());
|
}
|
|
return builder.toString();
|
}
|
|
/**
|
* Returns true when the argument handles properties.
|
*
|
* @param argument The argument.
|
* @return True if the argument handles properties.
|
*/
|
public static boolean doesHandleProperties(final Argument argument) {
|
// FixMe: This method is public so it can be used by the SubCommandUsageHandler
|
// in org.forgerock.opendj.config.dsconfig.DSConfig.
|
|
final String id = argument.getLongIdentifier();
|
return ("add".equals(id) || "remove".equals(id) || "reset".equals(id) || "set".equals(id));
|
}
|
}
|