001 package org.crsh.text; 002 003 import org.crsh.util.Safe; 004 005 import java.io.IOException; 006 import java.io.PrintWriter; 007 import java.io.Serializable; 008 import java.util.Iterator; 009 import java.util.LinkedList; 010 import java.util.NoSuchElementException; 011 012 /** 013 * @author <a href="mailto:alain.defrance@exoplatform.com">Alain Defrance</a> 014 */ 015 public class CharReader implements Iterable<Object>, Serializable { 016 017 private static class Chunk implements Serializable { 018 019 /** . */ 020 final Style style; 021 022 /** . */ 023 final StringBuilder buffer; 024 025 private Chunk(Style style) { 026 this.style = style; 027 this.buffer = new StringBuilder(); 028 } 029 } 030 031 /** . */ 032 private final LinkedList<Chunk> chunks; 033 034 /** . */ 035 private Style style; 036 037 public CharReader() { 038 this.chunks = new LinkedList<Chunk>(); 039 this.style = null; 040 } 041 042 public CharReader(CharSequence s) { 043 this(); 044 045 // 046 append(s); 047 } 048 049 public Iterator<Object> iterator() { 050 return new Iterator<Object>() { 051 Iterator<Chunk> i = chunks.iterator(); 052 Style nextStyle; 053 StringBuilder nextBuffer; 054 public boolean hasNext() { 055 if (nextStyle != null || nextBuffer != null) { 056 return true; 057 } else if (i != null) { 058 if (i.hasNext()) { 059 Chunk next = i.next(); 060 nextStyle = next.style; 061 nextBuffer = next.buffer; 062 return true; 063 } else { 064 i = null; 065 if (style != null) { 066 Style last = chunks.size() > 0 ? chunks.peekLast().style : null; 067 if (style.equals(last)) { 068 return false; 069 } else { 070 nextStyle = style; 071 return true; 072 } 073 } else { 074 return false; 075 } 076 } 077 } else { 078 return false; 079 } 080 } 081 public Object next() { 082 if (hasNext()) { 083 Object next; 084 if (nextStyle != null) { 085 next = nextStyle; 086 nextStyle = null; 087 } else { 088 next = nextBuffer; 089 nextBuffer = null; 090 } 091 return next; 092 } else { 093 throw new NoSuchElementException(); 094 } 095 } 096 public void remove() { 097 throw new UnsupportedOperationException(); 098 } 099 }; 100 } 101 102 public void writeAnsiTo(PrintWriter writer) { 103 try { 104 writeAnsiTo((Appendable)writer); 105 } 106 catch (IOException ignore) { 107 } 108 } 109 110 public void writeAnsiTo(Appendable appendable) throws IOException { 111 Iterator<Object> iterator = iterator(); 112 while (iterator.hasNext()) { 113 Object o = iterator.next(); 114 if (o instanceof Style) { 115 try { 116 ((Style)o).writeAnsiTo(appendable); 117 } 118 catch (IOException ignore) { 119 } 120 } else if (o instanceof CharSequence) { 121 appendable.append((CharSequence)o); 122 } else { 123 appendable.append(o.toString()); 124 } 125 } 126 } 127 128 public CharReader append(Object... data) throws NullPointerException { 129 for (Object o : data) { 130 append(o); 131 } 132 return this; 133 } 134 135 public CharReader append(Object data) throws NullPointerException { 136 if (data == null) { 137 throw new NullPointerException("No null accepted"); 138 } 139 if (data instanceof CharReader) { 140 CharReader reader = (CharReader)data; 141 for (Chunk chunk : reader.chunks) { 142 if (chunk.style != null) { 143 append(chunk.style); 144 } 145 append(chunk.buffer); 146 } 147 style = reader.style; 148 } else { 149 if (data instanceof Style) { 150 if (data == Style.reset) { 151 style = Style.reset; 152 } else { 153 if (style != null) { 154 style = style.merge((Style)data); 155 } else { 156 style = (Style)data; 157 } 158 } 159 } else { 160 CharSequence s; 161 if (data instanceof CharSequence) { 162 s = (CharSequence)data; 163 } else { 164 s = data.toString(); 165 } 166 if (s.length() > 0) { 167 Chunk chunk; 168 if (chunks.size() > 0) { 169 Chunk last = chunks.peekLast(); 170 if (Safe.equals(last.style, style)) { 171 chunk = last; 172 } else { 173 chunks.addLast(chunk = new Chunk(style)); 174 } 175 } else { 176 chunks.addLast(chunk = new Chunk(style)); 177 } 178 chunk.buffer.append(s); 179 } 180 } 181 } 182 return this; 183 } 184 185 public boolean contains(Object o) { 186 return toString().contains(o.toString()); 187 } 188 189 public boolean isEmpty() { 190 return chunks.isEmpty(); 191 } 192 193 public void clear() { 194 chunks.clear(); 195 } 196 197 @Override 198 public int hashCode() { 199 return toString().hashCode(); 200 } 201 202 @Override 203 public boolean equals(Object obj) { 204 if (obj == this) { 205 return true; 206 } 207 if (obj instanceof CharReader) { 208 CharReader that = (CharReader)obj; 209 return toString().equals(that.toString()); 210 } 211 return false; 212 } 213 214 @Override 215 public String toString() { 216 StringBuilder sb = new StringBuilder(); 217 for (Chunk chunk : chunks) { 218 sb.append(chunk.buffer); 219 } 220 return sb.toString(); 221 } 222 }