- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / chromeos / drive / file_system / create_directory_operation.cc
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 #include "chrome/browser/chromeos/drive/file_system/create_directory_operation.h"
6
7 #include "chrome/browser/chromeos/drive/drive.pb.h"
8 #include "chrome/browser/chromeos/drive/file_system/operation_observer.h"
9 #include "chrome/browser/chromeos/drive/file_system_util.h"
10 #include "chrome/browser/chromeos/drive/job_scheduler.h"
11 #include "chrome/browser/chromeos/drive/resource_entry_conversion.h"
12 #include "chrome/browser/google_apis/gdata_errorcode.h"
13 #include "chrome/browser/google_apis/gdata_wapi_parser.h"
14 #include "content/public/browser/browser_thread.h"
15
16 using content::BrowserThread;
17
18 namespace drive {
19 namespace file_system {
20
21 namespace {
22
23 // Part of CreateDirectoryRecursively(). Adds an |entry| for new directory
24 // to |metadata|, and return the status. If succeeded, |file_path| will store
25 // the path to the result file.
26 FileError UpdateLocalStateForCreateDirectoryRecursively(
27     internal::ResourceMetadata* metadata,
28     scoped_ptr<google_apis::ResourceEntry> resource_entry,
29     base::FilePath* file_path) {
30   DCHECK(metadata);
31   DCHECK(file_path);
32
33   ResourceEntry entry;
34   std::string parent_resource_id;
35   if (!ConvertToResourceEntry(*resource_entry, &entry, &parent_resource_id))
36     return FILE_ERROR_NOT_A_FILE;
37
38   std::string parent_local_id;
39   FileError result = metadata->GetIdByResourceId(parent_resource_id,
40                                                  &parent_local_id);
41   if (result != FILE_ERROR_OK)
42     return result;
43   entry.set_parent_local_id(parent_local_id);
44
45   std::string local_id;
46   result = metadata->AddEntry(entry, &local_id);
47   // Depending on timing, a metadata may be updated by change list already.
48   // So, FILE_ERROR_EXISTS is not an error.
49   if (result == FILE_ERROR_EXISTS)
50     result = metadata->GetIdByResourceId(entry.resource_id(), &local_id);
51
52   if (result == FILE_ERROR_OK)
53     *file_path = metadata->GetFilePath(local_id);
54
55   return result;
56 }
57
58 }  // namespace
59
60 CreateDirectoryOperation::CreateDirectoryOperation(
61     base::SequencedTaskRunner* blocking_task_runner,
62     OperationObserver* observer,
63     JobScheduler* scheduler,
64     internal::ResourceMetadata* metadata)
65     : blocking_task_runner_(blocking_task_runner),
66       observer_(observer),
67       scheduler_(scheduler),
68       metadata_(metadata),
69       weak_ptr_factory_(this) {
70   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
71 }
72
73 CreateDirectoryOperation::~CreateDirectoryOperation() {
74   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
75 }
76
77 void CreateDirectoryOperation::CreateDirectory(
78     const base::FilePath& directory_path,
79     bool is_exclusive,
80     bool is_recursive,
81     const FileOperationCallback& callback) {
82   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
83   DCHECK(!callback.is_null());
84
85   ResourceEntry* entry = new ResourceEntry;
86   base::PostTaskAndReplyWithResult(
87       blocking_task_runner_.get(),
88       FROM_HERE,
89       base::Bind(&CreateDirectoryOperation::GetExistingDeepestDirectory,
90                  metadata_,
91                  directory_path,
92                  entry),
93       base::Bind(&CreateDirectoryOperation::
94                      CreateDirectoryAfterGetExistingDeepestDirectory,
95                  weak_ptr_factory_.GetWeakPtr(),
96                  directory_path,
97                  is_exclusive,
98                  is_recursive,
99                  callback,
100                  base::Owned(entry)));
101 }
102
103 // static
104 base::FilePath CreateDirectoryOperation::GetExistingDeepestDirectory(
105     internal::ResourceMetadata* metadata,
106     const base::FilePath& directory_path,
107     ResourceEntry* entry) {
108   DCHECK(metadata);
109   DCHECK(entry);
110
111   std::vector<base::FilePath::StringType> components;
112   directory_path.GetComponents(&components);
113
114   if (components.empty() || components[0] != util::kDriveGrandRootDirName)
115     return base::FilePath();
116
117   std::string local_id = util::kDriveGrandRootSpecialResourceId;
118   for (size_t i = 1; i < components.size(); ++i) {
119     std::string child_local_id = metadata->GetChildId(local_id, components[i]);
120     if (child_local_id.empty())
121       break;
122     local_id = child_local_id;
123   }
124
125   FileError error = metadata->GetResourceEntryById(local_id, entry);
126   DCHECK_EQ(FILE_ERROR_OK, error);
127
128   if (!entry->file_info().is_directory())
129     return base::FilePath();
130
131   return metadata->GetFilePath(local_id);
132 }
133
134 void CreateDirectoryOperation::CreateDirectoryAfterGetExistingDeepestDirectory(
135     const base::FilePath& directory_path,
136     bool is_exclusive,
137     bool is_recursive,
138     const FileOperationCallback& callback,
139     ResourceEntry* existing_deepest_directory_entry,
140     const base::FilePath& existing_deepest_directory_path) {
141   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
142   DCHECK(!callback.is_null());
143   DCHECK(existing_deepest_directory_entry);
144
145   if (existing_deepest_directory_path.empty()) {
146     callback.Run(FILE_ERROR_NOT_FOUND);
147     return;
148   }
149
150   if (directory_path == existing_deepest_directory_path) {
151     callback.Run(is_exclusive ? FILE_ERROR_EXISTS : FILE_ERROR_OK);
152     return;
153   }
154
155   // If it is not recursive creation, the found directory must be the direct
156   // parent of |directory_path| to ensure creating exact one directory.
157   if (!is_recursive &&
158       existing_deepest_directory_path != directory_path.DirName()) {
159     callback.Run(FILE_ERROR_NOT_FOUND);
160     return;
161   }
162
163   // Create directories under the found directory.
164   base::FilePath remaining_path;
165   existing_deepest_directory_path.AppendRelativePath(
166       directory_path, &remaining_path);
167   CreateDirectoryRecursively(existing_deepest_directory_entry->resource_id(),
168                              remaining_path, callback);
169 }
170
171 void CreateDirectoryOperation::CreateDirectoryRecursively(
172     const std::string& parent_resource_id,
173     const base::FilePath& relative_file_path,
174     const FileOperationCallback& callback) {
175   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
176   DCHECK(!callback.is_null());
177
178   // Split the first component and remaining ones of |relative_file_path|.
179   std::vector<base::FilePath::StringType> components;
180   relative_file_path.GetComponents(&components);
181   DCHECK(!components.empty());
182   base::FilePath title(components[0]);
183   base::FilePath remaining_path;
184   title.AppendRelativePath(relative_file_path, &remaining_path);
185
186   scheduler_->AddNewDirectory(
187       parent_resource_id,
188       title.AsUTF8Unsafe(),
189       base::Bind(&CreateDirectoryOperation
190                      ::CreateDirectoryRecursivelyAfterAddNewDirectory,
191                  weak_ptr_factory_.GetWeakPtr(), remaining_path, callback));
192 }
193
194 void CreateDirectoryOperation::CreateDirectoryRecursivelyAfterAddNewDirectory(
195     const base::FilePath& remaining_path,
196     const FileOperationCallback& callback,
197     google_apis::GDataErrorCode gdata_error,
198     scoped_ptr<google_apis::ResourceEntry> resource_entry) {
199   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
200   DCHECK(!callback.is_null());
201
202   FileError error = GDataToFileError(gdata_error);
203   if (error != FILE_ERROR_OK) {
204     callback.Run(error);
205     return;
206   }
207   DCHECK(resource_entry);
208   const std::string& resource_id = resource_entry->resource_id();
209
210   // Note that the created directory may be renamed inside
211   // ResourceMetadata::AddEntry due to name confliction.
212   // What we actually need here is the new created path (not the path we try
213   // to create).
214   base::FilePath* file_path = new base::FilePath;
215   base::PostTaskAndReplyWithResult(
216       blocking_task_runner_.get(),
217       FROM_HERE,
218       base::Bind(&UpdateLocalStateForCreateDirectoryRecursively,
219                  metadata_,
220                  base::Passed(&resource_entry),
221                  file_path),
222       base::Bind(&CreateDirectoryOperation::
223                      CreateDirectoryRecursivelyAfterUpdateLocalState,
224                  weak_ptr_factory_.GetWeakPtr(),
225                  resource_id,
226                  remaining_path,
227                  callback,
228                  base::Owned(file_path)));
229 }
230
231 void CreateDirectoryOperation::CreateDirectoryRecursivelyAfterUpdateLocalState(
232     const std::string& resource_id,
233     const base::FilePath& remaining_path,
234     const FileOperationCallback& callback,
235     base::FilePath* file_path,
236     FileError error) {
237   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
238   DCHECK(!callback.is_null());
239
240   if (error != FILE_ERROR_OK) {
241     callback.Run(error);
242     return;
243   }
244
245   observer_->OnDirectoryChangedByOperation(file_path->DirName());
246
247   if (remaining_path.empty()) {
248     // All directories are created successfully.
249     callback.Run(FILE_ERROR_OK);
250     return;
251   }
252
253   // Create descendant directories.
254   CreateDirectoryRecursively(resource_id, remaining_path, callback);
255 }
256
257 }  // namespace file_system
258 }  // namespace drive