Class: RiCal::Parser

Inherits:
Object show all
Defined in:
lib/ri_cal/parser.rb

Overview

  • ©2009 Rick DeNatale

  • All rights reserved. Refer to the file README.txt for the license

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(io = StringIO.new("")) ⇒ Parser

:nodoc:



79
80
81
# File 'lib/ri_cal/parser.rb', line 79

def initialize(io = StringIO.new("")) #:nodoc:
  @io = io
end

Class Method Details

.params_and_value(string, optional_initial_semi = false) ⇒ Object

:nodoc:



38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/ri_cal/parser.rb', line 38

def self.params_and_value(string, optional_initial_semi = false) #:nodoc:
  string = string.sub(/^:/,'')
  return [{}, string] unless optional_initial_semi || string.match(/^;/)
  segments = string.sub(';','').split(":")
  return [{}, string] if segments.length < 2
  quote_count = 0
  gathering_params = true
  params = []
  values = []
  segments.each do |segment|
    if gathering_params
      params << segment
      quote_count += segment.count("\"")
      gathering_params = (1 == quote_count % 2)
    else
      values << segment
    end
  end
  [parse_params(params.join(":")), values.join(":")]
end

.parse(io = StringIO.new("")) ⇒ Object

:nodoc:



83
84
85
# File 'lib/ri_cal/parser.rb', line 83

def self.parse(io = StringIO.new("")) #:nodoc:
  new(io).parse
end

.parse_params(string) ⇒ Object

:nodoc:



22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# File 'lib/ri_cal/parser.rb', line 22

def self.parse_params(string) #:nodoc:
  if string
    string.split(";").inject({}) { |result, val|
      m = /^(.+)=(.+)$/.match(val)
      raise "Invalid parameter value #{val.inspect}" unless m
      #TODO - The gsub below is a simplest fix for http://rick_denatale.lighthouseapp.com/projects/30941/tickets/19
      #       it may need further examination if more pathological cases show up.
      param_val = m[2].sub(/^\"(.*)\"$/, '\1') 
      result[m[1]] = param_val
      result 
    }
  else
    nil
  end
end

Instance Method Details

#buffer_or_lineObject

:nodoc:



75
76
77
# File 'lib/ri_cal/parser.rb', line 75

def buffer_or_line #:nodoc:
  @buffer ||= @io.readline.chomp
end

#invalidObject

:nodoc:

Raises:

  • (Exception)


87
88
89
# File 'lib/ri_cal/parser.rb', line 87

def invalid #:nodoc:
  raise Exception.new("Invalid icalendar file")
end

#next_lineObject

:nodoc:



6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# File 'lib/ri_cal/parser.rb', line 6

def next_line #:nodoc:
  result = nil
  begin
    result = buffer_or_line
    @buffer = nil
    while /^\s/ =~ buffer_or_line
      result = "#{result}#{@buffer[1..-1]}"
      @buffer = nil
    end
  rescue EOFError
    return nil
  ensure
    return result
  end
end

#next_separated_lineObject

:nodoc:



70
71
72
73
# File 'lib/ri_cal/parser.rb', line 70

def next_separated_line #:nodoc:
  line = next_line
  line ? separate_line(line) : nil
end

#parseObject

:nodoc:



96
97
98
99
100
101
102
103
# File 'lib/ri_cal/parser.rb', line 96

def parse #:nodoc:
  result = []
  while start_line = next_line
    @parent_stack = []
    result << parse_one(start_line, nil)
  end
  result
end

#parse_one(start, parent_component) ⇒ Object

TODO: Need to parse non-standard component types (iana-token or x-name)



106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/ri_cal/parser.rb', line 106

def parse_one(start, parent_component) #:nodoc:

  @parent_stack << parent_component
  if Hash === start
    first_line = start
  else
    first_line = separate_line(start)
  end
  invalid unless first_line[:name] == "BEGIN"
  result = case first_line[:value]
  when "VCALENDAR"
    RiCal::Component::Calendar.from_parser(self, parent_component)
  when "VEVENT"
    RiCal::Component::Event.from_parser(self, parent_component)
  when "VTODO"
    RiCal::Component::Todo.from_parser(self, parent_component)
  when "VJOURNAL"
    RiCal::Component::Journal.from_parser(self, parent_component)
  when "VFREEBUSY"
    RiCal::Component::Freebusy.from_parser(self, parent_component)
  when "VTIMEZONE"
    RiCal::Component::Timezone.from_parser(self, parent_component)
  when "VALARM"
    RiCal::Component::Alarm.from_parser(self, parent_component)
  when "DAYLIGHT"
    RiCal::Component::Timezone::DaylightPeriod.from_parser(self, parent_component)
  when "STANDARD"
    RiCal::Component::Timezone::StandardPeriod.from_parser(self, parent_component)
  else
    invalid
  end
  @parent_stack.pop
  result
end

#separate_line(string) ⇒ Object

:nodoc:



59
60
61
62
63
64
65
66
67
68
# File 'lib/ri_cal/parser.rb', line 59

def separate_line(string) #:nodoc:
  match = string.match(/^([^;:]*)(.*)$/)
  name = match[1]
  params, value = *Parser.params_and_value(match[2])
  {
    :name => name,
    :params => params,
    :value => value
  }
end

#still_in(component, separated_line) ⇒ Object

:nodoc:



91
92
93
94
# File 'lib/ri_cal/parser.rb', line 91

def still_in(component, separated_line) #:nodoc:
  invalid unless separated_line
  separated_line[:value] != component || separated_line[:name] != "END"
end