Skip to content

Commit

Permalink
WIP OpcUaJsonDecoder + DateTime refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
kevinherron committed Jul 7, 2022
1 parent f1ba4ac commit 7084d22
Show file tree
Hide file tree
Showing 6 changed files with 386 additions and 98 deletions.
Expand Up @@ -12,6 +12,9 @@

import java.io.IOException;
import java.io.Reader;
import java.time.Instant;
import java.time.format.DateTimeParseException;
import java.util.Base64;
import java.util.UUID;
import java.util.function.Function;

Expand Down Expand Up @@ -403,27 +406,169 @@ public Double readDouble(String field) throws UaSerializationException {

@Override
public String readString(String field) throws UaSerializationException {
return null;
// String values shall be encoded as JSON strings.
// Any characters which are not allowed in JSON strings are escaped
// using the rules defined in RFC 7159.

try {
if (field != null) {
String nextName = jsonReader.nextName();
if (!field.equals(nextName)) {
throw new UaSerializationException(
StatusCodes.Bad_DecodingError,
String.format("readString: %s != %s", field, nextName)
);
}
}

if (jsonReader.peek() == JsonToken.STRING) {
return jsonReader.nextString();
} else {
throw new UaSerializationException(
StatusCodes.Bad_DecodingError,
"readString: unexpected token: " + jsonReader.peek()
);
}
} catch (IOException e) {
throw new UaSerializationException(StatusCodes.Bad_DecodingError, e);
}
}

@Override
public DateTime readDateTime(String field) throws UaSerializationException {
return null;
// DateTime values shall be formatted as specified by ISO 8601:2004
// and encoded as a JSON string.
// DateTime values which exceed the minimum or maximum values supported
// on a platform shall be encoded as "0001-01-01T00:00:00Z" or
// "9999-12-31T23:59:59Z" respectively. During decoding, these values
// shall be converted to the minimum or maximum values supported on the
// platform.

try {
if (field != null) {
String nextName = jsonReader.nextName();
if (!field.equals(nextName)) {
throw new UaSerializationException(
StatusCodes.Bad_DecodingError,
String.format("readDateTime: %s != %s", field, nextName)
);
}
}

if (jsonReader.peek() == JsonToken.STRING) {
String s = jsonReader.nextString();
try {
Instant instant = Instant.parse(s);

if (instant.isBefore(DateTime.MIN_ISO_8601_INSTANT)) {
return DateTime.MIN_DATE_TIME;
} else if (instant.isAfter(DateTime.MAX_ISO_8601_INSTANT)) {
return DateTime.MAX_DATE_TIME;
} else {
return new DateTime(instant);
}
} catch (DateTimeParseException e) {
throw new UaSerializationException(StatusCodes.Bad_DecodingError, e);
}
} else {
throw new UaSerializationException(
StatusCodes.Bad_DecodingError,
"readDateTime: unexpected token: " + jsonReader.peek()
);
}
} catch (IOException e) {
throw new UaSerializationException(StatusCodes.Bad_DecodingError, e);
}
}

@Override
public UUID readGuid(String field) throws UaSerializationException {
return null;
try {
if (field != null) {
String nextName = jsonReader.nextName();
if (!field.equals(nextName)) {
throw new UaSerializationException(
StatusCodes.Bad_DecodingError,
String.format("readGuid: %s != %s", field, nextName)
);
}
}

if (jsonReader.peek() == JsonToken.STRING) {
String s = jsonReader.nextString();

return UUID.fromString(s);
} else {
throw new UaSerializationException(
StatusCodes.Bad_DecodingError,
"readGuid: unexpected token: " + jsonReader.peek()
);
}
} catch (IOException e) {
throw new UaSerializationException(StatusCodes.Bad_DecodingError, e);
}
}

@Override
public ByteString readByteString(String field) throws UaSerializationException {
return null;
// ByteString values shall be formatted as a Base64 text and encoded as
// a JSON string.
// Any characters which are not allowed in JSON strings are escaped
// using the rules defined in RFC 7159.

try {
if (field != null) {
String nextName = jsonReader.nextName();
if (!field.equals(nextName)) {
throw new UaSerializationException(
StatusCodes.Bad_DecodingError,
String.format("readByteString: %s != %s", field, nextName)
);
}
}

if (jsonReader.peek() == JsonToken.STRING) {
String s = jsonReader.nextString();
byte[] bs = Base64.getDecoder().decode(s);

return ByteString.of(bs);
} else {
throw new UaSerializationException(
StatusCodes.Bad_DecodingError,
"readByteString: unexpected token: " + jsonReader.peek()
);
}
} catch (IOException e) {
throw new UaSerializationException(StatusCodes.Bad_DecodingError, e);
}
}

@Override
public XmlElement readXmlElement(String field) throws UaSerializationException {
return null;
try {
if (field != null) {
String nextName = jsonReader.nextName();
if (!field.equals(nextName)) {
throw new UaSerializationException(
StatusCodes.Bad_DecodingError,
String.format("readXmlElement: %s != %s", field, nextName)
);
}
}

if (jsonReader.peek() == JsonToken.STRING) {
String s = jsonReader.nextString();

return new XmlElement(s);
} else {
throw new UaSerializationException(
StatusCodes.Bad_DecodingError,
"readXmlElement: unexpected token: " + jsonReader.peek()
);
}
} catch (IOException e) {
throw new UaSerializationException(StatusCodes.Bad_DecodingError, e);
}
}

@Override
Expand Down
Expand Up @@ -15,10 +15,8 @@
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.lang.reflect.Array;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.time.format.DateTimeFormatter;
import java.util.Base64;
import java.util.TimeZone;
import java.util.UUID;
import java.util.function.BiConsumer;

Expand Down Expand Up @@ -53,40 +51,6 @@

public class OpcUaJsonEncoder implements UaEncoder {

/**
* Shared ISO-8601 {@link DateFormat} instance.
* <p>
* Do not access directly; use {@link #dateTimeToIso8601UtcString(DateTime)}.
*/
private static final DateFormat ISO_8601_UTC_DATE_FORMAT;

static {
ISO_8601_UTC_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
ISO_8601_UTC_DATE_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC"));
}

/**
* Minimum ISO-8601 formatted string.
*/
static final String MIN_ISO_8601 = "0001-01-01T00:00:00Z";

/**
* Maximum ISO-8601 formatted string.
*/
static final String MAX_ISO_8601 = "9999-12-31T23:59:59Z";

/**
* Minimum DateTime value that can be formatted in ISO-8601, i.e.
* "0001-01-01T00:00:00Z".
*/
static final DateTime MIN_DATE_TIME = new DateTime(-504912960000000000L);

/**
* Maximum DateTime value that can be formatted in ISO-8601, i.e.
* "9999-12-31T23:59:59Z".
*/
static final DateTime MAX_DATE_TIME = new DateTime(2650467743990000000L);

boolean reversible = true;
SerializationContext serializationContext;

Expand Down Expand Up @@ -121,18 +85,6 @@ void reset(OutputStream outputStream) {
jsonWriter.setHtmlSafe(false);
}

/**
* Format {@param dateTime} as an ISO-8601 String.
*
* @param dateTime the {@link DateTime} to format.
* @return an ISO-8601 formatted date string.
*/
static String dateTimeToIso8601UtcString(DateTime dateTime) {
synchronized (ISO_8601_UTC_DATE_FORMAT) {
return ISO_8601_UTC_DATE_FORMAT.format(dateTime.getJavaDate());
}
}

@Override
public void writeBoolean(String field, Boolean value) throws UaSerializationException {
try {
Expand Down Expand Up @@ -330,12 +282,13 @@ public void writeDateTime(String field, DateTime value) throws UaSerializationEx
if (field != null) {
jsonWriter.name(field);
}
if (value.getUtcTime() < MIN_DATE_TIME.getUtcTime()) {
jsonWriter.value(MIN_ISO_8601);
} else if (value.getUtcTime() > MAX_DATE_TIME.getUtcTime()) {
jsonWriter.value(MAX_ISO_8601);

if (value.getJavaInstant().isBefore(DateTime.MIN_ISO_8601_INSTANT)) {
jsonWriter.value(DateTime.MIN_ISO_8601_STRING);
} else if (value.getJavaInstant().isAfter(DateTime.MAX_ISO_8601_INSTANT)) {
jsonWriter.value(DateTime.MAX_ISO_8601_STRING);
} else {
jsonWriter.value(dateTimeToIso8601UtcString(value));
jsonWriter.value(DateTimeFormatter.ISO_INSTANT.format(value.getJavaInstant()));
}
} catch (IOException e) {
throw new UaSerializationException(StatusCodes.Bad_EncodingError, e);
Expand Down

0 comments on commit 7084d22

Please sign in to comment.