vobject 0.1.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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