Class: PositionRange::List
- Inherits:
-
Array
- Object
- Array
- PositionRange::List
- Defined in:
- lib/position_range/list.rb
Constant Summary collapse
- CHECK_POSITION_RANGE_LIST_RE =
Check-regexps
/^(#{PositionRange::BLOCK_POSITION_RANGE}(\:#{PositionRange::BLOCK_POSITION_RANGE})*)?$/
Class Method Summary collapse
-
.around(string) ⇒ Object
Returns a new PositionRangeList for the provided string, covering it from start to end (the ‘string’ can also be an array).
-
.from_s(position_range_list_string, pass_on_options = {}) ⇒ Object
Parses a list of PositionRanges from a string.
Instance Method Summary collapse
-
#&(other) ⇒ Object
Applies an intersection in the sense of Set theory.
-
#-(other) ⇒ Object
Applies a substraction in the sense of Set theory.
-
#align_chunks!(other_ranges) ⇒ Object
Ensures that the other list and this list don’t have any overlapping chunks, considering their size.
-
#apply_to_string(string, options = {}) ⇒ Object
Returns a new string containing only the parts of the old string designated by position_ranges.
-
#below?(size) ⇒ Boolean
Returns true if all PositionRanges in this list don’t refer to positions bigger than size.
-
#cluster_overlaps ⇒ Object
Adds all items to a cluster-array, where overlapping PositionRanges are added to the same cluster_array position.
-
#delete(p_r) ⇒ Object
Deletion returning a new list.
-
#delete!(p_r) ⇒ Object
Deletes the position_range that is specified.
-
#index(position_range, options = {}) ⇒ Object
Returns the index of the given PositionRange.
-
#insert_at_ranges(ranges_to_insert, ranges_at_which_to_insert, ranges_to_skip = []) ⇒ Object
Inserting at ranges returning a new list.
-
#insert_at_ranges!(ranges_to_insert, ranges_at_which_to_insert, ranges_to_skip = []) ⇒ Object
The ranges_to_insert are inserted at the ranges_at_which_to_insert of this list, counted in range_size from it’s beginning, and inter- luded with ranges_to_skip.
-
#invert(maximum_size = PositionRange::MaximumSize) ⇒ Object
Inversion returning a new list.
-
#invert!(maximum_size = PositionRange::MaximumSize) ⇒ Object
Results in all positions being included, being excluded now, and all positions that were excluded, being included now, upto the range below maximum_size.
-
#line_up_overlaps ⇒ Object
Lining up overlaps returning a new list.
-
#line_up_overlaps! ⇒ Object
Makes sure that there are no overlapping borders between PositionRanges.
-
#merge_adjacents(options = {}) ⇒ Object
Merging adjacents returning a new list.
-
#merge_adjacents!(options = {}) ⇒ Object
Simplifies the PositionRange::List by merging adjacent PositionRanges.
-
#range_size ⇒ Object
Returns the combined size of the ranges in this list.
-
#stack_adjacent(options = {}) ⇒ Object
Stacks the PositionRanges in the List adjacent in a new PositionRange::List, while maintaining their size.
-
#substract(other, options = {}) ⇒ Object
Substraction returning a new list.
-
#substract!(other, options = {}) ⇒ Object
Applies a substraction in the sense of Set theory.
-
#to_s ⇒ Object
Parses a PositionRange::List to a string.
-
#translate(integer) ⇒ Object
Translation returning a new list.
-
#translate!(integer) ⇒ Object
Translates the PositionRange::List in space, along the given vector.
-
#translate_from_view(view_position_range_list) ⇒ Object
Translates the PositionRange::List into absolute space.
-
#translate_to_view(view_position_range_list) ⇒ Object
Translates the PositionRange::List into the relative space defined by the view_position_range_list.
-
#within?(other) ⇒ Boolean
Returns true if all PositionRanges in this list fall within the PositionRanges in the given other PositionRange::List.
Class Method Details
.around(string) ⇒ Object
Returns a new PositionRangeList for the provided string, covering it from start to end (the ‘string’ can also be an array).
40 41 42 43 44 45 46 |
# File 'lib/position_range/list.rb', line 40 def self.around(string) if string.size > 0 return PositionRange::List.new([PositionRange.new(0,string.size)]) else return PositionRange::List.new end end |
.from_s(position_range_list_string, pass_on_options = {}) ⇒ Object
Parses a list of PositionRanges from a string.
Syntax: <position range string>[:<position range string>]*
Options: The argument pass_on_options allows you to give options to be passed on to the PositionRanges created from the string
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
# File 'lib/position_range/list.rb', line 19 def self.from_s(position_range_list_string, = {}) if position_range_list_string if position_range_list_string !~ CHECK_POSITION_RANGE_LIST_RE raise StandardError.new(), 'Invalid position_range_list string given: ' + position_range_list_string end p_r_l = PositionRange::List.new p_r_s_arr = position_range_list_string.split(':') p_r_s_arr.each {|p_r_s| p_r_l.push(PositionRange.from_s(p_r_s, )) } return p_r_l else return PositionRange::List.new end end |
Instance Method Details
#&(other) ⇒ Object
Applies an intersection in the sense of Set theory.
All PositionRanges and parts of PositionRanges that fall outside the PositionRanges given in the intersection_list are removed.
Example: 1,5:7,8:10,12’ becomes ‘2,5:11,12’ after limiting to ‘2,6:11,40’
116 117 118 119 |
# File 'lib/position_range/list.rb', line 116 def &(other) substraction_list = other.invert return self.substract(substraction_list, :ignore_attributes => true) end |
#-(other) ⇒ Object
Applies a substraction in the sense of Set theory.
See substract
125 126 127 |
# File 'lib/position_range/list.rb', line 125 def -(other) self.substract(other) end |
#align_chunks!(other_ranges) ⇒ Object
Ensures that the other list and this list don’t have any overlapping chunks, considering their size.
So PositionRange::List.from_s(‘10,20:50,70’).align_chunks!(
PositionRange::List.from_s('20,30:200,210,550,560'))
will result in: PositionRange::List.from_s(‘10,20:50,60:60,70’)
428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 |
# File 'lib/position_range/list.rb', line 428 def align_chunks!(other_ranges) i = -1 self_p = 0 other_p = 0 other_ranges.each {|p_r| i += 1 if !self[i] return self end other_p += p_r.size self_p += self[i].size if self_p > other_p copy = self[i] cut = self[i].begin + p_r.size self[i] = copy.new_dup(copy.begin, cut) self.insert(i + 1, copy.new_dup(cut, copy.end)) self_p = other_p end } return self end |
#apply_to_string(string, options = {}) ⇒ Object
Returns a new string containing only the parts of the old string designated by position_ranges.
Appends the string in the order in which they are found in this list.
Options :separator
=> The string to insert between the parts
559 560 561 562 563 564 565 566 567 568 569 |
# File 'lib/position_range/list.rb', line 559 def apply_to_string(string, = {}) separator = [:separator] || '' new_string = '' self.each {|p_r| if p_r.end > string.size raise StandardError, 'End-range bigger than string' end new_string += string[p_r] + separator } return new_string[0..-1 - separator.size] end |
#below?(size) ⇒ Boolean
Returns true if all PositionRanges in this list don’t refer to positions bigger than size. Otherwise false.
Attributes are ignored.
69 70 71 72 |
# File 'lib/position_range/list.rb', line 69 def below?(size) return self.within?( PositionRange::List.new([PositionRange.new(0,size)])) end |
#cluster_overlaps ⇒ Object
Adds all items to a cluster-array, where overlapping PositionRanges are added to the same cluster_array position.
So PositionRange::List.from_s(‘1,2:1,2:10,18:14,18’).cluster_overlaps will get you a cluster arr equal to the following:
[PositionRange::List.from_s(‘1,2:1,2’),
PositionRange::List.from_s('10,14'),
PositionRange::List.from_s('14,18:14,18')]
Except that the pointer_attributes are of course kept in order
533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 |
# File 'lib/position_range/list.rb', line 533 def cluster_overlaps if !self.empty? lined_up_self = self.line_up_overlaps clusters = [PositionRange::List.new().push(lined_up_self.shift)] lined_up_self.each {|p_r| if p_r == clusters.last[0] clusters.last.push(p_r) else clusters.push(PositionRange::List.new([p_r])) end } return clusters else return self.dup end end |
#delete(p_r) ⇒ Object
Deletion returning a new list.
See delete!
202 203 204 |
# File 'lib/position_range/list.rb', line 202 def delete(p_r) self.substract(PositionRange::List.new([p_r])) end |
#delete!(p_r) ⇒ Object
Deletes the position_range that is specified.
194 195 196 |
# File 'lib/position_range/list.rb', line 194 def delete!(p_r) self.substract!(PositionRange::List.new([p_r])) end |
#index(position_range, options = {}) ⇒ Object
Returns the index of the given PositionRange.
Options :dont_ignore_attributes
=> true, finds the one that has also equal attributes, defaults to false
93 94 95 96 97 98 99 100 101 102 103 104 |
# File 'lib/position_range/list.rb', line 93 def index(position_range, = {}) if [:dont_ignore_attributes] self.each_with_index do |s_p_r, i| if position_range == s_p_r and position_range.has_equal_pointer_attributes?(s_p_r) return i end end return nil else super(position_range) end end |
#insert_at_ranges(ranges_to_insert, ranges_at_which_to_insert, ranges_to_skip = []) ⇒ Object
Inserting at ranges returning a new list.
See insert_at_ranges!
454 455 456 457 458 |
# File 'lib/position_range/list.rb', line 454 def insert_at_ranges(ranges_to_insert, ranges_at_which_to_insert, ranges_to_skip = []) return self.dup.insert_at_ranges!(ranges_to_insert, ranges_at_which_to_insert, ranges_to_skip) end |
#insert_at_ranges!(ranges_to_insert, ranges_at_which_to_insert, ranges_to_skip = []) ⇒ Object
The ranges_to_insert are inserted at the ranges_at_which_to_insert of this list, counted in range_size from it’s beginning, and inter- luded with ranges_to_skip.
So PositionRange::List.from_s(‘39,49:16,20’).insert_at_ranges!(
PositionRange::List.from_s('100,102:6,7'),
PositionRange::List.from_s('10,12:19,20'),
PositionRange::List.from_s('12,19'))
will result in: PositionRange::List.from_s(‘39,49:100,102:6,7:16,20’)
377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 |
# File 'lib/position_range/list.rb', line 377 def insert_at_ranges!(ranges_to_insert, ranges_at_which_to_insert, ranges_to_skip = []) if ranges_to_insert.range_size != ranges_at_which_to_insert.range_size raise StandardError, 'Ranges to insert, and at which to insert are ' + 'of different range_sizes: ' + ranges_to_insert.to_s + ', ' + ranges_at_which_to_insert.to_s end ranges_to_insert.align_chunks!(ranges_at_which_to_insert) ranges_at_which_to_insert.align_chunks!(ranges_to_insert) ranges_to_act = ranges_at_which_to_insert.each {|p_r| p_r.action = :ins}.concat( ranges_to_skip).sort! i = -1 self_p = 0 ins_p = 0 ranges_to_act.each {|p_r| while self_p < p_r.begin - 1 i += 1 self_p += self[i].size end if self_p > p_r.begin copy = self[i] cut = copy.end + p_r.begin - self_p self[i] = copy.new_dup(copy.begin, cut) self.insert(i + 1, copy.new_dup(cut, copy.end)) self_p = p_r.begin end if p_r.action == :ins inner_p = 0 while inner_p < p_r.size self.insert(i + 1, ranges_to_insert[ins_p]) inner_p += ranges_to_insert[ins_p].size i += 1 ins_p += 1 end end self_p += p_r.size } return self end |
#invert(maximum_size = PositionRange::MaximumSize) ⇒ Object
Inversion returning a new list.
See invert!
249 250 251 |
# File 'lib/position_range/list.rb', line 249 def invert(maximum_size = PositionRange::MaximumSize) self.dup.invert!(maximum_size) end |
#invert!(maximum_size = PositionRange::MaximumSize) ⇒ Object
Results in all positions being included, being excluded now, and all positions that were excluded, being included now, upto the range below maximum_size.
NOTE: new ranges are created as PositionRanges, so references to objects or ordering_positions of subclasses are not maintained, as they are meaningless for inverted lists of ranges.
NOTE: Also that self is sorted.
216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 |
# File 'lib/position_range/list.rb', line 216 def invert!(maximum_size = PositionRange::MaximumSize) if self.size > 0 self.sort!.merge_adjacents! # sorts and prevents problems with adjacent ranges if self[-1].end > maximum_size raise PositionRange::Error.new(self[-1].begin, self[-1].end), 'PositionRange larger than the maximum' end start_point = 0 if self[0].begin > 0 self.insert(0, PositionRange.new(0, self[0].begin)) start_point += 1 end if self.size > 1 (start_point...(self.size - 1)).each {|i| self[i] = PositionRange.new(self[i].end, self[i + 1].begin) } end if self[-1].end < maximum_size - 1 self[-1] = PositionRange.new(self[-1].end, maximum_size) else self.delete_at(-1) end elsif maximum_size > 0 self.push(PositionRange.new(0, maximum_size)) end return self end |
#line_up_overlaps ⇒ Object
Lining up overlaps returning a new list.
See line_up_overlaps!
308 309 310 |
# File 'lib/position_range/list.rb', line 308 def line_up_overlaps self.dup.line_up_overlaps! end |
#line_up_overlaps! ⇒ Object
Makes sure that there are no overlapping borders between PositionRanges.
The guaranteed situation after calling this method:
-
Multiple PositionRanges can refer to the same ranges, but if they do they will have the same begin and end position.
-
All positions associated with an object (a Link or an Authorship for example) will still be associated with that same object, but possibly through a different or a new PositionRange.
Example: ‘3,7->a:5,9->b’ lined up will be ‘3,5->a:5,7->a:5,7->b:7,9->b’
Where the ->X indicates an association with object X
This is used for simplifying PositionRanges for parsing Links into Logis.
271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 |
# File 'lib/position_range/list.rb', line 271 def line_up_overlaps! self.sort!.merge_adjacents! # note that the merging and the sorting done by merge_adjacents # assures that he PositionRanges are always sorted by # begin-position AND size (short to long). i = 0 while i < (self.size - 1) if self[i].end > self[i + 1].begin # found an overlap if self[i].begin != self[i + 1].begin # the beginnings are not lined up, so align them self.insert(i + 1, self[i].new_dup(self[i + 1].begin, self[i].end)) self[i] = self[i].new_dup(self[i].begin, self[i + 1].begin) i = -1; self.sort! # restart in case more than 1 overlap elsif self[i].end != self[i + 1].end # the beginnings are already lined up, now do the ends if self[i].end < self[i + 1].end # i is the shortest, so self[i].end is used self.insert(i + 2, self[i + 1].new_dup(self[i].end, self[i + 1].end)) self[i + 1] = self[i + 1].new_dup(self[i + 1].begin, self[i].end) else # i + 1 is the shortest, so self[i + 1].end is used self.insert(i + 2, self[i].new_dup(self[i + 1].end, self[i].end)) self[i] = self[i].new_dup(self[i].begin, self[i + 1].end) end i = -1; self.sort! # restart in case more than 1 overlap end end i += 1 end return self end |
#merge_adjacents(options = {}) ⇒ Object
Merging adjacents returning a new list.
See merge_adjacents!
341 342 343 |
# File 'lib/position_range/list.rb', line 341 def merge_adjacents( = {}) self.dup.merge_adjacents!() end |
#merge_adjacents!(options = {}) ⇒ Object
Simplifies the PositionRange::List by merging adjacent PositionRanges.
Example: 1,4:4,7:10,11 => 1,7:10,11
Only merges adjacent PositionRanges if all their attributes (except for first and last) are the same
320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 |
# File 'lib/position_range/list.rb', line 320 def merge_adjacents!( = {}) ignore_attributes = [:ignore_attributes] if self.size > 1 i = 0 while i < self.size if self[i - 1].end == self[i].begin and (ignore_attributes or self[i - 1].has_equal_pointer_attributes?(self[i])) self[i - 1] = self[i - 1].new_dup(self[i - 1].begin, self[i].end) self.delete_at(i) else i += 1 end end end return self end |
#range_size ⇒ Object
Returns the combined size of the ranges in this list.
56 57 58 59 60 61 62 |
# File 'lib/position_range/list.rb', line 56 def range_size range_size = 0 self.each {|range| range_size += range.size } return range_size end |
#stack_adjacent(options = {}) ⇒ Object
Stacks the PositionRanges in the List adjacent in a new PositionRange::List, while maintaining their size.
So PositionRangeList.from_s(‘50,53:11,30’).stack_adjacents returns: PositionRangeList.from_s(‘0,3:4,23’)
Options :space
=> The space to leave inbetween
509 510 511 512 513 514 515 516 517 518 519 |
# File 'lib/position_range/list.rb', line 509 def stack_adjacent( = {}) space = [:space] || 0 adjacent = PositionRange::List.new adjacent_p = 0 self.collect do |p_r| step = p_r.size adjacent << PositionRange.new(adjacent_p, adjacent_p + step) adjacent_p += step + space end return adjacent end |
#substract(other, options = {}) ⇒ Object
Substraction returning a new list.
See substract!
188 189 190 |
# File 'lib/position_range/list.rb', line 188 def substract(other, = {}) self.dup.substract!(other, ) end |
#substract!(other, options = {}) ⇒ Object
Applies a substraction in the sense of Set theory.
It removes all PositionRanges and parts of PositionRanges that overlap with the PositionRanges given as the other.
So for example: 1,5:7,9:11,12’ becomes ‘1,4:7,8:11,12’ after substracting ‘4,6:8,9’
Only substracts PositionRanges if all their attributes (except for first and last) are the same, unless ignore_attributes is specified.
Options :ignore_attributes
=> Ignores attributes
143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 |
# File 'lib/position_range/list.rb', line 143 def substract!(other, = {}) ignore_attributes = [:ignore_attributes] sorted_self = self.dup.sort! if sorted_self.size > 0 and other.size > 0 other = other.dup.sort!.merge_adjacents! last_i = 0 other.each do |p_r| i = last_i while sorted_self[i] and sorted_self[i].end < p_r.begin i += 1 end last_i = i while sorted_self[i] and sorted_self[i].begin < p_r.end if ignore_attributes or sorted_self[i].has_equal_pointer_attributes?(p_r) self_i = self.index(sorted_self[i], :dont_ignore_attributes => !ignore_attributes) if sorted_self[i].begin < p_r.begin copy = sorted_self[i].dup sorted_self[i] = copy.new_dup(copy.begin, p_r.begin) self[self_i] = sorted_self[i] sorted_self.insert(i + 1, copy.new_dup(p_r.begin, copy.end)) self.insert(self_i + 1, sorted_self[i + 1]) i += 1 elsif sorted_self[i].end <= p_r.end sorted_self.delete_at(i) self.delete_at(self_i) else sorted_self[i] = sorted_self[i].new_dup( p_r.end, sorted_self[i].end) self[self_i] = sorted_self[i] end else i += 1 end end end end return self end |
#to_s ⇒ Object
Parses a PositionRange::List to a string
575 576 577 578 579 580 581 582 |
# File 'lib/position_range/list.rb', line 575 def to_s self.sort! p_r_l_string = '' self.each {|p_r| p_r_l_string += p_r.to_s + ':' } return p_r_l_string[0...-1] end |
#translate(integer) ⇒ Object
Translation returning a new list.
See translate!
361 362 363 |
# File 'lib/position_range/list.rb', line 361 def translate(integer) self.dup.translate!(integer) end |
#translate!(integer) ⇒ Object
Translates the PositionRange::List in space, along the given vector.
347 348 349 350 351 352 353 354 355 |
# File 'lib/position_range/list.rb', line 347 def translate!(integer) if !integer.kind_of?(Integer) raise StandardError.new, 'Tried to translate a PositionRange::List with a non-integer' end (0...self.size).each {|i| self[i] = self[i].new_dup(self[i].first + integer,self[i].last + integer) } return self end |
#translate_from_view(view_position_range_list) ⇒ Object
Translates the PositionRange::List into absolute space
483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 |
# File 'lib/position_range/list.rb', line 483 def translate_from_view(view_position_range_list) absolute = PositionRange::List.new self.each do |p_r| view_p = 0 p_r_list = PositionRange::List.new([p_r]) view_position_range_list.each do |snippet_p_r| translate_list = p_r_list & PositionRange::List.new( [PositionRange.new(view_p,view_p + snippet_p_r.size)]) vector = snippet_p_r.first - view_p absolute.concat(translate_list.translate!(vector)) view_p += snippet_p_r.size end end absolute.merge_adjacents! return absolute end |
#translate_to_view(view_position_range_list) ⇒ Object
Translates the PositionRange::List into the relative space defined by the view_position_range_list
465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 |
# File 'lib/position_range/list.rb', line 465 def translate_to_view(view_position_range_list) relative = PositionRange::List.new self.each do |p_r| view_p = 0 p_r_list = PositionRange::List.new([p_r]) view_position_range_list.each do |snippet_p_r| translate_list = p_r_list & PositionRange::List.new([snippet_p_r]) vector = view_p - snippet_p_r.first relative.concat(translate_list.translate!(vector)) view_p += snippet_p_r.size end end relative.merge_adjacents! return relative end |
#within?(other) ⇒ Boolean
Returns true if all PositionRanges in this list fall within the PositionRanges in the given other PositionRange::List
Attributes are ignored.
79 80 81 82 83 84 85 |
# File 'lib/position_range/list.rb', line 79 def within?(other) if (self.substract(other, :ignore_attributes => true)).empty? return true else return false end end |