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