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