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    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}