001 package ezvcard.types;
002
003 import java.util.Date;
004 import java.util.List;
005
006 import ezvcard.VCard;
007 import ezvcard.VCardSubTypes;
008 import ezvcard.VCardVersion;
009 import ezvcard.io.CompatibilityMode;
010 import ezvcard.io.SkipMeException;
011 import ezvcard.parameters.CalscaleParameter;
012 import ezvcard.parameters.ValueParameter;
013 import ezvcard.util.HCardElement;
014 import ezvcard.util.ISOFormat;
015 import ezvcard.util.VCardDateFormatter;
016 import ezvcard.util.VCardStringUtils;
017 import ezvcard.util.XCardElement;
018
019 /*
020 Copyright (c) 2012, Michael Angstadt
021 All rights reserved.
022
023 Redistribution and use in source and binary forms, with or without
024 modification, are permitted provided that the following conditions are met:
025
026 1. Redistributions of source code must retain the above copyright notice, this
027 list of conditions and the following disclaimer.
028 2. Redistributions in binary form must reproduce the above copyright notice,
029 this list of conditions and the following disclaimer in the documentation
030 and/or other materials provided with the distribution.
031
032 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
033 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
034 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
035 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
036 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
037 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
038 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
039 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
040 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
041 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
042
043 The views and conclusions contained in the software and documentation are those
044 of the authors and should not be interpreted as representing official policies,
045 either expressed or implied, of the FreeBSD Project.
046 */
047
048 /**
049 * Represents a type that contains a date and/or time (for example, the BDAY
050 * type).
051 * @author Michael Angstadt
052 */
053 public class DateOrTimeType extends VCardType {
054 private String text;
055 private Date date;
056 private String reducedAccuracyDate;
057
058 /**
059 * True if the "date" or "reduceAccuracyDate" fields have a time component,
060 * false if they just contain a date.
061 */
062 private boolean dateHasTime;
063
064 /**
065 * @param typeName the name of the type (e.g. "BDAY")
066 */
067 public DateOrTimeType(String typeName) {
068 super(typeName);
069 }
070
071 /**
072 * @param typeName the name of the type (e.g. "BDAY")
073 * @param date the date value
074 */
075 public DateOrTimeType(String typeName, Date date) {
076 super(typeName);
077 setDate(date, false);
078 }
079
080 /**
081 * Gets the date value.
082 * @return the date value or null if not set
083 */
084 public Date getDate() {
085 return date;
086 }
087
088 /**
089 * Sets the value of this type to a complete date.
090 * @param date the date
091 * @param dateHasTime true if the date contains a time component, false if
092 * it's just a date
093 */
094 public void setDate(Date date, boolean dateHasTime) {
095 this.date = date;
096 this.dateHasTime = dateHasTime;
097 text = null;
098 reducedAccuracyDate = null;
099 }
100
101 /**
102 * Gets the reduced accuracy date string. This is only supported by vCard
103 * 4.0.
104 * @return the reduced accuracy date string or null if not set
105 * @see "<a href="
106 * http://tools.ietf.org/html/rfc6350">RFC 6350</a> p.12-14 for examples"
107 */
108 public String getReducedAccuracyDate() {
109 return reducedAccuracyDate;
110 }
111
112 /**
113 * Sets the value of this type to a "reduced accuracy" date. This is only
114 * supported by vCard 4.0.
115 * @param reducedAccuracyDate the reduced accuracy date (e.g "--0210" for
116 * "February 10")
117 * @see "<a href="
118 * http://tools.ietf.org/html/rfc6350">RFC 6350</a> p.12-14 for examples"
119 */
120 public void setReducedAccuracyDate(String reducedAccuracyDate) {
121 this.reducedAccuracyDate = reducedAccuracyDate;
122 dateHasTime = reducedAccuracyDate.contains("T");
123 text = null;
124 date = null;
125 }
126
127 /**
128 * Gets the text value of this type. This is only supported by vCard 4.0.
129 * @return the text value or null if not set
130 */
131 public String getText() {
132 return text;
133 }
134
135 /**
136 * Sets the value of this type to a text string. This is only supported by
137 * vCard 4.0.
138 * @param text the text value
139 */
140 public void setText(String text) {
141 this.text = text;
142 date = null;
143 reducedAccuracyDate = null;
144 }
145
146 /**
147 * Gets the type of calendar this date uses.
148 * <p>
149 * vCard versions: 4.0
150 * </p>
151 * @return the type of calendar or null if not set
152 */
153 public CalscaleParameter getCalscale() {
154 return subTypes.getCalscale();
155 }
156
157 /**
158 * Sets the type of calendar this date uses.
159 * <p>
160 * vCard versions: 4.0
161 * </p>
162 * @param calscale the type of calendar or null to remove
163 */
164 public void setCalsclae(CalscaleParameter calscale) {
165 subTypes.setCalscale(calscale);
166 }
167
168 /**
169 * Gets the ALTID.
170 * <p>
171 * vCard versions: 4.0
172 * </p>
173 * @return the ALTID or null if it doesn't exist
174 * @see VCardSubTypes#getAltId
175 */
176 public String getAltId() {
177 return subTypes.getAltId();
178 }
179
180 /**
181 * Sets the ALTID.
182 * <p>
183 * vCard versions: 4.0
184 * </p>
185 * @param altId the ALTID or null to remove
186 * @see VCardSubTypes#setAltId
187 */
188 public void setAltId(String altId) {
189 subTypes.setAltId(altId);
190 }
191
192 @Override
193 protected void doMarshalSubTypes(VCardSubTypes copy, VCardVersion version, List<String> warnings, CompatibilityMode compatibilityMode, VCard vcard) {
194 if (version == VCardVersion.V4_0) {
195 if (date != null || reducedAccuracyDate != null) {
196 copy.setValue(ValueParameter.DATE_AND_OR_TIME);
197 if (getCalscale() == null) {
198 copy.setCalscale(CalscaleParameter.GREGORIAN);
199 }
200 } else if (text != null) {
201 copy.setValue(ValueParameter.TEXT);
202 }
203 } else {
204 if (dateHasTime) {
205 copy.setValue(ValueParameter.DATE_TIME);
206 } else {
207 copy.setValue(ValueParameter.DATE);
208 }
209 }
210 }
211
212 @Override
213 protected void doMarshalText(StringBuilder sb, VCardVersion version, List<String> warnings, CompatibilityMode compatibilityMode) {
214 if (version == VCardVersion.V2_1 || version == VCardVersion.V3_0) {
215 if (text != null) {
216 throw new SkipMeException("Text values are not allowed in vCard version " + version + ".");
217 } else if (reducedAccuracyDate != null) {
218 throw new SkipMeException("Reduced accuracy dates are not allowed in vCard version " + version + ".");
219 } else if (date != null) {
220 ISOFormat format = dateHasTime ? ISOFormat.TIME_BASIC : ISOFormat.DATE_BASIC;
221 sb.append(VCardDateFormatter.format(date, format));
222 } else {
223 throw new SkipMeException("Property has no date value associated with it.");
224 }
225 } else {
226 if (text != null) {
227 sb.append(VCardStringUtils.escape(text));
228 } else if (reducedAccuracyDate != null) {
229 sb.append(reducedAccuracyDate);
230 } else if (date != null) {
231 ISOFormat format = dateHasTime ? ISOFormat.TIME_BASIC : ISOFormat.DATE_BASIC;
232 sb.append(VCardDateFormatter.format(date, format));
233 } else {
234 throw new SkipMeException("Property has no date, reduced accuracy date, or text value associated with it.");
235 }
236 }
237 }
238
239 @Override
240 protected void doUnmarshalText(String value, VCardVersion version, List<String> warnings, CompatibilityMode compatibilityMode) {
241 if (version == VCardVersion.V4_0) {
242 if (subTypes.getValue() == ValueParameter.TEXT) {
243 text = VCardStringUtils.unescape(value);
244 } else if (value.contains("-")) {
245 reducedAccuracyDate = value;
246 } else {
247 try {
248 date = VCardDateFormatter.parse(value);
249 } catch (IllegalArgumentException e) {
250 //not all reduced accuracy dates have dashes (e.g. "2012")
251 if (value.matches("\\d+")) {
252 reducedAccuracyDate = value;
253 } else {
254 warnings.add("Date string \"" + value + "\" could not be parsed. Assuming it's a text value.");
255 text = VCardStringUtils.unescape(value);
256 }
257 }
258 }
259 } else {
260 try {
261 date = VCardDateFormatter.parse(value);
262 } catch (IllegalArgumentException e) {
263 warnings.add("Date string \"" + value + "\" for type \"" + typeName + "\" could not be parsed.");
264 }
265 }
266 }
267
268 @Override
269 protected void doMarshalXml(XCardElement parent, List<String> warnings, CompatibilityMode compatibilityMode) {
270 if (text != null) {
271 parent.text(text);
272 } else if (reducedAccuracyDate != null) {
273 parent.dateAndOrTime(reducedAccuracyDate);
274 } else if (date != null) {
275 ISOFormat format = dateHasTime ? ISOFormat.TIME_BASIC : ISOFormat.DATE_BASIC;
276 String value = VCardDateFormatter.format(date, format);
277 parent.dateAndOrTime(value);
278 } else {
279 throw new SkipMeException("Property has no date, reduced accuracy date, or text value associated with it.");
280 }
281 }
282
283 @Override
284 protected void doUnmarshalXml(XCardElement element, List<String> warnings, CompatibilityMode compatibilityMode) {
285 String value = element.dateAndOrTime();
286 if (value != null) {
287 if (value.contains("-")) {
288 setReducedAccuracyDate(value);
289 } else {
290 try {
291 boolean hasTime = value.contains("T");
292 setDate(VCardDateFormatter.parse(value), hasTime);
293 } catch (IllegalArgumentException e) {
294 //not all reduced accuracy dates have dashes (e.g. "2012")
295 if (value.matches("\\d+")) {
296 setReducedAccuracyDate(value);
297 } else {
298 warnings.add("Date string \"" + value + "\" could not be parsed. Assuming it's a text value.");
299 setText(value);
300 }
301 }
302 }
303 } else {
304 setText(element.text());
305 }
306 }
307
308 @Override
309 protected void doUnmarshalHtml(HCardElement element, List<String> warnings) {
310 String value = null;
311 if ("time".equals(element.tagName())) {
312 String datetime = element.attr("datetime");
313 if (datetime.length() > 0) {
314 value = datetime;
315 }
316 }
317 if (value == null) {
318 value = element.value();
319 }
320 doUnmarshalText(value, VCardVersion.V3_0, warnings, CompatibilityMode.RFC);
321 }
322 }