Skip to content

Commit 18bd3d0

Browse files
committed
Add builder to Base16 streams and deprecate some old constructors
1 parent e0ddfc1 commit 18bd3d0

File tree

5 files changed

+82
-23
lines changed

5 files changed

+82
-23
lines changed

src/changes/changes.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ The <action> type attribute can be add,update,fix,remove.
5252
<!-- ADD -->
5353
<action type="add" dev="ggregory" due-to="Fredrik Kjellberg, Gary Gregory">Add org.apache.commons.codec.digest.CRC16.</action>
5454
<action type="add" dev="ggregory" due-to="Gary Gregory">Add builders to org.apache.commons.codec.digest streams and deprecate some old constructors.</action>
55+
<action type="add" dev="ggregory" due-to="Gary Gregory">Add builder to Base16 streams and deprecate some old constructors.</action>
5556
<action type="add" dev="ggregory" due-to="Gary Gregory">Add support for SHAKE128-256 and SHAKE256-512 to `DigestUtils` and `MessageDigestAlgorithms` on Java 25 and up.</action>
5657
<!-- UPDATE -->
5758
<action type="update" dev="ggregory" due-to="Gary Gregory, Dependabot">Bump org.apache.commons:commons-parent from 85 to 89.</action>

src/main/java/org/apache/commons/codec/binary/Base16.java

Lines changed: 74 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
package org.apache.commons.codec.binary;
1919

20+
import java.util.Arrays;
2021
import java.util.Objects;
2122

