diff options
author | Eric Wong <e@80x24.org> | 2016-11-12 02:13:42 +0000 |
---|---|---|
committer | Eric Wong <e@80x24.org> | 2017-01-01 03:30:41 +0000 |
commit | 76c10dada86fd807d36ccafca393e945e381696a (patch) | |
tree | 88227412057f87821a78e6f97c0ec864e9431ce5 /lib/msgthr | |
download | msgthr-76c10dada86fd807d36ccafca393e945e381696a.tar.gz |
Diffstat (limited to 'lib/msgthr')
-rw-r--r-- | lib/msgthr/container.rb | 73 |
1 files changed, 73 insertions, 0 deletions
diff --git a/lib/msgthr/container.rb b/lib/msgthr/container.rb new file mode 100644 index 0000000..30f6775 --- /dev/null +++ b/lib/msgthr/container.rb @@ -0,0 +1,73 @@ +# Copyright (C) 2016 all contributors <msgthr-public@80x24.org> +# License: GPL-2.0+ <https://www.gnu.org/licenses/gpl-2.0.txt> + +# An internal container class, this is exposed for sorting APIs +# but should not be initialized in your own code. +class Msgthr::Container + + # Unique message identifier, typically the Message-Id header for mail + # and news messages. This may be any hashable object, Integer values + # are allowed and will not be coerced to string values. + attr_reader :mid + + # Opaque data pointer, may be used by the user for any purpose. + # This may be +nil+ to denote missing (aka "ghost") messages. + attr_accessor :msg + + attr_accessor :children # :nodoc: + attr_accessor :parent # :nodoc: + + def initialize(mid) # :nodoc: + @mid = mid + @children = {} # becomes an Array after order! + @parent = nil + @msg = nil # opaque + end + + # returns the topmost message container with an opaque message pointer + # in it. This may be +nil+ if none are available. + def topmost + q = [ self ] + while cont = q.shift + return cont if cont.msg + q.concat(cont.children.values) + end + nil + end + + def add_child(child) # :nodoc: + raise 'cannot become child of self' if child == self + cid = child.mid + + # reparenting: + parent = child.parent and parent.children.delete(cid) + + @children[cid] = child + child.parent = self + end + + def has_descendent(child) # :nodoc: + seen = Hash.new(0) + while child + return true if self == child || (seen[child] += 1) != 0 + child = child.parent + end + false + end + + def order! # :nodoc: + seen = { @mid => true } + q = [ self ] + while cur = q.shift + c = [] + cur.children.each do |cmid, cont| + next if seen[cmid] + c << cont + seen[cmid] = true + end.clear + yield(c) if c.size > 1 + cur.children = c + q.concat(c) + end + end +end |