001    package biweekly.property.marshaller;
002    
003    import java.util.Date;
004    import java.util.List;
005    
006    import biweekly.ICalDataType;
007    import biweekly.Warning;
008    import biweekly.io.CannotParseException;
009    import biweekly.io.json.JCalValue;
010    import biweekly.io.xml.XCalElement;
011    import biweekly.parameter.ICalParameters;
012    import biweekly.property.DateOrDateTimeProperty;
013    import biweekly.util.DateTimeComponents;
014    import biweekly.util.ICalDateFormatter;
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     */
046    public abstract class DateOrDateTimePropertyMarshaller<T extends DateOrDateTimeProperty> extends ICalPropertyMarshaller<T> {
047            public DateOrDateTimePropertyMarshaller(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 = ICalDateFormatter.dateHasTime(value);
150                    boolean localTz = !ICalDateFormatter.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    }