1/*
2 * ConnectBot: simple, powerful, open-source SSH client for Android
3 * Copyright 2007 Kenny Root, Jeffrey Sharkey
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18package org.connectbot.service;
19
20import com.googlecode.android_scripting.Log;
21
22import de.mud.terminal.vt320;
23
24import org.apache.harmony.niochar.charset.additional.IBM437;
25import org.connectbot.transport.AbsTransport;
26import org.connectbot.util.EastAsianWidth;
27
28import java.io.IOException;
29import java.nio.ByteBuffer;
30import java.nio.CharBuffer;
31import java.nio.charset.Charset;
32import java.nio.charset.CharsetDecoder;
33import java.nio.charset.CoderResult;
34import java.nio.charset.CodingErrorAction;
35
36/**
37 */
38public class Relay implements Runnable {
39
40  private static final int BUFFER_SIZE = 4096;
41
42  private static boolean useJNI = true;
43
44  private TerminalBridge bridge;
45
46  private Charset currentCharset;
47  private CharsetDecoder decoder;
48  private boolean isLegacyEastAsian = false;
49
50  private AbsTransport transport;
51
52  private vt320 buffer;
53
54  private ByteBuffer byteBuffer;
55  private CharBuffer charBuffer;
56
57  private byte[] byteArray;
58  private char[] charArray;
59
60  static {
61    useJNI = EastAsianWidth.useJNI;
62  }
63
64  public Relay(TerminalBridge bridge, AbsTransport transport, vt320 buffer, String encoding) {
65    setCharset(encoding);
66    this.bridge = bridge;
67    this.transport = transport;
68    this.buffer = buffer;
69  }
70
71  public void setCharset(String encoding) {
72    Log.d("changing charset to " + encoding);
73    Charset charset;
74    if (encoding.equals("CP437")) {
75      charset = new IBM437("IBM437", new String[] { "IBM437", "CP437" });
76    } else {
77      charset = Charset.forName(encoding);
78    }
79
80    if (charset == currentCharset || charset == null) {
81      return;
82    }
83
84    CharsetDecoder newCd = charset.newDecoder();
85    newCd.onUnmappableCharacter(CodingErrorAction.REPLACE);
86    newCd.onMalformedInput(CodingErrorAction.REPLACE);
87
88    currentCharset = charset;
89    synchronized (this) {
90      decoder = newCd;
91    }
92  }
93
94  public void run() {
95    byteBuffer = ByteBuffer.allocate(BUFFER_SIZE);
96    charBuffer = CharBuffer.allocate(BUFFER_SIZE);
97
98    /* for both JNI and non-JNI method */
99    byte[] wideAttribute = new byte[BUFFER_SIZE];
100
101    /* non-JNI fallback method */
102    float[] widths = null;
103
104    if (!useJNI) {
105      widths = new float[BUFFER_SIZE];
106    }
107
108    byteArray = byteBuffer.array();
109    charArray = charBuffer.array();
110
111    CoderResult result;
112
113    int bytesRead = 0;
114    byteBuffer.limit(0);
115    int bytesToRead;
116    int offset;
117    int charWidth;
118
119    try {
120      while (true) {
121        charWidth = bridge.charWidth;
122        bytesToRead = byteBuffer.capacity() - byteBuffer.limit();
123        offset = byteBuffer.arrayOffset() + byteBuffer.limit();
124        bytesRead = transport.read(byteArray, offset, bytesToRead);
125
126        if (bytesRead > 0) {
127          byteBuffer.limit(byteBuffer.limit() + bytesRead);
128
129          synchronized (this) {
130            result = decoder.decode(byteBuffer, charBuffer, false);
131          }
132
133          if (result.isUnderflow() && byteBuffer.limit() == byteBuffer.capacity()) {
134            byteBuffer.compact();
135            byteBuffer.limit(byteBuffer.position());
136            byteBuffer.position(0);
137          }
138
139          offset = charBuffer.position();
140
141          if (!useJNI) {
142            bridge.getPaint().getTextWidths(charArray, 0, offset, widths);
143            for (int i = 0; i < offset; i++) {
144              wideAttribute[i] = (byte) (((int) widths[i] != charWidth) ? 1 : 0);
145            }
146          } else {
147            EastAsianWidth.measure(charArray, 0, charBuffer.position(), wideAttribute,
148                isLegacyEastAsian);
149          }
150          buffer.putString(charArray, wideAttribute, 0, charBuffer.position());
151          charBuffer.clear();
152          bridge.redraw();
153        }
154      }
155    } catch (IOException e) {
156      Log.e("Problem while handling incoming data in relay thread", e);
157    }
158  }
159}
160