VideoTools
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
gdirectorytree.cpp
Go to the documentation of this file.
1 //
2 // Copyright (C) 2017 Graeme Walker
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
16 // ===
17 //
18 // gdirectorytree.cpp
19 //
20 
21 #include "gdef.h"
22 #include "gdirectorytree.h"
23 #include "gdirectory.h"
24 #include "groot.h"
25 #include "gfile.h"
26 #include "gpath.h"
27 #include "gassert.h"
28 #include "glog.h"
29 #include <algorithm>
30 #include <functional>
31 #include <iterator>
32 
33 namespace
34 {
35  bool compare_names( const G::DirectoryList::Item & a , const G::DirectoryList::Item & b )
36  {
37  return a.m_name < b.m_name ;
38  }
39  struct CompareNames
40  {
41  explicit CompareNames( bool reverse ) : m_reverse(reverse) {}
42  bool operator()( const G::DirectoryList::Item & a , const G::DirectoryList::Item & b ) const
43  {
44  return m_reverse ? compare_names( b , a ) : compare_names( a , b ) ;
45  }
46  bool m_reverse ;
47  } ;
48  template <typename T, typename C>
49  bool is__sorted( T p , T end , C cmp )
50  {
51  T prev = p ;
52  for( bool first = true ; p != end ; ++p , first = false )
53  {
54  if( !first && !cmp(*prev,*p) )
55  return false ;
56  prev = p ;
57  }
58  return true ;
59  }
60 }
61 
63  m_reverse(false) ,
64  m_callback(0)
65 {
66 }
67 
68 G::DirectoryTree::DirectoryTree( const Path & path , bool reverse , DirectoryTreeCallback * callback ) :
69  m_reverse(reverse) ,
70  m_callback(callback)
71 {
72  bool exists = false ;
73  {
74  G::Root claim_root ;
75  exists = G::File::exists(path) ;
76  }
77  if( exists )
78  {
79  FileItem item ;
80  item.m_is_dir = isDir(path) ;
81  item.m_path = path ;
82  item.m_name = path.basename() ;
83 
84  if( !ignore(item,0) )
85  {
86  m_stack.push_back( Context() ) ;
87  Context & context = m_stack.back() ;
88 
89  context.m_list.push_back( item ) ;
90  context.m_p = context.m_list.begin() ;
91  context.m_end = context.m_list.end() ;
92  }
93  }
94 }
95 
96 G::DirectoryTree::FileList G::DirectoryTree::readFileList() const
97 {
98  FileList file_list ;
99  {
100  G::Root claim_root ;
101  G::DirectoryList::readAll( (*m_stack.back().m_p).m_path , file_list , true ) ;
102  }
103  G_ASSERT( is__sorted(file_list.begin(),file_list.end(),compare_names) ) ;
104  filter( file_list , m_stack.size() ) ;
105  return file_list ;
106 }
107 
109 {
110  G_ASSERT( !m_stack.empty() ) ;
111 
112  FileList file_list ;
113  if( (*m_stack.back().m_p).m_is_dir )
114  {
115  file_list = readFileList() ;
116  if( m_reverse )
117  std::reverse( file_list.begin() , file_list.end() ) ;
118  }
119 
120  if( file_list.empty() )
121  {
122  while( !m_stack.empty() )
123  {
124  ++m_stack.back().m_p ;
125  if( m_stack.back().m_p == m_stack.back().m_end )
126  m_stack.pop_back() ;
127  else
128  break ;
129  }
130  }
131  else
132  {
133  m_stack.push_back( Context() ) ;
134  Context & context = m_stack.back() ;
135  context.m_list.swap( file_list ) ;
136  context.m_p = context.m_list.begin() ;
137  context.m_end = context.m_list.end() ;
138  }
139  return *this ;
140 }
141 
142 bool G::DirectoryTree::operator==( const DirectoryTree & other ) const
143 {
144  if( m_stack.size() != other.m_stack.size() )
145  return false ;
146  else if( m_stack.empty() )
147  return true ;
148  return
149  (*m_stack.back().m_p).m_path == (*other.m_stack.back().m_p).m_path ;
150 }
151 
152 bool G::DirectoryTree::operator!=( const DirectoryTree & other ) const
153 {
154  return !( *this == other ) ;
155 }
156 
158 {
159  G_ASSERT( !m_stack.empty() ) ;
160  return *m_stack.back().m_p ;
161 }
162 
163 bool G::DirectoryTree::isDir( const Path & path )
164 {
165  G::Root claim_root ;
166  return Directory(path).valid() ;
167 }
168 
170 {
171  G_ASSERT( !m_stack.empty() ) ;
172  return m_stack.size() - 1U ;
173 }
174 
176  m_stack(other.m_stack) ,
177  m_reverse(other.m_reverse) ,
178  m_callback(other.m_callback)
179 {
180  Stack::iterator other_p = (const_cast<DirectoryTree&>(other)).m_stack.begin() ;
181  for( Stack::iterator this_p = m_stack.begin() ; this_p != m_stack.end() ; ++this_p , ++other_p )
182  {
183  (*this_p).m_p = (*this_p).m_list.begin() ;
184  (*this_p).m_end = (*this_p).m_list.end() ;
185  std::advance( (*this_p).m_p , std::distance((*other_p).m_list.begin(),(*other_p).m_p) ) ;
186  }
187 }
188 
190 {
191  m_stack.swap( other.m_stack ) ;
192  std::swap( m_reverse , other.m_reverse ) ;
193  std::swap( m_callback , other.m_callback ) ;
194 }
195 
197 {
198  DirectoryTree tmp( other ) ;
199  swap( tmp ) ;
200  return *this ;
201 }
202 
203 bool G::DirectoryTree::down( const std::string & name , bool at_or_after )
204 {
205  typedef std::pair<FileList::iterator,FileList::iterator> Range ;
206  G_ASSERT( !m_stack.empty() ) ;
207  G_ASSERT( (*m_stack.back().m_p).m_is_dir ) ;
208 
209  FileList file_list = readFileList() ;
210 
211  if( m_reverse )
212  std::reverse( file_list.begin() , file_list.end() ) ;
213 
214  FileItem item ;
215  item.m_name = name ;
216  G_ASSERT( is__sorted(file_list.begin(),file_list.end(),CompareNames(m_reverse)) ) ;
217  Range range = std::equal_range( file_list.begin() , file_list.end() , item , CompareNames(m_reverse) ) ;
218  if( at_or_after && range.first == file_list.end() )
219  {
220  while( !m_stack.empty() )
221  {
222  ++m_stack.back().m_p ;
223  if( m_stack.back().m_p == m_stack.back().m_end )
224  m_stack.pop_back() ;
225  else
226  break ;
227  }
228  return false ;
229  }
230  else if( at_or_after || range.first != range.second )
231  {
232  m_stack.push_back( Context() ) ;
233  Context & context = m_stack.back() ;
234  context.m_list.swap( file_list ) ;
235  context.m_p = range.first ;
236  context.m_end = context.m_list.end() ;
237  return true ;
238  }
239  else
240  {
241  return false ;
242  }
243 }
244 
246 {
247  G_ASSERT( !m_stack.empty() ) ;
248  m_stack.pop_back() ;
249 }
250 
251 void G::DirectoryTree::reverse( bool in_reverse )
252 {
253  if( m_reverse != in_reverse )
254  {
255  m_reverse = in_reverse ;
256  for( Stack::iterator stack_p = m_stack.begin() ; stack_p != m_stack.end() ; ++stack_p )
257  {
258  Context & context = *stack_p ;
259  size_t pos = std::distance( context.m_list.begin() , context.m_p ) ;
260  std::reverse( context.m_list.begin() , context.m_list.end() ) ;
261  context.m_p = context.m_list.begin() ;
262  context.m_end = context.m_list.end() ;
263  std::advance( context.m_p , context.m_list.size()-pos-1 ) ;
264  }
265  }
266 }
267 
268 void G::DirectoryTree::filter( FileList & list , size_t depth ) const
269 {
270  for( FileList::iterator p = list.begin() ; p != list.end() ; )
271  {
272  if( ignore(*p,depth) )
273  p = list.erase( p ) ;
274  else
275  ++p ;
276  }
277 }
278 
279 bool G::DirectoryTree::ignore( const FileItem & item , size_t depth ) const
280 {
281  if( m_callback )
282  return m_callback->directoryTreeIgnore( item , depth ) ;
283  else
284  return false ;
285 }
286 
288 {
289  return m_reverse ;
290 }
291 
292 // ==
293 
294 G::DirectoryTreeCallback::~DirectoryTreeCallback()
295 {
296 }
297 
298 /// \file gdirectorytree.cpp
void reverse(bool in_reverse=true)
Changes the reversal state, returning the previous value.
DirectoryTree & operator++()
Pre-increment operator. Moves to the next file or directory.
A directory-entry item for G::DirectoryList.
Definition: gdirectory.h:148
A directory tree iterator for sorted, depth-first traversal of files and directories.
std::string basename() const
Returns the rightmost part of the path, ignoring "." parts.
Definition: gpath.cpp:310
A class which acquires the process's special privileges on construction and releases them on destruct...
Definition: groot.h:49
bool operator==(const DirectoryTree &) const
Comparison operator.
void readAll(const Path &dir)
An initialiser that is to be used after default construction.
Definition: gdirectory.cpp:88
bool operator!=(const DirectoryTree &) const
Comparison operator.
bool valid(bool for_creating_files=false) const
Returns true if the object represents a valid directory.
An encapsulation of a file system directory that allows for iterating through the set of contained fi...
Definition: gdirectory.h:45
DirectoryTree()
Default contructor for an off-the-end iterator.
const DirectoryList::Item & operator*() const
Dereference operator yielding a G::DirectoryList::Item.
static bool exists(const Path &file)
Returns true if the file (directory, device etc.) exists.
Definition: gfile.cpp:132
DirectoryTree & operator=(const DirectoryTree &)
Assignment operator.
void swap(DirectoryTree &other)
Swaps this and other.
bool down(const std::string &name, bool at_or_after=false)
Moves deeper into the directory tree, with a precondition that the iterator currently points to a dir...
bool reversed() const
Returns the current reversal state.
size_t depth() const
Returns the nesting depth compared to the root.
A callback interface to allow G::DirectoryTree to ignore paths.
A Path object represents a file system path.
Definition: gpath.h:72
void up()
Moves up to the parent directory.