TTLLFormatterBuilder.java

package io.jstach.rainbowgum.format;

import io.jstach.rainbowgum.LogProperties;
import io.jstach.rainbowgum.LogProperty;

/**
 * Builder to create {@link io.jstach.rainbowgum.LogFormatter }.
 * The TTLL (Time, Thread, Level, Logger) text format, the default encoder, with each
 * part configurable, see {@link TTLL}.
 * <p>
 * The defaults produce <code>12:00:00.123 [main] INFO  com.example.App - hello</code>
 * followed by any stack trace. Parts set to none are left out along with their
 * separating space. As properties of the encoder, each part is one of the lowercase
 * names of its {@link TTLL} enum, for example
 * <code>logging.appender.console.encoder.ttll.keyValues=json5</code>; keyValues
 * accepts <code>json</code> and <code>json5</code> as well as <code>true</code>
 * (percent encoding) and <code>false</code> (none). The timestamp property also
 * accepts a DateTimeFormatter pattern in UTC. The color property decides whether to
 * color and the theme property which colors; when neither is set the rainbowgum theme
 * is used if the console supports ANSI.
 * <table class="table">
 * <caption><strong>Properties retrieved from LogProperties</strong></caption>
 * <tr>
 * <th>Property Pattern</th>
 * <th>Type</th>
 * <th>Required</th>
 * <th>Default</th>
 * <th>Description</th>
 * </tr>
 * <tr>
 * <td><code>{@value PROPERTY_timestamp }</code></td>
 * <td><code>String (converted)</code></td>
 * <td><code>true</code></td>
 * <td>{@link io.jstach.rainbowgum.format.TTLL.TimestampFormat#TTLL ttll}</td>
 * <td>Time formatter, see {@link TTLL.TimestampFormat}.</td>
 * </tr>
 * <tr>
 * <td><code>{@value PROPERTY_thread }</code></td>
 * <td><code>String (converted)</code></td>
 * <td><code>true</code></td>
 * <td>{@link io.jstach.rainbowgum.format.TTLL.ThreadFormat#NAME name}</td>
 * <td>Thread formatter in square brackets, see {@link TTLL.ThreadFormat}.</td>
 * </tr>
 * <tr>
 * <td><code>{@value PROPERTY_level }</code></td>
 * <td><code>String (converted)</code></td>
 * <td><code>true</code></td>
 * <td>{@link io.jstach.rainbowgum.format.TTLL.LevelFormat#PADDED padded}</td>
 * <td>Level formatter, see {@link TTLL.LevelFormat}.</td>
 * </tr>
 * <tr>
 * <td><code>{@value PROPERTY_logger }</code></td>
 * <td><code>String (converted)</code></td>
 * <td><code>true</code></td>
 * <td>{@link io.jstach.rainbowgum.format.TTLL.LoggerFormat#FULL full}</td>
 * <td>Logger name formatter, see {@link TTLL.LoggerFormat}.</td>
 * </tr>
 * <tr>
 * <td><code>{@value PROPERTY_keyValues }</code></td>
 * <td><code>String (converted)</code></td>
 * <td><code>true</code></td>
 * <td>{@link io.jstach.rainbowgum.format.TTLL.KeyValuesFormat#PERCENT percent}</td>
 * <td>Key values formatter in braces, see {@link TTLL.KeyValuesFormat}.</td>
 * </tr>
 * <tr>
 * <td><code>{@value PROPERTY_color }</code></td>
 * <td><code>String (converted)</code></td>
 * <td><code>false</code></td>
 * <td><code>null</code></td>
 * <td>Whether to color, see {@link TTLL.ColorMode}.</td>
 * </tr>
 * <tr>
 * <td><code>{@value PROPERTY_theme }</code></td>
 * <td><code>String (converted)</code></td>
 * <td><code>false</code></td>
 * <td><code>null</code></td>
 * <td>ANSI color theme, see {@link TTLL.ColorTheme}.</td>
 * </tr>
 * <tr>
 * <td><code>{@value PROPERTY_keyValuesWhenEmpty }</code></td>
 * <td><code>String (converted)</code></td>
 * <td><code>false</code></td>
 * <td><code>null</code></td>
 * <td>Braces for no key values, see {@link TTLL.KeyValuesWhenEmpty}.</td>
 * </tr>
 * </table>
 */
