# Copyright (C) 2009, Tutorius.org # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import os import logging import copy as copy_module logger = logging.getLogger("ResourceTranslator") from .properties import * from .vault import Vault class ResourceTranslator(object): """ Handles the conversion of resource properties into file properties before action execution. This class works as a decorator to the ProbeManager class, as it is meant to be a transparent layer before sending the action to execution. An architectural note : every different type of translation should have its own method (translate_resource, etc...), and this function must be called from the translate method, under the type test. The translate_* method must take in the input property and give the output property that should replace it. """ def __init__(self, probe_manager, tutorial_id): """ Creates a new ResourceTranslator for the given tutorial. This translator is tasked with replacing resource properties of the incoming action into actually usable file properties pointing to the correct resource file. This is done by querying the vault for all the resources and creating a new file property from the returned path. @param probe_manager The probe manager to decorate @param tutorial_id The ID of the current tutorial """ self._probe_manager = probe_manager self._tutorial_id = tutorial_id self._translation_mapping = {} def translate_resource(self, res_value): """ Replace the TResourceProperty in the container by their runtime-defined file equivalent. Since the resources are stored in a relative manner in the vault and that only the Vault knows which is the current folder for the current tutorial, it needs to transform the resource identifier into the absolute path for the process to be able to use it properly. @param res_prop The resource property's value to be translated @return The TFileProperty corresponding to this resource, containing an absolute path to the resource """ # We need to replace the resource by a file representation filepath = Vault.get_resource_path(self._tutorial_id, res_value) logger.debug("ResourceTranslator :: Matching resource %s to file %s" % (res_value, filepath)) # Create the new file representation file_prop = TFileProperty(filepath) return file_prop def translate(self, prop_container): """ Applies the required translation to be able to send the container to an executing endpoint (like a Probe). For each type of property that requires it, there is translation function that will take care of mapping the property to its executable form. This function does not return anything, but its post-condition is that all the properties of the input container have been replaced by their corresponding executable versions. An example of translation is taking a resource (a relative path to a file under the tutorial folder) and transforming it into a file (a full absolute path) to be able to load it when the activity receives the action. @param prop_container The property container in which we want to replace all the resources for file properties and to recursively do so for addon and addonlist properties. """ for propname in prop_container.get_properties(): prop_value = getattr(prop_container, propname) prop_type = getattr(type(prop_container), propname).type # If the property is a resource, then we need to query the # vault to create its correspondent if prop_type == "resource": # Apply the translation file_prop = self.translate_resource(prop_value) # Set the property with the new value prop_container.replace_property(propname, file_prop) # If the property is an addon, then its value IS a # container too - we need to translate it elif prop_type == "addon": # Translate the sub properties self.translate(prop_value) # If the property is an addon list, then we need to translate all # the elements of the list elif prop_type == "addonlist": # Now, for each sub-container in the list, we will apply the # translation processing. This is done by popping the head of # the list, translating it and inserting it back at the end. for index in range(0, len(prop_value)): # Pop the head of the list container = prop_value[0] del prop_value[0] # Translate the sub-container self.translate(container) # Put the value back in the list prop_value.append(container) # Change the list contained in the addonlist property, since # we got a copy of the list when requesting it prop_container.replace_property(propname, prop_value) ### ProbeManager interface for decorator ### ## Unchanged functions ## def setCurrentActivity(self, activity_id): self._probe_manager.currentActivity = activity_id def getCurrentActivity(self): return self._probe_manager.currentActivity currentActivity = property(fget=getCurrentActivity, fset=setCurrentActivity) def attach(self, activity_id): self._probe_manager.attach(activity_id) def detach(self, activity_id): self._probe_manager.detach(activity_id) def subscribe(self, event, callback): return self._probe_manager.subscribe(event, callback) def unsubscribe(self, address): return self._probe_manager.unsubscribe(address) def register_probe(self, process_name, unique_id): self._probe_manager.register_probe(process_name, unique_id) def unregister_probe(self, unique_id): self._probe_manager.unregister_probe(unique_id) def get_registered_probes_list(self, process_name=None): return self._probe_manager.get_registered_probes_list(process_name) ## Decorated functions ## def install(self, action, callback, block=False): # Make a new copy of the action that we want to install, # because translate() changes the action and we # don't want to modify the caller's action representation new_action = copy_module.deepcopy(action) # Execute the replacement self.translate(new_action) # Send the new action to the probe manager action_address = self._probe_manager.install(new_action, callback, block) # Remember the address self._translation_mapping[action_address] = new_action return action_address def update(self, action_address, newaction, block=False): # TODO : Repair this as it currently doesn't work. # Actions are being copied, then translated in install(), so they # won't be addressable via the same object that is in the Tutorial # Runner. translated_new_action = copy_module.deepcopy(newaction) self.translate(translated_new_action) self._translation_mapping[action_address] = translated_new_action return self._probe_manager.update(action_address, translated_new_action, block) def uninstall(self, action_address, block=False): return_value = self._probe_manager.uninstall(action_address, block) if self._translation_mapping.has_key(action_address): del self._translation_mapping[action_address] return return_value