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