View Javadoc

1   /*
2    * Copyright (C) 2010 Atlassian
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
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; // insane representation for unassigned user - JRADEV-4262
195         }
196 
197         // deleted user? BUG in REST API: JRA-30263
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      * Tries to parse date and time and return that. If fails then tries to parse date only.
222      *
223      * @param str String contains either date and time or date only
224      * @return date and time or date only
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 }