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