public function substitute($item, $container = null)
    {
        $result = null;

        if (is_string($item)) {
            $result = $this->findAlias($item, $container);
        } elseif (is_array($item) || ($item instanceof \Traversable && ! $item instanceof ProjectInterface)) {
            $isModified = false;
            foreach ($item as $key => $element) {
                $isModified = true;

                $element = $this->substitute($element, $container);
                if ($element !== null) {
                    $item[$key] = $element;
                }
            }
            if ($isModified) {
                $result = $item;
            }
        } elseif (is_object($item) && $item instanceof UnknownTypeDescriptor) {
            $alias  = $this->findAlias($item->getName());
            $result = $alias ?: $item;
        } elseif (is_object($item)) {
            $hash = spl_object_hash($item);
            if (isset($this->processedObjects[$hash])) {
                // if analyzed; just return
                return null;
            }

            $newContainer = ($this->isDescriptorContainer($item)) ? $item : $container;

            $this->processedObjects[$hash] = true;

            $objectClassName = get_class($item);
            $fieldNames = isset($this->substitutions[$objectClassName])
                ? $this->substitutions[$objectClassName]
                : array();

            foreach ($fieldNames as $fieldName) {
                $fieldValue = $this->findFieldValue($item, $fieldName);
                $response = $this->substitute($fieldValue, $newContainer);

                // if the returned response is not an object it must be grafted on the calling object
                if ($response !== null) {
                    $setter = 'set'.ucfirst($fieldName);
                    $item->$setter($response);
                }
            }
        }

        return $result;
    }