Class: Vcard::V3_0::Typegrammars

Inherits:
Object
  • Object
show all
Defined in:
lib/vobject/vcard/v3_0/typegrammars.rb

Class Method Summary collapse

Class Method Details

.addressObject



245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
# File 'lib/vobject/vcard/v3_0/typegrammars.rb', line 245

def address
  text = C::TEXT3
  component = seq(text << ",".r, lazy { component }) do |(a, b)|
    [unescape(a), b].flatten
  end | text.map { |t| [unescape(t)] }
  address1 = seq(component << ";".r, component << ";".r, component << ";".r, component << ";".r,
                 component << ";".r, component << ";".r, component) do |(a, b, c, d, e, f, g)|
    a = a[0] if a.length == 1
    b = b[0] if b.length == 1
    c = c[0] if c.length == 1
    d = d[0] if d.length == 1
    e = e[0] if e.length == 1
    f = f[0] if f.length == 1
    g = g[0] if g.length == 1
    { pobox: a, ext: b, street: c,
      locality: d, region: e, code: f, country: g }
  end | seq(component << ";".r, component << ";".r, component << ";".r, component << ";".r,
            component << ";".r, component) do |(a, b, c, d, e, f)|
    a = a[0] if a.length == 1
    b = b[0] if b.length == 1
    c = c[0] if c.length == 1
    d = d[0] if d.length == 1
    e = e[0] if e.length == 1
    f = f[0] if f.length == 1
    { pobox: a, ext: b, street: c,
      locality: d, region: e, code: f, country: "" }
  end | seq(component << ";".r, component << ";".r, component << ";".r,
            component << ";".r, component) do |(a, b, c, d, e)|
    a = a[0] if a.length == 1
    b = b[0] if b.length == 1
    c = c[0] if c.length == 1
    d = d[0] if d.length == 1
    e = e[0] if e.length == 1
    { pobox: a, ext: b, street: c,
      locality: d, region: e, code: "", country: "" }
  end | seq(component << ";".r, component << ";".r, component << ";".r, component) do |(a, b, c, d)|
    a = a[0] if a.length == 1
    b = b[0] if b.length == 1
    c = c[0] if c.length == 1
    d = d[0] if d.length == 1
    { pobox: a, ext: b, street: c,
      locality: d, region: "", code: "", country: "" }
  end | seq(component << ";".r, component << ";".r, component) do |(a, b, c)|
    a = a[0] if a.length == 1
    b = b[0] if b.length == 1
    c = c[0] if c.length == 1
    { pobox: a, ext: b, street: c,
      locality: "", region: "", code: "", country: "" }
  end | seq(component << ";".r, component) do |(a, b)|
    a = a[0] if a.length == 1
    b = b[0] if b.length == 1
    { pobox: a, ext: b, street: "",
      locality: "", region: "", code: "", country: "" }
  end | component.map do |a|
    a = a[0] if a.length == 1
    { pobox: a, ext: "", street: "",
      locality: "", region: "", code: "", country: "" }
  end
  address = address1.map { |n| PropertyValue::Address.new(n) }
  address.eof
end

.binaryObject

property value types, each defining their own parser



14
15
16
17
18
19
20
21
22
23
# File 'lib/vobject/vcard/v3_0/typegrammars.rb', line 14

def binary
  binary = seq(/[a-zA-Z0-9+\/]*/.r, /={0,2}/.r) do |(b, q)|
    if (b.length + q.length) % 4 == 0
      PropertyValue::Binary.new(b + q)
    else
      { error: "Malformed binary coding" }
    end
  end
  binary.eof
end

.classvalueObject



44
45
46
47
48
49
50
51
# File 'lib/vobject/vcard/v3_0/typegrammars.rb', line 44

