/*
 * Copyright (c) 2002-2018, the original author or authors.
 *
 * This software is distributable under the BSD license. See the terms of the
 * BSD license in the documentation provided with this software.
 *
 * http://www.opensource.org/licenses/bsd-license.php
 */
package jdk.internal.org.jline.utils;

import java.util.Locale;
import java.util.function.Function;
//import java.util.logging.Level;
//import java.util.logging.Logger;

import static java.util.Objects.requireNonNull;
import static jdk.internal.org.jline.utils.AttributedStyle.*;

// TODO: document style specification

Resolves named (or source-referenced) AttributedStyle.
Since:3.6
/** * Resolves named (or source-referenced) {@link AttributedStyle}. * * @since 3.6 */
public class StyleResolver { // private static final Logger log = Logger.getLogger(StyleResolver.class.getName()); private final Function<String, String> source; public StyleResolver(final Function<String, String> source) { this.source = requireNonNull(source); }
Returns the color identifier for the given name.

Bright color can be specified with: !<color> or bright-<color>.

Full xterm256 color can be specified with: ~<color>.

Params:
  • name – the name of the color
Returns:color code, or null if unable to determine.
/** * Returns the color identifier for the given name. * <p> * Bright color can be specified with: {@code !<color>} or {@code bright-<color>}. * <p> * Full xterm256 color can be specified with: {@code ~<color>}. * * @param name the name of the color * @return color code, or {@code null} if unable to determine. */
private static Integer color(String name) { int flags = 0; name = name.toLowerCase(Locale.US); // extract bright flag from color name if (name.charAt(0) == '!') { name = name.substring(1, name.length()); flags = BRIGHT; } else if (name.startsWith("bright-")) { name = name.substring(7, name.length()); flags = BRIGHT; } else if (name.charAt(0) == '~') { try { // TODO: if the palette is not the default one, should be // TODO: translate into 24-bits first and let the #toAnsi() call // TODO: round with the current palette ? name = name.substring(1, name.length()); return Colors.rgbColor(name); } catch (IllegalArgumentException e) { // log.warning("Invalid style-color name: " + name); return null; } } switch (name) { case "black": case "k": return flags + BLACK; case "red": case "r": return flags + RED; case "green": case "g": return flags + GREEN; case "yellow": case "y": return flags + YELLOW; case "blue": case "b": return flags + BLUE; case "magenta": case "m": return flags + MAGENTA; case "cyan": case "c": return flags + CYAN; case "white": case "w": return flags + WHITE; } return null; } // TODO: could consider a small cache to reduce style calculations?
Resolve the given style specification.

If for some reason the specification is invalid, then AttributedStyle.DEFAULT will be used.

Params:
  • spec – the specification
Returns:the style
/** * Resolve the given style specification. * <p> * If for some reason the specification is invalid, then {@link AttributedStyle#DEFAULT} will be used. * * @param spec the specification * @return the style */
public AttributedStyle resolve(final String spec) { requireNonNull(spec); // if (log.isLoggable(Level.FINEST)) { // log.finest("Resolve: " + spec); // } int i = spec.indexOf(":-"); if (i != -1) { String[] parts = spec.split(":-"); return resolve(parts[0].trim(), parts[1].trim()); } return apply(DEFAULT, spec); }
Resolve the given style specification.

If this resolves to AttributedStyle.DEFAULT then given default specification is used if non-null.

Params:
  • spec – the specification
  • defaultSpec – the default specifiaction
Returns:the style
/** * Resolve the given style specification. * <p> * If this resolves to {@link AttributedStyle#DEFAULT} then given default specification is used if non-null. * * @param spec the specification * @param defaultSpec the default specifiaction * @return the style */
public AttributedStyle resolve(final String spec, final String defaultSpec) { requireNonNull(spec); // if (log.isLoggable(Level.FINEST)) { // log.finest(String.format("Resolve: %s; default: %s", spec, defaultSpec)); // } AttributedStyle style = apply(DEFAULT, spec); if (style == DEFAULT && defaultSpec != null) { style = apply(style, defaultSpec); } return style; }
Apply style specification.
Params:
  • style – the style to apply to
  • spec – the specification
Returns:the new style
/** * Apply style specification. * * @param style the style to apply to * @param spec the specification * @return the new style */
private AttributedStyle apply(AttributedStyle style, final String spec) { // if (log.isLoggable(Level.FINEST)) { // log.finest("Apply: " + spec); // } for (String item : spec.split(",")) { item = item.trim(); if (item.isEmpty()) { continue; } if (item.startsWith(".")) { style = applyReference(style, item); } else if (item.contains(":")) { style = applyColor(style, item); } else if (item.matches("[0-9]+(;[0-9]+)*")) { style = applyAnsi(style, item); } else { style = applyNamed(style, item); } } return style; } private AttributedStyle applyAnsi(final AttributedStyle style, final String spec) { // if (log.isLoggable(Level.FINEST)) { // log.finest("Apply-ansi: " + spec); // } return new AttributedStringBuilder() .style(style) .ansiAppend("\033[" + spec + "m") .style(); }
Apply source-referenced named style.
Params:
  • style – the style to apply to
  • spec – the specification
Returns:the new style
/** * Apply source-referenced named style. * * @param style the style to apply to * @param spec the specification * @return the new style */
private AttributedStyle applyReference(final AttributedStyle style, final String spec) { // if (log.isLoggable(Level.FINEST)) { // log.finest("Apply-reference: " + spec); // } if (spec.length() == 1) { // log.warning("Invalid style-reference; missing discriminator: " + spec); } else { String name = spec.substring(1, spec.length()); String resolvedSpec = source.apply(name); if (resolvedSpec != null) { return apply(style, resolvedSpec); } // null is normal if source has not be configured with named style } return style; }
Apply default named styles.
Params:
  • style – the style to apply to
  • name – the named style
Returns:the new style
/** * Apply default named styles. * * @param style the style to apply to * @param name the named style * @return the new style */
private AttributedStyle applyNamed(final AttributedStyle style, final String name) { // if (log.isLoggable(Level.FINEST)) { // log.finest("Apply-named: " + name); // } // TODO: consider short aliases for named styles switch (name.toLowerCase(Locale.US)) { case "default": return DEFAULT; case "bold": return style.bold(); case "faint": return style.faint(); case "italic": return style.italic(); case "underline": return style.underline(); case "blink": return style.blink(); case "inverse": return style.inverse(); case "inverse-neg": case "inverseneg": return style.inverseNeg(); case "conceal": return style.conceal(); case "crossed-out": case "crossedout": return style.crossedOut(); case "hidden": return style.hidden(); default: // log.warning("Unknown style: " + name); return style; } } // TODO: consider simplify and always using StyleColor, for now for compat with other bits leaving syntax complexity
Apply color styles specification.
Params:
  • style – The style to apply to
  • spec – Color specification: <color-mode>:<color-name>
Returns: The new style
/** * Apply color styles specification. * * @param style The style to apply to * @param spec Color specification: {@code <color-mode>:<color-name>} * @return The new style */
private AttributedStyle applyColor(final AttributedStyle style, final String spec) { // if (log.isLoggable(Level.FINEST)) { // log.finest("Apply-color: " + spec); // } // extract color-mode:color-name String[] parts = spec.split(":", 2); String colorMode = parts[0].trim(); String colorName = parts[1].trim(); // resolve the color-name Integer color = color(colorName); if (color == null) { // log.warning("Invalid color-name: " + colorName); } else { // resolve and apply color-mode switch (colorMode.toLowerCase(Locale.US)) { case "foreground": case "fg": case "f": return style.foreground(color); case "background": case "bg": case "b": return style.background(color); default: // log.warning("Invalid color-mode: " + colorMode); } } return style; } }