OLD | NEW |
(Empty) | |
| 1 # Copyright 2013-2015 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 |
| 5 |
| 6 class FieldComposerError(BaseException): |
| 7 """Base error class for this module.""" |
| 8 pass |
| 9 |
| 10 |
| 11 class RegistryConflict(FieldComposerError): |
| 12 pass |
| 13 |
| 14 |
| 15 class DegenerateRegistryError(FieldComposerError): |
| 16 pass |
| 17 |
| 18 |
| 19 class CompositionUndefined(FieldComposerError): |
| 20 pass |
| 21 |
| 22 |
| 23 class FieldComposer(object): |
| 24 |
| 25 def __init__(self, fields, registered_functors): |
| 26 """Initialize the internal registry mapping names to functors.""" |
| 27 try: |
| 28 self._registry = { |
| 29 name: {'combine': value['combine']} |
| 30 for name, value in registered_functors.iteritems()} |
| 31 except KeyError: |
| 32 raise DegenerateRegistryError( |
| 33 'Registry entries must contain key "combine."') |
| 34 self._fields = fields |
| 35 |
| 36 def __contains__(self, key): |
| 37 return key in self._fields |
| 38 |
| 39 def __getitem__(self, key): |
| 40 return self._fields[key] |
| 41 |
| 42 def get(self, index, default=None): |
| 43 """Wrapper for self._fields.get.""" |
| 44 return self._fields.get(index, default) |
| 45 |
| 46 def iteritems(self): |
| 47 """Wrapper for self._fields.iteritems.""" |
| 48 return self._fields.iteritems() |
| 49 |
| 50 def compose(self, second_compositor): |
| 51 """Return the monoidal composition of two FieldComposers.""" |
| 52 # If second_compositor is a FieldComposer, registries shouldn't conflict. |
| 53 if isinstance(second_compositor, dict): |
| 54 second_compositor = FieldComposer(second_compositor, {}) |
| 55 new_registry = self._registry.copy() |
| 56 second_registry = second_compositor._registry |
| 57 for key, value in second_registry.iteritems(): |
| 58 if key in self._registry and value != self._registry[key]: |
| 59 raise RegistryConflict('Conflicting values for key %s.' % key) |
| 60 new_registry[key] = value |
| 61 |
| 62 # populate new field dictionary with composed values |
| 63 all_keys = set().union(self._fields, second_compositor._fields) |
| 64 new_fields = {} |
| 65 for name in all_keys: |
| 66 if name not in new_registry: |
| 67 raise CompositionUndefined( |
| 68 "No combine function registered for %s." % name) |
| 69 if name in second_compositor: |
| 70 new_value = self.get_with_context(name, second_compositor[name]) |
| 71 else: |
| 72 # Name is in exactly one compositor, so get that value. |
| 73 new_value = self[name] |
| 74 new_fields[name] = new_value |
| 75 |
| 76 # create and return the new compositor |
| 77 return FieldComposer(new_fields, new_registry) |
| 78 |
| 79 def get_with_context(self, key, value): |
| 80 """Combine the current value for key (if any) with value.""" |
| 81 if key not in self._registry: |
| 82 raise CompositionUndefined( |
| 83 "No combine function registered for %s." % key) |
| 84 if key in self: |
| 85 return self._registry[key]['combine'](self.get(key), value) |
| 86 return value |
OLD | NEW |