[java-support] 04/21: Implement draft serializer, start on unit tests.
Scott Cantor
cantor.2 at osu.edu
Thu Jun 2 14:38:51 UTC 2022
This is an automated email from the git hooks/post-receive script.
scantor pushed a commit to branch dev/JSPT-111
in repository java-support.
View the commit online:
http://git.shibboleth.net/view/?p=java-support.git;a=commit;h=a52d50661616b2781aac561fdf64d212626f4e7b
commit a52d50661616b2781aac561fdf64d212626f4e7b
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Mon May 17 19:16:59 2021 -0400
Implement draft serializer, start on unit tests.
---
.../shibboleth/utilities/java/support/ddf/DDF.java | 131 ++++++++++++++++++++-
.../utilities/java/support/ddf/DDFTest.java | 67 +++++++++++
.../utilities/java/support/ddf/empty-name.ddf | 1 +
.../utilities/java/support/ddf/empty-noname.ddf | 1 +
4 files changed, 197 insertions(+), 3 deletions(-)
diff --git a/src/main/java/net/shibboleth/utilities/java/support/ddf/DDF.java b/src/main/java/net/shibboleth/utilities/java/support/ddf/DDF.java
index 83272ef..25724fc 100644
--- a/src/main/java/net/shibboleth/utilities/java/support/ddf/DDF.java
+++ b/src/main/java/net/shibboleth/utilities/java/support/ddf/DDF.java
@@ -17,7 +17,10 @@
package net.shibboleth.utilities.java.support.ddf;
+import java.io.IOException;
+import java.io.OutputStream;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
@@ -930,6 +933,7 @@ public class DDF implements Iterable<DDF> {
return dump(new StringBuilder(), 0).toString();
}
+// Checkstyle: MethodLength|CyclomaticComplexity OFF
/**
* Helper method to dump to a string for debugging.
*
@@ -938,7 +942,6 @@ public class DDF implements Iterable<DDF> {
*
* @return the first parameter
*/
-// Checkstyle: MethodLength|CyclomaticComplexity OFF
@Nonnull private StringBuilder dump(@Nonnull final StringBuilder builder, final long indent) {
for (long i = 0; i < indent; ++i) {
@@ -972,13 +975,17 @@ public class DDF implements Iterable<DDF> {
break;
case DDF_STRING_UNSAFE:
- builder.append("byte[]");
+ builder.append("char[]");
if (name != null) {
builder.append(' ').append(name);
}
builder.append(" = ");
if (value != null) {
- builder.append(((String) value).getBytes());
+ builder.append('{');
+ for (final char c : ((String) value).toCharArray()) {
+ builder.append(Integer.toHexString(c)).append(", ");
+ }
+ builder.append('}');
} else {
builder.append("null");
}
@@ -1057,6 +1064,124 @@ public class DDF implements Iterable<DDF> {
return builder;
}
+
+ /**
+ * Serialize this object to a provided stream.
+ *
+ * @param os output stream
+ *
+ * @return the output stream
+ *
+ * @throws IOException if an error occurs
+ */
+ @Nonnull public OutputStream serialize(@Nonnull final OutputStream os) throws IOException {
+ if (!isnull()) {
+ if (name != null) {
+ encode(os, name.getBytes("UTF8"));
+ } else {
+ os.write('.');
+ }
+ os.write(' ');
+
+ switch (type) {
+ case DDF_EMPTY:
+ case DDF_POINTER:
+ os.write(Integer.toString(DDFType.DDF_EMPTY.getValue()).getBytes("UTF8"));
+ os.write('\n');
+ break;
+
+ case DDF_STRING:
+ os.write(Integer.toString(type.getValue()).getBytes("UTF8"));
+ if (value != null) {
+ os.write(' ');
+ encode(os, ((String) value).getBytes("UTF-8"));
+ }
+ os.write('\n');
+ break;
+
+ case DDF_STRING_UNSAFE:
+ os.write(Integer.toString(type.getValue()).getBytes("UTF8"));
+ if (value != null) {
+ os.write(' ');
+ encode(os, ((String) value).getBytes("ISO-8859-1"));
+ }
+ os.write('\n');
+ break;
+
+ case DDF_INT:
+ os.write(Integer.toString(type.getValue()).getBytes("UTF8"));
+ os.write(' ');
+ os.write(Integer.toString((Integer) value).getBytes("UTF8"));
+ os.write('\n');
+ break;
+
+ case DDF_FLOAT:
+ os.write(Integer.toString(type.getValue()).getBytes("UTF8"));
+ os.write(' ');
+ os.write(Double.toString((Double) value).getBytes("UTF8"));
+ os.write('\n');
+ break;
+
+ case DDF_STRUCT:
+ @SuppressWarnings("unchecked")
+ final Collection<DDF> members = ((Map<String,DDF>) value).values();
+ os.write(Integer.toString(type.getValue()).getBytes("UTF8"));
+ os.write(' ');
+ os.write(Integer.toString(members.size()).getBytes("UTF8"));
+ os.write('\n');
+ for (final DDF child : members) {
+ child.serialize(os);
+ }
+ break;
+
+ case DDF_LIST:
+ @SuppressWarnings("unchecked")
+ final Collection<DDF> children = (List<DDF>) value;
+ os.write(Integer.toString(type.getValue()).getBytes("UTF8"));
+ os.write(' ');
+ os.write(Integer.toString(children.size()).getBytes("UTF8"));
+ os.write('\n');
+ for (final DDF child : children) {
+ child.serialize(os);
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return os;
+ }
// Checkstyle: MethodLength|CyclomaticComplexity ON
+
+ /**
+ * A simple encoder for non-ASCII characters.
+ *
+ * <p>Made this package-accessible for unit testing.</p>
+ *
+ * @param os output stream
+ * @param bytes bytes to encode
+ *
+ * @throws IOException if an error occurs
+ */
+ static void encode(@Nonnull final OutputStream os, @Nonnull final byte[] bytes) throws IOException {
+ for (final byte b : bytes) {
+ final int i = Byte.toUnsignedInt(b);
+ // 0x25 is the percent char itself.
+ if (i <= 0x20 || i >= 0x7F || i == 0x25) {
+ os.write('%');
+ os.write(hexchar(i >>> 4));
+ os.write(hexchar(i & 0x0F));
+ } else {
+ os.write(b);
+ }
+ }
+ }
+
+ private static int hexchar(final int b) {
+ // 48 is '0' and 65 is 'A'
+ return (b <= 9) ? (48 + b) : (65 + b - 10);
+ }
}
\ No newline at end of file
diff --git a/src/test/java/net/shibboleth/utilities/java/support/ddf/DDFTest.java b/src/test/java/net/shibboleth/utilities/java/support/ddf/DDFTest.java
index 5270b08..f6f7350 100644
--- a/src/test/java/net/shibboleth/utilities/java/support/ddf/DDFTest.java
+++ b/src/test/java/net/shibboleth/utilities/java/support/ddf/DDFTest.java
@@ -19,6 +19,11 @@ package net.shibboleth.utilities.java.support.ddf;
import static org.testng.Assert.*;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import javax.annotation.Nonnull;
+
import org.testng.annotations.Test;
import net.shibboleth.utilities.java.support.collection.Pair;
@@ -90,6 +95,9 @@ public class DDFTest {
assertTrue(obj.isfloat());
assertEquals(obj.integer(), Integer.valueOf(42));
assertEquals(obj.floating(), Double.valueOf(42.42));
+
+ obj.unsafe_string("bar");
+ System.out.print(obj);
}
@Test
@@ -160,4 +168,63 @@ public class DDFTest {
assertTrue(obj.getmember("foo2.foo3").string().equals("bar3"));
}
+ @Test
+ public void testEncoder() throws IOException {
+ try (final ByteArrayOutputStream sink = new ByteArrayOutputStream()) {
+ DDF.encode(sink, "foo".getBytes("UTF8"));
+ assertEquals(sink.toString(), "foo");
+ sink.reset();
+
+ DDF.encode(sink, "foo bar".getBytes("UTF8"));
+ assertEquals(sink.toString(), "foo%20bar");
+ sink.reset();
+
+ DDF.encode(sink, "foo\nbar".getBytes("UTF8"));
+ assertEquals(sink.toString(), "foo%0Abar");
+ sink.reset();
+
+ DDF.encode(sink, "foo☯️bar".getBytes("UTF8"));
+ assertEquals(sink.toString(), "foo%E2%98%AF%EF%B8%8Fbar");
+ sink.reset();
+
+ // -128 corresponds to 128, which is the extended ASCII Euro symbol.
+ // This test demonstrates that round-tripping through such an encoding
+ // will preserve the original 0x80 hex value in that position in the string
+ // rather than converting through the UTF-8 representation.
+ final byte[] unsafe = {102, 111, 111, -128, 98, 97, 114};
+ DDF.encode(sink, new String(unsafe, "ISO-8859-1").getBytes("ISO-8859-1"));
+ assertEquals(sink.toString(), "foo%80bar");
+ sink.reset();
+ }
+ }
+
+ @Test
+ public void testSerialize() throws IOException {
+
+ try (final ByteArrayOutputStream sink = new ByteArrayOutputStream()) {
+ DDF obj = new DDF(null);
+ obj.serialize(sink);
+ assertEquals(sink.toByteArray(), testFile("empty-noname.ddf"));
+ sink.reset();
+
+ obj.name("foo bar");
+ obj.serialize(sink);
+ assertEquals(sink.toByteArray(), testFile("empty-name.ddf"));
+ sink.reset();
+ }
+ }
+
+ /**
+ * Convert test file contents to a byte array.
+ *
+ * @param name file name
+ *
+ * @return byte array
+ *
+ * @throws IOException on error
+ */
+ private byte[] testFile(@Nonnull final String name) throws IOException {
+ return getClass().getResourceAsStream(name).readAllBytes();
+ }
+
}
\ No newline at end of file
diff --git a/src/test/resources/net/shibboleth/utilities/java/support/ddf/empty-name.ddf b/src/test/resources/net/shibboleth/utilities/java/support/ddf/empty-name.ddf
new file mode 100644
index 0000000..b40aabf
--- /dev/null
+++ b/src/test/resources/net/shibboleth/utilities/java/support/ddf/empty-name.ddf
@@ -0,0 +1 @@
+foo%20bar 0
diff --git a/src/test/resources/net/shibboleth/utilities/java/support/ddf/empty-noname.ddf b/src/test/resources/net/shibboleth/utilities/java/support/ddf/empty-noname.ddf
new file mode 100644
index 0000000..fa85f21
--- /dev/null
+++ b/src/test/resources/net/shibboleth/utilities/java/support/ddf/empty-noname.ddf
@@ -0,0 +1 @@
+. 0
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list