001    package com.nimbusds.jose;
002    
003    
004    import java.io.UnsupportedEncodingException;
005    import java.text.ParseException;
006    
007    import net.jcip.annotations.Immutable;
008    import net.minidev.json.JSONObject;
009    
010    import com.nimbusds.jose.util.Base64URL;
011    import 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
035    public 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    }