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}