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)
5  2015, Hermann Vosseler <Ichthyostega@web.de>
6 
7   **Lumiera** is free software; you can redistribute it and/or modify it
8   under the terms of the GNU General Public License as published by the
9   Free Software Foundation; either version 2 of the License, or (at your
10   option) any later version. See the file COPYING for further details.
11 
12 */
13 
14 
81 #ifndef LIB_DIFF_RECORD_H
82 #define LIB_DIFF_RECORD_H
83 
84 
85 #include "lib/error.hpp"
86 #include "lib/nocopy.hpp"
87 #include "lib/iter-adapter.hpp"
88 #include "lib/iter-adapter-stl.hpp"
89 #include "lib/itertools.hpp"
90 #include "lib/format-util.hpp"
91 #include "lib/util.hpp"
92 
93 
94 #include <algorithm>
95 #include <utility>
96 #include <vector>
97 #include <string>
98 
99 
100 
101 namespace lib {
102 
103  template<class BA, class DEFAULT>
105 
106 namespace idi {
107  class BareEntryID;
108 }
109 namespace diff{
110 
111  namespace error = lumiera::error;
112 
113  using util::isnil;
114  using std::string;
115 
116  class TreeMutator;
117 
118  template<typename VAL>
119  struct RecordSetup;
120 
121 
140  template<typename VAL>
141  class Record
142  {
143  using Storage = typename RecordSetup<VAL>::Storage;
144  using ElmIter = typename RecordSetup<VAL>::ElmIter;
145  using Access = typename RecordSetup<VAL>::Access;
146 
147 
148  string type_;
149  Storage attribs_;
150  Storage children_;
151 
152  public:
153  static const string TYPE_NIL;
154  static const Symbol TYPE_NIL_SYM;
155 
156  Record()
157  : type_(TYPE_NIL)
158  { }
159 
160  template<typename A, typename C>
161  Record(Symbol typeID, A&& att, C&& chi)
162  : type_(isnil(typeID)? TYPE_NIL:string(typeID))
163  , attribs_(std::forward<A> (att))
164  , children_(std::forward<C> (chi))
165  { }
166 
167  template<typename A, typename C>
168  Record(Symbol typeID, std::initializer_list<A> const&& att
169  , std::initializer_list<C> const&& chi)
170  : type_(isnil(typeID)? TYPE_NIL:string(typeID))
171  , attribs_(att)
172  , children_(chi)
173  { }
174 
175  template<typename SEQ>
176  explicit
177  Record (SEQ const& con)
178  : type_(TYPE_NIL)
179  {
180  auto p = std::begin(con);
181  auto e = std::end(con);
182  for ( ; p!=e && isAttribute(*p); ++p)
183  if (isTypeID (*p))
184  type_ = extractTypeID(*p);
185  else
186  attribs_.push_back (*p);
187  for ( ; p!=e; ++p)
188  children_.push_back (*p);
189  }
190 
191  Record (std::initializer_list<VAL> const&& ili)
192  : Record(ili)
193  { }
194 
195  // all default copy operations acceptable
196 
197 
199  operator std::string() const;
200 
201 
202  size_t
203  attribSize() const
204  {
205  return attribs_.size();
206  }
207 
208  size_t
209  childSize() const
210  {
211  return children_.size();
212  }
213 
214  bool
215  empty() const
216  {
217  return attribs_.empty()
218  and children_.empty();
219  }
220 
221 
222  string
223  getType() const
224  {
225  return type_;
226  }
227 
228  bool
229  hasAttribute (string key) const
230  {
231  return attribs_.end() != findKey(key);
232  }
233 
234  bool
235  contains (VAL const& val) const
236  {
237  return util::contains (children_, val);
238  }
239 
240  Access
241  get (string key) const
242  {
243  ElmIter found = findKey (key);
244  if (attribs_.end() == found)
245  throw error::Invalid ("Record has no attribute \""+key+"\"");
246  else
247  return extractVal (*found);
248  }
249 
250  Access
251  child (size_t idx) const
252  {
253  if (children_.size() <= idx)
254  throw error::Invalid ("Child index " +util::toString(idx)
255  +" out of bounds [0.."+util::toString(children_.size())
256  +"["
257  ,error::LUMIERA_ERROR_INDEX_BOUNDS);
258  return children_[idx];
259  }
260 
261 
272  class Mutator;
273 
274 
275 
289  Record (Mutator const& mut)
290  : Record((Record const&) mut)
291  { }
292  explicit
293  Record (Mutator && mut)
294  : Record(std::move (Record(mut)))
295  { }
296 
297  friend class Mutator;
298 
299 
300 
301  /* ==== Exposing scope and contents for iteration ====== */
302 
304  using scopeIter = typename iter_stl::_SeqT<const Storage>::Range;
307 
309  iterator begin () const { return iterator(this, attribs_.empty()? children_.begin() : attribs_.begin()); }
310  iterator end () const { return iterator(); }
311 
312  scopeIter attribs() const { return iter_stl::eachElm(attribs_); }
313  scopeIter scope() const { return iter_stl::eachElm(children_); }
314 
315  keyIter keys() const { return transformIterator(attribs(), extractKey); }
316  valIter vals() const { return transformIterator(attribs(), extractVal); }
317 
318  protected: /* ==== API for the IterAdapter ==== */
319 
321  friend void
322  iterNext (const Record*, ElmIter& pos)
323  {
324  ++pos;
325  }
326 
332  friend bool
333  checkPoint (const Record* src, ElmIter& pos)
334  {
335  REQUIRE (src);
336  static const ElmIter END;
337  if (pos != END && pos == src->attribs_.end() && !src->children_.empty())
338  {
339  pos = src->children_.begin();
340  return true;
341  }
342  else
343  if (pos != END && (pos != src->children_.end()))
344  return true;
345  else
346  {
347  pos = END;
348  return false;
349  } }
350 
351  private:
352  /* === abstract attribute handling : needs specialisation === */
353  static bool isAttribute (VAL const& v);
354  static bool isTypeID (VAL const& v);
355  static string extractTypeID (VAL const& v);
356  static string renderAttribute (VAL const& a);
357  static string extractKey (VAL const& v);
358  static Access extractVal (VAL const& v);
359  template<typename X>
360  static VAL buildAttribute (string const& key, X&& payload);
361 
362 
363  ElmIter
364  findKey (string key) const
365  {
366  return std::find_if (attribs_.begin()
367  ,attribs_.end()
368  ,[=](VAL const& elm)
369  {
370  return key == extractKey(elm);
371  });
372  }
373 
374 
375  friend bool
376  operator== (Record const& r1, Record const& r2)
377  {
378  return r1.type_ == r2.type_
379  and r1.attribs_ == r2.attribs_
380  and r1.children_ == r2.children_;
381  }
382 
383  friend bool
384  operator!= (Record const& r1, Record const& r2)
385  {
386  return not (r1 == r2);
387  }
388  };
389 
390  template<typename VAL>
391  const Symbol Record<VAL>::TYPE_NIL_SYM = "NIL";
392 
393  template<typename VAL>
394  const string Record<VAL>::TYPE_NIL = string(TYPE_NIL_SYM);
395 
396 
397 
398  template<typename VAL>
399  class Record<VAL>::Mutator
401  {
402  using Rec = Record<VAL>;
403 
404  Rec record_;
405 
406  public:
407  Mutator()
408  : record_()
409  { }
410 
411  explicit
412  Mutator (Rec const& startingPoint)
413  : record_(startingPoint)
414  { }
415 
416  explicit
417  Mutator (Rec && startingPoint)
418  : record_(std::move (startingPoint))
419  { }
420 
421  operator Rec&()
422  {
423  return record_;
424  }
425 
426  void
427  swap (Rec& existingInstance) noexcept
428  {
429  using std::swap;
430  swap (existingInstance, record_);
431  }
432 
433  bool
434  empty() const
435  {
436  return record_.empty();
437  }
438 
439 
440  /* === functions to alter contents === */
441 
442  void
443  setType (string const& newTypeID)
444  {
445  record_.type_ = newTypeID;
446  }
447 
448  Mutator&&
449  type (string const& typeID)
450  {
451  setType (typeID);
452  return move(*this);
453  }
454 
455  template<typename X>
456  Mutator&&
457  set (string const& key, X&& content)
458  {
459  VAL attribute(Rec::buildAttribute (key, std::forward<X>(content)));
460  return set (std::move (attribute));
461  }
462 
463  Mutator&&
464  set (VAL&& attribute)
465  {
466  string key = Rec::extractKey(attribute);
467  if (isnil (key))
468  throw error::Invalid ("Attempt to set an attribute with empty key");
469 
470  Rec::Storage& as =record_.attribs_;
471  auto found = std::find_if (as.begin(),as.end()
472  ,[=](VAL const& elm)
473  {
474  return key == extractKey(elm);
475  });
476  if (as.end() == found)
477  as.push_back (std::forward<VAL> (attribute));
478  else
479  (*found) = (std::forward<VAL> (attribute));
480  return move(*this);
481  }
482 
483  Mutator&&
484  appendAttrib (VAL const& newAttrib)
485  {
486  REQUIRE (Rec::isAttribute(newAttrib));
487  record_.attribs_.push_back (newAttrib);
488  return move(*this);
489  }
490 
491  Mutator&&
492  appendChild (VAL const& newChild)
493  {
494  record_.children_.push_back (newChild);
495  return move(*this);
496  }
497 
498  Mutator&&
499  prependChild (VAL const& newChild)
500  {
501  record_.children_.insert (record_.children_.begin(), newChild);
502  return move(*this);
503  }
504 
505  /* === low-level access (for diff application) === */
506 
508 
523  void buildMutator (BufferHandle);
524 
525  auto
526  exposeToDiff()
527  {
528  return std::tie (record_.attribs_, record_.children_);
529  }
530 
531 
538  VAL const&
540  {
541  if (record_.empty())
542  throw error::State("Record is empty, unable to access (last) element.");
543 
544  if (record_.children_.empty())
545  return record_.attribs_.back();
546  else
547  return record_.children_.back();
548  }
549 
550 
551  /* === extension point for building specific value types === */
552  /*
553  * the following builder functions need to be specialised
554  * to create a Record holding specific value types,
555  * especially for building a tree like structure
556  * with GenNode holding a Record<GenNode>
557  */
558 
559  VAL genNode();
560  VAL genNode(idi::BareEntryID rawID);
561  VAL genNode(string const& symbolicID);
562 
563  template<typename X, typename...ARGS>
564  Mutator&& attrib (string const& key, X&& initialiser, ARGS&& ...args)
565  {
566  set (key, std::forward<X>(initialiser));
567  return attrib (std::forward<ARGS>(args)...);
568  }
569  Mutator&& attrib () { return move(*this); } // argument recursion end
570 
571 
572  template<typename X, typename...ARGS>
573  Mutator&& scope (X const& initialiser, ARGS&& ...args)
574  {
575  appendChild (VAL(initialiser));
576  return scope (std::forward<ARGS>(args)...);
577  }
578  Mutator&& scope () { return move(*this); }
579 
580  };
581 
582 
592  template<typename VAL>
593  inline typename Record<VAL>::Mutator&
594  mutateInPlace (Record<VAL>& record_to_mutate)
595  {
596  return reinterpret_cast<typename Record<VAL>::Mutator &> (record_to_mutate);
597  }
598 
599 
611  template<typename VAL>
612  class RecordRef
613  {
614  using Target = Record<VAL>;
615 
616  Target* record_;
617 
618  public:
621  RecordRef() noexcept
622  : record_(nullptr)
623  { }
624 
627  RecordRef(Target& o) noexcept
628  : record_(&o)
629  { }
630 
632  RecordRef(Target&&) = delete;
633 
634  RecordRef(RecordRef const&) = default;
635  RecordRef(RecordRef &&) = default;
636 
638  RecordRef& operator= (RecordRef const&) = delete;
639  RecordRef& operator= (RecordRef &) = delete;
640 
642  RecordRef&
643  operator= (RecordRef &&o)
644  {
645  std::swap (record_, o.record_);
646  return *this;
647  }
648 
649 
650  explicit
651  operator bool() const
652  {
653  return bool(record_);
654  }
655 
656  bool
657  empty() const
658  {
659  return not record_;
660  }
661 
665  operator Target&() const
666  {
667  if (!record_)
668  throw error::Logic("attempt to dereference an unbound record reference"
669  ,error::LUMIERA_ERROR_BOTTOM_VALUE);
670  return *record_;
671  }
672 
673  Target*
674  get() const noexcept
675  {
676  return record_;
677  }
678 
679  operator string() const
680  {
681  return "Ref->" + (empty()? util::BOTTOM_INDICATOR
682  : string(*record_));
683  }
684 
686  friend bool
687  operator== (RecordRef const& r1, RecordRef const& r2)
688  {
689  return r1.record_ == r2.record_;
690  }
691  friend bool
692  operator!= (RecordRef const& r1, RecordRef const& r2)
693  {
694  return r1.record_ != r2.record_;
695  }
696  };
697 
698 
699 
700 
701  /* === Extension point: Specialisations for attribute handling === */
702 
707  template<>
708  struct RecordSetup<string>
709  {
710  using Storage = std::vector<string>;
711  using ElmIter = typename Storage::const_iterator;
712  using Access = string;
713  };
714 
715 
716 
717 
718  /* default handling defined for Record<string> */
719 
720  template<>
721  inline string
722  Record<string>::extractKey (string const& v)
723  {
724  size_t pos = v.find('=');
725  if (string::npos == pos)
726  return "";
727  else
728  return util::trim (v.substr (0,pos));
729  }
730 
731  template<>
732  inline string
733  Record<string>::extractVal (string const& v)
734  {
735  size_t pos = v.find('=');
736  if (string::npos == pos)
737  return v;
738  else
739  return util::trim (v.substr (pos+1, v.length() - pos));
740  }
741 
742  template<>
743  inline bool
744  Record<string>::isAttribute (string const& v)
745  {
746  return string::npos != v.find('=');
747  }
748 
749  template<>
750  inline bool
751  Record<string>::isTypeID (string const& v)
752  {
753  return isAttribute(v)
754  && "type" == extractKey(v);
755  }
756 
757  template<>
758  inline string
759  Record<string>::extractTypeID (string const& v)
760  {
761  return extractVal(v);
762  }
763 
764  template<>
765  inline string
766  Record<string>::renderAttribute (string const& attrib)
767  {
768  return extractKey(attrib) + " = " + extractVal(attrib);
769  }
770 
771  template<>
772  template<typename X>
773  inline string
774  Record<string>::buildAttribute (string const& key, X&& payload)
775  {
776  return string(key + " = " + extractVal(payload));
777  }
778 
779 
780 
781 
782 
783 
784  /* === Diagnostics === */
785 
786  template<typename VAL>
787  Record<VAL>::operator std::string() const
788  {
789  using util::join;
791 
792  return "Rec("
793  + (TYPE_NIL==type_? "" : type_)
794  + (isnil(this->attribs())? "" : "| "+join (transformIterator (this->attribs(), renderAttribute))+" ")
795  + (isnil(this->scope())? "" : "|{"+join (this->scope())+"}")
796  + ")"
797  ;
798  }
799 
800 
801 
802 }} // namespace lib::diff
803 #endif /*LIB_DIFF_GEN_NODE_H*/
type erased baseclass for building a combined hash and symbolic ID.
Definition: entry-id.hpp:133
friend void iterNext(const Record *, ElmIter &pos)
Implementation of Iteration-logic: pull next element.
Definition: record.hpp:322
Helper template(s) for creating Lumiera Forward Iterators.
Types marked with this mix-in may be moved but not copied.
Definition: nocopy.hpp:49
Record< VAL >::Mutator & mutateInPlace(Record< VAL > &record_to_mutate)
open an existing record for modification in-place.
Definition: record.hpp:594
friend bool checkPoint(const Record *src, ElmIter &pos)
Implementation of Iteration-logic: detect iteration end.
Definition: record.hpp:333
iterator begin() const
default iteration exposes all data within this "object", starting with the attributes ...
Definition: record.hpp:309
Implementation namespace for support and library code.
Record(Mutator const &mut)
copy-initialise (or convert) from the given Mutator instance.
Definition: record.hpp:289
Derived specific exceptions within Lumiera&#39;s exception hierarchy.
Definition: error.hpp:190
Token or Atom with distinct identity.
Definition: symbol.hpp:117
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:104
string Access
data access by value copy
Definition: record.hpp:712
RecordRef(Target &o) noexcept
create a reference bound to the given target; can not be rebound
Definition: record.hpp:627
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:754
wrapped record reference.
Definition: record.hpp:612
Lumiera error handling (C++ interface).
RecordRef() noexcept
by default create an invalid ("bottom") reference
Definition: record.hpp:621
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:788
object-like record of data.
Definition: record.hpp:141
Adapter for building an implementation of the »Lumiera Forward Iterator« concept. ...
VAL const & accessLast()
get the tail element.
Definition: record.hpp:539