2223
import org.apache.commons.codec.CodecPolicy;
@@ -43,11 +44,59 @@
4344
*/
4445
public class Base16 extends BaseNCodec {
4546

47+
/**
48+
* Builds {@link Base16} instances.
49+
*
50+
* <p>
51+
* To configure a new instance, use a {@link Builder}. For example:
52+
* </p>
53+
*
54+
* <pre>
55+
* Base16 Base16 = Base16.builder()
56+
* .setDecodingPolicy(DecodingPolicy.LENIENT) // default is lenient
57+
* .get()
58+
* </pre>
59+
*
60+
* @since 1.20.0
61+
*/
62+
public static class Builder extends AbstractBuilder<Base16, Builder> {
63+
64+
/**
65+
* Constructs a new instance.
66+
*/
67+
public Builder() {
68+
super(null);
69+
setEncodedBlockSize(BYTES_PER_ENCODED_BLOCK);
70+
setUnencodedBlockSize(BYTES_PER_UNENCODED_BLOCK);
71+
setLineLength(0);
72+
setLineSeparator(EMPTY_BYTE_ARRAY);
73+
}
74+
75+
@Override
76+
public Base16 get() {
77+
return new Base16(this);
78+
}
79+
80+
/**
81+
* Sets whether to use the the lower-case Base16 alphabet.
82+
*
83+
* @param lowerCase {@code true} to use the lower-case Base16 alphabet.
84+
* @return {@code this} instance.
85+
*/
86+
public Builder setLowerCase(final boolean lowerCase) {
87+
setEncodeTableRaw(lowerCase ? LOWER_CASE_ENCODE_TABLE : UPPER_CASE_ENCODE_TABLE);
88+
return asThis();
89+
}
90+
91+
}
92+
4693
/**
4794
* BASE16 characters are 4 bits in length. They are formed by taking an 8-bit group, which is converted into two BASE16 characters.
4895
*/
4996
private static final int BITS_PER_ENCODED_BYTE = 4;
97+
5098
private static final int BYTES_PER_ENCODED_BLOCK = 2;
99+
51100
private static final int BYTES_PER_UNENCODED_BLOCK = 1;
52101
/**
53102
* This array is a lookup table that translates Unicode characters drawn from the "Base16 Alphabet" (as specified in Table 5 of RFC 4648) into their 4-bit
@@ -68,6 +117,7 @@ public class Base16 extends BaseNCodec {
68117
* 4648.
69118
*/
70119
private static final byte[] UPPER_CASE_ENCODE_TABLE = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
120+
71121
/**
72122
* This array is a lookup table that translates Unicode characters drawn from the a lower-case "Base16 Alphabet" into their 4-bit positive integer
73123
* equivalents. Characters that are not in the Base16 alphabet but fall within the bounds of the array are translated to -1.
@@ -88,12 +138,25 @@ public class Base16 extends BaseNCodec {
88138
* This array is a lookup table that translates 4-bit positive integer index values into their "Base16 Alphabet" lower-case equivalents.
89139
*/
90140
private static final byte[] LOWER_CASE_ENCODE_TABLE = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
141+
91142
/** Mask used to extract 4 bits, used when decoding character. */
92143
private static final int MASK_4_BITS = 0x0f;
144+
145+
/**
146+
* Constructs a new builder.
147+
*
148+
* @return a new builder.
149+
* @since 1.20.0
150+
*/
151+
public static Builder builder() {
152+
return new Builder();
153+
}
154+
93155
/**
94156
* Decode table to use.
95157
*/
96158
private final byte[] decodeTable;
159+
97160
/**
98161
* Encode table to use.
99162
*/
@@ -109,33 +172,30 @@ public Base16() {
109172
/**
110173
* Constructs a Base16 codec used for decoding and encoding.
111174
*
112-
* @param lowerCase if {@code true} then use a lower-case Base16 alphabet.
175+
* @param lowerCase {@code true} to use the lower-case Base16 alphabet.
176+
* @deprecated Use {@link #builder()} and {@link Builder}.
113177
*/
178+
@Deprecated
114179
public Base16(final boolean lowerCase) {
115180
this(lowerCase, DECODING_POLICY_DEFAULT);
116181
}
117182

118183
/**
119184
* Constructs a Base16 codec used for decoding and encoding.
120185
*
121-
* @param lowerCase if {@code true} then use a lower-case Base16 alphabet.
186+
* @param lowerCase {@code true} to use the lower-case Base16 alphabet.
122187
* @param decodingPolicy Decoding policy.
188+
* @deprecated Use {@link #builder()} and {@link Builder}.
123189
*/
190+
@Deprecated
124191
public Base16(final boolean lowerCase, final CodecPolicy decodingPolicy) {
125-
this(lowerCase ? LOWER_CASE_ENCODE_TABLE : UPPER_CASE_ENCODE_TABLE, decodingPolicy);
192+
this(builder().setEncodeTable(lowerCase ? LOWER_CASE_ENCODE_TABLE : UPPER_CASE_ENCODE_TABLE).setDecodingPolicy(decodingPolicy));
126193
}
127194

128-
/**
129-
* Constructs a Base16 codec used for decoding and encoding.
130-
*
131-
* @param encodeTable the encode table.
132-
* @param decodingPolicy Decoding policy.
133-
*/
134-
private Base16(final byte[] encodeTable, final CodecPolicy decodingPolicy) {
135-
super(BYTES_PER_UNENCODED_BLOCK, BYTES_PER_ENCODED_BLOCK, 0, 0, PAD_DEFAULT, decodingPolicy);
136-
Objects.requireNonNull(encodeTable, "encodeTable");
137-
this.encodeTable = encodeTable;
138-
this.decodeTable = encodeTable == LOWER_CASE_ENCODE_TABLE ? LOWER_CASE_DECODE_TABLE : UPPER_CASE_DECODE_TABLE;
195+
private Base16(final Builder builder) {
196+
super(builder);
197+
this.encodeTable = Objects.requireNonNull(builder.getEncodeTable(), "encodeTable");
198+
this.decodeTable = Arrays.equals(encodeTable, LOWER_CASE_ENCODE_TABLE) ? LOWER_CASE_DECODE_TABLE : UPPER_CASE_DECODE_TABLE;
139199
}
140200

141201
@Override

src/test/java/org/apache/commons/codec/binary/Base16InputStreamTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ void testBase16InputStreamByChunk() throws IOException {
9393
decoded = BaseNTestData.DECODED;
9494
testByChunk(encoded, decoded);
9595
// test random data of sizes 0 through 150
96-
final BaseNCodec codec = new Base16(true);
96+
final BaseNCodec codec = Base16.builder().setLowerCase(true).get();
9797
for (int i = 0; i <= 150; i++) {
9898
final byte[][] randomData = BaseNTestData.randomData(codec, i);
9999
encoded = randomData[1];
@@ -122,7 +122,7 @@ void testBase16InputStreamByteByByte() throws IOException {
122122
decoded = BaseNTestData.DECODED;
123123
testByteByByte(encoded, decoded);
124124
// test random data of sizes 0 through 150
125-
final BaseNCodec codec = new Base16(true);
125+
final BaseNCodec codec = Base16.builder().setLowerCase(true).get();
126126
for (int i = 0; i <= 150; i++) {
127127
final byte[][] randomData = BaseNTestData.randomData(codec, i);
128128
encoded = randomData[1];
@@ -225,7 +225,7 @@ private void testByteByByte(final byte[] encoded, final byte[] decoded, final bo
225225
}
226226
try (InputStream in = Base16InputStream.builder()
227227
.setInputStream(new ByteArrayInputStream(decoded))
228-
.setEncode(true).setBaseNCodec(new Base16(lowerCase))
228+
.setEncode(true).setBaseNCodec(Base16.builder().setLowerCase(lowerCase).get())
229229
.get()) {
230230
final byte[] output = new byte[encoded.length];
231231
for (int i = 0; i < output.length; i++) {

src/test/java/org/apache/commons/codec/binary/Base16OutputStreamTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ void testBase16OutputStreamByChunk() throws IOException {
6565
testByChunk(encoded, decoded);
6666

6767
// test random data of sizes 0 through 150
68-
final BaseNCodec codec = new Base16(true);
68+
final BaseNCodec codec = Base16.builder().setLowerCase(true).get();
6969
for (int i = 0; i <= 150; i++) {
7070
final byte[][] randomData = BaseNTestData.randomData(codec, i);
7171
encoded = randomData[1];
@@ -90,7 +90,7 @@ void testBase16OutputStreamByteByByte() throws IOException {
9090
decoded = new byte[] { (byte) 0x41 };
9191
testByteByByte(encoded, decoded);
9292
// test random data of sizes 0 through 150
93-
final BaseNCodec codec = new Base16(true);
93+
final BaseNCodec codec = Base16.builder().setLowerCase(true).get();
9494
for (int i = 0; i <= 150; i++) {
9595
final byte[][] randomData = BaseNTestData.randomData(codec, i);
9696
encoded = randomData[1];
@@ -199,7 +199,7 @@ private void testByteByByte(final byte[] encoded, final byte[] decoded, final bo
199199
}
200200
try (ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
201201
OutputStream out = Base16OutputStream.builder()
202-
.setOutputStream(byteOut).setEncode(true).setBaseNCodec(new Base16(lowerCase))
202+
.setOutputStream(byteOut).setEncode(true).setBaseNCodec(Base16.builder().setLowerCase(lowerCase).get())
203203
.get()) {
204204
for (final byte element : decoded) {
205205
out.write(element);

src/test/java/org/apache/commons/codec/binary/Base16Test.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -248,15 +248,14 @@ void testEncodeDecodeSmall() {
248248
@Test
249249
void testIsInAlphabet() {
250250
// invalid bounds
251-
Base16 b16 = new Base16(true);
251+
Base16 b16 = Base16.builder().setLowerCase(true).get();
252252
assertFalse(b16.isInAlphabet((byte) 0));
253253
assertFalse(b16.isInAlphabet((byte) 1));
254254
assertFalse(b16.isInAlphabet((byte) -1));
255255
assertFalse(b16.isInAlphabet((byte) -15));
256256
assertFalse(b16.isInAlphabet((byte) -16));
257257
assertFalse(b16.isInAlphabet((byte) 128));
258258
assertFalse(b16.isInAlphabet((byte) 255));
259-
260259
// lower-case
261260
b16 = new Base16(true);
262261
for (char c = '0'; c <= '9'; c++) {
@@ -273,7 +272,6 @@ void testIsInAlphabet() {
273272
assertFalse(b16.isInAlphabet((byte) ('a' - 1)));
274273
assertFalse(b16.isInAlphabet((byte) ('f' + 1)));
275274
assertFalse(b16.isInAlphabet((byte) ('z' + 1)));
276-
277275
// upper-case
278276
b16 = new Base16(false);
279277
for (char c = '0'; c <= '9'; c++) {

0 commit comments

Comments
 (0)