<?php
/**
 * Bean Definition.
 *
 * PHP Version 5
 *
 * @category Ding
 * @package  Bean
 * @author   Marcelo Gornstein <marcelog@gmail.com>
 * @license  http://marcelog.github.com/ Apache License 2.0
 * @link     http://marcelog.github.com/
 *
 * Copyright 2011 Marcelo Gornstein <marcelog@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
namespace Ding\Bean;

/**
 * Bean Definition.
 *
 * PHP Version 5
 *
 * @category Ding
 * @package  Bean
 * @author   Marcelo Gornstein <marcelog@gmail.com>
 * @license  http://marcelog.github.com/ Apache License 2.0
 * @link     http://marcelog.github.com/
 */
class BeanDefinition
{
    /**
     * Specifies scope prototype for beans, meaning that a new instance will
     * be returned every time.
     * @var integer
     */
    const BEAN_PROTOTYPE = 0;

    /**
     * Specifies scope singleton for beans, meaning that the same instance will
     * be returned every time.
     * @var integer
     */
    const BEAN_SINGLETON = 1;

    /**
     * Bean name
     * @var string
     */
    private $_name;

    /**
     * Bean class name.
     * @var string
     */
    private $_class;

    /**
     * Bean type (scope). See this class constants.
     * @var integer
     */
    private $_scope;

    /**
     * Properties to be di'ed to this bean.
     * @var BeanPropertyDefinition[]
     */
    private $_properties;

    /**
     * Aspects mapped to this bean.
     * @var string[]
     */
    private $_aspects;

    /**
     * Constructor arguments.
     * @var BeanConstructorArgumentDefinition[]
     */
    private $_constructorArgs;

    /**
     * Factory method name (if any).
     * @var string
     */
    private $_factoryMethod;

    /**
     * Factory bean name (if any).
     * @var string
     */
    private $_factoryBean;

    /**
     * Init method (if any).
     * @var string
     */
    private $_initMethod;

    /**
     * Destroy method (called when container is destroyed).
     * @var string
     */
    private $_destroyMethod;

    /**
     * Dependency beans literally specified in the configuration.
     * @var string[]
     */
    private $_dependsOn;

    /**
     * Methods injection.
     * @var string[]
     */
    private $_lookupMethods;

    /**
     * True if this bean cant be instantiated.
     * @var boolean
     */
    private $_isAbstract;

    /**
     * Parent bean, if any.
     * @var string
     */
    private $_parent;

    /**
     * Bean aliases.
     * @var string[]
     */
    private $_aliases;

    /**
     * Holds the name of the proxy class that was generated for this bean, only
     * valid for those who actually have aspects applied.
     * @var string
     */
    private $_proxyClassName;

    /**
     * When wiring by type, this will mark this bean definition as the primary
     * source when multiple candidates are found.
     * @var boolean;
     */
    private $_isPrimaryCandidate;

    /**
     * Mark this bean definition as primary candidate when multiple candidates
     * are found for wiring by type.
     *
	 * @return void
     */
    public function markAsPrimaryCandidate()
    {
        $this->_isPrimaryCandidate = true;
    }
    /**
     * True if this bean definition is the primary candidate when wiring by type.
     *
     * @return boolean
     */
    public function isPrimaryCandidate()
    {
        return $this->_isPrimaryCandidate;
    }

    /**
     * Aliases for this bean.
     *
     * @return string[]
     */
    public function getAliases()
    {
        return $this->_aliases;
    }

    /**
     * This is needed when creating a child bean.
     *
     * @return void
     */
    public function clearAliases()
    {
        $this->_aliases = array();
    }
    /**
     * Add an alias to this bean.
     *
     * @param string $name New alias for this bean.
     *
     * @return void
     */
    public function addAlias($name)
    {
        $this->_aliases[$name] = $name;
    }

    /**
     * Returns true if this bean definition is abstract and cant be instantiated.
     *
     * @return boolean
     */
    public function isAbstract()
    {
        return $this->_isAbstract;
    }

    /**
     * Makes this bean an abstract bean definition.
     *
     * @return void
     */
    public function makeAbstract()
    {
        $this->_isAbstract = true;
    }

    /**
     * Makes this bean concrete, that is, not abstract.
     *
     * @return void
     */
    public function makeConcrete()
    {
        $this->_isAbstract = false;
    }

    /**
     * Returns true if this bean definition is for a bean of type singleton.
     *
     * @return boolean
     */
    public function isSingleton()
    {
        return $this->_scope == BeanDefinition::BEAN_SINGLETON;
    }

    /**
     * Returns true if this bean definition is for a bean of type prototype.
     *
     * @return boolean
     */
    public function isPrototype()
    {
        return $this->_scope == BeanDefinition::BEAN_PROTOTYPE;
    }

    /**
     * Returns true if this bean has mapped aspects.
     *
     * @return boolean
     */
    public function hasAspects()
    {
        return $this->_aspects !== false;
    }

    /**
     * Sets new aspects for this bean.
     *
     * @param string[] $aspects New aspects.
     *
     * @return void
     */
    public function setAspects(array $aspects)
    {
        $this->_aspects = $aspects;
    }

    /**
     * Returns aspects for this bean.
     *
     * @return string[]
     */
    public function getAspects()
    {
        return $this->_aspects;
    }

    /**
     * Changes the scope for this bean.
     *
     * @param string $scope New scope.
     *
     * @return void
     */
    public function setScope($scope)
    {
        $this->_scope = $scope;
    }

    /**
     * Sets new method injections.
     *
     * @param string[] $methods Methods injected.
     *
     * @return void
     */
    public function setMethodInjections($methods)
    {
        $this->_lookupMethods = $methods;
    }

    /**
     * Returns the method injections.
     *
     * @return string[]
     */
    public function getMethodInjections()
    {
        return $this->_lookupMethods;
    }

    /**
     * Sets a new name for this bean.
     *
     * @param string $name New name.
     *
     * @return void
     */
    public function setName($name)
    {
        $this->_name = $name;
    }

    /**
     * Returns bean name.
     *
     * @return string
     */
    public function getName()
    {
        return $this->_name;
    }

    /**
     * Sets a new class name for this bean.
     *
     * @param string $class New class name.
     *
     * @return void
     */
    public function setClass($class)
    {
        $this->_class = $class;
    }

    /**
     * Returns bean class.
     *
     * @return string
     */
    public function getClass()
    {
        return $this->_class;
    }

    /**
     * Sets new properties for this bean.
     *
     * @param BeanPropertyDefinition[] $properties New properties.
     *
     * @return void
     */
    public function setProperties(array $properties)
    {
        $this->_properties = $properties;
    }

    /**
     * Returns properties for this bean.
     *
     * @return BeanPropertyDefinition[]
     */
    public function getProperties()
    {
        return $this->_properties;
    }

    /**
     * Sets new arguments for this bean.
     *
     * @param BeanConstructorDefinition[] $arguments New arguments.
     *
     * @return void
     */
    public function setArguments(array $arguments)
    {
        $this->_constructorArgs = $arguments;
    }

    /**
     * Returns arguments for this bean.
     *
     * @return BeanConstructorArgumentDefinition[]
     */
    public function getArguments()
    {
        return $this->_constructorArgs;
    }

    /**
     * Sets a new factory method for this bean.
     *
     * @param string $factoryMethod New factory method.
     *
     * @return void
     */
    public function setFactoryMethod($factoryMethod)
    {
        $this->_factoryMethod = $factoryMethod;
    }
    /**
     * Factory method, false if none was set.
     *
     * @return string
     */
    public function getFactoryMethod()
    {
        return $this->_factoryMethod;
    }

    /**
     * Sets a new factory bean for this bean.
     *
     * @param string $factoryBean New factory bean.
     *
     * @return void
     */
    public function setFactoryBean($factoryBean)
    {
        $this->_factoryBean = $factoryBean;
    }

    /**
     * Factory bean, false if none was set.
     *
     * @return string
     */
    public function getFactoryBean()
    {
        return $this->_factoryBean;
    }

    /**
     * Sets a new init method for this bean.
     *
     * @param string $initMethod New init method.
     *
     * @return void
     */
    public function setInitMethod($initMethod)
    {
        $this->_initMethod = $initMethod;
    }

    /**
     * Init method, false if none was set.
     *
     * @return string
     */
    public function getInitMethod()
    {
        return $this->_initMethod;
    }

    /**
     * Sets a new destroy method for this bean.
     *
     * @param string $destroyMethod New destroy method.
     *
     * @return void
     */
    public function setDestroyMethod($destroyMethod)
    {
        $this->_destroyMethod = $destroyMethod;
    }

    /**
     * Destroy method, false if none was set.
     *
     * @return string
     */
    public function getDestroyMethod()
    {
        return $this->_destroyMethod;
    }

    /**
     * Returns all beans marked as dependencies for this bean.
     *
     * @return string[]
     */
    public function getDependsOn()
    {
        return $this->_dependsOn;
    }

    /**
     * Set bean dependencies.
	 *
     * @param string[] $dependsOn Dependencies (bean names).
     *
     * @return void
     */
    public function setDependsOn(array $dependsOn)
    {
        $this->_dependsOn = $dependsOn;
    }

    /**
     * Returns a new bean definition, a copy of this one.
     *
     * @param string $childName The name for the new bean
     *
     * @return BeanDefinition
     */
    public function makeChildBean($childName)
    {
        $bean = serialize($this);
        $bean = unserialize($bean);
        $bean->setName($childName);
        $bean->clearAliases();
        $bean->makeConcrete();
        return $bean;
    }

    /**
     * Creates a new unique bean name.
     *
     * @param string $prefix A name prefix
     *
     * @return string
     */
    public static function generateName($prefix)
    {
        return $prefix . mt_rand(1, microtime(true));
    }

    /**
     * Returns true if this bean is created by calling the constructor.
     *
     * @return boolean
     */
    public function isCreatedByConstructor()
    {
        return empty($this->_factoryMethod);
    }

    /**
     * Returns true if this bean is created by calling a factory bean (as
     * opposed to a factory class)
     *
     * @return boolean
     */
    public function isCreatedWithFactoryBean()
    {
        return !empty($this->_factoryBean);
    }

    public function setProxyClassName($name)
    {
        $this->_proxyClassName = $name;
    }

    public function getProxyClassName()
    {
        return $this->_proxyClassName;
    }

    public function hasProxyClass()
    {
        return !is_null($this->_proxyClassName);
    }

    public function hasInitMethod()
    {
        return !is_null($this->_initMethod);
    }
    public function hasDestroyMethod()
    {
        return !is_null($this->_destroyMethod);
    }