def classvalue
  iana_token = /[a-zA-Z\d\-]+/.r
  xname = seq(/[xX]-/, /[a-zA-Z0-9-]+/.r).map {|x, _| x.join }
  classvalue = (/PUBLIC/i.r | /PRIVATE/i.r | /CONFIDENTIAL/i.r | iana_token | xname).map do |m|
    PropertyValue::ClassValue.new m
  end
  classvalue.eof
end

.date_or_date_timeObject



167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
# File 'lib/vobject/vcard/v3_0/typegrammars.rb', line 167

def date_or_date_time
  utc_offset = seq(C::SIGN, /[0-9]{2}/.r << /:/.r._?, /[0-9]{2}/.r) do |(s, h, m)|
    { sign: s, hour: h, min: m }
  end
  zone = utc_offset.map { |u| u } |
    /Z/i.r.map { "Z" }
  hour = /[0-9]{2}/.r
  minute = /[0-9]{2}/.r
  second = /[0-9]{2}/.r
  secfrac = seq(",".r >> /[0-9]+/)
  date = seq(/[0-9]{4}/.r << /-/.r._?, /[0-9]{2}/.r << /-/.r._?, /[0-9]{2}/.r) do |(yy, mm, dd)|
    { year: yy, month: mm, day: dd }
  end
  time = seq(hour << /:/.r._?, minute << /:/.r._?, second, secfrac._?, zone._?) do |(h, m, s, f, z)|
    h = { hour: h, min: m, sec: s }
    h[:zone] = z[0] unless z.empty?
    h[:secfrac] = f[0] unless f.empty?
    h
  end
  date_or_date_time = seq(date << "T".r, time) do |(d, t)|
    PropertyValue::DateTimeLocal.new(d.merge(t))
  end | date.map { |d| PropertyValue::Date.new(d) }
  date_or_date_time.eof
end

.date_tObject



112
113
114
115
116
117
# File 'lib/vobject/vcard/v3_0/typegrammars.rb', line 112

def date_t
  date_t = seq(/[0-9]{4}/.r, /-/.r._? >> /[0-9]{2}/.r, /-/.r._? >> /[0-9]{2}/.r) do |(yy, mm, dd)|
    PropertyValue::Date.new(year: yy, month: mm, day: dd)
  end
  date_t.eof
end

.date_timeObject



138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
# File 'lib/vobject/vcard/v3_0/typegrammars.rb', line 138

def date_time
  utc_offset = seq(C::SIGN, /[0-9]{2}/.r << /:/.r._?, /[0-9]{2}/.r) do |(s, h, m)|
    { sign: s, hour: h, min: m }
  end
  zone = utc_offset.map { |u| u } |
    /Z/i.r.map { "Z" }
  hour = /[0-9]{2}/.r
  minute = /[0-9]{2}/.r
  second = /[0-9]{2}/.r
  secfrac = seq(",".r >> /[0-9]+/)
  date = seq(/[0-9]{4}/.r, /-/.r._?, /[0-9]{2}/.r, /-/.r._?, /[0-9]{2}/.r) do |(yy, _, mm, _, dd)|
    { year: yy, month: mm, day: dd }
  end
  time = seq(hour << /:/.r._?, minute << /:/.r._?, second, secfrac._?, zone._?) do |(h, m, s, f, z)|
    h = { hour: h, min: m, sec: s }
    h[:zone] = if z.empty?
                 ""
               else
                 z[0]
               end
    h[:secfrac] = f[0] unless f.empty?
    h
  end
  date_time = seq(date << "T".r, time) do |(d, t)|
    PropertyValue::DateTimeLocal.new(d.merge(t))
  end
  date_time.eof
end

.fivepartnameObject



209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
# File 'lib/vobject/vcard/v3_0/typegrammars.rb', line 209

