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/String.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 */
67struct PathSeparatorPolicy : UniTuple<bool,3> {
68
69 // Separator character
70 char character;
71
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
94inline
95std::ostream & operator<<(std::ostream & ostr, const PathSeparatorPolicy & policy) {
96 ostr << "Separator='" << policy.character << "', policy=" << policy.tuple();
97 return ostr;
98}
99
100
101
111template <class T,char SEP='/', bool ALEAD=true, bool AREPEAT=false, bool ATRAIL=true>
112class Path : public std::list<T> {
113
114public:
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 appendElem(arg); // replace with appendElem(elem), remove _append?
199 append(args...);
200 }
201
202 template <typename ... TT>
203 void append(const path_t &p, const TT &... args) {
204 this->insert(this->end(), p.begin(), p.end());
205 append(args...);
206 }
207
208 // NEW
209 template <typename ... TT>
210 void append(char c, const TT &... args) {
211 appendElem(c);
212 append(args...);
213 }
214
215 template <typename ... TT>
216 void append(const char * arg, const TT &... args) {
217 _appendPath(arg, 0);
218 append(args...);
219 }
220
221 template <typename ... TT>
222 void append(const std::string &arg, const TT &... args) {
223 _appendPath(arg, 0);
224 append(args...);
225 }
226
227
229 inline
230 void appendPath(const char *p){
231 _appendPath(p, 0);
232 }
233
234 // Reverse convenience...
235 inline
236 void appendPath(const path_t &p){
237 appendPath(p);
238 }
239
240
242 void appendElem(const elem_t & elem){
243
244 if (!elem.empty()){
245 // Always allow non-empty element
246 // std::cerr << __FUNCTION__ << ":" << elem << '\n';
247 if (!separator.acceptRepeated){
248 //std::cerr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << ":" << " validate " << *this << " + " << elem << '\n';
249 //removeTrailing();
250 trimTail();
251 }
252 //stripTail();
253 this->push_back(elem);
254 }
255 // elem is EMPTY: -----------------------------------------------------------
256 else if (this->empty()){
257 if (separator.acceptLeading)
258 this->push_back(elem); // empty
259 // std::cerr << __FUNCTION__ << " leading=" << separator.acceptLeading << " "<< separator.character << '\n';
260 }
261 else if (this->back().empty()){
262 //std::cerr << __FUNCTION__<< " repeating=" << separator.acceptRepeated << " " << separator.character << '\n';
263 if (separator.acceptRepeated){
264 this->push_back(elem);
265 }
266 }
267 else if (separator.acceptTrailing){
268 // std::cerr << __FUNCTION__ << " trailing=" << separator.acceptTrailing << " " << separator.character << '\n';
269 this->push_back(elem); // empty
270 }
271 else {
272 std::cerr << __FUNCTION__ << "? failed in appending: " << sprinter(*this) << '\n';
273 }
274
275 };
276
278 //
287 inline
288 bool isRoot() const {
289 return ((this->size()==1) && this->front().empty());
290 //return separator.acceptLeading && ((this->size()==1) && this->front().empty());
291 }
292
294
299 inline
300 bool hasRoot() const {
301 if (!this->empty()){
302 return this->front().empty();
303 }
304 else {
305 return false;
306 }
307 }
308
310
315 inline
317 if (!this->hasRoot()){
318 this->push_front(elem_t());
319 }
320 return *this;
321 }
322
323
325
329 void trimHead(bool COMPLETE=false){
330 const size_t minLength = COMPLETE ? 0 : 1;
331 while (this->size() > minLength){
332 //while (!this->empty()){
333 if (!this->front().empty()){
334 return;
335 }
336 this->pop_front();
337 //this->trimHead();
338 }
339 }
340
341
343 void trimTail(bool COMPLETE=false){ //
344 const size_t minLength = COMPLETE ? 0 : 1;
345 while (this->size() > minLength){
346 //while (!this->empty()){
347 if (!this->back().empty()){
348 return;
349 }
350 this->pop_back();
351 //this->trimTail();
352 }
353 }
354
356
368
370 inline
371 void simplify(){
372 simplifyHead();
373 simplifyTail();
374 }
375
377
380
381 if (this->empty())
382 return;
383
384 if (this->front().empty()){
385 typename path_t::const_iterator it0 = ++this->begin();
386 typename path_t::const_iterator it = it0;
387 while (it != this->end()) {
388 if (!it->empty()){
389 break;
390 }
391 ++it;
392 }
393 if (it != it0)
394 this->erase(it0,it);
395 }
396
397
398 }
399
401
404
405 if (this->empty())
406 return;
407
408 if (this->back().empty()){
409 typename path_t::const_iterator it0 = --this->end();
410 typename path_t::const_iterator it = it0;
411
412 while (it != this->begin()) {
413 --it;
414 if (!it->empty()){
415 ++it;
416 break;
417 }
418 }
419
420 if (it != it0)
421 this->erase(it,it0);
422 }
423
424
425 }
426
427
428
430
433 path_t & operator=(const path_t & p){
434
435 if (&p != this){
436 // Yes, important self-check.
437 // Otherwise clear() also makes the"source" empty.
438 set(p);
439 }
440 // std::list<T>::operator=(p);
441 // return assignPa(*this);
442 return *this;
443 }
444
446
449 template <class T2>
451 set(p);
452 return *this;
453 }
454
455
456 inline
457 path_t & operator=(const std::string & p){
458 set(p);
459 return *this;
460 }
461
462 inline
463 path_t & operator=(const char *p){
464 set(p);
465 return *this;
466 }
467
468 // Note: this would be ambiguous!
469 // If (elem_t == std::string), elem cannot be assigned directly, because string suggest full path assignment, at least
470 // path_t & operator=(const elem_t & e)
471
473 /*
474 inline
475 path_t & operator<<(const char *elem){
476 *this <<
477 }
478 */
479
480
481 // TODO: replace these with single
482 /*
483 template <class T2>
484 path_t & operator<<(const T2 & arg){
485 append(args);
486 return *this;
487 }
488 */
489
490
492 path_t & operator<<(const elem_t & elem){
493 appendElem(elem);
494 return *this;
495 }
496
497 inline
498 path_t & operator<<(const path_t & path){
499 this->insert(this->end(), path.begin(), path.end());
500 return *this;
501 }
502
503
504 // Experimental
505 template <class T2>
506 path_t & operator<<(const T2 & strlike){
507 appendPath((const std::string &) strlike);
508 return *this;
509 }
510
511
513 path_t & operator>>(elem_t & e){
514 e = this->back();
515 this->pop_back();
516 return *this;
517 }
518
520
525 virtual
526 std::ostream & toStream(std::ostream & ostr) const { //, char dirSeparator = 0) const {
527
528 if (isRoot())
529 ostr << separator.character;
530 else {
531 static const SprinterLayout layout("/", "", "", "");
532 layout.arrayChars.separator = separator.character;
533 // SprinterLayout layout;
534 // layout.arrayChars.set(0, separator.character,0);
535 // layout.stringChars.fill(0);
536 Sprinter::sequenceToStream(ostr, *this, layout);
537 }
538
539 //Sprinter::toStream(ostr, *this, layout);
540 return ostr;
541 }
542
543 virtual
544 std::string str() const {
545 std::stringstream sstr;
546 toStream(sstr);
547 return sstr.str();
548 }
549
550 virtual
551 operator std::string() const {
552 return str();
553 }
554
555 std::ostream & debug(std::ostream & ostr = std::cout) const {
556 //ostr << "Path<" << typeid(T).name() << "> " << separator << "\n";
557 ostr << "Path elems:" << this->size() << " sep:" << separator << " typeid: " << typeid(T).name() << "\n";
558 ostr << *this << '\n';
559 int i=0;
560 for (typename path_t::const_iterator it = this->begin(); it != this->end(); ++it) {
561 //if (it->empty())
562 // ostr << " " << i << '\t' << "{empty}" << '\n'; //else
563 ostr << " " << i << '\t' << *it << '\n';
564 ++i;
565 }
566 return ostr;
567 }
568
569protected:
570
572 /*
573 void _append(const elem_t &elem){
574 appendElem(elem);
575 }
576
577
578 // Handler for all the rest (non elem_t) arguments.
580 template <typename T2>
581 void _append(const T2 & p){
582 appendElem(p);
583 }
584 */
585
587 void _appendPath(const std::string & p, size_t start=0){
588
589 if (start == p.length()){
590 // Last char in the string has been passed by one, meaning that: previous elem was empty
591 // That is: the previous char was a separator.
592 //appendElems(elem_t()); // Try to append empty.
593 appendElem(elem_t()); // Try to append empty.
594 return;
595 }
596
597 // Append next segment, ie. up to next separator char.
598 size_t nextSep = p.find(separator.character, start);
599
600 // Remaining string...
601 if (nextSep==std::string::npos){
602 // ... is a single element
603 appendElem(p.substr(start));
604 return;
605 }
606 else {
607 // ... contains separator, hence contains several elements
608 appendElem(p.substr(start, nextSep - start)); // maybe empty, nextSep==start
609 _appendPath(p, nextSep + 1);
610 }
611
612 }
613
614
615protected:
616
618 void append(){
619 }
620
622 //void appendElems(){
623 //}
624
626 //void setElems(){
627 // };
628
629};
630
631
632
633template <class T, char SEP='/', bool ALEAD=true, bool AREPEAT=false, bool ATRAIL=true>
634inline
635std::ostream & operator<<(std::ostream & ostr, const Path<T,SEP,ALEAD,AREPEAT,ATRAIL> & p) {
636 return p.toStream(ostr);
637}
638
639
640template <class T, char SEP='/', bool ALEAD=true, bool AREPEAT=false, bool ATRAIL=true>
641inline
642std::istream & operator>>(std::istream &istr, Path<T,SEP,ALEAD,AREPEAT,ATRAIL> & p) {
643 std::string s;
644 istr >> s;
645 p = s;
646 return istr;
647}
648
649/*
650template <class T>
651inline
652Path<T> & operator<<(Path<T> & path, const Path<T> & path2){
653 path.insert(path.end(), path2.begin(), path2.end());
654 return path;
655}
656*/
657
658
659}
660
661#endif /* Path_H_ */
662
663// 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:371
void simplifyTail()
Remove duplicates of empty elements from the end.
Definition Path.h:403
void _appendPath(const std::string &p, size_t start=0)
"Default" append function.
Definition Path.h:587
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:526
void appendElem(const elem_t &elem)
Main method for adding elements.
Definition Path.h:242
void appendPath(const char *p)
Specialized handler for strings (note, possibly: elem_t==std:string)
Definition Path.h:230
void simplifyHead()
Remove duplicates of empty elements from the start.
Definition Path.h:379
path_t & operator=(const path_t &p)
Assigns a path directly with std::list assignment.
Definition Path.h:433
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:288
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
path_t & operator<<(const elem_t &elem)
Append an element, unless empty string.
Definition Path.h:492
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 & operator=(const Path< T2 > &p)
Conversion from str path type.
Definition Path.h:450
path_t & ensureRoot()
Returns true, if the path is not empty and the first element is empty.
Definition Path.h:316
path_t & operator>>(elem_t &e)
Extract last element.
Definition Path.h:513
void trimTail(bool COMPLETE=false)
Removes trailing empty elements. The resulting string presentation will not end with the separator.
Definition Path.h:343
void append()
Terminal function for variadic templates.
Definition Path.h:618
void trimHead(bool COMPLETE=false)
Removes leading empty elements. The resulting string presentation will not start with the separator.
Definition Path.h:329
bool hasRoot() const
Returns true, if the path is not empty and the first element is empty.
Definition Path.h:300
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