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