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



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
306
# File 'lib/vobject/vcard/v3_0/typegrammars.rb', line 246

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



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

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



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

def classvalue
  iana_token = /[a-zA-Z\d\-]+/.r
  xname = seq(/[xX]-/, /[a-zA-Z0-9-]+/.r).map(&: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



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

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



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

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



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
166
# File 'lib/vobject/vcard/v3_0/typegrammars.rb', line 139

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



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
244
# File 'lib/vobject/vcard/v3_0/typegrammars.rb', line 210

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



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

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

.geovalueObject



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

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



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

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

.integerObject



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

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

.kindvalueObject



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

def kindvalue
  iana_token = /[a-zA-Z\d\-]+/.r
  xname = seq(/[xX]-/, /[a-zA-Z0-9-]+/.r).map(&: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



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

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



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

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



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

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

.registered_propnameObject



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

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

.registered_propname?(x) ⇒ Boolean

Returns:

  • (Boolean)


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

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

.text_tObject



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

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

.textlistObject



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

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



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

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



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
413
# File 'lib/vobject/vcard/v3_0/typegrammars.rb', line 326

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



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

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



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

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



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

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



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

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