ruby-vobject 0.1.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
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