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
020package org.crsh.text;
021
022import org.crsh.io.Consumer;
023
024import java.io.IOException;
025import java.io.Serializable;
026import java.util.Iterator;
027import java.util.LinkedList;
028
029public 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}