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