public final class TTLLFormatterBuilder implements io.jstach.rainbowgum.LogBuilder<TTLLFormatterBuilder,io.jstach.rainbowgum.LogFormatter> {

	/**
	 * The properties to be retrieved from config will have
	 * this prefix.
	 */
	static final String PROPERTY_PREFIX = "logging.appender.{name}.encoder.ttll.";
	
	/**
	 * <code>{@value #PROPERTY_timestamp } = io.jstach.rainbowgum.LogFormatter</code> Time formatter, see {@link TTLL.TimestampFormat}.
	 */
	static final String PROPERTY_timestamp = PROPERTY_PREFIX + "timestamp";
	/**
	 * <code>{@value #PROPERTY_thread } = io.jstach.rainbowgum.LogFormatter</code> Thread formatter in square brackets, see {@link TTLL.ThreadFormat}.
	 */
	static final String PROPERTY_thread = PROPERTY_PREFIX + "thread";
	/**
	 * <code>{@value #PROPERTY_level } = io.jstach.rainbowgum.LogFormatter</code> Level formatter, see {@link TTLL.LevelFormat}.
	 */
	static final String PROPERTY_level = PROPERTY_PREFIX + "level";
	/**
	 * <code>{@value #PROPERTY_logger } = io.jstach.rainbowgum.LogFormatter</code> Logger name formatter, see {@link TTLL.LoggerFormat}.
	 */
	static final String PROPERTY_logger = PROPERTY_PREFIX + "logger";
	/**
	 * <code>{@value #PROPERTY_keyValues } = io.jstach.rainbowgum.LogFormatter</code> Key values formatter in braces, see {@link TTLL.KeyValuesFormat}.
	 */
	static final String PROPERTY_keyValues = PROPERTY_PREFIX + "keyValues";
	/**
	 * <code>{@value #PROPERTY_color } = io.jstach.rainbowgum.format.TTLL.ColorMode</code> Whether to color, see {@link TTLL.ColorMode}.
	 */
	static final String PROPERTY_color = PROPERTY_PREFIX + "color";
	/**
	 * <code>{@value #PROPERTY_theme } = io.jstach.rainbowgum.format.TTLL.ColorTheme</code> ANSI color theme, see {@link TTLL.ColorTheme}.
	 */
	static final String PROPERTY_theme = PROPERTY_PREFIX + "theme";
	/**
	 * <code>{@value #PROPERTY_keyValuesWhenEmpty } = io.jstach.rainbowgum.format.TTLL.KeyValuesWhenEmpty</code> Braces for no key values, see {@link TTLL.KeyValuesWhenEmpty}.
	 */
	static final String PROPERTY_keyValuesWhenEmpty = PROPERTY_PREFIX + "keyValuesWhenEmpty";
	
	private final String propertyPrefix;
	final String property_timestamp;
	final String property_thread;
	final String property_level;
	final String property_logger;
	final String property_keyValues;
	final String property_color;
	final String property_theme;
	final String property_keyValuesWhenEmpty;
	
