class Vcard::V3_0::Typegrammars

Public Class Methods

address() click to toggle source
# 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
binary() click to toggle source

property value types, each defining their own parser

# 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
classvalue() click to toggle source
# 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_time() click to toggle source
# 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_t() click to toggle source
# 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_time() click to toggle source
# 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
fivepartname() click to toggle source
# 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_t() click to toggle source
# 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
geovalue() click to toggle source
# 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_token() click to toggle source
# 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
integer() click to toggle source
# File lib/vobject/vcard/v3_0/typegrammars.rb, line 53
def integer
  integer = prim(:int32).map { |i| PropertyValue::Integer.new i }
  integer.eof
end
kindvalue() click to toggle source
# 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
org() click to toggle source
# 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_number() click to toggle source
# 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
profilevalue() click to toggle source
# 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_propname() click to toggle source
# 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) click to toggle source
# 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_t() click to toggle source
# 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
textlist() click to toggle source
# 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_t() click to toggle source
# 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) click to toggle source

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

# 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) click to toggle source

text escapes: \ ; , N n

# 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
uri() click to toggle source
# 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_offset() click to toggle source
# 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
versionvalue() click to toggle source
# 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

Private Class Methods

parse_err(strict, errors, msg, ctx) click to toggle source
# File lib/vobject/vcard/v3_0/typegrammars.rb, line 416
def parse_err(strict, errors, msg, ctx)
  if strict
    raise ctx.report_error msg, "source"
  else
    errors << ctx.report_error(msg, "source")
  end
end