001package ezvcard.io.scribe; 002 003import java.time.temporal.Temporal; 004 005import com.github.mangstadt.vinnie.io.VObjectPropertyValues; 006 007import ezvcard.VCardDataType; 008import ezvcard.VCardVersion; 009import ezvcard.io.CannotParseException; 010import ezvcard.io.ParseContext; 011import ezvcard.io.html.HCardElement; 012import ezvcard.io.json.JCardValue; 013import ezvcard.io.text.WriteContext; 014import ezvcard.io.xml.XCardElement; 015import ezvcard.parameter.VCardParameters; 016import ezvcard.property.DateOrTimeProperty; 017import ezvcard.util.PartialDate; 018import ezvcard.util.VCardDateFormat; 019 020/* 021 Copyright (c) 2012-2023, Michael Angstadt 022 All rights reserved. 023 024 Redistribution and use in source and binary forms, with or without 025 modification, are permitted provided that the following conditions are met: 026 027 1. Redistributions of source code must retain the above copyright notice, this 028 list of conditions and the following disclaimer. 029 2. Redistributions in binary form must reproduce the above copyright notice, 030 this list of conditions and the following disclaimer in the documentation 031 and/or other materials provided with the distribution. 032 033 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 034 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 035 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 036 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 037 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 038 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 039 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 040 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 041 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 042 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 043 */ 044 045/** 046 * Marshals properties with date-time values. 047 * @author Michael Angstadt 048 * @param <T> the property class 049 */ 050public abstract class DateOrTimePropertyScribe<T extends DateOrTimeProperty> extends VCardPropertyScribe<T> { 051 public DateOrTimePropertyScribe(Class<T> clazz, String propertyName) { 052 super(clazz, propertyName); 053 } 054 055 @Override 056 protected VCardDataType _defaultDataType(VCardVersion version) { 057 switch (version) { 058 case V2_1: 059 case V3_0: 060 return null; 061 case V4_0: 062 return VCardDataType.DATE_AND_OR_TIME; 063 } 064 return null; 065 } 066 067 @Override 068 protected VCardDataType _dataType(T property, VCardVersion version) { 069 switch (version) { 070 case V2_1: 071 case V3_0: 072 return null; 073 case V4_0: 074 if (property.getText() != null) { 075 return VCardDataType.TEXT; 076 } 077 if (property.getDate() != null) { 078 return VCardDateFormat.hasTime(property.getDate()) ? VCardDataType.DATE_TIME : VCardDataType.DATE; 079 } 080 if (property.getPartialDate() != null) { 081 return property.getPartialDate().hasTimeComponent() ? VCardDataType.DATE_TIME : VCardDataType.DATE; 082 } 083 return VCardDataType.DATE_AND_OR_TIME; 084 } 085 return null; 086 } 087 088 @Override 089 protected String _writeText(T property, WriteContext context) { 090 VCardVersion version = context.getVersion(); 091 Temporal date = property.getDate(); 092 if (date != null) { 093 boolean extended = (version == VCardVersion.V3_0); 094 return date(date).extended(extended).write(); 095 } 096 097 if (version == VCardVersion.V4_0) { 098 String text = property.getText(); 099 if (text != null) { 100 return VObjectPropertyValues.escape(text); 101 } 102 103 PartialDate partialDate = property.getPartialDate(); 104 if (partialDate != null) { 105 return partialDate.toISO8601(false); 106 } 107 } 108 109 return ""; 110 } 111 112 @Override 113 protected T _parseText(String value, VCardDataType dataType, VCardParameters parameters, ParseContext context) { 114 value = VObjectPropertyValues.unescape(value); 115 if (context.getVersion() == VCardVersion.V4_0 && dataType == VCardDataType.TEXT) { 116 return newInstance(value); 117 } 118 119 return parse(value, context); 120 } 121 122 @Override 123 protected void _writeXml(T property, XCardElement parent) { 124 Temporal date = property.getDate(); 125 if (date != null) { 126 String value = date(date).extended(false).write(); 127 VCardDataType dataType = VCardDateFormat.hasTime(date) ? VCardDataType.DATE_TIME : VCardDataType.DATE; 128 parent.append(dataType, value); 129 return; 130 } 131 132 PartialDate partialDate = property.getPartialDate(); 133 if (partialDate != null) { 134 VCardDataType dataType; 135 if (partialDate.hasTimeComponent() && partialDate.hasDateComponent()) { 136 dataType = VCardDataType.DATE_TIME; 137 } else if (partialDate.hasTimeComponent()) { 138 dataType = VCardDataType.TIME; 139 } else if (partialDate.hasDateComponent()) { 140 dataType = VCardDataType.DATE; 141 } else { 142 dataType = VCardDataType.DATE_AND_OR_TIME; 143 } 144 145 parent.append(dataType, partialDate.toISO8601(false)); 146 return; 147 } 148 149 String text = property.getText(); 150 if (text != null) { 151 parent.append(VCardDataType.TEXT, text); 152 return; 153 } 154 155 parent.append(VCardDataType.DATE_AND_OR_TIME, ""); 156 } 157 158 @Override 159 protected T _parseXml(XCardElement element, VCardParameters parameters, ParseContext context) { 160 String value = element.first(VCardDataType.DATE, VCardDataType.DATE_TIME, VCardDataType.DATE_AND_OR_TIME); 161 if (value != null) { 162 return parse(value, context); 163 } 164 165 value = element.first(VCardDataType.TEXT); 166 if (value != null) { 167 return newInstance(value); 168 } 169 170 throw missingXmlElements(VCardDataType.DATE, VCardDataType.DATE_TIME, VCardDataType.DATE_AND_OR_TIME, VCardDataType.TEXT); 171 } 172 173 @Override 174 protected T _parseHtml(HCardElement element, ParseContext context) { 175 String value = null; 176 if ("time".equals(element.tagName())) { 177 String datetime = element.attr("datetime"); 178 if (datetime.length() > 0) { 179 value = datetime; 180 } 181 } 182 if (value == null) { 183 value = element.value(); 184 } 185 return parse(value, context); 186 } 187 188 @Override 189 protected JCardValue _writeJson(T property) { 190 Temporal date = property.getDate(); 191 if (date != null) { 192 String value = date(date).extended(true).write(); 193 return JCardValue.single(value); 194 } 195 196 PartialDate partialDate = property.getPartialDate(); 197 if (partialDate != null) { 198 String value = partialDate.toISO8601(true); 199 return JCardValue.single(value); 200 } 201 202 String text = property.getText(); 203 if (text != null) { 204 return JCardValue.single(text); 205 } 206 207 return JCardValue.single(""); 208 } 209 210 @Override 211 protected T _parseJson(JCardValue value, VCardDataType dataType, VCardParameters parameters, ParseContext context) { 212 String valueStr = value.asSingle(); 213 if (dataType == VCardDataType.TEXT) { 214 return newInstance(valueStr); 215 } 216 217 return parse(valueStr, context); 218 } 219 220 private T parse(String value, ParseContext context) { 221 try { 222 return newInstance(date(value)); 223 } catch (IllegalArgumentException e) { 224 if (context.getVersion() == VCardVersion.V2_1 || context.getVersion() == VCardVersion.V3_0) { 225 throw new CannotParseException(5); 226 } 227 228 try { 229 return newInstance(PartialDate.parse(value)); 230 } catch (IllegalArgumentException e2) { 231 context.addWarning(6); 232 return newInstance(value); 233 } 234 } 235 } 236 237 protected abstract T newInstance(String text); 238 239 protected abstract T newInstance(Temporal date); 240 241 protected abstract T newInstance(PartialDate partialDate); 242}