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.Pipe;
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, Pipe<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 Pipe<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(Pipe<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 void provide(Chunk element) throws IOException {
136        append(element);
137      }
138    
139      public void flush() throws IOException {
140        if (out != null) {
141          for (Chunk chunk : chunks) {
142            out.provide(chunk);
143          }
144        }
145        chunks.clear();
146        if (out != null) {
147          out.flush();
148        }
149      }
150    
151      public ChunkBuffer append(ChunkBuffer s) throws NullPointerException {
152        for (Chunk chunk : s.chunks) {
153          write(chunk);
154        }
155        if (s.next != null && !s.next.equals(Style.style())) {
156          write(s.next);
157        }
158        return this;
159      }
160    
161      public void write(Chunk chunk) throws NullPointerException {
162        if (chunk instanceof Style) {
163          append((Style)chunk);
164        } else if (chunk instanceof Text){
165          append(((Text)chunk).buffer);
166        } else {
167          cls();
168        }
169      }
170    
171      public ChunkBuffer append(Object o) throws NullPointerException {
172        if (o == null) {
173          throw new NullPointerException("No null accepted");
174        }
175        if (o instanceof ChunkBuffer) {
176          append((ChunkBuffer)o);
177        } else if (o instanceof Chunk) {
178          write((Chunk)o);
179        } else {
180          CharSequence s;
181          if (o instanceof CharSequence) {
182            s = (CharSequence)o;
183          } else {
184            s = o.toString();
185          }
186          append(s);
187        }
188        return this;
189      }
190    
191      public boolean contains(Object o) {
192        return toString().contains(o.toString());
193      }
194    
195      public boolean isEmpty() {
196        return chunks.isEmpty();
197      }
198    
199      public void clear() {
200        chunks.clear();
201      }
202    
203      @Override
204      public int hashCode() {
205        return toString().hashCode();
206      }
207    
208      @Override
209      public boolean equals(Object obj) {
210        if (obj == this) {
211          return true;
212        }
213        if (obj instanceof ChunkBuffer) {
214          ChunkBuffer that = (ChunkBuffer)obj;
215          return toString().equals(that.toString());
216        }
217        return false;
218      }
219    
220      @Override
221      public String toString() {
222        StringBuilder sb = new StringBuilder();
223        for (Chunk chunk : chunks) {
224          if (chunk instanceof Text) {
225            sb.append(((Text)chunk).buffer);
226          }
227        }
228        return sb.toString();
229      }
230    
231      public void writeTo(Pipe<Chunk> writer) throws IOException {
232        for (Chunk chunk : chunks) {
233          writer.provide(chunk);
234        }
235      }
236    }