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    }