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}