Beispiel #1
0
    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()
Beispiel #2
0
class ArrayNodeDefinition(NodeDefinition, ParentNodeDefinitionInterface):
    """This class provides a fluent interface for defining an array node.

    @author: Johannes M. Schmitt <*****@*****.**>

    """
    def __init__(self, name, parent=None):
        NodeDefinition.__init__(self, name, parent=parent);
        self._performDeepMerging = True;
        self._ignoreExtraKeys = None;
        self._children = OrderedDict();
        self._prototype = None;
        self._atLeastOne = False;
        self._allowNewKeys = True;
        self._key = None;
        self._removeKeyItem = None;
        self._addDefaults = False;
        self._addDefaultChildren = False;
        self._nodeBuilder = None;
        self._normalizeKeys = True;

        self._allowEmptyValue = True;
        self._nullEquivalent = dict();
        self._trueEquivalent = dict();

    def setBuilder(self, builder):
        """Sets a custom children builder.

        @param builder: NodeBuilder A custom NodeBuilder
        """
        assert isinstance(builder, NodeBuilder);
        self._nodeBuilder = builder;

    def children(self):
        """Returns a builder to add children nodes.

        @return: NodeBuilder
        """
        return self._getNodeBuilder();

    def prototype(self, nodeType):
        """Sets a prototype for child nodes.

        @param nodeType: string the type of node

        @return: NodeDefinition
        """
        self._prototype = \
            self._getNodeBuilder().node(None, nodeType).setParent(self);
        return self._prototype;

    def addDefaultsIfNotSet(self):
        """Adds the default value if the node is not set in the configuration.

        This method is applicable to concrete nodes only
        (not to prototype nodes). If this function has been called
        and the node is not set during the finalization phase,
        it's default value will be derived from its children default values.

        @return: ArrayNodeDefinition
        """
        self._addDefaults = True;
        return self;

    def addDefaultChildrenIfNoneSet(self, children=None):
        """Adds children with a default value when none are defined.

        This method is applicable to prototype nodes only.

        @param children: integer|string|dict|None The number of
            children|The child name|The children names to be added

        @return: ArrayNodeDefinition
        """
        self._addDefaultChildren = children;
        return self;

    def requiresAtLeastOneElement(self):
        """Requires the node to have at least one element.

        This method is applicable to prototype nodes only.

        @return: ArrayNodeDefinition
        """
        self._atLeastOne = True;
        return self;

    def disallowNewKeysInSubsequentConfigs(self):
        """Disallows adding news keys in a subsequent configuration.

        If used all keys have to be defined in the same configuration file.

        @return: ArrayNodeDefinition
        """
        self._allowNewKeys = False;
        return self;

    def fixXmlConfig(self, singular, plural=None):
        """Sets a normalization rule for XML configurations.

        @param singular: string The key to remap
        @param plural: string The plural of the key for irregular plurals

        @return: ArrayNodeDefinition
        """
        self._normalization().remap(singular, plural);
        return self;

    def useAttributeAsKey(self, name, removeKeyItem=True):
        """Sets the attribute which value is to be used as key.

        This method is applicable to prototype nodes only.

        This is useful when you have an indexed array that should be an
        associative array. You can select an item from within the array
        to be the key of the particular item. For example, if "id" is the
        "key", then:
            {
                {'id': 'my_name', 'foo': 'bar'},
            }
        becomes
            {
                my_name', {'foo': 'bar'},
            }

        If you'd like "'id' => 'my_name'" to still be present in the resulting
        array, then you can set the second argument of this method to false.

        @param name: string The name of the key
        @param removeKeyItem: Boolean Whether
            or not the key item should be removed.

        @return: ArrayNodeDefinition
        """
        self._key = name;
        self._removeKeyItem = removeKeyItem;
        return self;

    def canBeUnset(self, allow=True):
        """Sets whether the node can be unset.

        @param allow: Boolean

        @return: ArrayNodeDefinition
        """
        self._merge().allowUnset(allow);
        return self;

    def canBeEnabled(self):
        """Adds an "enabled" boolean to enable the current section.

        By default, the section is disabled.

        @return: ArrayNodeDefinition
        """
        self.treatFalseLike({'enabled': False});
        self.treatTrueLike({'enabled': True});
        self.treatNullLike({'enabled': True});
        self.children().booleanNode('enabled').defaultFalse();
        return self;

    def canBeDisabled(self):
        """Adds an "enabled" boolean to enable the current section.

        By default, the section is enabled.

        @return: ArrayNodeDefinition
        """
        self.treatFalseLike({'enabled': False});
        self.treatTrueLike({'enabled': True});
        self.treatNullLike({'enabled': True});
        self.children().booleanNode('enabled').defaultFalse();
        return self;

    def performNoDeepMerging(self):
        """Disables the deep merging of the node.

        @return: ArrayNodeDefinition
        """
        self._performDeepMerging = False;
        return self;

    def ignoreExtraKeys(self):
        """Allows extra config keys to be specified under an array without
        throwing an exception.

        Those config values are simply ignored. This should be used only
        in special cases where you want to send an entire configuration
        array through a special tree that processes only part of the array.

        @return: ArrayNodeDefinition
        """
        self._ignoreExtraKeys = True;
        return self;

    def normalizeKeys(self, boolean):
        """Sets key normalization.

        @param boolean: boolean Whether to enable key normalization

        @return: ArrayNodeDefinition
        """
        self._normalizeKeys = bool(boolean);
        return self;

    def append(self, node):
        """Appends a node definition.

        $node = ArrayNodeDefinition()
            ->children()
            ->scalarNode('foo')->end()
            ->scalarNode('baz')->end()
            ->end()
            ->append($this->getBarNodeDefinition())
        ;

        @param node: NodeDefinition A NodeDefinition instance

        @return: ArrayNodeDefinition
        """
        assert isinstance(node, NodeDefinition);
        self._children[node._name] = node.setParent(self);
        return self;

    def _getNodeBuilder(self):
        """Returns a node builder to be used to add children and prototype

        @return: NodeBuilder The node builder
        """
        if self._nodeBuilder is None:
            self._nodeBuilder = NodeBuilder();
        return self._nodeBuilder.setParent(self);

    def _createNode(self):
        if self._prototype is None:
            node = ArrayNode(self._name, self._parent);
            self._validateConcreteNode(node);

            node.setAddIfNotSet(self._addDefaults);

            for child in self._children.values():
                child._parent = node;
                node.addChild(child.getNode());
        else:
            node = PrototypedArrayNode(self._name, self._parent);

            self._validatePrototypeNode(node);

            if not self._key is None:
                node.setKeyAttribute(self._key, self._removeKeyItem);

            if self._atLeastOne:
                node.setMinNumberOfElements(1);

            if self._default:
                node.setDefaultValue(self._defaultValue);

            if not self._addDefaultChildren is False:
                node.setAddChildrenIfNoneSet(self._addDefaultChildren);
                if isinstance(self._prototype, type(self)):
                    if self._prototype._prototype is None:
                        self._prototype.addDefaultsIfNotSet();

            self._prototype._parent = node;
            node.setPrototype(self._prototype.getNode());

        node.setAllowNewKeys(self._allowNewKeys);
        node.addEquivalentValue(None, self._nullEquivalent);
        node.addEquivalentValue(True, self._trueEquivalent);
        node.addEquivalentValue(False, self._falseEquivalent);
        node.setPerformDeepMerging(self._performDeepMerging);
        node.setRequired(self._required);
        node.setIgnoreExtraKeys(self._ignoreExtraKeys);
        node.setNormalizeKeys(self._normalizeKeys);

        if not self._normalizationBuilder is None:
            node.setNormalizationClosures(self._normalizationBuilder.befores);
            node.setXmlRemappings(self._normalizationBuilder.remappings);

        if not self._mergeBuilder is None:
            node.setAllowOverwrite(self._mergeBuilder.allowOverwrite);
            node.setAllowFalse(self._mergeBuilder.allowFalse);

        if not self._validationBuilder is None:
            node.setFinalValidationClosures(self._validationBuilder.rules);

        return node;

    def _validateConcreteNode(self, node):
        """Validate the configuration of a concrete node.

        @param node: ArrayNode  The related node

        @raise InvalidDefinitionException:
        """
        assert isinstance(node, ArrayNode);
        path = node.getPath();

        if not self._key is None:
            raise InvalidDefinitionException(
                '.useAttributeAsKey() is not applicable to concrete '
                'nodes at path "{0}"'.format(path)
            );

        if self._atLeastOne:
            raise InvalidDefinitionException(
                '.requiresAtLeastOneElement() is not applicable '
                'to concrete nodes at path "{0}"'.format(path)
            );

        if self._default:
            raise InvalidDefinitionException(
                '.defaultValue() is not applicable to concrete nodes '
                'at path "{0}"'.format(path)
            );

        if not self._addDefaultChildren is False:
            raise InvalidDefinitionException(
                '.addDefaultChildrenIfNoneSet() is not applicable '
                'to concrete nodes at path "{0}"'.format(path)
            );

    def _validatePrototypeNode(self, node):
        """Validate the configuration of a prototype node.

        @param node: PrototypedArrayNode The related node

        @raise InvalidDefinitionException:
        """
        assert isinstance(node, PrototypedArrayNode);
        path = node.getPath();

        if self._addDefaults:
            raise InvalidDefinitionException(
                '.addDefaultsIfNotSet() is not applicable to prototype '
                'nodes at path "{0}"'.format(path)
            );

        if not self._addDefaultChildren is False:
            if self._default:
                raise InvalidDefinitionException(
                    'A default value and default children might not be '
                    'used together at path "{0}"'.format(path)
                );

            if not self._key is None and (
                self._addDefaultChildren is None or \
                isinstance(self._addDefaultChildren, int) and \
                self._addDefaultChildren > 0
                ):
                raise InvalidDefinitionException(
                    '.addDefaultChildrenIfNoneSet() should set default '
                    'children names as ->useAttributeAsKey() is used '
                    'at path "{0}"'.format(path)
                );

            if self._key is None and (
                isinstance(self._addDefaultChildren, String) or \
                isinstance(self._addDefaultChildren, dict)
                ):
                raise InvalidDefinitionException(
                    '->addDefaultChildrenIfNoneSet() might not set default '
                    'children names as ->useAttributeAsKey() is not used '
                    'at path "{0}"'.format(path)
                );