OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2012 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 "base/metrics/histogram.h" |
| 6 #include "chrome/browser/chromeos/gdata/gdata_files.h" |
| 7 #include "chrome/browser/chromeos/gdata/gdata_wapi_feed_processor.h" |
| 8 #include "content/public/browser/browser_thread.h" |
| 9 |
| 10 using content::BrowserThread; |
| 11 |
| 12 namespace gdata { |
| 13 |
| 14 namespace { |
| 15 |
| 16 // Recursively extracts the paths set of all sub-directories of |entry|. |
| 17 void GetChildDirectoryPaths(GDataEntry* entry, |
| 18 std::set<FilePath>* changed_dirs) { |
| 19 GDataDirectory* dir = entry->AsGDataDirectory(); |
| 20 if (!dir) |
| 21 return; |
| 22 |
| 23 for (GDataDirectoryCollection::const_iterator it = |
| 24 dir->child_directories().begin(); |
| 25 it != dir->child_directories().end(); ++it) { |
| 26 GDataDirectory* child_dir = it->second; |
| 27 changed_dirs->insert(child_dir->GetFilePath()); |
| 28 GetChildDirectoryPaths(child_dir, changed_dirs); |
| 29 } |
| 30 } |
| 31 |
| 32 } // namespace |
| 33 |
| 34 FeedToFileResourceMapUmaStats::FeedToFileResourceMapUmaStats() |
| 35 : num_regular_files(0), |
| 36 num_hosted_documents(0) { |
| 37 } |
| 38 |
| 39 FeedToFileResourceMapUmaStats::~FeedToFileResourceMapUmaStats() { |
| 40 } |
| 41 |
| 42 GDataWapiFeedProcessor::GDataWapiFeedProcessor( |
| 43 GDataDirectoryService* directory_service) |
| 44 : directory_service_(directory_service) { |
| 45 } |
| 46 |
| 47 GDataWapiFeedProcessor::~GDataWapiFeedProcessor() { |
| 48 } |
| 49 |
| 50 GDataFileError GDataWapiFeedProcessor::ApplyFeeds( |
| 51 const std::vector<DocumentFeed*>& feed_list, |
| 52 int start_changestamp, |
| 53 int root_feed_changestamp, |
| 54 std::set<FilePath>* changed_dirs) { |
| 55 bool is_delta_feed = start_changestamp != 0; |
| 56 |
| 57 directory_service_->set_origin(FROM_SERVER); |
| 58 |
| 59 int delta_feed_changestamp = 0; |
| 60 FeedToFileResourceMapUmaStats uma_stats; |
| 61 FileResourceIdMap file_map; |
| 62 GDataFileError error = FeedToFileResourceMap(feed_list, |
| 63 &file_map, |
| 64 &delta_feed_changestamp, |
| 65 &uma_stats); |
| 66 if (error != GDATA_FILE_OK) |
| 67 return error; |
| 68 |
| 69 ApplyFeedFromFileUrlMap( |
| 70 is_delta_feed, |
| 71 is_delta_feed ? delta_feed_changestamp : root_feed_changestamp, |
| 72 &file_map, |
| 73 changed_dirs); |
| 74 |
| 75 // Shouldn't record histograms when processing delta feeds. |
| 76 if (!is_delta_feed) |
| 77 UpdateFileCountUmaHistograms(uma_stats); |
| 78 |
| 79 return GDATA_FILE_OK; |
| 80 } |
| 81 |
| 82 void GDataWapiFeedProcessor::UpdateFileCountUmaHistograms( |
| 83 const FeedToFileResourceMapUmaStats& uma_stats) const { |
| 84 const int num_total_files = |
| 85 uma_stats.num_hosted_documents + uma_stats.num_regular_files; |
| 86 UMA_HISTOGRAM_COUNTS("GData.NumberOfRegularFiles", |
| 87 uma_stats.num_regular_files); |
| 88 UMA_HISTOGRAM_COUNTS("GData.NumberOfHostedDocuments", |
| 89 uma_stats.num_hosted_documents); |
| 90 UMA_HISTOGRAM_COUNTS("GData.NumberOfTotalFiles", num_total_files); |
| 91 const std::vector<int> all_entry_kinds = DocumentEntry::GetAllEntryKinds(); |
| 92 for (FeedToFileResourceMapUmaStats::EntryKindToCountMap::const_iterator iter = |
| 93 uma_stats.num_files_with_entry_kind.begin(); |
| 94 iter != uma_stats.num_files_with_entry_kind.end(); |
| 95 ++iter) { |
| 96 const DocumentEntry::EntryKind kind = iter->first; |
| 97 const int count = iter->second; |
| 98 for (int i = 0; i < count; ++i) { |
| 99 UMA_HISTOGRAM_CUSTOM_ENUMERATION( |
| 100 "GData.EntryKind", kind, all_entry_kinds); |
| 101 } |
| 102 } |
| 103 } |
| 104 |
| 105 void GDataWapiFeedProcessor::ApplyFeedFromFileUrlMap( |
| 106 bool is_delta_feed, |
| 107 int feed_changestamp, |
| 108 FileResourceIdMap* file_map, |
| 109 std::set<FilePath>* changed_dirs) { |
| 110 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 111 DCHECK(changed_dirs); |
| 112 |
| 113 if (!is_delta_feed) { // Full update. |
| 114 directory_service_->root()->RemoveChildren(); |
| 115 changed_dirs->insert(directory_service_->root()->GetFilePath()); |
| 116 } |
| 117 directory_service_->set_largest_changestamp(feed_changestamp); |
| 118 |
| 119 scoped_ptr<GDataDirectoryService> orphaned_dir_service( |
| 120 new GDataDirectoryService); |
| 121 // Go through all entries generated by the feed and apply them to the local |
| 122 // snapshot of the file system. |
| 123 for (FileResourceIdMap::iterator it = file_map->begin(); |
| 124 it != file_map->end();) { |
| 125 // Ensure that the entry is deleted, unless the ownership is explicitly |
| 126 // transferred by entry.release(). |
| 127 scoped_ptr<GDataEntry> entry(it->second); |
| 128 DCHECK_EQ(it->first, entry->resource_id()); |
| 129 // Erase the entry so the deleted entry won't be referenced. |
| 130 file_map->erase(it++); |
| 131 |
| 132 GDataEntry* old_entry = |
| 133 directory_service_->GetEntryByResourceId(entry->resource_id()); |
| 134 GDataDirectory* dest_dir = NULL; |
| 135 if (entry->is_deleted()) { // Deleted file/directory. |
| 136 DVLOG(1) << "Removing file " << entry->base_name(); |
| 137 if (!old_entry) |
| 138 continue; |
| 139 |
| 140 dest_dir = old_entry->parent(); |
| 141 if (!dest_dir) { |
| 142 NOTREACHED(); |
| 143 continue; |
| 144 } |
| 145 RemoveEntryFromDirectoryAndCollectChangedDirectories( |
| 146 dest_dir, old_entry, changed_dirs); |
| 147 } else if (old_entry) { // Change or move of existing entry. |
| 148 // Please note that entry rename is just a special case of change here |
| 149 // since name is just one of the properties that can change. |
| 150 DVLOG(1) << "Changed file " << entry->base_name(); |
| 151 dest_dir = old_entry->parent(); |
| 152 if (!dest_dir) { |
| 153 NOTREACHED(); |
| 154 continue; |
| 155 } |
| 156 // Move children files over if we are dealing with directories. |
| 157 if (old_entry->AsGDataDirectory() && entry->AsGDataDirectory()) { |
| 158 entry->AsGDataDirectory()->TakeOverEntries( |
| 159 old_entry->AsGDataDirectory()); |
| 160 } |
| 161 // Remove the old instance of this entry. |
| 162 RemoveEntryFromDirectoryAndCollectChangedDirectories( |
| 163 dest_dir, old_entry, changed_dirs); |
| 164 // Did we actually move the new file to another directory? |
| 165 if (dest_dir->resource_id() != entry->parent_resource_id()) { |
| 166 changed_dirs->insert(dest_dir->GetFilePath()); |
| 167 dest_dir = FindDirectoryForNewEntry(entry.get(), |
| 168 *file_map, |
| 169 orphaned_dir_service.get()); |
| 170 } |
| 171 DCHECK(dest_dir); |
| 172 AddEntryToDirectoryAndCollectChangedDirectories( |
| 173 entry.release(), |
| 174 dest_dir, |
| 175 orphaned_dir_service.get(), |
| 176 changed_dirs); |
| 177 } else { // Adding a new file. |
| 178 dest_dir = FindDirectoryForNewEntry(entry.get(), |
| 179 *file_map, |
| 180 orphaned_dir_service.get()); |
| 181 DCHECK(dest_dir); |
| 182 AddEntryToDirectoryAndCollectChangedDirectories( |
| 183 entry.release(), |
| 184 dest_dir, |
| 185 orphaned_dir_service.get(), |
| 186 changed_dirs); |
| 187 } |
| 188 |
| 189 // Record changed directory if this was a delta feed and the parent |
| 190 // directory is already properly rooted within its parent. |
| 191 if (dest_dir && (dest_dir->parent() || |
| 192 dest_dir == directory_service_->root()) && |
| 193 dest_dir != orphaned_dir_service->root() && is_delta_feed) { |
| 194 changed_dirs->insert(dest_dir->GetFilePath()); |
| 195 } |
| 196 } |
| 197 // All entry must be erased from the map. |
| 198 DCHECK(file_map->empty()); |
| 199 } |
| 200 |
| 201 // static |
| 202 void GDataWapiFeedProcessor::AddEntryToDirectoryAndCollectChangedDirectories( |
| 203 GDataEntry* entry, |
| 204 GDataDirectory* directory, |
| 205 GDataDirectoryService* orphaned_dir_service, |
| 206 std::set<FilePath>* changed_dirs) { |
| 207 directory->AddEntry(entry); |
| 208 if (entry->AsGDataDirectory() && directory != orphaned_dir_service->root()) |
| 209 changed_dirs->insert(entry->GetFilePath()); |
| 210 } |
| 211 |
| 212 // static |
| 213 void GDataWapiFeedProcessor:: |
| 214 RemoveEntryFromDirectoryAndCollectChangedDirectories( |
| 215 GDataDirectory* directory, |
| 216 GDataEntry* entry, |
| 217 std::set<FilePath>* changed_dirs) { |
| 218 // Get the list of all sub-directory paths, so we can notify their listeners |
| 219 // that they are smoked. |
| 220 GetChildDirectoryPaths(entry, changed_dirs); |
| 221 directory->RemoveEntry(entry); |
| 222 } |
| 223 |
| 224 GDataDirectory* GDataWapiFeedProcessor::FindDirectoryForNewEntry( |
| 225 GDataEntry* new_entry, |
| 226 const FileResourceIdMap& file_map, |
| 227 GDataDirectoryService* orphaned_dir_service) { |
| 228 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 229 GDataDirectory* dir = NULL; |
| 230 // Added file. |
| 231 const std::string& parent_id = new_entry->parent_resource_id(); |
| 232 if (parent_id.empty()) { |
| 233 dir = directory_service_->root(); |
| 234 DVLOG(1) << "Root parent for " << new_entry->base_name(); |
| 235 } else { |
| 236 GDataEntry* entry = directory_service_->GetEntryByResourceId(parent_id); |
| 237 dir = entry ? entry->AsGDataDirectory() : NULL; |
| 238 if (!dir) { |
| 239 // The parent directory was also added with this set of feeds. |
| 240 FileResourceIdMap::const_iterator find_iter = |
| 241 file_map.find(parent_id); |
| 242 dir = (find_iter != file_map.end() && |
| 243 find_iter->second) ? |
| 244 find_iter->second->AsGDataDirectory() : NULL; |
| 245 if (dir) { |
| 246 DVLOG(1) << "Found parent for " << new_entry->base_name() |
| 247 << " in file_map " << parent_id; |
| 248 } else { |
| 249 DVLOG(1) << "Adding orphan " << new_entry->GetFilePath().value(); |
| 250 dir = orphaned_dir_service->root(); |
| 251 } |
| 252 } |
| 253 } |
| 254 return dir; |
| 255 } |
| 256 |
| 257 GDataFileError GDataWapiFeedProcessor::FeedToFileResourceMap( |
| 258 const std::vector<DocumentFeed*>& feed_list, |
| 259 FileResourceIdMap* file_map, |
| 260 int* feed_changestamp, |
| 261 FeedToFileResourceMapUmaStats* uma_stats) { |
| 262 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 263 DCHECK(uma_stats); |
| 264 |
| 265 GDataFileError error = GDATA_FILE_OK; |
| 266 uma_stats->num_regular_files = 0; |
| 267 uma_stats->num_hosted_documents = 0; |
| 268 uma_stats->num_files_with_entry_kind.clear(); |
| 269 for (size_t i = 0; i < feed_list.size(); ++i) { |
| 270 const DocumentFeed* feed = feed_list[i]; |
| 271 |
| 272 // Get upload url from the root feed. Links for all other collections will |
| 273 // be handled in GDatadirectory::FromDocumentEntry(); |
| 274 if (i == 0) { |
| 275 const Link* root_feed_upload_link = |
| 276 feed->GetLinkByType(Link::RESUMABLE_CREATE_MEDIA); |
| 277 if (root_feed_upload_link) |
| 278 directory_service_->root()->set_upload_url( |
| 279 root_feed_upload_link->href()); |
| 280 *feed_changestamp = feed->largest_changestamp(); |
| 281 DCHECK_GE(*feed_changestamp, 0); |
| 282 } |
| 283 |
| 284 for (ScopedVector<DocumentEntry>::const_iterator iter = |
| 285 feed->entries().begin(); |
| 286 iter != feed->entries().end(); ++iter) { |
| 287 DocumentEntry* doc = *iter; |
| 288 GDataEntry* entry = GDataEntry::FromDocumentEntry( |
| 289 NULL, doc, directory_service_); |
| 290 // Some document entries don't map into files (i.e. sites). |
| 291 if (!entry) |
| 292 continue; |
| 293 // Count the number of files. |
| 294 GDataFile* as_file = entry->AsGDataFile(); |
| 295 if (as_file) { |
| 296 if (as_file->is_hosted_document()) |
| 297 ++uma_stats->num_hosted_documents; |
| 298 else |
| 299 ++uma_stats->num_regular_files; |
| 300 ++uma_stats->num_files_with_entry_kind[as_file->kind()]; |
| 301 } |
| 302 |
| 303 FileResourceIdMap::iterator map_entry = |
| 304 file_map->find(entry->resource_id()); |
| 305 |
| 306 // An entry with the same self link may already exist, so we need to |
| 307 // release the existing GDataEntry instance before overwriting the |
| 308 // entry with another GDataEntry instance. |
| 309 if (map_entry != file_map->end()) { |
| 310 LOG(WARNING) << "Found duplicate file " |
| 311 << map_entry->second->base_name(); |
| 312 |
| 313 delete map_entry->second; |
| 314 file_map->erase(map_entry); |
| 315 } |
| 316 file_map->insert( |
| 317 std::pair<std::string, GDataEntry*>(entry->resource_id(), entry)); |
| 318 } |
| 319 } |
| 320 |
| 321 if (error != GDATA_FILE_OK) { |
| 322 // If the code above fails to parse a feed, any GDataEntry instance |
| 323 // added to |file_by_url| is not managed by a GDataDirectory instance, |
| 324 // so we need to explicitly release them here. |
| 325 STLDeleteValues(file_map); |
| 326 } |
| 327 |
| 328 return error; |
| 329 } |
| 330 |
| 331 } // namespace gdata |
OLD | NEW |