001    package org.crsh.term.spi.net;
002    
003    import org.crsh.term.CodeType;
004    import org.crsh.term.spi.TermIO;
005    import org.crsh.util.AbstractSocketClient;
006    
007    import java.io.IOException;
008    import java.io.InputStream;
009    import java.io.OutputStream;
010    import java.nio.ByteBuffer;
011    import java.nio.charset.Charset;
012    
013    /**
014     *
015     * @author <a href="mailto:julien.viet@exoplatform.com">Julien Viet</a>
016     */
017    public class TermIOClient extends AbstractSocketClient implements TermIO {
018    
019      /** . */
020      private static final Charset UTF_8 = Charset.forName("UTF-8");
021    
022      /** . */
023      private byte[] bytes = new byte[2000];
024    
025      /** . */
026      private ByteBuffer buffer = ByteBuffer.wrap(bytes);
027    
028      /** . */
029      private InputStream in;
030    
031      /** . */
032      private OutputStream out;
033    
034      public TermIOClient(int port) {
035        super(port);
036      }
037    
038      @Override
039      protected void handle(InputStream in, OutputStream out) throws IOException {
040        this.in = in;
041        this.out = out;
042      }
043    
044      private void put(byte b) {
045        if (buffer.remaining() == 0) {
046          byte[] bytesCopy = new byte[bytes.length * 2 + 1];
047          System.arraycopy(bytes, 0, bytesCopy, 0, bytes.length);
048          ByteBuffer bufferCopy = ByteBuffer.wrap(bytesCopy);
049          bufferCopy.position(buffer.position());
050    
051          //
052          bytes = bytesCopy;
053          buffer = bufferCopy;
054        }
055        buffer.put(b);
056      }
057    
058    
059      private int _read(byte[] buffer, int off, int len) throws IOException, Done {
060        int b = in.read(buffer, off, len);
061        if (b == -1) {
062          throw new Done();
063        }
064        return b;
065      }
066    
067      private byte _read() throws IOException, Done {
068        int b = in.read();
069        if (b == -1) {
070          throw new Done();
071        }
072        return (byte)b;
073      }
074    
075      public int read() throws IOException {
076        try {
077          out.write(0);
078          out.flush();
079          byte b = _read();
080          CodeType codeType = CodeType.valueOf(b);
081          if (codeType == null) {
082            throw new UnsupportedOperationException("todo " + b);
083          } else if (codeType == CodeType.CHAR) {
084            byte b1 = _read();
085            byte b2 = _read();
086            return (b1 << 8) + b2;
087          } else {
088            return codeType.ordinal() << 16;
089          }
090        } catch (Done done) {
091          throw new UnsupportedOperationException("implement me", done);
092        }
093      }
094    
095      public int getWidth() {
096        String width = getProperty("width");
097        return Integer.parseInt(width);
098      }
099    
100      public String getProperty(String name) {
101        // We don't process empty name
102        if (name.length() == 0) {
103          return null;
104        }
105        byte[] bytes = name.getBytes(UTF_8);
106        int len = bytes.length;
107        if (len > 256) {
108          throw new IllegalArgumentException("Property name too long : " + name);
109        }
110        try {
111          out.write(8);
112          out.write(len - 1);
113          out.write(bytes);
114          out.flush();
115          len = _read();
116          if (len == 0) {
117            return null;
118          } else if (len == 1) {
119            return "";
120          } else {
121            bytes = new byte[len - 1];
122            _read(bytes, 0, bytes.length);
123            return new String(bytes, 0, bytes.length);
124          }
125    
126          //
127        } catch (Done done) {
128          throw new UnsupportedOperationException("implement me", done);
129        } catch (IOException e) {
130          throw new UnsupportedOperationException("implement me", e);
131        }
132      }
133    
134      public CodeType decode(int code) {
135        code &= 0xFFFF0000;
136        if (code == 0) {
137          return CodeType.CHAR;
138        } else {
139          code >>= 16;
140          return CodeType.valueOf(code);
141        }
142      }
143    
144      public void flush() throws IOException {
145        put((byte)7);
146        out.write(bytes, 0, buffer.position());
147        buffer.clear();
148      }
149    
150      public void write(char c) throws IOException {
151        put((byte)1);
152        put((byte)((c & 0xFF00) >> 8));
153        put((byte)(c & 0xFF));
154      }
155    
156      public void write(String s) throws IOException {
157        int prev = 0;
158        int len = s.length();
159        while (prev < len) {
160          int pos = Math.min(len, prev + 257);
161          int chunkLen = pos - prev;
162          if (chunkLen == 1) {
163            write(s.charAt(prev++));
164          } else {
165            put((byte)2);
166            put((byte)(chunkLen - 2));
167            while (prev < pos) {
168              char c = s.charAt(prev++);
169              put((byte)((c & 0xFF00) >> 8));
170              put((byte)(c & 0xFF));
171            }
172          }
173        }
174      }
175    
176      public void writeDel() throws IOException {
177        put((byte)3);
178      }
179    
180      public void writeCRLF() throws IOException {
181        put((byte)4);
182      }
183    
184      public boolean moveRight(char c) throws IOException {
185        put((byte)5);
186        put((byte)((c & 0xFF00) >> 8));
187        put((byte)(c & 0xFF));
188        return true;
189      }
190    
191      public boolean moveLeft() throws IOException {
192        put((byte)6);
193        return true;
194      }
195    }