1 # Copyright (c) 2014 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 """ResourceFinder is a helper class for finding resources given their name."""
7 from tvcm import module
8 from tvcm import style_sheet as style_sheet_module
9 from tvcm import resource as resource_module
10 from tvcm import html_module
11 from tvcm import strip_js_comments
13 class ResourceLoader(object):
14 """Manges loading modules and their dependencies from files.
16 Modules handle parsing and the construction of their individual dependency
17 pointers. The loader deals with bookkeeping of what has been loaded, and
18 mapping names to file resources.
21 def __init__(self, project):
22 self.project = project
23 self.stripped_js_by_filename = {}
24 self.loaded_modules = {}
25 self.loaded_raw_scripts = {}
26 self.loaded_style_sheets = {}
27 self.loaded_images = {}
30 def source_paths(self):
31 """A list of base directories to search for modules under."""
32 return self.project.source_paths
34 def FindResource(self, some_path):
35 """Finds a Resource for the given path.
38 some_path: A relative or absolute path to a file.
43 if os.path.isabs(some_path):
44 return self.FindResourceGivenAbsolutePath(some_path)
46 return self.FindResourceGivenRelativePath(some_path)
48 def FindResourceGivenAbsolutePath(self, absolute_path):
49 """Returns a Resource for the given absolute path."""
51 for source_path in self.source_paths:
52 if absolute_path.startswith(source_path):
53 candidate_paths.append(source_path)
54 if len(candidate_paths) == 0:
57 # Sort by length. Longest match wins.
58 candidate_paths.sort(lambda x, y: len(x) - len(y))
59 longest_candidate = candidate_paths[-1]
60 return resource_module.Resource(longest_candidate, absolute_path)
62 def FindResourceGivenRelativePath(self, relative_path):
63 """Returns a Resource for the given relative path."""
65 for script_path in self.source_paths:
66 absolute_path = os.path.join(script_path, relative_path)
67 if os.path.exists(absolute_path):
68 return resource_module.Resource(script_path, absolute_path)
72 def _FindResourceGivenNameAndSuffix(self, requested_name, extension, return_resource=False):
73 """Searches for a file and reads its contents.
76 requested_name: The name of the resource that was requested.
77 extension: The extension for this requested resource.
80 A (path, contents) pair.
82 pathy_name = requested_name.replace('.', os.sep)
83 filename = pathy_name + extension
85 resource = self.FindResourceGivenRelativePath(filename)
90 return _read_file(resource.absolute_path)
92 def FindModuleResource(self, requested_module_name):
93 """Finds a module javascript file and returns a Resource, or none."""
94 js_resource = self._FindResourceGivenNameAndSuffix(requested_module_name, '.js', return_resource=True)
95 html_resource = self._FindResourceGivenNameAndSuffix(requested_module_name, '.html', return_resource=True)
96 if js_resource and html_resource:
97 if html_module.IsHTMLResourceTheModuleGivenConflictingResourceNames(js_resource, html_resource):
104 def LoadModule(self, module_name=None, module_filename=None):
105 assert bool(module_name) ^ bool(module_filename), 'Must provide module_name or module_filename.'
107 resource = self.FindResource(module_filename)
109 raise Exception('Could not find %s in %s' % (
110 module_filename, repr(self.source_paths)))
111 module_name = resource.name
113 resource = None # Will be set if we end up needing to load.
115 if module_name in self.loaded_modules:
116 assert self.loaded_modules[module_name].contents
117 return self.loaded_modules[module_name]
119 if not resource: # happens when module_name was given
120 resource = self.FindModuleResource(module_name)
122 raise module.DepsException('No resource for module "%s"' % module_name)
124 m = html_module.HTMLModule(self, module_name, resource)
125 self.loaded_modules[module_name] = m
127 # Fake it, this is probably either polymer.js or platform.js which are
128 # actually .js files....
129 if resource.absolute_path.endswith('.js'):
136 def LoadRawScript(self, relative_raw_script_path):
138 for source_path in self.source_paths:
139 possible_absolute_path = os.path.join(source_path, relative_raw_script_path)
140 if os.path.exists(possible_absolute_path):
141 resource = resource_module.Resource(source_path, possible_absolute_path)
144 raise module.DepsException('Could not find a file for raw script %s in %s' % (
145 relative_raw_script_path, self.source_paths))
146 assert relative_raw_script_path == resource.unix_style_relative_path, \
147 'Expected %s == %s' % (relative_raw_script_path, resource.unix_style_relative_path)
149 if resource.absolute_path in self.loaded_raw_scripts:
150 return self.loaded_raw_scripts[resource.absolute_path]
152 raw_script = module.RawScript(resource)
153 self.loaded_raw_scripts[resource.absolute_path] = raw_script
156 def LoadStyleSheet(self, name):
157 if name in self.loaded_style_sheets:
158 return self.loaded_style_sheets[name]
160 resource = self._FindResourceGivenNameAndSuffix(name, '.css', return_resource=True)
162 raise module.DepsException('Could not find a file for stylesheet %s' % name)
164 style_sheet = style_sheet_module.StyleSheet(self, name, resource)
166 self.loaded_style_sheets[name] = style_sheet
169 def LoadImage(self, abs_path):
170 if abs_path in self.loaded_images:
171 return self.loaded_images[abs_path]
173 if not os.path.exists(abs_path):
174 raise module.DepsException(
175 """url('%s') did not exist""" % abs_path)
177 res = self.FindResourceGivenAbsolutePath(abs_path)
179 raise module.DepsException(
180 """url('%s') was not in search path""" % abs_path)
182 image = style_sheet_module.Image(res)
183 self.loaded_images[abs_path] = image
186 def GetStrippedJSForFilename(self, filename, early_out_if_no_tvcm):
187 if filename in self.stripped_js_by_filename:
188 return self.stripped_js_by_filename[filename]
190 with open(filename, 'r') as f:
191 contents = f.read(4096)
192 if early_out_if_no_tvcm and ('tvcm' not in contents):
195 s = strip_js_comments.StripJSComments(contents)
196 self.stripped_js_by_filename[filename] = s
200 def _read_file(absolute_path):
201 """Reads a file and returns a (path, contents) pair.
204 absolute_path: Absolute path to a file.
207 Exception: The given file doesn't exist.
208 IOError: There was a problem opening or reading the file.
210 if not os.path.exists(absolute_path):
211 raise Exception('%s not found.' % absolute_path)
212 f = open(absolute_path, 'r')
215 return absolute_path, contents