    /**
     * Constructor.
     *
     * @param string $name Bean name.
     *
     * @return void
     */
    public function __construct($name)
    {
        $this->_name = $name;
        $soullessString = '';
        $soullessArray = array();
        $this->_class = 'stdclass';
        $this->_aliases = $soullessArray;
        $this->_scope = BeanDefinition::BEAN_SINGLETON;
        $this->_factoryMethod = $soullessString;
        $this->_factoryBean = $soullessString;
        $this->_initMethod = null;
        $this->_lookupMethods = $soullessArray;
        $this->_destroyMethod = null;
        $this->_dependsOn = $soullessArray;
        $this->_properties = $soullessArray;
        $this->_aspects = false;
        $this->_constructorArgs = $soullessArray;
        $this->_isAbstract = false;
        $this->_parent = null;
        $this->_proxyClassName = null;
        $this->_isPrimaryCandidate = false;
    }
}


<?php
/**
 * Internal reflection manager.
 *
 * PHP Version 5
 *
 * @category Ding
 * @package  Reflection
 * @author   Marcelo Gornstein <marcelog@gmail.com>
 * @license  http://marcelog.github.com/ Apache License 2.0
 * @link     http://marcelog.github.com/
 *
 * Copyright 2011 Marcelo Gornstein <marcelog@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
namespace Ding\Reflection;

use Ding\Annotation\Collection;
use Ding\Annotation\Parser;
use Ding\Cache\ICache;

/**
 * Internal reflection manager.
 *
 * PHP Version 5
 *
 * @category Ding
 * @package  Reflection
 * @author   Marcelo Gornstein <marcelog@gmail.com>
 * @license  http://marcelog.github.com/ Apache License 2.0
 * @link     http://marcelog.github.com/
 */
class ReflectionFactory implements IReflectionFactory
{
    /**
     * Cache reflection classes instantiated so far.
     * @var \ReflectionClass[]
     */
    private $_reflectionClasses = array();
    /**
     * A map where the key is the class name.
     * @var string[]
     */
    private $_annotatedClasses = array();
    /**
     * A map where the key is the method name.
     * @var string[]
     */
    private $_annotatedMethods = array();
    /**
     * A map where the key is the property name.
     * @var string[]
     */
    private $_annotatedProperties = array();

    /**
     * A map where the key is the annotations, and the value is an array with
     * all the classes (not their methods) with this annotation.
     * @var string[]
     */
    private $_classesAnnotated = array();

    /**
     * Reflection methods, indexed by class.
     * @var \ReflectionMethod[]
     */
    private $_reflectionMethods = array();

    /**
     * Reflection properties, indexed by class.
     * @var \ReflectionProperty[]
     */
    private $_reflectionProperties = array();

    /**
     * Wether to use annotations or not.
     * @var boolean
     */
    private $_withAnnotations = false;

    /**
     * Annotations cache.
     * @var ICache
     */
    private $_cache = false;

    /**
     * The annotation parser used to get annotations from code blocks.
     * @var Parser
     */
    private $_annotationParser;

    /**
     * To inject the annotation parser.
     *
     * @param Parser $parser
     *
     * @return void
     */
    public function setAnnotationParser(Parser $parser)
    {
        $this->_annotationParser = $parser;
    }
    /**
     * (non-PHPdoc)
     * @see Ding\Reflection.IReflectionFactory::getClassesByAnnotation()
     */
    public function getClassesByAnnotation($annotation)
    {
        if (!$this->_withAnnotations) {
            return array();
        }
        if (isset($this->_classesAnnotated[$annotation])) {
            return $this->_classesAnnotated[$annotation];
        }
        $cacheKey = $annotation . '.classbyannotations';
        $result = false;
        $classes = $this->_cache->fetch($cacheKey, $result);
        if ($result === true) {
            $this->_classesAnnotated[$annotation] = $classes[$annotation];
            return $this->_classesAnnotated[$annotation];
        }
        return array();
    }

    /**
     * (non-PHPdoc)
     * @see Ding\Reflection.IReflectionFactory::getMethodAnnotations()
     */
    public function getMethodAnnotations($class, $method)
    {
        if (!$this->_withAnnotations) {
            return array();
        }
        $key = $class . $method;
        if (isset($this->_annotatedMethods[$key])) {
            return $this->_annotatedMethods[$key];
        }
        $cacheKey = $key . '.methodannotations';
        $result = false;
        $annotations = $this->_cache->fetch($cacheKey, $result);
        if ($result === true) {
            $this->_annotatedMethods[$key] = $annotations;
            return $annotations;
        }
        $rMethod = $this->getMethod($class, $method);
        $annotations = $this->_annotationParser->parse($rMethod->getDocComment());
        $this->_cache->store($cacheKey, $annotations);
        return $annotations;
    }

    /**
     * (non-PHPdoc)
     * @see Ding\Reflection.IReflectionFactory::getPropertyAnnotations()
     */
    public function getPropertyAnnotations($class, $property)
    {
        if (!$this->_withAnnotations) {
            return array();
        }
        $key = $class . $property;
        if (isset($this->_annotatedProperties[$key])) {
            return $this->_annotatedProperties[$key];
        }
        $cacheKey = $key . '.propertyannotations';
        $result = false;
        $annotations = $this->_cache->fetch($cacheKey, $result);
        if ($result === true) {
            $this->_annotatedProperties[$key] = $annotations;
            return $annotations;
        }
        $rProperty = $this->getProperty($class, $property);
        $annotations = $this->_annotationParser->parse($rProperty->getDocComment());
        $this->_cache->store($cacheKey, $annotations);
        return $annotations;
    }

    /**
     * This one will populate the map indexed by annotations names, so we can
     * then get all classes with a particular annotation name.
     *
     * @param string $class A class name.
     * @param Collection $annotations The annotations to index.
     *
     * @return void
     */
    private function _populateClassesPerAnnotations($class, Collection $annotations)
    {
        foreach ($annotations->getAll() as $name => $annotation) {
            $cacheKey = $name . '.classbyannotations';
            if (!isset($this->_classesAnnotated[$name])) {
                $this->_classesAnnotated[$name] = array();
            }
            $this->_classesAnnotated[$name][$class] = $class;
            $this->_cache->store($cacheKey, $this->_classesAnnotated);
        }
    }
    /**
     * (non-PHPdoc)
     * @see Ding\Reflection.IReflectionFactory::getClassAnnotations()
     */
    public function getClassAnnotations($class)
    {
        if (!$this->_withAnnotations) {
            return array();
        }
        if (isset($this->_annotatedClasses[$class])) {
            return $this->_annotatedClasses[$class];
        }
        $cacheKey = $class . '.classannotations';
        $result = false;
        $annotations = $this->_cache->fetch($cacheKey, $result);
        if ($result === true) {
            $this->_annotatedClasses[$class] = $annotations;
            return $annotations;
        }
        $this->_annotatedClasses[$class] = array();
        $rClass = $this->getClass($class);
        $annotations = $this->_annotationParser->parse($rClass->getDocComment());
        $this->_populateClassesPerAnnotations($class, $annotations);
        $this->_annotatedClasses[$class] = $annotations;
        $this->_cache->store($cacheKey, $annotations);
        return $annotations;
    }

    /**
     * (non-PHPdoc)
     * @see Ding\Reflection.IReflectionFactory::getClass()
     */
    public function getClass($class)
    {
        if (isset($this->_reflectionClasses[$class])) {
            return $this->_reflectionClasses[$class];
        }
        $this->_reflectionClasses[$class] = new \ReflectionClass($class);
        return $this->_reflectionClasses[$class];
    }

    /**
     * (non-PHPdoc)
     * @see Ding\Reflection.IReflectionFactory::getMethod()
     */
    public function getMethod($class, $method)
    {
        if (isset($this->_reflectionMethods[$class][$method])) {
            return $this->_reflectionMethods[$class][$method];
        }
        if (!isset($this->_reflectionMethods[$class])) {
            $this->_reflectionMethods[$class] = array();
        }
        $this->_reflectionMethods[$class][$method] = new \ReflectionMethod($class, $method);
        return $this->_reflectionMethods[$class][$method];
    }

    /**
     * (non-PHPdoc)
     * @see Ding\Reflection.IReflectionFactory::getProperty()
     */
    public function getProperty($class, $property)
    {
        if (isset($this->_reflectionProperties[$class][$property])) {
            return $this->_reflectionProperties[$class][$property];
        }
        if (!isset($this->_reflectionProperties[$class])) {
            $this->_reflectionProperties[$class] = array();
        }
        $this->_reflectionProperties[$class][$property] = new \ReflectionProperty($class, $property);
        return $this->_reflectionProperties[$class][$property];
    }

    /**
     * (non-PHPdoc)
     * @see Ding\Reflection.IReflectionFactory::getClassAncestors()
     */
    public function getClassAncestors($class)
    {
        $ret = array();
        $rClass = $this->getClass($class);
        while($rClass = $rClass->getParentClass()) {
            $ret[] = $rClass->getName();
        }
        return $ret;
    }

    /**
     * (non-PHPdoc)
     * @see Ding\Reflection.IReflectionFactory::getClassAncestorsAndInterfaces()
     */
    public function getClassAncestorsAndInterfaces($class)
    {
        $ret = array();
        $ret = $this->getClassAncestors($class);
        $ret = array_merge($ret, $this->getClass($class)->getInterfaceNames());
        return $ret;
    }
    /**
     * To inject the annotations cache.
     *
     * @param ICache $cache
     *
     * @return void
     */
    public function setCache(ICache $cache)
    {
        $this->_cache = $cache;
    }

    public function __construct($withAnnotations)
    {
        $this->_withAnnotations = $withAnnotations;
    }

}


<?php
/**
 * XML bean factory.
 *
 * PHP Version 5
 *
 * @category   Ding
 * @package    Bean
 * @subpackage Provider
 * @author     Marcelo Gornstein <marcelog@gmail.com>
 * @license    http://marcelog.github.com/ Apache License 2.0
 * @link       http://marcelog.github.com/
 *
 * Copyright 2011 Marcelo Gornstein <marcelog@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
namespace Ding\Bean\Provider;

use Ding\Reflection\IReflectionFactory;
use Ding\Reflection\IReflectionFactoryAware;
use Ding\Logger\ILoggerAware;
use Ding\Container\IContainerAware;
use Ding\Aspect\IAspectManagerAware;
use Ding\Bean\IBeanDefinitionProvider;
use Ding\Bean\Lifecycle\IAfterConfigListener;
use Ding\Container\IContainer;
use Ding\Aspect\PointcutDefinition;
use Ding\Bean\Lifecycle\AfterConfigListener;
use Ding\Bean\Factory\Exception\BeanFactoryException;
use Ding\Bean\BeanConstructorArgumentDefinition;
use Ding\Bean\BeanDefinition;
use Ding\Bean\BeanPropertyDefinition;
use Ding\Aspect\AspectDefinition;
use Ding\Aspect\AspectManager;
use Ding\Aspect\IAspectProvider;
use Ding\Aspect\IPointcutProvider;

/**
 * XML bean factory.
 *
 * PHP Version 5
 *
 * @category   Ding
 * @package    Bean
 * @subpackage Driver
 * @author     Marcelo Gornstein <marcelog@gmail.com>
 * @license    http://marcelog.github.com/ Apache License 2.0
 * @link       http://marcelog.github.com/
 */