def fivepartname
  text = C::TEXT3
  component = seq(text << ",".r, lazy { component }) do |(a, b)|
    [unescape(a), b].flatten
  end | text.map { |t| [unescape(t)] }
  fivepartname1 = seq(component << ";".r, component << ";".r, component << ";".r,
                      component << ";".r, component) do |(a, b, c, d, e)|
    a = a[0] if a.length == 1
    b = b[0] if b.length == 1
    c = c[0] if c.length == 1
    d = d[0] if d.length == 1
    e = e[0] if e.length == 1
    { surname: a, givenname: b, middlename: c, honprefix: d, honsuffix: e }
  end | seq(component << ";".r, component << ";".r, component << ";".r, component) do |(a, b, c, d)|
    a = a[0] if a.length == 1
    b = b[0] if b.length == 1
    c = c[0] if c.length == 1
    d = d[0] if d.length == 1
    { surname: a, givenname: b, middlename: c, honprefix: d, honsuffix: "" }
  end | seq(component << ";".r, component << ";".r, component) do |(a, b, c)|
    a = a[0] if a.length == 1
    b = b[0] if b.length == 1
    c = c[0] if c.length == 1
    { surname: a, givenname: b, middlename: c, honprefix: "", honsuffix: "" }
  end | seq(component << ";".r, component) do |(a, b)|
    a = a[0] if a.length == 1
    b = b[0] if b.length == 1
    { surname: a, givenname: b, middlename: "", honprefix: "", honsuffix: "" }
  end | component.map do |a|
    a = a[0] if a.length == 1
    { surname: a, givenname: "", middlename: "", honprefix: "", honsuffix: "" }
  end
  fivepartname = fivepartname1.map { |n| PropertyValue::Fivepartname.new(n) }
  fivepartname.eof
end

.float_tObject



58
59
60
61
# File 'lib/vobject/vcard/v3_0/typegrammars.rb', line 58

def float_t
  float_t = prim(:double).map { |f| PropertyValue::Float.new f }
  float_t.eof
end

.geovalueObject



32
33
34
35
36
37
38
39
40
41
42
# File 'lib/vobject/vcard/v3_0/typegrammars.rb', line 32

def geovalue
  float = prim(:double)
  geovalue = seq(float << ";".r, float) do |(a, b)|
    if a <= 180.0 && a >= -180.0 && b <= 180 && b > -180
      PropertyValue::Geovalue.new(lat: a, long: b)
    else
      { error: "Latitude/Longitude outside of range -180..180" }
    end
  end
  geovalue.eof
end

.iana_tokenObject



63
64
65
66
# File 'lib/vobject/vcard/v3_0/typegrammars.rb', line 63

def iana_token
  iana_token = /[a-zA-Z\d\-]+/.r.map { |x| PropertyValue::Ianatoken.new x }
  iana_token.eof
end

.integerObject



53
54
55
56
# File 'lib/vobject/vcard/v3_0/typegrammars.rb', line 53

def integer
  integer = prim(:int32).map { |i| PropertyValue::Integer.new i }
  integer.eof
end

.kindvalueObject



199
200
201
202
203
204
205
206
207
# File 'lib/vobject/vcard/v3_0/typegrammars.rb', line 199

def kindvalue
  iana_token = /[a-zA-Z\d\-]+/.r
  xname = seq(/[xX]-/, /[a-zA-Z0-9-]+/.r).map {|x, _| x.join }
  kindvalue = (/individual/i.r | /group/i.r | /org/i.r | /location/i.r |
               iana_token | xname).map do |k|
    PropertyValue::Kindvalue.new(k)
  end
  kindvalue.eof
end

.orgObject



103
104
105
106
107
108
109
110
# File 'lib/vobject/vcard/v3_0/typegrammars.rb', line 103

def org
  text = C::TEXT3
  org1 =
    seq(text << ";".r, lazy { org1 }) { |(a, b)| [unescape(a), b].flatten } |
    text.map { |t| [unescape(t)] }
  org	 = org1.map { |o| PropertyValue::Org.new o }
  org.eof
end

.phone_numberObject



25
26
27
28
29
30
# File 'lib/vobject/vcard/v3_0/typegrammars.rb', line 25

