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 }