class Xml implements
    IAfterConfigListener, IAspectProvider, IPointcutProvider,
    IBeanDefinitionProvider, IAspectManagerAware, IContainerAware, ILoggerAware,
    IReflectionFactoryAware
{
    protected $container;
    /**
     * log4php logger or our own.
     * @var Logger
     */
    private $_logger;

    /**
     * beans.xml file path.
     * @var string
     */
    private $_filename;

    /**
     * SimpleXML object.
     * @var SimpleXML[]
     */
    private $_simpleXml;

    /**
     * Bean definition template to clone.
     * @var BeanDefinition
     */
    private $_templateBeanDef;

    /**
     * Bean property definition template to clone.
     * @var BeanPropertyDefinition
     */
    private $_templatePropDef;

    /**
     * Bean constructor argument definition template to clone.
     * @var BeanConstructorArgumentDefinition
     */
    private $_templateArgDef;

    /**
     * Aspect definition template to clone.
     * @var AspectDefinition
     */
    private $_templateAspectDef;

    /**
     * Pointcut definition template to clone.
     * @var PointcutDefinition
     */
    private $_templatePointcutDef;

    /**
     * The aspect manager.
     * @var AspectManager
     */
    private $_aspectManager = false;

    /**
     * Optional directories to search for bean files.
     * @var string[]
     */
    private $_directories = false;
    private $_knownBeansPerEvent = array();
    /**
     * @var IReflectionFactory
     */
    private $_reflectionFactory;

    /**
     * Dont serialize anything here. The container and the aspect manager have
     * references to this driver, and because of IContainerAware or IAspectManagerAware
     * and others, like the methodInjection, some beans and bean definitions will have
     * a reference to the container, so when serializing these beans/definitions, ultimately
     * this driver will be try for serialization.
     *
     * @return array
     */
    public function __sleep()
    {
        return array();
    }

    /**
     * Gets xml errors.
     *
     * @return string
     */
    private function _getXmlErrors()
    {
        $errors = '';
        foreach (libxml_get_errors() as $error) {
            $errors .= $error->message . "\n";
        }
        return $errors;
    }

    /**
     * Initializes SimpleXML Object
     *
     * @param string $filename
     *
     * @throws BeanFactoryException
     * @return SimpleXML
     */
    private function _loadXml($filename)
    {
        $xmls = array();
        libxml_use_internal_errors(true);
        if (is_array($filename)) {
            foreach ($filename as $file) {
                $result = $this->_loadXml($file);
                if ($result !== false) {
                    foreach ($result as $name => $xml) {
                        $xmls[$name] = $xml;
                    }
                }
            }
            return $xmls;
        }
        $contents = false;
        foreach ($this->_directories as $directory) {
            $fullname = $directory . DIRECTORY_SEPARATOR . $filename;
            if (!file_exists($fullname)) {
                continue;
            }
            $contents = @file_get_contents($fullname);
        }
        if ($contents === false) {
            throw new BeanFactoryException($filename . ' not found in ' . print_r($this->_directories, true));
        }
        $ret = simplexml_load_string($contents);
        if ($ret === false) {
            return $ret;
        }
        $xmls[$filename] = $ret;
        foreach ($ret->xpath("//import") as $imported) {
            $filename = (string)$imported->attributes()->resource;
            foreach ($this->_loadXml($filename) as $name => $xml) {
                $xmls[$name] = $xml;
            }
        }
        return $xmls;
    }

    /**
     * Returns an aspect definition.
     *
     * @param SimpleXML $simpleXmlAspect Aspect node.
     *
     * @throws BeanFactoryException
     * @return AspectDefinition
     */
    private function _loadAspect($simpleXmlAspect)
    {
        $aspects = array();
        $atts = $simpleXmlAspect->attributes();
        if (isset($atts->id)) {
            $name = (string)$atts->id;
        } else {
            $name = BeanDefinition::generateName('AspectXML');
        }
        if (isset($atts->expression)) {
            $expression = (string)$atts->expression;
        } else {
            $expression = '';
        }
        $aspectBean = (string)$atts->ref;
        $type = (string)$atts->type;
        if ($type == 'method') {
            $type = AspectDefinition::ASPECT_METHOD;
        } else if ($type == 'exception') {
            $type = AspectDefinition::ASPECT_EXCEPTION;
        } else {
            throw new BeanFactoryException('Invalid aspect type');
        }
        $pointcuts = array();
        foreach ($simpleXmlAspect->pointcut as $pointcut) {
            $pointcutAtts = $pointcut->attributes();
            if (isset($pointcutAtts->id)) {
                $pointcutName = (string)$pointcutAtts->id;
            } else {
                $pointcutName = BeanDefinition::generateName('PointcutXML');
            }
            if (isset($pointcutAtts->expression)) {
                $pointcut = clone $this->_templatePointcutDef;
                $pointcut->setName($pointcutName);
                $pointcut->setExpression((string)$pointcutAtts->expression);
                $pointcut->setMethod((string)$pointcutAtts->method);
                $this->_aspectManager->setPointcut($pointcut);
                $pointcuts[] = $pointcutName;
            } else if (isset($pointcutAtts->{'pointcut-ref'})) {
                $pointcuts[] = (string)$pointcutAtts->{'pointcut-ref'};
            }
        }
        $aspect = new AspectDefinition($name, $pointcuts, $type, $aspectBean, $expression);
        return $aspect;
    }

    /**
     * Returns a property definition.
     *
     * @param SimpleXML $simpleXmlProperty Property node.
     *
     * @throws BeanFactoryException
     * @return BeanPropertyDefinition
     */
    private function _loadProperty($simpleXmlProperty)
    {
        $propName = (string)$simpleXmlProperty->attributes()->name;
        if (isset($simpleXmlProperty->ref)) {
            $propType = BeanPropertyDefinition::PROPERTY_BEAN;
            $propValue = (string)$simpleXmlProperty->ref->attributes()->bean;
        } else if (isset($simpleXmlProperty->null)) {
            $propType = BeanPropertyDefinition::PROPERTY_SIMPLE;
            $propValue = null;
        } else if (isset($simpleXmlProperty->false)) {
            $propType = BeanPropertyDefinition::PROPERTY_SIMPLE;
            $propValue = false;
        } else if (isset($simpleXmlProperty->true)) {
            $propType = BeanPropertyDefinition::PROPERTY_SIMPLE;
            $propValue = true;
        } else if (isset($simpleXmlProperty->bean)) {
            $propType = BeanPropertyDefinition::PROPERTY_BEAN;
            $name = BeanDefinition::generateName('Bean');
            $simpleXmlProperty->bean->addAttribute('id', $name);
            $propValue = $name;
        } else if (isset($simpleXmlProperty->array)) {
            $propType = BeanPropertyDefinition::PROPERTY_ARRAY;
            $propValue = array();
            foreach ($simpleXmlProperty->array->entry as $arrayEntry) {
                if (isset($arrayEntry->attributes()->key)) {
                    $key = (string)$arrayEntry->attributes()->key;
                    $propValue[$key] = $this->_loadProperty($arrayEntry);
                } else {
                    $propValue[] = $this->_loadProperty($arrayEntry);
                }
            }
        } else if (isset($simpleXmlProperty->eval)) {
            $propType = BeanPropertyDefinition::PROPERTY_CODE;
            $propValue = (string)$simpleXmlProperty->eval;
        } else {
            $propType = BeanPropertyDefinition::PROPERTY_SIMPLE;
            $propValue = (string)$simpleXmlProperty->value;
        }
        return new BeanPropertyDefinition($propName, $propType, $propValue);
    }

    /**
     * Returns a constructor argument definition.
     *
     * @param SimpleXML $simpleXmlArg Argument node.
     *
     * @throws BeanFactoryException
     * @return BeanConstructorArgumentDefinition
     */
    private function _loadConstructorArg($simpleXmlArg)
    {
        if (isset($simpleXmlArg->ref)) {
            $argType = BeanConstructorArgumentDefinition::BEAN_CONSTRUCTOR_BEAN;
            $argValue = (string)$simpleXmlArg->ref->attributes()->bean;
        } else if (isset($simpleXmlArg->bean)) {
            $argType = BeanConstructorArgumentDefinition::BEAN_CONSTRUCTOR_BEAN;
            $name = BeanDefinition::generateName('Bean');
            $argValue = $name;
            $simpleXmlArg->bean->addAttribute('id', $name);
        } else if (isset($simpleXmlArg->null)) {
            $argType = BeanConstructorArgumentDefinition::BEAN_CONSTRUCTOR_VALUE;
            $argValue = null;
        } else if (isset($simpleXmlArg->false)) {
            $argType = BeanConstructorArgumentDefinition::BEAN_CONSTRUCTOR_VALUE;
            $argValue = false;
        } else if (isset($simpleXmlArg->true)) {
            $argType = BeanConstructorArgumentDefinition::BEAN_CONSTRUCTOR_VALUE;
            $argValue = true;
        } else if (isset($simpleXmlArg->array)) {
            $argType = BeanConstructorArgumentDefinition::BEAN_CONSTRUCTOR_ARRAY;
            $argValue = array();
            foreach ($simpleXmlArg->array->entry as $arrayEntry) {
                $key = (string)$arrayEntry->attributes()->key;
                $argValue[$key] = $this->_loadConstructorArg($arrayEntry);
            }
        } else if (isset($simpleXmlArg->eval)) {
            $argType = BeanConstructorArgumentDefinition::BEAN_CONSTRUCTOR_CODE;
            $argValue = (string)$simpleXmlArg->eval;
        } else {
            $argType = BeanConstructorArgumentDefinition::BEAN_CONSTRUCTOR_VALUE;
            $argValue = (string)$simpleXmlArg->value;
        }
        if (isset($simpleXmlArg->attributes()->name)) {
            $argName = (string)$simpleXmlArg->attributes()->name;
        } else {
            $argName = false;
        }
        return new BeanConstructorArgumentDefinition($argType, $argValue, $argName);
    }

    /**
     * Initialize SimpleXML.
     *
     * @throws BeanFactoryException
     * @return void
     */
    private function _load()
    {
        if ($this->_simpleXml !== false) {
            return;
        }
        $this->_simpleXml = $this->_loadXml($this->_filename);
        if (empty($this->_simpleXml)) {
            throw new BeanFactoryException(
                'Could not parse: ' . $this->_filename
                . ': ' . $this->_getXmlErrors()
            );
        }
    }

    /**
     * (non-PHPdoc)
     * @see Ding\Bean.IBeanDefinitionProvider::getBeansListeningOn()
     */
    public function getBeansListeningOn($eventName)
    {
        if (isset($this->_knownBeansPerEvent[$eventName])) {
            return $this->_knownBeansPerEvent[$eventName];
        }
        return array();
    }

    private function _addBeanToKnownByClass($class, $name)
    {
        if (strpos($class, "\${") !== false) {
            return;
        }
        if (!isset($this->_knownBeansByClass[$class])) {
            $this->_knownBeansByClass[$class] = array();
        }
        $this->_knownBeansByClass[$class][] = $name;
        // Load any parent classes
        $rClass = $this->_reflectionFactory->getClass($class);
        $parentClass = $rClass->getParentClass();
        while ($parentClass) {
            $parentClassName = $parentClass->getName();
            $this->_knownBeansByClass[$parentClassName][] = $name;
            $parentClass = $parentClass->getParentClass();
        }

        // Load any interfaces
        foreach ($rClass->getInterfaces() as $interfaceName => $rInterface) {
            $this->_knownBeansByClass[$interfaceName][] = $name;
        }
    }
    /**
     * (non-PHPdoc)
     * @see Ding\Bean\Lifecycle.IAfterConfigListener::afterConfig()
     */
    public function afterConfig()
    {
        $this->_load();
        foreach($this->_simpleXml as $xmlName => $xml) {
            $simpleXmlBeans = $xml->xpath("/beans/bean");
            if (!empty($simpleXmlBeans)) {
                foreach ($simpleXmlBeans as $bean) {
                    // Skip anonymous beans
                    if (isset($bean->attributes()->class) && isset($bean->attributes()->id)) {
                        $class = (string)$bean->attributes()->class;
                        $name = (string)$bean->attributes()->id;
                        if (isset($bean->attributes()->{'factory-method'})) {
                            // Skip beans that specify class as their factory class
                            if (isset($bean->attributes()->{'factory-bean'})) {
                                $this->_addBeanToKnownByClass($class, $name);
                            }
                        } else {
                            $this->_addBeanToKnownByClass($class, $name);
                        }
                    }
                    if (isset($bean->attributes()->{'listens-on'})) {
                        $events = (string)$bean->attributes()->{'listens-on'};
                        $beanName = (string)$bean->attributes()->id;
                        foreach (explode(',', $events) as $eventName) {
                            $eventName = trim($eventName);
                            if (!isset($this->_knownBeansPerEvent[$eventName])) {
                                $this->_knownBeansPerEvent[$eventName] = array();
                            }
                            $this->_knownBeansPerEvent[$eventName][] = $beanName;
                        }
                    }
                }
            }
        }
    }
    /**
     * (non-PHPdoc)
     * @see Ding\Aspect.IAspectProvider::getAspects()
     */
    public function getAspects()
    {
        $aspects = array();
        $this->_load();
        foreach($this->_simpleXml as $xmlName => $xml) {
            $simpleXmlAspect = $xml->xpath("/beans/aspect");
            if (!empty($simpleXmlAspect)) {
                foreach ($simpleXmlAspect as $aspect) {
                    $aspects[] = $this->_loadAspect($aspect);
                }
            }
        }
        return $aspects;
    }

    /**
     * (non-PHPdoc)
     * @see Ding\Aspect.IPointcutProvider::getPointcut()
     */
    public function getPointcut($name)
    {
        foreach($this->_simpleXml as $xmlName => $xml) {
            $simpleXmlPointcut = $xml->xpath("//pointcut[@id='$name']");
            if (!empty($simpleXmlPointcut)) {
                $simpleXmlPointcut = $simpleXmlPointcut[0];
                $pointcutAtts = $simpleXmlPointcut->attributes();
                $pointcutName = (string)$pointcutAtts->id;
                $pointcut = clone $this->_templatePointcutDef;
                $pointcut->setName($pointcutName);
                $pointcut->setExpression((string)$pointcutAtts->expression);
                $pointcut->setMethod((string)$pointcutAtts->method);
                return $pointcut;
            }
        }
        return false;
    }

    /**
     * (non-PHPdoc)
     * @see Ding\Aspect.IBeanDefinitionProvider::getBeanDefinition()
     */
    public function getBeanDefinition($beanName)
    {
        $simpleXmlBean = false;
        foreach($this->_simpleXml as $name => $xml) {
            $simpleXmlBean = $xml->xpath("//bean[@id='$beanName']");
            if (empty($simpleXmlBean)) {
                $simpleXmlBean = $xml->xpath("//bean[contains(@name, '$beanName')]");
                if (!empty($simpleXmlBean)) {
                    $name = (string)$simpleXmlBean[0]->attributes()->id;
                    return $this->getBeanDefinition($name);
                }
                $simpleXmlBean = $xml->xpath("//alias[@alias='$beanName']");
                if (!empty($simpleXmlBean)) {
                    $name = (string)$simpleXmlBean[0]->attributes()->name;
                    return $this->getBeanDefinition($name);
                }
            } else {
                break;
            }
        }
        if (empty($simpleXmlBean)) {
            return null;
        }
        // asume valid xml (only one bean with that id)
        $simpleXmlBean = $simpleXmlBean[0];
        $bMethods = $bProps = $bAspects = $constructorArgs = array();
        if (isset($simpleXmlBean->attributes()->parent)) {
            $value = (string)$simpleXmlBean->attributes()->parent;
            $bean = $this->container->getBeanDefinition($value);
            $bean = $bean->makeChildBean($beanName);
            $bProps = $bean->getProperties();
            $constructorArgs = $bean->getArguments();
            $bAspects = $bean->getAspects();
            $bMethods = $bean->getMethodInjections();
        } else {
            $bean = clone $this->_templateBeanDef;
        }
        $bean->setName($beanName);
        if (isset($simpleXmlBean->attributes()->class)) {
            $bean->setClass((string)$simpleXmlBean->attributes()->class);
        }
        if (isset($simpleXmlBean->attributes()->scope)) {
            $bScope = (string)$simpleXmlBean->attributes()->scope;
            if ($bScope == 'prototype') {
                $bean->setScope(BeanDefinition::BEAN_PROTOTYPE);
            } else if ($bScope == 'singleton') {
                $bean->setScope(BeanDefinition::BEAN_SINGLETON);
            } else {
                throw new BeanFactoryException('Invalid bean scope: ' . $bScope);
            }
        }
        if (isset($simpleXmlBean->attributes()->{'factory-method'})) {
            $bean->setFactoryMethod(
                (string)$simpleXmlBean->attributes()->{'factory-method'}
            );
        }

        if (isset($simpleXmlBean->attributes()->name)) {
            $aliases = (string)$simpleXmlBean->attributes()->name;
            $aliases = explode(',', $aliases);
            foreach ($aliases as $alias) {
                $bean->addAlias(trim($alias));
            }
        }
        if (isset($simpleXmlBean->attributes()->primary)) {
            $primary = (string)$simpleXmlBean->attributes()->primary;
            if ($primary == 'true') {
                $bean->markAsPrimaryCandidate();
            }
        }
        if (isset($simpleXmlBean->attributes()->{'depends-on'})) {
            $bean->setDependsOn(explode(
            	',',
                (string)$simpleXmlBean->attributes()->{'depends-on'}
            ));
        }
        if (isset($simpleXmlBean->attributes()->abstract)) {
            $value = (string)$simpleXmlBean->attributes()->abstract;
            if ($value == 'true') {
                $bean->makeAbstract();
            }
        }
        if (isset($simpleXmlBean->attributes()->{'factory-bean'})) {
            $bean->setFactoryBean(
                (string)$simpleXmlBean->attributes()->{'factory-bean'}
            );
        }
        if (isset($simpleXmlBean->attributes()->{'init-method'})) {
            $bean->setInitMethod(
                (string)$simpleXmlBean->attributes()->{'init-method'}
            );
        }
        if (isset($simpleXmlBean->attributes()->{'destroy-method'})) {
            $bean->setDestroyMethod(
                (string)$simpleXmlBean->attributes()->{'destroy-method'}
            );
        }
        foreach ($simpleXmlBean->property as $property) {
            $bProp = $this->_loadProperty($property);
            $bProps[$bProp->getName()] = $bProp;
        }
        foreach ($simpleXmlBean->aspect as $aspect) {
            $aspectDefinition = $this->_loadAspect($aspect);
            $bAspects[] = $aspectDefinition;
        }
        foreach ($simpleXmlBean->{'constructor-arg'} as $arg) {
            $constructorArgs[] = $this->_loadConstructorArg($arg);
        }
        foreach ($simpleXmlBean->{'lookup-method'} as $method) {
            $atts = $method->attributes();
            $bMethods[] = array((string)$atts->name, (string)$atts->bean);
        }
        if (!empty($bProps)) {
            $bean->setProperties($bProps);
        }
        if (!empty($bAspects)) {
            $bean->setAspects($bAspects);
        }
        if (!empty($constructorArgs)) {
            $bean->setArguments($constructorArgs);
        }
        if (!empty($bMethods)) {
            $bean->setMethodInjections($bMethods);
        }
        return $bean;
    }

    /**
     * (non-PHPdoc)
     * @see Ding\Aspect.IBeanDefinitionProvider::getBeanDefinitionByClass()
     */
    public function getBeansByClass($class)
    {
        if (isset($this->_knownBeansByClass[$class])) {
            return $this->_knownBeansByClass[$class];
        }
        return array();
    }

    /**
     * (non-PHPdoc)
     * @see Ding\Aspect.IAspectManagerAware::setAspectManager()
     */
    public function setAspectManager(AspectManager $aspectManager)
    {
        $this->_aspectManager = $aspectManager;
    }

    /**
     * (non-PHPdoc)
     * @see Ding\Container.IContainerAware::setContainer()
     */
    public function setContainer(IContainer $container)
    {
        $this->container = $container;
    }

    public function setLogger(\Logger $logger)
    {
        $this->_logger = $logger;
    }

    /**
     * (non-PHPdoc)
     * @see Ding\Reflection.IReflectionFactoryAware::setReflectionFactory()
     */
    public function setReflectionFactory(IReflectionFactory $reflectionFactory)
    {
        $this->_reflectionFactory = $reflectionFactory;
    }

    /**
     * Constructor.
     *
     * @param array                      $options
     * @param \Ding\Aspect\AspectManager $aspectManager
     *
     * @return void
     */
    public function __construct(array $options)
    {
        $this->_beanDefs = array();
        $this->_filename = $options['filename'];
        $this->_simpleXml = false;
        $this->_directories
            = isset($options['directories'])
            ? $options['directories']
            : array('.');
        $this->_templateBeanDef = new BeanDefinition('');
        $this->_templatePropDef = new BeanPropertyDefinition('', 0, null);
        $this->_templateArgDef = new BeanConstructorArgumentDefinition(0, null);
        $this->_templateAspectDef = new AspectDefinition('', '', 0, '', '');
        $this->_templatePointcutDef = new PointcutDefinition('', '', '');
    }
}


