001/* 002 * Copyright (C) 2009-2021 the original author(s). 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.fusesource.jansi; 017 018import java.io.IOException; 019import java.util.Locale; 020 021import org.fusesource.jansi.Ansi.Attribute; 022import org.fusesource.jansi.Ansi.Color; 023 024/** 025 * Renders ANSI color escape-codes in strings by parsing out some special syntax to pick up the correct fluff to use. 026 * 027 * The syntax for embedded ANSI codes is: 028 * 029 * <pre> 030 * @|<em>code</em>(,<em>code</em>)* <em>text</em>|@ 031 * </pre> 032 * 033 * Examples: 034 * 035 * <pre> 036 * @|bold Hello|@ 037 * </pre> 038 * 039 * <pre> 040 * @|bold,red Warning!|@ 041 * </pre> 042 * 043 * @since 2.2 044 */ 045public class AnsiRenderer { 046 047 public static final String BEGIN_TOKEN = "@|"; 048 049 public static final String END_TOKEN = "|@"; 050 051 public static final String CODE_TEXT_SEPARATOR = " "; 052 053 public static final String CODE_LIST_SEPARATOR = ","; 054 055 private static final int BEGIN_TOKEN_LEN = 2; 056 057 private static final int END_TOKEN_LEN = 2; 058 059 public static String render(final String input) throws IllegalArgumentException { 060 try { 061 return render(input, new StringBuilder()).toString(); 062 } catch (IOException e) { 063 // Cannot happen because StringBuilder does not throw IOException 064 throw new IllegalArgumentException(e); 065 } 066 } 067 068 /** 069 * Renders the given input to the target Appendable. 070 * 071 * @param input 072 * source to render 073 * @param target 074 * render onto this target Appendable. 075 * @return the given Appendable 076 * @throws IOException 077 * If an I/O error occurs 078 */ 079 public static Appendable render(final String input, Appendable target) throws IOException { 080 081 int i = 0; 082 int j, k; 083 084 while (true) { 085 j = input.indexOf(BEGIN_TOKEN, i); 086 if (j == -1) { 087 if (i == 0) { 088 target.append(input); 089 return target; 090 } 091 target.append(input.substring(i)); 092 return target; 093 } 094 target.append(input.substring(i, j)); 095 k = input.indexOf(END_TOKEN, j); 096 097 if (k == -1) { 098 target.append(input); 099 return target; 100 } 101 j += BEGIN_TOKEN_LEN; 102 String spec = input.substring(j, k); 103 104 String[] items = spec.split(CODE_TEXT_SEPARATOR, 2); 105 if (items.length == 1) { 106 target.append(input); 107 return target; 108 } 109 String replacement = render(items[1], items[0].split(CODE_LIST_SEPARATOR)); 110 111 target.append(replacement); 112 113 i = k + END_TOKEN_LEN; 114 } 115 } 116 117 public static String render(final String text, final String... codes) { 118 return render(Ansi.ansi(), codes) 119 .a(text).reset().toString(); 120 } 121 122 /** 123 * Renders {@link Code} names as an ANSI escape string. 124 * @param codes The code names to render 125 * @return an ANSI escape string. 126 */ 127 public static String renderCodes(final String... codes) { 128 return render(Ansi.ansi(), codes).toString(); 129 } 130 131 /** 132 * Renders {@link Code} names as an ANSI escape string. 133 * @param codes A space separated list of code names to render 134 * @return an ANSI escape string. 135 */ 136 public static String renderCodes(final String codes) { 137 return renderCodes(codes.split("\\s")); 138 } 139 140 private static Ansi render(Ansi ansi, String... names) { 141 for (String name : names) { 142 Code code = Code.valueOf(name.toUpperCase(Locale.ENGLISH)); 143 if (code.isColor()) { 144 if (code.isBackground()) { 145 ansi.bg(code.getColor()); 146 } else { 147 ansi.fg(code.getColor()); 148 } 149 } else if (code.isAttribute()) { 150 ansi.a(code.getAttribute()); 151 } 152 } 153 return ansi; 154 } 155 156 public static boolean test(final String text) { 157 return text != null && text.contains(BEGIN_TOKEN); 158 } 159 160 @SuppressWarnings("unused") 161 public enum Code { 162 // 163 // TODO: Find a better way to keep Code in sync with Color/Attribute/Erase 164 // 165 166 // Colors 167 BLACK(Color.BLACK), 168 RED(Color.RED), 169 GREEN(Color.GREEN), 170 YELLOW(Color.YELLOW), 171 BLUE(Color.BLUE), 172 MAGENTA(Color.MAGENTA), 173 CYAN(Color.CYAN), 174 WHITE(Color.WHITE), 175 DEFAULT(Color.DEFAULT), 176 177 // Foreground Colors 178 FG_BLACK(Color.BLACK, false), 179 FG_RED(Color.RED, false), 180 FG_GREEN(Color.GREEN, false), 181 FG_YELLOW(Color.YELLOW, false), 182 FG_BLUE(Color.BLUE, false), 183 FG_MAGENTA(Color.MAGENTA, false), 184 FG_CYAN(Color.CYAN, false), 185 FG_WHITE(Color.WHITE, false), 186 FG_DEFAULT(Color.DEFAULT, false), 187 188 // Background Colors 189 BG_BLACK(Color.BLACK, true), 190 BG_RED(Color.RED, true), 191 BG_GREEN(Color.GREEN, true), 192 BG_YELLOW(Color.YELLOW, true), 193 BG_BLUE(Color.BLUE, true), 194 BG_MAGENTA(Color.MAGENTA, true), 195 BG_CYAN(Color.CYAN, true), 196 BG_WHITE(Color.WHITE, true), 197 BG_DEFAULT(Color.DEFAULT, true), 198 199 // Attributes 200 RESET(Attribute.RESET), 201 INTENSITY_BOLD(Attribute.INTENSITY_BOLD), 202 INTENSITY_FAINT(Attribute.INTENSITY_FAINT), 203 ITALIC(Attribute.ITALIC), 204 UNDERLINE(Attribute.UNDERLINE), 205 BLINK_SLOW(Attribute.BLINK_SLOW), 206 BLINK_FAST(Attribute.BLINK_FAST), 207 BLINK_OFF(Attribute.BLINK_OFF), 208 NEGATIVE_ON(Attribute.NEGATIVE_ON), 209 NEGATIVE_OFF(Attribute.NEGATIVE_OFF), 210 CONCEAL_ON(Attribute.CONCEAL_ON), 211 CONCEAL_OFF(Attribute.CONCEAL_OFF), 212 UNDERLINE_DOUBLE(Attribute.UNDERLINE_DOUBLE), 213 UNDERLINE_OFF(Attribute.UNDERLINE_OFF), 214 215 // Aliases 216 BOLD(Attribute.INTENSITY_BOLD), 217 FAINT(Attribute.INTENSITY_FAINT),; 218 219 private final Enum<?> n; 220 221 private final boolean background; 222 223 Code(final Enum<?> n, boolean background) { 224 this.n = n; 225 this.background = background; 226 } 227 228 Code(final Enum<?> n) { 229 this(n, false); 230 } 231 232 public boolean isColor() { 233 return n instanceof Ansi.Color; 234 } 235 236 public Ansi.Color getColor() { 237 return (Ansi.Color) n; 238 } 239 240 public boolean isAttribute() { 241 return n instanceof Attribute; 242 } 243 244 public Attribute getAttribute() { 245 return (Attribute) n; 246 } 247 248 public boolean isBackground() { 249 return background; 250 } 251 } 252 253 private AnsiRenderer() { 254 } 255 256}