/* LIST-DIFF-APPLICATION.hpp - consume and apply a list diff Copyright (C) 2014, Hermann Vosseler   **Lumiera** is free software; you can redistribute it and/or modify it   under the terms of the GNU General Public License as published by the   Free Software Foundation; either version 2 of the License, or (at your   option) any later version. See the file COPYING for further details. */ /** @file list-diff-application.hpp ** Apply a "list diff" to a concrete sequence of elements in a container. ** This header provides specialisation(s) of the DiffApplicationStrategy to ** actual containers, choosing an implementation approach suitable for this ** specific kind of container. Together with a lib::diff::DiffApplicator, ** this allows to receive the description of changes (as a linearised sequence ** of DiffStep tokens) and apply them to a given concrete sequence of data ** elements, thereby transforming the contents of this target sequence. ** ** @see diff-list-application-test.cpp ** @see ListDiffLanguage ** */ #ifndef LIB_DIFF_LIST_DIFF_APPLICATION_H #define LIB_DIFF_LIST_DIFF_APPLICATION_H #include "lib/diff/list-diff.hpp" #include "lib/format-string.hpp" #include #include #include namespace lib { namespace diff{ using util::_Fmt; using std::vector; using std::move; /** * concrete strategy to apply a list diff to a target sequence given as vector. * The implementation swaps aside the existing content of the target sequence * and then consumes it step by step, while building up the altered content * within the previously emptied target vector. Whenever possible, elements * are moved directly to the target location. * @throws lumiera::error::State when diff application fails due to the * target sequence being different than assumed by the given diff. * @warning behaves only EX_SANE in case of diff application errors, * i.e. only partially modified / rebuilt sequence might be * in the target when diff application is aborted * @see #ListDiffInterpreter explanation of the verbs */ template class DiffApplicationStrategy> : public ListDiffInterpreter { using Vec = vector; using Iter = Vec::iterator; Vec orig_; Vec& seq_; Iter pos_; bool end_of_target() { return pos_ == orig_.end(); } void __expect_in_target (E const& elm, Literal oper) { if (end_of_target()) throw error::State(_Fmt("Unable to %s element %s from target as demanded; " "no (further) elements in target sequence") % oper % elm , LERR_(DIFF_CONFLICT)); if (*pos_ != elm) throw error::State(_Fmt("Unable to %s element %s from target as demanded; " "found element %s on current target position instead") % oper % elm % *pos_ , LERR_(DIFF_CONFLICT)); } void __expect_further_elements (E const& elm) { if (end_of_target()) throw error::State(_Fmt("Premature end of target sequence, still expecting element %s; " "unable to apply diff further.") % elm , LERR_(DIFF_CONFLICT)); } void __expect_found (E const& elm, Iter const& targetPos) { if (targetPos == orig_.end()) throw error::State(_Fmt("Premature end of sequence; unable to locate " "element %s in the remainder of the target.") % elm , LERR_(DIFF_CONFLICT)); } /* == Implementation of the diff application primitives == */ void ins (E const& elm) override { seq_.push_back(elm); } void del (E const& elm) override { __expect_in_target(elm, "remove"); ++pos_; } void pick (E const& elm) override { __expect_in_target(elm, "pick"); seq_.push_back (move(*pos_)); ++pos_; } void skip (E const& elm) override { __expect_further_elements (elm); ++pos_; } // assume the actual content has been moved away by a previous find() void find (E const& elm) override { __expect_further_elements (elm); Iter found = std::find(pos_, orig_.end(), elm); __expect_found (elm, found); seq_.push_back (move(*found)); } // consume and leave waste, expected to be cleaned-up by skip() later public: explicit DiffApplicationStrategy(vector& targetVector) : seq_(targetVector) { } void initDiffApplication() { swap (seq_, orig_); seq_.reserve (orig_.size() * 120 / 100); // heuristics for storage pre-allocation pos_ = orig_.begin(); } void completeDiffApplication() { if (not end_of_target()) throw error::State(_Fmt("Not all source data consumed after diff application. " "Element %s waiting to be consumed") % *pos_ , LERR_(DIFF_STRUCTURE)); // discard storage orig_.clear(); } }; }} // namespace lib::diff #endif /*LIB_DIFF_LIST_DIFF_APPLICATION_H*/