| # Copyright (c) 2014 The Chromium Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| """ResourceFinder is a helper class for finding resources given their name.""" |
| |
| import os |
| from tvcm import module |
| from tvcm import style_sheet as style_sheet_module |
| from tvcm import resource as resource_module |
| from tvcm import html_module |
| from tvcm import strip_js_comments |
| |
| class ResourceLoader(object): |
| """Manges loading modules and their dependencies from files. |
| |
| Modules handle parsing and the construction of their individual dependency |
| pointers. The loader deals with bookkeeping of what has been loaded, and |
| mapping names to file resources. |
| |
| """ |
| def __init__(self, project): |
| self.project = project |
| self.stripped_js_by_filename = {} |
| self.loaded_modules = {} |
| self.loaded_raw_scripts = {} |
| self.loaded_style_sheets = {} |
| self.loaded_images = {} |
| |
| @property |
| def source_paths(self): |
| """A list of base directories to search for modules under.""" |
| return self.project.source_paths |
| |
| def FindResource(self, some_path, binary=False): |
| """Finds a Resource for the given path. |
| |
| Args: |
| some_path: A relative or absolute path to a file. |
| |
| Returns: |
| A Resource or None. |
| """ |
| if os.path.isabs(some_path): |
| return self.FindResourceGivenAbsolutePath(some_path, binary) |
| else: |
| return self.FindResourceGivenRelativePath(some_path, binary) |
| |
| def FindResourceGivenAbsolutePath(self, absolute_path, binary=False): |
| """Returns a Resource for the given absolute path.""" |
| candidate_paths = [] |
| for source_path in self.source_paths: |
| if absolute_path.startswith(source_path): |
| candidate_paths.append(source_path) |
| if len(candidate_paths) == 0: |
| return None |
| |
| # Sort by length. Longest match wins. |
| candidate_paths.sort(lambda x, y: len(x) - len(y)) |
| longest_candidate = candidate_paths[-1] |
| return resource_module.Resource(longest_candidate, absolute_path, binary) |
| |
| def FindResourceGivenRelativePath(self, relative_path, binary=False): |
| """Returns a Resource for the given relative path.""" |
| absolute_path = None |
| for script_path in self.source_paths: |
| absolute_path = os.path.join(script_path, relative_path) |
| if os.path.exists(absolute_path): |
| return resource_module.Resource(script_path, absolute_path, binary) |
| return None |
| |
| |
| def _FindResourceGivenNameAndSuffix(self, requested_name, extension, return_resource=False): |
| """Searches for a file and reads its contents. |
| |
| Args: |
| requested_name: The name of the resource that was requested. |
| extension: The extension for this requested resource. |
| |
| Returns: |
| A (path, contents) pair. |
| """ |
| pathy_name = requested_name.replace('.', os.sep) |
| filename = pathy_name + extension |
| |
| resource = self.FindResourceGivenRelativePath(filename) |
| if return_resource: |
| return resource |
| if not resource: |
| return None, None |
| return _read_file(resource.absolute_path) |
| |
| def FindModuleResource(self, requested_module_name): |
| """Finds a module javascript file and returns a Resource, or none.""" |
| js_resource = self._FindResourceGivenNameAndSuffix(requested_module_name, '.js', return_resource=True) |
| html_resource = self._FindResourceGivenNameAndSuffix(requested_module_name, '.html', return_resource=True) |
| if js_resource and html_resource: |
| if html_module.IsHTMLResourceTheModuleGivenConflictingResourceNames(js_resource, html_resource): |
| return html_resource |
| return js_resource |
| elif js_resource: |
| return js_resource |
| return html_resource |
| |
| def LoadModule(self, module_name=None, module_filename=None): |
| assert bool(module_name) ^ bool(module_filename), 'Must provide module_name or module_filename.' |
| if module_filename: |
| resource = self.FindResource(module_filename) |
| if not resource: |
| raise Exception('Could not find %s in %s' % ( |
| module_filename, repr(self.source_paths))) |
| module_name = resource.name |
| else: |
| resource = None # Will be set if we end up needing to load. |
| |
| if module_name in self.loaded_modules: |
| assert self.loaded_modules[module_name].contents |
| return self.loaded_modules[module_name] |
| |
| if not resource: # happens when module_name was given |
| resource = self.FindModuleResource(module_name) |
| if not resource: |
| raise module.DepsException('No resource for module "%s"' % module_name) |
| |
| m = html_module.HTMLModule(self, module_name, resource) |
| self.loaded_modules[module_name] = m |
| |
| # Fake it, this is probably either polymer.min.js or platform.js which are |
| # actually .js files.... |
| if resource.absolute_path.endswith('.js'): |
| return m |
| |
| m.Parse() |
| m.Load() |
| return m |
| |
| def LoadRawScript(self, relative_raw_script_path): |
| resource = None |
| for source_path in self.source_paths: |
| possible_absolute_path = os.path.join(source_path, relative_raw_script_path) |
| if os.path.exists(possible_absolute_path): |
| resource = resource_module.Resource(source_path, possible_absolute_path) |
| break |
| if not resource: |
| raise module.DepsException('Could not find a file for raw script %s in %s' % ( |
| relative_raw_script_path, self.source_paths)) |
| assert relative_raw_script_path == resource.unix_style_relative_path, \ |
| 'Expected %s == %s' % (relative_raw_script_path, resource.unix_style_relative_path) |
| |
| if resource.absolute_path in self.loaded_raw_scripts: |
| return self.loaded_raw_scripts[resource.absolute_path] |
| |
| raw_script = module.RawScript(resource) |
| self.loaded_raw_scripts[resource.absolute_path] = raw_script |
| return raw_script |
| |
| def LoadStyleSheet(self, name): |
| if name in self.loaded_style_sheets: |
| return self.loaded_style_sheets[name] |
| |
| resource = self._FindResourceGivenNameAndSuffix(name, '.css', return_resource=True) |
| if not resource: |
| raise module.DepsException('Could not find a file for stylesheet %s' % name) |
| |
| style_sheet = style_sheet_module.StyleSheet(self, name, resource) |
| style_sheet.load() |
| self.loaded_style_sheets[name] = style_sheet |
| return style_sheet |
| |
| def LoadImage(self, abs_path): |
| if abs_path in self.loaded_images: |
| return self.loaded_images[abs_path] |
| |
| if not os.path.exists(abs_path): |
| raise module.DepsException( |
| """url('%s') did not exist""" % abs_path) |
| |
| res = self.FindResourceGivenAbsolutePath(abs_path, binary=True) |
| if res == None: |
| raise module.DepsException( |
| """url('%s') was not in search path""" % abs_path) |
| |
| image = style_sheet_module.Image(res) |
| self.loaded_images[abs_path] = image |
| return image |
| |
| def GetStrippedJSForFilename(self, filename, early_out_if_no_tvcm): |
| if filename in self.stripped_js_by_filename: |
| return self.stripped_js_by_filename[filename] |
| |
| with open(filename, 'r') as f: |
| contents = f.read(4096) |
| if early_out_if_no_tvcm and ('tvcm' not in contents): |
| return None |
| |
| s = strip_js_comments.StripJSComments(contents) |
| self.stripped_js_by_filename[filename] = s |
| return s |
| |
| |
| def _read_file(absolute_path): |
| """Reads a file and returns a (path, contents) pair. |
| |
| Args: |
| absolute_path: Absolute path to a file. |
| |
| Raises: |
| Exception: The given file doesn't exist. |
| IOError: There was a problem opening or reading the file. |
| """ |
| if not os.path.exists(absolute_path): |
| raise Exception('%s not found.' % absolute_path) |
| f = codecs.open(absolute_path, mode='r', encoding='utf-8') |
| contents = f.read() |
| f.close() |
| return absolute_path, contents |