Class: Ole::Types::PropertySet

Inherits:
Object
  • Object
show all
Includes:
Enumerable, Constants
Defined in:
lib/ole/types/property_set.rb

Overview

The PropertySet class currently supports readonly access to the properties serialized in “property set” streams, such as the file “005SummaryInformation”, in OLE files.

Think it has its roots in MFC property set serialization.

See poi.apache.org/hpsf/internals.html for details

Defined Under Namespace

Modules: Constants Classes: Section

Constant Summary collapse

HEADER_SIZE =
28
HEADER_PACK =
"vvVa#{Clsid::SIZE}V"
OS_MAP =
{
	0 => :win16,
	1 => :mac,
	2 => :win32,
	0x20001 => :ooffice, # open office on linux...
}
DATA =

define a smattering of the property set guids.

YAML.load_file(File.dirname(__FILE__) + '/../../../data/propids.yaml').
inject({}) { |hash, (key, value)| hash.update Clsid.parse(key) => value }
PROPERTY_MAP =

create an inverted map of names to guid/key pairs

DATA.inject({}) do |h1, (guid, data)|
	data[1].inject(h1) { |h2, (id, name)| h2.update name => [guid, id] }
end

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Enumerable

#group_by, #sum

Constructor Details

#initialize(io) ⇒ PropertySet

Returns a new instance of PropertySet.



106
107
108
109
110
111
112
# File 'lib/ole/types/property_set.rb', line 106

def initialize io
	@io = io
	load_header io.read(HEADER_SIZE)
	load_section_list io.read(@num_sections * Section::SIZE)
	# expect no gap between last section and start of data.
	#Log.warn "gap between section list and property data" unless io.pos == @sections.map(&:offset).min
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(name, *args, &block) ⇒ Object



137
138
139
140
141
142
143
144
145
146
147
# File 'lib/ole/types/property_set.rb', line 137

def method_missing name, *args, &block
	if name.to_s =~ /(.*)=$/
		return super unless args.length == 1
		return super unless PROPERTY_MAP[$1]
		self[$1] = args.first
	else
		return super unless args.length == 0
		return super unless PROPERTY_MAP[name.to_s]
		self[name]
	end
end

Instance Attribute Details

#guidObject (readonly)

Returns the value of attribute guid.



104
105
106
# File 'lib/ole/types/property_set.rb', line 104

def guid
  @guid
end

#ioObject (readonly)

Returns the value of attribute io.



104
105
106
# File 'lib/ole/types/property_set.rb', line 104

def io
  @io
end

#osObject (readonly)

Returns the value of attribute os.



104
105
106
# File 'lib/ole/types/property_set.rb', line 104

def os
  @os
end

#sectionsObject (readonly)

Returns the value of attribute sections.



104
105
106
# File 'lib/ole/types/property_set.rb', line 104

def sections
  @sections
end

#signatureObject (readonly)

Returns the value of attribute signature.



104
105
106
# File 'lib/ole/types/property_set.rb', line 104

def signature
  @signature
end

#unknownObject (readonly)

Returns the value of attribute unknown.



104
105
106
# File 'lib/ole/types/property_set.rb', line 104

def unknown
  @unknown
end

Instance Method Details

#[](key) ⇒ Object



125
126
127
128
129
# File 'lib/ole/types/property_set.rb', line 125

def [] key
	pair = PROPERTY_MAP[key.to_s] or return nil
	section = @sections.find { |s| s.guid == pair.first } or return nil
	section[pair.last]
end

#[]=(key, value) ⇒ Object



131
132
133
134
135
# File 'lib/ole/types/property_set.rb', line 131

def []= key, value
	pair = PROPERTY_MAP[key.to_s] or return nil
	section = @sections.find { |s| s.guid == pair.first } or return nil
	section[pair.last] = value
end

#eachObject



149
150
151
152
153
154
155
156
157
158
# File 'lib/ole/types/property_set.rb', line 149

def each
	@sections.each do |section|
		next unless pair = DATA[section.guid]
		map = pair.last
		section.each do |id, value|
			name = map[id] or next
			yield name, value
		end
	end
end

#load_header(str) ⇒ Object



114
115
116
117
118
119
# File 'lib/ole/types/property_set.rb', line 114

def load_header str
	@signature, @unknown, @os_id, @guid, @num_sections = str.unpack HEADER_PACK
	# should i check that unknown == 0? it usually is. so is the guid actually
	@guid = Clsid.load @guid
	@os = OS_MAP[@os_id] || Log.warn("unknown operating system id #{@os_id}")
end

#load_section_list(str) ⇒ Object



121
122
123
# File 'lib/ole/types/property_set.rb', line 121

def load_section_list str
	@sections = str.to_enum(:each_chunk, Section::SIZE).map { |s| Section.new s, self }
end

#to_hObject



160
161
162
# File 'lib/ole/types/property_set.rb', line 160

def to_h
	inject({}) { |hash, (name, value)| hash.update name.to_sym => value }
end