| /** @file |
| Errata implementation. |
| |
| @section license License |
| |
| Licensed to the Apache Software Foundation (ASF) under one |
| or more contributor license agreements. See the NOTICE file |
| distributed with this work for additional information |
| regarding copyright ownership. The ASF licenses this file |
| to you under the Apache License, Version 2.0 (the |
| "License"); you may not use this file except in compliance |
| with the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| */ |
| |
| #include "tscore/Errata.h" |
| |
| #include <iostream> |
| #include <sstream> |
| #include <iomanip> |
| #include <algorithm> |
| #include <memory.h> |
| |
| namespace ts |
| { |
| /** List of sinks for abandoned erratum. |
| */ |
| namespace |
| { |
| std::deque<Errata::Sink::Handle> Sink_List; |
| } |
| |
| std::string const Errata::DEFAULT_GLUE("\n"); |
| Errata::Message const Errata::NIL_MESSAGE; |
| Errata::Code Errata::Message::Default_Code = 0; |
| Errata::Message::SuccessTest const Errata::Message::DEFAULT_SUCCESS_TEST = &Errata::Message::isCodeZero; |
| Errata::Message::SuccessTest Errata::Message::Success_Test = Errata::Message::DEFAULT_SUCCESS_TEST; |
| |
| bool |
| Errata::Message::isCodeZero(Message const &msg) |
| { |
| return msg.m_code == 0; |
| } |
| |
| void |
| Errata::Data::push(Message const &msg) |
| { |
| m_items.push_back(msg); |
| } |
| |
| void |
| Errata::Data::push(Message &&msg) |
| { |
| m_items.push_back(std::move(msg)); |
| } |
| |
| Errata::Message const & |
| Errata::Data::top() const |
| { |
| return m_items.size() ? m_items.back() : NIL_MESSAGE; |
| } |
| |
| inline Errata::Errata(ImpPtr const &ptr) : m_data(ptr) {} |
| |
| Errata::Data::~Data() |
| { |
| if (m_log_on_delete) { |
| Errata tmp(ImpPtr(this)); // because client API requires a wrapper. |
| for (auto &f : Sink_List) { |
| (*f)(tmp); |
| } |
| tmp.m_data.release(); // don't delete this again. |
| } |
| } |
| |
| Errata::Errata(self const &that) : m_data(that.m_data) {} |
| |
| Errata::Errata(self &&that) : m_data(that.m_data) {} |
| |
| Errata::Errata(std::string const &text) |
| { |
| this->push(text); |
| } |
| |
| Errata::Errata(Id id, std::string const &text) |
| { |
| this->push(id, text); |
| } |
| |
| Errata::~Errata() {} |
| |
| /* This forces the errata to have a data object that only it references. |
| If we're sharing the data, clone. If there's no data, allocate. |
| This is used just before a write operation to have copy on write semantics. |
| */ |
| Errata::Data * |
| Errata::pre_write() |
| { |
| if (m_data) { |
| if (m_data.use_count() > 1) { |
| m_data.reset(new Data(*m_data)); // clone current data |
| } |
| } else { // create new data |
| m_data.reset(new Data); |
| } |
| return m_data.get(); |
| } |
| |
| // Just create an instance if needed. |
| Errata::Data const * |
| Errata::instance() |
| { |
| if (!m_data) { |
| m_data.reset(new Data); |
| } |
| return m_data.get(); |
| } |
| |
| Errata & |
| Errata::push(Message const &msg) |
| { |
| this->pre_write()->push(msg); |
| return *this; |
| } |
| |
| Errata & |
| Errata::push(Message &&msg) |
| { |
| this->pre_write()->push(std::move(msg)); |
| return *this; |
| } |
| |
| Errata & |
| Errata::operator=(self const &that) |
| { |
| m_data = that.m_data; |
| return *this; |
| } |
| |
| Errata & |
| Errata::operator=(Message const &msg) |
| { |
| // Avoid copy on write in the case where we discard. |
| if (!m_data || m_data.use_count() > 1) { |
| this->clear(); |
| this->push(msg); |
| } else { |
| m_data->m_items.clear(); |
| m_data->push(msg); |
| } |
| return *this; |
| } |
| |
| Errata & |
| Errata::operator=(self &&that) |
| { |
| m_data = that.m_data; |
| return *this; |
| } |
| |
| Errata & |
| Errata::pull(self &that) |
| { |
| if (that.m_data) { |
| this->pre_write(); |
| m_data->m_items.insert(m_data->m_items.end(), that.m_data->m_items.begin(), that.m_data->m_items.end()); |
| that.m_data->m_items.clear(); |
| } |
| return *this; |
| } |
| |
| void |
| Errata::pop() |
| { |
| if (m_data && m_data->size()) { |
| this->pre_write()->m_items.pop_front(); |
| } |
| return; |
| } |
| |
| void |
| Errata::clear() |
| { |
| m_data.reset(nullptr); |
| } |
| |
| /* We want to allow iteration on empty / nil containers because that's very |
| convenient for clients. We need only return the same value for begin() |
| and end() and everything works as expected. |
| |
| However we need to be a bit more clever for VC 8. It checks for |
| iterator compatibility, i.e. that the iterators are not |
| invalidated and that they are for the same container. It appears |
| that default iterators are not compatible with anything. So we |
| use static container for the nil data case. |
| */ |
| static Errata::Container NIL_CONTAINER; |
| |
| Errata::iterator |
| Errata::begin() |
| { |
| return m_data ? m_data->m_items.rbegin() : NIL_CONTAINER.rbegin(); |
| } |
| |
| Errata::const_iterator |
| Errata::begin() const |
| { |
| return m_data ? static_cast<Data const &>(*m_data).m_items.rbegin() : static_cast<Container const &>(NIL_CONTAINER).rbegin(); |
| } |
| |
| Errata::iterator |
| Errata::end() |
| { |
| return m_data ? m_data->m_items.rend() : NIL_CONTAINER.rend(); |
| } |
| |
| Errata::const_iterator |
| Errata::end() const |
| { |
| return m_data ? static_cast<Data const &>(*m_data).m_items.rend() : static_cast<Container const &>(NIL_CONTAINER).rend(); |
| } |
| |
| void |
| Errata::registerSink(Sink::Handle const &s) |
| { |
| Sink_List.push_back(s); |
| } |
| |
| std::ostream & |
| Errata::write(std::ostream &out, int offset, int indent, int shift, char const *lead) const |
| { |
| for (auto m : *this) { |
| if ((offset + indent) > 0) { |
| out << std::setw(indent + offset) << std::setfill(' ') << ((indent > 0 && lead) ? lead : " "); |
| } |
| |
| out << m.m_id << " [" << m.m_code << "]: " << m.m_text << std::endl; |
| if (m.getErrata().size()) { |
| m.getErrata().write(out, offset, indent + shift, shift, lead); |
| } |
| } |
| return out; |
| } |
| |
| size_t |
| Errata::write(char *buff, size_t n, int offset, int indent, int shift, char const *lead) const |
| { |
| std::ostringstream out; |
| std::string text; |
| this->write(out, offset, indent, shift, lead); |
| text = out.str(); |
| memcpy(buff, text.data(), std::min(n, text.size())); |
| return text.size(); |
| } |
| |
| std::ostream & |
| operator<<(std::ostream &os, Errata const &err) |
| { |
| return err.write(os, 0, 0, 2, "> "); |
| } |
| |
| } // namespace ts |