001package biweekly.io.scribe.property; 002 003import java.util.Date; 004import java.util.List; 005 006import biweekly.ICalDataType; 007import biweekly.Warning; 008import biweekly.io.CannotParseException; 009import biweekly.io.json.JCalValue; 010import biweekly.io.xml.XCalElement; 011import biweekly.parameter.ICalParameters; 012import biweekly.property.DateOrDateTimeProperty; 013import biweekly.util.DateTimeComponents; 014import biweekly.util.ICalDateFormat; 015 016/* 017 Copyright (c) 2013, Michael Angstadt 018 All rights reserved. 019 020 Redistribution and use in source and binary forms, with or without 021 modification, are permitted provided that the following conditions are met: 022 023 1. Redistributions of source code must retain the above copyright notice, this 024 list of conditions and the following disclaimer. 025 2. Redistributions in binary form must reproduce the above copyright notice, 026 this list of conditions and the following disclaimer in the documentation 027 and/or other materials provided with the distribution. 028 029 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 030 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 031 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 032 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 033 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 034 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 035 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 036 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 037 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 038 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 039 */ 040 041/** 042 * Marshals properties that have either "date" or "date-time" values. 043 * @param <T> the property class 044 * @author Michael Angstadt 045 */ 046public abstract class DateOrDateTimePropertyScribe<T extends DateOrDateTimeProperty> extends ICalPropertyScribe<T> { 047 public DateOrDateTimePropertyScribe(Class<T> clazz, String propertyName) { 048 super(clazz, propertyName, ICalDataType.DATE_TIME); 049 } 050 051 @Override 052 protected ICalDataType _dataType(T property) { 053 return (property.getRawComponents() != null || property.getValue() == null || property.hasTime()) ? ICalDataType.DATE_TIME : ICalDataType.DATE; 054 } 055 056 @Override 057 protected String _writeText(T property) { 058 DateTimeComponents components = property.getRawComponents(); 059 if (components != null) { 060 return components.toString(false); 061 } 062 063 Date value = property.getValue(); 064 if (value != null) { 065 return date(value).time(property.hasTime()).tz(property.isLocalTime(), property.getTimezoneId()).write(); 066 } 067 068 return ""; 069 } 070 071 @Override 072 protected T _parseText(String value, ICalDataType dataType, ICalParameters parameters, List<Warning> warnings) { 073 value = unescape(value); 074 return parse(value, parameters, warnings); 075 } 076 077 @Override 078 protected void _writeXml(T property, XCalElement element) { 079 String dateStr = null; 080 081 Date value = property.getValue(); 082 DateTimeComponents components = property.getRawComponents(); 083 if (components != null) { 084 dateStr = components.toString(true); 085 } else if (value != null) { 086 dateStr = date(value).time(property.hasTime()).tz(property.isLocalTime(), property.getTimezoneId()).extended(true).write(); 087 } 088 089 element.append(dataType(property), dateStr); 090 } 091 092 @Override 093 protected T _parseXml(XCalElement element, ICalParameters parameters, List<Warning> warnings) { 094 String value = element.first(ICalDataType.DATE_TIME); 095 if (value == null) { 096 value = element.first(ICalDataType.DATE); 097 } 098 099 if (value != null) { 100 return parse(value, parameters, warnings); 101 } 102 103 throw missingXmlElements(ICalDataType.DATE_TIME, ICalDataType.DATE); 104 } 105 106 @Override 107 protected JCalValue _writeJson(T property) { 108 DateTimeComponents components = property.getRawComponents(); 109 if (components != null) { 110 return JCalValue.single(components.toString(true)); 111 } 112 113 Date value = property.getValue(); 114 if (value != null) { 115 return JCalValue.single(date(value).time(property.hasTime()).tz(property.isLocalTime(), property.getTimezoneId()).extended(true).write()); 116 } 117 118 return JCalValue.single(""); 119 } 120 121 @Override 122 protected T _parseJson(JCalValue value, ICalDataType dataType, ICalParameters parameters, List<Warning> warnings) { 123 String valueStr = value.asSingle(); 124 return parse(valueStr, parameters, warnings); 125 } 126 127 protected abstract T newInstance(Date date, boolean hasTime); 128 129 private T parse(String value, ICalParameters parameters, List<Warning> warnings) { 130 if (value == null) { 131 return newInstance(null, true); 132 } 133 134 Date date; 135 try { 136 date = date(value).tzid(parameters.getTimezoneId(), warnings).parse(); 137 } catch (IllegalArgumentException e) { 138 throw new CannotParseException(17); 139 } 140 141 DateTimeComponents components; 142 try { 143 components = DateTimeComponents.parse(value); 144 } catch (IllegalArgumentException e) { 145 warnings.add(Warning.parse(6, value)); 146 components = null; 147 } 148 149 boolean hasTime = ICalDateFormat.dateHasTime(value); 150 boolean localTz = !ICalDateFormat.dateHasTimezone(value) && parameters.getTimezoneId() == null; 151 152 T prop = newInstance(date, hasTime); 153 prop.setRawComponents(components); 154 prop.setLocalTime(localTz); 155 return prop; 156 } 157}