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 // typedef Path<T,SEP,ALEAD,AREPEAT,ATRAIL> path_t;
123
124 // const PathSeparatorPolicy separator;
125 static
126 const PathSeparatorPolicy separator;
127
128 inline
129 Path(){ // : separator(SEP, ALEAD, AREPEAT, ATRAIL){
130 };
131
133 inline
134 Path(const path_t & p) : std::list<T>(p){ // , separator(p.separator){
135 };
136
138 inline
139 Path(typename path_t::const_iterator it, typename path_t::const_iterator it2){ // : separator(SEP, ALEAD, AREPEAT, ATRAIL) {
140 while (it != it2){
141 append(*it);
142 ++it;
143 }
144 };
145
147 // All the elements are treated as paths.
148 template <typename ... TT>
149 inline
150 Path(const path_t & arg, const TT &... args){ // : separator(SEP, ALEAD, AREPEAT, ATRAIL){
151 append(arg, args...);
152 };
153
155 // All the elements are treated as paths.
156 template <typename ... TT>
157 inline
158 Path(const std::string & arg, const TT &... args){ // : separator(SEP, ALEAD, AREPEAT, ATRAIL){
159 append(arg, args...);
160 };
161
163 // All the elements are treated as paths.
164 template <typename ... TT>
165 inline
166 Path(const char * arg, const TT &... args){ // : separator(SEP, ALEAD, AREPEAT, ATRAIL){
167 append(arg, args...);
168 };
169
170
172 // Answer: to prevent elem and string
173 /*
174 template <typename ... TT>
175 inline
176 Path(const TT &... args) : separator(SEP, ALEAD, AREPEAT, ATRAIL){ //: separator(separator) {
177 append(args...);
178 };
179 */
180
181
182
183
184 virtual inline
185 ~Path(){};
186
187 // Design principle: handle all strings as paths!
188 // This means that if path elements are strings, assigning elements goes through an "extra" path check.
189 // If string arguments were accepted directly as elements, separator characters could be passed in the elements.
190
191 template <typename ... TT>
192 void set(const TT &... args) {
193 this->clear();
194 append(args...);
195 }
196
198 // needed?
199 template <typename T2, typename ... TT>
200 void append(const T2 & arg, const TT &... args) {
201 //append_(arg); // replace with appendElem(elem), remove _append?
202 appendElem(arg); // replace with appendElem(elem), remove _append?
203 append(args...);
204 }
205
206 template <typename ... TT>
207 void append(const path_t &p, const TT &... args) {
208 this->insert(this->end(), p.begin(), p.end());
209 append(args...);
210 }
211
212 // NEW
213 template <typename ... TT>
214 void append(char c, const TT &... args) {
215 appendElem(c);
216 append(args...);
217 }
218
219 template <typename ... TT>
220 void append(const char * arg, const TT &... args) {
221 _appendPath(arg, 0);
222 append(args...);
223 }
224
225 template <typename ... TT>
226 void append(const std::string &arg, const TT &... args) {
227 _appendPath(arg, 0);
228 append(args...);
229 }
230
231
233 inline
234 void appendPath(const char *p){
235 _appendPath(p, 0);
236 }
237
238 // Reverse convenience...
239 inline
240 void appendPath(const path_t &p){
241 appendPath(p);
242 }
243
244
246 void appendElem(const elem_t & elem){
247
248 if (!elem.empty()){
249 // Always allow non-empty element
250 // std::cerr << __FUNCTION__ << ":" << elem << '\n';
251 if (!separator.acceptRepeated){
252 //std::cerr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << ":" << " validate " << *this << " + " << elem << '\n';
253 //removeTrailing();
254 trimTail();
255 }
256 //stripTail();
257 this->push_back(elem);
258 }
259 // elem is EMPTY: -----------------------------------------------------------
260 else if (this->empty()){
261 if (separator.acceptLeading)
262 this->push_back(elem); // empty
263 // std::cerr << __FUNCTION__ << " leading=" << separator.acceptLeading << " "<< separator.character << '\n';
264 }
265 else if (this->back().empty()){
266 //std::cerr << __FUNCTION__<< " repeating=" << separator.acceptRepeated << " " << separator.character << '\n';
267 if (separator.acceptRepeated){
268 this->push_back(elem);
269 }
270 }
271 else if (separator.acceptTrailing){
272 // std::cerr << __FUNCTION__ << " trailing=" << separator.acceptTrailing << " " << separator.character << '\n';
273 this->push_back(elem); // empty
274 }
275 else {
276 std::cerr << __FUNCTION__ << "? failed in appending: " << sprinter(*this) << '\n';
277 }
278
279 };
280
282 template <typename T2>
283 inline
284 void appendElem(const T2 & elem){
285 appendElem(elem_t(elem));
286 }
287
289 //
298 inline
299 bool isRoot() const {
300 return ((this->size()==1) && this->front().empty());
301 //return separator.acceptLeading && ((this->size()==1) && this->front().empty());
302 }
303
305
310 inline
311 bool hasRoot() const {
312 if (!this->empty()){
313 return this->front().empty();
314 }
315 else {
316 return false;
317 }
318 }
319
321
326 inline
328 if (!this->hasRoot()){
329 this->push_front(elem_t());
330 }
331 return *this;
332 }
333
334
336
340 void trimHead(bool COMPLETE=false){
341 const size_t minLength = COMPLETE ? 0 : 1;
342 while (this->size() > minLength){
343 //while (!this->empty()){
344 if (!this->front().empty()){
345 return;
346 }
347 this->pop_front();
348 //this->trimHead();
349 }
350 }
351
352
354 void trimTail(bool COMPLETE=false){ //
355 const size_t minLength = COMPLETE ? 0 : 1;
356 while (this->size() > minLength){
357 //while (!this->empty()){
358 if (!this->back().empty()){
359 return;
360 }
361 this->pop_back();
362 //this->trimTail();
363 }
364 }
365
367
379
381 inline
382 void simplify(){
383 simplifyHead();
384 simplifyTail();
385 }
386
388
391
392 if (this->empty())
393 return;
394
395 if (this->front().empty()){
396 typename path_t::const_iterator it0 = ++this->begin();
397 typename path_t::const_iterator it = it0;
398 while (it != this->end()) {
399 if (!it->empty()){
400 break;
401 }
402 ++it;
403 }
404 if (it != it0)
405 this->erase(it0,it);
406 }
407
408
409 }
410
412
415
416 if (this->empty())
417 return;
418
419 if (this->back().empty()){
420 typename path_t::const_iterator it0 = --this->end();
421 typename path_t::const_iterator it = it0;
422
423 while (it != this->begin()) {
424 --it;
425 if (!it->empty()){
426 ++it;
427 break;
428 }
429 }
430
431 if (it != it0)
432 this->erase(it,it0);
433 }
434
435
436 }
437
438
439
441
444 path_t & operator=(const path_t & p){
445
446 if (&p != this){
447 // Yes, important self-check.
448 // Otherwise clear() also makes the"source" empty.
449 set(p);
450 }
451 // std::list<T>::operator=(p);
452 // return assignPa(*this);
453 return *this;
454 }
455
457
460 template <class T2>
462 set(p);
463 return *this;
464 }
465
466
467 inline
468 path_t & operator=(const std::string & p){
469 set(p);
470 return *this;
471 }
472
473 inline
474 path_t & operator=(const char *p){
475 set(p);
476 return *this;
477 }
478
479 // Note: this would be ambiguous!
480 // If (elem_t == std::string), elem cannot be assigned directly, because string suggest full path assignment, at least
481 // path_t & operator=(const elem_t & e)
482
484 /*
485 inline
486 path_t & operator<<(const char *elem){
487 *this <<
488 }
489 */
490
491
492 // TODO: replace these with single
493 /*
494 template <class T2>
495 path_t & operator<<(const T2 & arg){
496 append(args);
497 return *this;
498 }
499 */
500
501
503 path_t & operator<<(const elem_t & elem){
504 appendElem(elem);
505 return *this;
506 }
507
508 inline
509 path_t & operator<<(const path_t & path){
510 this->insert(this->end(), path.begin(), path.end());
511 return *this;
512 }
513
514
515 // Experimental
516 template <class T2>
517 path_t & operator<<(const T2 & strlike){
518 appendPath((const std::string &) strlike);
519 return *this;
520 }
521
522
524 path_t & operator>>(elem_t & e){
525 e = this->back();
526 this->pop_back();
527 return *this;
528 }
529
531
536 virtual
537 std::ostream & toStream(std::ostream & ostr) const { //, char dirSeparator = 0) const {
538
539 if (isRoot())
540 ostr << separator.character;
541 else {
542 static const SprinterLayout layout("/", "", "", "");
543 layout.arrayChars.separator = separator.character;
544 // SprinterLayout layout;
545 // layout.arrayChars.set(0, separator.character,0);
546 // layout.stringChars.fill(0);
547 Sprinter::sequenceToStream(ostr, *this, layout);
548 }
549
550 //Sprinter::toStream(ostr, *this, layout);
551 return ostr;
552 }
553
554 virtual
555 std::string str() const {
556 std::stringstream sstr;
557 toStream(sstr);
558 return sstr.str();
559 }
560
561 virtual
562 operator std::string() const {
563 return str();
564 }
565
566 std::ostream & debug(std::ostream & ostr = std::cout) const {
567 //ostr << "Path<" << typeid(T).name() << "> " << separator << "\n";
568 ostr << "Path elems:" << this->size() << " sep:" << separator << " typeid: " << typeid(T).name() << "\n";
569 ostr << *this << '\n';
570 int i=0;
571 for (typename path_t::const_iterator it = this->begin(); it != this->end(); ++it) {
572 //if (it->empty())
573 // ostr << " " << i << '\t' << "{empty}" << '\n'; //else
574 ostr << " " << i << '\t' << *it << '\n';
575 ++i;
576 }
577 return ostr;
578 }
579
580protected:
581
583 /*
584 void _append(const elem_t &elem){
585 appendElem(elem);
586 }
587
588
589 // Handler for all the rest (non elem_t) arguments.
591 template <typename T2>
592 void _append(const T2 & p){
593 appendElem(p);
594 }
595 */
596
598 void _appendPath(const std::string & p, size_t start=0){
599
600 if (start == p.length()){
601 // Last char in the string has been passed by one, meaning that: previous elem was empty
602 // That is: the previous char was a separator.
603 //appendElems(elem_t()); // Try to append empty.
604 appendElem(elem_t()); // Try to append empty.
605 return;
606 }
607
608 // Append next segment, ie. up to next separator char.
609 size_t nextSep = p.find(separator.character, start);
610
611 // Remaining string...
612 if (nextSep==std::string::npos){
613 // ... is a single element
614 appendElem(p.substr(start));
615 return;
616 }
617 else {
618 // ... contains separator, hence contains several elements
619 appendElem(p.substr(start, nextSep - start)); // maybe empty, nextSep==start
620 _appendPath(p, nextSep + 1);
621 }
622
623 }
624
625
626protected:
627
629 void append(){
630 }
631
633 //void appendElems(){
634 //}
635
637 //void setElems(){
638 // };
639
640};
641
642
643
644template <class T, char SEP='/', bool ALEAD=true, bool AREPEAT=false, bool ATRAIL=true>
645inline
646std::ostream & operator<<(std::ostream & ostr, const Path<T,SEP,ALEAD,AREPEAT,ATRAIL> & p) {
647 return p.toStream(ostr);
648}
649
650
651template <class T, char SEP='/', bool ALEAD=true, bool AREPEAT=false, bool ATRAIL=true>
652inline
653std::istream & operator>>(std::istream &istr, Path<T,SEP,ALEAD,AREPEAT,ATRAIL> & p) {
654 std::string s;
655 istr >> s;
656 p = s;
657 return istr;
658}
659
660/*
661template <class T>
662inline
663Path<T> & operator<<(Path<T> & path, const Path<T> & path2){
664 path.insert(path.end(), path2.begin(), path2.end());
665 return path;
666}
667*/
668
669
670template <class T, char SEP, bool ALEAD, bool AREPEAT, bool ATRAIL>
671const PathSeparatorPolicy Path<T,SEP,ALEAD,AREPEAT,ATRAIL>::separator(SEP, ALEAD, AREPEAT, ATRAIL);
672
673}
674
675#endif /* Path_H_ */
676
677// Drain
Definition Path.h:112
virtual ~Path()
Why the three above instead of this?
Definition Path.h:185
void simplify()
ORIGINAL. Removes trailing empty elements, except for the root itself.
Definition Path.h:382
void simplifyTail()
Remove duplicates of empty elements from the end.
Definition Path.h:414
void _appendPath(const std::string &p, size_t start=0)
"Default" append function.
Definition Path.h:598
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:537
void appendElem(const elem_t &elem)
Main method for adding elements.
Definition Path.h:246
void appendPath(const char *p)
Specialized handler for strings (note, possibly: elem_t==std:string)
Definition Path.h:234
void simplifyHead()
Remove duplicates of empty elements from the start.
Definition Path.h:390
path_t & operator=(const path_t &p)
Assigns a path directly with std::list assignment.
Definition Path.h:444
Path(const char *arg, const TT &... args)
Initialize with a path.
Definition Path.h:166
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:299
Path(const path_t &arg, const TT &... args)
Initialize with a path.
Definition Path.h:150
Path(const path_t &p)
Copy constructor.
Definition Path.h:134
void appendElem(const T2 &elem)
To be specialized in subclasses.
Definition Path.h:284
path_t & operator<<(const elem_t &elem)
Append an element, unless empty string.
Definition Path.h:503
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:139
void append(const T2 &arg, const TT &... args)
Append path with element(s), path(s) or string(s).
Definition Path.h:200
Path(const std::string &arg, const TT &... args)
Initialize with a path.
Definition Path.h:158
path_t & operator=(const Path< T2 > &p)
Conversion from str path type.
Definition Path.h:461
path_t & ensureRoot()
Returns true, if the path is not empty and the first element is empty.
Definition Path.h:327
path_t & operator>>(elem_t &e)
Extract last element.
Definition Path.h:524
void trimTail(bool COMPLETE=false)
Removes trailing empty elements. The resulting string presentation will not end with the separator.
Definition Path.h:354
void append()
Terminal function for variadic templates.
Definition Path.h:629
void trimHead(bool COMPLETE=false)
Removes leading empty elements. The resulting string presentation will not start with the separator.
Definition Path.h:340
bool hasRoot() const
Returns true, if the path is not empty and the first element is empty.
Definition Path.h:311
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
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