about summary refs log tree commit
path: root/lib/msgthr/container.rb
blob: 30f677550383192a6243544728a622f7a418a876 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
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