first commit
This commit is contained in:
commit
2bba8c5fee
122 changed files with 8389 additions and 0 deletions
288
lib/cfpropertylist/rbCFPropertyList.rb
Normal file
288
lib/cfpropertylist/rbCFPropertyList.rb
Normal file
|
@ -0,0 +1,288 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# CFPropertyList implementation
|
||||
# class to read, manipulate and write both XML and binary property list
|
||||
# files (plist(5)) as defined by Apple
|
||||
#
|
||||
# == Example
|
||||
#
|
||||
# # create a arbitrary data structure of basic data types
|
||||
# data = {
|
||||
# 'name' => 'John Doe',
|
||||
# 'missing' => true,
|
||||
# 'last_seen' => Time.now,
|
||||
# 'friends' => ['Jane Doe','Julian Doe'],
|
||||
# 'likes' => {
|
||||
# 'me' => false
|
||||
# }
|
||||
# }
|
||||
#
|
||||
# # create CFPropertyList::List object
|
||||
# plist = CFPropertyList::List.new
|
||||
#
|
||||
# # call CFPropertyList.guess() to create corresponding CFType values
|
||||
# # pass in optional :convert_unknown_to_string => true to convert things like symbols into strings.
|
||||
# plist.value = CFPropertyList.guess(data)
|
||||
#
|
||||
# # write plist to file
|
||||
# plist.save("example.plist", CFPropertyList::List::FORMAT_BINARY)
|
||||
#
|
||||
# # … later, read it again
|
||||
# plist = CFPropertyList::List.new({:file => "example.plist"})
|
||||
# data = CFPropertyList.native_types(plist.value)
|
||||
#
|
||||
# Author:: Christian Kruse (mailto:cjk@wwwtech.de)
|
||||
# Copyright:: Copyright (c) 2010
|
||||
# License:: Distributes under the same terms as Ruby
|
||||
|
||||
require 'libxml'
|
||||
require 'kconv'
|
||||
require 'date'
|
||||
|
||||
module CFPropertyList
|
||||
# interface class for PList parsers
|
||||
class ParserInterface
|
||||
# load a plist
|
||||
def load(opts={})
|
||||
return ""
|
||||
end
|
||||
|
||||
# convert a plist to string
|
||||
def to_str(opts={})
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
dirname = File.dirname(__FILE__)
|
||||
require dirname + '/rbCFFormatError.rb'
|
||||
require dirname + '/rbCFTypes.rb'
|
||||
require dirname + '/rbXMLCFPropertyList.rb'
|
||||
require dirname + '/rbBinaryCFPropertyList.rb'
|
||||
|
||||
require 'iconv' unless "".respond_to?("encode")
|
||||
|
||||
module CFPropertyList
|
||||
# Create CFType hierarchy by guessing the correct CFType, e.g.
|
||||
#
|
||||
# x = {
|
||||
# 'a' => ['b','c','d']
|
||||
# }
|
||||
# cftypes = CFPropertyList.guess(x)
|
||||
def guess(object, options = {})
|
||||
return if object.nil?
|
||||
|
||||
if(object.is_a?(Fixnum) || object.is_a?(Integer)) then
|
||||
return CFInteger.new(object)
|
||||
elsif(object.is_a?(Float)) then
|
||||
return CFReal.new(object)
|
||||
elsif(object.is_a?(TrueClass) || object.is_a?(FalseClass)) then
|
||||
return CFBoolean.new(object)
|
||||
elsif(object.is_a?(String)) then
|
||||
return CFString.new(object)
|
||||
elsif(object.is_a?(Time) || object.is_a?(DateTime)) then
|
||||
return CFDate.new(object)
|
||||
elsif(object.is_a?(IO)) then
|
||||
return CFData.new(object.read, CFData::DATA_RAW)
|
||||
elsif(object.is_a?(Array)) then
|
||||
ary = Array.new
|
||||
object.each do
|
||||
|o|
|
||||
ary.push CFPropertyList.guess(o, options)
|
||||
end
|
||||
|
||||
return CFArray.new(ary)
|
||||
elsif(object.is_a?(Hash)) then
|
||||
hsh = Hash.new
|
||||
object.each_pair do
|
||||
|k,v|
|
||||
k = k.to_s if k.is_a?(Symbol)
|
||||
hsh[k] = CFPropertyList.guess(v, options)
|
||||
end
|
||||
|
||||
return CFDictionary.new(hsh)
|
||||
elsif options[:convert_unknown_to_string]
|
||||
return CFString.new(object.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
# Converts a CFType hiercharchy to native Ruby types
|
||||
def native_types(object,keys_as_symbols=false)
|
||||
return if object.nil?
|
||||
|
||||
if(object.is_a?(CFDate) || object.is_a?(CFString) || object.is_a?(CFInteger) || object.is_a?(CFReal) || object.is_a?(CFBoolean)) then
|
||||
return object.value
|
||||
elsif(object.is_a?(CFData)) then
|
||||
return object.decoded_value
|
||||
elsif(object.is_a?(CFArray)) then
|
||||
ary = []
|
||||
object.value.each do
|
||||
|v|
|
||||
ary.push CFPropertyList.native_types(v)
|
||||
end
|
||||
|
||||
return ary
|
||||
elsif(object.is_a?(CFDictionary)) then
|
||||
hsh = {}
|
||||
object.value.each_pair do
|
||||
|k,v|
|
||||
k = k.to_sym if keys_as_symbols
|
||||
hsh[k] = CFPropertyList.native_types(v)
|
||||
end
|
||||
|
||||
return hsh
|
||||
end
|
||||
end
|
||||
|
||||
module_function :guess, :native_types
|
||||
|
||||
class List
|
||||
# Format constant for binary format
|
||||
FORMAT_BINARY = 1
|
||||
|
||||
# Format constant for XML format
|
||||
FORMAT_XML = 2
|
||||
|
||||
# Format constant for automatic format recognizing
|
||||
FORMAT_AUTO = 0
|
||||
|
||||
@@parsers = [Binary,XML]
|
||||
|
||||
# Path of PropertyList
|
||||
attr_accessor :filename
|
||||
# Path of PropertyList
|
||||
attr_accessor :format
|
||||
# the root value in the plist file
|
||||
attr_accessor :value
|
||||
|
||||
def initialize(opts={})
|
||||
@filename = opts[:file]
|
||||
@format = opts[:format] || FORMAT_AUTO
|
||||
@data = opts[:data]
|
||||
|
||||
load(@filename) unless @filename.nil?
|
||||
load_str(@data) unless @data.nil?
|
||||
end
|
||||
|
||||
# Load an XML PropertyList
|
||||
# filename = nil:: The filename to read from; if nil, read from the file defined by instance variable +filename+
|
||||
def load_xml(filename=nil)
|
||||
load(filename,List::FORMAT_XML)
|
||||
end
|
||||
|
||||
# read a binary plist file
|
||||
# filename = nil:: The filename to read from; if nil, read from the file defined by instance variable +filename+
|
||||
def load_binary(filename=nil)
|
||||
load(filename,List::FORMAT_BINARY)
|
||||
end
|
||||
|
||||
# load a plist from a XML string
|
||||
# str:: The string containing the plist
|
||||
def load_xml_str(str=nil)
|
||||
load_str(str,List::FORMAT_XML)
|
||||
end
|
||||
|
||||
# load a plist from a binary string
|
||||
# str:: The string containing the plist
|
||||
def load_binary_str(str=nil)
|
||||
load_str(str,List::FORMAT_BINARY)
|
||||
end
|
||||
|
||||
# load a plist from a string
|
||||
# str = nil:: The string containing the plist
|
||||
# format = nil:: The format of the plist
|
||||
def load_str(str=nil,format=nil)
|
||||
str = @data if str.nil?
|
||||
format = @format if format.nil?
|
||||
|
||||
@value = {}
|
||||
case format
|
||||
when List::FORMAT_BINARY, List::FORMAT_XML then
|
||||
prsr = @@parsers[format-1].new
|
||||
@value = prsr.load({:data => str})
|
||||
|
||||
when List::FORMAT_AUTO then # what we now do is ugly, but neccessary to recognize the file format
|
||||
filetype = str[0..5]
|
||||
version = str[6..7]
|
||||
|
||||
prsr = nil
|
||||
if filetype == "bplist" then
|
||||
raise CFFormatError.new("Wong file version #{version}") unless version == "00"
|
||||
prsr = Binary.new
|
||||
else
|
||||
prsr = XML.new
|
||||
end
|
||||
|
||||
@value = prsr.load({:data => str})
|
||||
end
|
||||
end
|
||||
|
||||
# Read a plist file
|
||||
# file = nil:: The filename of the file to read. If nil, use +filename+ instance variable
|
||||
# format = nil:: The format of the plist file. Auto-detect if nil
|
||||
def load(file=nil,format=nil)
|
||||
file = @filename if file.nil?
|
||||
format = @format if format.nil?
|
||||
@value = {}
|
||||
|
||||
raise IOError.new("File #{file} not readable!") unless File.readable? file
|
||||
|
||||
case format
|
||||
when List::FORMAT_BINARY, List::FORMAT_XML then
|
||||
prsr = @@parsers[format-1].new
|
||||
@value = prsr.load({:file => file})
|
||||
|
||||
when List::FORMAT_AUTO then # what we now do is ugly, but neccessary to recognize the file format
|
||||
magic_number = IO.read(file,8)
|
||||
filetype = magic_number[0..5]
|
||||
version = magic_number[6..7]
|
||||
|
||||
prsr = nil
|
||||
if filetype == "bplist" then
|
||||
raise CFFormatError.new("Wong file version #{version}") unless version == "00"
|
||||
prsr = Binary.new
|
||||
else
|
||||
prsr = XML.new
|
||||
end
|
||||
|
||||
@value = prsr.load({:file => file})
|
||||
end
|
||||
end
|
||||
|
||||
# Serialize CFPropertyList object to specified format and write it to file
|
||||
# file = nil:: The filename of the file to write to. Uses +filename+ instance variable if nil
|
||||
# format = nil:: The format to save in. Uses +format+ instance variable if nil
|
||||
def save(file=nil,format=nil,opts={})
|
||||
format = @format if format.nil?
|
||||
file = @filename if file.nil?
|
||||
|
||||
raise CFFormatError.new("Format #{format} not supported, use List::FORMAT_BINARY or List::FORMAT_XML") if format != FORMAT_BINARY && format != FORMAT_XML
|
||||
|
||||
if(!File.exists?(file)) then
|
||||
raise IOError.new("File #{file} not writable!") unless File.writable?(File.dirname(file))
|
||||
elsif(!File.writable?(file)) then
|
||||
raise IOError.new("File #{file} not writable!")
|
||||
end
|
||||
|
||||
opts[:root] = @value
|
||||
prsr = @@parsers[format-1].new
|
||||
content = prsr.to_str(opts)
|
||||
|
||||
File.open(file, 'wb') {
|
||||
|fd|
|
||||
fd.write content
|
||||
}
|
||||
end
|
||||
|
||||
# convert plist to string
|
||||
# format = List::FORMAT_BINARY:: The format to save the plist
|
||||
# opts={}:: Pass parser options
|
||||
def to_str(format=List::FORMAT_BINARY,opts={})
|
||||
prsr = @@parsers[format-1].new
|
||||
opts[:root] = @value
|
||||
return prsr.to_str(opts)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# eof
|
Reference in a new issue