001package ezvcard.property;
002
003import java.util.LinkedHashMap;
004import java.util.List;
005import java.util.Map;
006
007import ezvcard.VCard;
008import ezvcard.VCardVersion;
009import ezvcard.ValidationWarning;
010import ezvcard.parameter.Pid;
011import ezvcard.util.GeoUri;
012
013/*
014 Copyright (c) 2012-2023, Michael Angstadt
015 All rights reserved.
016
017 Redistribution and use in source and binary forms, with or without
018 modification, are permitted provided that the following conditions are met: 
019
020 1. Redistributions of source code must retain the above copyright notice, this
021 list of conditions and the following disclaimer. 
022 2. Redistributions in binary form must reproduce the above copyright notice,
023 this list of conditions and the following disclaimer in the documentation
024 and/or other materials provided with the distribution. 
025
026 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
027 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
028 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
029 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
030 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
031 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
032 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
033 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
034 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
035 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
036
037 The views and conclusions contained in the software and documentation are those
038 of the authors and should not be interpreted as representing official policies, 
039 either expressed or implied, of the FreeBSD Project.
040 */
041
042/**
043 * <p>
044 * Defines a set of latitude/longitude coordinates. There is no rule as to what
045 * these coordinates represent, but their meaning could vary depending on the
046 * value of the vCard's {@link Kind} property:
047 * </p>
048 * 
049 * <table class="simpleTable">
050 * <caption>{@link Geo} meanings by {@link Kind} value</caption>
051 * <tr>
052 * <th>{@link Kind} value</th>
053 * <th>{@link Geo} meaning</th>
054 * </tr>
055 * <tr>
056 * <td>"individual"</td>
057 * <td>The location of the person's home or workplace.</td>
058 * </tr>
059 * <tr>
060 * <td>"group"</td>
061 * <td>The location of the group's meeting place.</td>
062 * </tr>
063 * <tr>
064 * <td>"org"</td>
065 * <td>The coordinates of the organization's headquarters.</td>
066 * </tr>
067 * <tr>
068 * <td>"location"</td>
069 * <td>The coordinates of the location itself.</td>
070 * </tr>
071 * </table>
072 * 
073 * <p>
074 * <b>Code sample</b>
075 * </p>
076 * 
077 * <pre class="brush:java">
078 * VCard vcard = new VCard();
079 * 
080 * Geo geo = new Geo(40.7127, -74.0059);
081 * vcard.setGeo(geo);
082 * </pre>
083 * 
084 * <p>
085 * <b>Property name:</b> {@code GEO}
086 * </p>
087 * <p>
088 * <b>Supported versions:</b> {@code 2.1, 3.0, 4.0}
089 * </p>
090 * @author Michael Angstadt
091 * @see <a href="http://tools.ietf.org/html/rfc6350#page-38">RFC 6350 p.38</a>
092 * @see <a href="http://tools.ietf.org/html/rfc2426#page-16">RFC 2426 p.16</a>
093 * @see <a href="http://www.imc.org/pdi/vcard-21.doc">vCard 2.1 p.16</a>
094 */
095public class Geo extends VCardProperty implements HasAltId {
096        private GeoUri uri;
097
098        /**
099         * Creates a geo property.
100         * @param latitude the latitude
101         * @param longitude the longitude
102         */
103        public Geo(Double latitude, Double longitude) {
104                this(new GeoUri.Builder(latitude, longitude).build());
105        }
106
107        /**
108         * Creates a geo property.
109         * @param uri the geo URI
110         */
111        public Geo(GeoUri uri) {
112                this.uri = uri;
113        }
114
115        /**
116         * Copy constructor.
117         * @param original the property to make a copy of
118         */
119        public Geo(Geo original) {
120                super(original);
121                uri = original.uri;
122        }
123
124        /**
125         * Gets the latitude.
126         * @return the latitude
127         */
128        public Double getLatitude() {
129                return (uri == null) ? null : uri.getCoordA();
130        }
131
132        /**
133         * Sets the latitude.
134         * @param latitude the latitude
135         */
136        public void setLatitude(Double latitude) {
137                if (uri == null) {
138                        uri = new GeoUri.Builder(latitude, null).build();
139                } else {
140                        uri = new GeoUri.Builder(uri).coordA(latitude).build();
141                }
142        }
143
144        /**
145         * Gets the longitude.
146         * @return the longitude
147         */
148        public Double getLongitude() {
149                return (uri == null) ? null : uri.getCoordB();
150        }
151
152        /**
153         * Sets the longitude.
154         * @param longitude the longitude
155         */
156        public void setLongitude(Double longitude) {
157                if (uri == null) {
158                        uri = new GeoUri.Builder(null, longitude).build();
159                } else {
160                        uri = new GeoUri.Builder(uri).coordB(longitude).build();
161                }
162        }
163
164        /**
165         * Gets the raw object used for storing the GEO information. This can be
166         * used to supplement the GEO value with additional information (such as
167         * altitude or level of accuracy). Geo URIs are only supported by vCard
168         * version 4.0. Only latitude and longitude values are used when marshalling
169         * to earlier vCard versions.
170         * @return the geo URI object or null if not set
171         * @see <a href="http://tools.ietf.org/html/rfc5870">RFC 5870</a>
172         */
173        public GeoUri getGeoUri() {
174                return uri;
175        }
176
177        /**
178         * Sets the raw object used for storing the GEO information. This can be
179         * used to supplement the GEO value with additional information (such as
180         * altitude or level of accuracy). Geo URIs are only supported by vCard
181         * version 4.0. Only latitude and longitude values are used when marshalling
182         * to earlier vCard versions.
183         * @param uri the geo URI object
184         * @see <a href="http://tools.ietf.org/html/rfc5870">RFC 5870</a>
185         */
186        public void setGeoUri(GeoUri uri) {
187                this.uri = uri;
188        }
189
190        /**
191         * Gets the TYPE parameter.
192         * <p>
193         * <b>Supported versions:</b> {@code 4.0}
194         * </p>
195         * @return the TYPE value (typically, this will be either "work" or "home")
196         * or null if it doesn't exist
197         */
198        public String getType() {
199                return parameters.getType();
200        }
201
202        /**
203         * Sets the TYPE parameter.
204         * <p>
205         * <b>Supported versions:</b> {@code 4.0}
206         * </p>
207         * @param type the TYPE value (this should be either "work" or "home") or
208         * null to remove
209         */
210        public void setType(String type) {
211                parameters.setType(type);
212        }
213
214        /**
215         * Gets the MEDIATYPE parameter.
216         * <p>
217         * <b>Supported versions:</b> {@code 4.0}
218         * </p>
219         * @return the media type or null if not set
220         */
221        public String getMediaType() {
222                return parameters.getMediaType();
223        }
224
225        /**
226         * Sets the MEDIATYPE parameter.
227         * <p>
228         * <b>Supported versions:</b> {@code 4.0}
229         * </p>
230         * @param mediaType the media type or null to remove
231         */
232        public void setMediaType(String mediaType) {
233                parameters.setMediaType(mediaType);
234        }
235
236        @Override
237        public List<Pid> getPids() {
238                return super.getPids();
239        }
240
241        @Override
242        public Integer getPref() {
243                return super.getPref();
244        }
245
246        @Override
247        public void setPref(Integer pref) {
248                super.setPref(pref);
249        }
250
251        //@Override
252        public String getAltId() {
253                return parameters.getAltId();
254        }
255
256        //@Override
257        public void setAltId(String altId) {
258                parameters.setAltId(altId);
259        }
260
261        @Override
262        protected void _validate(List<ValidationWarning> warnings, VCardVersion version, VCard vcard) {
263                if (getLatitude() == null) {
264                        warnings.add(new ValidationWarning(13));
265                }
266                if (getLongitude() == null) {
267                        warnings.add(new ValidationWarning(14));
268                }
269        }
270
271        @Override
272        protected Map<String, Object> toStringValues() {
273                Map<String, Object> values = new LinkedHashMap<>();
274                values.put("uri", uri);
275                return values;
276        }
277
278        @Override
279        public Geo copy() {
280                return new Geo(this);
281        }
282
283        @Override
284        public int hashCode() {
285                final int prime = 31;
286                int result = super.hashCode();
287                result = prime * result + ((uri == null) ? 0 : uri.hashCode());
288                return result;
289        }
290
291        @Override
292        public boolean equals(Object obj) {
293                if (this == obj) return true;
294                if (!super.equals(obj)) return false;
295                Geo other = (Geo) obj;
296                if (uri == null) {
297                        if (other.uri != null) return false;
298                } else if (!uri.equals(other.uri)) return false;
299                return true;
300        }
301}