ruby-vobject 0.1.0 → 1.0.1

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