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