2727import java .io .UnsupportedEncodingException ;
2828import java .io .Writer ;
2929
30+ import static org .codehaus .groovy .runtime .EncodingGroovyMethodsSupport .TRANSLATE_TABLE ;
31+ import static org .codehaus .groovy .runtime .EncodingGroovyMethodsSupport .TRANSLATE_TABLE_URLSAFE ;
32+
3033/**
3134 * This class defines all the encoding/decoding groovy methods which enhance
3235 * the normal JDK classes when inside the Groovy environment.
@@ -36,6 +39,8 @@ public class EncodingGroovyMethods {
3639
3740 private static final char [] T_TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=" .toCharArray ();
3841
42+ private static final char [] T_TABLE_URLSAFE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_=" .toCharArray ();
43+
3944 private static final String CHUNK_SEPARATOR = "\r \n " ;
4045
4146 /**
@@ -76,18 +81,22 @@ public static Writable encodeBase64(Byte[] data) {
7681 * @since 1.5.7
7782 */
7883 public static Writable encodeBase64 (final byte [] data , final boolean chunked ) {
84+ return encodeBase64 (data , chunked , false , true );
85+ }
86+
87+ private static Writable encodeBase64 (final byte [] data , final boolean chunked , final boolean urlSafe , final boolean pad ) {
7988 return new Writable () {
8089 public Writer writeTo (final Writer writer ) throws IOException {
8190 int charCount = 0 ;
8291 final int dLimit = (data .length / 3 ) * 3 ;
83-
92+ final char [] table = urlSafe ? T_TABLE_URLSAFE : T_TABLE ;
8493 for (int dIndex = 0 ; dIndex != dLimit ; dIndex += 3 ) {
8594 int d = ((data [dIndex ] & 0XFF ) << 16 ) | ((data [dIndex + 1 ] & 0XFF ) << 8 ) | (data [dIndex + 2 ] & 0XFF );
8695
87- writer .write (T_TABLE [d >> 18 ]);
88- writer .write (T_TABLE [(d >> 12 ) & 0X3F ]);
89- writer .write (T_TABLE [(d >> 6 ) & 0X3F ]);
90- writer .write (T_TABLE [d & 0X3F ]);
96+ writer .write (table [d >> 18 ]);
97+ writer .write (table [(d >> 12 ) & 0X3F ]);
98+ writer .write (table [(d >> 6 ) & 0X3F ]);
99+ writer .write (table [d & 0X3F ]);
91100
92101 if (chunked && ++charCount == 19 ) {
93102 writer .write (CHUNK_SEPARATOR );
@@ -102,10 +111,16 @@ public Writer writeTo(final Writer writer) throws IOException {
102111 d |= (data [dLimit + 1 ] & 0XFF ) << 8 ;
103112 }
104113
105- writer .write (T_TABLE [d >> 18 ]);
106- writer .write (T_TABLE [(d >> 12 ) & 0X3F ]);
107- writer .write ((dLimit + 1 < data .length ) ? T_TABLE [(d >> 6 ) & 0X3F ] : '=' );
108- writer .write ('=' );
114+ writer .write (table [d >> 18 ]);
115+ writer .write (table [(d >> 12 ) & 0X3F ]);
116+ if (pad ) {
117+ writer .write ((dLimit + 1 < data .length ) ? table [(d >> 6 ) & 0X3F ] : '=' );
118+ writer .write ('=' );
119+ } else {
120+ if (dLimit + 1 < data .length ) {
121+ writer .write (table [(d >> 6 ) & 0X3F ]);
122+ }
123+ }
109124 if (chunked && charCount != 0 ) {
110125 writer .write (CHUNK_SEPARATOR );
111126 }
@@ -141,6 +156,74 @@ public static Writable encodeBase64(final byte[] data) {
141156 return encodeBase64 (data , false );
142157 }
143158
159+ /**
160+ * Produce a Writable object which writes the Base64 URL and Filename Safe encoding of the byte array.
161+ * Calling toString() on the result returns the encoding as a String. For more
162+ * information on Base64 URL and Filename Safe encoding see <code>RFC 4648 - Section 5
163+ * Base 64 Encoding with URL and Filename Safe Alphabet</code>.
164+ * <p>
165+ * The method omits padding and is equivalent to calling
166+ * {@link org.codehaus.groovy.runtime.EncodingGroovyMethods#encodeBase64Url(Byte[], boolean)} with a
167+ * value of {@code false}.
168+ *
169+ * @param data Byte array to be encoded
170+ * @return object which will write the Base64 URL and Filename Safe encoding of the byte array
171+ * @see org.codehaus.groovy.runtime.EncodingGroovyMethods#encodeBase64Url(Byte[], boolean)
172+ * @since 2.5
173+ */
174+ public static Writable encodeBase64Url (Byte [] data ) {
175+ return encodeBase64Url (data , false );
176+ }
177+
178+ /**
179+ * Produce a Writable object which writes the Base64 URL and Filename Safe encoding of the byte array.
180+ * Calling toString() on the result returns the encoding as a String. For more
181+ * information on Base64 URL and Filename Safe encoding see <code>RFC 4648 - Section 5
182+ * Base 64 Encoding with URL and Filename Safe Alphabet</code>.
183+ *
184+ * @param data Byte array to be encoded
185+ * @param pad whether or not the encoded data should be padded
186+ * @return object which will write the Base64 URL and Filename Safe encoding of the byte array
187+ * @since 2.5
188+ */
189+ public static Writable encodeBase64Url (Byte [] data , boolean pad ) {
190+ return encodeBase64Url (DefaultTypeTransformation .convertToByteArray (data ), pad );
191+ }
192+
193+ /**
194+ * Produce a Writable object which writes the Base64 URL and Filename Safe encoding of the byte array.
195+ * Calling toString() on the result returns the encoding as a String. For more
196+ * information on Base64 URL and Filename Safe encoding see <code>RFC 4648 - Section 5
197+ * Base 64 Encoding with URL and Filename Safe Alphabet</code>.
198+ * <p>
199+ * The method omits padding and is equivalent to calling
200+ * {@link org.codehaus.groovy.runtime.EncodingGroovyMethods#encodeBase64Url(byte[], boolean)} with a
201+ * value of {@code false}.
202+ *
203+ * @param data Byte array to be encoded
204+ * @return object which will write the Base64 URL and Filename Safe encoding of the byte array
205+ * @see org.codehaus.groovy.runtime.EncodingGroovyMethods#encodeBase64Url(byte[], boolean)
206+ * @since 2.5
207+ */
208+ public static Writable encodeBase64Url (final byte [] data ) {
209+ return encodeBase64Url (data , false );
210+ }
211+
212+ /**
213+ * Produce a Writable object which writes the Base64 URL and Filename Safe encoding of the byte array.
214+ * Calling toString() on the result returns the encoding as a String. For more
215+ * information on Base64 URL and Filename Safe encoding see <code>RFC 4648 - Section 5
216+ * Base 64 Encoding with URL and Filename Safe Alphabet</code>.
217+ *
218+ * @param data Byte array to be encoded
219+ * @param pad whether or not the encoded data should be padded
220+ * @return object which will write the Base64 URL and Filename Safe encoding of the byte array
221+ * @since 2.5
222+ */
223+ public static Writable encodeBase64Url (final byte [] data , final boolean pad ) {
224+ return encodeBase64 (data , false , true , pad );
225+ }
226+
144227 /**
145228 * Decode the String from Base64 into a byte array.
146229 *
@@ -149,14 +232,29 @@ public static Writable encodeBase64(final byte[] data) {
149232 * @since 1.0
150233 */
151234 public static byte [] decodeBase64 (String value ) {
235+ return decodeBase64 (value , false );
236+ }
237+
238+ /**
239+ * Decodes a Base64 URL and Filename Safe encoded String into a byte array.
240+ *
241+ * @param value the string to be decoded
242+ * @return the decoded bytes as an array
243+ * @since 2.5
244+ */
245+ public static byte [] decodeBase64Url (String value ) {
246+ return decodeBase64 (value , true );
247+ }
248+
249+ private static byte [] decodeBase64 (String value , boolean urlSafe ) {
152250 int byteShift = 4 ;
153251 int tmp = 0 ;
154252 boolean done = false ;
155253 final StringBuilder buffer = new StringBuilder ();
156-
254+ final byte [] table = urlSafe ? TRANSLATE_TABLE_URLSAFE : TRANSLATE_TABLE ;
157255 for (int i = 0 ; i != value .length (); i ++) {
158256 final char c = value .charAt (i );
159- final int sixBit = (c < 123 ) ? EncodingGroovyMethodsSupport . TRANSLATE_TABLE [c ] : 66 ;
257+ final int sixBit = (c < 123 ) ? table [c ] : 66 ;
160258
161259 if (sixBit < 64 ) {
162260 if (done )
0 commit comments