Loading...
Searching...
No Matches
Path.h
1/*
2
3MIT License
4
5Copyright (c) 2017 FMI Open Development / Markus Peura, first.last@fmi.fi
6
7Permission is hereby granted, free of charge, to any person obtaining a copy
8of this software and associated documentation files (the "Software"), to deal
9in the Software without restriction, including without limitation the rights
10to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11copies of the Software, and to permit persons to whom the Software is
12furnished to do so, subject to the following conditions:
13
14The above copyright notice and this permission notice shall be included in all
15copies or substantial portions of the Software.
16
17THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23SOFTWARE.
24
25*/
26/*
27Part of Rack development has been done in the BALTRAD projects part-financed
28by the European Union (European Regional Development Fund and European
29Neighbourhood 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/StringTools.h> // ?
50#include <drain/Sprinter.h>
51
52
53
54namespace 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 * Future option: const version implemended as fully templated class (perhaps with const UniTuple ).
67 */
68struct PathSeparatorPolicy : UniTuple<bool,3> {
69
70 // Separator character
71 char character;
72
75
78
81
82 PathSeparatorPolicy(char separator='/', bool lead=true, bool repeated=false, bool trail=true) :
83 character(separator), acceptLeading(next()), acceptRepeated(next()), acceptTrailing(next()) {
84 set(lead, repeated, trail);
85 if (!separator)
86 throw std::runtime_error("PathSeparatorPolict (char separator): separator=0, did you mean empty init (\"\")");
87 }
88 PathSeparatorPolicy(const PathSeparatorPolicy &policy) : UniTuple<bool,3>(policy),
89 character(policy.character), acceptLeading(next()), acceptRepeated(next()), acceptTrailing(next()) {
90 };
91
92
93};
94
95DRAIN_TYPENAME(PathSeparatorPolicy);
96
97/*
98template <class T,char SEP, bool ALEAD, bool AREPEAT, bool ATRAIL>
99struct TypeName<Path<T,SEP, ALEAD, AREPEAT, ATRAIL> > {
100
101 typedef Path<T,SEP, ALEAD, AREPEAT, ATRAIL> path_t;
102
103 static
104 const std::string & str(){
105
106 static const std::string name = drain::StringBuilder<>(
107 "Path<", TypeName<T>::str(), ">[", SEP?SEP:'?', "](", ALEAD, AREPEAT, ATRAIL, ')');
108 //'<', drain::Type::call<drain::simpleName>(typeid(typename tree_t::node_data_t)), '>');
109 return name;
110 }
111
112};
113*/
114
115inline
116std::ostream & operator<<(std::ostream & ostr, const PathSeparatorPolicy & policy) {
117 ostr << "PathSeparatorPolicy";
118 if (policy.character){
119 ostr << '[' << policy.character << ']';
120 }
121 ostr << '(' << policy.tuple() << ')';
122 return ostr;
123}
124
125
126
136template <class T,char SEP='/', bool ALEAD=true, bool AREPEAT=false, bool ATRAIL=true>
137class Path : public std::list<T> {
138
139public:
140
141 typedef T elem_t;
142
144
145 typedef std::list< path_t > list_t;
146
147 // typedef Path<T,SEP,ALEAD,AREPEAT,ATRAIL> path_t;
148
149 // const PathSeparatorPolicy separator;
150 static
151 const PathSeparatorPolicy separator;
152
153 inline
154 Path(){ // : separator(SEP, ALEAD, AREPEAT, ATRAIL){
155 };
156
158 inline
159 Path(const path_t & p) : std::list<T>(p){ // , separator(p.separator){
160 };
161
163 inline
164 Path(typename path_t::const_iterator it, typename path_t::const_iterator it2){ // : separator(SEP, ALEAD, AREPEAT, ATRAIL) {
165 while (it != it2){
166 append(*it);
167 ++it;
168 }
169 };
170
172 // All the elements are treated as paths.
173 template <typename ... TT>
174 inline
175 Path(const path_t & arg, const TT &... args){ // : separator(SEP, ALEAD, AREPEAT, ATRAIL){
176 append(arg, args...);
177 };
178
180 // All the elements are treated as paths.
181 template <typename ... TT>
182 inline
183 Path(const std::string & arg, const TT &... args){ // : separator(SEP, ALEAD, AREPEAT, ATRAIL){
184 append(arg, args...);
185 };
186
188 // All the elements are treated as paths.
189 template <typename ... TT>
190 inline
191 Path(const char * arg, const TT &... args){ // : separator(SEP, ALEAD, AREPEAT, ATRAIL){
192 append(arg, args...);
193 };
194
195
197 // Answer: to prevent elem and string
198 /*
199 template <typename ... TT>
200 inline
201 Path(const TT &... args) : separator(SEP, ALEAD, AREPEAT, ATRAIL){ //: separator(separator) {
202 append(args...);
203 };
204 */
205
206
207
208
209 virtual inline
210 ~Path(){};
211
212 // Design principle: handle all strings as paths!
213 // This means that if path elements are strings, assigning elements goes through an "extra" path check.
214 // If string arguments were accepted directly as elements, separator characters could be passed in the elements.
215
216 template <typename ... TT>
217 void set(const TT &... args) {
218 this->clear();
219 append(args...);
220 }
221
223 // needed?
224 template <typename T2, typename ... TT>
225 void append(const T2 & arg, const TT &... args) {
226 //append_(arg); // replace with appendElem(elem), remove _append?
227 appendElem(arg); // replace with appendElem(elem), remove _append?
228 append(args...);
229 }
230
231 template <typename ... TT>
232 void append(const path_t &p, const TT &... args) {
233 this->insert(this->end(), p.begin(), p.end());
234 append(args...);
235 }
236
238 template <typename ... TT>
239 void append(char c, const TT &... args) {
240 if (!c){
241 std::cerr << __FILE__ << ' ' << __FUNCTION__ << "::" << " null separator char \n";
242 }
243 else if (c==separator.character){
244 appendElem(elem_t());
245 }
246 else {
247 appendElem(std::string(1,c));
248 }
249 append(args...);
250 }
251
253 template <typename ... TT>
254 void append(const char * arg, const TT &... args) {
255 appendSubStr(arg, 0);
256 append(args...);
257 }
258
260 template <typename ... TT>
261 void append(const std::string &arg, const TT &... args) {
262 appendSubStr(arg, 0);
263 append(args...);
264 }
265
266
268 inline
269 void appendPath(const char *p){
270 appendSubStr(p, 0);
271 }
272
273 // Reverse convenience...
274 inline
275 void appendPath(const path_t &p){
276 appendPath(p);
277 }
278
279
281 void appendElem(const elem_t & elem){
282
283 if (!elem.empty()){
284 // Always allow non-empty element
285 // std::cerr << __FUNCTION__ << ":" << elem << '\n';
286 if (!separator.acceptRepeated){
287 //std::cerr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << ":" << " validate " << *this << " + " << elem << '\n';
288 //removeTrailing();
289 trimTail();
290 }
291 //stripTail();
292 this->push_back(elem);
293 }
294 // elem is EMPTY: -----------------------------------------------------------
295 else if (this->empty()){
296 if (separator.acceptLeading)
297 this->push_back(elem); // empty
298 // std::cerr << __FUNCTION__ << " leading=" << separator.acceptLeading << " "<< separator.character << '\n';
299 }
300 else if (this->back().empty()){
301 //std::cerr << __FUNCTION__<< " repeating=" << separator.acceptRepeated << " " << separator.character << '\n';
302 if (separator.acceptRepeated){
303 this->push_back(elem);
304 }
305 }
306 else if (separator.acceptTrailing){
307 // std::cerr << __FUNCTION__ << " trailing=" << separator.acceptTrailing << " " << separator.character << '\n';
308 this->push_back(elem); // empty
309 }
310 else {
311 std::cerr << __FUNCTION__ << "? failed in appending: " << sprinter(*this) << '\n';
312 }
313
314 };
315
317 template <typename T2>
318 inline
319 void appendElem(const T2 & elem){
320 appendElem(elem_t(elem));
321 }
322
324 //
333 inline
334 bool isRoot() const {
335 return ((this->size()==1) && this->front().empty());
336 //return separator.acceptLeading && ((this->size()==1) && this->front().empty());
337 }
338
340
345 inline
346 bool hasRoot() const {
347 if (!this->empty()){
348 return this->front().empty();
349 }
350 else {
351 return false;
352 }
353 }
354
356
361 inline
363 if (!this->hasRoot()){
364 this->push_front(elem_t());
365 }
366 return *this;
367 }
368
369
371
375 void trimHead(bool COMPLETE=false){
376 const size_t minLength = COMPLETE ? 0 : 1;
377 while (this->size() > minLength){
378 //while (!this->empty()){
379 if (!this->front().empty()){
380 return;
381 }
382 this->pop_front();
383 //this->trimHead();
384 }
385 }
386
387
389 void trimTail(bool COMPLETE=false){ //
390 const size_t minLength = COMPLETE ? 0 : 1;
391 while (this->size() > minLength){
392 //while (!this->empty()){
393 if (!this->back().empty()){
394 return;
395 }
396 this->pop_back();
397 //this->trimTail();
398 }
399 }
400
402
414
416 inline
417 void simplify(){
418 simplifyHead();
419 simplifyTail();
420 }
421
423
426
427 if (this->empty())
428 return;
429
430 if (this->front().empty()){
431 typename path_t::const_iterator it0 = ++this->begin();
432 typename path_t::const_iterator it = it0;
433 while (it != this->end()) {
434 if (!it->empty()){
435 break;
436 }
437 ++it;
438 }
439 if (it != it0)
440 this->erase(it0,it);
441 }
442
443
444 }
445
447
450
451 if (this->empty())
452 return;
453
454 if (this->back().empty()){
455 typename path_t::const_iterator it0 = --this->end();
456 typename path_t::const_iterator it = it0;
457
458 while (it != this->begin()) {
459 --it;
460 if (!it->empty()){
461 ++it;
462 break;
463 }
464 }
465
466 if (it != it0)
467 this->erase(it,it0);
468 }
469
470
471 }
472
473
474
476
479 path_t & operator=(const path_t & p){
480
481 if (&p != this){
482 // Yes, important self-check.
483 // Otherwise clear() also makes the"source" empty.
484 set(p);
485 }
486 // std::list<T>::operator=(p);
487 // return assignPa(*this);
488 return *this;
489 }
490
492
495 template <class T2>
497 set(p);
498 return *this;
499 }
500
501
502 inline
503 path_t & operator=(const std::string & p){
504 set(p);
505 return *this;
506 }
507
508 inline
509 path_t & operator=(const char *p){
510 set(p);
511 return *this;
512 }
513
514 // Note: this would be ambiguous!
515 // If (elem_t == std::string), elem cannot be assigned directly, because string suggest full path assignment, at least
516 // path_t & operator=(const elem_t & e)
517
519 /*
520 inline
521 path_t & operator<<(const char *elem){
522 *this <<
523 }
524 */
525
526
527 // TODO: replace these with single
528 /*
529 template <class T2>
530 path_t & operator<<(const T2 & arg){
531 append(args);
532 return *this;
533 }
534 */
535
536
538 path_t & operator<<(const elem_t & elem){
539 appendElem(elem);
540 return *this;
541 }
542
543 inline
544 path_t & operator<<(const path_t & path){
545 this->insert(this->end(), path.begin(), path.end());
546 return *this;
547 }
548
549
550 // Experimental
551 template <class T2>
552 path_t & operator<<(const T2 & strlike){
553 appendPath((const std::string &) strlike);
554 return *this;
555 }
556
557
559 path_t & operator>>(elem_t & e){
560 e = this->back();
561 this->pop_back();
562 return *this;
563 }
564
566
571 virtual
572 std::ostream & toStream(std::ostream & ostr) const { //, char dirSeparator = 0) const {
573
574 if (isRoot())
575 ostr << separator.character;
576 else {
577 static const SprinterLayout layout("/", "", "", "");
578 layout.arrayChars.separator = separator.character;
579 // SprinterLayout layout;
580 // layout.arrayChars.set(0, separator.character,0);
581 // layout.stringChars.fill(0);
582 Sprinter::sequenceToStream(ostr, *this, layout);
583 }
584
585 //Sprinter::toStream(ostr, *this, layout);
586 return ostr;
587 }
588
589 virtual
590 std::string str() const {
591 std::stringstream sstr;
592 toStream(sstr);
593 return sstr.str();
594 }
595
596 virtual
597 operator std::string() const {
598 return str();
599 }
600
601 std::ostream & debug(std::ostream & ostr = std::cout) const {
602 //ostr << "Path<" << typeid(T).name() << "> " << separator << "\n";
603 ostr << "Path elems:" << this->size() << " sep:" << separator << " typeid: " << typeid(T).name() << "\n";
604 ostr << *this << '\n';
605 int i=0;
606 for (typename path_t::const_iterator it = this->begin(); it != this->end(); ++it) {
607 //if (it->empty())
608 // ostr << " " << i << '\t' << "{empty}" << '\n'; //else
609 ostr << " " << i << '\t' << *it << '\n';
610 ++i;
611 }
612 return ostr;
613 }
614
615protected:
616
618 /*
619 void _append(const elem_t &elem){
620 appendElem(elem);
621 }
622
623
624 // Handler for all the rest (non elem_t) arguments.
626 template <typename T2>
627 void _append(const T2 & p){
628 appendElem(p);
629 }
630 */
631
633 void appendSubStr(const std::string & p, size_t start=0){
634
635 if (start == p.length()){
636 // Last char in the string has been passed by one, meaning that: previous elem was empty
637 // That is: the previous char was a separator.
638 //appendElems(elem_t()); // Try to append empty.
639 appendElem(elem_t()); // Try to append empty.
640 return;
641 }
642
643 // Append next segment, ie. up to next separator char.
644 size_t nextSep = p.find(separator.character, start);
645
646 // Remaining string...
647 if (nextSep==std::string::npos){
648 // ... is a single element
649 appendElem(p.substr(start));
650 return;
651 }
652 else {
653 // ... contains separator, hence contains several elements
654 appendElem(p.substr(start, nextSep - start)); // maybe empty, nextSep==start
655 appendSubStr(p, nextSep + 1);
656 }
657
658 }
659
660
661protected:
662
664 void append(){
665 }
666
668 //void appendElems(){
669 //}
670
672 //void setElems(){
673 // };
674
675};
676
677
678
679template <class T, char SEP='/', bool ALEAD=true, bool AREPEAT=false, bool ATRAIL=true>
680inline
681std::ostream & operator<<(std::ostream & ostr, const Path<T,SEP,ALEAD,AREPEAT,ATRAIL> & p) {
682 return p.toStream(ostr);
683}
684
685
686template <class T, char SEP='/', bool ALEAD=true, bool AREPEAT=false, bool ATRAIL=true>
687inline
688std::istream & operator>>(std::istream &istr, Path<T,SEP,ALEAD,AREPEAT,ATRAIL> & p) {
689 std::string s;
690 istr >> s;
691 p = s;
692 return istr;
693}
694
695/*
696template <class T>
697inline
698Path<T> & operator<<(Path<T> & path, const Path<T> & path2){
699 path.insert(path.end(), path2.begin(), path2.end());
700 return path;
701}
702*/
703
704
705template <class T, char SEP, bool ALEAD, bool AREPEAT, bool ATRAIL>
706const PathSeparatorPolicy Path<T,SEP,ALEAD,AREPEAT,ATRAIL>::separator(SEP, ALEAD, AREPEAT, ATRAIL);
707
708
709template <class T,char SEP, bool ALEAD, bool AREPEAT, bool ATRAIL>
710struct TypeName<Path<T,SEP, ALEAD, AREPEAT, ATRAIL> > {
711
712 // typedef Path<T,SEP, ALEAD, AREPEAT, ATRAIL> path_t;
713
714 static
715 const std::string & str(){
716
717 static const std::string name = drain::StringBuilder<>(
718 "Path<", TypeName<T>::str(), ">[", SEP?SEP:'?', "](", ALEAD, AREPEAT, ATRAIL, ')');
719
720 return name;
721 }
722
723};
724
725
726
727}
728
729#endif /* Path_H_ */
730
731// Drain
Definition Path.h:137
virtual ~Path()
Why the three above instead of this?
Definition Path.h:210
void simplify()
ORIGINAL. Removes trailing empty elements, except for the root itself.
Definition Path.h:417
void simplifyTail()
Remove duplicates of empty elements from the end.
Definition Path.h:449
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:572
void appendElem(const elem_t &elem)
Main method for adding elements.
Definition Path.h:281
void appendSubStr(const std::string &p, size_t start=0)
"Default" append function.
Definition Path.h:633
void appendPath(const char *p)
Add path. Specialized handler for strings (note, possibly: elem_t==std:string)
Definition Path.h:269
void simplifyHead()
Remove duplicates of empty elements from the start.
Definition Path.h:425
void append(const std::string &arg, const TT &... args)
Add elements. Any string argument handled as a path.
Definition Path.h:261
path_t & operator=(const path_t &p)
Assigns a path directly with std::list assignment.
Definition Path.h:479
Path(const char *arg, const TT &... args)
Initialize with a path.
Definition Path.h:191
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:334
Path(const path_t &arg, const TT &... args)
Initialize with a path.
Definition Path.h:175
Path(const path_t &p)
Copy constructor.
Definition Path.h:159
void appendElem(const T2 &elem)
To be specialized in subclasses.
Definition Path.h:319
path_t & operator<<(const elem_t &elem)
Append an element, unless empty string.
Definition Path.h:538
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:164
void append(const T2 &arg, const TT &... args)
Append path with element(s), path(s) or string(s).
Definition Path.h:225
Path(const std::string &arg, const TT &... args)
Initialize with a path.
Definition Path.h:183
path_t & operator=(const Path< T2 > &p)
Conversion from str path type.
Definition Path.h:496
void append(char c, const TT &... args)
Add elements, first of which is a character. Any string argument is handled as a path.
Definition Path.h:239
path_t & ensureRoot()
Returns true, if the path is not empty and the first element is empty.
Definition Path.h:362
path_t & operator>>(elem_t &e)
Extract last element.
Definition Path.h:559
void append(const char *arg, const TT &... args)
Add elements. Any string argument handled as a path.
Definition Path.h:254
void trimTail(bool COMPLETE=false)
Removes trailing empty elements. The resulting string presentation will not end with the separator.
Definition Path.h:389
void append()
Terminal function for variadic templates.
Definition Path.h:664
void trimHead(bool COMPLETE=false)
Removes leading empty elements. The resulting string presentation will not start with the separator.
Definition Path.h:375
bool hasRoot() const
Returns true, if the path is not empty and the first element is empty.
Definition Path.h:346
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:325
Definition StringBuilder.h:58
Tuple of N elements of type T.
Definition UniTuple.h:65
Definition DataSelector.cpp:1277
DRAIN_TYPENAME(void)
Add a specialization for each type of those you want to support.
Determines how separator char (often / ) will be handled in the path.
Definition Path.h:68
bool & acceptRepeated
If true, allow empty elements in the middle (appearing as repeated separators)
Definition Path.h:77
bool & acceptTrailing
If true, allow trailing empty elements (appearing as repeated separators)
Definition Path.h:80
bool & acceptLeading
If true, allow leading empties (ie leading separators) // Accepts one. to start with.
Definition Path.h:74
Definition Sprinter.h:137
Default implementation.
Definition Type.h:541
static const std::string name
Default implementation: name returned by std::type_info::name()
Definition Type.h:549