1// Copyright (c) 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#ifndef STORAGE_BROWSER_FILEAPI_RECURSIVE_OPERATION_DELEGATE_H_
6#define STORAGE_BROWSER_FILEAPI_RECURSIVE_OPERATION_DELEGATE_H_
7
8#include <queue>
9#include <stack>
10
11#include "base/basictypes.h"
12#include "base/callback.h"
13#include "base/memory/weak_ptr.h"
14#include "storage/browser/fileapi/file_system_operation.h"
15#include "storage/browser/fileapi/file_system_url.h"
16
17namespace storage {
18
19class FileSystemContext;
20class FileSystemOperationRunner;
21
22// A base class for recursive operation delegates.
23//
24// In short, each subclass should override ProcessFile and ProcessDirectory
25// to process a directory or a file. To start the recursive operation it
26// should also call StartRecursiveOperation.
27class STORAGE_EXPORT RecursiveOperationDelegate
28    : public base::SupportsWeakPtr<RecursiveOperationDelegate> {
29 public:
30  typedef FileSystemOperation::StatusCallback StatusCallback;
31  typedef FileSystemOperation::FileEntryList FileEntryList;
32
33  virtual ~RecursiveOperationDelegate();
34
35  // This is called when the consumer of this instance starts a non-recursive
36  // operation.
37  virtual void Run() = 0;
38
39  // This is called when the consumer of this instance starts a recursive
40  // operation.
41  virtual void RunRecursively() = 0;
42
43  // This is called each time a file is found while recursively
44  // performing an operation.
45  virtual void ProcessFile(const FileSystemURL& url,
46                           const StatusCallback& callback) = 0;
47
48  // This is called each time a directory is found while recursively
49  // performing an operation.
50  virtual void ProcessDirectory(const FileSystemURL& url,
51                                const StatusCallback& callback) = 0;
52
53
54  // This is called each time after files and subdirectories for a
55  // directory is processed while recursively performing an operation.
56  virtual void PostProcessDirectory(const FileSystemURL& url,
57                                    const StatusCallback& callback) = 0;
58
59  // Cancels the currently running operation.
60  void Cancel();
61
62 protected:
63  explicit RecursiveOperationDelegate(FileSystemContext* file_system_context);
64
65  // Starts to process files/directories recursively from the given |root|.
66  // This will call ProcessFile and ProcessDirectory on each file or directory.
67  //
68  // First, this tries to call ProcessFile with |root| regardless whether it is
69  // actually a file or a directory. If it is a directory, ProcessFile should
70  // return File::FILE_NOT_A_FILE.
71  //
72  // For each directory, the recursive operation works as follows:
73  // ProcessDirectory is called first for the directory.
74  // Then the directory contents are read (to obtain its sub directories and
75  // files in it).
76  // ProcessFile is called for found files. This may run in parallel.
77  // The same step is recursively applied to each subdirectory.
78  // After all files and subdirectories in a directory are processed,
79  // PostProcessDirectory is called for the directory.
80  // Here is an example;
81  // a_dir/ -+- b1_dir/ -+- c1_dir/ -+- d1_file
82  //         |           |           |
83  //         |           +- c2_file  +- d2_file
84  //         |
85  //         +- b2_dir/ --- e_dir/
86  //         |
87  //         +- b3_file
88  //         |
89  //         +- b4_file
90  // Then traverse order is:
91  // ProcessFile(a_dir) (This should return File::FILE_NOT_A_FILE).
92  // ProcessDirectory(a_dir).
93  // ProcessFile(b3_file), ProcessFile(b4_file). (in parallel).
94  // ProcessDirectory(b1_dir).
95  // ProcessFile(c2_file)
96  // ProcessDirectory(c1_dir).
97  // ProcessFile(d1_file), ProcessFile(d2_file). (in parallel).
98  // PostProcessDirectory(c1_dir)
99  // PostProcessDirectory(b1_dir).
100  // ProcessDirectory(b2_dir)
101  // ProcessDirectory(e_dir)
102  // PostProcessDirectory(e_dir)
103  // PostProcessDirectory(b2_dir)
104  // PostProcessDirectory(a_dir)
105  //
106  // |callback| is fired with base::File::FILE_OK when every file/directory
107  // under |root| is processed, or fired earlier when any suboperation fails.
108  void StartRecursiveOperation(const FileSystemURL& root,
109                               const StatusCallback& callback);
110
111  FileSystemContext* file_system_context() { return file_system_context_; }
112  const FileSystemContext* file_system_context() const {
113    return file_system_context_;
114  }
115
116  FileSystemOperationRunner* operation_runner();
117
118  // Called when Cancel() is called. This is a hook to do something more
119  // in a derived class. By default, do nothing.
120  virtual void OnCancel();
121
122 private:
123  void DidTryProcessFile(const FileSystemURL& root,
124                         base::File::Error error);
125  void ProcessNextDirectory();
126  void DidProcessDirectory(base::File::Error error);
127  void DidReadDirectory(const FileSystemURL& parent,
128                        base::File::Error error,
129                        const FileEntryList& entries,
130                        bool has_more);
131  void ProcessPendingFiles();
132  void DidProcessFile(base::File::Error error);
133  void ProcessSubDirectory();
134  void DidPostProcessDirectory(base::File::Error error);
135
136  // Called when all recursive operation is done (or an error occurs).
137  void Done(base::File::Error error);
138
139  FileSystemContext* file_system_context_;
140  StatusCallback callback_;
141  std::stack<FileSystemURL> pending_directories_;
142  std::stack<std::queue<FileSystemURL> > pending_directory_stack_;
143  std::queue<FileSystemURL> pending_files_;
144  int inflight_operations_;
145  bool canceled_;
146
147  DISALLOW_COPY_AND_ASSIGN(RecursiveOperationDelegate);
148};
149
150}  // namespace storage
151
152#endif  // STORAGE_BROWSER_FILEAPI_RECURSIVE_OPERATION_DELEGATE_H_
153