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.io.Consumer;
023
024 import java.io.IOException;
025 import java.io.Serializable;
026 import java.util.Iterator;
027 import java.util.LinkedList;
028
029 public class ChunkBuffer implements Iterable<Chunk>, Serializable, Consumer<Chunk> {
030
031 /** . */
032 private final LinkedList<Chunk> chunks;
033
034 /** . */
035 private Style current;
036
037 /** . */
038 private Style next;
039
040 /** Where we flush. */
041 private final Consumer<Chunk> out;
042
043 public ChunkBuffer() {
044 this.chunks = new LinkedList<Chunk>();
045 this.current = Style.style();
046 this.next = Style.style();
047 this.out = null;
048 }
049
050 public ChunkBuffer(Consumer<Chunk> out) {
051 this.chunks = new LinkedList<Chunk>();
052 this.current = Style.style();
053 this.next = Style.style();
054 this.out = out;
055 }
056
057 public Iterator<Chunk> iterator() {
058 return chunks.iterator();
059 }
060
061 @Deprecated
062 public void writeAnsiTo(Appendable appendable) throws IOException {
063 Iterator<Chunk> iterator = iterator();
064 while (iterator.hasNext()) {
065 Chunk chunk = iterator.next();
066 if (chunk instanceof Text) {
067 Text text = (Text)chunk;
068 if (text.buffer.length() > 0) {
069 appendable.append(text.buffer);
070 }
071 } else if (chunk instanceof Style) {
072 ((Style)chunk).writeAnsiTo(appendable);
073 }
074 }
075 }
076
077 public ChunkBuffer append(Iterable<?> data) throws NullPointerException {
078 for (Object o : data) {
079 append(o);
080 }
081 return this;
082 }
083
084 public ChunkBuffer append(Object... data) throws NullPointerException {
085 for (Object o : data) {
086 append(o);
087 }
088 return this;
089 }
090
091 public ChunkBuffer cls() {
092 chunks.addLast(CLS.INSTANCE);
093 return this;
094 }
095
096 public ChunkBuffer append(Style style) throws NullPointerException {
097 next = next.merge(style);
098 return this;
099 }
100
101 public ChunkBuffer append(char c) {
102 last().buffer.append(c);
103 return this;
104 }
105
106 public ChunkBuffer append(CharSequence s) {
107 return append(s, 0, s.length());
108 }
109
110 public ChunkBuffer append(CharSequence s, int start, int end) {
111 if (end > start) {
112 last().buffer.append(s, start, end);
113 }
114 return this;
115 }
116
117 private Text last() {
118 if (!next.equals(current)) {
119 if (!Style.style().equals(next)) {
120 chunks.addLast(next);
121 }
122 current = next;
123 next = Style.style();
124 }
125 Chunk last = chunks.peekLast();
126 if (last instanceof Text) {
127 return (Text)last;
128 } else {
129 Text text = new Text();
130 chunks.addLast(text);
131 return text;
132 }
133 }
134
135 public Class<Chunk> getConsumedType() {
136 return Chunk.class;
137 }
138
139 public void provide(Chunk element) throws IOException {
140 append(element);
141 }
142
143 public void flush() throws IOException {
144 if (out != null) {
145 for (Chunk chunk : chunks) {
146 out.provide(chunk);
147 }
148 }
149 chunks.clear();
150 if (out != null) {
151 out.flush();
152 }
153 }
154
155 public ChunkBuffer append(ChunkBuffer s) throws NullPointerException {
156 for (Chunk chunk : s.chunks) {
157 write(chunk);
158 }
159 if (s.next != null && !s.next.equals(Style.style())) {
160 write(s.next);
161 }
162 return this;
163 }
164
165 public void write(Chunk chunk) throws NullPointerException {
166 if (chunk instanceof Style) {
167 append((Style)chunk);
168 } else if (chunk instanceof Text){
169 append(((Text)chunk).buffer);
170 } else {
171 cls();
172 }
173 }
174
175 public ChunkBuffer append(Object o) throws NullPointerException {
176 if (o == null) {
177 throw new NullPointerException("No null accepted");
178 }
179 if (o instanceof ChunkBuffer) {
180 append((ChunkBuffer)o);
181 } else if (o instanceof Chunk) {
182 write((Chunk)o);
183 } else {
184 CharSequence s;
185 if (o instanceof CharSequence) {
186 s = (CharSequence)o;
187 } else {
188 s = o.toString();
189 }
190 append(s);
191 }
192 return this;
193 }
194
195 public boolean contains(Object o) {
196 return toString().contains(o.toString());
197 }
198
199 public boolean isEmpty() {
200 return chunks.isEmpty();
201 }
202
203 public void clear() {
204 chunks.clear();
205 }
206
207 @Override
208 public int hashCode() {
209 return toString().hashCode();
210 }
211
212 @Override
213 public boolean equals(Object obj) {
214 if (obj == this) {
215 return true;
216 }
217 if (obj instanceof ChunkBuffer) {
218 ChunkBuffer that = (ChunkBuffer)obj;
219 return toString().equals(that.toString());
220 }
221 return false;
222 }
223
224 @Override
225 public String toString() {
226 StringBuilder sb = new StringBuilder();
227 for (Chunk chunk : chunks) {
228 if (chunk instanceof Text) {
229 sb.append(((Text)chunk).buffer);
230 }
231 }
232 return sb.toString();
233 }
234
235 public void writeTo(Consumer<Chunk> writer) throws IOException {
236 for (Chunk chunk : chunks) {
237 writer.provide(chunk);
238 }
239 }
240 }