001 /* 002 * Copyright (C) 2012 eXo Platform SAS. 003 * 004 * This is free software; you can redistribute it and/or modify it 005 * under the terms of the GNU Lesser General Public License as 006 * published by the Free Software Foundation; either version 2.1 of 007 * the License, or (at your option) any later version. 008 * 009 * This software is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * 014 * You should have received a copy of the GNU Lesser General Public 015 * License along with this software; if not, write to the Free 016 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 017 * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 018 */ 019 020 package org.crsh.text; 021 022 import org.crsh.util.Utils; 023 024 import java.io.IOException; 025 import java.io.Serializable; 026 import java.lang.reflect.UndeclaredThrowableException; 027 import java.util.Arrays; 028 029 /** 030 * A control for the text stylistric attributes: 031 * <u> 032 * <li>background color</li> 033 * <li>foreground color</li> 034 * <li>underline</li> 035 * <li>bold</li> 036 * <li>blink</li> 037 * </u> 038 * 039 * A style is either a composite style or the {@link #reset} style. Styles can be composed together to form a new 040 * style <code>style.merge(other)</code>. 041 */ 042 public abstract class Style implements Serializable { 043 044 public static final Style reset = new Style() { 045 046 @Override 047 public Style merge(Style s) throws NullPointerException { 048 if (s == null) { 049 throw new NullPointerException(); 050 } 051 return s; 052 } 053 054 @Override 055 public String toString() { 056 return "Style.Reset[]"; 057 } 058 059 @Override 060 public void writeAnsiTo(Appendable appendable) throws IOException { 061 appendable.append("\033[0m"); 062 } 063 }; 064 065 public static final class Composite extends Style { 066 067 /** . */ 068 protected final Boolean bold; 069 070 /** . */ 071 protected final Boolean underline; 072 073 /** . */ 074 protected final Boolean blink; 075 076 /** . */ 077 protected final Color foreground; 078 079 /** . */ 080 protected final Color background; 081 082 private Composite(Boolean bold, Boolean underline, Boolean blink, Color foreground, Color background) { 083 this.bold = bold; 084 this.underline = underline; 085 this.blink = blink; 086 this.foreground = foreground; 087 this.background = background; 088 } 089 090 public Composite fg(Color color) { 091 return foreground(color); 092 } 093 094 public Composite foreground(Color color) { 095 return style(bold, underline, blink, color, background); 096 } 097 098 public Composite bg(Color value) { 099 return background(value); 100 } 101 102 public Composite background(Color value) { 103 return style(bold, underline, blink, foreground, value); 104 } 105 106 public Composite bold() { 107 return bold(true); 108 } 109 110 public Composite underline() { 111 return underline(true); 112 } 113 114 public Composite blink() { 115 return blink(true); 116 } 117 118 public Composite bold(Boolean value) { 119 return style(value, underline, blink, foreground, background); 120 } 121 122 public Composite underline(Boolean value) { 123 return style(bold, value, blink, foreground, background); 124 } 125 126 public Composite blink(Boolean value) { 127 return style(bold, underline, value, foreground, background); 128 } 129 130 public Composite decoration(Decoration decoration) { 131 if (decoration != null) { 132 switch (decoration) { 133 case bold: 134 return bold(true); 135 case bold_off: 136 return bold(false); 137 case underline: 138 return underline(true); 139 case underline_off: 140 return underline(false); 141 case blink: 142 return blink(true); 143 case blink_off: 144 return blink(false); 145 } 146 } 147 return this; 148 } 149 150 public Boolean getBold() { 151 return bold; 152 } 153 154 public Boolean getUnderline() { 155 return underline; 156 } 157 158 public Boolean getBlink() { 159 return blink; 160 } 161 162 public Color getForeground() { 163 return foreground; 164 } 165 166 public Color getBackground() { 167 return background; 168 } 169 170 public Style merge(Style s) throws NullPointerException { 171 if (s == null) { 172 throw new NullPointerException(); 173 } 174 if (s == reset) { 175 return reset; 176 } else { 177 Style.Composite that = (Composite)s; 178 Boolean bold = Utils.notNull(that.getBold(), getBold()); 179 Boolean underline = Utils.notNull(that.getUnderline(), getUnderline()); 180 Boolean blink = Utils.notNull(that.getBlink(), getBlink()); 181 Color foreground = Utils.notNull(that.getForeground(), getForeground()); 182 Color background = Utils.notNull(that.getBackground(), getBackground()); 183 return style(bold, underline, blink, foreground, background); 184 } 185 } 186 187 @Override 188 public String toString() { 189 return "Style.Composite[bold=" + bold + ",underline=" + underline + ",blink=" + blink + 190 ",background=" + background + ",foreground=" + foreground + "]"; 191 } 192 193 private static boolean decoration( 194 Appendable appendable, 195 String on, 196 String off, 197 Boolean value, 198 boolean append) throws IOException { 199 if (value != null) { 200 if (append) { 201 appendable.append(';'); 202 } else { 203 appendable.append("\033["); 204 } 205 if (value) { 206 appendable.append(on); 207 } else { 208 appendable.append(off); 209 } 210 return true; 211 } 212 return false; 213 } 214 215 private static boolean color( 216 Appendable appendable, 217 Color color, 218 char base, 219 boolean append) throws IOException { 220 if (color != null) { 221 if (append) { 222 appendable.append(';'); 223 } else { 224 appendable.append("\033["); 225 } 226 appendable.append(base); 227 appendable.append(color.c); 228 return true; 229 } 230 return false; 231 } 232 233 @Override 234 public void writeAnsiTo(Appendable appendable) throws IOException { 235 boolean appended = decoration(appendable, Decoration.bold.code, Decoration.bold_off.code, bold, false); 236 appended |= decoration(appendable, Decoration.underline.code, Decoration.underline_off.code, underline, appended); 237 appended |= decoration(appendable, Decoration.blink.code, Decoration.blink_off.code, blink, appended); 238 appended |= color(appendable, foreground, '3', appended); 239 appended |= color(appendable, background, '4', appended); 240 if (appended) { 241 appendable.append("m"); 242 } 243 } 244 } 245 246 /** . */ 247 private static final Boolean[] BOOLEANS = {true,false,null}; 248 249 /** . */ 250 private static final Color[] COLORS = Arrays.copyOf(Color.values(), Color.values().length + 1); 251 252 /** [bold][underline][blink][foreground][background]. */ 253 private static final Composite[][][][][] ALL; 254 255 static { 256 ALL = new Composite[BOOLEANS.length][][][][]; 257 for (int bold = 0;bold < BOOLEANS.length;bold++) { 258 ALL[bold] = new Composite[BOOLEANS.length][][][]; 259 for (int underline = 0;underline < BOOLEANS.length;underline++) { 260 ALL[bold][underline] = new Composite[BOOLEANS.length][][]; 261 for (int blink = 0;blink < BOOLEANS.length;blink++) { 262 ALL[bold][underline][blink] = new Composite[COLORS.length][]; 263 for (int foreground = 0;foreground < COLORS.length;foreground++) { 264 ALL[bold][underline][blink][foreground] = new Composite[COLORS.length]; 265 for (int background = 0;background < COLORS.length;background++) { 266 ALL[bold][underline][blink][foreground][background] = new Composite( 267 BOOLEANS[bold], 268 BOOLEANS[underline], 269 BOOLEANS[blink], 270 COLORS[foreground], 271 COLORS[background]); 272 } 273 } 274 } 275 } 276 } 277 } 278 279 public static Composite style(Color foreground) { 280 return style(null, foreground, null); 281 } 282 283 public static Composite style(Color foreground, Color background) { 284 return style(null, foreground, background); 285 } 286 287 public static Composite style(Decoration decoration, Color foreground, Color background) { 288 Boolean bold = null; 289 Boolean underline = null; 290 Boolean blink = null; 291 if (decoration != null) { 292 switch (decoration) { 293 case bold: 294 bold = true; 295 break; 296 case bold_off: 297 bold = false; 298 break; 299 case underline: 300 underline = true; 301 break; 302 case underline_off: 303 underline = false; 304 break; 305 case blink: 306 blink = true; 307 break; 308 case blink_off: 309 blink = false; 310 break; 311 } 312 } 313 return style(bold, underline, blink, foreground, background); 314 } 315 316 public static Composite style(Boolean bold, Boolean underline, Boolean blink, Color foreground, Color background) { 317 int bo = bold != null ? bold ? 0 : 1: 2; 318 int un = underline != null ? underline ? 0 : 1: 2; 319 int bl = blink != null ? blink ? 0 : 1: 2; 320 int fg = foreground != null ? foreground.ordinal() : COLORS.length - 1; 321 int bg = background != null ? background.ordinal() : COLORS.length - 1; 322 return ALL[bo][un][bl][fg][bg]; 323 } 324 325 /** 326 * Create a new blank style. 327 * 328 * @return the style 329 */ 330 public static Composite style() { 331 return style(null, null, null); 332 } 333 334 public static Composite style(Decoration decoration) { 335 return style(decoration, null, null); 336 } 337 338 public static Composite style(Decoration decoration, Color foreground) { 339 return style(decoration, foreground, null); 340 } 341 342 public abstract Style merge(Style s) throws NullPointerException; 343 344 public CharSequence toAnsiSequence() { 345 StringBuilder sb = new StringBuilder(); 346 try { 347 writeAnsiTo(sb); 348 } 349 catch (IOException e) { 350 // Should not happen 351 throw new UndeclaredThrowableException(e); 352 } 353 return sb.toString(); 354 } 355 356 public abstract void writeAnsiTo(Appendable appendable) throws IOException; 357 358 @Override 359 public abstract String toString(); 360 }