<?php
/**
 * This driver will inject the MessageSource to the beans that implement
 * IMessageSourceAware
 *
 * PHP Version 5
 *
 * @category   Ding
 * @package    Bean
 * @subpackage Factory.Driver
 * @author     Marcelo Gornstein <marcelog@gmail.com>
 * @license    http://marcelog.github.com/ Apache License 2.0
 * @link       http://marcelog.github.com/
 *
 * Copyright 2011 Marcelo Gornstein <marcelog@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
namespace Ding\Bean\Factory\Driver;

use Ding\Reflection\IReflectionFactoryAware;
use Ding\Reflection\IReflectionFactory;
use Ding\Bean\BeanDefinition;
use Ding\Bean\Lifecycle\IAfterCreateListener;
use Ding\Bean\Lifecycle\IAfterConfigListener;
use Ding\Container\IContainerAware;
use Ding\Container\IContainer;

/**
 * This driver will inject the MessageSource to the beans that implement
 * IMessageSourceAware
 *
 * PHP Version 5
 *
 * @category   Ding
 * @package    Bean
 * @subpackage Factory.Driver
 * @author     Marcelo Gornstein <marcelog@gmail.com>
 * @license    http://marcelog.github.com/ Apache License 2.0
 * @link       http://marcelog.github.com/
 */
