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