Class: RiCal::Parser

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

Overview

:nodoc:

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

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

:nodoc:



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

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

Instance Attribute Details

#last_line_strObject (readonly)

:nodoc:



3
4
5
# File 'lib/ri_cal/parser.rb', line 3

def last_line_str
  @last_line_str
end

Class Method Details

.params_and_value(string, optional_initial_semi = false) ⇒ Object

:nodoc:



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

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:



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

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

.parse_params(string) ⇒ Object

:nodoc:



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

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:



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

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

#invalidObject

:nodoc:



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

def invalid #:nodoc:
  raise "Invalid icalendar file"
end

#next_lineObject

:nodoc:



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

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:



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

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

#parseObject

:nodoc:



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

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)



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
140
# 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"
  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:



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

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:



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

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