001    /*
002     * Copyright (C) 2010 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.shell.io;
021    
022    import org.crsh.text.CharReader;
023    import org.crsh.text.ShellAppendable;
024    import org.crsh.text.Style;
025    
026    import java.io.IOException;
027    
028    /**
029     * @author <a href="mailto:julien.viet@exoplatform.com">Julien Viet</a>
030     * @version $Revision$
031     */
032    public class ShellWriter implements ShellAppendable {
033    
034      /** . */
035      private static final int NOT_PADDED = 0;
036    
037      /** . */
038      private static final int PADDING = 1;
039    
040      /** . */
041      private static final int PADDED = 2;
042    
043      /** . */
044      private final CharReader reader;
045    
046      /** . */
047      private final String lineFeed;
048    
049      /** . */
050      private int status;
051    
052      public ShellWriter(CharReader reader) {
053        this(reader, "\r\n");
054      }
055    
056      public ShellWriter(CharReader reader, String lineFeed) {
057        this.reader = reader;
058        this.lineFeed = lineFeed;
059        this.status = NOT_PADDED;
060      }
061    
062      public Appendable append(char c) throws IOException {
063        return append(null, c);
064      }
065    
066      public ShellWriter append(ShellWriterContext ctx, final char c) throws IOException {
067        return append(ctx, Character.toString(c));
068      }
069    
070      public ShellWriter append(final Style d) {
071        reader.append(d);
072        return this;
073      }
074    
075      public Appendable append(CharSequence csq, int start, int end) throws IOException {
076        return append(null, csq, start, end);
077      }
078    
079      public Appendable append(CharSequence csq) throws IOException {
080        return append(null, csq);
081      }
082    
083      public ShellWriter append(ShellWriterContext ctx, CharSequence csq) throws IOException {
084        return append(ctx, csq, 0, csq.length());
085      }
086    
087      public ShellWriter append(ShellWriterContext ctx, CharSequence csq, int start, int end) throws IOException {
088        int previous = start;
089        int to = start + end;
090        for (int i = start;i < to;i++) {
091          char c = csq.charAt(i);
092          if (c == '\r') {
093            if (i > previous) {
094              realAppend(ctx, csq, previous, i);
095            }
096            previous = i + 1;
097          } else if (c == '\n') {
098            if (i > previous) {
099              realAppend(ctx, csq, previous, i);
100            }
101            writeLF(ctx);
102            previous = i + 1;
103            i++;
104          }
105        }
106        if (to != previous) {
107          realAppend(ctx, csq, previous, to);
108        }
109        return this;
110      }
111    
112      private void realAppend(ShellWriterContext ctx, CharSequence csq, int off, int end) throws IOException {
113        if (end > off) {
114    
115          //
116          switch (status) {
117            case NOT_PADDED:
118              if (ctx != null) {
119                status = PADDING;
120                ctx.pad(this);
121              }
122              status = PADDED;
123              break;
124            case PADDING:
125            case PADDED:
126              // Do nothing
127              break;
128            default:
129              throw new AssertionError();
130          }
131    
132          //
133          reader.append(csq.subSequence(off, end).toString());
134    
135          //
136          switch (status) {
137            case PADDING:
138              // Do nothing
139              break;
140            case PADDED:
141              if (ctx != null) {
142                ctx.text(csq, off, end);
143              }
144              break;
145            default:
146              throw new AssertionError();
147          }
148        }
149      }
150    
151      private void writeLF(ShellWriterContext ctx) throws IOException {
152        switch (status) {
153          case PADDING:
154            throw new IllegalStateException();
155          case PADDED:
156            status = NOT_PADDED;
157          case NOT_PADDED:
158            reader.append(lineFeed);
159            if (ctx != null) {
160              ctx.lineFeed();
161            }
162            break;
163          default:
164            throw new AssertionError();
165        }
166      }
167    
168      public boolean isEmpty() {
169        return reader.isEmpty();
170      }
171    }