Lumiera  0.pre.03
»edit your freedom«
record.hpp
Go to the documentation of this file.
1 /*
2  RECORD.hpp - collection to represent object-like data
3 
4  Copyright (C) Lumiera.org
5  2015, Hermann Vosseler <Ichthyostega@web.de>
6 
7  This program is free software; you can redistribute it and/or
8  modify it under the terms of the GNU General Public License as
9  published by the Free Software Foundation; either version 2 of
10  the License, or (at your option) any later version.
11 
12  This program is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU General Public License for more details.
16 
17  You should have received a copy of the GNU General Public License
18  along with this program; if not, write to the Free Software
19  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 
21 */
22 
23 
90 #ifndef LIB_DIFF_RECORD_H
91 #define LIB_DIFF_RECORD_H
92 
93 
94 #include "lib/error.hpp"
95 #include "lib/nocopy.hpp"
96 #include "lib/iter-adapter.hpp"
97 #include "lib/iter-adapter-stl.hpp"
98 #include "lib/itertools.hpp"
99 #include "lib/format-util.hpp"
100 #include "lib/util.hpp"
101 
102 
103 #include <algorithm>
104 #include <utility>
105 #include <vector>
106 #include <string>
107 
108 
109 
110 namespace lib {
111 
112  template<class BA, class DEFAULT>
114 
115 namespace idi {
116  class BareEntryID;
117 }
118 namespace diff{
119 
120  namespace error = lumiera::error;
121 
122  using util::isnil;
123  using std::string;
124 
125  class TreeMutator;
126 
127  template<typename VAL>
128  struct RecordSetup;
129 
130 
149  template<typename VAL>
150  class Record
151  {
152  using Storage = typename RecordSetup<VAL>::Storage;
153  using ElmIter = typename RecordSetup<VAL>::ElmIter;
154  using Access = typename RecordSetup<VAL>::Access;
155 
156 
157  string type_;
158  Storage attribs_;
159  Storage children_;
160 
161  public:
162  static const string TYPE_NIL;
163  static const Symbol TYPE_NIL_SYM;
164 
165  Record()
166  : type_(TYPE_NIL)
167  { }
168 
169  template<typename A, typename C>
170  Record(Symbol typeID, A&& att, C&& chi)
171  : type_(isnil(typeID)? TYPE_NIL:string(typeID))
172  , attribs_(std::forward<A> (att))
173  , children_(std::forward<C> (chi))
174  { }
175 
176  template<typename A, typename C>
177  Record(Symbol typeID, std::initializer_list<A> const&& att
178  , std::initializer_list<C> const&& chi)
179  : type_(isnil(typeID)? TYPE_NIL:string(typeID))
180  , attribs_(att)
181  , children_(chi)
182  { }
183 
184  template<typename SEQ>
185  explicit
186  Record (SEQ const& con)
187  : type_(TYPE_NIL)
188  {
189  auto p = std::begin(con);
190  auto e = std::end(con);
191  for ( ; p!=e && isAttribute(*p); ++p)
192  if (isTypeID (*p))
193  type_ = extractTypeID(*p);
194  else
195  attribs_.push_back (*p);
196  for ( ; p!=e; ++p)
197  children_.push_back (*p);
198  }
199 
200  Record (std::initializer_list<VAL> const&& ili)
201  : Record(ili)
202  { }
203 
204  // all default copy operations acceptable
205 
206 
208  operator std::string() const;
209 
210 
211  size_t
212  attribSize() const
213  {
214  return attribs_.size();
215  }
216 
217  size_t
218  childSize() const
219  {
220  return children_.size();
221  }
222 
223  bool
224  empty() const
225  {
226  return attribs_.empty()
227  and children_.empty();
228  }
229 
230 
231  string
232  getType() const
233  {
234  return type_;
235  }
236 
237  bool
238  hasAttribute (string key) const
239  {
240  return attribs_.end() != findKey(key);
241  }
242 
243  bool
244  contains (VAL const& val) const
245  {
246  return util::contains (children_, val);
247  }
248 
249  Access
250  get (string key) const
251  {
252  ElmIter found = findKey (key);
253  if (attribs_.end() == found)
254  throw error::Invalid ("Record has no attribute \""+key+"\"");
255  else
256  return extractVal (*found);
257  }
258 
259  Access
260  child (size_t idx) const
261  {
262  if (children_.size() <= idx)
263  throw error::Invalid ("Child index " +util::toString(idx)
264  +" out of bounds [0.."+util::toString(children_.size())
265  +"["
266  ,error::LUMIERA_ERROR_INDEX_BOUNDS);
267  return children_[idx];
268  }
269 
270 
281  class Mutator;
282 
283 
284 
298  Record (Mutator const& mut)
299  : Record((Record const&) mut)
300  { }
301  explicit
302  Record (Mutator && mut)
303  : Record(std::move (Record(mut)))
304  { }
305 
306  friend class Mutator;
307 
308 
309 
310  /* ==== Exposing scope and contents for iteration ====== */
311 
313  using scopeIter = typename iter_stl::_SeqT<const Storage>::Range;
316 
318  iterator begin () const { return iterator(this, attribs_.empty()? children_.begin() : attribs_.begin()); }
319  iterator end () const { return iterator(); }
320 
321  scopeIter attribs() const { return iter_stl::eachElm(attribs_); }
322  scopeIter scope() const { return iter_stl::eachElm(children_); }
323 
324  keyIter keys() const { return transformIterator(attribs(), extractKey); }
325  valIter vals() const { return transformIterator(attribs(), extractVal); }
326 
327  protected: /* ==== API for the IterAdapter ==== */
328 
330  friend void
331  iterNext (const Record*, ElmIter& pos)
332  {
333  ++pos;
334  }
335 
341  friend bool
342  checkPoint (const Record* src, ElmIter& pos)
343  {
344  REQUIRE (src);
345  static const ElmIter END;
346  if (pos != END && pos == src->attribs_.end() && !src->children_.empty())
347  {
348  pos = src->children_.begin();
349  return true;
350  }
351  else
352  if (pos != END && (pos != src->children_.end()))
353  return true;
354  else
355  {
356  pos = END;
357  return false;
358  } }
359 
360  private:
361  /* === abstract attribute handling : needs specialisation === */
362  static bool isAttribute (VAL const& v);
363  static bool isTypeID (VAL const& v);
364  static string extractTypeID (VAL const& v);
365  static string renderAttribute (VAL const& a);
366  static string extractKey (VAL const& v);
367  static Access extractVal (VAL const& v);
368  template<typename X>
369  static VAL buildAttribute (string const& key, X&& payload);
370 
371 
372  ElmIter
373  findKey (string key) const
374  {
375  return std::find_if (attribs_.begin()
376  ,attribs_.end()
377  ,[=](VAL const& elm)
378  {
379  return key == extractKey(elm);
380  });
381  }
382 
383 
384  friend bool
385  operator== (Record const& r1, Record const& r2)
386  {
387  return r1.type_ == r2.type_
388  and r1.attribs_ == r2.attribs_
389  and r1.children_ == r2.children_;
390  }
391 
392  friend bool
393  operator!= (Record const& r1, Record const& r2)
394  {
395  return not (r1 == r2);
396  }
397  };
398 
399  template<typename VAL>
400  const Symbol Record<VAL>::TYPE_NIL_SYM = "NIL";
401 
402  template<typename VAL>
403  const string Record<VAL>::TYPE_NIL = string(TYPE_NIL_SYM);
404 
405 
406 
407  template<typename VAL>
408  class Record<VAL>::Mutator
410  {
411  using Rec = Record<VAL>;
412 
413  Rec record_;
414 
415  public:
416  Mutator()
417  : record_()
418  { }
419 
420  explicit
421  Mutator (Rec const& startingPoint)
422  : record_(startingPoint)
423  { }
424 
425  explicit
426  Mutator (Rec && startingPoint)
427  : record_(std::move (startingPoint))
428  { }
429 
430  operator Rec&()
431  {
432  return record_;
433  }
434 
435  void
436  swap (Rec& existingInstance) noexcept
437  {
438  std::swap (existingInstance, record_);
439  }
440 
441  bool
442  empty() const
443  {
444  return record_.empty();
445  }
446 
447 
448  /* === functions to alter contents === */
449 
450  void
451  setType (string const& newTypeID)
452  {
453  record_.type_ = newTypeID;
454  }
455 
456  Mutator&&
457  type (string const& typeID)
458  {
459  setType (typeID);
460  return move(*this);
461  }
462 
463  template<typename X>
464  Mutator&&
465  set (string const& key, X&& content)
466  {
467  VAL attribute(Rec::buildAttribute (key, std::forward<X>(content)));
468  return set (std::move (attribute));
469  }
470 
471  Mutator&&
472  set (VAL&& attribute)
473  {
474  string key = Rec::extractKey(attribute);
475  if (isnil (key))
476  throw error::Invalid ("Attempt to set an attribute with empty key");
477 
478  Rec::Storage& as =record_.attribs_;
479  auto found = std::find_if (as.begin(),as.end()
480  ,[=](VAL const& elm)
481  {
482  return key == extractKey(elm);
483  });
484  if (as.end() == found)
485  as.push_back (std::forward<VAL> (attribute));
486  else
487  (*found) = (std::forward<VAL> (attribute));
488  return move(*this);
489  }
490 
491  Mutator&&
492  appendAttrib (VAL const& newAttrib)
493  {
494  REQUIRE (Rec::isAttribute(newAttrib));
495  record_.attribs_.push_back (newAttrib);
496  return move(*this);
497  }
498 
499  Mutator&&
500  appendChild (VAL const& newChild)
501  {
502  record_.children_.push_back (newChild);
503  return move(*this);
504  }
505 
506  Mutator&&
507  prependChild (VAL const& newChild)
508  {
509  record_.children_.insert (record_.children_.begin(), newChild);
510  return move(*this);
511  }
512 
513  /* === low-level access (for diff application) === */
514 
516 
531  void buildMutator (BufferHandle);
532 
533  auto
534  exposeToDiff()
535  {
536  return std::tie (record_.attribs_, record_.children_);
537  }
538 
539 
546  VAL const&
548  {
549  if (record_.empty())
550  throw error::State("Record is empty, unable to access (last) element.");
551 
552  if (record_.children_.empty())
553  return record_.attribs_.back();
554  else
555  return record_.children_.back();
556  }
557 
558 
559  /* === extension point for building specific value types === */
560  /*
561  * the following builder functions need to be specialised
562  * to create a Record holding specific value types,
563  * especially for building a tree like structure
564  * with GenNode holding a Record<GenNode>
565  */
566 
567  VAL genNode();
568  VAL genNode(idi::BareEntryID rawID);
569  VAL genNode(string const& symbolicID);
570 
571  template<typename X, typename...ARGS>
572  Mutator&& attrib (string const& key, X&& initialiser, ARGS&& ...args)
573  {
574  set (key, std::forward<X>(initialiser));
575  return attrib (std::forward<ARGS>(args)...);
576  }
577  Mutator&& attrib () { return move(*this); } // argument recursion end
578 
579 
580  template<typename X, typename...ARGS>
581  Mutator&& scope (X const& initialiser, ARGS&& ...args)
582  {
583  appendChild (VAL(initialiser));
584  return scope (std::forward<ARGS>(args)...);
585  }
586  Mutator&& scope () { return move(*this); }
587 
588  };
589 
590 
600  template<typename VAL>
601  inline typename Record<VAL>::Mutator&
602  mutateInPlace (Record<VAL>& record_to_mutate)
603  {
604  return reinterpret_cast<typename Record<VAL>::Mutator &> (record_to_mutate);
605  }
606 
607 
619  template<typename VAL>
620  class RecordRef
621  {
622  using Target = Record<VAL>;
623 
624  Target* record_;
625 
626  public:
629  RecordRef() noexcept
630  : record_(nullptr)
631  { }
632 
635  RecordRef(Target& o) noexcept
636  : record_(&o)
637  { }
638 
640  RecordRef(Target&&) = delete;
641 
642  RecordRef(RecordRef const&) = default;
643  RecordRef(RecordRef &&) = default;
644 
646  RecordRef& operator= (RecordRef const&) = delete;
647  RecordRef& operator= (RecordRef &) = delete;
648 
650  RecordRef&
651  operator= (RecordRef &&o)
652  {
653  std::swap(record_, o.record_);
654  return *this;
655  }
656 
657 
658  explicit
659  operator bool() const
660  {
661  return bool(record_);
662  }
663 
664  bool
665  empty() const
666  {
667  return not record_;
668  }
669 
673  operator Target&() const
674  {
675  if (!record_)
676  throw error::Logic("attempt to dereference an unbound record reference"
677  ,error::LUMIERA_ERROR_BOTTOM_VALUE);
678  return *record_;
679  }
680 
681  Target*
682  get() const noexcept
683  {
684  return record_;
685  }
686 
687  operator string() const
688  {
689  return "Ref->" + (empty()? util::BOTTOM_INDICATOR
690  : string(*record_));
691  }
692 
694  friend bool
695  operator== (RecordRef const& r1, RecordRef const& r2)
696  {
697  return r1.record_ == r2.record_;
698  }
699  friend bool
700  operator!= (RecordRef const& r1, RecordRef const& r2)
701  {
702  return r1.record_ != r2.record_;
703  }
704  };
705 
706 
707 
708 
709  /* === Extension point: Specialisations for attribute handling === */
710 
715  template<>
716  struct RecordSetup<string>
717  {
718  using Storage = std::vector<string>;
719  using ElmIter = typename Storage::const_iterator;
720  using Access = string;
721  };
722 
723 
724 
725 
726  /* default handling defined for Record<string> */
727 
728  template<>
729  inline string
730  Record<string>::extractKey (string const& v)
731  {
732  size_t pos = v.find('=');
733  if (string::npos == pos)
734  return "";
735  else
736  return util::trim (v.substr (0,pos));
737  }
738 
739  template<>
740  inline string
741  Record<string>::extractVal (string const& v)
742  {
743  size_t pos = v.find('=');
744  if (string::npos == pos)
745  return v;
746  else
747  return util::trim (v.substr (pos+1, v.length() - pos));
748  }
749 
750  template<>
751  inline bool
752  Record<string>::isAttribute (string const& v)
753  {
754  return string::npos != v.find('=');
755  }
756 
757  template<>
758  inline bool
759  Record<string>::isTypeID (string const& v)
760  {
761  return isAttribute(v)
762  && "type" == extractKey(v);
763  }
764 
765  template<>
766  inline string
767  Record<string>::extractTypeID (string const& v)
768  {
769  return extractVal(v);
770  }
771 
772  template<>
773  inline string
774  Record<string>::renderAttribute (string const& attrib)
775  {
776  return extractKey(attrib) + " = " + extractVal(attrib);
777  }
778 
779  template<>
780  template<typename X>
781  inline string
782  Record<string>::buildAttribute (string const& key, X&& payload)
783  {
784  return string(key + " = " + extractVal(payload));
785  }
786 
787 
788 
789 
790 
791 
792  /* === Diagnostics === */
793 
794  template<typename VAL>
795  Record<VAL>::operator std::string() const
796  {
797  using util::join;
799 
800  return "Rec("
801  + (TYPE_NIL==type_? "" : type_)
802  + (isnil(this->attribs())? "" : "| "+join (transformIterator (this->attribs(), renderAttribute))+" ")
803  + (isnil(this->scope())? "" : "|{"+join (this->scope())+"}")
804  + ")"
805  ;
806  }
807 
808 
809 
810 }} // namespace lib::diff
811 #endif /*LIB_DIFF_GEN_NODE_H*/
type erased baseclass for building a combined hash and symbolic ID.
Definition: entry-id.hpp:142
friend void iterNext(const Record *, ElmIter &pos)
Implementation of Iteration-logic: pull next element.
Definition: record.hpp:331
Helper template(s) for creating Lumiera Forward Iterators.
Types marked with this mix-in may be moved but not copied.
Definition: nocopy.hpp:58
Record< VAL >::Mutator & mutateInPlace(Record< VAL > &record_to_mutate)
open an existing record for modification in-place.
Definition: record.hpp:602
friend bool checkPoint(const Record *src, ElmIter &pos)
Implementation of Iteration-logic: detect iteration end.
Definition: record.hpp:342
iterator begin() const
default iteration exposes all data within this "object", starting with the attributes ...
Definition: record.hpp:318
Implementation namespace for support and library code.
Record(Mutator const &mut)
copy-initialise (or convert) from the given Mutator instance.
Definition: record.hpp:298
Derived specific exceptions within Lumiera&#39;s exception hierarchy.
Definition: error.hpp:199
Token or Atom with distinct identity.
Definition: symbol.hpp:126
Mix-Ins to allow or prohibit various degrees of copying and cloning.
A handle to allow for safe »remote implantation« of an unknown subclass into a given opaque InPlaceBu...
Definition: record.hpp:113
string Access
data access by value copy
Definition: record.hpp:720
RecordRef(Target &o) noexcept
create a reference bound to the given target; can not be rebound
Definition: record.hpp:635
Tiny helper functions and shortcuts to be used everywhere Consider this header to be effectively incl...
Iterator tool treating pulled data by a custom transformation (function)
Definition: itertools.hpp:763
wrapped record reference.
Definition: record.hpp:620
Lumiera error handling (C++ interface).
RecordRef() noexcept
by default create an invalid ("bottom") reference
Definition: record.hpp:629
Collection of small helpers and convenience shortcuts for diagnostics & formatting.
Helpers for working with iterators based on the pipeline model.
Preconfigured adapters for some STL container standard usage situations.
auto transformIterator(IT const &src, FUN processingFunc)
Build a TransformIter: convenience free function shortcut, picking up the involved types automaticall...
Definition: itertools.hpp:797
object-like record of data.
Definition: record.hpp:150
Adapter for building an implementation of the »Lumiera Forward Iterator« concept. ...
VAL const & accessLast()
get the tail element.
Definition: record.hpp:547