check lone surrogate (compatible with utf8.js v2.1.0)

This commit is contained in:
nkzawa
2015-10-11 01:10:06 +09:00
parent 0974154181
commit 45977f1a90
8 changed files with 88 additions and 47 deletions

View File

@@ -5,6 +5,7 @@ import io.socket.emitter.Emitter;
import io.socket.engineio.parser.Packet; import io.socket.engineio.parser.Packet;
import io.socket.engineio.parser.Parser; import io.socket.engineio.parser.Parser;
import io.socket.thread.EventThread; import io.socket.thread.EventThread;
import io.socket.utf8.UTF8Exception;
import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext; import javax.net.ssl.SSLContext;
@@ -98,7 +99,11 @@ public abstract class Transport extends Emitter {
@Override @Override
public void run() { public void run() {
if (Transport.this.readyState == ReadyState.OPEN) { if (Transport.this.readyState == ReadyState.OPEN) {
try {
Transport.this.write(packets); Transport.this.write(packets);
} catch (UTF8Exception err) {
throw new RuntimeException(err);
}
} else { } else {
throw new RuntimeException("Transport not open"); throw new RuntimeException("Transport not open");
} }
@@ -129,7 +134,7 @@ public abstract class Transport extends Emitter {
this.emit(EVENT_CLOSE); this.emit(EVENT_CLOSE);
} }
abstract protected void write(Packet[] packets); abstract protected void write(Packet[] packets) throws UTF8Exception;
abstract protected void doOpen(); abstract protected void doOpen();

View File

@@ -7,6 +7,7 @@ import io.socket.engineio.parser.Parser;
import io.socket.parseqs.ParseQS; import io.socket.parseqs.ParseQS;
import io.socket.thread.EventThread; import io.socket.thread.EventThread;
import io.socket.emitter.Emitter; import io.socket.emitter.Emitter;
import io.socket.utf8.UTF8Exception;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
@@ -152,7 +153,11 @@ abstract public class Polling extends Transport {
@Override @Override
public void call(Object... args) { public void call(Object... args) {
logger.fine("writing close packet"); logger.fine("writing close packet");
self.write(new Packet[] {new Packet(Packet.CLOSE)}); try {
self.write(new Packet[]{new Packet(Packet.CLOSE)});
} catch (UTF8Exception err) {
throw new RuntimeException(err);
}
} }
}; };
@@ -167,7 +172,7 @@ abstract public class Polling extends Transport {
} }
} }
protected void write(Packet[] packets) { protected void write(Packet[] packets) throws UTF8Exception {
final Polling self = this; final Polling self = this;
this.writable = false; this.writable = false;
final Runnable callbackfn = new Runnable() { final Runnable callbackfn = new Runnable() {

View File

@@ -12,6 +12,7 @@ import com.squareup.okhttp.Response;
import com.squareup.okhttp.ws.WebSocket.PayloadType; import com.squareup.okhttp.ws.WebSocket.PayloadType;
import com.squareup.okhttp.ws.WebSocketCall; import com.squareup.okhttp.ws.WebSocketCall;
import com.squareup.okhttp.ws.WebSocketListener; import com.squareup.okhttp.ws.WebSocketListener;
import io.socket.utf8.UTF8Exception;
import okio.Buffer; import okio.Buffer;
import okio.BufferedSource; import okio.BufferedSource;
@@ -142,7 +143,7 @@ public class WebSocket extends Transport {
client.getDispatcher().getExecutorService().shutdown(); client.getDispatcher().getExecutorService().shutdown();
} }
protected void write(Packet[] packets) { protected void write(Packet[] packets) throws UTF8Exception {
final WebSocket self = this; final WebSocket self = this;
this.writable = false; this.writable = false;
for (Packet packet : packets) { for (Packet packet : packets) {

View File

@@ -38,11 +38,11 @@ public class Parser {
private Parser() {} private Parser() {}
public static void encodePacket(Packet packet, EncodeCallback callback) { public static void encodePacket(Packet packet, EncodeCallback callback) throws UTF8Exception {
encodePacket(packet, false, callback); encodePacket(packet, false, callback);
} }
public static void encodePacket(Packet packet, boolean utf8encode, EncodeCallback callback) { public static void encodePacket(Packet packet, boolean utf8encode, EncodeCallback callback) throws UTF8Exception {
if (packet.data instanceof byte[]) { if (packet.data instanceof byte[]) {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
Packet<byte[]> _packet = packet; Packet<byte[]> _packet = packet;
@@ -109,7 +109,7 @@ public class Parser {
return new Packet<byte[]>(packetslist.get(type), intArray); return new Packet<byte[]>(packetslist.get(type), intArray);
} }
public static void encodePayload(Packet[] packets, EncodeCallback<byte[]> callback) { public static void encodePayload(Packet[] packets, EncodeCallback<byte[]> callback) throws UTF8Exception {
if (packets.length == 0) { if (packets.length == 0) {
callback.call(new byte[0]); callback.call(new byte[0]);
return; return;

View File

@@ -14,8 +14,8 @@ public class UTF8 {
private static int byteCount; private static int byteCount;
private static int byteIndex; private static int byteIndex;
public static String encode(String string) { public static String encode(String string) throws UTF8Exception {
int[] codePoints = uc2decode(string); int[] codePoints = ucs2decode(string);
int length = codePoints.length; int length = codePoints.length;
int index = -1; int index = -1;
int codePoint; int codePoint;
@@ -28,7 +28,7 @@ public class UTF8 {
} }
public static String decode(String byteString) throws UTF8Exception { public static String decode(String byteString) throws UTF8Exception {
byteArray = uc2decode(byteString); byteArray = ucs2decode(byteString);
byteCount = byteArray.length; byteCount = byteArray.length;
byteIndex = 0; byteIndex = 0;
List<Integer> codePoints = new ArrayList<Integer>(); List<Integer> codePoints = new ArrayList<Integer>();
@@ -39,7 +39,7 @@ public class UTF8 {
return ucs2encode(listToArray(codePoints)); return ucs2encode(listToArray(codePoints));
} }
private static int[] uc2decode(String string) { private static int[] ucs2decode(String string) {
int length = string.length(); int length = string.length();
int[] output = new int[string.codePointCount(0, length)]; int[] output = new int[string.codePointCount(0, length)];
int counter = 0; int counter = 0;
@@ -51,7 +51,7 @@ public class UTF8 {
return output; return output;
} }
private static String encodeCodePoint(int codePoint) { private static String encodeCodePoint(int codePoint) throws UTF8Exception {
StringBuilder symbol = new StringBuilder(); StringBuilder symbol = new StringBuilder();
if ((codePoint & 0xFFFFFF80) == 0) { if ((codePoint & 0xFFFFFF80) == 0) {
return symbol.append(Character.toChars(codePoint)).toString(); return symbol.append(Character.toChars(codePoint)).toString();
@@ -59,6 +59,7 @@ public class UTF8 {
if ((codePoint & 0xFFFFF800) == 0) { if ((codePoint & 0xFFFFF800) == 0) {
symbol.append(Character.toChars(((codePoint >> 6) & 0x1F) | 0xC0)); symbol.append(Character.toChars(((codePoint >> 6) & 0x1F) | 0xC0));
} else if ((codePoint & 0xFFFF0000) == 0) { } else if ((codePoint & 0xFFFF0000) == 0) {
checkScalarValue(codePoint);
symbol.append(Character.toChars(((codePoint >> 12) & 0x0F) | 0xE0)); symbol.append(Character.toChars(((codePoint >> 12) & 0x0F) | 0xE0));
symbol.append(createByte(codePoint, 6)); symbol.append(createByte(codePoint, 6));
} else if ((codePoint & 0xFFE00000) == 0) { } else if ((codePoint & 0xFFE00000) == 0) {
@@ -111,6 +112,7 @@ public class UTF8 {
byte3 = readContinuationByte(); byte3 = readContinuationByte();
codePoint = ((byte1 & 0x0F) << 12) | (byte2 << 6) | byte3; codePoint = ((byte1 & 0x0F) << 12) | (byte2 << 6) | byte3;
if (codePoint >= 0x0800) { if (codePoint >= 0x0800) {
checkScalarValue(codePoint);
return codePoint; return codePoint;
} else { } else {
throw new UTF8Exception("Invalid continuation byte"); throw new UTF8Exception("Invalid continuation byte");
@@ -153,6 +155,15 @@ public class UTF8 {
return output.toString(); return output.toString();
} }
private static void checkScalarValue(int codePoint) throws UTF8Exception {
if (codePoint >= 0xD800 && codePoint <= 0xDFFF) {
throw new UTF8Exception(
"Lone surrogate U+" + Integer.toHexString(codePoint).toUpperCase() +
" is not a scalar value"
);
}
}
private static int[] listToArray(List<Integer> list) { private static int[] listToArray(List<Integer> list) {
int size = list.size(); int size = list.size();
int[] array = new int[size]; int[] array = new int[size];

View File

@@ -72,7 +72,7 @@ public class ConnectionTest extends Connection {
socket.on(Socket.EVENT_OPEN, new Emitter.Listener() { socket.on(Socket.EVENT_OPEN, new Emitter.Listener() {
@Override @Override
public void call(Object... args) { public void call(Object... args) {
socket.send("\uD800-\uDB7F\uDB80-\uDBFF\uDC00-\uDFFF\uE000-\uF8FF"); socket.send("\uD800\uDC00-\uDB7F\uDFFF\uDB80\uDC00-\uDBFF\uDFFF\uE000-\uF8FF");
socket.on(Socket.EVENT_MESSAGE, new Emitter.Listener() { socket.on(Socket.EVENT_MESSAGE, new Emitter.Listener() {
@Override @Override
public void call(Object... args) { public void call(Object... args) {
@@ -85,7 +85,7 @@ public class ConnectionTest extends Connection {
}); });
socket.open(); socket.open();
assertThat((String)values.take(), is("\uD800-\uDB7F\uDB80-\uDBFF\uDC00-\uDFFF\uE000-\uF8FF")); assertThat((String)values.take(), is("\uD800\uDC00-\uDB7F\uDFFF\uDB80\uDC00-\uDBFF\uDFFF\uE000-\uF8FF"));
} }
@Test(timeout = TIMEOUT) @Test(timeout = TIMEOUT)

View File

@@ -1,5 +1,6 @@
package io.socket.engineio.parser; package io.socket.engineio.parser;
import io.socket.utf8.UTF8Exception;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.junit.runners.JUnit4; import org.junit.runners.JUnit4;
@@ -14,7 +15,7 @@ public class ParserTest {
static final String ERROR_DATA = "parser error"; static final String ERROR_DATA = "parser error";
@Test @Test
public void encodeAsString() { public void encodeAsString() throws UTF8Exception {
encodePacket(new Packet<String>(Packet.MESSAGE, "test"), new EncodeCallback<String>() { encodePacket(new Packet<String>(Packet.MESSAGE, "test"), new EncodeCallback<String>() {
@Override @Override
public void call(String data) { public void call(String data) {
@@ -24,7 +25,7 @@ public class ParserTest {
} }
@Test @Test
public void decodeAsPacket() { public void decodeAsPacket() throws UTF8Exception {
encodePacket(new Packet<String>(Packet.MESSAGE, "test"), new EncodeCallback<String>() { encodePacket(new Packet<String>(Packet.MESSAGE, "test"), new EncodeCallback<String>() {
@Override @Override
public void call(String data) { public void call(String data) {
@@ -34,7 +35,7 @@ public class ParserTest {
} }
@Test @Test
public void noData() { public void noData() throws UTF8Exception {
encodePacket(new Packet(Packet.MESSAGE), new EncodeCallback<String>() { encodePacket(new Packet(Packet.MESSAGE), new EncodeCallback<String>() {
@Override @Override
public void call(String data) { public void call(String data) {
@@ -46,7 +47,7 @@ public class ParserTest {
} }
@Test @Test
public void encodeOpenPacket() { public void encodeOpenPacket() throws UTF8Exception {
encodePacket(new Packet<String>(Packet.OPEN, "{\"some\":\"json\"}"), new EncodeCallback<String>() { encodePacket(new Packet<String>(Packet.OPEN, "{\"some\":\"json\"}"), new EncodeCallback<String>() {
@Override @Override
public void call(String data) { public void call(String data) {
@@ -58,7 +59,7 @@ public class ParserTest {
} }
@Test @Test
public void encodeClosePacket() { public void encodeClosePacket() throws UTF8Exception {
encodePacket(new Packet<String>(Packet.CLOSE), new EncodeCallback<String>() { encodePacket(new Packet<String>(Packet.CLOSE), new EncodeCallback<String>() {
@Override @Override
public void call(String data) { public void call(String data) {
@@ -69,7 +70,7 @@ public class ParserTest {
} }
@Test @Test
public void encodePingPacket() { public void encodePingPacket() throws UTF8Exception {
encodePacket(new Packet<String>(Packet.PING, "1"), new EncodeCallback<String>() { encodePacket(new Packet<String>(Packet.PING, "1"), new EncodeCallback<String>() {
@Override @Override
public void call(String data) { public void call(String data) {
@@ -81,7 +82,7 @@ public class ParserTest {
} }
@Test @Test
public void encodePongPacket() { public void encodePongPacket() throws UTF8Exception {
encodePacket(new Packet<String>(Packet.PONG, "1"), new EncodeCallback<String>() { encodePacket(new Packet<String>(Packet.PONG, "1"), new EncodeCallback<String>() {
@Override @Override
public void call(String data) { public void call(String data) {
@@ -93,7 +94,7 @@ public class ParserTest {
} }
@Test @Test
public void encodeMessagePacket() { public void encodeMessagePacket() throws UTF8Exception {
encodePacket(new Packet<String>(Packet.MESSAGE, "aaa"), new EncodeCallback<String>() { encodePacket(new Packet<String>(Packet.MESSAGE, "aaa"), new EncodeCallback<String>() {
@Override @Override
public void call(String data) { public void call(String data) {
@@ -105,7 +106,7 @@ public class ParserTest {
} }
@Test @Test
public void encodeUTF8SpecialCharsMessagePacket() { public void encodeUTF8SpecialCharsMessagePacket() throws UTF8Exception {
encodePacket(new Packet<String>(Packet.MESSAGE, "utf8 — string"), new EncodeCallback<String>() { encodePacket(new Packet<String>(Packet.MESSAGE, "utf8 — string"), new EncodeCallback<String>() {
@Override @Override
public void call(String data) { public void call(String data) {
@@ -117,7 +118,7 @@ public class ParserTest {
} }
@Test @Test
public void encodeMessagePacketCoercingToString() { public void encodeMessagePacketCoercingToString() throws UTF8Exception {
encodePacket(new Packet<Integer>(Packet.MESSAGE, 1), new EncodeCallback<String>() { encodePacket(new Packet<Integer>(Packet.MESSAGE, 1), new EncodeCallback<String>() {
@Override @Override
public void call(String data) { public void call(String data) {
@@ -129,7 +130,7 @@ public class ParserTest {
} }
@Test @Test
public void encodeUpgradePacket() { public void encodeUpgradePacket() throws UTF8Exception {
encodePacket(new Packet<String>(Packet.UPGRADE), new EncodeCallback<String>() { encodePacket(new Packet<String>(Packet.UPGRADE), new EncodeCallback<String>() {
@Override @Override
public void call(String data) { public void call(String data) {
@@ -140,7 +141,7 @@ public class ParserTest {
} }
@Test @Test
public void encodingFormat() { public void encodingFormat() throws UTF8Exception {
encodePacket(new Packet<String>(Packet.MESSAGE, "test"), new EncodeCallback<String>() { encodePacket(new Packet<String>(Packet.MESSAGE, "test"), new EncodeCallback<String>() {
@Override @Override
public void call(String data) { public void call(String data) {
@@ -177,7 +178,7 @@ public class ParserTest {
} }
@Test @Test
public void encodePayloads() { public void encodePayloads() throws UTF8Exception {
encodePayload(new Packet[]{new Packet(Packet.PING), new Packet(Packet.PONG)}, new EncodeCallback<byte[]>() { encodePayload(new Packet[]{new Packet(Packet.PING), new Packet(Packet.PONG)}, new EncodeCallback<byte[]>() {
@Override @Override
public void call(byte[] data) { public void call(byte[] data) {
@@ -187,7 +188,7 @@ public class ParserTest {
} }
@Test @Test
public void encodeAndDecodePayloads() { public void encodeAndDecodePayloads() throws UTF8Exception {
encodePayload(new Packet[] {new Packet<String>(Packet.MESSAGE, "a")}, new EncodeCallback<byte[]>() { encodePayload(new Packet[] {new Packet<String>(Packet.MESSAGE, "a")}, new EncodeCallback<byte[]>() {
@Override @Override
public void call(byte[] data) { public void call(byte[] data) {
@@ -221,7 +222,7 @@ public class ParserTest {
} }
@Test @Test
public void encodeAndDecodeEmptyPayloads() { public void encodeAndDecodeEmptyPayloads() throws UTF8Exception {
encodePayload(new Packet[] {}, new EncodeCallback<byte[]>() { encodePayload(new Packet[] {}, new EncodeCallback<byte[]>() {
@Override @Override
public void call(byte[] data) { public void call(byte[] data) {
@@ -335,7 +336,7 @@ public class ParserTest {
} }
@Test @Test
public void encodeBinaryMessage() { public void encodeBinaryMessage() throws UTF8Exception {
final byte[] data = new byte[5]; final byte[] data = new byte[5];
for (int i = 0; i < data.length; i++) { for (int i = 0; i < data.length; i++) {
data[0] = (byte)i; data[0] = (byte)i;
@@ -351,7 +352,7 @@ public class ParserTest {
} }
@Test @Test
public void encodeBinaryContents() { public void encodeBinaryContents() throws UTF8Exception {
final byte[] firstBuffer = new byte[5]; final byte[] firstBuffer = new byte[5];
for (int i = 0 ; i < firstBuffer.length; i++) { for (int i = 0 ; i < firstBuffer.length; i++) {
firstBuffer[0] = (byte)i; firstBuffer[0] = (byte)i;
@@ -385,7 +386,7 @@ public class ParserTest {
} }
@Test @Test
public void encodeMixedBinaryAndStringContents() { public void encodeMixedBinaryAndStringContents() throws UTF8Exception {
final byte[] firstBuffer = new byte[123]; final byte[] firstBuffer = new byte[123];
for (int i = 0 ; i < firstBuffer.length; i++) { for (int i = 0 ; i < firstBuffer.length; i++) {
firstBuffer[0] = (byte)i; firstBuffer[0] = (byte)i;

View File

@@ -26,25 +26,25 @@ public class UTF8Test {
new Data(0x07FF, "\uFFFF", "\u00EF\u00BF\u00BF"), new Data(0x07FF, "\uFFFF", "\u00EF\u00BF\u00BF"),
// unmatched surrogate halves // unmatched surrogate halves
// high surrogates: 0xD800 to 0xDBFF // high surrogates: 0xD800 to 0xDBFF
new Data(0xD800, "\uD800", "\u00ED\u00A0\u0080"), new Data(0xD800, "\uD800", "\u00ED\u00A0\u0080", true),
new Data("High surrogate followed by another high surrogate", new Data("High surrogate followed by another high surrogate",
"\uD800\uD800", "\u00ED\u00A0\u0080\u00ED\u00A0\u0080"), "\uD800\uD800", "\u00ED\u00A0\u0080\u00ED\u00A0\u0080", true),
new Data("High surrogate followed by a symbol that is not a surrogate", new Data("High surrogate followed by a symbol that is not a surrogate",
"\uD800A", "\u00ED\u00A0\u0080A"), "\uD800A", "\u00ED\u00A0\u0080A", true),
new Data("Unmatched high surrogate, followed by a surrogate pair, followed by an unmatched high surrogate", new Data("Unmatched high surrogate, followed by a surrogate pair, followed by an unmatched high surrogate",
"\uD800\uD834\uDF06\uD800", "\u00ED\u00A0\u0080\u00F0\u009D\u008C\u0086\u00ED\u00A0\u0080"), "\uD800\uD834\uDF06\uD800", "\u00ED\u00A0\u0080\u00F0\u009D\u008C\u0086\u00ED\u00A0\u0080", true),
new Data(0xD9AF, "\uD9AF", "\u00ED\u00A6\u00AF"), new Data(0xD9AF, "\uD9AF", "\u00ED\u00A6\u00AF", true),
new Data(0xDBFF, "\uDBFF", "\u00ED\u00AF\u00BF"), new Data(0xDBFF, "\uDBFF", "\u00ED\u00AF\u00BF", true),
// low surrogates: 0xDC00 to 0xDFFF // low surrogates: 0xDC00 to 0xDFFF
new Data(0xDC00, "\uDC00", "\u00ED\u00B0\u0080"), new Data(0xDC00, "\uDC00", "\u00ED\u00B0\u0080", true),
new Data("Low surrogate followed by another low surrogate", new Data("Low surrogate followed by another low surrogate",
"\uDC00\uDC00", "\u00ED\u00B0\u0080\u00ED\u00B0\u0080"), "\uDC00\uDC00", "\u00ED\u00B0\u0080\u00ED\u00B0\u0080", true),
new Data("Low surrogate followed by a symbol that is not a surrogate", new Data("Low surrogate followed by a symbol that is not a surrogate",
"\uDC00A", "\u00ED\u00B0\u0080A"), "\uDC00A", "\u00ED\u00B0\u0080A", true),
new Data("Unmatched low surrogate, followed by a surrogate pair, followed by an unmatched low surrogate", new Data("Unmatched low surrogate, followed by a surrogate pair, followed by an unmatched low surrogate",
"\uDC00\uD834\uDF06\uDC00", "\u00ED\u00B0\u0080\u00F0\u009D\u008C\u0086\u00ED\u00B0\u0080"), "\uDC00\uD834\uDF06\uDC00", "\u00ED\u00B0\u0080\u00F0\u009D\u008C\u0086\u00ED\u00B0\u0080", true),
new Data(0xDEEE, "\uDEEE", "\u00ED\u00BB\u00AE"), new Data(0xDEEE, "\uDEEE", "\u00ED\u00BB\u00AE", true),
new Data(0xDFFF, "\uDFFF", "\u00ED\u00BF\u00BF"), new Data(0xDFFF, "\uDFFF", "\u00ED\u00BF\u00BF", true),
// 4-byte // 4-byte
new Data(0x010000, "\uD800\uDC00", "\u00F0\u0090\u0080\u0080"), new Data(0x010000, "\uD800\uDC00", "\u00F0\u0090\u0080\u0080"),
new Data(0x01D306, "\uD834\uDF06", "\u00F0\u009D\u008C\u0086"), new Data(0x01D306, "\uD834\uDF06", "\u00F0\u009D\u008C\u0086"),
@@ -58,9 +58,16 @@ public class UTF8Test {
public void encodeAndDecode() throws UTF8Exception { public void encodeAndDecode() throws UTF8Exception {
for (Data data : DATA) { for (Data data : DATA) {
String reason = data.description != null? data.description : "U+" + Integer.toHexString(data.codePoint).toUpperCase(); String reason = data.description != null? data.description : "U+" + Integer.toHexString(data.codePoint).toUpperCase();
if (data.error) {
exception.expect(UTF8Exception.class);
UTF8.decode(data.encoded);
exception.expect(UTF8Exception.class);
UTF8.encode(data.decoded);
} else {
assertThat("Encoding: " + reason, data.encoded, is(UTF8.encode(data.decoded))); assertThat("Encoding: " + reason, data.encoded, is(UTF8.encode(data.decoded)));
assertThat("Decoding: " + reason, data.decoded, is(UTF8.decode(data.encoded))); assertThat("Decoding: " + reason, data.decoded, is(UTF8.decode(data.encoded)));
} }
}
exception.expect(UTF8Exception.class); exception.expect(UTF8Exception.class);
UTF8.decode("\uFFFF"); UTF8.decode("\uFFFF");
@@ -80,17 +87,28 @@ public class UTF8Test {
public String description; public String description;
public String decoded; public String decoded;
public String encoded; public String encoded;
public boolean error;
public Data(int codePoint, String decoded, String encoded) { public Data(int codePoint, String decoded, String encoded) {
this(codePoint, decoded, encoded, false);
}
public Data(int codePoint, String decoded, String encoded, boolean error) {
this.codePoint = codePoint; this.codePoint = codePoint;
this.decoded = decoded; this.decoded = decoded;
this.encoded = encoded; this.encoded = encoded;
this.error = error;
} }
public Data(String description, String decoded, String encoded) { public Data(String description, String decoded, String encoded) {
this(description, decoded, encoded, false);
}
public Data(String description, String decoded, String encoded, boolean error) {
this.description = description; this.description = description;
this.decoded = decoded; this.decoded = decoded;
this.encoded = encoded; this.encoded = encoded;
this.error = error;
} }
} }
} }