001    package ezvcard.io.scribe;
002    
003    import java.util.List;
004    
005    import ezvcard.VCardDataType;
006    import ezvcard.VCardVersion;
007    import ezvcard.io.CannotParseException;
008    import ezvcard.io.html.HCardElement;
009    import ezvcard.io.json.JCardValue;
010    import ezvcard.io.xml.XCardElement;
011    import ezvcard.parameter.VCardParameters;
012    import ezvcard.property.Geo;
013    import ezvcard.util.GeoUri;
014    import ezvcard.util.VCardFloatFormatter;
015    
016    /*
017     Copyright (c) 2013, Michael Angstadt
018     All rights reserved.
019    
020     Redistribution and use in source and binary forms, with or without
021     modification, are permitted provided that the following conditions are met: 
022    
023     1. Redistributions of source code must retain the above copyright notice, this
024     list of conditions and the following disclaimer. 
025     2. Redistributions in binary form must reproduce the above copyright notice,
026     this list of conditions and the following disclaimer in the documentation
027     and/or other materials provided with the distribution. 
028    
029     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
030     ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
031     WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
032     DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
033     ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
034     (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
035     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
036     ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
037     (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
038     SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
039     */
040    
041    /**
042     * Marshals {@link Geo} properties.
043     * @author Michael Angstadt
044     */
045    public class GeoScribe extends VCardPropertyScribe<Geo> {
046            public GeoScribe() {
047                    super(Geo.class, "GEO");
048            }
049    
050            @Override
051            protected VCardDataType _defaultDataType(VCardVersion version) {
052                    switch (version) {
053                    case V2_1:
054                    case V3_0:
055                            return null;
056                    case V4_0:
057                            return VCardDataType.URI;
058                    }
059                    return null;
060            }
061    
062            @Override
063            protected String _writeText(Geo property, VCardVersion version) {
064                    return write(property, version);
065            }
066    
067            @Override
068            protected Geo _parseText(String value, VCardDataType dataType, VCardVersion version, VCardParameters parameters, List<String> warnings) {
069                    value = unescape(value);
070                    return parse(value, version, warnings);
071            }
072    
073            @Override
074            protected void _writeXml(Geo property, XCardElement parent) {
075                    parent.append(VCardDataType.URI, write(property, parent.version()));
076            }
077    
078            @Override
079            protected Geo _parseXml(XCardElement element, VCardParameters parameters, List<String> warnings) {
080                    String value = element.first(VCardDataType.URI);
081                    if (value != null) {
082                            return parse(value, element.version(), warnings);
083                    }
084    
085                    throw missingXmlElements(VCardDataType.URI);
086            }
087    
088            @Override
089            protected Geo _parseHtml(HCardElement element, List<String> warnings) {
090                    String latitudeStr = element.firstValue("latitude");
091                    if (latitudeStr == null) {
092                            throw new CannotParseException(7);
093                    }
094    
095                    Double latitude;
096                    try {
097                            latitude = Double.parseDouble(latitudeStr);
098                    } catch (NumberFormatException e) {
099                            throw new CannotParseException(8, latitudeStr);
100                    }
101    
102                    String longitudeStr = element.firstValue("longitude");
103                    if (longitudeStr == null) {
104                            throw new CannotParseException(9);
105                    }
106    
107                    Double longitude;
108                    try {
109                            longitude = Double.parseDouble(longitudeStr);
110                    } catch (NumberFormatException e) {
111                            throw new CannotParseException(10, longitudeStr);
112                    }
113    
114                    return new Geo(latitude, longitude);
115            }
116    
117            @Override
118            protected JCardValue _writeJson(Geo property) {
119                    return JCardValue.single(write(property, VCardVersion.V4_0));
120            }
121    
122            @Override
123            protected Geo _parseJson(JCardValue value, VCardDataType dataType, VCardParameters parameters, List<String> warnings) {
124                    return parse(value.asSingle(), VCardVersion.V4_0, warnings);
125            }
126    
127            private Geo parse(String value, VCardVersion version, List<String> warnings) {
128                    if (value == null || value.length() == 0) {
129                            return new Geo(null);
130                    }
131    
132                    switch (version) {
133                    case V2_1:
134                    case V3_0:
135                            SemiStructuredIterator it = semistructured(value);
136                            String latitudeStr = it.next();
137                            String longitudeStr = it.next();
138                            if (latitudeStr == null || longitudeStr == null) {
139                                    throw new CannotParseException(11);
140                            }
141    
142                            Double latitude;
143                            try {
144                                    latitude = Double.valueOf(latitudeStr);
145                            } catch (NumberFormatException e) {
146                                    throw new CannotParseException(8, latitudeStr);
147                            }
148    
149                            Double longitude;
150                            try {
151                                    longitude = Double.valueOf(longitudeStr);
152                            } catch (NumberFormatException e) {
153                                    throw new CannotParseException(10, longitudeStr);
154                            }
155    
156                            return new Geo(latitude, longitude);
157                    case V4_0:
158                            try {
159                                    return new Geo(GeoUri.parse(value));
160                            } catch (IllegalArgumentException e) {
161                                    throw new CannotParseException(12);
162                            }
163                    }
164                    return null;
165            }
166    
167            private String write(Geo property, VCardVersion version) {
168                    if (property.getGeoUri() == null) {
169                            return "";
170                    }
171    
172                    switch (version) {
173                    case V2_1:
174                    case V3_0:
175                            VCardFloatFormatter formatter = new VCardFloatFormatter(6);
176                            return structured(formatter.format(property.getLatitude()), formatter.format(property.getLongitude()));
177                    case V4_0:
178                            return property.getGeoUri().toString(6);
179                    }
180                    return null;
181            }
182    }