| 1 | /* This file is part of Alue, the multiprotocol Internet discussion daemon |
|---|
| 2 | |
|---|
| 3 | Copyright © 2009 Antti-Juhani Kaijanaho |
|---|
| 4 | |
|---|
| 5 | Alue is free software: you can redistribute it and/or modify it |
|---|
| 6 | under the terms of the GNU General Public License as published by |
|---|
| 7 | the Free Software Foundation, either version 3 of the License, or |
|---|
| 8 | (at your option) any later version. |
|---|
| 9 | |
|---|
| 10 | Alue is distributed in the hope that it will be useful, but |
|---|
| 11 | WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|---|
| 13 | General Public License for more details. |
|---|
| 14 | |
|---|
| 15 | You should have received a copy of the GNU General Public License |
|---|
| 16 | along with Alue. If not, see <http://www.gnu.org/licenses/>. |
|---|
| 17 | |
|---|
| 18 | */ |
|---|
| 19 | |
|---|
| 20 | #include "threaded.hh" |
|---|
| 21 | |
|---|
| 22 | #include "../msg/lexutils.hh" |
|---|
| 23 | #include "../msg/msg.hh" |
|---|
| 24 | #include "../html/util.hh" |
|---|
| 25 | #include "../tlate/sequential_value.hh" |
|---|
| 26 | #include "../tlate/lazy_structured_value.hh" |
|---|
| 27 | #include "../uri.hh" |
|---|
| 28 | |
|---|
| 29 | #include "../assert.hh" |
|---|
| 30 | |
|---|
| 31 | namespace db |
|---|
| 32 | { |
|---|
| 33 | thread_node::ptr thread_node::previous() const |
|---|
| 34 | { |
|---|
| 35 | if (!parent) return ptr(); |
|---|
| 36 | if (sibling_iterator == parent->children.begin()) |
|---|
| 37 | return parent; |
|---|
| 38 | std::list<ptr>::iterator it = sibling_iterator; |
|---|
| 39 | assert(msgid == (*it)->msgid); |
|---|
| 40 | it--; |
|---|
| 41 | return *it; |
|---|
| 42 | } |
|---|
| 43 | |
|---|
| 44 | thread_node::ptr thread_node::next() const |
|---|
| 45 | { |
|---|
| 46 | if (!children.empty()) return *children.begin(); |
|---|
| 47 | const_ptr tn = shared_from_this(); |
|---|
| 48 | while (true) |
|---|
| 49 | { |
|---|
| 50 | if (!tn->parent) return ptr(); |
|---|
| 51 | std::list<ptr>::iterator it = tn->sibling_iterator; |
|---|
| 52 | assert(it != tn->parent->children.end()); |
|---|
| 53 | it++; |
|---|
| 54 | if (it == tn->parent->children.end()) |
|---|
| 55 | { |
|---|
| 56 | tn = tn->parent; |
|---|
| 57 | continue; |
|---|
| 58 | } |
|---|
| 59 | return *it; |
|---|
| 60 | } |
|---|
| 61 | } |
|---|
| 62 | |
|---|
| 63 | void thread_node::disown_children() |
|---|
| 64 | { |
|---|
| 65 | for (std::list<ptr>::iterator it = children.begin(); |
|---|
| 66 | it != children.end(); it++) |
|---|
| 67 | { |
|---|
| 68 | (*it)->parent.reset(); |
|---|
| 69 | } |
|---|
| 70 | children.clear(); |
|---|
| 71 | } |
|---|
| 72 | |
|---|
| 73 | void thread_node::set_parent(ptr p) |
|---|
| 74 | { |
|---|
| 75 | if (parent) |
|---|
| 76 | parent->children.erase(sibling_iterator); |
|---|
| 77 | parent = p; |
|---|
| 78 | if (!parent) return; |
|---|
| 79 | p->children.push_back(shared_from_this()); |
|---|
| 80 | sibling_iterator = p->children.end(); |
|---|
| 81 | assert(sibling_iterator != p->children.begin()); |
|---|
| 82 | sibling_iterator--; |
|---|
| 83 | } |
|---|
| 84 | |
|---|
| 85 | thread_node::ptr |
|---|
| 86 | thread_node::get_principal() |
|---|
| 87 | { |
|---|
| 88 | if (art) return ptr(); |
|---|
| 89 | if (children.empty()) return ptr(); |
|---|
| 90 | std::list<ptr>::iterator it = children.begin(); |
|---|
| 91 | it++; |
|---|
| 92 | if (it != children.end()) return ptr(); |
|---|
| 93 | return *it; |
|---|
| 94 | } |
|---|
| 95 | |
|---|
| 96 | size_t thread_node::cardinality() const |
|---|
| 97 | { |
|---|
| 98 | size_t rv = art ? 1 : 0; |
|---|
| 99 | for (std::list<ptr>::const_iterator it = children.begin(); |
|---|
| 100 | it != children.end(); it++) |
|---|
| 101 | { |
|---|
| 102 | rv += (*it)->cardinality(); |
|---|
| 103 | } |
|---|
| 104 | return rv; |
|---|
| 105 | } |
|---|
| 106 | |
|---|
| 107 | bool thread_node::has_descendant(ptr tn) |
|---|
| 108 | { |
|---|
| 109 | for (std::list<ptr>::const_iterator it = children.begin(); |
|---|
| 110 | it != children.end(); it++) |
|---|
| 111 | { |
|---|
| 112 | if (*it == tn) return true; |
|---|
| 113 | if ((*it)->has_descendant(tn)) return true; |
|---|
| 114 | } |
|---|
| 115 | return false; |
|---|
| 116 | } |
|---|
| 117 | |
|---|
| 118 | void thread_node::prune() |
|---|
| 119 | { |
|---|
| 120 | for (std::list<ptr>::iterator it = children.begin(); |
|---|
| 121 | it != children.end(); /**/) |
|---|
| 122 | { |
|---|
| 123 | (*it)->prune(); |
|---|
| 124 | if ((*it)->art) |
|---|
| 125 | { |
|---|
| 126 | it++; |
|---|
| 127 | } |
|---|
| 128 | else |
|---|
| 129 | { |
|---|
| 130 | for (std::list<ptr>::iterator jt = |
|---|
| 131 | (*it)->children.begin(); |
|---|
| 132 | jt != (*it)->children.end(); |
|---|
| 133 | jt++) |
|---|
| 134 | { |
|---|
| 135 | (*jt)->sibling_iterator = |
|---|
| 136 | children.insert(it, *jt); |
|---|
| 137 | (*jt)->parent = shared_from_this(); |
|---|
| 138 | } |
|---|
| 139 | |
|---|
| 140 | std::list<ptr>::iterator next = it; |
|---|
| 141 | next++; |
|---|
| 142 | children.erase(it); |
|---|
| 143 | (*it)->parent.reset(); |
|---|
| 144 | it = next; |
|---|
| 145 | } |
|---|
| 146 | } |
|---|
| 147 | } |
|---|
| 148 | |
|---|
| 149 | void thread_node::add_msg(db::group::number grinx_, |
|---|
| 150 | boost::shared_ptr<msg::msg> art_) |
|---|
| 151 | { |
|---|
| 152 | if (art) return; |
|---|
| 153 | grinx = grinx_; |
|---|
| 154 | art = art_; |
|---|
| 155 | std::string refss = art->get_field("references", false); |
|---|
| 156 | ptr prev; |
|---|
| 157 | while (!refss.empty()) |
|---|
| 158 | { |
|---|
| 159 | std::string ref = msg::get_msgid(refss); |
|---|
| 160 | if (ref.empty()) continue; |
|---|
| 161 | ptr &tn = msgids[ref]; |
|---|
| 162 | if (!tn) tn.reset(new thread_node(msgids,ref)); |
|---|
| 163 | if (prev && !tn->parent && !tn->has_descendant(prev)) |
|---|
| 164 | tn->set_parent(prev); |
|---|
| 165 | prev = tn; |
|---|
| 166 | } |
|---|
| 167 | if (prev) |
|---|
| 168 | { |
|---|
| 169 | set_parent(prev); |
|---|
| 170 | } |
|---|
| 171 | } |
|---|
| 172 | |
|---|
| 173 | void threaded::add_msg(db::group::number grinx, |
|---|
| 174 | boost::shared_ptr<msg::msg> art) |
|---|
| 175 | { |
|---|
| 176 | thread_node::ptr &tn = msgids[art->msgid()]; |
|---|
| 177 | if (!tn) tn.reset(new thread_node(msgids, art->msgid())); |
|---|
| 178 | tn->add_msg(grinx, art); |
|---|
| 179 | } |
|---|
| 180 | |
|---|
| 181 | void threaded::recalc_roots() |
|---|
| 182 | { |
|---|
| 183 | root->disown_children(); |
|---|
| 184 | for (std::map<std::string, thread_node::ptr>::const_iterator it |
|---|
| 185 | = msgids.begin(); |
|---|
| 186 | it != msgids.end(); it++) |
|---|
| 187 | { |
|---|
| 188 | thread_node::ptr tn = it->second; |
|---|
| 189 | if (tn->parent) continue; |
|---|
| 190 | |
|---|
| 191 | thread_node::ptr princ = tn->get_principal(); |
|---|
| 192 | if (princ) |
|---|
| 193 | tn = princ; |
|---|
| 194 | |
|---|
| 195 | if (!tn->is_empty()) |
|---|
| 196 | tn->set_parent(root); |
|---|
| 197 | } |
|---|
| 198 | root->prune(); |
|---|
| 199 | } |
|---|
| 200 | void threaded::sort() |
|---|
| 201 | { |
|---|
| 202 | root->sort_children_recursive(thread_node::cmp_grinx); |
|---|
| 203 | } |
|---|
| 204 | |
|---|
| 205 | thread_node::ptr |
|---|
| 206 | thread_node::restrict_to_group(boost::shared_ptr<group> gr, |
|---|
| 207 | ptr parent) const |
|---|
| 208 | { |
|---|
| 209 | ptr rv(new thread_node(msgids, msgid)); |
|---|
| 210 | rv->grinx = grinx; |
|---|
| 211 | rv->art = art; |
|---|
| 212 | rv->set_parent(parent); |
|---|
| 213 | |
|---|
| 214 | for (std::list<ptr>::const_iterator it = children.begin(); |
|---|
| 215 | it != children.end(); it++) |
|---|
| 216 | { |
|---|
| 217 | (*it)->restrict_to_group(gr, rv); |
|---|
| 218 | } |
|---|
| 219 | if (rv->children.empty() && !art) return ptr(); |
|---|
| 220 | if (art) |
|---|
| 221 | { |
|---|
| 222 | std::map<boost::shared_ptr<db::group>,db::group::number> |
|---|
| 223 | ::const_iterator git = art->get_xref().find(gr); |
|---|
| 224 | if (git == art->get_xref().end()) |
|---|
| 225 | { |
|---|
| 226 | rv->set_parent(ptr()); |
|---|
| 227 | return ptr(); |
|---|
| 228 | } |
|---|
| 229 | } |
|---|
| 230 | return rv; |
|---|
| 231 | } |
|---|
| 232 | |
|---|
| 233 | const std::list<thread_node::ptr> threaded::empty_list; |
|---|
| 234 | |
|---|
| 235 | const std::list<thread_node::ptr> &threaded::get_threads |
|---|
| 236 | (boost::shared_ptr<group> gr) const |
|---|
| 237 | { |
|---|
| 238 | thread_node::ptr tn = root->restrict_to_group |
|---|
| 239 | (gr, thread_node::ptr()); |
|---|
| 240 | if (!tn) return empty_list; |
|---|
| 241 | return tn->get_children(); |
|---|
| 242 | } |
|---|
| 243 | |
|---|
| 244 | } |
|---|
| 245 | |
|---|