From a359464fcf59af8abc6f69ae0e88e42adc6018df Mon Sep 17 00:00:00 2001 From: Niki Roo Date: Sun, 3 Mar 2019 16:51:34 +0100 Subject: [PATCH] Version 4.5.0 --- VERSION | 2 +- changelog.md | 6 + src/be/nikiroo/utils/StringUtils.java | 141 +++++++++++++++++- src/be/nikiroo/utils/serial/Exporter.java | 74 +++++++-- src/be/nikiroo/utils/serial/Importer.java | 13 +- src/be/nikiroo/utils/serial/SerialUtils.java | 4 +- .../utils/serial/server/ConnectAction.java | 3 +- .../utils/serial/server/ServerBridge.java | 8 +- .../nikiroo/utils/test/StringUtilsTest.java | 4 +- 9 files changed, 229 insertions(+), 26 deletions(-) diff --git a/VERSION b/VERSION index fa1ba04..a84947d 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -4.4.5 +4.5.0 diff --git a/changelog.md b/changelog.md index 085898e..643d0b1 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,11 @@ # nikiroo-utils +## Version 4.5.0 + +- Base64: allow access to streams +- Deprecated: do not use our on deprecated functions +- Serial: fix ZIP/noZIP error + ## Version 4.4.5 - Base64: allow access to not-zipped Base64 utilities diff --git a/src/be/nikiroo/utils/StringUtils.java b/src/be/nikiroo/utils/StringUtils.java index 79bccec..1884c21 100644 --- a/src/be/nikiroo/utils/StringUtils.java +++ b/src/be/nikiroo/utils/StringUtils.java @@ -2,6 +2,8 @@ package be.nikiroo.utils; import java.io.ByteArrayInputStream; import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; @@ -523,7 +525,7 @@ public class StringUtils { @Deprecated public static String zip64(String data) { try { - return Base64.encodeBytes(data.getBytes(), Base64.GZIP); + return Base64.encodeBytes(data.getBytes("UTF-8"), Base64.GZIP); } catch (IOException e) { e.printStackTrace(); return null; @@ -558,6 +560,25 @@ public class StringUtils { } } + /** + * Convert the given data to Base64 format. + * + * @param data + * the data to convert + * @param zip + * TRUE to also compress the data in GZIP format; remember that + * compressed and not-compressed content are different; you need + * to know which is which when decoding + * + * @return the Base64 {@link String} representation of the data + * + * @throws IOException + * in case of I/O errors + */ + public static String base64(String data, boolean zip) throws IOException { + return base64(data.getBytes("UTF-8"), zip); + } + /** * Convert the given data to Base64 format. * @@ -578,7 +599,64 @@ public class StringUtils { } /** - * Unonvert the given data from Base64 format back to a raw array of bytes. + * Convert the given data to Base64 format. + * + * @param data + * the data to convert + * @param zip + * TRUE to also uncompress the data from a GZIP format; take care + * about this flag, as it could easily cause errors in the + * returned content or an {@link IOException} + * @param breakLines + * TRUE to break lines on every 76th character + * + * @return the Base64 {@link String} representation of the data + * + * @throws IOException + * in case of I/O errors + */ + public static OutputStream base64(OutputStream data, boolean zip, + boolean breakLines) throws IOException { + OutputStream out = new Base64.OutputStream(data, + breakLines ? Base64.DO_BREAK_LINES & Base64.ENCODE + : Base64.ENCODE); + + if (zip) { + out = new java.util.zip.GZIPOutputStream(out); + } + + return out; + } + + /** + * Convert the given data to Base64 format. + * + * @param data + * the data to convert + * @param zip + * TRUE to also uncompress the data from a GZIP format; take care + * about this flag, as it could easily cause errors in the + * returned content or an {@link IOException} + * @param breakLines + * TRUE to break lines on every 76th character + * + * @return the Base64 {@link String} representation of the data + * + * @throws IOException + * in case of I/O errors + */ + public static InputStream base64(InputStream data, boolean zip, + boolean breakLines) throws IOException { + if (zip) { + data = new java.util.zip.GZIPInputStream(data); + } + + return new Base64.InputStream(data, breakLines ? Base64.DO_BREAK_LINES + & Base64.ENCODE : Base64.ENCODE); + } + + /** + * Unconvert the given data from Base64 format back to a raw array of bytes. * * @param data * the data to unconvert @@ -597,6 +675,63 @@ public class StringUtils { return Base64.decode(data, zip ? Base64.GZIP : Base64.NO_OPTIONS); } + /** + * Unconvert the given data from Base64 format back to a raw array of bytes. + * + * @param data + * the data to unconvert + * @param zip + * TRUE to also uncompress the data from a GZIP format; take care + * about this flag, as it could easily cause errors in the + * returned content or an {@link IOException} + * @param breakLines + * TRUE to break lines on every 76th character + * + * @return the raw data represented by the given Base64 {@link String} + * + * @throws IOException + * in case of I/O errors + */ + public static OutputStream unbase64(OutputStream data, boolean zip, + boolean breakLines) throws IOException { + OutputStream out = new Base64.OutputStream(data, + breakLines ? Base64.DO_BREAK_LINES & Base64.ENCODE + : Base64.ENCODE); + + if (zip) { + out = new java.util.zip.GZIPOutputStream(out); + } + + return out; + } + + /** + * Unconvert the given data from Base64 format back to a raw array of bytes. + * + * @param data + * the data to unconvert + * @param zip + * TRUE to also uncompress the data from a GZIP format; take care + * about this flag, as it could easily cause errors in the + * returned content or an {@link IOException} + * @param breakLines + * TRUE to break lines on every 76th character + * + * @return the raw data represented by the given Base64 {@link String} + * + * @throws IOException + * in case of I/O errors + */ + public static InputStream unbase64(InputStream data, boolean zip, + boolean breakLines) throws IOException { + if (zip) { + data = new java.util.zip.GZIPInputStream(data); + } + + return new Base64.InputStream(data, breakLines ? Base64.DO_BREAK_LINES + & Base64.ENCODE : Base64.ENCODE); + } + /** * Unonvert the given data from Base64 format back to a {@link String}. * @@ -616,7 +751,7 @@ public class StringUtils { public static String unbase64s(String data, boolean zip) throws IOException { ByteArrayInputStream in = new ByteArrayInputStream(unbase64(data, zip)); - Scanner scan = new Scanner(in); + Scanner scan = new Scanner(in, "UTF-8"); scan.useDelimiter("\\A"); try { return scan.next(); diff --git a/src/be/nikiroo/utils/serial/Exporter.java b/src/be/nikiroo/utils/serial/Exporter.java index 3a6e025..d5f3a5f 100644 --- a/src/be/nikiroo/utils/serial/Exporter.java +++ b/src/be/nikiroo/utils/serial/Exporter.java @@ -1,5 +1,6 @@ package be.nikiroo.utils.serial; +import java.io.IOException; import java.io.NotSerializableException; import java.util.HashMap; import java.util.Map; @@ -55,37 +56,84 @@ public class Exporter { map.clear(); } + /** + * Append the exported items in a serialised form into the given + * {@link StringBuilder}. + * + * @param toBuilder + * the {@link StringBuilder} + * @param b64 + * TRUE to have BASE64-coded content, FALSE to have raw content, + * NULL to let the system decide + * @param zip + * TRUE to zip the BASE64 output if the output is indeed in + * BASE64 format, FALSE not to + */ + public void appendTo(StringBuilder toBuilder, Boolean b64, boolean zip) { + if (b64 == null && builder.length() < 128) { + b64 = false; + } + + if (b64 != null || b64) { + try { + String zipped = StringUtils.base64(builder.toString(), zip); + if (b64 != null || zipped.length() < builder.length() - 4) { + toBuilder.append(zip ? "ZIP:" : "B64:"); + toBuilder.append(zipped); + return; + } + } catch (IOException e) { + throw new RuntimeException( + "Base64 conversion of data failed, maybe not enough memory?", + e); + } + } + + toBuilder.append(builder); + } + /** * The exported items in a serialised form. * + * @deprecated use {@link Exporter#toString(Boolean, boolean)} instead + * * @param zip * TRUE to have zipped (and BASE64-coded) content, FALSE to have * raw content, NULL to let the system decide * * @return the items currently in this {@link Exporter} */ + @Deprecated public String toString(Boolean zip) { - if (zip == null && builder.length() > 128) { - zip = false; - } - - if (zip == null || zip) { - String zipped = "ZIP:" + StringUtils.zip64(builder.toString()); - - if (zip != null || builder.length() < zipped.length()) - return zipped; - } + return toString(zip, zip == null || zip); + } - return builder.toString(); + /** + * The exported items in a serialised form. + * + * @param b64 + * TRUE to have BASE64-coded content, FALSE to have raw content, + * NULL to let the system decide + * @param zip + * TRUE to zip the BASE64 output if the output is indeed in + * BASE64 format, FALSE not to + * + * @return the items currently in this {@link Exporter} + */ + public String toString(Boolean b64, boolean zip) { + StringBuilder toBuilder = new StringBuilder(); + appendTo(toBuilder, b64, zip); + return toBuilder.toString(); } /** - * The exported items in a serialised form (possibly zipped). + * The exported items in a serialised form (possibly BASE64-coded, possibly + * zipped). * * @return the items currently in this {@link Exporter} */ @Override public String toString() { - return toString(null); + return toString(null, true); } } \ No newline at end of file diff --git a/src/be/nikiroo/utils/serial/Importer.java b/src/be/nikiroo/utils/serial/Importer.java index e7285c4..84fb5aa 100644 --- a/src/be/nikiroo/utils/serial/Importer.java +++ b/src/be/nikiroo/utils/serial/Importer.java @@ -70,13 +70,22 @@ public class Importer { if (line.startsWith("ZIP:")) { try { - line = StringUtils.unzip64(line.substring("ZIP:" - .length())); + line = StringUtils.unbase64s( + line.substring("ZIP:".length()), true); } catch (IOException e) { throw new IOException( "Internal error when decoding ZIP content: input may be corrupt"); } read(line); + } else if (line.startsWith("B64:")) { + try { + line = StringUtils.unbase64s( + line.substring("B64:".length()), false); + } catch (IOException e) { + throw new IOException( + "Internal error when decoding B64 content: input may be corrupt"); + } + read(line); } else { processLine(line); } diff --git a/src/be/nikiroo/utils/serial/SerialUtils.java b/src/be/nikiroo/utils/serial/SerialUtils.java index 502d0ba..706d579 100644 --- a/src/be/nikiroo/utils/serial/SerialUtils.java +++ b/src/be/nikiroo/utils/serial/SerialUtils.java @@ -66,8 +66,8 @@ public class SerialUtils { if (!SerialUtils.encode(builder, item)) { try { // use ZIP: if not - builder.append(new Exporter().append(item) - .toString(true)); + new Exporter().append(item).appendTo(builder, + true, false); } catch (NotSerializableException e) { throw new UnknownFormatConversionException(e .getMessage()); diff --git a/src/be/nikiroo/utils/serial/server/ConnectAction.java b/src/be/nikiroo/utils/serial/server/ConnectAction.java index 97243d5..10435e2 100644 --- a/src/be/nikiroo/utils/serial/server/ConnectAction.java +++ b/src/be/nikiroo/utils/serial/server/ConnectAction.java @@ -175,7 +175,8 @@ abstract class ConnectAction { protected Object sendObject(Object data) throws IOException, NoSuchFieldException, NoSuchMethodException, ClassNotFoundException { synchronized (lock) { - String rep = sendString(new Exporter().append(data).toString(true)); + String rep = sendString(new Exporter().append(data).toString(true, + true)); if (rep != null) { return new Importer().read(rep).getValue(); } diff --git a/src/be/nikiroo/utils/serial/server/ServerBridge.java b/src/be/nikiroo/utils/serial/server/ServerBridge.java index ea9fd1b..db50876 100644 --- a/src/be/nikiroo/utils/serial/server/ServerBridge.java +++ b/src/be/nikiroo/utils/serial/server/ServerBridge.java @@ -238,8 +238,12 @@ public class ServerBridge extends Server { if (getTraceHandler().getTraceLevel() >= 2) { try { - if (data.startsWith("ZIP:")) { - data = StringUtils.unzip64(data.substring(4)); + while (data.startsWith("ZIP:") || data.startsWith("B64:")) { + if (data.startsWith("ZIP:")) { + data = StringUtils.unbase64s(data.substring(4), true); + } else if (data.startsWith("B64:")) { + data = StringUtils.unbase64s(data.substring(4), false); + } } Object obj = new Importer().read(data).getValue(); diff --git a/src/be/nikiroo/utils/test/StringUtilsTest.java b/src/be/nikiroo/utils/test/StringUtilsTest.java index 29f5bf3..e35be4f 100644 --- a/src/be/nikiroo/utils/test/StringUtilsTest.java +++ b/src/be/nikiroo/utils/test/StringUtilsTest.java @@ -223,8 +223,8 @@ class StringUtilsTest extends TestLauncher { @Override public void test() throws Exception { String orig = "test"; - String zipped = StringUtils.zip64(orig); - String unzipped = StringUtils.unzip64(zipped); + String zipped = StringUtils.base64(orig, true); + String unzipped = StringUtils.unbase64s(zipped, true); assertEquals(orig, unzipped); } }); -- 2.27.0