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 java.io.IOException;
023    import java.util.LinkedList;
024    
025    public class RenderAppendable implements ScreenContext {
026    
027      /** . */
028      private final ScreenContext context;
029    
030      /** . */
031      private LinkedList<Style.Composite> stack;
032    
033      public RenderAppendable(ScreenContext context) {
034        this.context = context;
035      }
036    
037      @Override
038      public RenderAppendable append(CharSequence s) {
039        try {
040          context.append(s);
041        }
042        catch (java.io.IOException ignore) {
043        }
044        return this;
045      }
046    
047      @Override
048      public Screenable append(char c) {
049        try {
050          context.append(c);
051        }
052        catch (java.io.IOException ignore) {
053        }
054        return this;
055      }
056    
057      @Override
058      public Screenable append(CharSequence csq, int start, int end) {
059        try {
060          context.append(csq, start, end);
061        }
062        catch (java.io.IOException ignore) {
063        }
064        return this;
065      }
066    
067      @Override
068      public Screenable append(Style style) {
069        try {
070          context.append(style);
071        }
072        catch (java.io.IOException ignore) {
073        }
074        return this;
075      }
076    
077      @Override
078      public Screenable cls() {
079        try {
080          context.cls();
081        }
082        catch (java.io.IOException ignore) {
083        }
084        return this;
085      }
086    
087      public int getWidth() {
088        // Use one less char to have a correct display on windows
089        return Math.max(0, context.getWidth() - 1);
090      }
091    
092      public int getHeight() {
093        return context.getHeight();
094      }
095    
096      public void flush() throws IOException {
097        context.flush();
098      }
099    
100      public void enterStyle(Style.Composite style) {
101        if (stack == null) {
102          stack = new LinkedList<Style.Composite>();
103        }
104        append(style);
105        stack.addLast(style);
106      }
107    
108      public Style.Composite leaveStyle() {
109        if (stack == null || stack.isEmpty()) {
110          throw new IllegalStateException("Cannot leave non existing style");
111        }
112        Style.Composite last = stack.removeLast();
113        if (stack.size() > 0) {
114    
115          // Compute merged
116          Style.Composite merged = getMerged();
117    
118          // Compute diff with removed
119          Boolean bold = foo(last.getBold(), merged.getBold());
120          Boolean underline = foo(last.getUnderline(), merged.getUnderline());
121          Boolean blink = foo(last.getBlink(), merged.getBlink());
122    
123          // For now we assume that black is the default background color
124          // and white is the default foreground color
125          Color fg = foo(last.getForeground(), merged.getForeground(), Color.def);
126          Color bg = foo(last.getBackground(), merged.getBackground(), Color.def);
127    
128          //
129          Style.Composite bilto = Style.style(bold, underline, blink, fg, bg);
130    
131          //   
132          append(bilto);
133        } else {
134          append(Style.reset);
135        }
136        return last;
137      }
138    
139      /**
140       * Compute the current merged style.
141       *
142       * @return the merged style
143       */
144      private Style.Composite getMerged() {
145        Style.Composite merged = Style.style();
146        for (Style s : stack) {
147          merged = (Style.Composite)merged.merge(s);
148        }
149        return merged;
150      }
151    
152      private Boolean foo(Boolean last, Boolean merged) {
153        if (last != null) {
154          if (merged != null) {
155            return merged;
156          } else {
157            return !last;
158          }
159        } else {
160          return null;
161        }
162      }
163    
164      private Color foo(Color last, Color merged, Color def) {
165        if (last != null) {
166          if (merged != null) {
167            return merged;
168          } else {
169            return def;
170          }
171        } else {
172          return null;
173        }
174      }
175    
176      public void styleOff() {
177        if (stack != null && stack.size() > 0) {
178          append(Style.reset);
179        }
180      }
181    
182      public void styleOn() {
183        if (stack != null && stack.size() > 0) {
184          append(getMerged());
185        }
186      }
187    }