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
47
48#include <drain/Sprinter.h>
49
50
51
52namespace drain {
53
55/*
56 *
57 * In Unix style, intermediate separators are accepted but not stored.
58 *
59 * Note that technically, accepting leading, intermediate or trailing separators means also
60 * accepting \e empty path elements, respectively.
61 *
62 * When using strings as path elements, the root is identified with an empty string -- not actually with the separator char like '/'.
63 *
64 * Future option: const version implemended as fully templated class (perhaps with const UniTuple ).
65 */
66struct PathSeparatorPolicy : UniTuple<bool,3> {
67
68 // Separator character
69 char character;
70
73
76
79
80 PathSeparatorPolicy(char separator='/', bool lead=true, bool repeated=false, bool trail=true) :
81 character(separator), acceptLeading(next()), acceptRepeated(next()), acceptTrailing(next()) {
82 set(lead, repeated, trail);
83 if (!separator)
84 throw std::runtime_error("PathSeparatorPolict (char separator): separator=0, did you mean empty init (\"\")");
85 }
86 PathSeparatorPolicy(const PathSeparatorPolicy &policy) : UniTuple<bool,3>(policy),
87 character(policy.character), acceptLeading(next()), acceptRepeated(next()), acceptTrailing(next()) {
88 };
89
90
91};
92
93DRAIN_TYPENAME(PathSeparatorPolicy);
94
95/*
96template <class T,char SEP, bool ALEAD, bool AREPEAT, bool ATRAIL>
97struct TypeName<Path<T,SEP, ALEAD, AREPEAT, ATRAIL> > {
98
99 typedef Path<T,SEP, ALEAD, AREPEAT, ATRAIL> path_t;
100
101 static
102 const std::string & str(){
103
104 static const std::string name = drain::StringBuilder<>(
105 "Path<", TypeName<T>::str(), ">[", SEP?SEP:'?', "](", ALEAD, AREPEAT, ATRAIL, ')');
106 //'<', drain::Type::call<drain::simpleName>(typeid(typename tree_t::node_data_t)), '>');
107 return name;
108 }
109
110};
111*/
112
113inline
114std::ostream & operator<<(std::ostream & ostr, const PathSeparatorPolicy & policy) {
115 ostr << "PathSeparatorPolicy";
116 if (policy.character){
117 ostr << '[' << policy.character << ']';
118 }
119 ostr << '(' << policy.tuple() << ')';
120 return ostr;
121}
122
123
124
134template <class T,char SEP='/', bool ALEAD=true, bool AREPEAT=false, bool ATRAIL=true>
135class Path : public std::list<T> {
136
137public:
138
139 typedef T elem_t;
140
142
143 typedef std::list< path_t > list_t;
144
145 // typedef Path<T,SEP,ALEAD,AREPEAT,ATRAIL> path_t;
146
147 // const PathSeparatorPolicy separator;
148 static
149 const PathSeparatorPolicy separator;
150
151 inline
152 Path(){ // : separator(SEP, ALEAD, AREPEAT, ATRAIL){
153 };
154
156 inline
157 Path(const path_t & p) : std::list<T>(p){ // , separator(p.separator){
158 };
159
161 inline
162 Path(typename path_t::const_iterator it, typename path_t::const_iterator it2){ // : separator(SEP, ALEAD, AREPEAT, ATRAIL) {
163 while (it != it2){
164 append(*it);
165 ++it;
166 }
167 };
168
170 // All the elements are treated as paths.
171 template <typename ... TT>
172 inline
173 Path(const path_t & arg, const TT &... args){ // : separator(SEP, ALEAD, AREPEAT, ATRAIL){
174 append(arg, args...);
175 };
176
178 // All the elements are treated as paths.
179 template <typename ... TT>
180 inline
181 Path(const std::string & arg, const TT &... args){ // : separator(SEP, ALEAD, AREPEAT, ATRAIL){
182 append(arg, args...);
183 };
184
186 // All the elements are treated as paths.
187 template <typename ... TT>
188 inline
189 Path(const char * arg, const TT &... args){ // : separator(SEP, ALEAD, AREPEAT, ATRAIL){
190 append(arg, args...);
191 };
192
193
195 // Answer: to prevent elem and string
196 /*
197 template <typename ... TT>
198 inline
199 Path(const TT &... args) : separator(SEP, ALEAD, AREPEAT, ATRAIL){ //: separator(separator) {
200 append(args...);
201 };
202 */
203
204
205
206
207 virtual inline
208 ~Path(){};
209
210 // Design principle: handle all strings as paths!
211 // This means that if path elements are strings, assigning elements goes through an "extra" path check.
212 // If string arguments were accepted directly as elements, separator characters could be passed in the elements.
213
214 template <typename ... TT>
215 void set(const TT &... args) {
216 this->clear();
217 append(args...);
218 }
219
221 // needed?
222 template <typename T2, typename ... TT>
223 void append(const T2 & arg, const TT &... args) {
224 //append_(arg); // replace with appendElem(elem), remove _append?
225 appendElem(arg); // replace with appendElem(elem), remove _append?
226 append(args...);
227 }
228
229 template <typename ... TT>
230 void append(const path_t &p, const TT &... args) {
231 this->insert(this->end(), p.begin(), p.end());
232 append(args...);
233 }
234
236 template <typename ... TT>
237 void append(char c, const TT &... args) {
238 if (!c){
239 std::cerr << __FILE__ << ' ' << __FUNCTION__ << "::" << " null separator char \n";
240 }
241 else if (c==separator.character){
242 appendElem(elem_t());
243 }
244 else {
245 appendElem(std::string(1,c));
246 }
247 append(args...);
248 }
249
251 template <typename ... TT>
252 void append(const char * arg, const TT &... args) {
253 appendSubStr(arg, 0);
254 append(args...);
255 }
256
258 template <typename ... TT>
259 void append(const std::string &arg, const TT &... args) {
260 appendSubStr(arg, 0);
261 append(args...);
262 }
263
264
266 inline
267 void appendPath(const char *p){
268 appendSubStr(p, 0);
269 }
270
271 // Reverse convenience...
272 inline
273 void appendPath(const path_t &p){
274 appendPath(p);
275 }
276
277
279 void appendElem(const elem_t & elem){
280
281 if (!elem.empty()){
282 // Always allow non-empty element
283 // std::cerr << __FUNCTION__ << ":" << elem << '\n';
284 if (!separator.acceptRepeated){
285 //std::cerr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << ":" << " validate " << *this << " + " << elem << '\n';
286 //removeTrailing();
287 trimTail();
288 }
289 //stripTail();
290 this->push_back(elem);
291 }
292 // elem is EMPTY: -----------------------------------------------------------
293 else if (this->empty()){
294 if (separator.acceptLeading)
295 this->push_back(elem); // empty
296 // std::cerr << __FUNCTION__ << " leading=" << separator.acceptLeading << " "<< separator.character << '\n';
297 }
298 else if (this->back().empty()){
299 //std::cerr << __FUNCTION__<< " repeating=" << separator.acceptRepeated << " " << separator.character << '\n';
300 if (separator.acceptRepeated){
301 this->push_back(elem);
302 }
303 }
304 else if (separator.acceptTrailing){
305 // std::cerr << __FUNCTION__ << " trailing=" << separator.acceptTrailing << " " << separator.character << '\n';
306 this->push_back(elem); // empty
307 }
308 else {
309 std::cerr << __FUNCTION__ << "? failed in appending: " << sprinter(*this) << '\n';
310 }
311
312 };
313
315 template <typename T2>
316 inline
317 void appendElem(const T2 & elem){
318 appendElem(elem_t(elem));
319 }
320
322 //
331 inline
332 bool isRoot() const {
333 return ((this->size()==1) && this->front().empty());
334 //return separator.acceptLeading && ((this->size()==1) && this->front().empty());
335 }
336
338
343 inline
344 bool hasRoot() const {
345 if (!this->empty()){
346 return this->front().empty();
347 }
348 else {
349 return false;
350 }
351 }
352
354
359 inline
361 if (!this->hasRoot()){
362 this->push_front(elem_t());
363 }
364 return *this;
365 }
366
367
369
373 void trimHead(bool COMPLETE=false){
374 const size_t minLength = COMPLETE ? 0 : 1;
375 while (this->size() > minLength){
376 //while (!this->empty()){
377 if (!this->front().empty()){
378 return;
379 }
380 this->pop_front();
381 //this->trimHead();
382 }
383 }
384
385
387 void trimTail(bool COMPLETE=false){ //
388 const size_t minLength = COMPLETE ? 0 : 1;
389 while (this->size() > minLength){
390 //while (!this->empty()){
391 if (!this->back().empty()){
392 return;
393 }
394 this->pop_back();
395 //this->trimTail();
396 }
397 }
398
400
412
414 inline
415 void simplify(){
416 simplifyHead();
417 simplifyTail();
418 }
419
421
424
425 if (this->empty())
426 return;
427
428 if (this->front().empty()){
429 typename path_t::const_iterator it0 = ++this->begin();
430 typename path_t::const_iterator it = it0;
431 while (it != this->end()) {
432 if (!it->empty()){
433 break;
434 }
435 ++it;
436 }
437 if (it != it0)
438 this->erase(it0,it);
439 }
440
441
442 }
443
445
448
449 if (this->empty())
450 return;
451
452 if (this->back().empty()){
453 typename path_t::const_iterator it0 = --this->end();
454 typename path_t::const_iterator it = it0;
455
456 while (it != this->begin()) {
457 --it;
458 if (!it->empty()){
459 ++it;
460 break;
461 }
462 }
463
464 if (it != it0)
465 this->erase(it,it0);
466 }
467
468
469 }
470
471
472
474
477 path_t & operator=(const path_t & p){
478
479 if (&p != this){
480 // Yes, important self-check.
481 // Otherwise clear() also makes the"source" empty.
482 set(p);
483 }
484 // std::list<T>::operator=(p);
485 // return assignPa(*this);
486 return *this;
487 }
488
490
493 template <class T2>
495 set(p);
496 return *this;
497 }
498
499
500 inline
501 path_t & operator=(const std::string & p){
502 set(p);
503 return *this;
504 }
505
506 inline
507 path_t & operator=(const char *p){
508 set(p);
509 return *this;
510 }
511
512 // Note: this would be ambiguous!
513 // If (elem_t == std::string), elem cannot be assigned directly, because string suggest full path assignment, at least
514 // path_t & operator=(const elem_t & e)
515
517 /*
518 inline
519 path_t & operator<<(const char *elem){
520 *this <<
521 }
522 */
523
524
525 // TODO: replace these with single
526 /*
527 template <class T2>
528 path_t & operator<<(const T2 & arg){
529 append(args);
530 return *this;
531 }
532 */
533
534
536 path_t & operator<<(const elem_t & elem){
537 appendElem(elem);
538 return *this;
539 }
540
541 inline
542 path_t & operator<<(const path_t & path){
543 this->insert(this->end(), path.begin(), path.end());
544 return *this;
545 }
546
547
548 // Experimental
549 template <class T2>
550 path_t & operator<<(const T2 & strlike){
551 appendPath((const std::string &) strlike);
552 return *this;
553 }
554
555
557 path_t & operator>>(elem_t & e){
558 e = this->back();
559 this->pop_back();
560 return *this;
561 }
562
564
569 virtual
570 std::ostream & toStream(std::ostream & ostr) const { //, char dirSeparator = 0) const {
571
572 if (isRoot())
573 ostr << separator.character;
574 else {
575 static const SprinterLayout layout("/", "", "", "");
576 layout.arrayChars.separator = separator.character;
577 // SprinterLayout layout;
578 // layout.arrayChars.set(0, separator.character,0);
579 // layout.stringChars.fill(0);
580 Sprinter::sequenceToStream(ostr, *this, layout);
581 }
582
583 //Sprinter::toStream(ostr, *this, layout);
584 return ostr;
585 }
586
587 virtual
588 std::string str() const {
589 std::stringstream sstr;
590 toStream(sstr);
591 return sstr.str();
592 }
593
594 virtual
595 operator std::string() const {
596 return str();
597 }
598
599 std::ostream & debug(std::ostream & ostr = std::cout) const {
600 //ostr << "Path<" << typeid(T).name() << "> " << separator << "\n";
601 ostr << "Path elems:" << this->size() << " sep:" << separator << " typeid: " << typeid(T).name() << "\n";
602 ostr << *this << '\n';
603 int i=0;
604 for (typename path_t::const_iterator it = this->begin(); it != this->end(); ++it) {
605 //if (it->empty())
606 // ostr << " " << i << '\t' << "{empty}" << '\n'; //else
607 ostr << " " << i << '\t' << *it << '\n';
608 ++i;
609 }
610 return ostr;
611 }
612
613protected:
614
616 /*
617 void _append(const elem_t &elem){
618 appendElem(elem);
619 }
620
621
622 // Handler for all the rest (non elem_t) arguments.
624 template <typename T2>
625 void _append(const T2 & p){
626 appendElem(p);
627 }
628 */
629
631 void appendSubStr(const std::string & p, size_t start=0){
632
633 if (start == p.length()){
634 // Last char in the string has been passed by one, meaning that: previous elem was empty
635 // That is: the previous char was a separator.
636 //appendElems(elem_t()); // Try to append empty.
637 appendElem(elem_t()); // Try to append empty.
638 return;
639 }
640
641 // Append next segment, ie. up to next separator char.
642 size_t nextSep = p.find(separator.character, start);
643
644 // Remaining string...
645 if (nextSep==std::string::npos){
646 // ... is a single element
647 appendElem(p.substr(start));
648 return;
649 }
650 else {
651 // ... contains separator, hence contains several elements
652 appendElem(p.substr(start, nextSep - start)); // maybe empty, nextSep==start
653 appendSubStr(p, nextSep + 1);
654 }
655
656 }
657
658
659protected:
660
662 void append(){
663 }
664
666 //void appendElems(){
667 //}
668
670 //void setElems(){
671 // };
672
673};
674
675
676
677template <class T, char SEP='/', bool ALEAD=true, bool AREPEAT=false, bool ATRAIL=true>
678inline
679std::ostream & operator<<(std::ostream & ostr, const Path<T,SEP,ALEAD,AREPEAT,ATRAIL> & p) {
680 return p.toStream(ostr);
681}
682
683
684template <class T, char SEP='/', bool ALEAD=true, bool AREPEAT=false, bool ATRAIL=true>
685inline
686std::istream & operator>>(std::istream &istr, Path<T,SEP,ALEAD,AREPEAT,ATRAIL> & p) {
687 std::string s;
688 istr >> s;
689 p = s;
690 return istr;
691}
692
693/*
694template <class T>
695inline
696Path<T> & operator<<(Path<T> & path, const Path<T> & path2){
697 path.insert(path.end(), path2.begin(), path2.end());
698 return path;
699}
700*/
701
702
703template <class T, char SEP, bool ALEAD, bool AREPEAT, bool ATRAIL>
704const PathSeparatorPolicy Path<T,SEP,ALEAD,AREPEAT,ATRAIL>::separator(SEP, ALEAD, AREPEAT, ATRAIL);
705
706
707template <class T,char SEP, bool ALEAD, bool AREPEAT, bool ATRAIL>
708struct TypeName<Path<T,SEP, ALEAD, AREPEAT, ATRAIL> > {
709
710 // typedef Path<T,SEP, ALEAD, AREPEAT, ATRAIL> path_t;
711
712 static
713 const std::string & str(){
714
715 static const std::string name = drain::StringBuilder<>(
716 "Path<", TypeName<T>::str(), ">[", SEP?SEP:'?', "](", ALEAD, AREPEAT, ATRAIL, ')');
717
718 return name;
719 }
720
721};
722
723
724
725}
726
727#endif /* Path_H_ */
728
729// Drain
Definition Path.h:135
virtual ~Path()
Why the three above instead of this?
Definition Path.h:208
void simplify()
ORIGINAL. Removes trailing empty elements, except for the root itself.
Definition Path.h:415
void simplifyTail()
Remove duplicates of empty elements from the end.
Definition Path.h:447
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:570
void appendElem(const elem_t &elem)
Main method for adding elements.
Definition Path.h:279
void appendSubStr(const std::string &p, size_t start=0)
"Default" append function.
Definition Path.h:631
void appendPath(const char *p)
Add path. Specialized handler for strings (note, possibly: elem_t==std:string)
Definition Path.h:267
void simplifyHead()
Remove duplicates of empty elements from the start.
Definition Path.h:423
void append(const std::string &arg, const TT &... args)
Add elements. Any string argument handled as a path.
Definition Path.h:259
path_t & operator=(const path_t &p)
Assigns a path directly with std::list assignment.
Definition Path.h:477
Path(const char *arg, const TT &... args)
Initialize with a path.
Definition Path.h:189
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:332
Path(const path_t &arg, const TT &... args)
Initialize with a path.
Definition Path.h:173
Path(const path_t &p)
Copy constructor.
Definition Path.h:157
void appendElem(const T2 &elem)
To be specialized in subclasses.
Definition Path.h:317
path_t & operator<<(const elem_t &elem)
Append an element, unless empty string.
Definition Path.h:536
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:162
void append(const T2 &arg, const TT &... args)
Append path with element(s), path(s) or string(s).
Definition Path.h:223
Path(const std::string &arg, const TT &... args)
Initialize with a path.
Definition Path.h:181
path_t & operator=(const Path< T2 > &p)
Conversion from str path type.
Definition Path.h:494
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:237
path_t & ensureRoot()
Returns true, if the path is not empty and the first element is empty.
Definition Path.h:360
path_t & operator>>(elem_t &e)
Extract last element.
Definition Path.h:557
void append(const char *arg, const TT &... args)
Add elements. Any string argument handled as a path.
Definition Path.h:252
void trimTail(bool COMPLETE=false)
Removes trailing empty elements. The resulting string presentation will not end with the separator.
Definition Path.h:387
void append()
Terminal function for variadic templates.
Definition Path.h:662
void trimHead(bool COMPLETE=false)
Removes leading empty elements. The resulting string presentation will not start with the separator.
Definition Path.h:373
bool hasRoot() const
Returns true, if the path is not empty and the first element is empty.
Definition Path.h:344
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:324
Definition StringBuilder.h:58
Tuple of N elements of type T.
Definition UniTuple.h:65
Definition DataSelector.cpp:1277
Determines how separator char (often / ) will be handled in the path.
Definition Path.h:66
bool & acceptRepeated
If true, allow empty elements in the middle (appearing as repeated separators)
Definition Path.h:75
bool & acceptTrailing
If true, allow trailing empty elements (appearing as repeated separators)
Definition Path.h:78
bool & acceptLeading
If true, allow leading empties (ie leading separators) // Accepts one. to start with.
Definition Path.h:72
Definition Sprinter.h:136
Default implementation.
Definition TypeName.h:54