Path.h
1 /*
2 
3 MIT License
4 
5 Copyright (c) 2017 FMI Open Development / Markus Peura, first.last@fmi.fi
6 
7 Permission is hereby granted, free of charge, to any person obtaining a copy
8 of this software and associated documentation files (the "Software"), to deal
9 in the Software without restriction, including without limitation the rights
10 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 copies of the Software, and to permit persons to whom the Software is
12 furnished to do so, subject to the following conditions:
13 
14 The above copyright notice and this permission notice shall be included in all
15 copies or substantial portions of the Software.
16 
17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 SOFTWARE.
24 
25 */
26 /*
27 Part of Rack development has been done in the BALTRAD projects part-financed
28 by the European Union (European Regional Development Fund and European
29 Neighbourhood Partnership Instrument, Baltic Sea Region Programme 2007-2013)
30 */
31 /*
32  * Path.h
33  *
34  * Created on: Jul 21, 2010
35  * Author: mpeura
36  */
37 
38 #ifndef DRAIN_PATH_H_
39 #define DRAIN_PATH_H_
40 
41 #include <drain/UniTuple.h>
42 #include <stdexcept>
43 #include <iostream>
44 #include <string>
45 #include <list>
46 #include <iterator>
47 
48 
49 #include <drain/String.h> // ?
50 #include <drain/Sprinter.h>
51 
52 
53 
54 namespace drain {
55 
57 /*
58  *
59  * In unix style, intermediate separators are accepted but not stored.
60  *
61  * Note that technically, accepting leading, intermediate or trailing separators means also
62  * accepting \e empty path elements, respectively.
63  *
64  * When using strings as path elements, the root is identified with an empty string -- not actually with the separator char like '/'.
65  *
66  */
67 struct PathSeparatorPolicy : UniTuple<bool,3> {
68 
69  // Separator character
70  char character;
71 
73  bool & acceptLeading;
74 
77 
80 
81  PathSeparatorPolicy(char separator='/', bool lead=true, bool repeated=false, bool trail=true) :
82  character(separator), acceptLeading(next()), acceptRepeated(next()), acceptTrailing(next()) {
83  set(lead, repeated, trail);
84  if (!separator)
85  throw std::runtime_error("PathSeparatorPolict (char separator): separator=0, did you mean empty init (\"\")");
86  }
87  PathSeparatorPolicy(const PathSeparatorPolicy &policy) : UniTuple<bool,3>(policy),
88  character(policy.character), acceptLeading(next()), acceptRepeated(next()), acceptTrailing(next()) {
89  };
90 
91 
92 };
93 
94 inline
95 std::ostream & operator<<(std::ostream & ostr, const PathSeparatorPolicy & policy) {
96  ostr << "Separator='" << policy.character << "', policy=" << policy.tuple();
97  return ostr;
98 }
99 
100 
101 
111 template <class T,char SEP='/', bool ALEAD=true, bool AREPEAT=false, bool ATRAIL=true>
112 class Path : public std::list<T> {
113 
114 public:
115 
116  typedef T elem_t;
117 
119 
120  typedef std::list< path_t > list_t;
121 
122  const PathSeparatorPolicy separator;
123 
124  inline
125  Path() : separator(SEP, ALEAD, AREPEAT, ATRAIL){
126  };
127 
129  inline
130  Path(const path_t & p) : std::list<T>(p), separator(p.separator){
131  };
132 
134  inline
135  Path(typename path_t::const_iterator it, typename path_t::const_iterator it2) : separator(SEP, ALEAD, AREPEAT, ATRAIL) {
136  while (it != it2){
137  append(*it);
138  ++it;
139  }
140  };
141 
143  // All the elements are treated as paths.
144  template <typename ... TT>
145  inline
146  Path(const path_t & arg, const TT &... args) : separator(SEP, ALEAD, AREPEAT, ATRAIL){
147  append(arg, args...);
148  };
149 
151  // All the elements are treated as paths.
152  template <typename ... TT>
153  inline
154  Path(const std::string & arg, const TT &... args) : separator(SEP, ALEAD, AREPEAT, ATRAIL){
155  append(arg, args...);
156  };
157 
159  // All the elements are treated as paths.
160  template <typename ... TT>
161  inline
162  Path(const char * arg, const TT &... args) : separator(SEP, ALEAD, AREPEAT, ATRAIL){
163  append(arg, args...);
164  };
165 
166 
168  // Answer: to prevent elem and string
169  /*
170  template <typename ... TT>
171  inline
172  Path(const TT &... args) : separator(SEP, ALEAD, AREPEAT, ATRAIL){ //: separator(separator) {
173  append(args...);
174  };
175  */
176 
177 
178 
179 
180  virtual inline
181  ~Path(){};
182 
183  // Design principle: handle all strings as paths!
184  // This means that if path elements are strings, assigning elements goes through an "extra" path check.
185  // If string arguments were accepted directly as elements, separator characters could be passed in the elements.
186 
187  template <typename ... TT>
188  void set(const TT &... args) {
189  this->clear();
190  append(args...);
191  }
192 
194  // needed?
195  template <typename T2, typename ... TT>
196  void append(const T2 & arg, const TT &... args) {
197  _append(arg); // replace with appendElem(elem), remove _append?
198  append(args...);
199  }
200 
201  template <typename ... TT>
202  void append(const path_t &p, const TT &... args) {
203  this->insert(this->end(), p.begin(), p.end());
204  append(args...);
205  }
206 
207  template <typename ... TT>
208  void append(const char * arg, const TT &... args) {
209  _appendPath(arg, 0);
210  append(args...);
211  }
212 
213  template <typename ... TT>
214  void append(const std::string &arg, const TT &... args) {
215  _appendPath(arg, 0);
216  append(args...);
217  }
218 
219 
221  inline
222  void appendPath(const char *p){
223  _appendPath(p, 0);
224  }
225 
226  // Reverse convenience...
227  inline
228  void appendPath(const path_t &p){
229  appendPath(p);
230  }
231 
232 
234  void appendElem(const elem_t & elem){
235 
236  if (!elem.empty()){
237  // Always allow non-empty element
238  // std::cerr << __FUNCTION__ << ":" << elem << '\n';
239  if (!separator.acceptRepeated){
240  //std::cerr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << ":" << " validate " << *this << " + " << elem << '\n';
241  //removeTrailing();
242  trimTail();
243  }
244  //stripTail();
245  this->push_back(elem);
246  }
247  // elem is EMPTY: -----------------------------------------------------------
248  else if (this->empty()){
249  if (separator.acceptLeading)
250  this->push_back(elem); // empty
251  // std::cerr << __FUNCTION__ << " leading=" << separator.acceptLeading << " "<< separator.character << '\n';
252  }
253  else if (this->back().empty()){
254  //std::cerr << __FUNCTION__<< " repeating=" << separator.acceptRepeated << " " << separator.character << '\n';
255  if (separator.acceptRepeated){
256  this->push_back(elem);
257  }
258  }
259  else if (separator.acceptTrailing){
260  // std::cerr << __FUNCTION__ << " trailing=" << separator.acceptTrailing << " " << separator.character << '\n';
261  this->push_back(elem); // empty
262  }
263  else {
264  std::cerr << __FUNCTION__ << "? failed in appending: " << sprinter(*this) << '\n';
265  }
266 
267  };
268 
270  //
279  inline
280  bool isRoot() const {
281  return ((this->size()==1) && this->front().empty());
282  //return separator.acceptLeading && ((this->size()==1) && this->front().empty());
283  }
284 
286 
291  inline
292  bool hasRoot() const {
293  if (!this->empty()){
294  return this->front().empty();
295  }
296  else {
297  return false;
298  }
299  }
300 
302 
307  inline
309  if (!this->hasRoot()){
310  this->push_front(elem_t());
311  }
312  return *this;
313  }
314 
315 
317 
321  void trimHead(bool COMPLETE=false){
322  const size_t minLength = COMPLETE ? 0 : 1;
323  while (this->size() > minLength){
324  //while (!this->empty()){
325  if (!this->front().empty()){
326  return;
327  }
328  this->pop_front();
329  //this->trimHead();
330  }
331  }
332 
333 
335  void trimTail(bool COMPLETE=false){ //
336  const size_t minLength = COMPLETE ? 0 : 1;
337  while (this->size() > minLength){
338  //while (!this->empty()){
339  if (!this->back().empty()){
340  return;
341  }
342  this->pop_back();
343  //this->trimTail();
344  }
345  }
346 
348 
360 
362  inline
363  void simplify(){
364  simplifyHead();
365  simplifyTail();
366  }
367 
369 
371  void simplifyHead(){
372 
373  if (this->empty())
374  return;
375 
376  if (this->front().empty()){
377  typename path_t::const_iterator it0 = ++this->begin();
378  typename path_t::const_iterator it = it0;
379  while (it != this->end()) {
380  if (!it->empty()){
381  break;
382  }
383  ++it;
384  }
385  if (it != it0)
386  this->erase(it0,it);
387  }
388 
389 
390  }
391 
393 
395  void simplifyTail(){
396 
397  if (this->empty())
398  return;
399 
400  if (this->back().empty()){
401  typename path_t::const_iterator it0 = --this->end();
402  typename path_t::const_iterator it = it0;
403 
404  while (it != this->begin()) {
405  --it;
406  if (!it->empty()){
407  ++it;
408  break;
409  }
410  }
411 
412  if (it != it0)
413  this->erase(it,it0);
414  }
415 
416 
417  }
418 
419 
420 
422 
425  path_t & operator=(const path_t & p){
426 
427  if (&p != this){
428  // Yes, important self-check.
429  // Otherwise clear() also makes the"source" empty.
430  set(p);
431  }
432  // std::list<T>::operator=(p);
433  // return assignPa(*this);
434  return *this;
435  }
436 
438 
441  template <class T2>
442  path_t & operator=(const Path<T2> & p){
443  set(p);
444  return *this;
445  }
446 
447 
448  inline
449  path_t & operator=(const std::string & p){
450  set(p);
451  return *this;
452  }
453 
454  inline
455  path_t & operator=(const char *p){
456  set(p);
457  return *this;
458  }
459 
460  // Note: this would be ambiguous!
461  // If (elem_t == std::string), elem cannot be assigned directly, because string suggest full path assignment, at least
462  // path_t & operator=(const elem_t & e)
463 
465  /*
466  inline
467  path_t & operator<<(const char *elem){
468  *this <<
469  }
470  */
471 
472 
473  // TODO: replace these with single
474  /*
475  template <class T2>
476  path_t & operator<<(const T2 & arg){
477  append(args);
478  return *this;
479  }
480  */
481 
482 
484  path_t & operator<<(const elem_t & elem){
485  appendElem(elem);
486  return *this;
487  }
488 
489  inline
490  path_t & operator<<(const path_t & path){
491  this->insert(this->end(), path.begin(), path.end());
492  return *this;
493  }
494 
495 
496  // Experimental
497  template <class T2>
498  path_t & operator<<(const T2 & strlike){
499  appendPath((const std::string &) strlike);
500  return *this;
501  }
502 
503 
505  path_t & operator>>(elem_t & e){
506  e = this->back();
507  this->pop_back();
508  return *this;
509  }
510 
512 
517  virtual
518  std::ostream & toStream(std::ostream & ostr) const { //, char dirSeparator = 0) const {
519 
520  if (isRoot())
521  ostr << separator.character;
522  else {
523  static const SprinterLayout layout("/", "", "", "");
524  layout.arrayChars.separator = separator.character;
525  // SprinterLayout layout;
526  // layout.arrayChars.set(0, separator.character,0);
527  // layout.stringChars.fill(0);
528  Sprinter::sequenceToStream(ostr, *this, layout);
529  }
530 
531  //Sprinter::toStream(ostr, *this, layout);
532  return ostr;
533  }
534 
535  virtual
536  std::string str() const {
537  std::stringstream sstr;
538  toStream(sstr);
539  return sstr.str();
540  }
541 
542  virtual
543  operator std::string() const {
544  return str();
545  }
546 
547  std::ostream & debug(std::ostream & ostr = std::cout) const {
548  //ostr << "Path<" << typeid(T).name() << "> " << separator << "\n";
549  ostr << "Path elems:" << this->size() << " sep:" << separator << " typeid: " << typeid(T).name() << "\n";
550  ostr << *this << '\n';
551  int i=0;
552  for (typename path_t::const_iterator it = this->begin(); it != this->end(); ++it) {
553  //if (it->empty())
554  // ostr << " " << i << '\t' << "{empty}" << '\n'; //else
555  ostr << " " << i << '\t' << *it << '\n';
556  ++i;
557  }
558  return ostr;
559  }
560 
561 protected:
562 
564  void _append(const elem_t &elem){
565  appendElem(elem);
566  }
567 
568 
569  // Handler for all the rest (non elem_t) arguments.
571  template <typename T2>
572  void _append(const T2 & p){
573  appendElem(p);
574  //_appendPath(p);
575  }
576 
578  void _appendPath(const std::string & p, size_t start=0){
579 
580  if (start == p.length()){
581  // Last char in the string has been passed by one, meaning that: previous elem was empty
582  // That is: the previous char was a separator.
583  //appendElems(elem_t()); // Try to append empty.
584  appendElem(elem_t()); // Try to append empty.
585  return;
586  }
587 
588  // Append next segment, ie. up to next separator char.
589  size_t nextSep = p.find(separator.character, start);
590 
591  // Remaining string...
592  if (nextSep==std::string::npos){
593  // ... is a single element
594  appendElem(p.substr(start));
595  return;
596  }
597  else {
598  // ... contains separator, hence contains several elements
599  appendElem(p.substr(start, nextSep - start)); // maybe empty, nextSep==start
600  _appendPath(p, nextSep + 1);
601  }
602 
603  }
604 
605 
606 protected:
607 
609  void append(){
610  }
611 
613  //void appendElems(){
614  //}
615 
617  //void setElems(){
618  // };
619 
620 };
621 
622 
623 
624 template <class T, char SEP='/', bool ALEAD=true, bool AREPEAT=false, bool ATRAIL=true>
625 inline
626 std::ostream & operator<<(std::ostream & ostr, const Path<T,SEP,ALEAD,AREPEAT,ATRAIL> & p) {
627  return p.toStream(ostr);
628 }
629 
630 
631 template <class T, char SEP='/', bool ALEAD=true, bool AREPEAT=false, bool ATRAIL=true>
632 inline
633 std::istream & operator>>(std::istream &istr, Path<T,SEP,ALEAD,AREPEAT,ATRAIL> & p) {
634  std::string s;
635  istr >> s;
636  p = s;
637  return istr;
638 }
639 
640 /*
641 template <class T>
642 inline
643 Path<T> & operator<<(Path<T> & path, const Path<T> & path2){
644  path.insert(path.end(), path2.begin(), path2.end());
645  return path;
646 }
647 */
648 
649 
650 }
651 
652 #endif /* Path_H_ */
653 
654 // Drain
Definition: Path.h:112
virtual ~Path()
Why the three above instead of this?
Definition: Path.h:181
void simplify()
ORIGINAL. Removes trailing empty elements, except for the root itself.
Definition: Path.h:363
void _append(const T2 &p)
Handler for all the rest args, assumed convertable to elem_t.
Definition: Path.h:572
void simplifyTail()
Remove duplicates of empty elements from the end.
Definition: Path.h:395
void _appendPath(const std::string &p, size_t start=0)
Extract elements from the string, starting at position i.
Definition: Path.h:578
path_t & operator<<(const elem_t &elem)
Append an element, unless empty string.
Definition: Path.h:484
void appendElem(const elem_t &elem)
Main method for adding elements.
Definition: Path.h:234
void appendPath(const char *p)
Specialized handler for strings (note, possibly: elem_t==std:::string)
Definition: Path.h:222
void simplifyHead()
Remove duplicates of empty elements from the start.
Definition: Path.h:371
path_t & operator=(const Path< T2 > &p)
Conversion from str path type.
Definition: Path.h:442
Path(const char *arg, const TT &... args)
Initialize with a path.
Definition: Path.h:162
bool isRoot() const
Returns true, if the path as only one element which is empty. An empty path is not a root.
Definition: Path.h:280
Path(const path_t &arg, const TT &... args)
Initialize with a path.
Definition: Path.h:146
Path(const path_t &p)
Copy constructor.
Definition: Path.h:130
virtual std::ostream & toStream(std::ostream &ostr) const
Path is written like a list, adding the separator between items. Exception: root is displayed as sepa...
Definition: Path.h:518
Path(typename path_t::const_iterator it, typename path_t::const_iterator it2)
Secondary copy constructor. Handy for creating a parent path, for example.
Definition: Path.h:135
void append(const T2 &arg, const TT &... args)
Append path with element(s), path(s) or string(s).
Definition: Path.h:196
Path(const std::string &arg, const TT &... args)
Initialize with a path.
Definition: Path.h:154
path_t & ensureRoot()
Returns true, if the path is not empty and the first element is empty.
Definition: Path.h:308
void trimTail(bool COMPLETE=false)
Removes trailing empty elements. The resulting string presentation will not end with the separator.
Definition: Path.h:335
void append()
Terminal function for variadic templates.
Definition: Path.h:609
void trimHead(bool COMPLETE=false)
Removes leading empty elements. The resulting string presentation will not start with the separator.
Definition: Path.h:321
bool hasRoot() const
Returns true, if the path is not empty and the first element is empty.
Definition: Path.h:292
void _append(const elem_t &elem)
"Default" append function.
Definition: Path.h:564
path_t & operator=(const path_t &p)
Assigns a path directly with std::list assignment.
Definition: Path.h:425
path_t & operator>>(elem_t &e)
Extract last element.
Definition: Path.h:505
static std::ostream & sequenceToStream(std::ostream &ostr, const T &x, const SprinterLayout &layout)
Convenience: if sequence type (array, list, set, map) not given, assume array.
Definition: Sprinter.h:321
Tuple of N elements of type T.
Definition: UniTuple.h:65
Definition: DataSelector.cpp:1277
Determines if separators will be stored in the path.
Definition: Path.h:67
bool & acceptRepeated
If true, allow empty elements in the middle (appearing as repeated separators)
Definition: Path.h:76
bool & acceptTrailing
If true, allow trailing empty elements (appearing as repeated separators)
Definition: Path.h:79
bool & acceptLeading
If true, allow leading empties (ie leading separators) // Accepts one. to start with.
Definition: Path.h:73
Definition: Sprinter.h:137