001/** 002 * 003 * Copyright 2015-2017 Ishan Khanna, Fernando Ramirez, 2019-2020 Florian Schmaus 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.jivesoftware.smackx.geoloc.packet; 018 019import java.io.Serializable; 020import java.net.URI; 021import java.util.Date; 022 023import javax.xml.namespace.QName; 024 025import org.jivesoftware.smack.packet.ExtensionElement; 026import org.jivesoftware.smack.packet.Message; 027import org.jivesoftware.smack.util.EqualsUtil; 028import org.jivesoftware.smack.util.HashCode; 029import org.jivesoftware.smack.util.XmlStringBuilder; 030 031import org.jivesoftware.smackx.xdata.FormField; 032import org.jivesoftware.smackx.xdata.FormFieldChildElement; 033 034/** 035 * A GeoLocation Extension packet, which is used by the XMPP clients to exchange their respective geographic locations. 036 * 037 * @see <a href="http://www.xmpp.org/extensions/xep-0080.html">XEP-0080</a> 038 * @author Ishan Khanna 039 */ 040public final class GeoLocation implements Serializable, ExtensionElement, FormFieldChildElement { 041 042 private static final long serialVersionUID = 1L; 043 044 public static final String NAMESPACE = "http://jabber.org/protocol/geoloc"; 045 046 public static final String ELEMENT = "geoloc"; 047 048 public static final QName QNAME = new QName(NAMESPACE, ELEMENT); 049 050 public static final GeoLocation EMPTY_GEO_LOCATION = GeoLocation.builder().build(); 051 052 private final Double accuracy; 053 private final Double alt; 054 private final Double altAccuracy; 055 private final String area; 056 private final Double bearing; 057 private final String building; 058 private final String country; 059 private final String countryCode; 060 private final String datum; 061 private final String description; 062 private final Double error; 063 private final String floor; 064 private final Double lat; 065 private final String locality; 066 private final Double lon; 067 private final String postalcode; 068 private final String region; 069 private final String room; 070 private final Double speed; 071 private final String street; 072 private final String text; 073 private final Date timestamp; 074 private final String tzo; 075 private final URI uri; 076 077 private GeoLocation(Builder builder) { 078 accuracy = builder.accuracy; 079 alt = builder.alt; 080 altAccuracy = builder.altAccuracy; 081 area = builder.area; 082 bearing = builder.bearing; 083 building = builder.building; 084 country = builder.country; 085 countryCode = builder.countryCode; 086 datum = builder.datum; 087 description = builder.description; 088 error = builder.error; 089 floor = builder.floor; 090 lat = builder.lat; 091 locality = builder.locality; 092 lon = builder.lon; 093 postalcode = builder.postalcode; 094 region = builder.region; 095 room = builder.room; 096 speed = builder.speed; 097 street = builder.street; 098 text = builder.text; 099 timestamp = builder.timestamp; 100 tzo = builder.tzo; 101 uri = builder.uri; 102 } 103 104 public Double getAccuracy() { 105 return accuracy; 106 } 107 108 public Double getAlt() { 109 return alt; 110 } 111 112 public Double getAltAccuracy() { 113 return altAccuracy; 114 } 115 116 public String getArea() { 117 return area; 118 } 119 120 public Double getBearing() { 121 return bearing; 122 } 123 124 public String getBuilding() { 125 return building; 126 } 127 128 public String getCountry() { 129 return country; 130 } 131 132 public String getCountryCode() { 133 return countryCode; 134 } 135 136 public String getDatum() { 137 return datum; 138 } 139 140 public String getDescription() { 141 return description; 142 } 143 144 /** 145 * Get the error. 146 * 147 * @return the error. 148 * @deprecated use {@link #getAccuracy()} instead. 149 */ 150 @Deprecated 151 public Double getError() { 152 return error; 153 } 154 155 public String getFloor() { 156 return floor; 157 } 158 159 public Double getLat() { 160 return lat; 161 } 162 163 public String getLocality() { 164 return locality; 165 } 166 167 public Double getLon() { 168 return lon; 169 } 170 171 public String getPostalcode() { 172 return postalcode; 173 } 174 175 public String getRegion() { 176 return region; 177 } 178 179 public String getRoom() { 180 return room; 181 } 182 183 public Double getSpeed() { 184 return speed; 185 } 186 187 public String getStreet() { 188 return street; 189 } 190 191 public String getText() { 192 return text; 193 } 194 195 public Date getTimestamp() { 196 return timestamp; 197 } 198 199 public String getTzo() { 200 return tzo; 201 } 202 203 public URI getUri() { 204 return uri; 205 } 206 207 @Override 208 public QName getQName() { 209 return QNAME; 210 } 211 212 @Override 213 public String getElementName() { 214 return ELEMENT; 215 } 216 217 @Override 218 public CharSequence toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) { 219 XmlStringBuilder xml = new XmlStringBuilder(this); 220 xml.rightAngleBracket(); 221 xml.optElement("accuracy", accuracy); 222 xml.optElement("alt", alt); 223 xml.optElement("altaccuracy", altAccuracy); 224 xml.optElement("area", area); 225 xml.optElement("bearing", bearing); 226 xml.optElement("building", building); 227 xml.optElement("country", country); 228 xml.optElement("countrycode", countryCode); 229 xml.optElement("datum", datum); 230 xml.optElement("description", description); 231 xml.optElement("error", error); 232 xml.optElement("floor", floor); 233 xml.optElement("lat", lat); 234 xml.optElement("locality", locality); 235 xml.optElement("lon", lon); 236 xml.optElement("postalcode", postalcode); 237 xml.optElement("region", region); 238 xml.optElement("room", room); 239 xml.optElement("speed", speed); 240 xml.optElement("street", street); 241 xml.optElement("text", text); 242 xml.optElement("timestamp", timestamp); 243 xml.optElement("tzo", tzo); 244 xml.optElement("uri", uri); 245 xml.closeElement(this); 246 return xml; 247 } 248 249 @Override 250 public String getNamespace() { 251 return NAMESPACE; 252 } 253 254 private final HashCode.Cache hashCodeCache = new HashCode.Cache(); 255 256 @Override 257 public int hashCode() { 258 return hashCodeCache.getHashCode(c -> 259 c 260 .append(accuracy) 261 .append(alt) 262 .append(altAccuracy) 263 .append(area) 264 .append(bearing) 265 .append(building) 266 .append(country) 267 .append(countryCode) 268 .append(datum) 269 .append(description) 270 .append(error) 271 .append(floor) 272 .append(lat) 273 .append(locality) 274 .append(lon) 275 .append(postalcode) 276 .append(region) 277 .append(room) 278 .append(speed) 279 .append(street) 280 .append(text) 281 .append(timestamp) 282 .append(tzo) 283 .append(uri) 284 ); 285 } 286 287 @Override 288 public boolean equals(Object obj) { 289 return EqualsUtil.equals(this, obj, (e, o) -> { 290 e 291 .append(accuracy, o.accuracy) 292 .append(altAccuracy, o.altAccuracy) 293 .append(area, o.area) 294 .append(bearing, o.bearing) 295 .append(building, o.building) 296 .append(country, o.country) 297 .append(countryCode, o.countryCode) 298 .append(datum, o.datum) 299 .append(description, o.description) 300 .append(error, o.error) 301 .append(floor, o.floor) 302 .append(lat, o.lat) 303 .append(locality, o.locality) 304 .append(lon, o.lon) 305 .append(postalcode, o.postalcode) 306 .append(region, o.region) 307 .append(room, o.room) 308 .append(speed, o.speed) 309 .append(street, o.street) 310 .append(text, o.text) 311 .append(timestamp, o.timestamp) 312 .append(tzo, o.tzo) 313 .append(uri, o.uri) 314 ; 315 }); 316 } 317 318 /** 319 * Returns a new instance of {@link Builder}. 320 * @return Builder 321 */ 322 public static Builder builder() { 323 return new GeoLocation.Builder(); 324 } 325 326 @Override 327 public boolean isExclusiveElement() { 328 return true; 329 } 330 331 /** 332 * Returns the first GeoLocation, or <code>null</code> if it doesn't exist in {@link Message}. 333 * <br> 334 * @param message The Message stanza containing GeoLocation 335 * @return GeoLocation 336 */ 337 public static GeoLocation from(Message message) { 338 return message.getExtension(GeoLocation.class); 339 } 340 341 /** 342 * Returns the first GeoLocation, or <code>null</code> if it doesn't exist in {@link FormField}. 343 * <br> 344 * @param formField the Formfield containing GeoLocation 345 * @return GeoLocation 346 */ 347 public static GeoLocation from(FormField formField) { 348 return (GeoLocation) formField.getFormFieldChildElement(QNAME); 349 } 350 351 /** 352 * This class defines a builder class for {@link GeoLocation}. 353 * <br> 354 * {@link GeoLocation} instance can be obtained using {@link #build()} method as follows.<br><br> 355 * <code>GeoLocation.Builder builder = GeoLocation.builder(); <br> 356 * GeoLocation geoLocation = builder.build();</code> 357 * <br><br> 358 * To set GeoLocation parameters, use their respective setters. 359 */ 360 public static final class Builder { 361 362 private Double accuracy; 363 private Double alt; 364 private Double altAccuracy; 365 private String area; 366 private Double bearing; 367 private String building; 368 private String country; 369 private String countryCode; 370 371 // If datum is not included, receiver MUST assume WGS84; receivers MUST implement WGS84; senders MAY use another 372 // datum, but it is not recommended. 373 private String datum = "WGS84"; 374 375 private String description; 376 private Double error; 377 private String floor; 378 private Double lat; 379 private String locality; 380 private Double lon; 381 private String postalcode; 382 private String region; 383 private String room; 384 private Double speed; 385 private String street; 386 private String text; 387 private Date timestamp; 388 private String tzo; 389 private URI uri; 390 391 /** 392 * Deprecated, do not use. 393 * @deprecated use {@link GeoLocation#builder()} instead. 394 */ 395 @Deprecated 396 // TODO Make constructor private in Smack 4.6. 397 public Builder() { 398 } 399 400 /** 401 * Sets accuracy of horizontal GPS error in meters. 402 * 403 * @param accuracy accuracy in meters 404 * @return Builder 405 */ 406 public Builder setAccuracy(Double accuracy) { 407 this.accuracy = accuracy; 408 return this; 409 } 410 411 /** 412 * Sets Altitude in meters above or below sea level. 413 * 414 * @param alt altitude in meters 415 * @return Builder 416 */ 417 public Builder setAlt(Double alt) { 418 this.alt = alt; 419 return this; 420 } 421 422 /** 423 * Sets Vertical GPS error in meters. 424 * 425 * @param altAccuracy altAccuracy in meters 426 * @return Builder 427 */ 428 public Builder setAltAccuracy(Double altAccuracy) { 429 this.altAccuracy = altAccuracy; 430 return this; 431 } 432 433 /** 434 * Sets a named area such as a campus or neighborhood. 435 * 436 * @param area the named area 437 * @return Builder 438 */ 439 public Builder setArea(String area) { 440 this.area = area; 441 return this; 442 } 443 444 /** 445 * Sets GPS bearing (direction in which the entity is heading<br> 446 * to reach its next waypoint), measured in decimal degrees,<br> 447 * relative to true north. 448 * 449 * @param bearing bearing in decimal degrees 450 * @return Builder 451 */ 452 public Builder setBearing(Double bearing) { 453 this.bearing = bearing; 454 return this; 455 } 456 457 /** 458 * Sets a specific building on a street or in an area. 459 * 460 * @param building name of the building 461 * @return Builder 462 */ 463 public Builder setBuilding(String building) { 464 this.building = building; 465 return this; 466 } 467 468 /** 469 * Sets the nation where the user is located. 470 * 471 * @param country user's country of location 472 * @return Builder 473 */ 474 public Builder setCountry(String country) { 475 this.country = country; 476 return this; 477 } 478 479 /** 480 * Sets The ISO 3166 two-letter country code. 481 * 482 * @param countryCode two-letter country code 483 * @return Builder 484 */ 485 public Builder setCountryCode(String countryCode) { 486 this.countryCode = countryCode; 487 return this; 488 } 489 490 /** 491 * Sets GPS Datum. 492 * 493 * @param datum GPS datum 494 * @return Builder 495 */ 496 public Builder setDatum(String datum) { 497 this.datum = datum; 498 return this; 499 } 500 501 /** 502 * Sets A natural-language name for or description of the location. 503 * 504 * @param description description of the location 505 * @return Builder 506 */ 507 public Builder setDescription(String description) { 508 this.description = description; 509 return this; 510 } 511 512 /** 513 * Sets Horizontal GPS error in arc minutes;<br> 514 * this element is deprecated in favor of accuracy. 515 * 516 * @param error error in arc minutes 517 * @return Builder 518 * @deprecated use {@link #setAccuracy(Double)} instead. 519 */ 520 @Deprecated 521 public Builder setError(Double error) { 522 this.error = error; 523 return this; 524 } 525 526 /** 527 * Sets a particular floor in a building. 528 * 529 * @param floor floor in a building 530 * @return Builder 531 */ 532 public Builder setFloor(String floor) { 533 this.floor = floor; 534 return this; 535 } 536 537 /** 538 * Sets Latitude in decimal degrees North. 539 * 540 * @param lat latitude in decimal degrees 541 * @return Builder 542 */ 543 public Builder setLat(Double lat) { 544 this.lat = lat; 545 return this; 546 } 547 548 /** 549 * Sets Locality within the administrative region,<br> 550 * such as a town or city. 551 * 552 * @param locality locality in a region 553 * @return Builder 554 */ 555 public Builder setLocality(String locality) { 556 this.locality = locality; 557 return this; 558 } 559 560 /** 561 * Sets Longitude in decimal degrees East. 562 * 563 * @param lon longitude in decimal degrees 564 * @return Builder 565 */ 566 public Builder setLon(Double lon) { 567 this.lon = lon; 568 return this; 569 } 570 571 /** 572 * Sets PostalCode used for postal delivery. 573 * 574 * @param postalcode code for postal delivery 575 * @return Builder 576 */ 577 public Builder setPostalcode(String postalcode) { 578 this.postalcode = postalcode; 579 return this; 580 } 581 582 /** 583 * Sets an administrative region of the nation,<br> 584 * such as a state or province. 585 * 586 * @param region an administrative region 587 * @return Builder 588 */ 589 public Builder setRegion(String region) { 590 this.region = region; 591 return this; 592 } 593 594 /** 595 * Sets a particular room in a building. 596 * 597 * @param room room inside a building 598 * @return Builder 599 */ 600 public Builder setRoom(String room) { 601 this.room = room; 602 return this; 603 } 604 605 /** 606 * Sets Speed at which the entity is moving, in meters per second. 607 * 608 * @param speed speed in meters per second 609 * @return Builder 610 */ 611 public Builder setSpeed(Double speed) { 612 this.speed = speed; 613 return this; 614 } 615 616 /** 617 * Sets a thoroughfare within the locality, or a crossing of two thoroughfares. 618 * 619 * @param street name of the street 620 * @return Builder 621 */ 622 public Builder setStreet(String street) { 623 this.street = street; 624 return this; 625 } 626 627 /** 628 * Sets a catch-all element that captures any other information about the location. 629 * 630 * @param text distinctive feature about the location 631 * @return Builder 632 */ 633 public Builder setText(String text) { 634 this.text = text; 635 return this; 636 } 637 638 /** 639 * Sets UTC timestamp specifying the moment when the reading was taken. 640 * 641 * @param timestamp timestamp of the reading 642 * @return Builder 643 */ 644 public Builder setTimestamp(Date timestamp) { 645 this.timestamp = timestamp; 646 return this; 647 } 648 649 /** 650 * Sets the time zone offset from UTC for the current location. 651 * 652 * @param tzo time zone offset 653 * @return Builder 654 */ 655 public Builder setTzo(String tzo) { 656 this.tzo = tzo; 657 return this; 658 } 659 660 /** 661 * Sets URI or URL pointing to information about the location. 662 * 663 * @param uri uri to the location 664 * @return Builder 665 */ 666 public Builder setUri(URI uri) { 667 this.uri = uri; 668 return this; 669 } 670 671 /** 672 * This method is called to build {@link GeoLocation} from the Builder. 673 * 674 * @return GeoLocation 675 */ 676 public GeoLocation build() { 677 return new GeoLocation(this); 678 } 679 } 680}