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 public void format(Format format, Appendable appendable) throws IOException {
062 Iterator<Chunk> iterator = iterator();
063 while (iterator.hasNext()) {
064 format.append(iterator.next(), appendable);
065 }
066 }
067
068 public ChunkBuffer append(Iterable<?> data) throws NullPointerException {
069 for (Object o : data) {
070 append(o);
071 }
072 return this;
073 }
074
075 public ChunkBuffer append(Object... data) throws NullPointerException {
076 for (Object o : data) {
077 append(o);
078 }
079 return this;
080 }
081
082 public ChunkBuffer cls() {
083 chunks.addLast(CLS.INSTANCE);
084 return this;
085 }
086
087 public ChunkBuffer append(Style style) throws NullPointerException {
088 next = next.merge(style);
089 return this;
090 }
091
092 public ChunkBuffer append(char c) {
093 last().buffer.append(c);
094 return this;
095 }
096
097 public ChunkBuffer append(CharSequence s) {
098 return append(s, 0, s.length());
099 }
100
101 public ChunkBuffer append(CharSequence s, int start, int end) {
102 if (end > start) {
103 last().buffer.append(s, start, end);
104 }
105 return this;
106 }
107
108 private Text last() {
109 if (!next.equals(current)) {
110 if (!Style.style().equals(next)) {
111 chunks.addLast(next);
112 }
113 current = next;
114 next = Style.style();
115 }
116 Chunk last = chunks.peekLast();
117 if (last instanceof Text) {
118 return (Text)last;
119 } else {
120 Text text = new Text();
121 chunks.addLast(text);
122 return text;
123 }
124 }
125
126 public Class<Chunk> getConsumedType() {
127 return Chunk.class;
128 }
129
130 public void provide(Chunk element) throws IOException {
131 append(element);
132 }
133
134 public void flush() throws IOException {
135 if (out != null) {
136 for (Chunk chunk : chunks) {
137 out.provide(chunk);
138 }
139 }
140 chunks.clear();
141 if (out != null) {
142 out.flush();
143 }
144 }
145
146 public ChunkBuffer append(ChunkBuffer s) throws NullPointerException {
147 for (Chunk chunk : s.chunks) {
148 write(chunk);
149 }
150 if (s.next != null && !s.next.equals(Style.style())) {
151 write(s.next);
152 }
153 return this;
154 }
155
156 public void write(Chunk chunk) throws NullPointerException {
157 if (chunk instanceof Style) {
158 append((Style)chunk);
159 } else if (chunk instanceof Text){
160 append(((Text)chunk).buffer);
161 } else {
162 cls();
163 }
164 }
165
166 public ChunkBuffer append(Object o) throws NullPointerException {
167 if (o == null) {
168 throw new NullPointerException("No null accepted");
169 }
170 if (o instanceof ChunkBuffer) {
171 append((ChunkBuffer)o);
172 } else if (o instanceof Chunk) {
173 write((Chunk)o);
174 } else {
175 CharSequence s;
176 if (o instanceof CharSequence) {
177 s = (CharSequence)o;
178 } else {
179 s = o.toString();
180 }
181 append(s);
182 }
183 return this;
184 }
185
186 public boolean contains(Object o) {
187 return toString().contains(o.toString());
188 }
189
190 public boolean isEmpty() {
191 return chunks.isEmpty();
192 }
193
194 public void clear() {
195 chunks.clear();
196 }
197
198 @Override
199 public int hashCode() {
200 return toString().hashCode();
201 }
202
203 @Override
204 public boolean equals(Object obj) {
205 if (obj == this) {
206 return true;
207 }
208 if (obj instanceof ChunkBuffer) {
209 ChunkBuffer that = (ChunkBuffer)obj;
210 return toString().equals(that.toString());
211 }
212 return false;
213 }
214
215 @Override
216 public String toString() {
217 StringBuilder sb = new StringBuilder();
218 try {
219 format(Format.TEXT, sb);
220 }
221 catch (IOException ignore) {
222 }
223 return sb.toString();
224 }
225 }