class MessageSourceDriver
    implements IAfterConfigListener, IAfterCreateListener,
    IContainerAware, IReflectionFactoryAware
{
    /**
     * Container.
     * @var IContainer
     */
    private $_container;
    /**
     * A ReflectionFactory implementation.
     * @var IReflectionFactory
     */
    protected $reflectionFactory;

    /**
     * (non-PHPdoc)
     * @see Ding\Reflection.IReflectionFactoryAware::setReflectionFactory()
     */
    public function setReflectionFactory(IReflectionFactory $reflectionFactory)
    {
        $this->reflectionFactory = $reflectionFactory;
    }
    /**
     * (non-PHPdoc)
     * @see Ding\Container.IContainerAware::setContainer()
     */
    public function setContainer(IContainer $container)
    {
        $this->_container = $container;
    }
    /**
     * (non-PHPdoc)
     * @see Ding\Bean\Lifecycle.ILifecycleListener::afterConfig()
     */
    public function afterConfig()
    {
        try
        {
            $bean = $this->_container->getBean('messageSource');
            $this->_container->setMessageSource($bean);
        } catch(\Exception $e) {
        }
    }

    public function afterCreate($bean, BeanDefinition $beanDefinition)
    {
        $rClass = $this->reflectionFactory->getClass(get_class($bean));
        if ($rClass->implementsInterface('Ding\MessageSource\IMessageSourceAware')) {
            $bean->setMessageSource($this->_container);
        }
        return $bean;
    }
}


<?php
/**
 * This driver will search for @Required setter methods.
 *
 * PHP Version 5
 *
 * @category   Ding
 * @package    Bean
 * @subpackage Factory.Driver
 * @author     Marcelo Gornstein <marcelog@gmail.com>
 * @license    http://marcelog.github.com/ Apache License 2.0
 * @link       http://marcelog.github.com/
 *
 * Copyright 2011 Marcelo Gornstein <marcelog@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
namespace Ding\Bean\Factory\Driver;

use Ding\Reflection\IReflectionFactoryAware;
use Ding\Bean\Factory\Exception\BeanFactoryException;
use Ding\Bean\BeanPropertyDefinition;
use Ding\Bean\Lifecycle\IAfterDefinitionListener;
use Ding\Bean\BeanDefinition;
use Ding\Reflection\IReflectionFactory;

/**
 * This driver will search for @Required setter methods.
 *
 * PHP Version 5
 *
 * @category   Ding
 * @package    Bean
 * @subpackage Factory.Driver
 * @author     Marcelo Gornstein <marcelog@gmail.com>
 * @license    http://marcelog.github.com/ Apache License 2.0
 * @link       http://marcelog.github.com/
 */
class AnnotationRequiredDriver implements IAfterDefinitionListener, IReflectionFactoryAware
{
    /**
     * A ReflectionFactory implementation.
     * @var IReflectionFactory
     */
    protected $reflectionFactory;

    /**
     * (non-PHPdoc)
     * @see Ding\Reflection.IReflectionFactoryAware::setReflectionFactory()
     */
    public function setReflectionFactory(IReflectionFactory $reflectionFactory)
    {
        $this->reflectionFactory = $reflectionFactory;
    }
    /**
     * (non-PHPdoc)
     * @see Ding\Bean\Lifecycle.IAfterDefinitionListener::afterDefinition()
     */
    public function afterDefinition(BeanDefinition $bean)
    {
        $class = $bean->getClass();
        $rClass = $this->reflectionFactory->getClass($class);
        $annotations = $this->reflectionFactory->getClassAnnotations($class);
        $props = $bean->getProperties();
        foreach ($rClass->getMethods() as $rMethod) {
            $methodName = $rMethod->getName();
            if (strpos($methodName, 'set') !== 0) {
                continue;
            }
            $annotations = $this->reflectionFactory->getMethodAnnotations($class, $methodName);
            if (!$annotations->contains('required')) {
                continue;
            }
            $propName = lcfirst(substr($methodName, 3));
            if (!isset($props[$propName])) {
                throw new BeanFactoryException('Missing @Required property: ' . $methodName);
            }
        }
        return $bean;
    }
}


<?php
/**
 * Tcp Server helper. You need to declare(ticks) in your own source code or
 * manually call process() in an infinite loop from your software.
 *
 * PHP Version 5
 *
 * @category   Ding
 * @package    Helpers
 * @subpackage Tcp
 * @author     Marcelo Gornstein <marcelog@gmail.com>
 * @license    http://marcelog.github.com/ Apache License 2.0
 * @link       http://marcelog.github.com/
 *
 * Copyright 2011 Marcelo Gornstein <marcelog@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
namespace Ding\Helpers\Tcp;

use Ding\Helpers\Tcp\Exception\TcpException;

/**
 * Tcp Server helper. You need to declare(ticks) in your own source code or
 * manually call process() in an infinite loop from your software.
 *
 * PHP Version 5
 *
 * @category   Ding
 * @package    Helpers
 * @subpackage Tcp
 * @author     Marcelo Gornstein <marcelog@gmail.com>
 * @license    http://marcelog.github.com/ Apache License 2.0
 * @link       http://marcelog.github.com/
 */
class TcpServerHelper
{
    /**
     * Target port
     * @var integer
     */
    private $_port;

    /**
     * Target host or ip address.
     * @var string
     */
    private $_address;

    /**
     * Socket resource.
     * @var socket
     */
    private $_socket;

    /**
     * Handler for this connection (your class).
     * @var ITcpClientHandler
     */
    private $_handler;

    /**
     * Minimum needed bytes in the socket before calling data() on the handler.
     * @var integer
     */
    private $_readLen;

    /**
     * Read timeout in milliseconds.
     * @var integer
     */
    private $_rTo;

    /**
     * Maximum number of connections to handle at any given time.
     * @var integer
     */
    private $_backlog;

    /**
     * Internal flag in order to know if the socket is connected.
     * @var boolean
     */
    private $_open;

    /**
     * Holds peers sockets.
     * @var socket[]
     */
    private $_peersSockets;
    /**
     * Holds peers.
     * @var \Ding\Helpers\Tcp\TcpPeer[]
     */
    private $_peers;

    /**
     * Holds last time for received data for each peer.
     * @var float[]
     */
    private $_peersLastDataReceived;

    /**
     * Wether to reuse or not the binding of the socket.
     * @var boolean
     */
    private $_reuse;

    /**
     * Call this to close the server.
     *
     * @return void
     */
    public function close()
    {
        $this->_open = false;
        $this->_peers = array();
        $this->_peersSockets = array();
        $this->_peersLastDataReceived = array();
        $this->_handler->close();
        socket_close($this->_socket);
        $this->_socket = false;
    }

    /**
     * Call this to bind the socket and start listening for connections.
     * Will also set the socket non blocking.
     *
     * @throws TcpException
     * @return void
     */
    public function open()
    {
        $this->_open = false;
        $this->_handler->beforeOpen();
        $this->_socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
        if ($this->_socket === false) {
            throw new TcpException(
            	'Error opening socket: ' . socket_strerror(socket_last_error())
            );
        }
        if ($this->_reuse) {
            socket_set_option ($this->_socket, SOL_SOCKET, SO_REUSEADDR, 1);
        }
        if (!@socket_bind($this->_socket, $this->_address, $this->_port)) {
            $error = socket_strerror(socket_last_error($this->_socket));
            socket_close($this->_socket);
            $this->_socket = false;
            throw new TcpException('Error binding socket: ' . $error);
        }
        socket_set_nonblock($this->_socket);
        $this->_handler->beforeListen();
        if (!socket_listen($this->_socket, $this->_backlog)) {
            $error = socket_strerror(socket_last_error($this->_socket));
            socket_close($this->_socket);
            $this->_socket = false;
            throw new TcpException('Error listening socket: ' . $error);
        }
        $this->_open = true;
    }

    /**
     * From php examples. Returns time including millseconds.
     *
     * @todo duplicated code, where can this go?
     * @return float
     */
    protected function getMicrotime()
    {
        list($usec, $sec) = explode(" ", microtime());
        return ((float)$usec + (float)$sec);
    }

