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