source: josm/trunk/src/org/openstreetmap/josm/io/ChangesetQuery.java@ 2688

Last change on this file since 2688 was 2688, checked in by Gubaer, 14 years ago

Partial commit due to issue described in #4137
Breaks the build

File size: 18.2 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.io;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.text.DateFormat;
7import java.text.ParseException;
8import java.text.SimpleDateFormat;
9import java.util.Date;
10import java.util.HashMap;
11import java.util.Map;
12
13import org.openstreetmap.josm.data.Bounds;
14import org.openstreetmap.josm.data.coor.LatLon;
15import org.openstreetmap.josm.tools.CheckParameterUtil;
16
17public class ChangesetQuery {
18
19 /**
20 * Replies a changeset query object from the query part of a OSM API URL for querying
21 * changesets.
22 *
23 * @param query the query part
24 * @return the query object
25 * @throws ChangesetQueryUrlException thrown if query doesn't consist of valid query parameters
26 *
27 */
28 static public ChangesetQuery buildFromUrlQuery(String query) throws ChangesetQueryUrlException{
29 return new ChangesetQueryUrlParser().parse(query);
30 }
31
32 /** the user id this query is restricted to. null, if no restriction to a user id applies */
33 private Integer uid = null;
34 /** the user name this query is restricted to. null, if no restriction to a user name applies */
35 private String userName = null;
36 /** the bounding box this query is restricted to. null, if no restriction to a bounding box applies */
37 private Bounds bounds = null;
38
39 private Date closedAfter = null;
40 private Date createdBefore = null;
41 /** indicates whether only open changesets are queried. null, if no restrictions regarding open changesets apply */
42 private Boolean open = null;
43 /** indicates whether only closed changesets are queried. null, if no restrictions regarding open changesets apply */
44 private Boolean closed = null;
45
46 public ChangesetQuery() {}
47
48 /**
49 * Restricts the query to changesets owned by the user with id <code>uid</code>.
50 *
51 * @param uid the uid of the user. >0 expected.
52 * @return the query object with the applied restriction
53 * @throws IllegalArgumentException thrown if uid <= 0
54 * @see #forUser(String)
55 */
56 public ChangesetQuery forUser(int uid) throws IllegalArgumentException{
57 if (uid <= 0)
58 throw new IllegalArgumentException(tr("Parameter ''{0}'' > 0 expected. Got ''{1}''.", "uid", uid));
59 this.uid = uid;
60 this.userName = null;
61 return this;
62 }
63
64 /**
65 * Restricts the query to changesets owned by the user with user name <code>username</code>.
66 *
67 * Caveat: for historical reasons the username might not be unique! It is recommended to use
68 * {@see #forUser(int)} to restrict the query to a specific user.
69 *
70 * @param username the username. Must not be null.
71 * @return the query object with the applied restriction
72 * @throws IllegalArgumentException thrown if username is null.
73 * @see #forUser(int)
74 */
75 public ChangesetQuery forUser(String username) {
76 CheckParameterUtil.ensureParameterNotNull(username, "username");
77 this.userName = username;
78 this.uid = 0;
79 return this;
80 }
81
82 /**
83 * Replies true if this query is restricted to user whom we only know the user name
84 * for.
85 *
86 * @return true if this query is restricted to user whom we only know the user name
87 * for
88 */
89 public boolean isRestrictedToPartiallyIdentifiedUser() {
90 return userName != null;
91 }
92
93 /**
94 * Replies the user name which this query is restricted to. null, if this query isn't
95 * restricted to a user name, i.e. if {@see #isRestrictedToPartiallyIdentifiedUser()} is false.
96 *
97 * @return the user name which this query is restricted to
98 */
99 public String getUserName() {
100 return userName;
101 }
102
103 /**
104 * Replies true if this query is restricted to user whom know the user id for.
105 *
106 * @return true if this query is restricted to user whom know the user id for
107 */
108 public boolean isRestrictedToFullyIdentifiedUser() {
109 return uid > 0;
110 }
111
112 /**
113 * Replies a query which is restricted to a bounding box.
114 *
115 * @param minLon min longitude of the bounding box. Valid longitude value expected.
116 * @param minLat min latitude of the bounding box. Valid latitude value expected.
117 * @param maxLon max longitude of the bounding box. Valid longitude value expected.
118 * @param maxLat max latitude of the bounding box. Valid latitude value expected.
119 *
120 * @return the restricted changeset query
121 * @throws IllegalArgumentException thrown if either of the parameters isn't a valid longitude or
122 * latitude value
123 */
124 public ChangesetQuery inBbox(double minLon, double minLat, double maxLon, double maxLat) throws IllegalArgumentException{
125 if (!LatLon.isValidLon(minLon))
126 throw new IllegalArgumentException(tr("Illegal longitude value for parameter ''{0}'', got {1}", "minLon", minLon));
127 if (!LatLon.isValidLon(maxLon))
128 throw new IllegalArgumentException(tr("Illegal longitude value for parameter ''{0}'', got {1}", "maxLon", maxLon));
129 if (!LatLon.isValidLat(minLat))
130 throw new IllegalArgumentException(tr("Illegal latitude value for parameter ''{0}'', got {1}", "minLat", minLat));
131 if (!LatLon.isValidLat(maxLat))
132 throw new IllegalArgumentException(tr("Illegal longitude value for parameter ''{0}'', got {1}", "maxLat", maxLat));
133
134 return inBbox(new LatLon(minLon, minLat), new LatLon(maxLon, maxLat));
135 }
136
137 /**
138 * Replies a query which is restricted to a bounding box.
139 *
140 * @param min the min lat/lon coordinates of the bounding box. Must not be null.
141 * @param max the max lat/lon coordiantes of the bounding box. Must not be null.
142 *
143 * @return the restricted changeset query
144 * @throws IllegalArgumentException thrown if min is null
145 * @throws IllegalArgumentException thrown if max is null
146 */
147 public ChangesetQuery inBbox(LatLon min, LatLon max) {
148 CheckParameterUtil.ensureParameterNotNull(min, "min");
149 CheckParameterUtil.ensureParameterNotNull(max, "max");
150 this.bounds = new Bounds(min,max);
151 return this;
152 }
153
154 /**
155 * Replies a query which is restricted to a bounding box given by <code>bbox</code>.
156 *
157 * @param bbox the bounding box. Must not be null.
158 * @return the changeset query
159 * @throws IllegalArgumentException thrown if bbox is null.
160 */
161 public ChangesetQuery inBbox(Bounds bbox) throws IllegalArgumentException {
162 CheckParameterUtil.ensureParameterNotNull(bbox, "bbox");
163 this.bounds = bbox;
164 return this;
165 }
166
167 /**
168 * Restricts the result to changesets which have been closed after the date given by <code>d</code>.
169 * <code>d</code> d is a date relative to the current time zone.
170 *
171 * @param d the date . Must not be null.
172 * @return the restricted changeset query
173 * @throws IllegalArgumentException thrown if d is null
174 */
175 public ChangesetQuery closedAfter(Date d) throws IllegalArgumentException{
176 CheckParameterUtil.ensureParameterNotNull(d, "d");
177 this.closedAfter = d;
178 return this;
179 }
180
181 /**
182 * Restricts the result to changesets which have been closed after <code>closedAfter</code> and which
183 * habe been created before <code>createdBefore</code>. Both dates are expressed relative to the current
184 * time zone.
185 *
186 * @param closedAfter only reply changesets closed after this date. Must not be null.
187 * @param createdBefore only reply changesets created before this date. Must not be null.
188 * @return the restricted changeset query
189 * @throws IllegalArgumentException thrown if closedAfter is null
190 * @throws IllegalArgumentException thrown if createdBefore is null
191 */
192 public ChangesetQuery closedAfterAndCreatedBefore(Date closedAfter, Date createdBefore ) throws IllegalArgumentException{
193 CheckParameterUtil.ensureParameterNotNull(closedAfter, "closedAfter");
194 CheckParameterUtil.ensureParameterNotNull(createdBefore, "createdBefore");
195 this.closedAfter = closedAfter;
196 this.createdBefore = createdBefore;
197 return this;
198 }
199
200 /**
201 * Restricts the result to changesets which are or aren't open, depending on the value of
202 * <code>isOpen</code>
203 *
204 * @param isOpen whether changesets should or should not be open
205 * @return the restricted changeset query
206 */
207 public ChangesetQuery beingOpen(boolean isOpen) {
208 this.open = isOpen;
209 return this;
210 }
211
212 /**
213 * Restricts the result to changesets which are or aren't closed, depending on the value of
214 * <code>isClosed</code>
215 *
216 * @param isClosed whether changesets should or should not be open
217 * @return the restricted changeset query
218 */
219 public ChangesetQuery beingClosed(boolean isClosed) {
220 this.closed = isClosed;
221 return this;
222 }
223
224 /**
225 * Replies the query string to be used in a query URL for the OSM API.
226 *
227 * @return the query string
228 */
229 public String getQueryString() {
230 StringBuffer sb = new StringBuffer();
231 if (uid != null) {
232 sb.append("user").append("=").append(uid);
233 } else if (userName != null) {
234 sb.append("display_name").append("=").append(userName);
235 }
236 if (bounds != null) {
237 if (sb.length() > 0) {
238 sb.append("&");
239 }
240 sb.append("bbox=").append(bounds.encodeAsString(","));
241 }
242 if (closedAfter != null && createdBefore != null) {
243 if (sb.length() > 0) {
244 sb.append("&");
245 }
246 SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssz");
247 sb.append("time").append("=").append(df.format(closedAfter));
248 sb.append(",").append(df.format(createdBefore));
249 } else if (closedAfter != null) {
250 if (sb.length() > 0) {
251 sb.append("&");
252 }
253 SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssz");
254 sb.append("time").append("=").append(df.format(closedAfter));
255 }
256
257 if (open != null) {
258 if (sb.length() > 0) {
259 sb.append("&");
260 }
261 sb.append("open=").append(Boolean.toString(open));
262 } else if (closed != null) {
263 if (sb.length() > 0) {
264 sb.append("&");
265 }
266 sb.append("closed=").append(Boolean.toString(closed));
267 }
268 return sb.toString();
269 }
270
271
272 public static class ChangesetQueryUrlException extends Exception {
273
274 public ChangesetQueryUrlException() {
275 super();
276 }
277
278 public ChangesetQueryUrlException(String arg0, Throwable arg1) {
279 super(arg0, arg1);
280 }
281
282 public ChangesetQueryUrlException(String arg0) {
283 super(arg0);
284 }
285
286 public ChangesetQueryUrlException(Throwable arg0) {
287 super(arg0);
288 }
289 }
290
291 public static class ChangesetQueryUrlParser {
292 protected int parseUid(String value) throws ChangesetQueryUrlException {
293 if (value == null || value.trim().equals(""))
294 throw new ChangesetQueryUrlException(tr("Unexpected value for ''{0}'' in changeset query url, got {1}", "uid",value));
295 int id;
296 try {
297 id = Integer.parseInt(value);
298 if (id <= 0)
299 throw new ChangesetQueryUrlException(tr("Unexpected value for ''{0}'' in changeset query url, got {1}", "uid",value));
300 } catch(NumberFormatException e) {
301 throw new ChangesetQueryUrlException(tr("Unexpected value for ''{0}'' in changeset query url, got {1}", "uid",value));
302 }
303 return id;
304 }
305
306 protected boolean parseOpen(String value) throws ChangesetQueryUrlException {
307 if (value == null || value.trim().equals(""))
308 throw new ChangesetQueryUrlException(tr("Unexpected value for ''{0}'' in changeset query url, got {1}", "open",value));
309 if (value.equals("true"))
310 return true;
311 else if (value.equals("false"))
312 return false;
313 else
314 throw new ChangesetQueryUrlException(tr("Unexpected value for ''{0}'' in changeset query url, got {1}", "open",value));
315 }
316
317 protected boolean parseBoolean(String value, String parameter) throws ChangesetQueryUrlException {
318 if (value == null || value.trim().equals(""))
319 throw new ChangesetQueryUrlException(tr("Unexpected value for ''{0}'' in changeset query url, got {1}", parameter,value));
320 if (value.equals("true"))
321 return true;
322 else if (value.equals("false"))
323 return false;
324 else
325 throw new ChangesetQueryUrlException(tr("Unexpected value for ''{0}'' in changeset query url, got {1}", parameter,value));
326 }
327
328 protected Date parseDate(String value, String parameter) throws ChangesetQueryUrlException {
329 if (value == null || value.trim().equals(""))
330 throw new ChangesetQueryUrlException(tr("Unexpected value for ''{0}'' in changeset query url, got {1}", parameter,value));
331 if (value.endsWith("Z")) {
332 // OSM API generates date strings we time zone abbreviation "Z" which Java SimpleDateFormat
333 // doesn't understand. Convert into GMT time zone before parsing.
334 //
335 value = value.substring(0,value.length() - 1) + "GMT+00:00";
336 }
337 DateFormat formatter = new SimpleDateFormat("MM/dd/yy");
338 formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssz");
339 try {
340 return formatter.parse(value);
341 } catch(ParseException e) {
342 throw new ChangesetQueryUrlException(tr("Unexpected value for ''{0}'' in changeset query url, got {1}", parameter,value));
343 }
344 }
345
346 protected Date[] parseTime(String value) throws ChangesetQueryUrlException {
347 String[] dates = value.split(",");
348 if (dates == null || dates.length == 0 || dates.length > 2)
349 throw new ChangesetQueryUrlException(tr("Unexpected value for ''{0}'' in changeset query url, got {1}", "time", value));
350 if (dates.length == 1)
351 return new Date[]{parseDate(dates[0], "time")};
352 else if (dates.length == 2)
353 return new Date[]{parseDate(dates[0], "time"),parseDate(dates[1], "time")};
354 return null;
355 }
356
357 protected ChangesetQuery crateFromMap(Map<String,String> queryParams) throws ChangesetQueryUrlException {
358 ChangesetQuery csQuery = new ChangesetQuery();
359
360 for (String k: queryParams.keySet()) {
361 if (k.equals("uid")) {
362 if (queryParams.containsKey("display_name"))
363 throw new ChangesetQueryUrlException(tr("Can''t create a changeset query including both the query parameters ''uid'' and ''display_name''"));
364 csQuery.forUser(parseUid(queryParams.get("uid")));
365 } else if (k.equals("display_name")) {
366 if (queryParams.containsKey("uid"))
367 throw new ChangesetQueryUrlException(tr("Can''t create a changeset query including both the query parameters ''uid'' and ''display_name''"));
368 csQuery.forUser(queryParams.get("display_name"));
369 } else if (k.equals("open")) {
370 boolean b = parseBoolean(queryParams.get(k), "open");
371 csQuery.beingOpen(b);
372 } else if (k.equals("closed")) {
373 boolean b = parseBoolean(queryParams.get(k), "closed");
374 csQuery.beingClosed(b);
375 } else if (k.equals("time")) {
376 Date[] dates = parseTime(queryParams.get(k));
377 switch(dates.length) {
378 case 1:
379 csQuery.closedAfter(dates[0]);
380 break;
381 case 2:
382 csQuery.closedAfterAndCreatedBefore(dates[0], dates[1]);
383 break;
384 }
385 } else if (k.equals("bbox")) {
386 try {
387 csQuery.inBbox(new Bounds(queryParams.get(k), ","));
388 } catch(IllegalArgumentException e) {
389 throw new ChangesetQueryUrlException(e);
390 }
391 } else
392 throw new ChangesetQueryUrlException(tr("Unsupported parameter ''{0}'' in changeset query string",k ));
393 }
394 return csQuery;
395 }
396
397 protected Map<String,String> createMapFromQueryString(String query) {
398 Map<String,String> queryParams = new HashMap<String, String>();
399 String[] keyValuePairs = query.split("&");
400 for (String keyValuePair: keyValuePairs) {
401 String[] kv = keyValuePair.split("=");
402 queryParams.put(kv[0], kv[1]);
403 }
404 return queryParams;
405 }
406
407 /**
408 * Parses the changeset query given as URL query parameters and replies a
409 * {@see ChangesetQuery}
410 *
411 * <code>query</code> is the query part of a API url for querying changesets,
412 * see <a href="http://wiki.openstreetmap.org/wiki/API_v0.6#Query:_GET_.2Fapi.2F0.6.2Fchangesets">OSM API</a>.
413 *
414 * Example for an query string:<br>
415 * <pre>
416 * uid=1234&open=true
417 * </pre>
418 *
419 * @param query the query string. If null, an empty query (identical to a query for all changesets) is
420 * assumed
421 * @return the changeset query
422 * @throws ChangesetQueryUrlException if the query string doesn't represent a legal query for changesets
423 */
424 public ChangesetQuery parse(String query) throws ChangesetQueryUrlException{
425 if (query == null)
426 return new ChangesetQuery();
427 query = query.trim();
428 if (query.equals(""))
429 return new ChangesetQuery();
430 Map<String,String> queryParams = createMapFromQueryString(query);
431 return crateFromMap(queryParams);
432 }
433 }
434}
Note: See TracBrowser for help on using the repository browser.