    /**
     * After disconnecting a peer, this will release all the associated
     * information.
     *
     * @return void
     */
    private function _freePeer(\Ding\Helpers\Tcp\TcpPeer $peer)
    {
        $peerName = $peer->getName();
        unset($this->_peers[$peerName]);
        unset($this->_peersSockets[$peerName]);
        unset($this->_peersLastDataReceived[$peerName]);
    }
    /**
     * This will process activity in all peers. Called from process().
     *
     * @return void
     */
    public function processPeers()
    {
        // Control peers.
        if (count($this->_peers) < 1) {
            return;
        }
        $now = $this->getMicrotime();
        foreach ($this->_peers as $peerName => $peer) {
            if (!$peer->hasActivity()) {
                continue;
            }
            $buffer = '';
            $len = 1;
            $len = $peer->read($buffer, $len, true);
            if ($len > 0) {
                if ($len >= $this->_readLen) {
                    $this->_handler->handleData($peer);
                    $this->_peersLastDataReceived[$peerName] = $now;
                }
            } else {
                $peer->disconnect();
                $this->_freePeer($peer);
                $this->_handler->disconnect($peer);
            }
        }
        foreach ($this->_peers as $peerName => $peer) {
            $peerTime = $this->_peersLastDataReceived[$peerName];
            if (($now - $peerTime) > $this->_rTo) {
                if ($this->_rTo > 0) {
                    $peer->disconnect();
                    $this->_freePeer($peer);
                    $this->_handler->readTimeout($peer);
                }
                $this->_peersLastDataReceived[$peerName] = $now;
            }
        }
    }
    /**
     * Main reading loop. Call this in your own infinite loop or declare(ticks)
     * in your software. This routine will call your server handler when there
     * is data available to read, new connections, or timeouts. Will always
     * detect when the other side closed the connection.
     *
     * @return void
     */
    public function process()
    {
        if ($this->_socket === false || !$this->_open) {
            return;
        }
        // Control server.
        $read = array($this->_socket);
        $write = null;
        $ex = null;
        $result = @socket_select($read, $write, $ex, 0, 1);
        if ($result === false) {
            throw new TcpException(
            	'Error selecting from socket: '
                . socket_strerror(socket_last_error($this->_socket))
            );
        }
        if ($result > 0) {
            if (in_array($this->_socket, $read)) {
                $newSocket = socket_accept($this->_socket);
                if ($newSocket !== false) {
                    $address = '';
                    $port = 0;
                    socket_getpeername($newSocket, $address, $port);
                    $peerObject = new \Ding\Helpers\Tcp\TcpPeer($address, $port, $newSocket);
                    $peername = $peerObject->getName();
                    $this->_peers[$peername] = $peerObject;
                    $this->_peersSockets[$peername] = $newSocket;
                    $this->_peersLastDataReceived[$peername] = $this->getMicrotime();
                    $this->_handler->handleConnection($peerObject);
                }
            }
        }
        $this->processPeers();
    }

    /**
     * Minimum needed bytes available in the socket before calling data() on the
     * server handler.
     *
     * @param integer $rLen Minimum data needed in socket.
     *
     * @return void
     */
    public function setReadMinLength($rLen)
    {
        $this->_readLen = intval($rLen);
    }

    /**
     * Sets the read timeout in milliseconds. 0 to disable.
     *
     * @param integer $rTo Read timeout.
     *
     * @return void
     */
    public function setReadTimeout($rTo)
    {
        $this->_rTo = (float)($rTo / 1000);
    }

    /**
     * Sets maximum number of connections.
     *
     * @param integer $backlog Max number of connections to handle.
     *
     * @return void
     */
    public function setBacklog($backlog)
    {
        $this->_backlog = intval($backlog);
    }

    /**
     * Sets the tcp server handler.
     *
     * @param ITcpServerHandler $handler Server handler to use for callbacks.
     *
     * @return void
     */
    public function setHandler(ITcpServerHandler $handler)
    {
        $this->_handler = $handler;
    }

    /**
     * Sets server port.
     *
     * @param integer $port Server port.
     *
     * @return void
     */
    public function setPort($port)
    {
        $this->_port = $port;
    }

    /**
     * Sets server host or ip address.
     *
     * @param string $address Server host or ip address.
     *
     * @return void
     */
    public function setAddress($address)
    {
        $this->_address = $address;
    }

    /**
     * Sets wether to reuse or not the socket bind.
     *
     * @param boolean $reuse True to reuse the binding address.
     *
     * @return void
     */
    public function setReuse($reuse)
    {
        $this->_reuse = $reuse;
    }

    /**
     * Constructor. Not much to see here. Will register a tick function(),
     * process().
     *
     * @return void
     */
    public function __construct()
    {
        $this->_handler = false;
        $this->_socket = false;
        $this->_address = false;
        $this->_port = false;
        $this->_backlog = 0;
        $this->_rTo = 0;
        $this->_rLen = 1;
        $this->_connected = false;
        $this->_peers = array();
        $this->_peersSockets = array();
        $this->_peersLastDataReceived = array();
        $this->_reuse = false;
        register_tick_function(array($this, 'process'));
    }
}


<?php
/**
 * So... php does not have such a thing.. and here's what it needs to be done
 * to have a proxy class and any kind of "dynamic class".
 *
 * PHP Version 5
 *
 * @category Ding
 * @package  Aspect
 * @author   Marcelo Gornstein <marcelog@gmail.com>
 * @license  http://marcelog.github.com/ Apache License 2.0
 * @link     http://marcelog.github.com/
 *
 * Copyright 2011 Marcelo Gornstein <marcelog@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
namespace Ding\Aspect;

use Ding\Reflection\IReflectionFactoryAware;
use Ding\Cache\ICache;
use Ding\Aspect\Interceptor\IDispatcher;
use Ding\Reflection\IReflectionFactory;

/**
 * So... php does not have such a thing.. and here's what it needs to be done
 * to have a proxy class and any kind of "dynamic class".
 *
 * PHP Version 5
 *
 * @category Ding
 * @package  Aspect
 * @author   Marcelo Gornstein <marcelog@gmail.com>
 * @license  http://marcelog.github.com/ Apache License 2.0
 * @link     http://marcelog.github.com/
 *
 * @todo Performance: Remove new MethodInvocation in proxied invocation.
 */
class Proxy implements IReflectionFactoryAware
{
    /**
     * A ReflectionFactory implementation
     * @var IReflectionFactory
     */
    protected $reflectionFactory;

    /**
     * Proxy cache implementation.
     * @var ICache
     */
    protected $cache = false;

    /**
     * Proxy class template (i.e: the dynamic class)
     * @var string
     */
    protected $proxyTemplate = <<<TEXT

use Ding\Aspect\Interceptor\IDispatcher;
use Ding\Aspect\MethodInvocation;
use Ding\Reflection\IReflectionFactory;

final class NEW_NAME extends CLASS_NAME {
    /**
     * Holds advice dispatcher.
     * @var Dispatcher
     */
    private static \$_dispatcher = false;
	private static \$_reflectionFactory = false;

    /**
	 * Used to recognice proxies along the container.
     * @var boolean
     */
    public static \$iAmADingProxy = true;

    /**
     * Clone this object.
     *
     * @return void
     */
    public function __clone()
    {
        self::\$_dispatcher = clone self::\$_dispatcher;
    }

    /**
     * This is used from the container to set the dispatcher for the aspects.
     *
     * @param IDispatcher \$dispatcher Advice dispatcher.
     *
     * @return void
     */
    public static function setDispatcher(IDispatcher \$dispatcher)
    {
        self::\$_dispatcher = \$dispatcher;
    }

    public static function setReflectionFactory(IReflectionFactory \$reflectionFactory)
    {
    	self::\$_reflectionFactory = \$reflectionFactory;
    }
    METHODS
}
TEXT;

    /**
     * Method template (i.e: effetively, the proxy methods).
     * @var string
     */
    protected $methodTemplate = <<<TEXT
    VISIBILITY ADDITIONAL function METHOD_NAME(METHOD_ARGS)
    {
        \$invocation = new MethodInvocation(
            'CLASS_NAME', 'METHOD_NAME', func_get_args(), \$this,
            self::\$_reflectionFactory
        );
        try
        {
        	return self::\$_dispatcher->invoke(\$invocation);
        } catch (Exception \$exception) {
            \$invocation->setException(\$exception);
        	return self::\$_dispatcher->invokeException(\$invocation);
        }
	}
TEXT;

    /**
     * This will return a proxy class source.
     *
     * @param string          $newName     Name for the proxy class.
     * @param ReflectionClass $targetClass Class to be proxied.
     *
     * @see Proxy::$proxyTemplate
     *
     * @return string
     */
    protected function createClass($newName, array $proxyMethods, \ReflectionClass $class)
    {
        $src = $this->proxyTemplate;
        $src = str_replace('NEW_NAME', $newName, $src);
        $src = str_replace('CLASS_NAME', $class->getName(), $src);
        $methods = array();
        foreach ($proxyMethods as $method) {
            if ($class->hasMethod($method)) {
                $methods[] = $this->createMethod($class->getMethod($method));
            }
        }
        $src = str_replace('METHODS', implode("\n", $methods), $src);
        return $src;
    }

    /**
     * This will return a full proxy-method-parameter source.
     *
     * @param \ReflectionParameter $parameter The method parameter.
     *
     * @see Proxy::$methodTemplate
     *
     * @return string
     */
    protected function createParameter(\ReflectionParameter $parameter)
    {
        $parameterSrc = '';
        $paramClass = $parameter->getClass();
        if ($parameter->isArray()) {
            $parameterSrc .= 'array ';
        } else if ($paramClass) {
            $parameterSrc .= $paramClass->getName() . ' ';
        }
        if ($parameter->isPassedByReference()) {
            $parameterSrc .= ' &';
        }
        $parameterSrc .= '$' . $parameter->getName();
        if ($parameter->isOptional()) {
            $parameterSrc .= '=';
            if ($parameter->getDefaultValue() === null) {
                $parameterSrc .= 'null';
            } else if ($parameter->getDefaultValue() === false) {
                $parameterSrc .= 'false';
            } else if ($parameter->getDefaultValue() === true) {
                $parameterSrc .= 'true';
            } else if (is_array($parameter->getDefaultValue())) {
                $parameterSrc .= 'array()';
            } else {
                $parameterSrc .= "'" . $parameter->getDefaultValue() . "'";
            }
        }
        return $parameterSrc;
    }

    /**
     * This will return a full proxy-method source.
     *
     * @param \ReflectionMethod $method The method to be proxied.
     *
     * @see Proxy::$methodTemplate
     *
     * @return string
     */
    protected function createMethod(\ReflectionMethod $method)
    {
        $visibility = '';
        $additional = '';
        $name = $method->getName();
        if ($method->isPublic()) {
            $visibility = ' public';
        } else if ($method->isProtected()) {
            $visibility = ' protected';
        } else if ($method->isPrivate()) {
            // useless really. $visibility = ' private';
            return '';
        }
        if ($method->isStatic()) {
            // useless really. $additional .= ' static ';
            return '';
        }
        //if ($method->isAbstract()) {
            // useless really. $$additional .= ' abstract ';
            //return '';
        //}
        if ($method->isConstructor()) {
            $name = '__construct';
        } else if ($method->isDestructor()) {
            $name = '__destruct';
        }
        $args = array();
        foreach ($method->getParameters() as $parameter) {
            $args[] = $this->createParameter($parameter);
        }

        $src = $this->methodTemplate;
        $src = str_replace('VISIBILITY', $visibility, $src);
        $src = str_replace('ADDITIONAL', $additional, $src);
        $src = str_replace('METHOD_NAME', $name, $src);
        $src = str_replace('METHOD_ARGS', implode(',', $args), $src);
        $src = str_replace(
        	'CLASS_NAME', $method->getDeclaringClass()->getName(), $src
        );
        return $src;
    }

