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.



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

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



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

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.



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

def guid
  @guid
end

#ioObject (readonly)

Returns the value of attribute io.



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

def io
  @io
end

#osObject (readonly)

Returns the value of attribute os.



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

def os
  @os
end

#sectionsObject (readonly)

Returns the value of attribute sections.



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

def sections
  @sections
end

#signatureObject (readonly)

Returns the value of attribute signature.



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

def signature
  @signature
end

#unknownObject (readonly)

Returns the value of attribute unknown.



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

def unknown
  @unknown
end

Instance Method Details

#[](key) ⇒ Object



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

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



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

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



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

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



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

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



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

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

#to_hObject



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

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