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