    /**
     * Sets Proxy cache to use.
     *
     * @param ICache $cache
     *
     * @return void
     */
    public function setCache(ICache $cache)
    {
        $this->cache = $cache;
    }

    /**
     * (non-PHPdoc)
     * @see Ding\Reflection.IReflectionFactoryAware::setReflectionFactory()
     */
    public function setReflectionFactory(IReflectionFactory $reflectionFactory)
    {
        $this->reflectionFactory = $reflectionFactory;
    }
    /**
     * This will give you the name of a proxy class as a string. The class will
     * already exist in the vm.
     *
     * @return string
     */
    public function create($class, IDispatcher $dispatcher)
    {
        $subject = $this->reflectionFactory->getClass($class);
        $proxyClassName = 'Proxy' . str_replace('\\', '', $subject->getName());
        $cacheKey = $proxyClassName . '.proxy';
        $result = false;
        $src = $this->cache->fetch($cacheKey, $result);
        if (!$result) {
            $src = $this->createClass(
                $proxyClassName, $dispatcher->getMethodsIntercepted(), $subject
            );
            $this->cache->store($cacheKey, $src);
        }
        eval($src);
        $proxyClassName::setDispatcher($dispatcher);
        $proxyClassName::setReflectionFactory($this->reflectionFactory);
        return $proxyClassName;
    }
}


<?php
/**
 * YAML bean factory.
 *
 * PHP Version 5
 *
 * @category   Ding
 * @package    Bean
 * @subpackage Provider
 * @author     Marcelo Gornstein <marcelog@gmail.com>
 * @license    http://marcelog.github.com/ Apache License 2.0
 * @link       http://marcelog.github.com/
 *
 * Copyright 2011 Marcelo Gornstein <marcelog@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
namespace Ding\Bean\Provider;

use Ding\Reflection\IReflectionFactory;
use Ding\Reflection\IReflectionFactoryAware;
use Ding\Logger\ILoggerAware;
use Ding\Aspect\IAspectManagerAware;
use Ding\Container\IContainerAware;
use Ding\Bean\IBeanDefinitionProvider;
use Ding\Bean\Lifecycle\IAfterConfigListener;
use Ding\Aspect\PointcutDefinition;
use Ding\Container\IContainer;
use Ding\Aspect\AspectManager;
use Ding\Bean\Factory\Exception\BeanFactoryException;
use Ding\Bean\BeanConstructorArgumentDefinition;
use Ding\Bean\BeanDefinition;
use Ding\Bean\BeanPropertyDefinition;
use Ding\Aspect\AspectDefinition;
use Ding\Aspect\IAspectProvider;
use Ding\Aspect\IPointcutProvider;

/**
 * YAML bean factory.
 *
 * PHP Version 5
 *
 * @category   Ding
 * @package    Bean
 * @subpackage Driver
 * @author     Marcelo Gornstein <marcelog@gmail.com>
 * @license    http://marcelog.github.com/ Apache License 2.0
 * @link       http://marcelog.github.com/
 */
