vobject 0.1.0 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/macos.yml +38 -0
  3. data/.github/workflows/ubuntu.yml +56 -0
  4. data/.github/workflows/windows.yml +40 -0
  5. data/.hound.yml +3 -0
  6. data/.rubocop.tb.yml +650 -0
  7. data/.rubocop.yml +1077 -0
  8. data/Gemfile +1 -1
  9. data/LICENSE.txt +21 -17
  10. data/README.adoc +151 -0
  11. data/Rakefile +1 -1
  12. data/lib/c.rb +173 -0
  13. data/lib/error.rb +19 -0
  14. data/lib/vcalendar.rb +77 -0
  15. data/lib/vcard.rb +67 -0
  16. data/lib/vobject.rb +13 -170
  17. data/lib/vobject/component.rb +87 -36
  18. data/lib/vobject/parameter.rb +116 -0
  19. data/lib/vobject/parametervalue.rb +26 -0
  20. data/lib/vobject/property.rb +134 -55
  21. data/lib/vobject/propertyvalue.rb +46 -0
  22. data/lib/vobject/vcalendar/component.rb +106 -0
  23. data/lib/vobject/vcalendar/grammar.rb +595 -0
  24. data/lib/vobject/vcalendar/paramcheck.rb +259 -0
  25. data/lib/vobject/vcalendar/propertyparent.rb +98 -0
  26. data/lib/vobject/vcalendar/propertyvalue.rb +606 -0
  27. data/lib/vobject/vcalendar/typegrammars.rb +605 -0
  28. data/lib/vobject/vcard/v3_0/component.rb +40 -0
  29. data/lib/vobject/vcard/v3_0/grammar.rb +175 -0
  30. data/lib/vobject/vcard/v3_0/paramcheck.rb +110 -0
  31. data/lib/vobject/vcard/v3_0/parameter.rb +17 -0
  32. data/lib/vobject/vcard/v3_0/property.rb +18 -0
  33. data/lib/vobject/vcard/v3_0/propertyvalue.rb +401 -0
  34. data/lib/vobject/vcard/v3_0/typegrammars.rb +425 -0
  35. data/lib/vobject/vcard/v4_0/component.rb +40 -0
  36. data/lib/vobject/vcard/v4_0/grammar.rb +224 -0
  37. data/lib/vobject/vcard/v4_0/paramcheck.rb +269 -0
  38. data/lib/vobject/vcard/v4_0/parameter.rb +18 -0
  39. data/lib/vobject/vcard/v4_0/property.rb +63 -0
  40. data/lib/vobject/vcard/v4_0/propertyvalue.rb +404 -0
  41. data/lib/vobject/vcard/v4_0/typegrammars.rb +539 -0
  42. data/lib/vobject/version.rb +1 -1
  43. data/vobject.gemspec +19 -16
  44. metadata +81 -26
  45. data/.travis.yml +0 -5
  46. data/README.md +0 -94
