vendor/symfony/src/Symfony/Component/Config/Definition/ArrayNode.php
changeset 0 7f95f8617b0b
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/symfony/src/Symfony/Component/Config/Definition/ArrayNode.php	Sat Sep 24 15:40:41 2011 +0200
@@ -0,0 +1,348 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Config\Definition;
+
+use Symfony\Component\Config\Definition\Exception\ForbiddenOverwriteException;
+
+use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
+use Symfony\Component\Config\Definition\Exception\DuplicateKeyException;
+use Symfony\Component\Config\Definition\Exception\InvalidTypeException;
+use Symfony\Component\Config\Definition\Exception\UnsetKeyException;
+use Symfony\Component\Config\Definition\Builder\NodeDefinition;
+
+/**
+ * Represents an Array node in the config tree.
+ *
+ * @author Johannes M. Schmitt <schmittjoh@gmail.com>
+ */
+class ArrayNode extends BaseNode implements PrototypeNodeInterface
+{
+    protected $xmlRemappings;
+    protected $children;
+    protected $allowFalse;
+    protected $allowNewKeys;
+    protected $addIfNotSet;
+    protected $performDeepMerging;
+    protected $ignoreExtraKeys;
+
+    /**
+     * Constructor.
+     *
+     * @param string $name The Node's name
+     * @param NodeInterface $parent The node parent
+     */
+    public function __construct($name, NodeInterface $parent = null)
+    {
+        parent::__construct($name, $parent);
+
+        $this->children = array();
+        $this->xmlRemappings = array();
+        $this->removeKeyAttribute = true;
+        $this->allowFalse = false;
+        $this->addIfNotSet = false;
+        $this->allowNewKeys = true;
+        $this->performDeepMerging = true;
+    }
+
+    /**
+     * Sets the xml remappings that should be performed.
+     *
+     * @param array $remappings an array of the form array(array(string, string))
+     */
+    public function setXmlRemappings(array $remappings)
+    {
+        $this->xmlRemappings = $remappings;
+    }
+
+    /**
+     * Sets whether to add default values for this array if it has not been
+     * defined in any of the configuration files.
+     *
+     * @param Boolean $boolean
+     */
+    public function setAddIfNotSet($boolean)
+    {
+        $this->addIfNotSet = (Boolean) $boolean;
+    }
+
+    /**
+     * Sets whether false is allowed as value indicating that the array should
+     * be unset.
+     *
+     * @param Boolean $allow
+     */
+    public function setAllowFalse($allow)
+    {
+        $this->allowFalse = (Boolean) $allow;
+    }
+
+    /**
+     * Sets whether new keys can be defined in subsequent configurations.
+     *
+     * @param Boolean $allow
+     */
+    public function setAllowNewKeys($allow)
+    {
+        $this->allowNewKeys = (Boolean) $allow;
+    }
+
+    /**
+     * Sets if deep merging should occur.
+     *
+     * @param Boolean $boolean
+     */
+    public function setPerformDeepMerging($boolean)
+    {
+        $this->performDeepMerging = (Boolean) $boolean;
+    }
+
+    /**
+     * Whether extra keys should just be ignore without an exception.
+     *
+     * @param Boolean $boolean To allow extra keys
+     */
+    public function setIgnoreExtraKeys($boolean)
+    {
+        $this->ignoreExtraKeys = (Boolean) $boolean;
+    }
+
+    /**
+     * Sets the node Name.
+     *
+     * @param string $name The node's name
+     */
+    public function setName($name)
+    {
+        $this->name = $name;
+    }
+
+    /**
+     * Checks if the node has a default value.
+     *
+     * @return Boolean
+     */
+    public function hasDefaultValue()
+    {
+        return $this->addIfNotSet;
+    }
+
+    /**
+     * Retrieves the default value.
+     *
+     * @return array The default value
+     * @throws \RuntimeException if the node has no default value
+     */
+    public function getDefaultValue()
+    {
+        if (!$this->hasDefaultValue()) {
+            throw new \RuntimeException(sprintf('The node at path "%s" has no default value.', $this->getPath()));
+        }
+
+        $defaults = array();
+        foreach ($this->children as $name => $child) {
+            if ($child->hasDefaultValue()) {
+                $defaults[$name] = $child->getDefaultValue();
+            }
+        }
+
+        return $defaults;
+    }
+
+    /**
+     * Adds a child node.
+     *
+     * @param NodeInterface $node The child node to add
+     * @throws \InvalidArgumentException when the child node has no name
+     * @throws \InvalidArgumentException when the child node's name is not unique
+     */
+    public function addChild(NodeInterface $node)
+    {
+        $name = $node->getName();
+        if (empty($name)) {
+            throw new \InvalidArgumentException('Child nodes must be named.');
+        }
+        if (isset($this->children[$name])) {
+            throw new \InvalidArgumentException(sprintf('A child node named "%s" already exists.', $name));
+        }
+
+        $this->children[$name] = $node;
+    }
+
+    /**
+     * Finalizes the value of this node.
+     *
+     * @param mixed $value
+     * @return mixed The finalised value
+     * @throws UnsetKeyException
+     * @throws InvalidConfigurationException if the node doesn't have enough children
+     */
+    protected function finalizeValue($value)
+    {
+        if (false === $value) {
+            $msg = sprintf('Unsetting key for path "%s", value: %s', $this->getPath(), json_encode($value));
+            throw new UnsetKeyException($msg);
+        }
+
+        foreach ($this->children as $name => $child) {
+            if (!array_key_exists($name, $value)) {
+                if ($child->isRequired()) {
+                    $msg = sprintf('The child node "%s" at path "%s" must be configured.', $name, $this->getPath());
+                    $ex = new InvalidConfigurationException($msg);
+                    $ex->setPath($this->getPath());
+
+                    throw $ex;
+                }
+
+                if ($child->hasDefaultValue()) {
+                    $value[$name] = $child->getDefaultValue();
+                }
+
+                continue;
+            }
+
+            try {
+                $value[$name] = $child->finalize($value[$name]);
+            } catch (UnsetKeyException $unset) {
+                unset($value[$name]);
+            }
+        }
+
+        return $value;
+    }
+
+    /**
+     * Validates the type of the value.
+     *
+     * @param mixed $value
+     * @throws InvalidTypeException
+     */
+    protected function validateType($value)
+    {
+        if (!is_array($value) && (!$this->allowFalse || false !== $value)) {
+            $ex = new InvalidTypeException(sprintf(
+                'Invalid type for path "%s". Expected array, but got %s',
+                $this->getPath(),
+                gettype($value)
+            ));
+            $ex->setPath($this->getPath());
+
+            throw $ex;
+        }
+    }
+
+    /**
+     * Normalizes the value.
+     *
+     * @param mixed $value The value to normalize
+     * @return mixed The normalized value
+     */
+    protected function normalizeValue($value)
+    {
+        if (false === $value) {
+            return $value;
+        }
+
+        $value = $this->remapXml($value);
+
+        $normalized = array();
+        foreach ($this->children as $name => $child) {
+            if (array_key_exists($name, $value)) {
+                $normalized[$name] = $child->normalize($value[$name]);
+                unset($value[$name]);
+            }
+        }
+
+        // if extra fields are present, throw exception
+        if (count($value) && !$this->ignoreExtraKeys) {
+            $msg = sprintf('Unrecognized options "%s" under "%s"', implode(', ', array_keys($value)), $this->getPath());
+            $ex = new InvalidConfigurationException($msg);
+            $ex->setPath($this->getPath().'.'.reset($value));
+
+            throw $ex;
+        }
+
+        return $normalized;
+    }
+
+    /**
+     * Remap multiple singular values to a single plural value
+     *
+     * @param array $value The source values
+     * @return array The remapped values
+     */
+    protected function remapXml($value)
+    {
+        foreach ($this->xmlRemappings as $transformation) {
+            list($singular, $plural) = $transformation;
+
+            if (!isset($value[$singular])) {
+                continue;
+            }
+
+            $value[$plural] = Processor::normalizeConfig($value, $singular, $plural);
+            unset($value[$singular]);
+        }
+
+        return $value;
+    }
+
+    /**
+     * Merges values together.
+     *
+     * @param mixed $leftSide The left side to merge.
+     * @param mixed $rightSide The right side to merge.
+     * @return mixed The merged values
+     * @throws InvalidConfigurationException
+     * @throws \RuntimeException
+     */
+    protected function mergeValues($leftSide, $rightSide)
+    {
+        if (false === $rightSide) {
+            // if this is still false after the last config has been merged the
+            // finalization pass will take care of removing this key entirely
+            return false;
+        }
+
+        if (false === $leftSide || !$this->performDeepMerging) {
+            return $rightSide;
+        }
+
+        foreach ($rightSide as $k => $v) {
+            // no conflict
+            if (!array_key_exists($k, $leftSide)) {
+                if (!$this->allowNewKeys) {
+                    $ex = new InvalidConfigurationException(sprintf(
+                        'You are not allowed to define new elements for path "%s". '
+                       .'Please define all elements for this path in one config file. '
+                       .'If you are trying to overwrite an element, make sure you redefine it '
+                       .'with the same name.',
+                        $this->getPath()
+                    ));
+                    $ex->setPath($this->getPath());
+
+                    throw $ex;
+                }
+
+                $leftSide[$k] = $v;
+                continue;
+            }
+
+            if (!isset($this->children[$k])) {
+                throw new \RuntimeException('merge() expects a normalized config array.');
+            }
+
+            $leftSide[$k] = $this->children[$k]->merge($leftSide[$k], $v);
+        }
+
+        return $leftSide;
+    }
+}