From 2b7f72bbea23c1c0c5203d9350742b2c46f38382 Mon Sep 17 00:00:00 2001 From: Bhushan Thombre Date: Sat, 22 Aug 2026 13:30:13 -0600 Subject: [PATCH] Bugfix: Replace removed `sun.misc` Base64 classes with `java.util.Base64` `sun.misc.BASE64Decoder` and `sun.misc.BASE64Encoder` are internal JDK classes that were removed in Java 9. Matlab versions shipping a newer JRE (e.g. R2026a, Java 11) fail to even parse the files that import them: Error: File: import_label.m Line: 27 Column: 8 Unable to find or import 'sun.misc.BASE64Decoder'. This breaks importing any anatomy with FreeSurfer/CAT12 labels, reading and writing GIFTI surfaces, and the snapshots in HTML reports. Added `bst_base64()` in toolbox/misc, which uses `java.util.Base64` (Java >= 8) and falls back to `matlab.net.base64decode/encode` (Matlab >= R2016b) and then to the old `sun.misc` classes, so older installations keep working. Like the previous decoder, it tolerates line breaks and white spaces in the encoded string, and it strips the line breaks when encoding. Updated the callers: - import_label.m, in_tess_gii.m: removed the unused imports - in_gii.m: decoding of Base64Binary / GZipBase64Binary data arrays - out_tess_gii.m: encoding of the vertices and faces - bst_report.m: encoding of the PNG snapshots Tested with Matlab R2026a (Java 11): Base64 round-trip on random bytes, on known test vectors and on line-wrapped input; and read/write round-trip of a GZipBase64Binary .gii surface (7861 vertices, 15359 faces) returning identical vertices and faces. --- toolbox/io/import_label.m | 2 - toolbox/io/in_gii.m | 5 +-- toolbox/io/in_tess_gii.m | 2 - toolbox/io/out_tess_gii.m | 9 +--- toolbox/misc/bst_base64.m | 84 ++++++++++++++++++++++++++++++++++++ toolbox/process/bst_report.m | 6 +-- 6 files changed, 89 insertions(+), 19 deletions(-) create mode 100644 toolbox/misc/bst_base64.m diff --git a/toolbox/io/import_label.m b/toolbox/io/import_label.m index 31c83f0f4c..47ace95698 100644 --- a/toolbox/io/import_label.m +++ b/toolbox/io/import_label.m @@ -24,8 +24,6 @@ % % Authors: Francois Tadel, 2012-2022 -import sun.misc.BASE64Decoder; - %% ===== GET FILES ===== sAllAtlas = repmat(db_template('Atlas'), 0); diff --git a/toolbox/io/in_gii.m b/toolbox/io/in_gii.m index 55a57ecdb7..bc77f35cc9 100644 --- a/toolbox/io/in_gii.m +++ b/toolbox/io/in_gii.m @@ -21,8 +21,6 @@ % % Authors: Francois Tadel, 2017 -import sun.misc.BASE64Decoder; - % Read XML file sXml = in_xml(GiiFile); @@ -56,8 +54,7 @@ case {'Base64Binary', 'GZipBase64Binary'} % Base64 decoding - decoder = BASE64Decoder(); - Values{iArray} = decoder.decodeBuffer(sXml.GIFTI.DataArray(iArray).Data.text); + Values{iArray} = bst_base64('decode', sXml.GIFTI.DataArray(iArray).Data.text); % Unpack gzipped stream if strcmpi(sXml.GIFTI.DataArray(iArray).Encoding, 'GZipBase64Binary') Values{iArray} = dunzip(Values{iArray}); diff --git a/toolbox/io/in_tess_gii.m b/toolbox/io/in_tess_gii.m index 5c990ff196..dd151e9227 100644 --- a/toolbox/io/in_tess_gii.m +++ b/toolbox/io/in_tess_gii.m @@ -30,8 +30,6 @@ % % Authors: Francois Tadel, 2012-2017 -import sun.misc.BASE64Decoder; - % Read GII file [sXml, Values] = in_gii(TessFile); % Initialize matrices diff --git a/toolbox/io/out_tess_gii.m b/toolbox/io/out_tess_gii.m index 13c9c2a6aa..a851e86329 100644 --- a/toolbox/io/out_tess_gii.m +++ b/toolbox/io/out_tess_gii.m @@ -61,19 +61,14 @@ function out_tess_gii( TessMat, OutputFile, isSPM ) end % ===== ENCODE DATA ===== -import sun.misc.BASE64Encoder; -% Initialize Base64 encoder -encoder = BASE64Encoder(); % Encode vertices: millimeters Vertices = single(TessMat.Vertices' .* 1000); Vertices = typecast(Vertices(:), 'uint8'); -Vertices = char(encoder.encodeBuffer(Vertices)); -Vertices((Vertices == 10) | (Vertices == 13)) = []; +Vertices = bst_base64('encode', Vertices); % Encode faces: 0-based array Faces = int32(TessMat.Faces' - 1); Faces = typecast(Faces(:), 'uint8'); -Faces = char(encoder.encodeBuffer(Faces)); -Faces((Faces == 10) | (Faces == 13)) = []; +Faces = bst_base64('encode', Faces); % ===== CREATE XML STRING ===== % Create XML file from a template diff --git a/toolbox/misc/bst_base64.m b/toolbox/misc/bst_base64.m new file mode 100644 index 0000000000..7d9eec96d4 --- /dev/null +++ b/toolbox/misc/bst_base64.m @@ -0,0 +1,84 @@ +function y = bst_base64(action, x) +% BST_BASE64: Encode or decode Base64 data, independently from the Java version. +% +% USAGE: bytes = bst_base64('decode', str) % str : char array (line breaks allowed) +% % bytes: int8 vector +% str = bst_base64('encode', bytes) % bytes: int8/uint8 vector (or Java byte[]) +% % str : char array (no line breaks) +% +% DESCRIPTION: +% The classes sun.misc.BASE64Decoder and sun.misc.BASE64Encoder used previously were +% internal JDK classes, removed in Java 9. Recent versions of Matlab ship with a newer +% JDK, in which they are not available anymore ("Unable to find or import +% 'sun.misc.BASE64Decoder'"). This function uses java.util.Base64 instead (Java >= 8), +% with fallbacks for older environments. + +% @============================================================================= +% This function is part of the Brainstorm software: +% https://neuroimage.usc.edu/brainstorm +% +% Copyright (c) University of Southern California & McGill University +% This software is distributed under the terms of the GNU General Public License +% as published by the Free Software Foundation. Further details on the GPLv3 +% license can be found at http://www.gnu.org/copyleft/gpl.html. +% +% FOR RESEARCH PURPOSES ONLY. THE SOFTWARE IS PROVIDED "AS IS," AND THE +% UNIVERSITY OF SOUTHERN CALIFORNIA AND ITS COLLABORATORS DO NOT MAKE ANY +% WARRANTY, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO WARRANTIES OF +% MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, NOR DO THEY ASSUME ANY +% LIABILITY OR RESPONSIBILITY FOR THE USE OF THIS SOFTWARE. +% +% For more information type "brainstorm license" at command prompt. +% =============================================================================@ +% +% Authors: Bhushan Thombre, 2026 + +switch lower(action) + % ===== DECODE ===== + case 'decode' + % Convert to a simple char row, remove all the white spaces and line breaks + x = char(x); + x = x(:)'; + x(x <= 32) = []; + if isempty(x) + y = zeros(0, 1, 'int8'); + return; + end + try + % Java >= 8 + y = java.util.Base64.getMimeDecoder().decode(java.lang.String(x)); + catch + try + % Matlab >= R2016b, no Java needed + y = typecast(matlab.net.base64decode(x), 'int8'); + catch + % Java <= 8 + decoder = sun.misc.BASE64Decoder(); + y = decoder.decodeBuffer(x); + end + end + y = y(:); + + % ===== ENCODE ===== + case 'encode' + x = typecast(x(:), 'int8'); + try + % Java >= 8 + y = char(java.util.Base64.getEncoder().encodeToString(x)); + catch + try + % Matlab >= R2016b, no Java needed + y = char(matlab.net.base64encode(typecast(x, 'uint8'))); + catch + % Java <= 8 + encoder = sun.misc.BASE64Encoder(); + y = char(encoder.encode(x)); + end + end + % Remove the line breaks possibly added by the encoder + y = y(:)'; + y((y == 10) | (y == 13)) = []; + + otherwise + error(['Unsupported action: ' action]); +end diff --git a/toolbox/process/bst_report.m b/toolbox/process/bst_report.m index 65663c65e3..d158cd0c76 100644 --- a/toolbox/process/bst_report.m +++ b/toolbox/process/bst_report.m @@ -1040,8 +1040,6 @@ function Info(sProcess, sInputs, strMsg) % ===== IMAGES ===== if ~isempty(iImages) html = [html '

Snapshots

' 10]; - % Create Base64 encoder - encoder = sun.misc.BASE64Encoder(); % Loop on all the images for i = 1:length(iImages) iMsg = iImages(i); @@ -1063,7 +1061,7 @@ function Info(sProcess, sInputs, strMsg) jByteStream = java.io.ByteArrayOutputStream(); javax.imageio.ImageIO.write(jImage, 'png', jByteStream); % Encode PNG image in Base64 - jStringImage = encoder.encode(jByteStream.toByteArray()); + strImage = bst_base64('encode', jByteStream.toByteArray()); % Display image in HTML html = [html, Comment]; if ~isempty(FileNames) @@ -1071,7 +1069,7 @@ function Info(sProcess, sInputs, strMsg) else html = [html, '
']; end - html = [html, '

']; + html = [html, '

']; end end