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 }