	private final java.lang.String name;
	private io.jstach.rainbowgum.LogFormatter timestamp = io.jstach.rainbowgum.format.TTLLFormatter.DEFAULT_TIMESTAMP;
	private io.jstach.rainbowgum.LogFormatter thread = io.jstach.rainbowgum.format.TTLLFormatter.DEFAULT_THREAD;
	private io.jstach.rainbowgum.LogFormatter level = io.jstach.rainbowgum.format.TTLLFormatter.DEFAULT_LEVEL;
	private io.jstach.rainbowgum.LogFormatter logger = io.jstach.rainbowgum.format.TTLLFormatter.DEFAULT_LOGGER;
	private io.jstach.rainbowgum.LogFormatter keyValues = io.jstach.rainbowgum.format.TTLLFormatter.DEFAULT_KEY_VALUES;
	private io.jstach.rainbowgum.format.TTLL.@org.jspecify.annotations.Nullable ColorMode color = null;
	private io.jstach.rainbowgum.format.TTLL.@org.jspecify.annotations.Nullable ColorTheme theme = null;
	private io.jstach.rainbowgum.format.TTLL.@org.jspecify.annotations.Nullable KeyValuesWhenEmpty keyValuesWhenEmpty = null;
	private io.jstach.rainbowgum.@org.jspecify.annotations.Nullable ServiceRegistry serviceRegistry;
	
	/**
	 * Create a builder for {@code io.jstach.rainbowgum.LogFormatter }.
	 * @param name will fill <code>{name}</code> in <code>logging.appender.{name}.encoder.ttll.</code>. Encoder name, used for property lookup.
	 */
	public TTLLFormatterBuilder(
			java.lang.String name
			) {
		try {
			java.util.Map<String,String> prefixParameters = java.util.Map.of(
					"name", name
				);
			this.propertyPrefix = LogProperties.interpolateKey(PROPERTY_PREFIX, prefixParameters);
			this.name = name;
			property_timestamp = LogProperties.interpolateKey(PROPERTY_timestamp, prefixParameters);
			property_thread = LogProperties.interpolateKey(PROPERTY_thread, prefixParameters);
			property_level = LogProperties.interpolateKey(PROPERTY_level, prefixParameters);
			property_logger = LogProperties.interpolateKey(PROPERTY_logger, prefixParameters);
			property_keyValues = LogProperties.interpolateKey(PROPERTY_keyValues, prefixParameters);
			property_color = LogProperties.interpolateKey(PROPERTY_color, prefixParameters);
			property_theme = LogProperties.interpolateKey(PROPERTY_theme, prefixParameters);
			property_keyValuesWhenEmpty = LogProperties.interpolateKey(PROPERTY_keyValuesWhenEmpty, prefixParameters);
		}
		catch (IllegalArgumentException e) {
			throw LogProperty.ValidationException.of(this.getClass(), e);
		}
	}


	/**
	 * Sets serviceRegistry.
	 * Where themes chosen by name in properties are looked up.
	 * Default is <code>null</code>.
	 * @param serviceRegistry not configurable through properties Where themes chosen by name in properties are looked up.
	 * @return this builder.
	 */
	public TTLLFormatterBuilder serviceRegistry(io.jstach.rainbowgum.@org.jspecify.annotations.Nullable ServiceRegistry serviceRegistry) {
		this.serviceRegistry = serviceRegistry;
		return this;
	}

	
	/**
	 * Sets <strong>required</strong> timestamp.
	 * Time formatter, see {@link TTLL.TimestampFormat}.
	 * Default is {@link io.jstach.rainbowgum.format.TTLL.TimestampFormat#TTLL ttll}.
	 * @param timestamp <code>{@value #PROPERTY_timestamp } = io.jstach.rainbowgum.LogFormatter</code> Time formatter, see {@link TTLL.TimestampFormat}.
	 * @return this builder.
	 */
	public TTLLFormatterBuilder timestamp(io.jstach.rainbowgum.LogFormatter timestamp) {
		this.timestamp = timestamp;
		return this;
	}
	