class Yaml implements
    IAfterConfigListener, IAspectProvider,
    IPointcutProvider, IBeanDefinitionProvider, IAspectManagerAware, IContainerAware,
    ILoggerAware, IReflectionFactoryAware
{
    protected $container;

    /**
     * log4php logger or our own.
     * @var Logger
     */
    private $_logger;

    /**
     * beans.yaml file path.
     * @var string
     */
    private $_filename;

    /**
     * Yaml contents.
     * @var string[]
     */
    private $_yamlFiles = false;

    /**
     * Bean definition template to clone.
     * @var BeanDefinition
     */
    private $_templateBeanDef;

    /**
     * Bean property definition template to clone.
     * @var BeanPropertyDefinition
     */
    private $_templatePropDef;

    /**
     * Bean constructor argument definition template to clone.
     * @var BeanConstructorArgumentDefinition
     */
    private $_templateArgDef;

    /**
     * Aspect definition template to clone.
     * @var AspectDefinition
     */
    private $_templateAspectDef;

    /**
     * Pointcut definition template to clone.
     * @var PointcutDefinition
     */
    private $_templatePointcutDef;

    /**
     * The aspect manager.
     * @var AspectManager
     */
    private $_aspectManager = false;

    /**
     * Optional directories to search for bean files.
     * @var string[]
     */
    private $_directories = false;

    /**
     * Bean aliases, pre-scanned
     * @var string[]
     */
    private $_beanAliases = array();

    /**
     * Maps beans from their classes.
     * @var string[]
     */
    private $_knownBeansByClass = array();

    private $_knownBeansPerEvent = array();

    /**
     * @var IReflectionFactory
     */
    private $_reflectionFactory;

    /**
     * Serialization.
     *
     * @return array
     */
    public function __sleep()
    {
        return array();
    }

    /**
     * Initializes yaml contents.
     *
     * @param string $filename
     *
     * @throws BeanFactoryException
     * @return mixed[]
     */
    private function _loadYaml($filename)
    {
        $yamls = array();
        if (is_array($filename)) {
            foreach ($filename as $file) {
                foreach ($this->_loadYaml($file) as $name => $yaml) {
                    $yamls[$name] = $yaml;
                }
            }
            return $yamls;
        }
            $contents = false;
        foreach ($this->_directories as $directory) {
            $fullname = $directory . DIRECTORY_SEPARATOR . $filename;
            if (!file_exists($fullname)) {
                continue;
            }
            $contents = @file_get_contents($fullname);
        }
        if ($contents === false) {
            throw new BeanFactoryException($filename . ' not found in ' . print_r($this->_directories, true));
        }
        $ret = @yaml_parse($contents);
        if ($ret === false) {
            return $ret;
        }
        $yamls[$filename] = $ret;
        if (isset($ret['import'])) {
            foreach ($ret['import'] as $imported) {
                foreach ($this->_loadYaml($imported) as $name => $yaml) {
                    $yamls[$name] = $yaml;
                }
            }
        }
        return $yamls;
    }

    /**
     * Returns an aspect definition.
     *
     * @param mixed[] $aspect Aspect data.
     *
     * @throws BeanFactoryException
     * @return AspectDefinition
     */
    private function _loadAspect($aspect)
    {
        $aspects = array();
        if (isset($aspect['id'])) {
            $name = $aspect['id'];
        } else {
            $name = BeanDefinition::generateName('AspectYAML');
        }
        if (isset($aspect['expression'])) {
            $expression = $aspect['expression'];
        } else {
            $expression = '';
        }
        $aspectBean = $aspect['ref'];
        $type = $aspect['type'];
        if ($type == 'method') {
            $type = AspectDefinition::ASPECT_METHOD;
        } else if ($type == 'exception') {
            $type = AspectDefinition::ASPECT_EXCEPTION;
        } else {
            throw new BeanFactoryException('Invalid aspect type');
        }
        $pointcuts = array();
        foreach ($aspect['pointcuts'] as $pointcut) {
            if (isset($pointcut['id'])) {
                $pointcutName = $pointcut['id'];
            } else {
                $pointcutName = BeanDefinition::generateName('PointcutYAML');
            }
            if (isset($pointcut['expression'])) {
                $pointcutDef = clone $this->_templatePointcutDef;
                $pointcutDef->setName($pointcutName);
                $pointcutDef->setExpression($pointcut['expression']);
                $pointcutDef->setMethod($pointcut['method']);
                $this->_aspectManager->setPointcut($pointcutDef);
                $pointcuts[] = $pointcutName;
            } else if (isset($pointcut['pointcut-ref'])) {
                $pointcuts[] = $pointcut['pointcut-ref'];
            }
        }
        return new AspectDefinition($name, $pointcuts, $type, $aspectBean, $expression);
    }

    /**
     * Returns a property definition.
     *
     * @param string  $name         Property name.
     * @param mixed[] $value        Property YAML structure value.
     * @param string  $yamlFilename Filename for yaml file.
     *
     * @throws BeanFactoryException
     * @return BeanPropertyDefinition
     */
    private function _loadProperty($name, $value, $yamlFilename)
    {
        if (isset($value['ref'])) {
            $propType = BeanPropertyDefinition::PROPERTY_BEAN;
            $propValue = $value['ref'];
        } else if (isset($value['eval'])) {
            $propType = BeanPropertyDefinition::PROPERTY_CODE;
            $propValue = $value['eval'];
        } else if (isset($value['bean'])) {
            $propType = BeanPropertyDefinition::PROPERTY_BEAN;
            $innerBean = BeanDefinition::generateName('Bean');
            $this->_yamlFiles[$yamlFilename]['beans'][$innerBean] = $value['bean'];
            $propValue = $innerBean;
        } else if (is_array($value['value'])) {
            $propType = BeanPropertyDefinition::PROPERTY_ARRAY;
            $propValue = array();
            foreach ($value['value'] as $key => $inValue) {
                $propValue[$key] = $this->_loadProperty($key, $inValue, $yamlFilename);
            }
        } else {
            $propType = BeanPropertyDefinition::PROPERTY_SIMPLE;
            $propValue = $value['value'];
        }
        return new BeanPropertyDefinition($name, $propType, $propValue);
    }

    /**
     * Returns a constructor argument definition.
     *
     * @param mixed  $value Constructor arg YAML structure value.
     * @param string $yamlFilename Filename for yaml file.
     *
     * @throws BeanFactoryException
     * @return BeanConstructorArgumentDefinition
     */
    private function _loadConstructorArg($name, $value, $yamlFilename)
    {
        if (is_array($value)) {
            if (isset($value['ref'])) {
                $argType = BeanConstructorArgumentDefinition::BEAN_CONSTRUCTOR_BEAN;
                $argValue = $value['ref'];
            } else if (isset($value['eval'])) {
                $argType = BeanConstructorArgumentDefinition::BEAN_CONSTRUCTOR_CODE;
                $argValue = $value['eval'];
            } else if (isset($value['bean'])) {
                $argType = BeanConstructorArgumentDefinition::BEAN_CONSTRUCTOR_BEAN;
                $innerBean = BeanDefinition::generateName('Bean');
                $this->_yamlFiles[$yamlFilename]['beans'][$innerBean] = $value['bean'];
                $argValue = $innerBean;
            } else {
                $argType = BeanConstructorArgumentDefinition::BEAN_CONSTRUCTOR_ARRAY;
                $argValue = array();
                foreach ($value as $key => $inValue) {
                    $argValue[$key] = $this->_loadConstructorArg(false, $inValue, $yamlFilename);
                }
            }
        } else {
            $argType = BeanConstructorArgumentDefinition::BEAN_CONSTRUCTOR_VALUE;
            $argValue = $value;
        }
        if (is_string($name)) {
            $argName = $name;
        } else {
            $argName = false;
        }
        return new BeanConstructorArgumentDefinition($argType, $argValue, $argName);
    }

    /**
     * Initialize YAML contents.
     *
     * @throws BeanFactoryException
     * @return void
     */
    private function _load()
    {
        if ($this->_yamlFiles !== false) {
            return;
        }
        $this->_yamlFiles = $this->_loadYaml($this->_filename);
        if (empty($this->_yamlFiles)) {
            throw new BeanFactoryException('Could not parse: ' . $this->_filename);
        }
    }

    public function getPointcut($name)
    {
        foreach($this->_yamlFiles as $yamlFilename => $yaml) {
            if (isset($yaml['pointcuts'][$name])) {
                $pointcutDef = clone $this->_templatePointcutDef;
                $pointcutDef->setName($name);
                $pointcutDef->setExpression($yaml['pointcuts'][$name]['expression']);
                $pointcutDef->setMethod($yaml['pointcuts'][$name]['method']);
                return $pointcutDef;
            }
        }
        return false;
    }
    /**
     * (non-PHPdoc)
     * @see Ding\Bean.IBeanDefinitionProvider::getBeansListeningOn()
     */
    public function getBeansListeningOn($eventName)
    {
        if (isset($this->_knownBeansPerEvent[$eventName])) {
            return $this->_knownBeansPerEvent[$eventName];
        }
        return array();
    }

    private function _addBeanToKnownByClass($class, $name)
    {
        if (strpos($class, "\${") !== false) {
            return;
        }
        if (!isset($this->_knownBeansByClass[$class])) {
            $this->_knownBeansByClass[$class] = array();
        }
        $this->_knownBeansByClass[$class][] = $name;
        // Load any parent classes
        $rClass = $this->_reflectionFactory->getClass($class);
        $parentClass = $rClass->getParentClass();
        while ($parentClass) {
            $parentClassName = $parentClass->getName();
            $this->_knownBeansByClass[$parentClassName][] = $name;
            $parentClass = $parentClass->getParentClass();
        }

        // Load any interfaces
        foreach ($rClass->getInterfaces() as $interfaceName => $rInterface) {
            $this->_knownBeansByClass[$interfaceName][] = $name;
        }
    }

    public function afterConfig()
    {
        $this->_load();
        foreach($this->_yamlFiles as $yamlFilename => $yaml) {
            if (isset($yaml['alias'])) {
                foreach ($yaml['alias'] as $beanName => $aliases) {
                    $aliases = explode(',', $aliases);
                    foreach ($aliases as $alias) {
                        $alias = trim($alias);
                        $this->_beanAliases[$alias] = $beanName;
                    }
                }
            }
            if (isset($yaml['beans'])) {
                foreach ($yaml['beans'] as $beanName => $beanDef) {
                    if (isset($beanDef['class'])) {
                        $class = $beanDef['class'];
                        if (isset($beanDef['factory-method'])) {
                            // Skip beans that specify class as their factory class
                            if (isset($beanDef['factory-bean'])) {
                                $this->_addBeanToKnownByClass($class, $beanName);
                            }
                        } else {
                            $this->_addBeanToKnownByClass($class, $beanName);
                        }
                    }
                    if (isset($beanDef['name'])) {
                        $aliases = explode(',', $beanDef['name']);
                        foreach ($aliases as $alias) {
                            $alias = trim($alias);
                            $this->_beanAliases[$alias] = $beanName;
                        }
                    }
                    if (isset($beanDef['listens-on'])) {
                        $events = $beanDef['listens-on'];
                        foreach (explode(',', $events) as $eventName) {
                            $eventName = trim($eventName);
                            if (!isset($this->_knownBeansPerEvent[$eventName])) {
                                $this->_knownBeansPerEvent[$eventName] = array();
                            }
                            $this->_knownBeansPerEvent[$eventName][] = $beanName;
                        }
                    }
                }
            }
        }
    }
    public function getAspects()
    {
        $aspects = array();
        $this->_load();
        foreach($this->_yamlFiles as $yamlFilename => $yaml) {
            if (isset($yaml['aspects'])) {
                foreach ($yaml['aspects'] as $aspect) {
                    $aspects[] = $this->_loadAspect($aspect);
                }
            }
        }
        return $aspects;
    }

    /**
     * (non-PHPdoc)
     * @see Ding\Aspect.IBeanDefinitionProvider::getBeanDefinition()
     */
    public function getBeanDefinition($beanName)
    {
        $beanDef = false;
        if (isset($this->_beanAliases[$beanName])) {
            return $this->getBeanDefinition($this->_beanAliases[$beanName]);
        }
        foreach($this->_yamlFiles as $yamlFilename => $yaml) {
            if (isset($yaml['beans'][$beanName])) {
                $beanDef = $yaml['beans'][$beanName];
                break;
            }
        }
        if (false == $beanDef) {
            return null;
        }
        $bMethods = $bProps = $bAspects = $constructorArgs = array();
        if (isset($beanDef['parent'])) {
            $bean = $this->container->getBeanDefinition($beanDef['parent']);
            $bean = $bean->makeChildBean($beanName);
            $bProps = $bean->getProperties();
            $constructorArgs = $bean->getArguments();
            $bAspects = $bean->getAspects();
            $bMethods = $bean->getMethodInjections();
        } else {
            $bean = clone $this->_templateBeanDef;
        }
        $bean->setName($beanName);
        if (isset($beanDef['class'])) {
            $bean->setClass($beanDef['class']);
        }

        if (isset($beanDef['scope'])) {
            if ($beanDef['scope'] == 'prototype') {
                $bean->setScope(BeanDefinition::BEAN_PROTOTYPE);
            } else if ($beanDef['scope'] == 'singleton') {
                $bean->setScope(BeanDefinition::BEAN_SINGLETON);
            } else {
                throw new BeanFactoryException('Invalid bean scope: ' . $beanDef['scope']);
            }
        }
        if (isset($beanDef['primary'])) {
            $primary = $beanDef['primary'];
            if ($primary == 'true') {
                $bean->markAsPrimaryCandidate();
            }
        }

        if (isset($beanDef['factory-method'])) {
            $bean->setFactoryMethod($beanDef['factory-method']);
        }

        if (isset($beanDef['depends-on'])) {
            $bean->setDependsOn(explode(',', $beanDef['depends-on']));
        }
        if (isset($beanDef['abstract'])) {
            if ($beanDef['abstract'] == 'true') {
                $bean->makeAbstract();
            }
        }
        if (isset($beanDef['factory-bean'])) {
            $bean->setFactoryBean($beanDef['factory-bean']);
        }
        if (isset($beanDef['init-method'])) {
            $bean->setInitMethod($beanDef['init-method']);
        }
        if (isset($beanDef['destroy-method'])) {
            $bean->setDestroyMethod($beanDef['destroy-method']);
        }
        if (isset($beanDef['properties'])) {
            foreach ($beanDef['properties'] as $name => $value) {
                $bProp = $this->_loadProperty($name, $value, $yamlFilename);
                $bProps[$name] = $bProp;
            }
        }
        if (isset($beanDef['constructor-args'])) {
            foreach ($beanDef['constructor-args'] as $name => $arg) {
                $constructorArgs[] = $this->_loadConstructorArg($name, $arg, $yamlFilename);
            }
        }

        if (isset($beanDef['aspects'])) {
            foreach ($beanDef['aspects'] as $name => $aspect) {
                $aspect['id'] = $name;
                $aspectDefinition = $this->_loadAspect($aspect);
                $bAspects[] = $aspectDefinition;
            }
        }

        if (isset($beanDef['lookup-methods'])) {
            foreach ($beanDef['lookup-methods'] as $name => $beanName) {
                $bMethods[] = array($name, $beanName);
            }
        }
        if (!empty($bProps)) {
            $bean->setProperties($bProps);
        }
        if (!empty($bAspects)) {
            $bean->setAspects($bAspects);
        }
        if (!empty($constructorArgs)) {
            $bean->setArguments($constructorArgs);
        }
        if (!empty($bMethods)) {
            $bean->setMethodInjections($bMethods);
        }
        return $bean;
    }

    /**
     * (non-PHPdoc)
     * @see Ding\Aspect.IBeanDefinitionProvider::getBeanDefinitionByClass()
     */
    public function getBeansByClass($class)
    {
        if (isset($this->_knownBeansByClass[$class])) {
            return $this->_knownBeansByClass[$class];
        }
        return array();
    }

    /**
     * (non-PHPdoc)
     * @see Ding\Aspect.IAspectManagerAware::setAspectManager()
     */
    public function setAspectManager(AspectManager $aspectManager)
    {
        $this->_aspectManager = $aspectManager;
    }

    /**
     * (non-PHPdoc)
     * @see Ding\Container.IContainerAware::setContainer()
     */
    public function setContainer(IContainer $container)
    {
        $this->container = $container;
    }

    /**
     * (non-PHPdoc)
     * @see Ding\Logger.ILoggerAware::setLogger()
     */
    public function setLogger(\Logger $logger)
    {
        $this->_logger = $logger;
    }

    /**
     * (non-PHPdoc)
     * @see Ding\Reflection.IReflectionFactoryAware::setReflectionFactory()
     */
    public function setReflectionFactory(IReflectionFactory $reflectionFactory)
    {
        $this->_reflectionFactory = $reflectionFactory;
    }

    /**
     * Constructor.
     *
     * @param array                      $options
     * @param \Ding\Aspect\AspectManager $aspectManager
     *
     * @return void
     */
    public function __construct(array $options)
    {
        $this->_beanDefs = array();
        $this->_filename = $options['filename'];
        $this->_directories
            = isset($options['directories'])
            ? $options['directories']
            : array('.');
        $this->_yamlFiles = false;
        $this->_templateBeanDef = new BeanDefinition('');
        $this->_templatePropDef = new BeanPropertyDefinition('', 0, null);
        $this->_templateArgDef = new BeanConstructorArgumentDefinition(0, null);
        $this->_templateAspectDef = new AspectDefinition('', '', 0, '', '');
        $this->_templatePointcutDef = new PointcutDefinition('', '', '');
    }
}