xbrlware-ruby19 1.1.2.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +4 -0
- data/Rakefile +2 -0
- data/lib/xbrlware-ruby19/cgi_patch.rb +58 -0
- data/lib/xbrlware-ruby19/constants.rb +29 -0
- data/lib/xbrlware-ruby19/context.rb +193 -0
- data/lib/xbrlware-ruby19/date_util.rb +45 -0
- data/lib/xbrlware-ruby19/float_patch.rb +26 -0
- data/lib/xbrlware-ruby19/hash_util.rb +175 -0
- data/lib/xbrlware-ruby19/instance.rb +475 -0
- data/lib/xbrlware-ruby19/item.rb +123 -0
- data/lib/xbrlware-ruby19/linkbase/calculation_linkbase.rb +181 -0
- data/lib/xbrlware-ruby19/linkbase/definition_linkbase.rb +226 -0
- data/lib/xbrlware-ruby19/linkbase/label_linkbase.rb +132 -0
- data/lib/xbrlware-ruby19/linkbase/linkbase.rb +193 -0
- data/lib/xbrlware-ruby19/linkbase/presentation_linkbase.rb +204 -0
- data/lib/xbrlware-ruby19/meta_util.rb +50 -0
- data/lib/xbrlware-ruby19/ns_aware.rb +5 -0
- data/lib/xbrlware-ruby19/taxonomies/ifrs_taxonomy_20090401.rb +8278 -0
- data/lib/xbrlware-ruby19/taxonomies/us_gaap_taxonomy_20090131.rb +40365 -0
- data/lib/xbrlware-ruby19/taxonomy.rb +143 -0
- data/lib/xbrlware-ruby19/unit.rb +43 -0
- data/lib/xbrlware-ruby19/util.rb +86 -0
- data/lib/xbrlware-ruby19/version.rb +22 -0
- data/lib/xbrlware-ruby19/xml_parser.rb +142 -0
- data/lib/xbrlware-ruby19.rb +73 -0
- data/xbrlware-ruby19.gemspec +27 -0
- metadata +109 -0
@@ -0,0 +1,475 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
#
|
3
|
+
# Author:: xbrlware@bitstat.com
|
4
|
+
#
|
5
|
+
# Copyright:: 2009, 2010 bitstat (http://www.bitstat.com). All Rights Reserved.
|
6
|
+
#
|
7
|
+
# License:: Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
16
|
+
# implied.
|
17
|
+
# See the License for the specific language governing permissions and
|
18
|
+
# limitations under the License.
|
19
|
+
#
|
20
|
+
module Xbrlware
|
21
|
+
|
22
|
+
# Class to deal with valid, well-formatted XBRl instance file.
|
23
|
+
# This class provides methods to deal with instance file.
|
24
|
+
# Look at {delaing with instance page on xbrlware wiki}[http://code.google.com/p/xbrlware/wiki/InstanceTaxonomy] for more details.
|
25
|
+
class Instance
|
26
|
+
|
27
|
+
attr_reader :taxonomy
|
28
|
+
|
29
|
+
# Creates an Instance.
|
30
|
+
#
|
31
|
+
# instance_str:: XBRL Instance source. Tries to load and parse instance file from the instance_str. This can be XBRL instance file from the file system or XBRL content string.
|
32
|
+
#
|
33
|
+
# taxonomy_filepath::
|
34
|
+
# optional parameter, XBRL Taxonomy source. Tries to load and parse taxonomy file from path.
|
35
|
+
# If this param is specified, taxonomy file in the instance document will be ignored
|
36
|
+
#
|
37
|
+
# Expects instance source is well-formatted and valid
|
38
|
+
# Sometimes instance document contains large chunk of HTML content with new lines in-between,
|
39
|
+
# which will cause parsing error. Hence any exceptions during instance source parsing, will trigger re-parsing of
|
40
|
+
# the entire instnace file with new lines replaced.
|
41
|
+
# Look at {delaing with instance page on xbrlware wiki}[http://code.google.com/p/xbrlware/wiki/InstanceTaxonomy] for details.
|
42
|
+
def initialize(instance_str, taxonomy_filepath=nil)
|
43
|
+
m=Benchmark.measure do
|
44
|
+
begin
|
45
|
+
@file_name=instance_str unless instance_str =~ /<.*?>/m
|
46
|
+
@xbrl_content = XmlParser.xml_in(instance_str, {'ForceContent' => true})
|
47
|
+
rescue Exception
|
48
|
+
new_content=nil
|
49
|
+
if instance_str =~ /<.*?>/m
|
50
|
+
$LOG.warn "Supplied XBRL content is not well formed. Starting reparsing after removing new lines."
|
51
|
+
new_content=instance_str.gsub("\n", "")
|
52
|
+
else
|
53
|
+
$LOG.warn "File ["+instance_str+"] is not well formed. Starting reparsing after removing new lines."
|
54
|
+
new_content=File.open(instance_str).read.gsub("\n", "")
|
55
|
+
end
|
56
|
+
@xbrl_content = XmlParser.xml_in(new_content, {'ForceContent' => true}) unless new_content.nil?
|
57
|
+
end
|
58
|
+
end
|
59
|
+
bm("Parsing [" + instance_str + "] took", m)
|
60
|
+
|
61
|
+
# if taxonomy file is not supplied, get it from instance schema_ref
|
62
|
+
if taxonomy_filepath.nil?
|
63
|
+
taxonomy_file_location=File.dirname(instance_str)+File::Separator+schema_ref
|
64
|
+
taxonomy_filepath = taxonomy_file_location if File.exist?(taxonomy_file_location) && (not File.directory?(taxonomy_file_location))
|
65
|
+
end
|
66
|
+
|
67
|
+
@taxonomy=Xbrlware::Taxonomy.new(taxonomy_filepath, self)
|
68
|
+
@entity_details=Hash.new("UNKNOWN")
|
69
|
+
end
|
70
|
+
|
71
|
+
# Returns raw content of instance file in the form of Hash
|
72
|
+
def raw
|
73
|
+
@xbrl_content
|
74
|
+
end
|
75
|
+
|
76
|
+
# Returns schemaRef element of instance file. schemaRef holds path to taxonomy file for the instance file.
|
77
|
+
def schema_ref
|
78
|
+
base = @xbrl_content["schemaRef"][0]["xml:base"]
|
79
|
+
href = @xbrl_content["schemaRef"][0]["xlink:href"]
|
80
|
+
return base + href if base
|
81
|
+
href
|
82
|
+
end
|
83
|
+
|
84
|
+
# Takes optional context_id as string and dimensions as array
|
85
|
+
# Returns all contexts when context_id is nil
|
86
|
+
# Returns instance of Context object if context_id or dimensions is passed and matching context exist
|
87
|
+
# Returns nil if context_id or dimensions is passed and no matching context exist
|
88
|
+
def context(context_id=nil, dimensions=[])
|
89
|
+
all_contexts= context_by_id(context_id)
|
90
|
+
|
91
|
+
return all_contexts if dimensions.size==0
|
92
|
+
|
93
|
+
contexts=[]
|
94
|
+
|
95
|
+
all_contexts=[all_contexts] unless all_contexts.is_a?(Array)
|
96
|
+
all_contexts.each do |ctx|
|
97
|
+
next unless ctx.has_explicit_dimensions?(dimensions)
|
98
|
+
contexts << ctx
|
99
|
+
end
|
100
|
+
|
101
|
+
return contexts[0] unless context_id.nil?
|
102
|
+
contexts
|
103
|
+
end
|
104
|
+
|
105
|
+
# Returns contexts grouped by dimension as map. Map contains dimension as key and corresponding contexts as value
|
106
|
+
def ctx_groupby_dim
|
107
|
+
dim_group={}
|
108
|
+
all_contexts= context
|
109
|
+
all_contexts=[all_contexts] unless all_contexts.is_a?(Array)
|
110
|
+
all_contexts.each do |ctx|
|
111
|
+
ctx.explicit_dimensions.each do |dim|
|
112
|
+
dim_group[dim] = [] if dim_group[dim].nil?
|
113
|
+
dim_group[dim] << ctx
|
114
|
+
end
|
115
|
+
end
|
116
|
+
dim_group
|
117
|
+
end
|
118
|
+
|
119
|
+
# Takes optional dimensions as array
|
120
|
+
# Returns contexts group by domain as map. Map contains domain as key and corresponding contexts as value
|
121
|
+
def ctx_groupby_dom(dimensions=[])
|
122
|
+
dom_group={}
|
123
|
+
all_contexts= context(nil, dimensions)
|
124
|
+
all_contexts=[all_contexts] unless all_contexts.is_a?(Array)
|
125
|
+
all_contexts.each do |ctx|
|
126
|
+
ctx.explicit_domains(dimensions).each do |dom|
|
127
|
+
dom_group[dom] = [] if dom_group[dom].nil?
|
128
|
+
dom_group[dom] << ctx
|
129
|
+
end
|
130
|
+
end
|
131
|
+
dom_group
|
132
|
+
end
|
133
|
+
|
134
|
+
# Takes optional dimensions as array
|
135
|
+
# Returns contexts group by period as map. Map contains period as key and corresponding contexts as value
|
136
|
+
def ctx_groupby_period(dimensions=[])
|
137
|
+
period_group={}
|
138
|
+
all_contexts= context(nil, dimensions)
|
139
|
+
all_contexts=[all_contexts] unless all_contexts.is_a?(Array)
|
140
|
+
all_contexts.each do |ctx|
|
141
|
+
period_group[ctx.period] = [] if period_group[ctx.period].nil?
|
142
|
+
period_group[ctx.period] << ctx
|
143
|
+
end
|
144
|
+
period_group
|
145
|
+
end
|
146
|
+
|
147
|
+
# Prints dimension -> domain -> context relationship for all contexts in the console.
|
148
|
+
def ctx_groupby_dim_dom_print
|
149
|
+
group_dim=ctx_groupby_dim
|
150
|
+
group_dim.keys.each do |dimension|
|
151
|
+
puts " dimension :: " + dimension
|
152
|
+
group_dom=ctx_groupby_dom(dimension)
|
153
|
+
group_dom.keys.each do |domain|
|
154
|
+
puts " \t domain :: " + domain
|
155
|
+
group_dom[domain].each do |ctx|
|
156
|
+
puts " \t\t ctx :: " + ctx.id
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def inspect
|
163
|
+
self.to_s
|
164
|
+
end
|
165
|
+
|
166
|
+
private
|
167
|
+
# Takes optional context_id
|
168
|
+
# Returns all contexts if context_id is nil
|
169
|
+
# Returns matching context as Context object if context_id is given and matching context found
|
170
|
+
# Returns nil if context_id is given and no matching context found
|
171
|
+
def context_by_id(context_id=nil)
|
172
|
+
if context_id.nil?
|
173
|
+
contexts=[]
|
174
|
+
@xbrl_content["context"].each {|c| contexts << to_ctx_obj(c) }
|
175
|
+
return contexts
|
176
|
+
end
|
177
|
+
|
178
|
+
ctx_content=nil
|
179
|
+
@xbrl_content["context"].each { |ctx| ctx_content=ctx if ctx["id"]==context_id}
|
180
|
+
$LOG.warn " unable to find context for id [" + context_id+"]" if ctx_content.nil?
|
181
|
+
return nil if ctx_content.nil?
|
182
|
+
return to_ctx_obj(ctx_content)
|
183
|
+
|
184
|
+
end
|
185
|
+
|
186
|
+
# Creates Context object from context content of XBRL instance file
|
187
|
+
def to_ctx_obj (ctx_content)
|
188
|
+
id=ctx_content["id"]
|
189
|
+
|
190
|
+
entity_content = ctx_content["entity"][0]
|
191
|
+
e = entity(entity_content)
|
192
|
+
|
193
|
+
period_content = ctx_content["period"][0]
|
194
|
+
p = period(period_content)
|
195
|
+
|
196
|
+
s = scenario(ctx_content)
|
197
|
+
_context = Context.new(id, e, p, s)
|
198
|
+
_context.ns=ctx_content["nspace"]
|
199
|
+
_context.nsp=ctx_content["nspace_prefix"]
|
200
|
+
return _context
|
201
|
+
end
|
202
|
+
|
203
|
+
# Returns map if period is duration. Map has key with name "start_date" and "end_date"
|
204
|
+
# Returns string if period is instant
|
205
|
+
# Returns -1 if period is forever
|
206
|
+
def period(period_content)
|
207
|
+
if period_content["startDate"] && period_content["endDate"]
|
208
|
+
return {"start_date" => Date.parse(period_content["startDate"][0]["content"]), "end_date" => Date.parse(period_content["endDate"][0]["content"])}
|
209
|
+
elsif period_content["instant"]
|
210
|
+
return Date.parse(period_content["instant"][0]["content"])
|
211
|
+
elsif period_content["forever"]
|
212
|
+
return Context::PERIOD_FOREVER
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
# Returns Entity object
|
217
|
+
def entity(entity_content)
|
218
|
+
entity_identifier = Identifier.new(entity_content["identifier"][0]["scheme"], entity_content["identifier"][0]["content"].strip!)
|
219
|
+
|
220
|
+
entity_segment_content=entity_content["segment"]
|
221
|
+
entity_segment=nil
|
222
|
+
unless entity_segment_content.nil?
|
223
|
+
entity_segment=entity_segment_content[0]
|
224
|
+
end
|
225
|
+
|
226
|
+
return Entity.new(entity_identifier, entity_segment)
|
227
|
+
end
|
228
|
+
|
229
|
+
# Returns scenario content
|
230
|
+
def scenario(ctx_content)
|
231
|
+
s=nil
|
232
|
+
unless ctx_content["scenario"].nil?
|
233
|
+
s = ctx_content["scenario"][0]
|
234
|
+
end
|
235
|
+
return s
|
236
|
+
end
|
237
|
+
|
238
|
+
public
|
239
|
+
# Takes optional unit_id
|
240
|
+
# Returns all units if unit_id is nil
|
241
|
+
# Returns matching unit as Unit object if unit_id is given and matching unit found
|
242
|
+
# Returns nil if unit_id is given and no matching unit found
|
243
|
+
def unit(unit_id=nil)
|
244
|
+
unit_content = @xbrl_content["unit"]
|
245
|
+
return nil if unit_content.nil?
|
246
|
+
|
247
|
+
units=[]
|
248
|
+
|
249
|
+
l = lambda {|measure_list| measures=[]; measure_list.each { |measure| measures << measure["content"]}; return measures}
|
250
|
+
unit_content.each do |unit|
|
251
|
+
|
252
|
+
next unless unit_id.nil? || unit["id"].to_s == unit_id
|
253
|
+
|
254
|
+
_unit=nil
|
255
|
+
unless unit["measure"].nil?
|
256
|
+
_unit = Unit.new(unit["id"], l.call(unit["measure"]))
|
257
|
+
else
|
258
|
+
divide_content = unit["divide"][0]
|
259
|
+
|
260
|
+
numerator = l.call(divide_content["unitNumerator"][0]["measure"])
|
261
|
+
denominator = l.call(divide_content["unitDenominator"][0]["measure"])
|
262
|
+
|
263
|
+
divide=Unit::Divide.new(numerator, denominator)
|
264
|
+
_unit = Unit.new(unit["id"], divide)
|
265
|
+
end
|
266
|
+
_unit.ns=unit["nspace"]
|
267
|
+
_unit.nsp=unit["nspace_prefix"]
|
268
|
+
units << _unit
|
269
|
+
end
|
270
|
+
return units[0] unless unit_id.nil?
|
271
|
+
units
|
272
|
+
end
|
273
|
+
|
274
|
+
private
|
275
|
+
def to_item_obj(item, name)
|
276
|
+
context, unit, precision, decimals, _footnotes=nil
|
277
|
+
|
278
|
+
context = context(item["contextRef"]) unless item["contextRef"].nil?
|
279
|
+
value = item["content"]
|
280
|
+
|
281
|
+
unit = unit(item["unitRef"]) unless item["unitRef"].nil?
|
282
|
+
precision = item["precision"] unless item["precision"].nil?
|
283
|
+
decimals = item["decimals"] unless item["decimals"].nil?
|
284
|
+
|
285
|
+
_footnotes = footnotes(item["id"]) unless item["id"].nil?
|
286
|
+
_item=Item.new(self, name, context, value, unit, precision, decimals, _footnotes)
|
287
|
+
_item.ns=item["nspace"]
|
288
|
+
_item.nsp=item["nspace_prefix"]
|
289
|
+
_item.def=@taxonomy.definition(name)
|
290
|
+
return _item
|
291
|
+
end
|
292
|
+
|
293
|
+
public
|
294
|
+
def item_all
|
295
|
+
|
296
|
+
return @item_all unless @item_all.nil?
|
297
|
+
|
298
|
+
all_items = @xbrl_content
|
299
|
+
return nil if all_items.nil?
|
300
|
+
|
301
|
+
@item_all=[]
|
302
|
+
|
303
|
+
all_items.each do |name, item_content|
|
304
|
+
next unless item_content.is_a?(Array)
|
305
|
+
next if item_content.size > 0 && item_content[0]["contextRef"].nil?
|
306
|
+
@item_all = @item_all + item(name)
|
307
|
+
end
|
308
|
+
@item_all
|
309
|
+
end
|
310
|
+
|
311
|
+
def item_all_map
|
312
|
+
items=item_all
|
313
|
+
return nil if items.nil?
|
314
|
+
|
315
|
+
items_hash={}
|
316
|
+
|
317
|
+
items.each do |item|
|
318
|
+
_name= item.name.upcase
|
319
|
+
items_hash[_name] = [] unless items_hash.include?(_name)
|
320
|
+
items_hash[_name] << item
|
321
|
+
end
|
322
|
+
items_hash
|
323
|
+
end
|
324
|
+
|
325
|
+
# Takes name and optional context_ref and unit_ref
|
326
|
+
# Returns array of Item for given name, context_ref and unit_ref
|
327
|
+
# Returns empty array if item is not found
|
328
|
+
def item(name, context_ref=nil, unit_ref=nil)
|
329
|
+
|
330
|
+
item_content = @xbrl_content[name]
|
331
|
+
|
332
|
+
return [] if item_content.nil?
|
333
|
+
|
334
|
+
items=[]
|
335
|
+
|
336
|
+
item_content.each do |item|
|
337
|
+
|
338
|
+
next unless context_ref.nil? || context_ref == item["contextRef"]
|
339
|
+
next unless unit_ref.nil? || unit_ref == item["unitRef"]
|
340
|
+
|
341
|
+
item = to_item_obj(item, name)
|
342
|
+
items << item
|
343
|
+
end
|
344
|
+
items
|
345
|
+
end
|
346
|
+
|
347
|
+
# Takes item name
|
348
|
+
# Returns array of contexts for given item name
|
349
|
+
# Returns empty array if no item with given name found
|
350
|
+
def context_for_item(item_name)
|
351
|
+
contexts=[]
|
352
|
+
items = item(item_name)
|
353
|
+
items.each {|item| contexts << item.context}
|
354
|
+
return contexts
|
355
|
+
end
|
356
|
+
|
357
|
+
|
358
|
+
# Takes item name and filter block
|
359
|
+
# Fetches item with name and invokes filter block with item context
|
360
|
+
# Returns matched items.
|
361
|
+
def item_ctx_filter(name, &context_filter_block)
|
362
|
+
items=item(name)
|
363
|
+
return items if context_filter_block.nil?
|
364
|
+
filtered_items=[]
|
365
|
+
items.each do |item|
|
366
|
+
filtered_items << item if yield(item.context)
|
367
|
+
end
|
368
|
+
filtered_items
|
369
|
+
end
|
370
|
+
|
371
|
+
public
|
372
|
+
# Takes optional item id and language
|
373
|
+
# Every item in XBRL instance file may contain optional id element.
|
374
|
+
# Footnotes is associated with id of the item. Footnotes may be in different languages.
|
375
|
+
# Returns Map with lang as key and corresponding footnotes in array as value if item_id is givien
|
376
|
+
# Returns Map with item_id as key and another Map as value.
|
377
|
+
# Second map has lang as key and corresponding footnotes in array as value if item_id is givien
|
378
|
+
# Returns nil if no match found for item_it or footnotes not exist
|
379
|
+
def footnotes (item_id=nil, lang=nil)
|
380
|
+
@item_footnote_map=nil
|
381
|
+
raise " lang can't be passed when item id is nil" if item_id.nil? && (not lang.nil?)
|
382
|
+
@item_footnote_map = compute_footnotes if @item_footnote_map.nil?
|
383
|
+
return nil if @item_footnote_map.nil?
|
384
|
+
return @item_footnote_map[item_id] if (not item_id.nil?) && lang.nil?
|
385
|
+
return @item_footnote_map[item_id][lang] unless item_id.nil? || lang.nil?
|
386
|
+
@item_footnote_map
|
387
|
+
end
|
388
|
+
|
389
|
+
def entity_details=(value)
|
390
|
+
@entity_details.merge!(value) if value.is_a?(Hash)
|
391
|
+
end
|
392
|
+
|
393
|
+
def entity_details
|
394
|
+
if @entity_details.size==0
|
395
|
+
begin
|
396
|
+
# Specific to US filing
|
397
|
+
e_name=item("EntityRegistrantName")[0]
|
398
|
+
e_ci_key=item("EntityCentralIndexKey")[0]
|
399
|
+
e_doc_type=item("DocumentType")[0]
|
400
|
+
e_doc_end_type=item("DocumentPeriodEndDate")[0]
|
401
|
+
|
402
|
+
fedate=item("CurrentFiscalYearEndDate")
|
403
|
+
e_fiscal_end_date=fedate[0] unless fedate.nil?
|
404
|
+
|
405
|
+
shares_outstanding = item("EntityCommonStockSharesOutstanding")
|
406
|
+
e_common_shares_outstanding=shares_outstanding[0] unless shares_outstanding.nil?
|
407
|
+
|
408
|
+
@entity_details["name"]=e_name.value unless e_name.nil?
|
409
|
+
@entity_details["ci_key"]=e_ci_key.value unless e_ci_key.nil?
|
410
|
+
@entity_details["doc_type"]=e_doc_type.value unless e_doc_type.nil?
|
411
|
+
@entity_details["doc_end_date"]=e_doc_end_type.value unless e_doc_end_type.nil?
|
412
|
+
@entity_details["fiscal_end_date"]=e_fiscal_end_date.value unless e_fiscal_end_date.nil?
|
413
|
+
@entity_details["common_shares_outstanding"]=e_common_shares_outstanding.value unless e_common_shares_outstanding.nil?
|
414
|
+
|
415
|
+
unless @file_name.nil?
|
416
|
+
file_name=File.basename(@file_name)
|
417
|
+
symbol=file_name.split("-")[0]
|
418
|
+
symbol.upcase!
|
419
|
+
|
420
|
+
@entity_details["symbol"]=symbol unless symbol.nil?
|
421
|
+
end
|
422
|
+
rescue Exception => e
|
423
|
+
@entity_details
|
424
|
+
end
|
425
|
+
end
|
426
|
+
@entity_details
|
427
|
+
end
|
428
|
+
|
429
|
+
private
|
430
|
+
def compute_footnotes()
|
431
|
+
return nil if @xbrl_content["footnoteLink"].nil?
|
432
|
+
item_map={}
|
433
|
+
@xbrl_content["footnoteLink"][0]["loc"].each do |loc|
|
434
|
+
item_map[loc["xlink:label"]]=[] if item_map[loc["xlink:label"]].nil?
|
435
|
+
item_map[loc["xlink:label"]] << loc["xlink:href"].split("#")[-1]
|
436
|
+
end unless @xbrl_content["footnoteLink"][0]["loc"].nil?
|
437
|
+
|
438
|
+
footnote_map = {}
|
439
|
+
@xbrl_content["footnoteLink"][0]["footnote"].each do |fn|
|
440
|
+
label=fn["xlink:label"]
|
441
|
+
lang=fn["xml:lang"]
|
442
|
+
content=fn["content"]
|
443
|
+
|
444
|
+
footnote_map[label]={} if footnote_map[label].nil?
|
445
|
+
footnote_map[label][lang]=content
|
446
|
+
end unless @xbrl_content["footnoteLink"][0]["footnote"].nil?
|
447
|
+
|
448
|
+
label_to_footnote_map = {}
|
449
|
+
@xbrl_content["footnoteLink"][0]["footnoteArc"].each do |fn_arc|
|
450
|
+
label_to_footnote_map[fn_arc["xlink:from"]] = [] if label_to_footnote_map[fn_arc["xlink:from"]].nil?
|
451
|
+
label_to_footnote_map[fn_arc["xlink:from"]] << fn_arc["xlink:to"]
|
452
|
+
end unless @xbrl_content["footnoteLink"][0]["footnoteArc"].nil?
|
453
|
+
|
454
|
+
return nil if label_to_footnote_map.size==0
|
455
|
+
|
456
|
+
item_footnote_map ={}
|
457
|
+
label_to_footnote_map.each do |item_label, fn_labels|
|
458
|
+
item_ids=item_map[item_label]
|
459
|
+
item_ids.each do |item_id|
|
460
|
+
item_footnote_map[item_id] = {} if item_footnote_map[item_id].nil?
|
461
|
+
map=item_footnote_map[item_id]
|
462
|
+
fn_labels.each do |fn_lab|
|
463
|
+
fn=footnote_map[fn_lab]
|
464
|
+
fn.each do |lang, content|
|
465
|
+
map[lang]=[] if map[lang].nil?
|
466
|
+
map[lang] << content
|
467
|
+
end
|
468
|
+
end unless fn_labels.nil?
|
469
|
+
end unless item_ids.nil?
|
470
|
+
end
|
471
|
+
item_footnote_map
|
472
|
+
end
|
473
|
+
|
474
|
+
end
|
475
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
#
|
3
|
+
# Author:: xbrlware@bitstat.com
|
4
|
+
#
|
5
|
+
# Copyright:: 2009, 2010 bitstat (http://www.bitstat.com). All Rights Reserved.
|
6
|
+
#
|
7
|
+
# License:: Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
16
|
+
# implied.
|
17
|
+
# See the License for the specific language governing permissions and
|
18
|
+
# limitations under the License.
|
19
|
+
#
|
20
|
+
module Xbrlware
|
21
|
+
|
22
|
+
# This class represents each item in the XBRL instance file.
|
23
|
+
# Taxonomy definition of a item can be retrieved using def or meta methods.
|
24
|
+
# Look at {delaing with instance page on xbrlware wiki}[http://code.google.com/p/xbrlware/wiki/InstanceTaxonomy] for more details.
|
25
|
+
class Item
|
26
|
+
include NSAware
|
27
|
+
|
28
|
+
attr_reader :name, :context, :unit, :precision, :decimals, :footnotes
|
29
|
+
attr_accessor :ins, :def
|
30
|
+
|
31
|
+
# Constructs item
|
32
|
+
# value is normalized based on precision and decimals passed as per XBRL specification
|
33
|
+
def initialize(instance, name, context, value, unit=nil, precision=nil, decimals=nil, footnotes=nil)
|
34
|
+
@ins=instance
|
35
|
+
@name=name
|
36
|
+
@context = context
|
37
|
+
@precision=precision
|
38
|
+
@decimals=decimals
|
39
|
+
@footnotes=footnotes
|
40
|
+
@value = ItemValue.new(value, precision, decimals).value
|
41
|
+
@unit = unit
|
42
|
+
end
|
43
|
+
|
44
|
+
def value
|
45
|
+
return yield(@value) if block_given?
|
46
|
+
@value
|
47
|
+
end
|
48
|
+
|
49
|
+
def is_value_numeric?
|
50
|
+
@value.to_i.to_s == @value || @value.to_f.to_s == @value
|
51
|
+
end
|
52
|
+
|
53
|
+
alias_method :meta, :def
|
54
|
+
|
55
|
+
def balance
|
56
|
+
_balance=meta["xbrli:balance"] unless meta.nil?
|
57
|
+
_balance.nil? ? "" : _balance
|
58
|
+
end
|
59
|
+
|
60
|
+
class ItemValue # :nodoc:
|
61
|
+
|
62
|
+
attr_reader :item_value
|
63
|
+
|
64
|
+
def initialize(item_value, precision=nil, decimals=nil)
|
65
|
+
@item_value=item_value
|
66
|
+
@precision=precision
|
67
|
+
@decimals=decimals
|
68
|
+
end
|
69
|
+
|
70
|
+
def value()
|
71
|
+
return precision() unless @precision.nil?
|
72
|
+
return decimals() unless @decimals.nil?
|
73
|
+
return @item_value
|
74
|
+
end
|
75
|
+
|
76
|
+
# returns BigDecimal float representation as String
|
77
|
+
def precision()
|
78
|
+
return @item_value if @precision=="INF"
|
79
|
+
|
80
|
+
precision_i=@precision.to_i
|
81
|
+
new_value=BigDecimal(@item_value)
|
82
|
+
|
83
|
+
is_value_integer = new_value==@item_value.to_i
|
84
|
+
|
85
|
+
|
86
|
+
return to_precision_from_integer(@item_value.to_i, precision_i) if is_value_integer
|
87
|
+
|
88
|
+
index_of_dot = new_value.abs.to_s("F").index(".")
|
89
|
+
|
90
|
+
# When mod value is greater than 1 and float number
|
91
|
+
if new_value.abs > 1
|
92
|
+
#Precision is less than number of digits before decimal
|
93
|
+
return to_precision_from_integer(new_value.to_i, precision_i) if precision_i <= index_of_dot
|
94
|
+
|
95
|
+
#Precision is greater than number of digits before decimal
|
96
|
+
return new_value.round(precision_i-index_of_dot).to_s("F")
|
97
|
+
else
|
98
|
+
new_value_s = new_value.abs.to_s("F")
|
99
|
+
no_of_zeroes = new_value_s.split(/[1-9].*/).join.length - new_value_s.index(".")-1
|
100
|
+
return new_value.round(no_of_zeroes + precision_i).to_s("F")
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# returns BigDecimal float representation as String
|
105
|
+
def decimals
|
106
|
+
return @item_value if @decimals=="INF"
|
107
|
+
return BigDecimal(@item_value).round(@decimals.to_i).to_s("F")
|
108
|
+
end
|
109
|
+
|
110
|
+
private
|
111
|
+
def to_precision_from_integer(new_value, precision_i)
|
112
|
+
factor=10 **(new_value.abs.to_s.length - precision_i)
|
113
|
+
return to_big_decimal_float_str(((BigDecimal(new_value.to_s) / factor).to_i * factor).to_s)
|
114
|
+
end
|
115
|
+
|
116
|
+
def to_big_decimal_float_str(value)
|
117
|
+
return BigDecimal(value).to_s("F")
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|