Skip to content

Commit 935d762

Browse files
committed
JAVA-2507: Replace use of javax.xml.bind.DatatypeConverter for hex-decoding
1 parent 96440b5 commit 935d762

File tree

6 files changed

+92
-20
lines changed

6 files changed

+92
-20
lines changed

bson/src/main/org/bson/json/JsonReader.java

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,12 @@
2424
import org.bson.BsonContextType;
2525
import org.bson.BsonDbPointer;
2626
import org.bson.BsonInvalidOperationException;
27+
import org.bson.BsonReaderMark;
2728
import org.bson.BsonRegularExpression;
2829
import org.bson.BsonTimestamp;
2930
import org.bson.BsonType;
3031
import org.bson.BsonUndefined;
3132
import org.bson.internal.Base64;
32-
import org.bson.BsonReaderMark;
3333
import org.bson.internal.UnsignedLongs;
3434
import org.bson.types.Decimal128;
3535
import org.bson.types.MaxKey;
@@ -680,7 +680,7 @@ private BsonBinary visitUUIDConstructor(final String uuidConstructorName) {
680680
}
681681
verifyToken(JsonTokenType.RIGHT_PAREN);
682682
String hexString = bytesToken.getValue(String.class).replaceAll("\\{", "").replaceAll("\\}", "").replaceAll("-", "");
683-
byte[] bytes = DatatypeConverter.parseHexBinary(hexString);
683+
byte[] bytes = decodeHex(hexString);
684684
BsonBinarySubType subType = BsonBinarySubType.UUID_STANDARD;
685685
if (!"UUID".equals(uuidConstructorName) || !"GUID".equals(uuidConstructorName)) {
686686
subType = BsonBinarySubType.UUID_LEGACY;
@@ -857,10 +857,10 @@ private BsonBinary visitHexDataConstructor() {
857857

858858
for (final BsonBinarySubType subType : BsonBinarySubType.values()) {
859859
if (subType.getValue() == subTypeToken.getValue(Integer.class)) {
860-
return new BsonBinary(subType, DatatypeConverter.parseHexBinary(hex));
860+
return new BsonBinary(subType, decodeHex(hex));
861861
}
862862
}
863-
return new BsonBinary(DatatypeConverter.parseHexBinary(hex));
863+
return new BsonBinary(decodeHex(hex));
864864
}
865865

866866
private long visitDateTimeConstructor() {
@@ -1250,5 +1250,25 @@ protected BsonContextType getContextType() {
12501250
return super.getContextType();
12511251
}
12521252
}
1253+
1254+
private static byte[] decodeHex(final String hex) {
1255+
if (hex.length() % 2 != 0) {
1256+
throw new IllegalArgumentException("A hex string must contain an even number of characters: " + hex);
1257+
}
1258+
1259+
byte[] out = new byte[hex.length() / 2];
1260+
1261+
for (int i = 0; i < hex.length(); i += 2) {
1262+
int high = Character.digit(hex.charAt(i), 16);
1263+
int low = Character.digit(hex.charAt(i + 1), 16);
1264+
if (high == -1 || low == -1) {
1265+
throw new IllegalArgumentException("A hex string can only contain the characters 0-9, A-F, a-f: " + hex);
1266+
}
1267+
1268+
out[i / 2] = (byte) (high * 16 + low);
1269+
}
1270+
1271+
return out;
1272+
}
12531273
}
12541274

bson/src/test/unit/org/bson/GenericBsonDecimal128Test.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@
2525
import org.junit.Test;
2626
import org.junit.runner.RunWith;
2727
import org.junit.runners.Parameterized;
28+
import util.Hex;
2829
import util.JsonPoweredTestHelper;
2930

30-
import javax.xml.bind.DatatypeConverter;
3131
import java.io.File;
3232
import java.io.IOException;
3333
import java.net.URISyntaxException;
@@ -121,7 +121,7 @@ private void runValid() {
121121
}
122122

