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