1 package org.djutils.decoderdumper;
2
3 import java.io.ByteArrayOutputStream;
4 import java.io.IOException;
5
6 import org.djutils.logger.CategoryLogger;
7
8
9
10
11
12
13
14
15
16
17
18 public class Base64Decoder implements Decoder
19 {
20
21 private int notYetDecodedData = 0;
22
23
24 private int accumulatedBits = 0;
25
26
27 private final Dumper<Base64Decoder> internalDumper = new Dumper<>();
28
29
30 private final ByteArrayOutputStream baos;
31
32
33 private int endOfInputCharsSeen = 0;
34
35
36 private boolean errorDetected = false;
37
38
39
40
41
42
43
44 public Base64Decoder(final int decodedBytesPerLine, final int extraSpaceAfterEvery)
45 {
46 this.baos = new ByteArrayOutputStream();
47 this.internalDumper.setOutputStream(this.baos);
48 int maximumBytesPerOutputLine = (decodedBytesPerLine + 3) / 4 * 3;
49 this.internalDumper.addDecoder(new HexDecoder(maximumBytesPerOutputLine, extraSpaceAfterEvery));
50 this.internalDumper.addDecoder(new FixedString(" "));
51 this.internalDumper.addDecoder(new CharDecoder(maximumBytesPerOutputLine, extraSpaceAfterEvery));
52 }
53
54
55 @Override
56 public String getResult()
57 {
58 try
59 {
60 this.internalDumper.flush();
61 String result = this.baos.toString("UTF-8");
62 this.baos.reset();
63 return result;
64 }
65 catch (IOException ioe)
66 {
67
68 return null;
69 }
70 }
71
72
73 @Override
74 public int getMaximumWidth()
75 {
76 return this.internalDumper.getMaximumWidth();
77 }
78
79
80 @Override
81 public boolean append(final int address, final byte theByte) throws IOException
82 {
83 if (theByte == 61)
84 {
85 this.endOfInputCharsSeen++;
86 }
87 if (this.endOfInputCharsSeen > 0)
88 {
89 return false;
90 }
91 int value;
92 if (theByte >= 48 && theByte <= 57)
93 {
94 value = theByte - 48 + 52;
95 }
96 else if (theByte >= 65 && theByte <= 90)
97 {
98 value = theByte - 65 + 0;
99 }
100 else if (theByte >= 97 && theByte <= 122)
101 {
102 value = theByte - 97 + 26;
103 }
104 else if (theByte == 43 || theByte == 45 || theByte == 46)
105 {
106 value = 62;
107 }
108 else if (theByte == 47 || theByte == 95 || theByte == 44)
109 {
110 value = 63;
111 }
112 else if (theByte <= 32)
113 {
114 return false;
115 }
116 else
117 {
118
119 if (!this.errorDetected)
120 {
121 CategoryLogger.always().info("illegal character found in Base64Decoder stream at address {}, character {}",
122 address, theByte);
123 }
124 this.errorDetected = true;
125 return false;
126 }
127 this.notYetDecodedData = (this.notYetDecodedData << 6) + value;
128 this.accumulatedBits += 6;
129 if (this.accumulatedBits >= 8)
130 {
131 int byteValue = this.notYetDecodedData >> (this.accumulatedBits - 8);
132 this.accumulatedBits -= 8;
133 this.notYetDecodedData -= byteValue << this.accumulatedBits;
134 boolean result = this.internalDumper.append((byte) byteValue);
135 return result;
136 }
137 return false;
138 }
139
140
141 @Override
142 public boolean ignoreForIdenticalOutputCheck()
143 {
144 return false;
145 }
146
147 }