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 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         * Sets accuracy of horizontal GPS error in meters.
393         *
394         * @param accuracy accuracy in meters
395         * @return Builder
396         */
397        public Builder setAccuracy(Double accuracy) {
398            this.accuracy = accuracy;
399            return this;
400        }
401
402        /**
403         * Sets Altitude in meters above or below sea level.
404         *
405         * @param alt altitude in meters
406         * @return Builder
407         */
408        public Builder setAlt(Double alt) {
409            this.alt = alt;
410            return this;
411        }
412
413        /**
414         * Sets Vertical GPS error in meters.
415         *
416         * @param altAccuracy altAccuracy in meters
417         * @return Builder
418         */
419        public Builder setAltAccuracy(Double altAccuracy) {
420            this.altAccuracy = altAccuracy;
421            return this;
422        }
423
424        /**
425         * Sets a named area such as a campus or neighborhood.
426         *
427         * @param area the named area
428         * @return Builder
429         */
430        public Builder setArea(String area) {
431            this.area = area;
432            return this;
433        }
434
435        /**
436         * Sets GPS bearing (direction in which the entity is heading<br>
437         * to reach its next waypoint), measured in decimal degrees,<br>
438         * relative to true north.
439         *
440         * @param bearing bearing in decimal degrees
441         * @return Builder
442         */
443        public Builder setBearing(Double bearing) {
444            this.bearing = bearing;
445            return this;
446        }
447
448        /**
449         * Sets a specific building on a street or in an area.
450         *
451         * @param building name of the building
452         * @return Builder
453         */
454        public Builder setBuilding(String building) {
455            this.building = building;
456            return this;
457        }
458
459        /**
460         * Sets the nation where the user is located.
461         *
462         * @param country user's country of location
463         * @return Builder
464         */
465        public Builder setCountry(String country) {
466            this.country = country;
467            return this;
468        }
469
470        /**
471         * Sets The ISO 3166 two-letter country code.
472         *
473         * @param countryCode two-letter country code
474         * @return Builder
475         */
476        public Builder setCountryCode(String countryCode) {
477            this.countryCode = countryCode;
478            return this;
479        }
480
481        /**
482         * Sets GPS Datum.
483         *
484         * @param datum GPS datum
485         * @return Builder
486         */
487        public Builder setDatum(String datum) {
488            this.datum = datum;
489            return this;
490        }
491
492        /**
493         * Sets A natural-language name for or description of the location.
494         *
495         * @param description description of the location
496         * @return Builder
497         */
498        public Builder setDescription(String description) {
499            this.description = description;
500            return this;
501        }
502
503        /**
504         * Sets Horizontal GPS error in arc minutes;<br>
505         * this element is deprecated in favor of accuracy.
506         *
507         * @param error error in arc minutes
508         * @return Builder
509         * @deprecated use {@link #setAccuracy(Double)} instead.
510         */
511        @Deprecated
512        public Builder setError(Double error) {
513            this.error = error;
514            return this;
515        }
516
517        /**
518         * Sets a particular floor in a building.
519         *
520         * @param floor floor in a building
521         * @return Builder
522         */
523        public Builder setFloor(String floor) {
524            this.floor = floor;
525            return this;
526        }
527
528        /**
529         * Sets Latitude in decimal degrees North.
530         *
531         * @param lat latitude in decimal degrees
532         * @return Builder
533         */
534        public Builder setLat(Double lat) {
535            this.lat = lat;
536            return this;
537        }
538
539        /**
540         * Sets Locality within the administrative region,<br>
541         * such as a town or city.
542         *
543         * @param locality locality in a region
544         * @return Builder
545         */
546        public Builder setLocality(String locality) {
547            this.locality = locality;
548            return this;
549        }
550
551        /**
552         * Sets Longitude in decimal degrees East.
553         *
554         * @param lon longitude in decimal degrees
555         * @return Builder
556         */
557        public Builder setLon(Double lon) {
558            this.lon = lon;
559            return this;
560        }
561
562        /**
563         * Sets PostalCode used for postal delivery.
564         *
565         * @param postalcode code for postal delivery
566         * @return Builder
567         */
568        public Builder setPostalcode(String postalcode) {
569            this.postalcode = postalcode;
570            return this;
571        }
572
573        /**
574         * Sets an administrative region of the nation,<br>
575         * such as a state or province.
576         *
577         * @param region an administrative region
578         * @return Builder
579         */
580        public Builder setRegion(String region) {
581            this.region = region;
582            return this;
583        }
584
585        /**
586         * Sets a particular room in a building.
587         *
588         * @param room room inside a building
589         * @return Builder
590         */
591        public Builder setRoom(String room) {
592            this.room = room;
593            return this;
594        }
595
596        /**
597         * Sets Speed at which the entity is moving, in meters per second.
598         *
599         * @param speed speed in meters per second
600         * @return Builder
601         */
602        public Builder setSpeed(Double speed) {
603            this.speed = speed;
604            return this;
605        }
606
607        /**
608         * Sets a thoroughfare within the locality, or a crossing of two thoroughfares.
609         *
610         * @param street name of the street
611         * @return Builder
612         */
613        public Builder setStreet(String street) {
614            this.street = street;
615            return this;
616        }
617
618        /**
619         * Sets a catch-all element that captures any other information about the location.
620         *
621         * @param text distinctive feature about the location
622         * @return Builder
623         */
624        public Builder setText(String text) {
625            this.text = text;
626            return this;
627        }
628
629        /**
630         * Sets UTC timestamp specifying the moment when the reading was taken.
631         *
632         * @param timestamp timestamp of the reading
633         * @return Builder
634         */
635        public Builder setTimestamp(Date timestamp) {
636            this.timestamp = timestamp;
637            return this;
638        }
639
640        /**
641         * Sets the time zone offset from UTC for the current location.
642         *
643         * @param tzo time zone offset
644         * @return Builder
645         */
646        public Builder setTzo(String tzo) {
647            this.tzo = tzo;
648            return this;
649        }
650
651        /**
652         * Sets URI or URL pointing to information about the location.
653         *
654         * @param uri uri to the location
655         * @return Builder
656         */
657        public Builder setUri(URI uri) {
658            this.uri = uri;
659            return this;
660        }
661
662        /**
663         * This method is called to build {@link GeoLocation} from the Builder.
664         *
665         * @return GeoLocation
666         */
667        public GeoLocation build() {
668            return new GeoLocation(this);
669        }
670    }
671}