/*
* 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("(? 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.
*
*
*
* 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.
*
*
*
* 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));
}
}