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.Warning;
009    import biweekly.io.json.JCalValue;
010    import biweekly.io.xml.XCalElement;
011    import biweekly.parameter.ICalParameters;
012    import biweekly.property.FreeBusy;
013    import biweekly.util.Duration;
014    import biweekly.util.Period;
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 {@link FreeBusy} properties.
043     * @author Michael Angstadt
044     */
045    public class FreeBusyMarshaller extends ICalPropertyMarshaller<FreeBusy> {
046            public FreeBusyMarshaller() {
047                    super(FreeBusy.class, "FREEBUSY", ICalDataType.PERIOD);
048            }
049    
050            @Override
051            protected String _writeText(FreeBusy property) {
052                    List<Period> values = property.getValues();
053    
054                    return list(values, new ListCallback<Period>() {
055                            public String asString(Period period) {
056                                    StringBuilder sb = new StringBuilder();
057    
058                                    if (period.getStartDate() != null) {
059                                            String date = date(period.getStartDate()).write();
060                                            sb.append(date);
061                                    }
062    
063                                    sb.append('/');
064    
065                                    if (period.getEndDate() != null) {
066                                            String date = date(period.getEndDate()).write();
067                                            sb.append(date);
068                                    } else if (period.getDuration() != null) {
069                                            sb.append(period.getDuration());
070                                    }
071    
072                                    return sb.toString();
073                            }
074                    });
075            }
076    
077            @Override
078            protected FreeBusy _parseText(String value, ICalDataType dataType, ICalParameters parameters, List<Warning> warnings) {
079                    return parse(list(value), parameters, warnings);
080            }
081    
082            @Override
083            protected void _writeXml(FreeBusy property, XCalElement element) {
084                    for (Period period : property.getValues()) {
085                            XCalElement periodElement = element.append(ICalDataType.PERIOD);
086    
087                            Date start = period.getStartDate();
088                            if (start != null) {
089                                    periodElement.append("start", date(start).extended(true).write());
090                            }
091    
092                            Date end = period.getEndDate();
093                            if (end != null) {
094                                    periodElement.append("end", date(end).extended(true).write());
095                            }
096    
097                            Duration duration = period.getDuration();
098                            if (duration != null) {
099                                    periodElement.append("duration", duration.toString());
100                            }
101                    }
102            }
103    
104            @Override
105            protected FreeBusy _parseXml(XCalElement element, ICalParameters parameters, List<Warning> warnings) {
106                    List<XCalElement> periodElements = element.children(ICalDataType.PERIOD);
107                    if (periodElements.isEmpty()) {
108                            throw missingXmlElements(ICalDataType.PERIOD);
109                    }
110    
111                    FreeBusy prop = new FreeBusy();
112                    for (XCalElement periodElement : periodElements) {
113                            String startStr = periodElement.first("start");
114                            if (startStr == null) {
115                                    warnings.add(Warning.parse(9));
116                                    continue;
117                            }
118    
119                            Date start = null;
120                            try {
121                                    start = date(startStr).tzid(parameters.getTimezoneId(), warnings).parse();
122                            } catch (IllegalArgumentException e) {
123                                    warnings.add(Warning.parse(10, startStr));
124                                    continue;
125                            }
126    
127                            String endStr = periodElement.first("end");
128                            if (endStr != null) {
129                                    try {
130                                            Date end = date(endStr).tzid(parameters.getTimezoneId(), warnings).parse();
131                                            prop.addValue(start, end);
132                                    } catch (IllegalArgumentException e) {
133                                            warnings.add(Warning.parse(11, endStr));
134                                    }
135                                    continue;
136                            }
137    
138                            String durationStr = periodElement.first("duration");
139                            if (durationStr != null) {
140                                    try {
141                                            Duration duration = Duration.parse(durationStr);
142                                            prop.addValue(start, duration);
143                                    } catch (IllegalArgumentException e) {
144                                            warnings.add(Warning.parse(12, durationStr));
145                                    }
146                                    continue;
147                            }
148    
149                            warnings.add(Warning.parse(13));
150                    }
151                    return prop;
152            }
153    
154            @Override
155            protected JCalValue _writeJson(FreeBusy property) {
156                    List<Period> values = property.getValues();
157                    if (values.isEmpty()) {
158                            return JCalValue.single("");
159                    }
160    
161                    List<String> valuesStr = new ArrayList<String>();
162                    for (Period period : values) {
163                            StringBuilder sb = new StringBuilder();
164                            if (period.getStartDate() != null) {
165                                    String date = date(period.getStartDate()).extended(true).write();
166                                    sb.append(date);
167                            }
168    
169                            sb.append('/');
170    
171                            if (period.getEndDate() != null) {
172                                    String date = date(period.getEndDate()).extended(true).write();
173                                    sb.append(date);
174                            } else if (period.getDuration() != null) {
175                                    sb.append(period.getDuration());
176                            }
177    
178                            valuesStr.add(sb.toString());
179                    }
180    
181                    return JCalValue.multi(valuesStr);
182            }
183    
184            @Override
185            protected FreeBusy _parseJson(JCalValue value, ICalDataType dataType, ICalParameters parameters, List<Warning> warnings) {
186                    return parse(value.asMulti(), parameters, warnings);
187            }
188    
189            private FreeBusy parse(List<String> periods, ICalParameters parameters, List<Warning> warnings) {
190                    FreeBusy freebusy = new FreeBusy();
191    
192                    for (String period : periods) {
193                            String periodSplit[] = period.split("/");
194    
195                            if (periodSplit.length < 2) {
196                                    warnings.add(Warning.parse(13));
197                                    continue;
198                            }
199    
200                            String startStr = periodSplit[0];
201                            Date start = null;
202                            try {
203                                    start = date(startStr).tzid(parameters.getTimezoneId(), warnings).parse();
204                            } catch (IllegalArgumentException e) {
205                                    warnings.add(Warning.parse(10, startStr));
206                                    continue;
207                            }
208    
209                            String endStr = periodSplit[1];
210                            try {
211                                    Date end = date(endStr).tzid(parameters.getTimezoneId(), warnings).parse();
212                                    freebusy.addValue(start, end);
213                            } catch (IllegalArgumentException e) {
214                                    //must be a duration
215                                    try {
216                                            Duration duration = Duration.parse(endStr);
217                                            freebusy.addValue(start, duration);
218                                    } catch (IllegalArgumentException e2) {
219                                            warnings.add(Warning.parse(14, endStr));
220                                            continue;
221                                    }
222                            }
223                    }
224    
225                    return freebusy;
226            }
227    }