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 }