	/**
	 * Sets <strong>required</strong> thread.
	 * Thread formatter in square brackets, see {@link TTLL.ThreadFormat}.
	 * Default is {@link io.jstach.rainbowgum.format.TTLL.ThreadFormat#NAME name}.
	 * @param thread <code>{@value #PROPERTY_thread } = io.jstach.rainbowgum.LogFormatter</code> Thread formatter in square brackets, see {@link TTLL.ThreadFormat}.
	 * @return this builder.
	 */
	public TTLLFormatterBuilder thread(io.jstach.rainbowgum.LogFormatter thread) {
		this.thread = thread;
		return this;
	}
	
	/**
	 * Sets <strong>required</strong> level.
	 * Level formatter, see {@link TTLL.LevelFormat}.
	 * Default is {@link io.jstach.rainbowgum.format.TTLL.LevelFormat#PADDED padded}.
	 * @param level <code>{@value #PROPERTY_level } = io.jstach.rainbowgum.LogFormatter</code> Level formatter, see {@link TTLL.LevelFormat}.
	 * @return this builder.
	 */
	public TTLLFormatterBuilder level(io.jstach.rainbowgum.LogFormatter level) {
		this.level = level;
		return this;
	}
	
	/**
	 * Sets <strong>required</strong> logger.
	 * Logger name formatter, see {@link TTLL.LoggerFormat}.
	 * Default is {@link io.jstach.rainbowgum.format.TTLL.LoggerFormat#FULL full}.
	 * @param logger <code>{@value #PROPERTY_logger } = io.jstach.rainbowgum.LogFormatter</code> Logger name formatter, see {@link TTLL.LoggerFormat}.
	 * @return this builder.
	 */
	public TTLLFormatterBuilder logger(io.jstach.rainbowgum.LogFormatter logger) {
		this.logger = logger;
		return this;
	}
	
	/**
	 * Sets <strong>required</strong> keyValues.
	 * Key values formatter in braces, see {@link TTLL.KeyValuesFormat}.
	 * Default is {@link io.jstach.rainbowgum.format.TTLL.KeyValuesFormat#PERCENT percent}.
	 * @param keyValues <code>{@value #PROPERTY_keyValues } = io.jstach.rainbowgum.LogFormatter</code> Key values formatter in braces, see {@link TTLL.KeyValuesFormat}.
	 * @return this builder.
	 */
	public TTLLFormatterBuilder keyValues(io.jstach.rainbowgum.LogFormatter keyValues) {
		this.keyValues = keyValues;
		return this;
	}
	
	/**
	 * Sets color.
	 * Whether to color, see {@link TTLL.ColorMode}.
	 * Default is <code>null</code>.
	 * @param color <code>{@value #PROPERTY_color } = io.jstach.rainbowgum.format.TTLL.ColorMode</code> Whether to color, see {@link TTLL.ColorMode}.
	 * @return this builder.
	 */
	public TTLLFormatterBuilder color(io.jstach.rainbowgum.format.TTLL.@org.jspecify.annotations.Nullable ColorMode color) {
		this.color = color;
		return this;
	}
	
	/**
	 * Sets theme.
	 * ANSI color theme, see {@link TTLL.ColorTheme}.
	 * Default is <code>null</code>.
	 * @param theme <code>{@value #PROPERTY_theme } = io.jstach.rainbowgum.format.TTLL.ColorTheme</code> ANSI color theme, see {@link TTLL.ColorTheme}.
	 * @return this builder.
	 */
	public TTLLFormatterBuilder theme(io.jstach.rainbowgum.format.TTLL.@org.jspecify.annotations.Nullable ColorTheme theme) {
		this.theme = theme;
		return this;
	}
	
	/**
	 * Sets keyValuesWhenEmpty.
	 * Braces for no key values, see {@link TTLL.KeyValuesWhenEmpty}.
	 * Default is <code>null</code>.
	 * @param keyValuesWhenEmpty <code>{@value #PROPERTY_keyValuesWhenEmpty } = io.jstach.rainbowgum.format.TTLL.KeyValuesWhenEmpty</code> Braces for no key values, see {@link TTLL.KeyValuesWhenEmpty}.
	 * @return this builder.
	 */
	public TTLLFormatterBuilder keyValuesWhenEmpty(io.jstach.rainbowgum.format.TTLL.@org.jspecify.annotations.Nullable KeyValuesWhenEmpty keyValuesWhenEmpty) {
		this.keyValuesWhenEmpty = keyValuesWhenEmpty;
		return this;
	}
	
