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

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

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

:nodoc:



81
82
83
# File 'lib/ri_cal/parser.rb', line 81

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

Instance Attribute Details

#last_line_strObject (readonly)

:nodoc:



6
7
8
# File 'lib/ri_cal/parser.rb', line 6

def last_line_str
  @last_line_str
end

Class Method Details

.params_and_value(string, optional_initial_semi = false) ⇒ Object

:nodoc:



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

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(":", -1)
  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:



85
86
87
# File 'lib/ri_cal/parser.rb', line 85

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

.parse_params(string) ⇒ Object

:nodoc:



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

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:



77
78
79
# File 'lib/ri_cal/parser.rb', line 77

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

#invalidObject

:nodoc:

Raises:

  • (Exception)


89
90
91
# File 'lib/ri_cal/parser.rb', line 89

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

#next_lineObject

:nodoc:



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

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:



72
73
74
75
# File 'lib/ri_cal/parser.rb', line 72

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

#parseObject

:nodoc:



98
99
100
101
102
103
104
105
106
# File 'lib/ri_cal/parser.rb', line 98

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

#parse_one(start, parent_component) ⇒ Object

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



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
140
141
142
143
# File 'lib/ri_cal/parser.rb', line 109

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"
  entity_name = first_line[:value]
  result = case entity_name
  when "VCALENDAR"
    RiCal::Component::Calendar.from_parser(self, parent_component, entity_name)
  when "VEVENT"
    RiCal::Component::Event.from_parser(self, parent_component, entity_name)
  when "VTODO"
    RiCal::Component::Todo.from_parser(self, parent_component, entity_name)
  when "VJOURNAL"
    RiCal::Component::Journal.from_parser(self, parent_component, entity_name)
  when "VFREEBUSY"
    RiCal::Component::Freebusy.from_parser(self, parent_component, entity_name)
  when "VTIMEZONE"
    RiCal::Component::Timezone.from_parser(self, parent_component, entity_name)
  when "VALARM"
    RiCal::Component::Alarm.from_parser(self, parent_component, entity_name)
  when "DAYLIGHT"
    RiCal::Component::Timezone::DaylightPeriod.from_parser(self, parent_component, entity_name)
  when "STANDARD"
    RiCal::Component::Timezone::StandardPeriod.from_parser(self, parent_component, entity_name)
  else
    RiCal::Component::NonStandard.from_parser(self, parent_component, entity_name)
  end
  @parent_stack.pop
  result
end

#separate_line(string) ⇒ Object

:nodoc:



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

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

#still_in(component, separated_line) ⇒ Object

:nodoc:



93
94
95
96
# File 'lib/ri_cal/parser.rb', line 93

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