123123
private BsonDocument decodeToDocument(final String subjectHex, final String description) {
124-
ByteBuffer byteBuffer = ByteBuffer.wrap(DatatypeConverter.parseHexBinary(subjectHex));
124+
ByteBuffer byteBuffer = ByteBuffer.wrap(Hex.decode(subjectHex));
125125
BsonDocument actualDecodedDocument = new BsonDocumentCodec().decode(new BsonBinaryReader(byteBuffer),
126126
DecoderContext.builder().build());
127127

@@ -135,7 +135,7 @@ private BsonDocument decodeToDocument(final String subjectHex, final String desc
135135
private String encodeToHex(final BsonDocument decodedDocument) {
136136
BasicOutputBuffer outputBuffer = new BasicOutputBuffer();
137137
new BsonDocumentCodec().encode(new BsonBinaryWriter(outputBuffer), decodedDocument, EncoderContext.builder().build());
138-
return DatatypeConverter.printHexBinary(outputBuffer.toByteArray());
138+
return Hex.encode(outputBuffer.toByteArray());
139139
}
140140

141141
private void runParseError() {

bson/src/test/unit/org/bson/GenericBsonTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@
2727
import org.junit.Test;
2828
import org.junit.runner.RunWith;
2929
import org.junit.runners.Parameterized;
30+
import util.Hex;
3031
import util.JsonPoweredTestHelper;
3132

32-
import javax.xml.bind.DatatypeConverter;
3333
import java.io.File;
3434
import java.io.IOException;
3535
import java.io.StringReader;
@@ -216,7 +216,7 @@ private boolean shouldEscapeCharacter(final char escapedChar) {
216216
}
217217

218218
private BsonDocument decodeToDocument(final String subjectHex, final String description) {
219-
ByteBuffer byteBuffer = ByteBuffer.wrap(DatatypeConverter.parseHexBinary(subjectHex));
219+
ByteBuffer byteBuffer = ByteBuffer.wrap(Hex.decode(subjectHex));
220220
BsonDocument actualDecodedDocument = new BsonDocumentCodec().decode(new BsonBinaryReader(byteBuffer),
221221
DecoderContext.builder().build());
222222

@@ -231,7 +231,7 @@ private BsonDocument decodeToDocument(final String subjectHex, final String desc
231231
private String encodeToHex(final BsonDocument decodedDocument) {
232232
BasicOutputBuffer outputBuffer = new BasicOutputBuffer();
233233
new BsonDocumentCodec().encode(new BsonBinaryWriter(outputBuffer), decodedDocument, EncoderContext.builder().build());
234-
return DatatypeConverter.printHexBinary(outputBuffer.toByteArray());
234+
return Hex.encode(outputBuffer.toByteArray());
235235
}
236236

237237
private void runDecodeError() {

bson/src/test/unit/util/Hex.java

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
* Copyright 2017 MongoDB, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
*/
17+
18+
package util;
19+
20+
public final class Hex {
21+
public static byte[] decode(final String hex) {
22+
if (hex.length() % 2 != 0) {
23+
throw new IllegalArgumentException("A hex string must contain an even number of characters: " + hex);
24+
}
25+
26+
byte[] out = new byte[hex.length() / 2];
27+
28+
for (int i = 0; i < hex.length(); i += 2) {
29+
int high = Character.digit(hex.charAt(i), 16);
30+
int low = Character.digit(hex.charAt(i + 1), 16);
31+
if (high == -1 || low == -1) {
32+
throw new IllegalArgumentException("A hex string can only contain the characters 0-9, A-F, a-f: " + hex);
33+
}
34+
35+
out[i / 2] = (byte) (high * 16 + low);
36+
}
37+
38+
return out;
39+
}
40+
41+
private static final char[] UPPER_HEX_DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', };
42+
43+
public static String encode(final byte[] bytes) {
44+
StringBuilder stringBuilder = new StringBuilder(bytes.length * 2);
45+
for (byte cur : bytes) {
46+
stringBuilder.append(UPPER_HEX_DIGITS[(cur >> 4) & 0xF]);
47+
stringBuilder.append(UPPER_HEX_DIGITS[(cur & 0xF)]);
48+
}
49+
return stringBuilder.toString();
50+
}
51+
52+
private Hex() {
53+
}
54+
}

driver-async/src/test/functional/com/mongodb/async/client/gridfs/GridFSTest.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,9 @@
3838
import org.junit.Test;
3939
import org.junit.runner.RunWith;
4040
import org.junit.runners.Parameterized;
41+
import util.Hex;
4142
import util.JsonPoweredTestHelper;
4243

43-
import javax.xml.bind.DatatypeConverter;
4444
import java.io.ByteArrayInputStream;
4545
import java.io.ByteArrayOutputStream;
4646
import java.io.File;
@@ -54,7 +54,6 @@
5454
import static com.mongodb.ClusterFixture.getDefaultDatabaseName;
5555
import static com.mongodb.async.client.gridfs.helpers.AsyncStreamHelper.toAsyncInputStream;
5656
import static com.mongodb.async.client.gridfs.helpers.AsyncStreamHelper.toAsyncOutputStream;
57-
import static javax.xml.bind.DatatypeConverter.printHexBinary;
5857
import static org.junit.Assert.assertEquals;
5958
import static org.junit.Assert.assertNotNull;
6059
import static org.junit.Assert.assertNull;
@@ -279,7 +278,7 @@ public void execute() {
279278

280279
if (assertion.containsKey("result")) {
281280
assertNull("Should not have thrown an exception", error);
282-
assertEquals(printHexBinary(outputStream.toByteArray()).toLowerCase(),
281+
assertEquals(Hex.encode(outputStream.toByteArray()).toLowerCase(),
283282
assertion.getDocument("result").getString("$hex").getValue());
284283
} else if (assertion.containsKey("error")) {
285284
assertNotNull("Should have thrown an exception", error);
@@ -311,7 +310,7 @@ public void execute() {
311310
}
312311
if (assertion.containsKey("result")) {
313312
assertNull("Should not have thrown an exception", error);
314-
assertEquals(printHexBinary(outputStream.toByteArray()).toLowerCase(),
313+
assertEquals(Hex.encode(outputStream.toByteArray()).toLowerCase(),
315314
assertion.getDocument("result").getString("$hex").getValue());
316315
} else if (assertion.containsKey("error")) {
317316
assertNotNull("Should have thrown an exception", error);
@@ -455,7 +454,7 @@ private BsonDocument parseHexDocument(final BsonDocument document) {
455454

456455
private BsonDocument parseHexDocument(final BsonDocument document, final String hexDocument) {
457456
if (document.containsKey(hexDocument) && document.get(hexDocument).isDocument()) {
458-
byte[] bytes = DatatypeConverter.parseHexBinary(document.getDocument(hexDocument).getString("$hex").getValue());
457+
byte[] bytes = Hex.decode(document.getDocument(hexDocument).getString("$hex").getValue());
459458
document.put(hexDocument, new BsonBinary(bytes));
460459
}
461460
return document;

driver/src/test/functional/com/mongodb/client/gridfs/GridFSTest.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,9 @@
3434
import org.junit.Test;
3535
import org.junit.runner.RunWith;
3636
import org.junit.runners.Parameterized;
37+
import util.Hex;
3738
import util.JsonPoweredTestHelper;
3839

39-
import javax.xml.bind.DatatypeConverter;
4040
import java.io.ByteArrayInputStream;
4141
import java.io.ByteArrayOutputStream;
4242
import java.io.File;
@@ -47,7 +47,6 @@
4747
import java.util.Collection;
4848
import java.util.List;
4949

50-
import static javax.xml.bind.DatatypeConverter.printHexBinary;
5150
import static org.junit.Assert.assertEquals;
5251
import static org.junit.Assert.assertNotNull;
5352
import static org.junit.Assert.assertNull;
@@ -225,7 +224,7 @@ private void doDownload(final BsonDocument arguments, final BsonDocument asserti
225224

226225
if (assertion.containsKey("result")) {
227226
assertNull("Should not have thrown an exception", error);
228-
assertEquals(printHexBinary(outputStream.toByteArray()).toLowerCase(),
227+
assertEquals(Hex.encode(outputStream.toByteArray()).toLowerCase(),
229228
assertion.getDocument("result").getString("$hex").getValue());
230229
} else if (assertion.containsKey("error")) {
231230
assertNotNull("Should have thrown an exception", error);
@@ -250,7 +249,7 @@ private void doDownloadByName(final BsonDocument arguments, final BsonDocument a
250249
}
251250
if (assertion.containsKey("result")) {
252251
assertNull("Should not have thrown an exception", error);
253-
assertEquals(printHexBinary(outputStream.toByteArray()).toLowerCase(),
252+
assertEquals(Hex.encode(outputStream.toByteArray()).toLowerCase(),
254253
assertion.getDocument("result").getString("$hex").getValue());
255254
} else if (assertion.containsKey("error")) {
256255
assertNotNull("Should have thrown an exception", error);
@@ -360,7 +359,7 @@ private BsonDocument parseHexDocument(final BsonDocument document) {
360359

361360
private BsonDocument parseHexDocument(final BsonDocument document, final String hexDocument) {
362361
if (document.containsKey(hexDocument) && document.get(hexDocument).isDocument()) {
363-
byte[] bytes = DatatypeConverter.parseHexBinary(document.getDocument(hexDocument).getString("$hex").getValue());
362+
byte[] bytes = Hex.decode(document.getDocument(hexDocument).getString("$hex").getValue());
364363
document.put(hexDocument, new BsonBinary(bytes));
365364
}
366365
return document;

0 commit comments

Comments
 (0)