File: rbLibXMLParser.rb

package info (click to toggle)
ruby-cfpropertylist 2.2.8-1.1%2Bdeb11u1
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 156 kB
  • sloc: ruby: 1,203; makefile: 2
file content (147 lines) | stat: -rw-r--r-- 3,672 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
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
141
142
143
144
145
146
147
# -*- coding: utf-8 -*-

require 'libxml'

module CFPropertyList
  # XML parser
  class LibXMLParser < XMLParserInterface
    # read a XML file
    # opts::
    # * :file - The filename of the file to load
    # * :data - The data to parse
    def load(opts)
      doc = nil

      if(opts.has_key?(:file)) then
        doc = LibXML::XML::Document.file(opts[:file],:options => LibXML::XML::Parser::Options::NOBLANKS|LibXML::XML::Parser::Options::NOENT)
      else
        doc = LibXML::XML::Document.string(opts[:data],:options => LibXML::XML::Parser::Options::NOBLANKS|LibXML::XML::Parser::Options::NOENT)
      end

      if doc
        root = doc.root.first
        return import_xml(root)
      end
    rescue LibXML::XML::Error => e
      raise CFFormatError.new('invalid XML: ' + e.message)
    end

    # serialize CFPropertyList object to XML
    # opts = {}:: Specify options: :formatted - Use indention and line breaks
    def to_str(opts={})
      doc = LibXML::XML::Document.new

      doc.root = LibXML::XML::Node.new('plist')
      doc.encoding = LibXML::XML::Encoding::UTF_8

      doc.root['version'] = '1.0'
      doc.root << opts[:root].to_xml(self)

      # ugly hack, but there's no other possibility I know
      str = doc.to_s(:indent => opts[:formatted])
      str1 = String.new
      first = false
      str.each_line do |line|
        str1 << line
        unless(first) then
          str1 << "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n" if line =~ /^\s*<\?xml/
        end

        first = true
      end

      str1.force_encoding('UTF-8') if str1.respond_to?(:force_encoding)
      return str1
    end

    def new_node(name)
      LibXML::XML::Node.new(name)
    end

    def new_text(val)
      LibXML::XML::Node.new_text(val)
    end

    def append_node(parent, child)
      parent << child
    end

    protected

    # get the value of a DOM node
    def get_value(n)
      content = if n.children?
        n.first.content
      else
        n.content
      end

      content.force_encoding('UTF-8') if content.respond_to?(:force_encoding)
      content
    end

    # import the XML values
    def import_xml(node)
      ret = nil

      case node.name
      when 'dict'
        hsh = Hash.new
        key = nil

        if node.children? then
          node.children.each do |n|
            next if n.text? # avoid a bug of libxml
            next if n.comment?

            if n.name == "key" then
              key = get_value(n)
            else
              raise CFFormatError.new("Format error!") if key.nil?
              hsh[key] = import_xml(n)
              key = nil
            end
          end
        end

        if hsh['CF$UID'] and hsh.keys.length == 1
          ret = CFUid.new(hsh['CF$UID'].value)
        else
          ret = CFDictionary.new(hsh)
        end

      when 'array'
        ary = Array.new

        if node.children? then
          node.children.each do |n|
            next if n.text? # avoid a bug of libxml
            next if n.comment?
            ary.push import_xml(n)
          end
        end

        ret = CFArray.new(ary)

      when 'true'
        ret = CFBoolean.new(true)
      when 'false'
        ret = CFBoolean.new(false)
      when 'real'
        ret = CFReal.new(get_value(node).to_f)
      when 'integer'
        ret = CFInteger.new(get_value(node).to_i)
      when 'string'
        ret = CFString.new(get_value(node))
      when 'data'
        ret = CFData.new(get_value(node))
      when 'date'
        ret = CFDate.new(CFDate.parse_date(get_value(node)))
      end

      return ret
    end
  end
end

# eof