001package com.nimbusds.jose;
002
003
004import java.io.UnsupportedEncodingException;
005import java.text.ParseException;
006
007import net.jcip.annotations.Immutable;
008import net.minidev.json.JSONObject;
009
010import com.nimbusds.jose.util.Base64URL;
011import com.nimbusds.jose.util.JSONObjectUtils;
012
013
014/**
015 * Payload with JSON object, string, byte array and Base64URL views. Represents
016 * the original object that was signed with JWS or encrypted with JWE. This 
017 * class is immutable.
018 *
019 * <p>Non-initial views are created on demand to conserve resources.
020 *
021 * <p>UTF-8 is the character set for all string from / to byte array 
022 * conversions.
023 *
024 * <p>Conversion relations:
025 *
026 * <pre>
027 * JSONObject <=> String <=> Base64URL
028 *                       <=> byte[]
029 * </pre>
030 *
031 * @author Vladimir Dzhuvinov
032 * @version $version$ (2012-10-23)
033 */
034@Immutable
035public class Payload {
036
037
038        /**
039         * Enumeration of the original data types used to create a 
040         * {@link Payload}.
041         */
042        public static enum Origin {
043
044
045                /**
046                 * The payload was created from a JSON object.
047                 */
048                JSON,
049
050
051                /**
052                 * The payload was created from a string.
053                 */
054                STRING,
055
056
057                /**
058                 * The payload was created from a byte array.
059                 */
060                BYTE_ARRAY,
061
062
063                /**
064                 * The payload was created from a Base64URL-encoded object.
065                 */
066                BASE64URL;
067        }
068
069
070        /**
071         * UTF-8 is the character set for all string from / to byte array
072         * conversions.
073         */
074        private static final String CHARSET = "UTF-8";
075
076
077        /**
078         * The original payload data type.
079         */
080        private Origin origin;
081
082
083        /**
084         * The JSON object view.
085         */
086        private JSONObject jsonView = null;
087
088
089        /**
090         * The string view.
091         */
092        private String stringView = null;
093
094
095        /**
096         * The byte array view.
097         */
098        private byte[] bytesView = null;
099
100
101        /**
102         * The Base64URL view.
103         */
104        private Base64URL base64URLView = null;
105
106
107        /**
108         * Converts a byte array to a string using {@link #CHARSET}.
109         *
110         * @param bytes The byte array to convert. May be {@code null}.
111         *
112         * @return The resulting string, {@code null} if conversion failed.
113         */
114        private static String byteArrayToString(final byte[] bytes) {
115
116                if (bytes == null) {
117                        return null;
118                }
119
120                try {
121                        return new String(bytes, CHARSET);
122
123                } catch (UnsupportedEncodingException e) {
124
125                        // UTF-8 should always be supported
126                        return null;
127                }
128        }
129
130
131        /**
132         * Converts a string to a byte array using {@link #CHARSET}.
133         *
134         * @param stirng The string to convert. May be {@code null}.
135         *
136         * @return The resulting byte array, {@code null} if conversion failed.
137         */
138        private static byte[] stringToByteArray(final String string) {
139
140                if (string == null) {
141                        return null;
142                }
143
144                try {
145                        return string.getBytes(CHARSET);
146
147                } catch (UnsupportedEncodingException e) {
148
149                        // UTF-8 should always be supported
150                        return null;
151                }
152        }
153
154
155        /**
156         * Creates a new payload from the specified JSON object.
157         *
158         * @param json The JSON object representing the payload. Must not be
159         *             {@code null}.
160         */
161        public Payload(final JSONObject json) {
162
163                if (json == null) {
164                        throw new IllegalArgumentException("The JSON object must not be null");
165                }
166
167                jsonView = json;
168
169                origin = Origin.JSON;
170        }
171
172
173        /**
174         * Creates a new payload from the specified string.
175         *
176         * @param string The string representing the payload. Must not be 
177         *               {@code null}.
178         */
179        public Payload(final String string) {
180
181                if (string == null) {
182                        throw new IllegalArgumentException("The string must not be null");
183                }
184
185                stringView = string;
186
187                origin = Origin.STRING;
188        }
189
190
191        /**
192         * Creates a new payload from the specified byte array.
193         *
194         * @param bytes The byte array representing the payload. Must not be 
195         *              {@code null}.
196         */
197        public Payload(final byte[] bytes) {
198
199                if (bytes == null) {
200                        throw new IllegalArgumentException("The byte array must not be null");
201                }
202
203                bytesView = bytes;
204
205                origin = Origin.BYTE_ARRAY;
206        }
207
208
209        /**
210         * Creates a new payload from the specified Base64URL-encoded object.
211         *
212         * @param base64URL The Base64URL-encoded object representing the 
213         *                  payload. Must not be {@code null}.
214         */
215        public Payload(final Base64URL base64URL) {
216
217                if (base64URL == null) {
218                        throw new IllegalArgumentException("The Base64URL-encoded object must not be null");
219                }
220
221                base64URLView = base64URL;
222
223                origin = Origin.BASE64URL;
224        }
225
226
227        /**
228         * Gets the original data type used to create this payload.
229         *
230         * @return The payload origin.
231         */
232        public Origin getOrigin() {
233
234                return origin;
235        }
236
237
238        /**
239         * Returns a JSON object view of this payload.
240         *
241         * @return The JSON object view, {@code null} if the payload couldn't
242         *         be converted to a JSON object.
243         */
244        public JSONObject toJSONObject() {
245
246                if (jsonView != null) {
247                        return jsonView;
248                }
249
250                // Convert
251                if (stringView != null) {
252
253                        try {
254                                jsonView = JSONObjectUtils.parseJSONObject(stringView);
255
256                        } catch (ParseException e) {
257
258                                // jsonView remains null
259                        }
260                }
261                else if (bytesView != null) {
262
263                        stringView = byteArrayToString(bytesView);
264
265                        try {
266                                jsonView = JSONObjectUtils.parseJSONObject(stringView);
267
268                        } catch (ParseException e) {
269
270                                // jsonView remains null
271                        }
272                }
273                else if (base64URLView != null) {
274
275                        stringView = base64URLView.decodeToString();
276
277                        try {
278                                jsonView = JSONObjectUtils.parseJSONObject(stringView);
279
280                        } catch (ParseException e) {
281
282                                // jsonView remains null
283                        }
284                }
285
286                return jsonView;
287        }
288
289
290        /**
291         * Returns a string view of this payload.
292         *
293         * @return The string view.
294         */
295        @Override
296        public String toString() {
297
298                if (stringView != null) {
299                        return stringView;
300                }
301
302                // Convert
303                if (jsonView != null) {
304
305                        stringView = jsonView.toString();
306                }
307                else if (bytesView != null) {
308
309                        stringView = byteArrayToString(bytesView);
310                }
311                else if (base64URLView != null) {
312
313                        stringView = base64URLView.decodeToString();
314                }
315
316                return stringView;
317        }
318
319
320        /**
321         * Returns a byte array view of this payload.
322         *
323         * @return The byte array view.
324         */
325        public byte[] toBytes() {
326
327                if (bytesView != null) {
328                        return bytesView;
329                }
330
331                // Convert
332                if (stringView != null) {
333
334                        bytesView = stringToByteArray(stringView);
335                }               
336                else if (jsonView != null) {
337
338                        stringView = jsonView.toString();
339                        bytesView = stringToByteArray(stringView);
340                }
341                else if (base64URLView != null) {
342
343                        bytesView = base64URLView.decode();
344                }
345
346                return bytesView;       
347        }
348
349
350        /**
351         * Returns a Base64URL view of this payload.
352         *
353         * @return The Base64URL view.
354         */
355        public Base64URL toBase64URL() {
356
357                if (base64URLView != null) {
358                        return base64URLView;
359                }
360
361                // Convert
362
363                if (stringView != null) {
364
365                        base64URLView = Base64URL.encode(stringView);
366
367                }
368                else if (bytesView != null) {
369
370                        base64URLView = Base64URL.encode(bytesView);
371
372                }
373                else if (jsonView != null) {
374
375                        stringView = jsonView.toString();
376                        base64URLView = Base64URL.encode(stringView);
377                }
378
379                return base64URLView;
380        }
381}