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}