def enterScope(self, name): """This is called when you enter a scope @param: string name @raise RuntimeException When the parent scope is inactive @raise InvalidArgumentException When the scope does not exist @api """ if name not in self._scopes: raise InvalidArgumentException( 'The scope "{0}" does not exist.'.format(name)) if self.SCOPE_CONTAINER != self._scopes[name] and self._scopes[ name] not in self._scopedServices: raise RuntimeException( 'The parent scope "{0}" must be active when entering this ' 'scope.'.format(self._scopes[name])) # check if a scope of this name is already active, if so we need to # remove all services of this scope, and those of any of its child # scopes from the global services map if name in self._scopedServices: services = OrderedDict() services[0] = self._services services[name] = self._scopedServices[name] self._scopedServices.pop(name, None) for child in self._scopeChildren[name]: if child in self._scopedServices: services[child] = self._scopedServices[child] self._scopedServices.pop(child, None) # update global map self._services = Array.diffKey(*services.values()) services.pop(0) # add stack entry for this scope so we can restore the removed services later if name not in self._scopeStacks: self._scopeStacks[name] = list() self._scopeStacks[name].append(services) self._scopedServices[name] = dict()
class BaseNode(NodeInterface): """The base node class @author Johannes M. Schmitt <*****@*****.**> """ def __init__(self, name, parent=None): """Constructor. @param name: string The name of the node @param parent: NodeInterface The parent of this node @raise InvalidArgumentException: if the name contains a period. """ assert isinstance(name, String); if parent is not None: assert isinstance(parent, NodeInterface); self._attributes = OrderedDict(); self._name = name; self._parent = parent; self._normalizationClosures = list(); self._finalValidationClosures = list(); self._allowOverwrite = True; self._required = False; self._equivalentValues = list(); if '.' in name: raise InvalidArgumentException('The name must not contain ".".'); def setAttribute(self, key, value): self._attributes[key] = value; def hasAttribute(self, key): return key in self._attributes; def getAttribute(self, key, default=None): if self.hasAttribute(key): return self._attributes[key]; return default; def getAttributes(self, key): return self._attributes; def setAttributes(self, attributes): assert isinstance(attributes, dict); self._attributes = attributes; def removeAttribute(self, key): return self._attributes.pop(key); def setInfo(self, info): """Sets an info message. @param info: string """ self.setAttribute('info', info); def getInfo(self): """Returns info message. @return: string The info message. """ return self.getAttribute('info'); def setExample(self, example): """Sets the example configuration for this node. @param example: string|array """ self.setAttribute('example', example); def getExample(self): """Retrieves the example configuration for this node. @return: string|array """ return self.getAttribute('example'); def addEquivalentValue(self, originalValue, equivalentValue): """Adds an equivalent value. @param originalValue: mixed @param equivalentValue: mixed """ self._equivalentValues.append([originalValue, equivalentValue]); def setRequired(self, boolean): """Set this node as required. @param boolean: Boolean Required node """ self._required = bool(boolean); def setAllowOverwrite(self, allow): """Sets if this node can be overridden. @param allow: Boolean """ self._allowOverwrite = bool(allow); def setNormalizationClosures(self, closures): """Sets the closures used for normalization. @param closures: callable[] An array of Closures used for normalization """ assert isinstance(closures, list); self._normalizationClosures = closures; def setFinalValidationClosures(self, closures): """Sets the closures used for final validation. @param closures: callable[] An array of Closures used for final validation """ assert isinstance(closures, list); self._finalValidationClosures = closures; def isRequired(self): """Checks if this node is required. @return Boolean """ return self._required; def getName(self): """Returns the name of this node @return string The Node's name. """ return self._name; def getPath(self): """Retrieves the path of this node. @return string The Node's path """ path = self._name; if not self._parent is None: path = ".".join([self._parent.getPath(), self._name]); return path; @final def merge(self, leftSide, rightSide): """Merges two values together. @param leftSide: mixed @param rightSide: mixed @return mixed The merged value @raise ForbiddenOverwriteException: """ if not self._allowOverwrite: raise ForbiddenOverwriteException( 'Configuration path "{0}" cannot be overwritten. You have to ' 'define all options for this path, and any of its sub-paths ' 'in one configuration section.'.format(self.getPath()) ); self._validateType(leftSide); self._validateType(rightSide); return self._mergeValues(leftSide, rightSide); @final def normalize(self, value): """Normalizes a value, applying all normalization closures. @param value: mixed Value to normalize. @return: mixed The normalized value. """ # pre-normalize value value = self._preNormalize(value); # run custom normalization closures for closure in self._normalizationClosures: value = closure(value); # replace value with their equivalent for data in self._equivalentValues: if data[0] == value: value = data[1]; # validate type self._validateType(value); # normalize value return self._normalizeValue(value); def _preNormalize(self, value): """Normalizes the value before any other normalization is applied. @param value: @return: mixed The normalized array value """ return value; @final def finalize(self, value): """Finalizes a value, applying all finalization closures. @param mixed $value The value to finalize @return mixed The finalized value @raise InvalidConfigurationException: """ self._validateType(value); value = self._finalizeValue(value); # Perform validation on the final value if a closure has been set. # The closure is also allowed to return another value. for closure in self._finalValidationClosures: try: value = closure(value); except DefinitionException as correctEx: raise correctEx; except Exception as invalid: raise InvalidConfigurationException( 'Invalid configuration for path "{0}": {1}' ''.format(self.getPath(), str(invalid)), previous=invalid ); return value; @abstract def _validateType(self, value): """Validates the type of a Node. @param value: mixed The value to validate @raise InvalidTypeException: When the value is invalid """ pass; @abstract def _normalizeValue(self, value): """Normalizes the value. @param value: mixed The value to normalize. @return: mixed The normalized value """ pass; @abstract def _mergeValues(self, leftSide, rightSide): """Merges two values together. @param leftSide: mixed @param rightSide: mixed @return: mixed """ pass; @abstract def _finalizeValue(self, value): """Finalizes a value. @param value: The value to finalize @return: mixed The finalized value """ pass;
class BaseNode(NodeInterface): """The base node class @author Johannes M. Schmitt <*****@*****.**> """ def __init__(self, name, parent=None): """Constructor. @param name: string The name of the node @param parent: NodeInterface The parent of this node @raise InvalidArgumentException: if the name contains a period. """ assert isinstance(name, String) if parent is not None: assert isinstance(parent, NodeInterface) self._attributes = OrderedDict() self._name = name self._parent = parent self._normalizationClosures = list() self._finalValidationClosures = list() self._allowOverwrite = True self._required = False self._equivalentValues = list() if '.' in name: raise InvalidArgumentException('The name must not contain ".".') def setAttribute(self, key, value): self._attributes[key] = value def hasAttribute(self, key): return key in self._attributes def getAttribute(self, key, default=None): if self.hasAttribute(key): return self._attributes[key] return default def getAttributes(self, key): return self._attributes def setAttributes(self, attributes): assert isinstance(attributes, dict) self._attributes = attributes def removeAttribute(self, key): return self._attributes.pop(key) def setInfo(self, info): """Sets an info message. @param info: string """ self.setAttribute('info', info) def getInfo(self): """Returns info message. @return: string The info message. """ return self.getAttribute('info') def setExample(self, example): """Sets the example configuration for this node. @param example: string|array """ self.setAttribute('example', example) def getExample(self): """Retrieves the example configuration for this node. @return: string|array """ return self.getAttribute('example') def addEquivalentValue(self, originalValue, equivalentValue): """Adds an equivalent value. @param originalValue: mixed @param equivalentValue: mixed """ self._equivalentValues.append([originalValue, equivalentValue]) def setRequired(self, boolean): """Set this node as required. @param boolean: Boolean Required node """ self._required = bool(boolean) def setAllowOverwrite(self, allow): """Sets if this node can be overridden. @param allow: Boolean """ self._allowOverwrite = bool(allow) def setNormalizationClosures(self, closures): """Sets the closures used for normalization. @param closures: callable[] An array of Closures used for normalization """ assert isinstance(closures, list) self._normalizationClosures = closures def setFinalValidationClosures(self, closures): """Sets the closures used for final validation. @param closures: callable[] An array of Closures used for final validation """ assert isinstance(closures, list) self._finalValidationClosures = closures def isRequired(self): """Checks if this node is required. @return Boolean """ return self._required def getName(self): """Returns the name of this node @return string The Node's name. """ return self._name def getPath(self): """Retrieves the path of this node. @return string The Node's path """ path = self._name if not self._parent is None: path = ".".join([self._parent.getPath(), self._name]) return path @final def merge(self, leftSide, rightSide): """Merges two values together. @param leftSide: mixed @param rightSide: mixed @return mixed The merged value @raise ForbiddenOverwriteException: """ if not self._allowOverwrite: raise ForbiddenOverwriteException( 'Configuration path "{0}" cannot be overwritten. You have to ' 'define all options for this path, and any of its sub-paths ' 'in one configuration section.'.format(self.getPath())) self._validateType(leftSide) self._validateType(rightSide) return self._mergeValues(leftSide, rightSide) @final def normalize(self, value): """Normalizes a value, applying all normalization closures. @param value: mixed Value to normalize. @return: mixed The normalized value. """ # pre-normalize value value = self._preNormalize(value) # run custom normalization closures for closure in self._normalizationClosures: value = closure(value) # replace value with their equivalent for data in self._equivalentValues: if data[0] == value: value = data[1] # validate type self._validateType(value) # normalize value return self._normalizeValue(value) def _preNormalize(self, value): """Normalizes the value before any other normalization is applied. @param value: @return: mixed The normalized array value """ return value @final def finalize(self, value): """Finalizes a value, applying all finalization closures. @param mixed $value The value to finalize @return mixed The finalized value @raise InvalidConfigurationException: """ self._validateType(value) value = self._finalizeValue(value) # Perform validation on the final value if a closure has been set. # The closure is also allowed to return another value. for closure in self._finalValidationClosures: try: value = closure(value) except DefinitionException as correctEx: raise correctEx except Exception as invalid: raise InvalidConfigurationException( 'Invalid configuration for path "{0}": {1}' ''.format(self.getPath(), str(invalid)), previous=invalid) return value @abstract def _validateType(self, value): """Validates the type of a Node. @param value: mixed The value to validate @raise InvalidTypeException: When the value is invalid """ pass @abstract def _normalizeValue(self, value): """Normalizes the value. @param value: mixed The value to normalize. @return: mixed The normalized value """ pass @abstract def _mergeValues(self, leftSide, rightSide): """Merges two values together. @param leftSide: mixed @param rightSide: mixed @return: mixed """ pass @abstract def _finalizeValue(self, value): """Finalizes a value. @param value: The value to finalize @return: mixed The finalized value """ pass