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