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 public abstract class Style extends Chunk implements Serializable { 030 031 public static final Style reset = new Style() { 032 033 @Override 034 public Style merge(Style s) throws NullPointerException { 035 if (s == null) { 036 throw new NullPointerException(); 037 } 038 return s; 039 } 040 041 @Override 042 public String toString() { 043 return "Style.Reset[]"; 044 } 045 046 @Override 047 public void writeAnsiTo(Appendable appendable) throws IOException { 048 appendable.append("\033[0m"); 049 } 050 }; 051 052 public static final class Composite extends Style { 053 054 /** . */ 055 protected final Boolean bold; 056 057 /** . */ 058 protected final Boolean underline; 059 060 /** . */ 061 protected final Boolean blink; 062 063 /** . */ 064 protected final Color foreground; 065 066 /** . */ 067 protected final Color background; 068 069 private Composite(Boolean bold, Boolean underline, Boolean blink, Color foreground, Color background) { 070 this.bold = bold; 071 this.underline = underline; 072 this.blink = blink; 073 this.foreground = foreground; 074 this.background = background; 075 } 076 077 public Composite fg(Color color) { 078 return foreground(color); 079 } 080 081 public Composite foreground(Color color) { 082 return style(bold, underline, blink, color, background); 083 } 084 085 public Composite bg(Color value) { 086 return background(value); 087 } 088 089 public Composite background(Color value) { 090 return style(bold, underline, blink, foreground, value); 091 } 092 093 public Composite bold() { 094 return bold(true); 095 } 096 097 public Composite underline() { 098 return underline(true); 099 } 100 101 public Composite blink() { 102 return blink(true); 103 } 104 105 public Composite bold(Boolean value) { 106 return style(value, underline, blink, foreground, background); 107 } 108 109 public Composite underline(Boolean value) { 110 return style(bold, value, blink, foreground, background); 111 } 112 113 public Composite blink(Boolean value) { 114 return style(bold, underline, value, foreground, background); 115 } 116 117 public Composite decoration(Decoration decoration) { 118 if (decoration != null) { 119 switch (decoration) { 120 case bold: 121 return bold(true); 122 case bold_off: 123 return bold(false); 124 case underline: 125 return underline(true); 126 case underline_off: 127 return underline(false); 128 case blink: 129 return blink(true); 130 case blink_off: 131 return blink(false); 132 } 133 } 134 return this; 135 } 136 137 public Boolean getBold() { 138 return bold; 139 } 140 141 public Boolean getUnderline() { 142 return underline; 143 } 144 145 public Boolean getBlink() { 146 return blink; 147 } 148 149 public Color getForeground() { 150 return foreground; 151 } 152 153 public Color getBackground() { 154 return background; 155 } 156 157 public Style merge(Style s) throws NullPointerException { 158 if (s == null) { 159 throw new NullPointerException(); 160 } 161 if (s == reset) { 162 return reset; 163 } else { 164 Style.Composite that = (Composite)s; 165 Boolean bold = Utils.notNull(that.getBold(), getBold()); 166 Boolean underline = Utils.notNull(that.getUnderline(), getUnderline()); 167 Boolean blink = Utils.notNull(that.getBlink(), getBlink()); 168 Color foreground = Utils.notNull(that.getForeground(), getForeground()); 169 Color background = Utils.notNull(that.getBackground(), getBackground()); 170 return style(bold, underline, blink, foreground, background); 171 } 172 } 173 174 @Override 175 public String toString() { 176 return "Style.Composite[bold=" + bold + ",underline=" + underline + ",blink=" + blink + 177 ",background=" + background + ",foreground=" + foreground + "]"; 178 } 179 180 private static boolean decoration( 181 Appendable appendable, 182 String on, 183 String off, 184 Boolean value, 185 boolean append) throws IOException { 186 if (value != null) { 187 if (append) { 188 appendable.append(';'); 189 } else { 190 appendable.append("\033["); 191 } 192 if (value) { 193 appendable.append(on); 194 } else { 195 appendable.append(off); 196 } 197 return true; 198 } 199 return false; 200 } 201 202 private static boolean color( 203 Appendable appendable, 204 Color color, 205 char base, 206 boolean append) throws IOException { 207 if (color != null) { 208 if (append) { 209 appendable.append(';'); 210 } else { 211 appendable.append("\033["); 212 } 213 appendable.append(base); 214 appendable.append(color.c); 215 return true; 216 } 217 return false; 218 } 219 220 @Override 221 public void writeAnsiTo(Appendable appendable) throws IOException { 222 boolean appended = decoration(appendable, Decoration.bold.code, Decoration.bold_off.code, bold, false); 223 appended |= decoration(appendable, Decoration.underline.code, Decoration.underline_off.code, underline, appended); 224 appended |= decoration(appendable, Decoration.blink.code, Decoration.blink_off.code, blink, appended); 225 appended |= color(appendable, foreground, '3', appended); 226 appended |= color(appendable, background, '4', appended); 227 if (appended) { 228 appendable.append("m"); 229 } 230 } 231 } 232 233 /** . */ 234 private static final Boolean[] BOOLEANS = {true,false,null}; 235 236 /** . */ 237 private static final Color[] COLORS = Arrays.copyOf(Color.values(), Color.values().length + 1); 238 239 /** [bold][underline][blink][foreground][background]. */ 240 private static final Composite[][][][][] ALL; 241 242 static { 243 ALL = new Composite[BOOLEANS.length][][][][]; 244 for (int bold = 0;bold < BOOLEANS.length;bold++) { 245 ALL[bold] = new Composite[BOOLEANS.length][][][]; 246 for (int underline = 0;underline < BOOLEANS.length;underline++) { 247 ALL[bold][underline] = new Composite[BOOLEANS.length][][]; 248 for (int blink = 0;blink < BOOLEANS.length;blink++) { 249 ALL[bold][underline][blink] = new Composite[COLORS.length][]; 250 for (int foreground = 0;foreground < COLORS.length;foreground++) { 251 ALL[bold][underline][blink][foreground] = new Composite[COLORS.length]; 252 for (int background = 0;background < COLORS.length;background++) { 253 ALL[bold][underline][blink][foreground][background] = new Composite( 254 BOOLEANS[bold], 255 BOOLEANS[underline], 256 BOOLEANS[blink], 257 COLORS[foreground], 258 COLORS[background]); 259 } 260 } 261 } 262 } 263 } 264 } 265 266 public static Composite style(Color foreground) { 267 return style(null, foreground, null); 268 } 269 270 public static Composite style(Color foreground, Color background) { 271 return style(null, foreground, background); 272 } 273 274 public static Composite style(Decoration decoration, Color foreground, Color background) { 275 Boolean bold = null; 276 Boolean underline = null; 277 Boolean blink = null; 278 if (decoration != null) { 279 switch (decoration) { 280 case bold: 281 bold = true; 282 break; 283 case bold_off: 284 bold = false; 285 break; 286 case underline: 287 underline = true; 288 break; 289 case underline_off: 290 underline = false; 291 break; 292 case blink: 293 blink = true; 294 break; 295 case blink_off: 296 blink = false; 297 break; 298 } 299 } 300 return style(bold, underline, blink, foreground, background); 301 } 302 303 public static Composite style(Boolean bold, Boolean underline, Boolean blink, Color foreground, Color background) { 304 int bo = bold != null ? bold ? 0 : 1: 2; 305 int un = underline != null ? underline ? 0 : 1: 2; 306 int bl = blink != null ? blink ? 0 : 1: 2; 307 int fg = foreground != null ? foreground.ordinal() : COLORS.length - 1; 308 int bg = background != null ? background.ordinal() : COLORS.length - 1; 309 return ALL[bo][un][bl][fg][bg]; 310 } 311 312 public static Composite style() { 313 return style(null, null, null); 314 } 315 316 public static Composite style(Decoration decoration) { 317 return style(decoration, null, null); 318 } 319 320 public static Composite style(Decoration decoration, Color foreground) { 321 return style(decoration, foreground, null); 322 } 323 324 public abstract Style merge(Style s) throws NullPointerException; 325 326 public CharSequence toAnsiSequence() { 327 StringBuilder sb = new StringBuilder(); 328 try { 329 writeAnsiTo(sb); 330 } 331 catch (IOException e) { 332 // Should not happen 333 throw new UndeclaredThrowableException(e); 334 } 335 return sb.toString(); 336 } 337 338 public abstract void writeAnsiTo(Appendable appendable) throws IOException; 339 340 @Override 341 public abstract String toString(); 342 }