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    }