001package biweekly.io.scribe.property; 002 003import java.util.regex.Matcher; 004import java.util.regex.Pattern; 005 006import biweekly.ICalDataType; 007import biweekly.ICalVersion; 008import biweekly.io.ParseContext; 009import biweekly.io.WriteContext; 010import biweekly.parameter.ICalParameters; 011import biweekly.parameter.ParticipationLevel; 012import biweekly.parameter.ParticipationStatus; 013import biweekly.parameter.Role; 014import biweekly.property.Attendee; 015 016/* 017 Copyright (c) 2013-2015, 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 Attendee} properties. 043 * @author Michael Angstadt 044 */ 045public class AttendeeScribe extends ICalPropertyScribe<Attendee> { 046 public AttendeeScribe() { 047 super(Attendee.class, "ATTENDEE"); 048 } 049 050 @Override 051 protected ICalDataType _defaultDataType(ICalVersion version) { 052 switch (version) { 053 case V1_0: 054 return null; 055 default: 056 return ICalDataType.CAL_ADDRESS; 057 } 058 } 059 060 @Override 061 protected ICalDataType _dataType(Attendee property, ICalVersion version) { 062 if (version == ICalVersion.V1_0 && property.getUri() != null) { 063 return ICalDataType.URL; 064 } 065 return defaultDataType(version); 066 } 067 068 @Override 069 protected ICalParameters _prepareParameters(Attendee property, WriteContext context) { 070 ICalParameters copy = new ICalParameters(property.getParameters()); 071 072 //RSVP parameter 073 //1.0 - Uses the values "YES" and "NO" 074 //2.0 - Uses the values "TRUE" and "FALSE" 075 Boolean rsvp = property.getRsvp(); 076 if (rsvp != null) { 077 String value = null; 078 switch (context.getVersion()) { 079 case V1_0: 080 value = rsvp ? "YES" : "NO"; 081 break; 082 083 default: 084 value = rsvp ? "TRUE" : "FALSE"; 085 break; 086 } 087 088 copy.put(ICalParameters.RSVP, value); 089 } 090 091 //ROLE and EXPECT parameters 092 //1.0 - Uses ROLE and EXPECT 093 //2.0 - Uses only ROLE 094 Role role = property.getRole(); 095 ParticipationLevel level = property.getParticipationLevel(); 096 switch (context.getVersion()) { 097 case V1_0: 098 if (role != null) { 099 copy.put("ROLE", role.getValue()); 100 } 101 if (level != null) { 102 copy.put("EXPECT", level.getValue(context.getVersion())); 103 } 104 break; 105 106 default: 107 String value = null; 108 if (role == Role.CHAIR) { 109 value = role.getValue(); 110 } else if (level != null) { 111 value = level.getValue(context.getVersion()); 112 } else if (role != null) { 113 value = role.getValue(); 114 } 115 116 if (value != null) { 117 copy.put("ROLE", value); 118 } 119 break; 120 } 121 122 //PARTSTAT vs STATUS 123 //1.0 - Calls the parameter "STATUS" 124 //2.0 - Calls the parameter "PARTSTAT" 125 ParticipationStatus partStat = property.getParticipationStatus(); 126 if (partStat != null) { 127 String paramName; 128 String paramValue; 129 130 switch (context.getVersion()) { 131 case V1_0: 132 paramName = "STATUS"; 133 paramValue = (partStat == ParticipationStatus.NEEDS_ACTION) ? "NEEDS ACTION" : partStat.getValue(); 134 break; 135 136 default: 137 paramName = "PARTSTAT"; 138 paramValue = partStat.getValue(); 139 break; 140 } 141 142 copy.put(paramName, paramValue); 143 } 144 145 //CN parameter 146 String name = property.getCommonName(); 147 if (name != null && context.getVersion() != ICalVersion.V1_0) { 148 copy.put(ICalParameters.CN, name); 149 } 150 151 return copy; 152 } 153 154 @Override 155 protected Attendee _parseText(String value, ICalDataType dataType, ICalParameters parameters, ParseContext context) { 156 String uri = null, name = null, email = null; 157 158 Boolean rsvp = null; 159 String rsvpStr = parameters.first(ICalParameters.RSVP); 160 161 Role role = null; 162 String roleStr = parameters.first(ICalParameters.ROLE); 163 164 ParticipationLevel level = null; 165 ParticipationStatus status = null; 166 167 switch (context.getVersion()) { 168 case V1_0: 169 if (rsvpStr != null) { 170 if ("YES".equalsIgnoreCase(rsvpStr)) { 171 rsvp = Boolean.TRUE; 172 } else if ("NO".equalsIgnoreCase(rsvpStr)) { 173 rsvp = Boolean.FALSE; 174 } 175 } 176 177 if (roleStr != null) { 178 role = Role.get(roleStr); 179 } 180 181 String expect = parameters.first("EXPECT"); 182 if (expect != null) { 183 parameters.remove("EXPECT", expect); 184 level = ParticipationLevel.get(expect); 185 } 186 187 String statusStr = parameters.first("STATUS"); 188 if (statusStr != null) { 189 parameters.remove("STATUS", statusStr); 190 status = ParticipationStatus.get(statusStr); 191 } 192 193 Pattern p = Pattern.compile("^(.*?)<(.*?)>$"); 194 Matcher m = p.matcher(value); 195 if (m.find()) { 196 name = m.group(1).trim(); 197 email = m.group(2).trim(); 198 } else if (dataType == ICalDataType.URL) { 199 uri = value; 200 } else { 201 email = value; 202 } 203 204 break; 205 206 default: 207 if (rsvpStr != null) { 208 if ("TRUE".equalsIgnoreCase(rsvpStr)) { 209 rsvp = Boolean.TRUE; 210 } else if ("FALSE".equalsIgnoreCase(rsvpStr)) { 211 rsvp = Boolean.FALSE; 212 } 213 } 214 215 if (roleStr != null) { 216 if (roleStr.equalsIgnoreCase(Role.CHAIR.getValue())) { 217 role = Role.CHAIR; 218 } else { 219 ParticipationLevel l = ParticipationLevel.find(roleStr); 220 if (l == null) { 221 role = Role.get(roleStr); 222 } else { 223 level = l; 224 } 225 } 226 } 227 228 String partStat = parameters.first("PARTSTAT"); 229 if (partStat != null) { 230 parameters.remove("PARTSTAT", partStat); 231 status = ParticipationStatus.get(partStat); 232 } 233 234 name = parameters.first(ICalParameters.CN); 235 if (name != null) { 236 parameters.remove(ICalParameters.CN, name); 237 } 238 239 p = Pattern.compile("^mailto:(.*?)$"); 240 m = p.matcher(value); 241 if (m.find()) { 242 email = m.group(1); 243 } else { 244 uri = value; 245 } 246 247 break; 248 } 249 250 if (rsvp != null) { 251 parameters.removeAll(ICalParameters.RSVP); 252 } 253 if (roleStr != null) { 254 parameters.remove(ICalParameters.ROLE, roleStr); 255 } 256 257 Attendee attendee = new Attendee(name, email); 258 attendee.setParticipationStatus(status); 259 attendee.setParticipationLevel(level); 260 attendee.setRole(role); 261 attendee.setRsvp(rsvp); 262 attendee.setUri(uri); 263 264 return attendee; 265 } 266 267 @Override 268 protected String _writeText(Attendee property, WriteContext context) { 269 String uri = property.getUri(); 270 if (uri != null) { 271 return uri; 272 } 273 274 String name = property.getCommonName(); 275 String email = property.getEmail(); 276 switch (context.getVersion()) { 277 case V1_0: 278 if (name != null && email != null) { 279 return escape(name + " <" + email + ">"); 280 } 281 if (email != null) { 282 return escape(email); 283 } 284 285 break; 286 287 default: 288 if (email != null) { 289 return "mailto:" + email; 290 } 291 break; 292 } 293 294 return ""; 295 } 296}