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