001    package biweekly.property.marshaller;
002    
003    import java.util.ArrayList;
004    import java.util.Date;
005    import java.util.List;
006    
007    import biweekly.ICalDataType;
008    import biweekly.io.json.JCalValue;
009    import biweekly.io.xml.XCalElement;
010    import biweekly.parameter.ICalParameters;
011    import biweekly.property.FreeBusy;
012    import biweekly.util.Duration;
013    import biweekly.util.Period;
014    
015    /*
016     Copyright (c) 2013, Michael Angstadt
017     All rights reserved.
018    
019     Redistribution and use in source and binary forms, with or without
020     modification, are permitted provided that the following conditions are met: 
021    
022     1. Redistributions of source code must retain the above copyright notice, this
023     list of conditions and the following disclaimer. 
024     2. Redistributions in binary form must reproduce the above copyright notice,
025     this list of conditions and the following disclaimer in the documentation
026     and/or other materials provided with the distribution. 
027    
028     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
029     ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
030     WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
031     DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
032     ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
033     (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
034     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
035     ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
036     (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
037     SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
038     */
039    
040    /**
041     * Marshals {@link FreeBusy} properties.
042     * @author Michael Angstadt
043     */
044    public class FreeBusyMarshaller extends ICalPropertyMarshaller<FreeBusy> {
045            public FreeBusyMarshaller() {
046                    super(FreeBusy.class, "FREEBUSY", ICalDataType.PERIOD);
047            }
048    
049            @Override
050            protected String _writeText(FreeBusy property) {
051                    List<Period> values = property.getValues();
052    
053                    return list(values, new ListCallback<Period>() {
054                            public String asString(Period period) {
055                                    StringBuilder sb = new StringBuilder();
056    
057                                    if (period.getStartDate() != null) {
058                                            String date = date(period.getStartDate()).write();
059                                            sb.append(date);
060                                    }
061    
062                                    sb.append('/');
063    
064                                    if (period.getEndDate() != null) {
065                                            String date = date(period.getEndDate()).write();
066                                            sb.append(date);
067                                    } else if (period.getDuration() != null) {
068                                            sb.append(period.getDuration());
069                                    }
070    
071                                    return sb.toString();
072                            }
073                    });
074            }
075    
076            @Override
077            protected FreeBusy _parseText(String value, ICalDataType dataType, ICalParameters parameters, List<String> warnings) {
078                    return parse(list(value), parameters, warnings);
079            }
080    
081            @Override
082            protected void _writeXml(FreeBusy property, XCalElement element) {
083                    for (Period period : property.getValues()) {
084                            XCalElement periodElement = element.append(ICalDataType.PERIOD);
085    
086                            Date start = period.getStartDate();
087                            if (start != null) {
088                                    periodElement.append("start", date(start).extended(true).write());
089                            }
090    
091                            Date end = period.getEndDate();
092                            if (end != null) {
093                                    periodElement.append("end", date(end).extended(true).write());
094                            }
095    
096                            Duration duration = period.getDuration();
097                            if (duration != null) {
098                                    periodElement.append("duration", duration.toString());
099                            }
100                    }
101            }
102    
103            @Override
104            protected FreeBusy _parseXml(XCalElement element, ICalParameters parameters, List<String> warnings) {
105                    List<XCalElement> periodElements = element.children(ICalDataType.PERIOD);
106                    if (periodElements.isEmpty()) {
107                            throw missingXmlElements(ICalDataType.PERIOD);
108                    }
109    
110                    FreeBusy prop = new FreeBusy();
111                    for (XCalElement periodElement : periodElements) {
112                            String startStr = periodElement.first("start");
113                            if (startStr == null) {
114                                    warnings.add("No start date found in time period, skipping.");
115                                    continue;
116                            }
117    
118                            Date start = null;
119                            try {
120                                    start = date(startStr).tzid(parameters.getTimezoneId(), warnings).parse();
121                            } catch (IllegalArgumentException e) {
122                                    warnings.add("Could not parse start date, skipping time period: " + startStr);
123                                    continue;
124                            }
125    
126                            String endStr = periodElement.first("end");
127                            if (endStr != null) {
128                                    try {
129                                            Date end = date(endStr).tzid(parameters.getTimezoneId(), warnings).parse();
130                                            prop.addValue(start, end);
131                                    } catch (IllegalArgumentException e) {
132                                            warnings.add("Could not parse end date, skipping time period: " + endStr);
133                                    }
134                                    continue;
135                            }
136    
137                            String durationStr = periodElement.first("duration");
138                            if (durationStr != null) {
139                                    try {
140                                            Duration duration = Duration.parse(durationStr);
141                                            prop.addValue(start, duration);
142                                    } catch (IllegalArgumentException e) {
143                                            warnings.add("Could not parse duration, skipping time period: " + durationStr);
144                                    }
145                                    continue;
146                            }
147    
148                            warnings.add("Start date has no accompanying end date or duration, skipping.");
149                    }
150                    return prop;
151            }
152    
153            @Override
154            protected JCalValue _writeJson(FreeBusy property) {
155                    List<Period> values = property.getValues();
156                    if (values.isEmpty()) {
157                            return JCalValue.single("");
158                    }
159    
160                    List<String> valuesStr = new ArrayList<String>();
161                    for (Period period : values) {
162                            StringBuilder sb = new StringBuilder();
163                            if (period.getStartDate() != null) {
164                                    String date = date(period.getStartDate()).extended(true).write();
165                                    sb.append(date);
166                            }
167    
168                            sb.append('/');
169    
170                            if (period.getEndDate() != null) {
171                                    String date = date(period.getEndDate()).extended(true).write();
172                                    sb.append(date);
173                            } else if (period.getDuration() != null) {
174                                    sb.append(period.getDuration());
175                            }
176    
177                            valuesStr.add(sb.toString());
178                    }
179    
180                    return JCalValue.multi(valuesStr);
181            }
182    
183            @Override
184            protected FreeBusy _parseJson(JCalValue value, ICalDataType dataType, ICalParameters parameters, List<String> warnings) {
185                    return parse(value.asMulti(), parameters, warnings);
186            }
187    
188            private FreeBusy parse(List<String> periods, ICalParameters parameters, List<String> warnings) {
189                    FreeBusy freebusy = new FreeBusy();
190    
191                    for (String period : periods) {
192                            String periodSplit[] = period.split("/");
193    
194                            if (periodSplit.length < 2) {
195                                    warnings.add("No end date or duration found, skipping time period: " + period);
196                                    continue;
197                            }
198    
199                            String startStr = periodSplit[0];
200                            Date start = null;
201                            try {
202                                    start = date(startStr).tzid(parameters.getTimezoneId(), warnings).parse();
203                            } catch (IllegalArgumentException e) {
204                                    warnings.add("Could not parse start date, skipping time period: " + period);
205                                    continue;
206                            }
207    
208                            String endStr = periodSplit[1];
209                            try {
210                                    Date end = date(endStr).tzid(parameters.getTimezoneId(), warnings).parse();
211                                    freebusy.addValue(start, end);
212                            } catch (IllegalArgumentException e) {
213                                    //must be a duration
214                                    try {
215                                            Duration duration = Duration.parse(endStr);
216                                            freebusy.addValue(start, duration);
217                                    } catch (IllegalArgumentException e2) {
218                                            warnings.add("Could not parse end date or duration value, skipping time period: " + period);
219                                            continue;
220                                    }
221                            }
222                    }
223    
224                    return freebusy;
225            }
226    }