def phone_number
  # This is on the lax side; there should be up to 15 digits
  # Will allow letters
  phone_number = /[0-9() +A-Z-]+/i.r.map { |p| PropertyValue::Phonenumber.new p }
  phone_number.eof
end

.profilevalueObject



73
74
75
76
# File 'lib/vobject/vcard/v3_0/typegrammars.rb', line 73

def profilevalue
  profilevalue = /VCARD/i.r.map { |v| PropertyValue::Profilevalue.new v }
  profilevalue.eof
end

.registered_propnameObject



307
308
309
310
# File 'lib/vobject/vcard/v3_0/typegrammars.rb', line 307

def registered_propname
  registered_propname = C::NAME_VCARD
  registered_propname.eof
end

.registered_propname?(x) ⇒ Boolean

Returns:

  • (Boolean)


312
313
314
315
# File 'lib/vobject/vcard/v3_0/typegrammars.rb', line 312

def registered_propname?(x)
  p = registered_propname.parse(x)
  not(Rsec::INVALID[p])
end

.text_tObject



89
90
91
92
# File 'lib/vobject/vcard/v3_0/typegrammars.rb', line 89

def text_t
  text_t = C::TEXT3.map { |t| PropertyValue::Text.new(unescape(t)) }
  text_t.eof
end

.textlistObject



94
95
96
97
98
99
100
101
# File 'lib/vobject/vcard/v3_0/typegrammars.rb', line 94

def textlist
  text = C::TEXT3
  textlist1 =
    seq(text << ",".r, lazy { textlist1 }) { |(a, b)| [unescape(a), b].flatten } |
    text.map { |t| [unescape(t)] }
  textlist = textlist1.map { |m| PropertyValue::Textlist.new m }
  textlist.eof
end

.time_tObject



119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# File 'lib/vobject/vcard/v3_0/typegrammars.rb', line 119

def time_t
  utc_offset = seq(C::SIGN, /[0-9]{2}/.r << /:/.r._?, /[0-9]{2}/.r) do |(s, h, m)|
    { sign: s, hour: h, min: m }
  end
  zone = utc_offset.map { |u| u } |
    /Z/i.r.map { "Z" }
  hour = /[0-9]{2}/.r
  minute = /[0-9]{2}/.r
  second = /[0-9]{2}/.r
  secfrac = seq(",".r >> /[0-9]+/)
  time_t = seq(hour << /:/._?, minute << /:/._?, second, secfrac._?, zone._?) do |(h, m, s, f, z)|
    h = { hour: h, min: m, sec: s }
    h[:zone] = z[0] unless z.empty?
    h[:secfrac] = f[0] unless f.empty?
    PropertyValue::Time.new(h)
  end
  time_t.eof
end

.typematch(strict, key, params, _component, value, ctx) ⇒ Object

Enforce type restrictions on values of particular properties. If successful, return typed interpretation of string



325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
# File 'lib/vobject/vcard/v3_0/typegrammars.rb', line 325

