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.domain.BasicUser;
20 import com.atlassian.jira.rest.client.api.ExpandableProperty;
21 import com.atlassian.jira.rest.client.api.OptionalIterable;
22 import com.atlassian.jira.rest.client.api.RestClientException;
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 @SuppressWarnings("UnusedDeclaration")
66 public static <T> ExpandableProperty<T> parseExpandableProperty(final JSONObject json, final JsonObjectParser<T> expandablePropertyBuilder)
67 throws JSONException {
68 return parseExpandableProperty(json, false, expandablePropertyBuilder);
69 }
70
71 @Nullable
72 public static <T> ExpandableProperty<T> parseOptionalExpandableProperty(@Nullable final JSONObject json, final JsonObjectParser<T> expandablePropertyBuilder)
73 throws JSONException {
74 return parseExpandableProperty(json, true, expandablePropertyBuilder);
75 }
76
77 @Nullable
78 private static <T> ExpandableProperty<T> parseExpandableProperty(@Nullable final JSONObject json, final Boolean optional, final JsonObjectParser<T> expandablePropertyBuilder)
79 throws JSONException {
80 if (json == null) {
81 if (!optional) {
82 throw new IllegalArgumentException("json object cannot be null while optional is false");
83 }
84 return null;
85 }
86
87 final int numItems = json.getInt("size");
88 final Collection<T> items;
89 JSONArray itemsJa = json.getJSONArray("items");
90
91 if (itemsJa.length() > 0) {
92 items = new ArrayList<T>(numItems);
93 for (int i = 0; i < itemsJa.length(); i++) {
94 final T item = expandablePropertyBuilder.parse(itemsJa.getJSONObject(i));
95 items.add(item);
96 }
97 } else {
98 items = null;
99 }
100
101 return new ExpandableProperty<T>(numItems, items);
102 }
103
104
105 public static URI getSelfUri(final JSONObject jsonObject) throws JSONException {
106 return parseURI(jsonObject.getString(SELF_ATTR));
107 }
108
109 public static URI optSelfUri(final JSONObject jsonObject, final URI defaultUri) throws JSONException {
110 final String selfUri = jsonObject.optString(SELF_ATTR, null);
111 return selfUri != null ? parseURI(selfUri) : defaultUri;
112 }
113
114 @SuppressWarnings("unused")
115 public static JSONObject getNestedObject(JSONObject json, final String... path) throws JSONException {
116 for (String s : path) {
117 json = json.getJSONObject(s);
118 }
119 return json;
120 }
121
122 @Nullable
123 public static JSONObject getNestedOptionalObject(JSONObject json, final String... path) throws JSONException {
124 for (int i = 0; i < path.length - 1; i++) {
125 String s = path[i];
126 json = json.getJSONObject(s);
127 }
128 return json.optJSONObject(path[path.length - 1]);
129 }
130
131 @SuppressWarnings("unused")
132 public static JSONArray getNestedArray(JSONObject json, final String... path) throws JSONException {
133 for (int i = 0; i < path.length - 1; i++) {
134 String s = path[i];
135 json = json.getJSONObject(s);
136 }
137 return json.getJSONArray(path[path.length - 1]);
138 }
139
140 public static JSONArray getNestedOptionalArray(JSONObject json, final String... path) throws JSONException {
141 for (int i = 0; json != null && i < path.length - 1; i++) {
142 String s = path[i];
143 json = json.optJSONObject(s);
144 }
145 return json == null ? null : json.optJSONArray(path[path.length - 1]);
146 }
147
148
149 public static String getNestedString(JSONObject json, final String... path) throws JSONException {
150 for (int i = 0; i < path.length - 1; i++) {
151 String s = path[i];
152 json = json.getJSONObject(s);
153 }
154 return json.getString(path[path.length - 1]);
155 }
156
157 @SuppressWarnings("unused")
158 public static boolean getNestedBoolean(JSONObject json, final String... path) throws JSONException {
159 for (int i = 0; i < path.length - 1; i++) {
160 String s = path[i];
161 json = json.getJSONObject(s);
162 }
163 return json.getBoolean(path[path.length - 1]);
164 }
165
166
167 public static URI parseURI(final String str) {
168 try {
169 return new URI(str);
170 } catch (URISyntaxException e) {
171 throw new RestClientException(e);
172 }
173 }
174
175 @Nullable
176 public static URI parseOptionalURI(final JSONObject jsonObject, final String attributeName) {
177 final String s = getOptionalString(jsonObject, attributeName);
178 return s != null ? parseURI(s) : null;
179 }
180
181 @Nullable
182 public static BasicUser parseBasicUser(@Nullable final JSONObject json) throws JSONException {
183 if (json == null) {
184 return null;
185 }
186 final String username = json.getString("name");
187 if (!json.has(JsonParseUtil.SELF_ATTR) && "Anonymous".equals(username)) {
188 return null;
189 }
190
191
192 final URI selfUri = optSelfUri(json, BasicUser.INCOMPLETE_URI);
193 return new BasicUser(selfUri, username, json.optString("displayName", null));
194 }
195
196 public static DateTime parseDateTime(final JSONObject jsonObject, final String attributeName) throws JSONException {
197 return parseDateTime(jsonObject.getString(attributeName));
198 }
199
200 @Nullable
201 public static DateTime parseOptionalDateTime(final JSONObject jsonObject, final String attributeName) throws JSONException {
202 final String s = getOptionalString(jsonObject, attributeName);
203 return s != null ? parseDateTime(s) : null;
204 }
205
206 public static DateTime parseDateTime(final String str) {
207 try {
208 return JIRA_DATE_TIME_FORMATTER.parseDateTime(str);
209 } catch (Exception e) {
210 throw new RestClientException(e);
211 }
212 }
213
214
215
216
217
218
219
220 public static DateTime parseDateTimeOrDate(final String str) {
221 try {
222 return JIRA_DATE_TIME_FORMATTER.parseDateTime(str);
223 } catch (Exception ignored) {
224 try {
225 return JIRA_DATE_FORMATTER.parseDateTime(str);
226 } catch (Exception e) {
227 throw new RestClientException(e);
228 }
229 }
230 }
231
232 public static DateTime parseDate(final String str) {
233 try {
234 return JIRA_DATE_FORMATTER.parseDateTime(str);
235 } catch (Exception e) {
236 throw new RestClientException(e);
237 }
238 }
239
240 public static String formatDate(final DateTime dateTime) {
241 return JIRA_DATE_FORMATTER.print(dateTime);
242 }
243
244 @SuppressWarnings("unused")
245 public static String formatDateTime(final DateTime dateTime) {
246 return JIRA_DATE_TIME_FORMATTER.print(dateTime);
247 }
248
249
250 @Nullable
251 public static String getNullableString(final JSONObject jsonObject, final String attributeName) throws JSONException {
252 final Object o = jsonObject.get(attributeName);
253 if (o == JSONObject.NULL) {
254 return null;
255 }
256 return o.toString();
257 }
258
259
260 @Nullable
261 public static String getOptionalString(final JSONObject jsonObject, final String attributeName) {
262 final Object res = jsonObject.opt(attributeName);
263 if (res == JSONObject.NULL || res == null) {
264 return null;
265 }
266 return res.toString();
267 }
268
269 @SuppressWarnings("unused")
270 @Nullable
271 public static JSONObject getOptionalJsonObject(final JSONObject jsonObject, final String attributeName) {
272 final JSONObject res = jsonObject.optJSONObject(attributeName);
273 if (res == JSONObject.NULL || res == null) {
274 return null;
275 }
276 return res;
277 }
278
279
280 public static Collection<String> toStringCollection(final JSONArray jsonArray) throws JSONException {
281 final ArrayList<String> res = new ArrayList<String>(jsonArray.length());
282 for (int i = 0; i < jsonArray.length(); i++) {
283 res.add(jsonArray.getString(i));
284 }
285 return res;
286 }
287
288 public static Integer parseOptionInteger(final JSONObject json, final String attributeName) throws JSONException {
289 return json.has(attributeName) ? json.getInt(attributeName) : null;
290 }
291
292 @Nullable
293 public static Long getOptionalLong(final JSONObject jsonObject, final String attributeName) throws JSONException {
294 return jsonObject.has(attributeName) ? jsonObject.getLong(attributeName) : null;
295 }
296
297 public static Optional<JSONArray> getOptionalArray(final JSONObject jsonObject, final String attributeName)
298 throws JSONException {
299 return jsonObject.has(attributeName) ?
300 Optional.of(jsonObject.getJSONArray(attributeName)) : Optional.<JSONArray>absent();
301 }
302
303 public static Map<String, URI> getAvatarUris(final JSONObject jsonObject) throws JSONException {
304 Map<String, URI> uris = Maps.newHashMap();
305
306 final Iterator iterator = jsonObject.keys();
307 while (iterator.hasNext()) {
308 final Object o = iterator.next();
309 if (!(o instanceof String)) {
310 throw new JSONException(
311 "Cannot parse URIs: key is expected to be valid String. Got " + (o == null ? "null" : o.getClass())
312 + " instead.");
313 }
314 final String key = (String) o;
315 uris.put(key, JsonParseUtil.parseURI(jsonObject.getString(key)));
316 }
317 return uris;
318 }
319
320 @SuppressWarnings("unchecked")
321 public static Iterator<String> getStringKeys(final JSONObject json) {
322 return json.keys();
323 }
324
325 public static Map<String, String> toStringMap(final JSONArray names, final JSONObject values) throws JSONException {
326 final Map<String, String> result = Maps.newHashMap();
327 for (int i = 0; i < names.length(); i++) {
328 final String key = names.getString(i);
329 result.put(key, values.getString(key));
330 }
331 return result;
332 }
333 }