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 5class _CacheEntry(object): 6 def __init__(self, cache_data, version): 7 self._cache_data = cache_data 8 self.version = version 9 10class CompiledFileSystem(object): 11 """This class caches FileSystem data that has been processed. 12 """ 13 class Factory(object): 14 """A class to build a CompiledFileSystem backed by |file_system|. 15 """ 16 def __init__(self, file_system, object_store_creator): 17 self._file_system = file_system 18 self._object_store_creator = object_store_creator 19 20 def Create(self, populate_function, cls, category=None): 21 """Create a CompiledFileSystem that populates the cache by calling 22 |populate_function| with (path, data), where |data| is the data that was 23 fetched from |path|. 24 The namespace for the file system is derived like ObjectStoreCreator: from 25 |cls| along with an optional |category|. 26 """ 27 assert isinstance(cls, type) 28 assert not cls.__name__[0].islower() # guard against non-class types 29 full_name = [cls.__name__, self._file_system.GetIdentity()] 30 if category is not None: 31 full_name.append(category) 32 def create_object_store(my_category): 33 return self._object_store_creator.Create( 34 CompiledFileSystem, category='/'.join(full_name + [my_category])) 35 return CompiledFileSystem(self._file_system, 36 populate_function, 37 create_object_store('file'), 38 create_object_store('list')) 39 40 def __init__(self, 41 file_system, 42 populate_function, 43 file_object_store, 44 list_object_store): 45 self._file_system = file_system 46 self._populate_function = populate_function 47 self._file_object_store = file_object_store 48 self._list_object_store = list_object_store 49 50 def _RecursiveList(self, path): 51 files = [] 52 for filename in self._file_system.ReadSingle(path): 53 if filename.endswith('/'): 54 files.extend(['%s%s' % (filename, f) 55 for f in self._RecursiveList('%s%s' % (path, filename))]) 56 else: 57 files.append(filename) 58 return files 59 60 def GetFromFile(self, path, binary=False): 61 """Calls |populate_function| on the contents of the file at |path|. If 62 |binary| is True then the file will be read as binary - but this will only 63 apply for the first time the file is fetched; if already cached, |binary| 64 will be ignored. 65 """ 66 version = self._file_system.Stat(path).version 67 cache_entry = self._file_object_store.Get(path).Get() 68 if (cache_entry is not None) and (version == cache_entry.version): 69 return cache_entry._cache_data 70 cache_data = self._populate_function( 71 path, 72 self._file_system.ReadSingle(path, binary=binary)) 73 self._file_object_store.Set(path, _CacheEntry(cache_data, version)) 74 return cache_data 75 76 def GetFromFileListing(self, path): 77 """Calls |populate_function| on the listing of the files at |path|. 78 Assumes that the path given is to a directory. 79 """ 80 if not path.endswith('/'): 81 path += '/' 82 version = self._file_system.Stat(path).version 83 cache_entry = self._list_object_store.Get(path).Get() 84 if (cache_entry is not None) and (version == cache_entry.version): 85 return cache_entry._cache_data 86 cache_data = self._populate_function(path, self._RecursiveList(path)) 87 self._list_object_store.Set(path, _CacheEntry(cache_data, version)) 88 return cache_data 89 90 def StatFile(self, path): 91 cache_entry = self._file_object_store.Get(path).Get() 92 if cache_entry is not None: 93 return cache_entry.version 94 return self._file_system.Stat(path).version 95 96 def StatFileListing(self, path): 97 if not path.endswith('/'): 98 path += '/' 99 cache_entry = self._list_object_store.Get(path).Get() 100 if cache_entry is not None: 101 return cache_entry.version 102 return self._file_system.Stat(path).version 103