@@ -0,0 +1,539 @@
1
+ require "rsec"
2
+ require "set"
3
+ require "uri"
4
+ require "date"
5
+ include Rsec::Helpers
6
+ require "vobject"
7
+ require_relative "./propertyvalue"
8
+
9
+ module Vcard::V4_0
10
+ class Typegrammars
11
+ class << self
12
+ # property value types, each defining their own parser
13
+ def integer
14
+ integer = prim(:int32).map { |i| PropertyValue::Integer.new i }
15
+ integer.eof
16
+ end
17
+
18
+ def float_t
19
+ float_t = prim(:double).map { |f| PropertyValue::Float.new f }
20
+ float_t.eof
21
+ end
22
+
23
+ def iana_token
24
+ iana_token = C::IANATOKEN.map { |x| PropertyValue::Ianatoken.new x }
25
+ iana_token.eof
26
+ end
27
+
28
+ def versionvalue
29
+ versionvalue = "4.0".r.map { |v| PropertyValue::Version.new v }
30
+ versionvalue.eof
31
+ end
32
+
33
+ def uri
34
+ uri = /\S+/.r.map do |s|
35
+ if s =~ URI::DEFAULT_PARSER.make_regexp
36
+ PropertyValue::Uri.new(s)
37
+ else
38
+ { error: "Invalid URI" }
39
+ end
40
+ end
41
+ uri.eof
42
+ end
43
+
44
+ def clientpidmap
45
+ uri = /\S+/.r.map do |s|
46
+ if s =~ URI::DEFAULT_PARSER.make_regexp
47
+ s
48
+ else
49
+ { error: "Invalid URI" }
50
+ end
51
+ end
52
+ clientpidmap = seq(/[0-9]/.r << ";".r, uri) do |a, b|
53
+ PropertyValue::Clientpidmap.new(pid: a, uri: b)
54
+ end
55
+ clientpidmap.eof
56
+ end
57
+
58
+ def text_t
59
+ text_t = C::TEXT4.map { |t| PropertyValue::Text.new(unescape(t)) }
60
+ text_t.eof
61
+ end
62
+
63
+ def textlist
64
+ textlist1 =
65
+ seq(C::TEXT4 << ",".r, lazy { textlist1 }) { |a, b| [unescape(a), b].flatten } |
66
+ C::TEXT4.map { |t| [unescape(t)] }
67
+ textlist = textlist1.map { |m| PropertyValue::Textlist.new m }
68
+ textlist.eof
69
+ end
70
+
71
+ def date_t
72
+ date_t1 = seq(/[0-9]{4}/.r, /[0-9]{2}/.r, /[0-9]{2}/.r) do |yy, mm, dd|
73
+ { year: yy, month: mm, day: dd }
74
+ end | seq(/[0-9]{4}/.r << "-".r, /[0-9]{2}/.r) do |yy, dd|
75
+ { year: yy, day: dd }
76
+ end | /[0-9]{4}/.r do |yy|
77
+ { year: yy }
78
+ end | seq("--".r >> /[0-9]{2}/.r, /[0-9]{2}/.r) do |mm, dd|
79
+ { month: mm, day: dd }
80
+ end | seq("--".r >> /[0-9]{2}/.r) do |mm|
81
+ { month: mm }
82
+ end | seq("--", "-", /[0-9]{2}/.r) do |_, _, dd|
83
+ { day: dd }
84
+ end
85
+ date_t = date_t1.map { |d| PropertyValue::Date.new d }
86
+ date_t.eof
87
+ end
88
+
89
+ def date_noreduc
90
+ date_noreduc1 = seq(/[0-9]{4}/.r, /[0-9]{2}/.r, /[0-9]{2}/.r) do |yy, mm, dd|
91
+ { year: yy, month: mm, day: dd }
92
+ end | seq("--".r >> /[0-9]{2}/.r, /[0-9]{2}/.r) do |mm, dd|
93
+ { month: mm, day: dd }
94
+ end | seq("--", "-", /[0-9]{2}/.r) do |_, _, dd|
95
+ { day: dd }
96
+ end
97
+ date_noreduc = date_noreduc1.map { |d| PropertyValue::Date.new d }
98
+ date_noreduc.eof
99
+ end
100
+
101
+ def date_complete
102
+ date_complete1 = seq(/[0-9]{4}/.r, /[0-9]{2}/.r, /[0-9]{2}/.r) do |yy, mm, dd|
103
+ { year: yy, month: mm, day: dd }
104
+ end
105
+ date_complete = date_complete1.map { |d| PropertyValue::Date.new d }
106
+ date_complete.eof
107
+ end
108
+
109
+ def time_t
110
+ hour = /[0-9]{2}/.r
111
+ minute = /[0-9]{2}/.r
112
+ second = /[0-9]{2}/.r
113
+ time1 = seq(hour, minute, second, C::ZONE._?) do |h, m, s, z|
114
+ h = { hour: h, min: m, sec: s }
115
+ h[:zone] = z[0] unless z.empty?
116
+ h
117
+ end | seq(hour, minute, C::ZONE._?) do |h, m, z|
118
+ h = { hour: h, min: m }
119
+ h[:zone] = z[0] unless z.empty?
120
+ h
121
+ end | seq(hour, C::ZONE._?) do |h, z|
122
+ h = { hour: h }
123
+ h[:zone] = z[0] unless z.empty?
124
+ h
125
+ # } | seq("-", minute, second, C::ZONE._?) { |m, s, z|
126
+ # errata: remove zones from truncated times
127
+ end | seq("-".r >> minute, second) do |m, s|
128
+ { min: m, sec: s }
129
+ # } | seq("-", minute, C::ZONE._?) { |m, z|
130
+ # errata: remove zones from truncated times
131
+ end | seq("-".r >> minute) do |m|
132
+ { min: m }
133
+ # } | seq("-", "-", second, C::ZONE._?) do |s, z|
134
+ # errata: remove zones from truncated times
135
+ end | seq("-", "-", second) do |_, _, s|
136
+ h = { sec: s }
137
+ h
138
+ end
139
+ time = time1.map { |d| PropertyValue::Time.new d }
140
+ time.eof
141
+ end
142
+
143
+ def time_notrunc
144
+ time_notrunc1 = seq(hour, minute, second, C::ZONE._?) do |h, m, s, z|
145
+ h = { hour: h, min: m, sec: s }
146
+ h[:zone] = z[0] unless z.empty?
147
+ h
148
+ end | seq(hour, minute, C::ZONE._?) do |h, m, z|
149
+ h = { hour: h, min: m }
150
+ h[:zone] = z[0] unless z.empty?
151
+ h
152
+ end | seq(hour, C::ZONE._?) do |h, z|
153
+ h = { hour: h }
154
+ h[:zone] = z[0] unless z.empty?
155
+ h
156
+ end
157
+ time_notrunc = time_notrunc1.map { |d| PropertyValue::Time.new d }
158
+ time_notrunc.eof
159
+ end
160
+
161
+ def time_complete
162
+ time_complete1 = seq(hour, minute, second, C::ZONE._?) do |h, m, s, z|
163
+ h = { hour: h, min: m, sec: s }
164
+ h[:zone] = z[0] unless z.empty?
165
+ h
166
+ end
167
+ time_complete = time_complete1.map { |d| PropertyValue::Time.new d }
168
+ time_complete.eof
169
+ end
170
+
171
+ def date_time
172
+ hour = /[0-9]{2}/.r
173
+ minute = /[0-9]{2}/.r
174
+ second = /[0-9]{2}/.r
175
+ time_notrunc = seq(hour, minute, second, C::ZONE._?) do |h, m, s, z|
176
+ h = { hour: h, min: m, sec: s }
177
+ h[:zone] = z[0] unless z.empty?
178
+ h
179
+ end | seq(hour, minute, C::ZONE._?) do |h, m, z|
180
+ h = { hour: h, min: m }
181
+ h[:zone] = z[0] unless z.empty?
182
+ h
183
+ end | seq(hour, C::ZONE._?) do |h, z|
184
+ h = { hour: h }
185
+ h[:zone] = z[0] unless z.empty?
186
+ h
187
+ end
188
+ date_noreduc = seq(/[0-9]{4}/.r, /[0-9]{2}/.r, /[0-9]{2}/.r) do |yy, mm, dd|
189
+ { year: yy, month: mm, day: dd }
190
+ end | seq("--".r >> /[0-9]{2}/.r, /[0-9]{2}/.r) do |mm, dd|
191
+ { month: mm, day: dd }
192
+ end | seq("--", "-", /[0-9]{2}/.r) do |_, _, dd|
193
+ { day: dd }
194
+ end
195
+ date_time = seq(date_noreduc << "T".r, time_notrunc) do |d, t|
196
+ d = d.merge t
197
+ PropertyValue::DateTimeLocal.new d
198
+ end
199
+ date_time.eof
200
+ end
201
+
202
+ def timestamp
203
+ date_complete = seq(/[0-9]{4}/.r, /[0-9]{2}/.r, /[0-9]{2}/.r) do |yy, mm, dd|
204
+ { year: yy, month: mm, day: dd }
205
+ end
206
+ hour = /[0-9]{2}/.r
207
+ minute = /[0-9]{2}/.r
208
+ second = /[0-9]{2}/.r
209
+ time_complete = seq(hour, minute, second, C::ZONE._?) do |h, m, s, z|
210
+ h = { hour: h, min: m, sec: s }
211
+ h[:zone] = z[0] unless z.empty?
212
+ h
213
+ end
214
+ timestamp = seq(date_complete << "T".r, time_complete) do |d, t|
215
+ ret = PropertyValue::DateTimeLocal.new(d.merge(t))
216
+ ret.type = 'timestamp'
217
+ ret
218
+ end
219
+ timestamp.eof
220
+ end
221
+
222
+ def date_and_or_time
223
+ hour = /[0-9]{2}/.r
224
+ minute = /[0-9]{2}/.r
225
+ second = /[0-9]{2}/.r
226
+ time_notrunc = seq(hour, minute, second, C::ZONE._?) do |h, m, s, z|
227
+ h = { hour: h, min: m, sec: s }
228
+ h[:zone] = z[0] unless z.empty?
229
+ h
230
+ end | seq(hour, minute, C::ZONE._?) do |h, m, z|
231
+ h = { hour: h, min: m }
232
+ h[:zone] = z[0] unless z.empty?
233
+ h
234
+ end | seq(hour, C::ZONE._?) do
235
+ h = { hour: h }
236
+ h[:zone] = z[0] unless z.empty?
237
+ h
238
+ end
239
+ date_noreduc = seq(/[0-9]{4}/.r, /[0-9]{2}/.r, /[0-9]{2}/.r) do |yy, mm, dd|
240
+ { year: yy, month: mm, day: dd }
241
+ end | seq("--", /[0-9]{2}/.r, /[0-9]{2}/.r) do |_, mm, dd|
242
+ { month: mm, day: dd }
243
+ end | seq("--", "-", /[0-9]{2}/.r) { |_, _, dd| { day: dd } }
244
+ date_time = seq(date_noreduc << "T".r, time_notrunc) { |d, t| d.merge t }
245
+ date = seq(/[0-9]{4}/.r, /[0-9]{2}/.r, /[0-9]{2}/.r) do |yy, mm, dd|
246
+ { year: yy, month: mm, day: dd }
247
+ end | seq(/[0-9]{4}/.r << "-".r, /[0-9]{2}/.r) do |yy, dd|
248
+ { year: yy, day: dd }
249
+ end | /[0-9]{4}/.r do |yy|
250
+ { year: yy }
251
+ end | seq("--", /[0-9]{2}/.r, /[0-9]{2}/.r) do |_, mm, dd|
252
+ { month: mm, day: dd }
253
+ end | seq("--", /[0-9]{2}/.r) do |_, mm|
254
+ { month: mm }
255
+ end | seq("--", "-", /[0-9]{2}/.r) do |_, _, dd|
256
+ { day: dd }
257
+ end
258
+ time = seq(hour, minute, second, C::ZONE._?) do |h, m, s, z|
259
+ h = { hour: h, min: m, sec: s }
260
+ h[:zone] = z[0] unless z.empty?
261
+ h
262
+ end | seq(hour, minute, C::ZONE._?) do |h, m, z|
263
+ h = { hour: h, min: m }
264
+ h[:zone] = z[0] unless z.empty?
265
+ h
266
+ end | seq(hour, C::ZONE._?) do |h, z|
267
+ h = { hour: h }
268
+ h[:zone] = z[0] unless z.empty?
269
+ h
270
+ # } | seq("-", minute, second, C::ZONE._?) { |m, s, z|
271
+ # errata: remove zones from truncated times
272
+ end | seq("-".r >> minute, second) do |m, s|
273
+ { min: m, sec: s }
274
+ # } | seq("-", minute, C::ZONE._?) { |m, z|
275
+ # errata: remove zones from truncated times
276
+ end | seq("-".r >> minute) do |m|
277
+ { min: m }
278
+ # } | seq("-", "-", second, C::ZONE._?) { |s, z|
279
+ # errata: remove zones from truncated times
280
+ end | seq("-", "-", second) do |_, _, s|
281
+ { sec: s }
282
+ end
283
+ date_and_or_time = date_time.map do |d|
284
+ ret = PropertyValue::DateTimeLocal.new d
285
+ ret.type = "date-and-or-time"
286
+ ret
287
+ end | date.map do |d|
288
+ ret = PropertyValue::Date.new d
289
+ ret.type = "date-and-or-time"
290
+ ret
291
+ end | seq("T".r >> time).map do |t|
292
+ ret = PropertyValue::Time.new t
293
+ ret.type = "date-and-or-time"
294
+ ret
295
+ end
296
+ date_and_or_time.eof
297
+ end
298
+
299
+ def utc_offset
300
+ utc_offset = C::UTC_OFFSET.map { |u| PropertyValue::Utcoffset.new u }
301
+ utc_offset.eof
302
+ end
303
+
304
+ def kindvalue
305
+ kindvalue = (/individual/i.r | /group/i.r | /org/i.r | /location/i.r |
306
+ /application/i.r | C::IANATOKEN |
307
+ C::XNAME_VCARD).map { |v| PropertyValue::Kindvalue.new v }
308
+ kindvalue.eof
309
+ end
310
+
311
+ def fivepartname
312
+ component = seq(C::COMPONENT4 << ",".r, lazy { component }) do |a, b|
313
+ [unescape_component(a), b].flatten
314
+ end | C::COMPONENT4.map { |t| [unescape_component(t)] }
315
+ fivepartname = seq(component << ";".r, component << ";".r,
316
+ component << ";".r, component << ";".r,
317
+ component) do |a, b, c, d, e|
318
+ a = a[0] if a.length == 1
319
+ b = b[0] if b.length == 1
320
+ c = c[0] if c.length == 1
321
+ d = d[0] if d.length == 1
322
+ e = e[0] if e.length == 1
323
+ PropertyValue::Fivepartname.new(surname: a, givenname: b,
324
+ additionalname: c, honprefix: d, honsuffix: e)
325
+ end
326
+ fivepartname.eof
327
+ end
328
+
329
+ def address
330
+ component = seq(C::COMPONENT4 << ",".r, lazy { component }) do |a, b|
331
+ [unescape_component(a), b].flatten
332
+ end | C::COMPONENT4.map { |t| [unescape_component(t)] }
333
+ address = seq(component << ";".r, component << ";".r,
334
+ component << ";".r, component << ";".r,
335
+ component << ";".r, component << ";".r,
336
+ component) do |a, b, c, d, e, f, g|
337
+ a = a[0] if a.length == 1
338
+ b = b[0] if b.length == 1
339
+ c = c[0] if c.length == 1
340
+ d = d[0] if d.length == 1
341
+ e = e[0] if e.length == 1
342
+ f = f[0] if f.length == 1
343
+ g = g[0] if g.length == 1
344
+ PropertyValue::Address.new(pobox: a, ext: b,
345
+ street: c,
346
+ locality: d, region: e, code: f,
347
+ country: g)
348
+ end
349
+ address.eof
350
+ end
351
+
352
+ def gender
353
+ gender1 = seq(/[MFONU]/.r._? << ";", C::TEXT4) do |sex, gender|
354
+ sex = sex[0] unless sex.empty?
355
+ { sex: sex, gender: gender }
356
+ end | /[MFONU]/.r.map { |sex| { sex: sex, gender: "" } }
357
+ gender = gender1.map { |g| PropertyValue::Gender.new g }
358
+ gender.eof
359
+ end
360
+
361
+ def org
362
+ text = C::COMPONENT4
363
+ org1 =
364
+ seq(text, ";", lazy { org1 }) { |a, _, b| [unescape_component(a), b].flatten } |
365
+ text.map { |t| [unescape_component(t)] }
366
+ org = org1.map { |g| PropertyValue::Org.new g }
367
+ org.eof
368
+ end
369
+
370
+ def lang
371
+ lang = C::RFC5646LANGVALUE.map { |l| PropertyValue::Lang.new l }
372
+ lang.eof
373
+ end
374
+
375
+ def typeparamtel1list
376
+ typeparamtel1 = /TEXT/i.r | /VOICE/i.r | /FAX/i.r | /CELL/i.r | /VIDEO/i.r |
377
+ /PAGER/i.r | /TEXTPHONE/i.r | C::IANATOKEN | C::XNAME_VCARD
378
+ typeparamtel1list = seq(typeparamtel1 << ",".r,
379
+ lazy { typeparamtel1list }) do |a, b|
380
+ [a, b].flatten
381
+ end | typeparamtel1.map { |t| [t] }
382
+ typeparamtel1list.eof
383
+ end
384
+
385
+ def typerelatedlist
386
+ typeparamrelated = /CONTACT/i.r | /ACQUAINTANCE/i.r | /FRIEND/i.r |
387
+ /MET/i.r | /CO-WORKER/i.r | /COLLEAGUE/i.r | /CO-RESIDENT/i.r |
388
+ /NEIGHBOR/i.r | /CHILD/i.r | /PARENT/i.r | /SIBLING/i.r | /SPOUSE/i.r |
389
+ /KIN/i.r | /MUSE/i.r | /CRUSH/i.r | /DATE/i.r | /SWEETHEART/i.r |
390
+ /ME/i.r | /AGENT/i.r | /EMERGENCY/i.r
391
+ typerelatedlist =
392
+ seq(typeparamrelated << ";".r,
393
+ lazy { typerelatedlist }) { |a, b| [a, b].flatten } |
394
+ typeparamrelated.map { |t| [t] }
395
+ typerelatedlist.eof
396
+ end
397
+
398
+ # text escapes: \\ \; \, \N \n
399
+ def unescape(x)
400
+ # temporarily escape \\ as \007f, which is disallowed in any text
401
+ x.gsub(/\\\\/, "\u007f").gsub(/\\,/, ",").gsub(/\\[Nn]/, "\n").
402
+ tr("\u007f", "\\")
403
+ end
404
+
405
+ # also escape semicolon for compound types
406
+ def unescape_component(x)
407
+ # temporarily escape \\ as \007f, which is disallowed in any text
408
+ x.gsub(/\\\\/, "\u007f").gsub(/\\;/, ";").gsub(/\\,/, ",").
409
+ gsub(/\\[Nn]/, "\n").tr("\u007f", "\\")
410
+ end
411
+
412
+ # Enforce type restrictions on values of particular properties.
413
+ # If successful, return typed interpretation of string
414
+ def typematch(strict, key, params, _component, value)
415
+ errors = []
416
+ ctx1 = Rsec::ParseContext.new value, "source"
417
+ case key
418
+ when :VERSION
419
+ ret = versionvalue._parse ctx1
420
+ when :SOURCE, :PHOTO, :IMPP, :GEO, :LOGO, :MEMBER, :SOUND, :URL, :FBURL,
421
+ :CALADRURI, :CALURI, :ORG_DIRECTORY
422
+ ret = uri._parse ctx1
423
+ when :KIND
424
+ ret = kindvalue._parse ctx1
425
+ when :XML, :FN, :EMAIL, :TITLE, :ROLE, :NOTE, :EXPERTISE, :HOBBY,
426
+ :INTEREST
427
+ ret = text_t._parse ctx1
428
+ when :NICKNAME, :CATEGORIES
429
+ ret = textlist._parse ctx1
430
+ when :ORG
431
+ ret = org._parse ctx1
432
+ when :N
433
+ ret = fivepartname._parse ctx1
434
+ when :ADR
435
+ ret = address._parse ctx1
436
+ when :BDAY, :ANNIVERSARY
437
+ if params && params[:VALUE] == "text"
438
+ if params[:CALSCALE]
439
+ parse_err(strict, errors,
440
+ "Specified CALSCALE within property #{key} as text", ctx1)
441
+ end
442
+ ret = text_t._parse ctx1
443
+ else
444
+ if params && params[:CALSCALE] && /^T/ =~ value
445
+ parse_err(strict, errors,
446
+ "Specified CALSCALE within property #{key} as time", ctx1)
447
+ end
448
+ ret = date_and_or_time._parse ctx1
449
+ end
450
+ when :DEATHDATE
451
+ ret = if params && params[:VALUE] == "text"
452
+ text_t._parse ctx1
453
+ else
454
+ date_and_or_time._parse ctx1
455
+ end
456
+ when :TEL
457
+ if params && params[:TYPE]
458
+ typestr = params[:TYPE].is_a?(Array) ? params[:TYPE].join(",") : params[:TYPE]
459
+ ret1 = typeparamtel1list.parse typestr
460
+ if !ret1 || Rsec::INVALID[ret1]
461
+ parse_err(strict, errors,
462
+ "Specified illegal TYPE parameter #{typestr} within property #{key}", ctx1)
463
+ end
464
+ end
465
+ ret = if params && params[:VALUE] == "uri"
466
+ uri._parse ctx1
467
+ else
468
+ text_t._parse ctx1
469
+ end
470
+ when :BIRTHPLACE, :DEATHPLACE
471
+ ret = if params && params[:VALUE] == "uri"
472
+ uri._parse ctx1
473
+ else
474
+ text_t._parse ctx1
475
+ end
476
+ when :RELATED
477
+ if params && params[:TYPE]
478
+ typestr = params[:TYPE].is_a?(Array) ? params[:TYPE].join(";") : params[:TYPE]
479
+ ret1 = typerelatedlist.parse typestr
480
+ if !ret1 || Rsec::INVALID[ret1]
481
+ parse_err(strict, errors,
482
+ "Specified illegal TYPE parameter #{typestr} within property #{key}", ctx1)
483
+ end
484
+ end
485
+ ret = if params && params[:VALUE] == "uri"
486
+ uri._parse ctx1
487
+ else
488
+ text_t._parse ctx1
489
+ end
490
+ when :UID, :KEY
491
+ ret = if params && params[:VALUE] == "text"
492
+ text_t._parse ctx1
493
+ else
494
+ uri._parse ctx1
495
+ end
496
+ when :GENDER
497
+ ret = gender._parse ctx1
498
+ when :LANG
499
+ ret = lang._parse ctx1
500
+ when :TZ
501
+ ret = if params && params[:VALUE] == "uri"
502
+ uri._parse ctx1
503
+ elsif params && params[:VALUE] == "utc_offset"
504
+ utc_offset._parse ctx1
505
+ else
506
+ text_t._parse ctx1
507
+ end
508
+ when :REV
509
+ ret = timestamp._parse ctx1
510
+ when :CLIENTPIDMAP
511
+ if params && params[:PID]
512
+ parse_err(strict, errors, "Specified PID parameter in CLIENTPIDMAP property", @ctx)
513
+ end
514
+ ret = clientpidmap._parse ctx1
515
+ else
516
+ # left completely open in spec
517
+ ret = Vobject::PropertyValue.new value
518
+ end
519
+ if ret.is_a?(Hash) && ret[:error]
520
+ parse_err(strict, errors, "#{ret[:error]} for property #{key}, value #{value}", ctx1)
521
+ end
522
+ if Rsec::INVALID[ret]
523
+ parse_err(strict, errors, "Type mismatch for property #{key}, value #{value}", ctx1)
524
+ end
525
+ ret
526
+ end
527
+
528
+ private
529
+
530
+ def parse_err(strict, errors, msg, ctx)
531
+ if strict
532
+ raise ctx.report_error msg, "source"
533
+ else
534
+ errors << ctx.report_error(msg, "source")
535
+ end
536
+ end
537
+ end
538
+ end
539
+ end