	/**
	 * Creates {@code io.jstach.rainbowgum.LogFormatter } from this builder.
	 * @return {@code io.jstach.rainbowgum.LogFormatter }.
	 */
	public io.jstach.rainbowgum.LogFormatter build() {
		try {
			return io.jstach.rainbowgum.format.TTLLFormatter.of(
					this.name
					, io.jstach.rainbowgum.LogProperty.require(property_timestamp, this.timestamp)
					, io.jstach.rainbowgum.LogProperty.require(property_thread, this.thread)
					, io.jstach.rainbowgum.LogProperty.require(property_level, this.level)
					, io.jstach.rainbowgum.LogProperty.require(property_logger, this.logger)
					, io.jstach.rainbowgum.LogProperty.require(property_keyValues, this.keyValues)
					, this.color
					, this.theme
					, this.keyValuesWhenEmpty
					, this.serviceRegistry
					);
		}
		catch (IllegalArgumentException | LogProperty.PropertyMissingException e) {
			throw LogProperty.ValidationException.of(this.getClass(), e);
		}
	}
	
	@Override
	public TTLLFormatterBuilder fromProperties(LogProperties properties) {
		var __v = LogProperty.Validator.of(this.getClass());
		var _timestamp = properties.forKey(property_timestamp).ofString().map(_v -> io.jstach.rainbowgum.format.TTLLFormatter.convertTimestamp(_v)).or(this.timestamp).validate(__v);
		var _thread = properties.forKey(property_thread).ofString().map(_v -> io.jstach.rainbowgum.format.TTLLFormatter.convertThread(_v)).or(this.thread).validate(__v);
		var _level = properties.forKey(property_level).ofString().map(_v -> io.jstach.rainbowgum.format.TTLLFormatter.convertLevel(_v)).or(this.level).validate(__v);
		var _logger = properties.forKey(property_logger).ofString().map(_v -> io.jstach.rainbowgum.format.TTLLFormatter.convertLogger(_v)).or(this.logger).validate(__v);
		var _keyValues = properties.forKey(property_keyValues).ofString().map(_v -> io.jstach.rainbowgum.format.TTLLFormatter.convertKeyValues(_v)).or(this.keyValues).validate(__v);
		var _color = properties.forKey(property_color).ofString().map(_v -> io.jstach.rainbowgum.format.TTLLFormatter.convertColor(_v)).or(this.color).validateIfError(__v);
		var _theme = properties.forKey(property_theme).ofString().map(_v -> io.jstach.rainbowgum.format.TTLLFormatter.convertTheme(_v)).or(this.theme).validateIfError(__v);
		var _keyValuesWhenEmpty = properties.forKey(property_keyValuesWhenEmpty).ofString().map(_v -> io.jstach.rainbowgum.format.TTLLFormatter.convertKeyValuesWhenEmpty(_v)).or(this.keyValuesWhenEmpty).validateIfError(__v);
		__v.validate();
		this.timestamp = _timestamp.value();
		this.thread = _thread.value();
		this.level = _level.value();
		this.logger = _logger.value();
		this.keyValues = _keyValues.value();
		this.color = _color.valueOrNull();
		this.theme = _theme.valueOrNull();
		this.keyValuesWhenEmpty = _keyValuesWhenEmpty.valueOrNull();
		return this;
	}
	
	/**
	 * The interpolated property prefix: {@value #PROPERTY_PREFIX}.
	 * @return resolved prefix which should end with a "<code>.</code>".
	 */
	@Override
	public String propertyPrefix() {
		return this.propertyPrefix;
	}
	
}