1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package com.atlassian.jira.rest.client.internal.json;
18
19 import com.atlassian.jira.rest.client.api.ExpandableProperty;
20 import com.atlassian.jira.rest.client.api.OptionalIterable;
21 import com.atlassian.jira.rest.client.api.RestClientException;
22 import com.atlassian.jira.rest.client.api.domain.BasicUser;
23 import com.google.common.base.Optional;
24 import com.google.common.collect.Maps;
25 import org.codehaus.jettison.json.JSONArray;
26 import org.codehaus.jettison.json.JSONException;
27 import org.codehaus.jettison.json.JSONObject;
28 import org.joda.time.DateTime;
29 import org.joda.time.format.DateTimeFormat;
30 import org.joda.time.format.DateTimeFormatter;
31 import org.joda.time.format.ISODateTimeFormat;
32
33 import javax.annotation.Nullable;
34 import java.net.URI;
35 import java.net.URISyntaxException;
36 import java.util.ArrayList;
37 import java.util.Collection;
38 import java.util.Iterator;
39 import java.util.Map;
40
41 public class JsonParseUtil {
42 public static final String JIRA_DATE_TIME_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSSZ";
43 public static final DateTimeFormatter JIRA_DATE_TIME_FORMATTER = DateTimeFormat.forPattern(JIRA_DATE_TIME_PATTERN);
44 public static final DateTimeFormatter JIRA_DATE_FORMATTER = ISODateTimeFormat.date();
45 public static final String SELF_ATTR = "self";
46
47 public static <T> Collection<T> parseJsonArray(final JSONArray jsonArray, final JsonObjectParser<T> jsonParser)
48 throws JSONException {
49 final Collection<T> res = new ArrayList<T>(jsonArray.length());
50 for (int i = 0; i < jsonArray.length(); i++) {
51 res.add(jsonParser.parse(jsonArray.getJSONObject(i)));
52 }
53 return res;
54 }
55
56 public static <T> OptionalIterable<T> parseOptionalJsonArray(final JSONArray jsonArray, final JsonObjectParser<T> jsonParser)
57 throws JSONException {
58 if (jsonArray == null) {
59 return OptionalIterable.absent();
60 } else {
61 return new OptionalIterable<T>(JsonParseUtil.<T>parseJsonArray(jsonArray, jsonParser));
62 }
63 }
64
65 public static <T> T parseOptionalJsonObject(final JSONObject json, final String attributeName, final JsonObjectParser<T> jsonParser)
66 throws JSONException {
67 JSONObject attributeObject = getOptionalJsonObject(json, attributeName);
68 return attributeObject != null ? jsonParser.parse(attributeObject) : null;
69 }
70
71 @SuppressWarnings("UnusedDeclaration")
72 public static <T> ExpandableProperty<T> parseExpandableProperty(final JSONObject json, final JsonObjectParser<T> expandablePropertyBuilder)
73 throws JSONException {
74 return parseExpandableProperty(json, false, expandablePropertyBuilder);
75 }
76
77 @Nullable
78 public static <T> ExpandableProperty<T> parseOptionalExpandableProperty(@Nullable final JSONObject json, final JsonObjectParser<T> expandablePropertyBuilder)
79 throws JSONException {
80 return parseExpandableProperty(json, true, expandablePropertyBuilder);
81 }
82
83 @Nullable
84 private static <T> ExpandableProperty<T> parseExpandableProperty(@Nullable final JSONObject json, final Boolean optional, final JsonObjectParser<T> expandablePropertyBuilder)
85 throws JSONException {
86 if (json == null) {
87 if (!optional) {
88 throw new IllegalArgumentException("json object cannot be null while optional is false");
89 }
90 return null;
91 }
92
93 final int numItems = json.getInt("size");
94 final Collection<T> items;
95 JSONArray itemsJa = json.getJSONArray("items");
96
97 if (itemsJa.length() > 0) {
98 items = new ArrayList<T>(numItems);
99 for (int i = 0; i < itemsJa.length(); i++) {
100 final T item = expandablePropertyBuilder.parse(itemsJa.getJSONObject(i));
101 items.add(item);
102 }
103 } else {
104 items = null;
105 }
106
107 return new ExpandableProperty<T>(numItems, items);
108 }
109
110
111 public static URI getSelfUri(final JSONObject jsonObject) throws JSONException {
112 return parseURI(jsonObject.getString(SELF_ATTR));
113 }
114
115 public static URI optSelfUri(final JSONObject jsonObject, final URI defaultUri) throws JSONException {
116 final String selfUri = jsonObject.optString(SELF_ATTR, null);
117 return selfUri != null ? parseURI(selfUri) : defaultUri;
118 }
119
120 @SuppressWarnings("unused")
121 public static JSONObject getNestedObject(JSONObject json, final String... path) throws JSONException {
122 for (String s : path) {
123 json = json.getJSONObject(s);
124 }
125 return json;
126 }
127
128 @Nullable
129 public static JSONObject getNestedOptionalObject(JSONObject json, final String... path) throws JSONException {
130 for (int i = 0; i < path.length - 1; i++) {
131 String s = path[i];
132 json = json.getJSONObject(s);
133 }
134 return json.optJSONObject(path[path.length - 1]);
135 }
136
137 @SuppressWarnings("unused")
138 public static JSONArray getNestedArray(JSONObject json, final String... path) throws JSONException {
139 for (int i = 0; i < path.length - 1; i++) {
140 String s = path[i];
141 json = json.getJSONObject(s);
142 }
143 return json.getJSONArray(path[path.length - 1]);
144 }
145
146 public static JSONArray getNestedOptionalArray(JSONObject json, final String... path) throws JSONException {
147 for (int i = 0; json != null && i < path.length - 1; i++) {
148 String s = path[i];
149 json = json.optJSONObject(s);
150 }
151 return json == null ? null : json.optJSONArray(path[path.length - 1]);
152 }
153
154
155 public static String getNestedString(JSONObject json, final String... path) throws JSONException {
156 for (int i = 0; i < path.length - 1; i++) {
157 String s = path[i];
158 json = json.getJSONObject(s);
159 }
160 return json.getString(path[path.length - 1]);
161 }
162
163 @SuppressWarnings("unused")
164 public static boolean getNestedBoolean(JSONObject json, final String... path) throws JSONException {
165 for (int i = 0; i < path.length - 1; i++) {
166 String s = path[i];
167 json = json.getJSONObject(s);
168 }
169 return json.getBoolean(path[path.length - 1]);
170 }
171
172
173 public static URI parseURI(final String str) {
174 try {
175 return new URI(str);
176 } catch (URISyntaxException e) {
177 throw new RestClientException(e);
178 }
179 }
180
181 @Nullable
182 public static URI parseOptionalURI(final JSONObject jsonObject, final String attributeName) {
183 final String s = getOptionalString(jsonObject, attributeName);
184 return s != null ? parseURI(s) : null;
185 }
186
187 @Nullable
188 public static BasicUser parseBasicUser(@Nullable final JSONObject json) throws JSONException {
189 if (json == null) {
190 return null;
191 }
192 final String username = json.getString("name");
193 if (!json.has(JsonParseUtil.SELF_ATTR) && "Anonymous".equals(username)) {
194 return null;
195 }
196
197
198 final URI selfUri = optSelfUri(json, BasicUser.INCOMPLETE_URI);
199 return new BasicUser(selfUri, username, json.optString("displayName", null));
200 }
201
202 public static DateTime parseDateTime(final JSONObject jsonObject, final String attributeName) throws JSONException {
203 return parseDateTime(jsonObject.getString(attributeName));
204 }
205
206 @Nullable
207 public static DateTime parseOptionalDateTime(final JSONObject jsonObject, final String attributeName) throws JSONException {
208 final String s = getOptionalString(jsonObject, attributeName);
209 return s != null ? parseDateTime(s) : null;
210 }
211
212 public static DateTime parseDateTime(final String str) {
213 try {
214 return JIRA_DATE_TIME_FORMATTER.parseDateTime(str);
215 } catch (Exception e) {
216 throw new RestClientException(e);
217 }
218 }
219
220
221
222
223
224
225
226 public static DateTime parseDateTimeOrDate(final String str) {
227 try {
228 return JIRA_DATE_TIME_FORMATTER.parseDateTime(str);
229 } catch (Exception ignored) {
230 try {
231 return JIRA_DATE_FORMATTER.parseDateTime(str);
232 } catch (Exception e) {
233 throw new RestClientException(e);
234 }
235 }
236 }
237
238 public static DateTime parseDate(final String str) {
239 try {
240 return JIRA_DATE_FORMATTER.parseDateTime(str);
241 } catch (Exception e) {
242 throw new RestClientException(e);
243 }
244 }
245
246 public static String formatDate(final DateTime dateTime) {
247 return JIRA_DATE_FORMATTER.print(dateTime);
248 }
249
250 @SuppressWarnings("unused")
251 public static String formatDateTime(final DateTime dateTime) {
252 return JIRA_DATE_TIME_FORMATTER.print(dateTime);
253 }
254
255
256 @Nullable
257 public static String getNullableString(final JSONObject jsonObject, final String attributeName) throws JSONException {
258 final Object o = jsonObject.get(attributeName);
259 if (o == JSONObject.NULL) {
260 return null;
261 }
262 return o.toString();
263 }
264
265
266 @Nullable
267 public static String getOptionalString(final JSONObject jsonObject, final String attributeName) {
268 final Object res = jsonObject.opt(attributeName);
269 if (res == JSONObject.NULL || res == null) {
270 return null;
271 }
272 return res.toString();
273 }
274
275 @Nullable
276 public static <T> T getOptionalJsonObject(final JSONObject jsonObject, final String attributeName, final JsonObjectParser<T> jsonParser) throws JSONException {
277 final JSONObject res = jsonObject.optJSONObject(attributeName);
278 if (res == JSONObject.NULL || res == null) {
279 return null;
280 }
281 return jsonParser.parse(res);
282 }
283
284 @SuppressWarnings("unused")
285 @Nullable
286 public static JSONObject getOptionalJsonObject(final JSONObject jsonObject, final String attributeName) {
287 final JSONObject res = jsonObject.optJSONObject(attributeName);
288 if (res == JSONObject.NULL || res == null) {
289 return null;
290 }
291 return res;
292 }
293
294
295 public static Collection<String> toStringCollection(final JSONArray jsonArray) throws JSONException {
296 final ArrayList<String> res = new ArrayList<String>(jsonArray.length());
297 for (int i = 0; i < jsonArray.length(); i++) {
298 res.add(jsonArray.getString(i));
299 }
300 return res;
301 }
302
303 public static Integer parseOptionInteger(final JSONObject json, final String attributeName) throws JSONException {
304 return json.has(attributeName) ? json.getInt(attributeName) : null;
305 }
306
307 @Nullable
308 public static Long getOptionalLong(final JSONObject jsonObject, final String attributeName) throws JSONException {
309 return jsonObject.has(attributeName) ? jsonObject.getLong(attributeName) : null;
310 }
311
312 public static Optional<JSONArray> getOptionalArray(final JSONObject jsonObject, final String attributeName)
313 throws JSONException {
314 return jsonObject.has(attributeName) ?
315 Optional.of(jsonObject.getJSONArray(attributeName)) : Optional.<JSONArray>absent();
316 }
317
318 public static Map<String, URI> getAvatarUris(final JSONObject jsonObject) throws JSONException {
319 Map<String, URI> uris = Maps.newHashMap();
320
321 final Iterator iterator = jsonObject.keys();
322 while (iterator.hasNext()) {
323 final Object o = iterator.next();
324 if (!(o instanceof String)) {
325 throw new JSONException(
326 "Cannot parse URIs: key is expected to be valid String. Got " + (o == null ? "null" : o.getClass())
327 + " instead.");
328 }
329 final String key = (String) o;
330 uris.put(key, JsonParseUtil.parseURI(jsonObject.getString(key)));
331 }
332 return uris;
333 }
334
335 @SuppressWarnings("unchecked")
336 public static Iterator<String> getStringKeys(final JSONObject json) {
337 return json.keys();
338 }
339
340 public static Map<String, String> toStringMap(final JSONArray names, final JSONObject values) throws JSONException {
341 final Map<String, String> result = Maps.newHashMap();
342 for (int i = 0; i < names.length(); i++) {
343 final String key = names.getString(i);
344 result.put(key, values.getString(key));
345 }
346 return result;
347 }
348 }