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