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}