def typematch(strict, key, params, _component, value, ctx)
  errors = []
  params[:VALUE] = params[:VALUE].downcase if params && params[:VALUE]
  ctx1 = Rsec::ParseContext.new value, "source"
  case key
  when :VERSION
    ret = versionvalue._parse ctx1
  when :SOURCE, :URL, :IMPP, :FBURL, :CALURI, :CALADRURI, :CAPURI
    ret = uri._parse ctx1
    # not imposing filename restrictions on calendar URIs
  when :NAME, :FN, :LABEL, :EMAIL, :MAILER, :TITLE, :ROLE, :NOTE, :PRODID, :SORT_STRING, :UID
    ret = text_t._parse ctx1
  when :CLASS
    ret = classvalue._parse ctx1
  when :CATEGORIES, :NICKNAME
    ret = textlist._parse ctx1
  when :ORG
    ret = org._parse ctx1
  when :PROFILE
    ret = profilevalue._parse ctx1
  when :N
    ret = fivepartname._parse ctx1
  when :PHOTO, :LOGO, :SOUND
    ret = if params && params[:VALUE] == "uri"
            uri._parse ctx1
          else
            binary._parse ctx1
          end
  when :KEY
    ret = if params && params[:ENCODING] == "b"
            binary._parse ctx1
          else
            text_t._parse ctx1
          end
  when :BDAY
    ret = if params && params[:VALUE] == "date-time"
            date_time._parse ctx1
          elsif params && params[:VALUE] == "date"
            date_t._parse ctx1
          else
            # unlike VCARD 4, can have either date || date_time without explicit value switch
            date_or_date_time._parse ctx1
          end
  when :REV
    ret = if params && params[:VALUE] == "date"
            date_t._parse ctx1
          elsif params && params[:VALUE] == "date-time"
            date_time._parse ctx1
          else
            # unlike VCARD 4, can have either date || date_time without explicit value switch
            ret = date_or_date_time._parse ctx1
          end
  when :ADR
    ret = address._parse ctx1
  when :TEL
    ret = phone_number._parse ctx1
  when :TZ
    ret = if params && params[:VALUE] == "text"
            text_t._parse ctx1
          else
            utc_offset._parse ctx1
          end
  when :GEO
    ret = geovalue._parse ctx1
  when :AGENT
    if params && params[:VALUE] == "uri"
      ret = uri._parse ctx1
    else
      # unescape
      value = value.gsub(/\\n/, "\n").gsub(/\\;/, ";").gsub(/\\,/, ",").gsub(/\\:/, ":")
      # spec says that colons need to be escaped, but none of the examples do so
      value = value.gsub(/BEGIN:VCARD\n/, "BEGIN:VCARD\nVERSION:3.0\n") unless value =~ /\nVERSION:3\.0/
      ctx1 = Rsec::ParseContext.new value, "source"
      ret = PropertyValue::Agent.new(Grammar.new(strict).vobject_grammar._parse(ctx1))
      # TODO same strictness as grammar
    end
  else
    ret = text_t._parse ctx1
  end
  if ret.is_a?(Hash) && ret[:error]
    parse_err(strict, errors, "#{ret[:error]} for property #{key}, value #{value}", ctx)
  end
  if Rsec::INVALID[ret]
    parse_err(strict, errors, "Type mismatch for property #{key}, value #{value}", ctx)
  end
  Rsec::Fail.reset
  [ret, errors]
end

.unescape(x) ⇒ Object

text escapes: \ ; , N n



318
319
320
321
# File 'lib/vobject/vcard/v3_0/typegrammars.rb', line 318

def unescape(x)
  # temporarily escape \\ as \007f, which is disallowed in any text
  x.gsub(/\\\\/, "\u007f").gsub(/\\;/, ";").gsub(/\\,/, ",").gsub(/\\[Nn]/, "\n").tr("\u007f", "\\")
end

.uriObject



78
79
80
81
82
83
84
85
86
87
# File 'lib/vobject/vcard/v3_0/typegrammars.rb', line 78

def uri
  uri    = /\S+/.r.map do |s|
    if s =~ URI::DEFAULT_PARSER.make_regexp
      PropertyValue::Uri.new(s)
    else
      { error: "Invalid URI" }
    end
  end
  uri.eof
end

.utc_offsetObject



192
193
194
195
196
197
# File 'lib/vobject/vcard/v3_0/typegrammars.rb', line 192

def utc_offset
  utc_offset = seq(C::SIGN, /[0-9]{2}/.r, /:/.r._?, /[0-9]{2}/.r) do |(s, h, _, m)|
    PropertyValue::Utcoffset.new(sign: s, hour: h, min: m)
  end
  utc_offset.eof
end

.versionvalueObject



68
69
70
71
# File 'lib/vobject/vcard/v3_0/typegrammars.rb', line 68

def versionvalue
  versionvalue = "3.0".r.map { |v| PropertyValue::Version.new v }
  versionvalue.eof
end