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    }