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.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; // insane representation for unassigned user - JRADEV-4262
172 		}
173 
174 		// deleted user? BUG in REST API: JRA-30263
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 	 * Tries to parse date and time and return that. If fails then tries to parse date only.
199 	 * @param str String contains either date and time or date only
200 	 * @return date and time or date only
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 }