<?php
/**
 * PHP Token Reflection
 *
 * Version 1.4.0
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this library in the file LICENSE.md.
 *
 * @author Ondřej Nešpor
 * @author Jaroslav Hanslík
 */

namespace TokenReflection;

use TokenReflection\Exception, TokenReflection\Stream\StreamBase as Stream;
use ReflectionClass as InternalReflectionClass, ReflectionProperty as InternalReflectionProperty, ReflectionMethod as InternalReflectionMethod;

/**
 * Tokenized class reflection.
 */
class ReflectionClass extends ReflectionElement implements IReflectionClass
{
	/**
	 * Modifier for determining if the reflected object is an interface.
	 *
	 * @var integer
	 * @see http://svn.php.net/viewvc/php/php-src/branches/PHP_5_3/Zend/zend_compile.h?revision=306939&view=markup#l122
	 */
	const IS_INTERFACE = 0x80;

	/**
	 * Modifier for determining if the reflected object is a trait.
	 *
	 * @var integer
	 * @see http://svn.php.net/viewvc/php/php-src/trunk/Zend/zend_compile.h?revision=306938&view=markup#l150
	 */
	const IS_TRAIT = 0x120;

	/**
	 * Class implements interfaces.
	 *
	 * @see http://svn.php.net/viewvc/php/php-src/branches/PHP_5_3/Zend/zend_compile.h?revision=306939&view=markup#l152
	 *
	 * @var integer
	 */
	const IMPLEMENTS_INTERFACES = 0x80000;

	/**
	 * Class implements traits.
	 *
	 * @see http://svn.php.net/viewvc/php/php-src/trunk/Zend/zend_compile.h?revision=306938&view=markup#l181
	 *
	 * @var integer
	 */
	const IMPLEMENTS_TRAITS = 0x400000;

	/**
	 * Class namespace name.
	 *
	 * @var string
	 */
	private $namespaceName;

	/**
	 * Class modifiers.
	 *
	 * @var integer
	 */
	private $modifiers = 0;

	/**
	 * Class type (class/interface/trait).
	 *
	 * @var integer
	 */
	private $type = 0;

	/**
	 * Determines if modifiers are complete.
	 *
	 * @var boolean
	 */
	private $modifiersComplete = false;

	/**
	 * Parent class name.
	 *
	 * @var string
	 */
	private $parentClassName;

	/**
	 * Implemented interface names.
	 *
	 * @var array
	 */
	private $interfaces = array();

	/**
	 * Used trait names.
	 *
	 * @var array
	 */
	private $traits = array();

	/**
	 * Aliases used at trait methods.
	 *
	 * Compatible with the internal reflection.
	 *
	 * @var array
	 */
	private $traitAliases = array();

	/**
	 * Trait importing rules.
	 *
	 * Format:
	 * [<trait>::]<method> => array(
	 *    array(<new-name>, [<access-level>])|null
	 * 	  [, ...]
	 * )
	 *
	 * @var array
	 */
	private $traitImports = array();

	/**
	 * Stores if the class definition is complete.
	 *
	 * @var array
	 */
	private $methods = array();

	/**
	 * Constant reflections.
	 *
	 * @var array
	 */
	private $constants = array();

	/**
	 * Properties reflections.
	 *
	 * @var array
	 */
	private $properties = array();

	/**
	 * Stores if the class definition is complete.
	 *
	 * @var boolean
	 */
	private $definitionComplete = false;

	/**
	 * Imported namespace/class aliases.
	 *
	 * @var array
	 */
	private $aliases = array();

	/**
	 * Returns the unqualified name (UQN).
	 *
	 * @return string
	 */
	public function getShortName()
	{
		$name = $this->getName();
		if ($this->namespaceName !== ReflectionNamespace::NO_NAMESPACE_NAME) {
			$name = substr($name, strlen($this->namespaceName) + 1);
		}

		return $name;
	}

	/**
	 * Returns the namespace name.
	 *
	 * @return string
	 */
	public function getNamespaceName()
	{
		return $this->namespaceName === ReflectionNamespace::NO_NAMESPACE_NAME ? '' : $this->namespaceName;
	}

	/**
	 * Returns if the class is defined within a namespace.
	 *
	 * @return boolean
	 */
	public function inNamespace()
	{
		return null !== $this->namespaceName && ReflectionNamespace::NO_NAMESPACE_NAME !== $this->namespaceName;
	}

	/**
	 * Returns modifiers.
	 *
	 * @return array
	 */
	public function getModifiers()
	{
		if (false === $this->modifiersComplete) {
			if (($this->modifiers & InternalReflectionClass::IS_EXPLICIT_ABSTRACT) && !($this->modifiers & InternalReflectionClass::IS_IMPLICIT_ABSTRACT)) {
				foreach ($this->getMethods() as $reflectionMethod) {
					if ($reflectionMethod->isAbstract()) {
						$this->modifiers |= InternalReflectionClass::IS_IMPLICIT_ABSTRACT;
					}
				}

				if (!empty($this->interfaces)) {
					$this->modifiers |= InternalReflectionClass::IS_IMPLICIT_ABSTRACT;
				}
			}

			if (!empty($this->interfaces)) {
				$this->modifiers |= self::IMPLEMENTS_INTERFACES;
			}

			if ($this->isInterface() && !empty($this->methods)) {
				$this->modifiers |= InternalReflectionClass::IS_IMPLICIT_ABSTRACT;
			}

			if (!empty($this->traits)) {
				$this->modifiers |= self::IMPLEMENTS_TRAITS;
			}

			$this->modifiersComplete = null === $this->parentClassName || $this->getParentClass()->isComplete();

			if ($this->modifiersComplete) {
				foreach ($this->getInterfaces() as $interface) {
					if (!$interface->isComplete()) {
						$this->modifiersComplete = false;
						break;
					}
				}
			}
			if ($this->modifiersComplete) {
				foreach ($this->getTraits() as $trait) {
					if (!$trait->isComplete()) {
						$this->modifiersComplete = false;
						break;
					}
				}
			}
		}

		return $this->modifiers;
	}

	/**
	 * Returns if the class is abstract.
	 *
	 * @return boolean
	 */
	public function isAbstract()
	{
		if ($this->modifiers & InternalReflectionClass::IS_EXPLICIT_ABSTRACT) {
			return true;
		} elseif ($this->isInterface() && !empty($this->methods)) {
			return true;
		}

		return false;
	}

	/**
	 * Returns if the class is final.
	 *
	 * @return boolean
	 */
	public function isFinal()
	{
		return (bool) ($this->modifiers & InternalReflectionClass::IS_FINAL);
	}

	/**
	 * Returns if the class is an interface.
	 *
	 * @return boolean
	 */
	public function isInterface()
	{
		return (bool) ($this->modifiers & self::IS_INTERFACE);
	}

	/**
	 * Returns if the class is an exception or its descendant.
	 *
	 * @return boolean
	 */
	public function isException()
	{
		return 'Exception' === $this->name || $this->isSubclassOf('Exception');
	}

	/**
	 * Returns if it is possible to create an instance of this class.
	 *
	 * @return boolean
	 */
	public function isInstantiable()
	{
		if ($this->isInterface() || $this->isAbstract()) {
			return false;
		}

		if (null === ($constructor = $this->getConstructor())) {
			return true;
		}

		return $constructor->isPublic();
	}

	/**
	 * Returns if objects of this class are cloneable.
	 *
	 * Introduced in PHP 5.4.
	 *
	 * @return boolean
	 * @see http://svn.php.net/viewvc/php/php-src/trunk/ext/reflection/php_reflection.c?revision=307971&view=markup#l4059
	 */
	public function isCloneable()
	{
		if ($this->isInterface() || $this->isAbstract()) {
			return false;
		}

		if ($this->hasMethod('__clone')) {
			return $this->getMethod('__clone')->isPublic();
		}

		return true;
	}

	/**
	 * Returns if the class is iterateable.
	 *
	 * Returns true if the class implements the Traversable interface.
	 *
	 * @return boolean
	 * @todo traits
	 */
	public function isIterateable()
	{
		return $this->implementsInterface('Traversable');
	}

	/**
	 * Returns if the current class is a subclass of the given class.
	 *
	 * @param string|object $class Class name or reflection object
	 * @return boolean
	 * @throws \TokenReflection\Exception\RuntimeException If the provided parameter is not a reflection class instance.
	 */
	public function isSubclassOf($class)
	{
		if (is_object($class)) {
			if ($class instanceof InternalReflectionClass || $class instanceof IReflectionClass) {
				$class = $class->getName();
			} else {
				$class = get_class($class);
			}
		}

		if ($class === $this->parentClassName) {
			return true;
		}

		$parent = $this->getParentClass();
		return false === $parent ? false : $parent->isSubclassOf($class);
	}

	/**
	 * Returns the parent class reflection.
	 *
	 * @return \TokenReflection\ReflectionClass|boolean
	 */
	public function getParentClass()
	{
		$className = $this->getParentClassName();
		if (null === $className) {
			return false;
		}

		return $this->getBroker()->getClass($className);
	}

	/**
	 * Returns the parent class name.
	 *
	 * @return string|null
	 */
	public function getParentClassName()
	{
		return $this->parentClassName;
	}

	/**
	 * Returns the parent classes reflections.
	 *
	 * @return array
	 */
	public function getParentClasses()
	{
		$parent = $this->getParentClass();
		if (false === $parent) {
			return array();
		}

		return array_merge(array($parent->getName() => $parent), $parent->getParentClasses());
	}

	/**
	 * Returns the parent classes names.
	 *
	 * @return array
	 */
	public function getParentClassNameList()
	{
		$parent = $this->getParentClass();
		if (false === $parent) {
			return array();
		}

		return array_merge(array($parent->getName()), $parent->getParentClassNameList());
	}

	/**
	 * Returns if the class implements the given interface.
	 *
	 * @param string|object $interface Interface name or reflection object
	 * @return boolean
	 * @throws \TokenReflection\Exception\RuntimeException If the provided parameter is not an interface.
	 */
	public function implementsInterface($interface)
	{
		if (is_object($interface)) {
			if (!$interface instanceof InternalReflectionClass && !$interface instanceof IReflectionClass) {
				throw new Exception\RuntimeException(sprintf('Parameter must be a string or an instance of class reflection, "%s" provided.', get_class($interface)), Exception\RuntimeException::INVALID_ARGUMENT, $this);
			}

			if (!$interface->isInterface()) {
				throw new Exception\RuntimeException(sprintf('"%s" is not an interface.', $interfaceName), Exception\RuntimeException::INVALID_ARGUMENT, $this);
			}

			$interfaceName = $interface->getName();
		} else {
			$interfaceName = $interface;
		}

		return in_array($interfaceName, $this->getInterfaceNames());
	}

	/**
	 * Returns interface reflections.
	 *
	 * @return array
	 */
	public function getInterfaces()
	{
		$interfaceNames = $this->getInterfaceNames();
		if (empty($interfaceNames)) {
			return array();
		}

		$broker = $this->getBroker();
		return array_combine($interfaceNames, array_map(function($interfaceName) use ($broker) {
			return $broker->getClass($interfaceName);
		}, $interfaceNames));
	}

	/**
	 * Returns interface names.
	 *
	 * @return array
	 */
	public function getInterfaceNames()
	{
		$parentClass = $this->getParentClass();

		$names = false !== $parentClass ? array_reverse(array_flip($parentClass->getInterfaceNames())) : array();
		foreach ($this->interfaces as $interfaceName) {
			$names[$interfaceName] = true;
			foreach (array_reverse($this->getBroker()->getClass($interfaceName)->getInterfaceNames()) as $parentInterfaceName) {
				$names[$parentInterfaceName] = true;
			}
		}

		return array_keys($names);
	}

	/**
	 * Returns reflections of interfaces implemented by this class, not its parents.
	 *
	 * @return array
	 */
	public function getOwnInterfaces()
	{
		$interfaceNames = $this->getOwnInterfaceNames();
		if (empty($interfaceNames)) {
			return array();
		}

		$broker = $this->getBroker();
		return array_combine($interfaceNames, array_map(function($interfaceName) use ($broker) {
			return $broker->getClass($interfaceName);
		}, $interfaceNames));
	}

	/**
	 * Returns names of interfaces implemented by this class, not its parents.
	 *
	 * @return array
	 */
	public function getOwnInterfaceNames()
	{
		return $this->interfaces;
	}

	/**
	 * Returns the class constructor reflection.
	 *
	 * @return \TokenReflection\ReflectionMethod|null
	 */
	public function getConstructor()
	{
		foreach ($this->getMethods() as $method) {
			if ($method->isConstructor()) {
				return $method;
			}
		}

		return null;
	}

	/**
	 * Returns the class destructor reflection.
	 *
	 * @return \TokenReflection\ReflectionMethod|null
	 */
	public function getDestructor()
	{
		foreach ($this->getMethods() as $method) {
			if ($method->isDestructor()) {
				return $method;
			}
		}

		return null;
	}

	/**
	 * Returns if the class implements the given method.
	 *
	 * @param string $name Method name
	 * @return boolean
	 */
	public function hasMethod($name)
	{
		foreach ($this->getMethods() as $method) {
			if ($name === $method->getName()) {
				return true;
			}
		}

		return false;
	}

	/**
	 * Returns a method reflection.
	 *
	 * @param string $name Method name
	 * @return \TokenReflection\ReflectionMethod
	 * @throws \TokenReflection\Exception\RuntimeException If the requested method does not exist.
	 */
	public function getMethod($name)
	{
		if (isset($this->methods[$name])) {
			return $this->methods[$name];
		}

		foreach ($this->getMethods() as $method) {
			if ($name === $method->getName()) {
				return $method;
			}
		}

		throw new Exception\RuntimeException(sprintf('There is no method "%s".', $name), Exception\RuntimeException::DOES_NOT_EXIST, $this);
	}

	/**
	 * Returns method reflections.
	 *
	 * @param integer $filter Methods filter
	 * @return array
	 */
	public function getMethods($filter = null)
	{
		$methods = $this->methods;

		foreach ($this->getTraitMethods() as $traitMethod) {
			if (!isset($methods[$traitMethod->getName()])) {
				$methods[$traitMethod->getName()] = $traitMethod;
			}
		}

		if (null !== $this->parentClassName) {
			foreach ($this->getParentClass()->getMethods(null) as $parentMethod) {
				if (!isset($methods[$parentMethod->getName()])) {
					$methods[$parentMethod->getName()] = $parentMethod;
				}
			}
		}
		foreach ($this->getOwnInterfaces() as $interface) {
			foreach ($interface->getMethods(null) as $parentMethod) {
				if (!isset($methods[$parentMethod->getName()])) {
					$methods[$parentMethod->getName()] = $parentMethod;
				}
			}
		}

		if (null !== $filter) {
			$methods = array_filter($methods, function(IReflectionMethod $method) use ($filter) {
				return $method->is($filter);
			});
		}

		return array_values($methods);
	}

	/**
	 * Returns if the class implements (and not its parents) the given method.
	 *
	 * @param string $name Method name
	 * @return boolean
	 */
	public function hasOwnMethod($name)
	{
		return isset($this->methods[$name]);
	}

	/**
	 * Returns reflections of methods declared by this class, not its parents.
	 *
	 * @param integer $filter Methods filter
	 * @return array
	 */
	public function getOwnMethods($filter = null)
	{
		$methods = $this->methods;

		if (null !== $filter) {
			$methods = array_filter($methods, function(ReflectionMethod $method) use ($filter) {
				return $method->is($filter);
			});
		}

		return array_values($methods);
	}

	/**
	 * Returns if the class imports the given method from traits.
	 *
	 * @param string $name Method name
	 * @return boolean
	 */
	public function hasTraitMethod($name)
	{
		if (isset($this->methods[$name])) {
			return false;
		}

		foreach ($this->getOwnTraits() as $trait) {
			if ($trait->hasMethod($name)) {
				return true;
			}
		}

		return false;
	}

	/**
	 * Returns reflections of method imported from traits.
	 *
	 * @param integer $filter Methods filter
	 * @return array
	 * @throws \TokenReflection\Exception\RuntimeException If trait method was already imported.
	 */
	public function getTraitMethods($filter = null)
	{
		$methods = array();

		foreach ($this->getOwnTraits() as $trait) {
			$traitName = $trait->getName();
			foreach ($trait->getMethods(null) as $traitMethod) {
				$methodName = $traitMethod->getName();

				$imports = array();
				if (isset($this->traitImports[$traitName . '::' . $methodName])) {
					$imports = $this->traitImports[$traitName . '::' . $methodName];
				}
				if (isset($this->traitImports[$methodName])) {
					$imports = empty($imports) ? $this->traitImports[$methodName] : array_merge($imports, $this->traitImports[$methodName]);
				}

				foreach ($imports as $import) {
					if (null !== $import) {
						list($newName, $accessLevel) = $import;

						if ('' === $newName) {
							$newName = $methodName;
							$imports[] = null;
						}

						if (!isset($this->methods[$newName])) {
							if (isset($methods[$newName])) {
								throw new Exception\RuntimeException(sprintf('Trait method "%s" was already imported.', $newName), Exception\RuntimeException::ALREADY_EXISTS, $this);
							}

							$methods[$newName] = $traitMethod->alias($this, $newName, $accessLevel);
						}
					}
				}

				if (!in_array(null, $imports)) {
					if (!isset($this->methods[$methodName])) {
						if (isset($methods[$methodName])) {
							throw new Exception\RuntimeException(sprintf('Trait method "%s" was already imported.', $methodName), Exception\RuntimeException::ALREADY_EXISTS, $this);
						}

						$methods[$methodName] = $traitMethod->alias($this);
					}
				}
			}
		}

		if (null !== $filter) {
			$methods = array_filter($methods, function(IReflectionMethod $method) use ($filter) {
				return (bool) ($method->getModifiers() & $filter);
			});
		}

		return array_values($methods);
	}

	/**
	 * Returns if the class defines the given constant.
	 *
	 * @param string $name Constant name.
	 * @return boolean
	 */
	public function hasConstant($name)
	{
		if (isset($this->constants[$name])) {
			return true;
		}

		foreach ($this->getConstantReflections() as $constant) {
			if ($name === $constant->getName()) {
				return true;
			}
		}

		return false;
	}

	/**
	 * Returns a constant value.
	 *
	 * @param string $name Constant name
	 * @return mixed|false
	 */
	public function getConstant($name)
	{
		try {
			return $this->getConstantReflection($name)->getValue();
		} catch (Exception\BaseException $e) {
			return false;
		}
	}

	/**
	 * Returns a constant reflection.
	 *
	 * @param string $name Constant name
	 * @return \TokenReflection\ReflectionConstant
	 * @throws \TokenReflection\Exception\RuntimeException If the requested constant does not exist.
	 */
	public function getConstantReflection($name)
	{
		if (isset($this->constants[$name])) {
			return $this->constants[$name];
		}

		foreach ($this->getConstantReflections() as $constant) {
			if ($name === $constant->getName()) {
				return $constant;
			}
		}

		throw new Exception\RuntimeException(sprintf('There is no constant "%s".', $name), Exception\RuntimeException::DOES_NOT_EXIST, $this);
	}

	/**
	 * Returns constant values.
	 *
	 * @return array
	 */
	public function getConstants()
	{
		$constants = array();
		foreach ($this->getConstantReflections() as $constant) {
			$constants[$constant->getName()] = $constant->getValue();
		}
		return $constants;
	}

	/**
	 * Returns constant reflections.
	 *
	 * @return array
	 */
	public function getConstantReflections()
	{
		if (null === $this->parentClassName && empty($this->interfaces)) {
			return array_values($this->constants);
		} else {
			$reflections = array_values($this->constants);

			if (null !== $this->parentClassName) {
				$reflections = array_merge($reflections, $this->getParentClass()->getConstantReflections());
			}
			foreach ($this->getOwnInterfaces() as $interface) {
				$reflections = array_merge($reflections, $interface->getConstantReflections());
			}

			return $reflections;
		}
	}

	/**
	 * Returns if the class (and not its parents) defines the given constant.
	 *
	 * @param string $name Constant name.
	 * @return boolean
	 */
	public function hasOwnConstant($name)
	{
		return isset($this->constants[$name]);
	}

	/**
	 * Returns constants declared by this class, not by its parents.
	 *
	 * @return array
	 */
	public function getOwnConstants()
	{
		return array_map(function(ReflectionConstant $constant) {
			return $constant->getValue();
		}, $this->constants);
	}

	/**
	 * Returns reflections of constants declared by this class, not by its parents.
	 *
	 * @return array
	 */
	public function getOwnConstantReflections()
	{
		return array_values($this->constants);
	}

	/**
	 * Returns if the class defines the given property.
	 *
	 * @param string $name Property name
	 * @return boolean
	 */
	public function hasProperty($name)
	{
		foreach ($this->getProperties() as $property) {
			if ($name === $property->getName()) {
				return true;
			}
		}

		return false;
	}

	/**
	 * Return a property reflection.
	 *
	 * @param string $name Property name
	 * @return \TokenReflection\ReflectionProperty
	 * @throws \TokenReflection\Exception\RuntimeException If the requested property does not exist.
	 */
	public function getProperty($name)
	{
		if (isset($this->properties[$name])) {
			return $this->properties[$name];
		}

		foreach ($this->getProperties() as $property) {
			if ($name === $property->getName()) {
				return $property;
			}
		}

		throw new Exception\RuntimeException(sprintf('There is no property "%s".', $name, $this->name), Exception\RuntimeException::DOES_NOT_EXIST, $this);
	}

	/**
	 * Returns property reflections.
	 *
	 * @param integer $filter Properties filter
	 * @return array
	 */
	public function getProperties($filter = null)
	{
		$properties = $this->properties;

		foreach ($this->getTraitProperties(null) as $traitProperty) {
			if (!isset($properties[$traitProperty->getName()])) {
				$properties[$traitProperty->getName()] = $traitProperty->alias($this);
			}
		}

		if (null !== $this->parentClassName) {
			foreach ($this->getParentClass()->getProperties(null) as $parentProperty) {
				if (!isset($properties[$parentProperty->getName()])) {
					$properties[$parentProperty->getName()] = $parentProperty;
				}
			}
		}

		if (null !== $filter) {
			$properties = array_filter($properties, function(IReflectionProperty $property) use ($filter) {
				return (bool) ($property->getModifiers() & $filter);
			});
		}

		return array_values($properties);
	}

	/**
	 * Returns if the class (and not its parents) defines the given property.
	 *
	 * @param string $name Property name
	 * @return boolean
	 */
	public function hasOwnProperty($name)
	{
		return isset($this->properties[$name]);
	}

	/**
	 * Returns reflections of properties declared by this class, not its parents.
	 *
	 * @param integer $filter Properties filter
	 * @return array
	 */
	public function getOwnProperties($filter = null)
	{
		$properties = $this->properties;

		if (null !== $filter) {
			$properties = array_filter($properties, function(ReflectionProperty $property) use ($filter) {
				return (bool) ($property->getModifiers() & $filter);
			});
		}

		return array_values($properties);
	}

	/**
	 * Returns if the class imports the given property from traits.
	 *
	 * @param string $name Property name
	 * @return boolean
	 */
	public function hasTraitProperty($name)
	{
		if (isset($this->properties[$name])) {
			return false;
		}

		foreach ($this->getOwnTraits() as $trait) {
			if ($trait->hasProperty($name)) {
				return true;
			}
		}

		return false;
	}

	/**
	 * Returns reflections of properties imported from traits.
	 *
	 * @param integer $filter Properties filter
	 * @return array
	 */
	public function getTraitProperties($filter = null)
	{
		$properties = array();

		foreach ($this->getOwnTraits() as $trait) {
			foreach ($trait->getProperties(null) as $traitProperty) {
				if (!isset($this->properties[$traitProperty->getName()]) && !isset($properties[$traitProperty->getName()])) {
					$properties[$traitProperty->getName()] = $traitProperty->alias($this);
				}
			}
		}

		if (null !== $filter) {
			$properties = array_filter($properties, function(IReflectionProperty $property) use ($filter) {
				return (bool) ($property->getModifiers() & $filter);
			});
		}

		return array_values($properties);
	}

	/**
	 * Returns default properties.
	 *
	 * @return array
	 */
	public function getDefaultProperties()
	{
		static $accessLevels = array(InternalReflectionProperty::IS_PUBLIC, InternalReflectionProperty::IS_PROTECTED, InternalReflectionProperty::IS_PRIVATE);

		$defaults = array();
		$properties = $this->getProperties();
		foreach (array(true, false) as $static) {
			foreach ($properties as $property) {
				foreach ($accessLevels as $level) {
					if ($property->isStatic() === $static && ($property->getModifiers() & $level)) {
						$defaults[$property->getName()] = $property->getDefaultValue();
					}
				}
			}
		}

		return $defaults;
	}

	/**
	 * Returns static properties reflections.
	 *
	 * @return array
	 */
	public function getStaticProperties()
	{
		$defaults = array();
		foreach ($this->getProperties(InternalReflectionProperty::IS_STATIC) as $property) {
			if ($property instanceof ReflectionProperty) {
				$defaults[$property->getName()] = $property->getDefaultValue();
			}
		}

		return $defaults;
	}

	/**
	 * Returns a value of a static property.
	 *
	 * @param string $name Property name
	 * @param mixed $default Default value
	 * @return mixed
	 * @throws \TokenReflection\Exception\RuntimeException If the requested static property does not exist.
	 * @throws \TokenReflection\Exception\RuntimeException If the requested static property is not accessible.
	 */
	public function getStaticPropertyValue($name, $default = null)
	{
		if ($this->hasProperty($name) && ($property = $this->getProperty($name)) && $property->isStatic()) {
			if (!$property->isPublic() && !$property->isAccessible()) {
				throw new Exception\RuntimeException(sprintf('Static property "%s" is not accessible.', $name), Exception\RuntimeException::NOT_ACCESSBILE, $this);
			}

			return $property->getDefaultValue();
		}

		throw new Exception\RuntimeException(sprintf('There is no static property "%s".', $name), Exception\RuntimeException::DOES_NOT_EXIST, $this);
	}

	/**
	 * Returns traits used by this class.
	 *
	 * @return array
	 */
	public function getTraits()
	{
		$traitNames = $this->getTraitNames();
		if (empty($traitNames)) {
			return array();
		}

		$broker = $this->getBroker();
		return array_combine($traitNames, array_map(function($traitName) use ($broker) {
			return $broker->getClass($traitName);
		}, $traitNames));
	}

	/**
	 * Returns traits used by this class and not its parents.
	 *
	 * @return array
	 */
	public function getOwnTraits()
	{
		$ownTraitNames = $this->getOwnTraitNames();
		if (empty($ownTraitNames)) {
			return array();
		}

		$broker = $this->getBroker();
		return array_combine($ownTraitNames, array_map(function($traitName) use ($broker) {
			return $broker->getClass($traitName);
		}, $ownTraitNames));
	}

	/**
	 * Returns names of used traits.
	 *
	 * @return array
	 */
	public function getTraitNames()
	{
		$parentClass = $this->getParentClass();

		$names = $parentClass ? $parentClass->getTraitNames() : array();
		foreach ($this->traits as $traitName) {
			$names[] = $traitName;
		}

		return array_unique($names);
	}

	/**
	 * Returns names of traits used by this class an not its parents.
	 *
	 * @return array
	 */
	public function getOwnTraitNames()
	{
		return $this->traits;
	}

	/**
	 * Returns method aliases from traits.
	 *
	 * @return array
	 */
	public function getTraitAliases()
	{
		return $this->traitAliases;
	}

	/**
	 * Returns if the class is a trait.
	 *
	 * @return boolean
	 */
	public function isTrait()
	{
		return self::IS_TRAIT === $this->type;
	}

	/**
	 * Returns if the class definition is valid.
	 *
	 * @return boolean
	 */
	public function isValid()
	{
		if (null !== $this->parentClassName && !$this->getParentClass()->isValid()) {
			return false;
		}

		foreach ($this->getInterfaces() as $interface) {
			if (!$interface->isValid()) {
				return false;
			}
		}

		foreach ($this->getTraits() as $trait) {
			if (!$trait->isValid()) {
				return false;
			}
		}

		return true;
	}

	/**
	 * Returns if the class uses a particular trait.
	 *
	 * @param \ReflectionClass|\TokenReflection\IReflectionClass|string $trait Trait reflection or name
	 * @return boolean
	 * @throws \TokenReflection\Exception\RuntimeException If an invalid parameter was provided.
	 */
	public function usesTrait($trait)
	{
		if (is_object($trait)) {
			if (!$trait instanceof InternalReflectionClass && !$trait instanceof IReflectionClass) {
				throw new Exception\RuntimeException(sprintf('Parameter must be a string or an instance of trait reflection, "%s" provided.', get_class($trait)), Exception\RuntimeException::INVALID_ARGUMENT, $this);
			}

			$traitName = $trait->getName();

			if (!$trait->isTrait()) {
				throw new Exception\RuntimeException(sprintf('"%s" is not a trait.', $traitName), Exception\RuntimeException::INVALID_ARGUMENT, $this);
			}
		} else {
			$reflection = $this->getBroker()->getClass($trait);
			if (!$reflection->isTrait()) {
				throw new Exception\RuntimeException(sprintf('"%s" is not a trait.', $trait), Exception\RuntimeException::INVALID_ARGUMENT, $this);
			}

			$traitName = $trait;
		}

		return in_array($traitName, $this->getTraitNames());
	}

	/**
	 * Returns reflections of direct subclasses.
	 *
	 * @return array
	 */
	public function getDirectSubclasses()
	{
		$that = $this->name;
		return array_filter($this->getBroker()->getClasses(), function(ReflectionClass $class) use ($that) {
			if (!$class->isSubclassOf($that)) {
				return false;
			}

			return null === $class->getParentClassName() || !$class->getParentClass()->isSubClassOf($that);
		});
	}

	/**
	 * Returns names of direct subclasses.
	 *
	 * @return array
	 */
	public function getDirectSubclassNames()
	{
		return array_keys($this->getDirectSubclasses());
	}

	/**
	 * Returns reflections of indirect subclasses.
	 *
	 * @return array
	 */
	public function getIndirectSubclasses()
	{
		$that = $this->name;
		return array_filter($this->getBroker()->getClasses(), function(ReflectionClass $class) use ($that) {
			if (!$class->isSubclassOf($that)) {
				return false;
			}

			return null !== $class->getParentClassName() && $class->getParentClass()->isSubClassOf($that);
		});
	}

	/**
	 * Returns names of indirect subclasses.
	 *
	 * @return array
	 */
	public function getIndirectSubclassNames()
	{
		return array_keys($this->getIndirectSubclasses());
	}

	/**
	 * Returns reflections of classes directly implementing this interface.
	 *
	 * @return array
	 */
	public function getDirectImplementers()
	{
		if (!$this->isInterface()) {
			return array();
		}

		$that = $this->name;
		return array_filter($this->getBroker()->getClasses(), function(ReflectionClass $class) use ($that) {
			if ($class->isInterface() || !$class->implementsInterface($that)) {
				return false;
			}

			return null === $class->getParentClassName() || !$class->getParentClass()->implementsInterface($that);
		});
	}

	/**
	 * Returns names of classes directly implementing this interface.
	 *
	 * @return array
	 */
	public function getDirectImplementerNames()
	{
		return array_keys($this->getDirectImplementers());
	}

	/**
	 * Returns reflections of classes indirectly implementing this interface.
	 *
	 * @return array
	 */
	public function getIndirectImplementers()
	{
		if (!$this->isInterface()) {
			return array();
		}

		$that = $this->name;
		return array_filter($this->getBroker()->getClasses(), function(ReflectionClass $class) use ($that) {
			if ($class->isInterface() || !$class->implementsInterface($that)) {
				return false;
			}

			return null !== $class->getParentClassName() && $class->getParentClass()->implementsInterface($that);
		});
	}

	/**
	 * Returns names of classes indirectly implementing this interface.
	 *
	 * @return array
	 */
	public function getIndirectImplementerNames()
	{
		return array_keys($this->getIndirectImplementers());
	}

	/**
	 * Returns if the given object is an instance of this class.
	 *
	 * @param object $object Instance
	 * @return boolean
	 * @throws \TokenReflection\Exception\RuntimeException If the provided argument is not an object.
	 */
	public function isInstance($object)
	{
		if (!is_object($object)) {
			throw new Exception\RuntimeException(sprintf('Parameter must be an object, "%s" provided.', gettype($object)), Exception\RuntimeException::INVALID_ARGUMENT, $this);
		}

		return $this->name === get_class($object) || is_subclass_of($object, $this->getName());
	}

	/**
	 * Creates a new class instance without using a constructor.
	 *
	 * @return object
	 * @throws \TokenReflection\Exception\RuntimeException If the class inherits from an internal class.
	 */
	public function newInstanceWithoutConstructor()
	{
		if (!class_exists($this->name, true)) {
			throw new Exception\RuntimeException('Could not create an instance; class does not exist.', Exception\RuntimeException::DOES_NOT_EXIST, $this);
		}

		$reflection = new \TokenReflection\Php\ReflectionClass($this->getName(), $this->getBroker());
		return $reflection->newInstanceWithoutConstructor();
	}

	/**
	 * Creates a new instance using variable number of parameters.
	 *
	 * Use any number of constructor parameters as function parameters.
	 *
	 * @param mixed $args
	 * @return object
	 */
	public function newInstance($args)
	{
		return $this->newInstanceArgs(func_get_args());
	}

	/**
	 * Creates a new instance using an array of parameters.
	 *
	 * @param array $args Array of constructor parameters
	 * @return object
	 * @throws \TokenReflection\Exception\RuntimeException If the required class does not exist.
	 */
	public function newInstanceArgs(array $args = array())
	{
		if (!class_exists($this->name, true)) {
			throw new Exception\RuntimeException('Could not create an instance; class does not exist.', Exception\RuntimeException::DOES_NOT_EXIST, $this);
		}

		$reflection = new InternalReflectionClass($this->name);
		return $reflection->newInstanceArgs($args);
	}

	/**
	 * Sets a static property value.
	 *
	 * @param string $name Property name
	 * @param mixed $value Property value
	 * @throws \TokenReflection\Exception\RuntimeException If the requested static property does not exist.
	 * @throws \TokenReflection\Exception\RuntimeException If the requested static property is not accessible.
	 */
	public function setStaticPropertyValue($name, $value)
	{
		if ($this->hasProperty($name) && ($property = $this->getProperty($name)) && $property->isStatic()) {
			if (!$property->isPublic() && !$property->isAccessible()) {
				throw new Exception\RuntimeException(sprintf('Static property "%s" is not accessible.', $name), Exception\RuntimeException::NOT_ACCESSBILE, $this);
			}

			$property->setDefaultValue($value);
			return;
		}

		throw new Exception\RuntimeException(sprintf('There is no static property "%s".', $name), Exception\RuntimeException::DOES_NOT_EXIST, $this);
	}

	/**
	 * Returns the string representation of the reflection object.
	 *
	 * @return string
	 */
	public function __toString()
	{
		$implements = '';
		$interfaceNames = $this->getInterfaceNames();
		if (count($interfaceNames) > 0) {
			$implements = sprintf(
				' %s %s',
				$this->isInterface() ? 'extends' : 'implements',
				implode(', ', $interfaceNames)
			);
		}

		$buffer = '';
		$count = 0;
		foreach ($this->getConstantReflections() as $constant) {
			$buffer .= '    ' . $constant->__toString();
			$count++;
		}
		$constants = sprintf("\n\n  - Constants [%d] {\n%s  }", $count, $buffer);

		$sBuffer = '';
		$sCount = 0;
		$buffer = '';
		$count = 0;
		foreach ($this->getProperties() as $property) {
			$string = '    ' . preg_replace('~\n(?!$)~', "\n    ", $property->__toString());
			if ($property->isStatic()) {
				$sBuffer .= $string;
				$sCount++;
			} else {
				$buffer .= $string;
				$count++;
			}
		}
		$staticProperties = sprintf("\n\n  - Static properties [%d] {\n%s  }", $sCount, $sBuffer);
		$properties = sprintf("\n\n  - Properties [%d] {\n%s  }", $count, $buffer);

		$sBuffer = '';
		$sCount = 0;
		$buffer = '';
		$count = 0;
		foreach ($this->getMethods() as $method) {
			// Skip private methods of parent classes
			if ($method->getDeclaringClassName() !== $this->getName() && $method->isPrivate()) {
				continue;
			}
			// Indent
			$string = "\n    ";

			$string .= preg_replace('~\n(?!$|\n|\s*\*)~', "\n    ", $method->__toString());
			// Add inherits
			if ($method->getDeclaringClassName() !== $this->getName()) {
				$string = preg_replace(
					array('~Method [ <[\w:]+~', '~, overwrites[^,]+~'),
					array('\0, inherits ' . $method->getDeclaringClassName(), ''),
					$string
				);
			}
			if ($method->isStatic()) {
				$sBuffer .= $string;
				$sCount++;
			} else {
				$buffer .= $string;
				$count++;
			}
		}
		$staticMethods = sprintf("\n\n  - Static methods [%d] {\n%s  }", $sCount, ltrim($sBuffer, "\n"));
		$methods = sprintf("\n\n  - Methods [%d] {\n%s  }", $count, ltrim($buffer, "\n"));

		return sprintf(
			"%s%s [ <user>%s %s%s%s %s%s%s ] {\n  @@ %s %d-%d%s%s%s%s%s\n}\n",
			$this->getDocComment() ? $this->getDocComment() . "\n" : '',
			$this->isInterface() ? 'Interface' : 'Class',
			$this->isIterateable() ? ' <iterateable>' : '',
			$this->isAbstract() && !$this->isInterface() ? 'abstract ' : '',
			$this->isFinal() ? 'final ' : '',
			$this->isInterface() ? 'interface' : 'class',
			$this->getName(),
			null !== $this->getParentClassName() ? ' extends ' . $this->getParentClassName() : '',
			$implements,
			$this->getFileName(),
			$this->getStartLine(),
			$this->getEndLine(),
			$constants,
			$staticProperties,
			$staticMethods,
			$properties,
			$methods
		);
	}

	/**
	 * Exports a reflected object.
	 *
	 * @param \TokenReflection\Broker $broker Broker instance
	 * @param string|object $className Class name or class instance
	 * @param boolean $return Return the export instead of outputting it
	 * @return string|null
	 * @throws \TokenReflection\Exception\RuntimeException If requested parameter doesn't exist.
	 */
	public static function export(Broker $broker, $className, $return = false)
	{
		if (is_object($className)) {
			$className = get_class($className);
		}

		$class = $broker->getClass($className);
		if ($class instanceof Invalid\ReflectionClass) {
			throw new Exception\RuntimeException('Class is invalid.', Exception\RuntimeException::UNSUPPORTED);
		} elseif ($class instanceof Dummy\ReflectionClass) {
			throw new Exception\RuntimeException('Class does not exist.', Exception\RuntimeException::DOES_NOT_EXIST);
		}

		if ($return) {
			return $class->__toString();
		}

		echo $class->__toString();
	}

	/**
	 * Returns if the class definition is complete.
	 *
	 * @return boolean
	 */
	public function isComplete()
	{
		if (!$this->definitionComplete) {
			if (null !== $this->parentClassName && !$this->getParentClass()->isComplete()) {
				return false;
			}

			foreach ($this->getOwnInterfaces() as $interface) {
				if (!$interface->isComplete()) {
					return false;
				}
			}

			$this->definitionComplete = true;
		}

		return $this->definitionComplete;
	}

	/**
	 * Returns imported namespaces and aliases from the declaring namespace.
	 *
	 * @return array
	 */
	public function getNamespaceAliases()
	{
		return $this->aliases;
	}

	/**
	 * Processes the parent reflection object.
	 *
	 * @param \TokenReflection\IReflection $parent Parent reflection object
	 * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream
	 * @return \TokenReflection\ReflectionClass
	 * @throws \TokenReflection\ParseException On invalid parent reflection provided
	 */
	protected function processParent(IReflection $parent, Stream $tokenStream)
	{
		if (!$parent instanceof ReflectionFileNamespace) {
			throw new Exception\ParseException($this, $tokenStream, sprintf('Invalid parent reflection provided: "%s".', get_class($parent)), Exception\ParseException::INVALID_PARENT);
		}

		$this->namespaceName = $parent->getName();
		$this->aliases = $parent->getNamespaceAliases();
		return parent::processParent($parent, $tokenStream);
	}

	/**
	 * Parses reflected element metadata from the token stream.
	 *
	 * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream
	 * @param \TokenReflection\IReflection $parent Parent reflection object
	 * @return \TokenReflection\ReflectionClass
	 */
	protected function parse(Stream $tokenStream, IReflection $parent)
	{
		return $this
			->parseModifiers($tokenStream)
			->parseName($tokenStream)
			->parseParent($tokenStream, $parent)
			->parseInterfaces($tokenStream, $parent);
	}

	/**
	 * Parses class modifiers (abstract, final) and class type (class, interface).
	 *
	 * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream
	 * @return \TokenReflection\ReflectionClass
	 */
	private function parseModifiers(Stream $tokenStream)
	{
		while (true) {
			switch ($tokenStream->getType()) {
				case null:
					break 2;
				case T_ABSTRACT:
					$this->modifiers = InternalReflectionClass::IS_EXPLICIT_ABSTRACT;
					break;
				case T_FINAL:
					$this->modifiers = InternalReflectionClass::IS_FINAL;
					break;
				case T_INTERFACE:
					$this->modifiers = self::IS_INTERFACE;
					$this->type = self::IS_INTERFACE;
					$tokenStream->skipWhitespaces(true);
					break 2;
				case T_TRAIT:
					$this->modifiers = self::IS_TRAIT;
					$this->type = self::IS_TRAIT;
					$tokenStream->skipWhitespaces(true);
					break 2;
				case T_CLASS:
					$tokenStream->skipWhitespaces(true);
					break 2;
				default:
					break;
			}

			$tokenStream->skipWhitespaces(true);
		}

		return $this;
	}

	/**
	 * Parses the class/interface name.
	 *
	 * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream
	 * @return \TokenReflection\ReflectionClass
	 * @throws \TokenReflection\Exception\ParseException If the class name could not be determined.
	 */
	protected function parseName(Stream $tokenStream)
	{
		if (!$tokenStream->is(T_STRING)) {
			throw new Exception\ParseException($this, $tokenStream, 'Unexpected token found.', Exception\ParseException::UNEXPECTED_TOKEN);
		}

		if ($this->namespaceName === ReflectionNamespace::NO_NAMESPACE_NAME) {
			$this->name = $tokenStream->getTokenValue();
		} else {
			$this->name = $this->namespaceName . '\\' . $tokenStream->getTokenValue();
		}

		$tokenStream->skipWhitespaces(true);

		return $this;
	}

	/**
	 * Parses the parent class.
	 *
	 * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream
	 * @param \TokenReflection\IReflection $parent Parent reflection object
	 * @return \TokenReflection\ReflectionClass
	 */
	private function parseParent(Stream $tokenStream, ReflectionElement $parent = null)
	{
		if (!$tokenStream->is(T_EXTENDS)) {
			return $this;
		}

		while (true) {
			$tokenStream->skipWhitespaces(true);

			$parentClassName = '';
			while (true) {
				switch ($tokenStream->getType()) {
					case T_STRING:
					case T_NS_SEPARATOR:
						$parentClassName .= $tokenStream->getTokenValue();
						break;
					default:
						break 2;
				}

				$tokenStream->skipWhitespaces(true);
			}

			$parentClassName = Resolver::resolveClassFQN($parentClassName, $this->aliases, $this->namespaceName);

			if ($this->isInterface()) {
				$this->interfaces[] = $parentClassName;

				if (',' === $tokenStream->getTokenValue()) {
					continue;
				}
			} else {
				$this->parentClassName = $parentClassName;
			}

			break;
		}

		return $this;
	}

	/**
	 * Parses implemented interfaces.
	 *
	 * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream
	 * @param \TokenReflection\IReflection $parent Parent reflection object
	 * @return \TokenReflection\ReflectionClass
	 * @throws \TokenReflection\Exception\ParseException On error while parsing interfaces.
	 */
	private function parseInterfaces(Stream $tokenStream, ReflectionElement $parent = null)
	{
		if (!$tokenStream->is(T_IMPLEMENTS)) {
			return $this;
		}

		if ($this->isInterface()) {
			throw new Exception\ParseException($this, $tokenStream, 'Interfaces cannot implement interfaces.', Exception\ParseException::LOGICAL_ERROR);
		}

		while (true) {
			$interfaceName = '';

			$tokenStream->skipWhitespaces(true);
			while (true) {
				switch ($tokenStream->getType()) {
					case T_STRING:
					case T_NS_SEPARATOR:
						$interfaceName .= $tokenStream->getTokenValue();
						break;
					default:
						break 2;
				}

				$tokenStream->skipWhitespaces(true);
			}

			$this->interfaces[] = Resolver::resolveClassFQN($interfaceName, $this->aliases, $this->namespaceName);

			$type = $tokenStream->getType();
			if ('{' === $type) {
				break;
			} elseif (',' !== $type) {
				throw new Exception\ParseException($this, $tokenStream, 'Unexpected token found, expected "{" or ";".', Exception\ParseException::UNEXPECTED_TOKEN);
			}
		}

		return $this;
	}

	/**
	 * Parses child reflection objects from the token stream.
	 *
	 * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream
	 * @param \TokenReflection\IReflection $parent Parent reflection object
	 * @return \TokenReflection\ReflectionClass
	 * @throws \TokenReflection\Exception\ParseException If a parse error was detected.
	 */
	protected function parseChildren(Stream $tokenStream, IReflection $parent)
	{
		while (true) {
			switch ($type = $tokenStream->getType()) {
				case null:
					break 2;
				case T_COMMENT:
				case T_DOC_COMMENT:
					$docblock = $tokenStream->getTokenValue();
					if (preg_match('~^' . preg_quote(self::DOCBLOCK_TEMPLATE_START, '~') . '~', $docblock)) {
						array_unshift($this->docblockTemplates, new ReflectionAnnotation($this, $docblock));
					} elseif (self::DOCBLOCK_TEMPLATE_END === $docblock) {
						array_shift($this->docblockTemplates);
					}
					$tokenStream->next();
					break;
				case '}':
					break 2;
				case T_PUBLIC:
				case T_PRIVATE:
				case T_PROTECTED:
				case T_STATIC:
				case T_VAR:
				case T_VARIABLE:
					static $searching = array(T_VARIABLE => true, T_FUNCTION => true);

					if (T_VAR !== $tokenStream->getType()) {
						$position = $tokenStream->key();
						while (null !== ($type = $tokenStream->getType($position)) && !isset($searching[$type])) {
							$position++;
						}
					}

					if (T_VARIABLE === $type || T_VAR === $type) {
						$property = new ReflectionProperty($tokenStream, $this->getBroker(), $this);
						$this->properties[$property->getName()] = $property;
						$tokenStream->next();
						break;
					}
					// Break missing on purpose
				case T_FINAL:
				case T_ABSTRACT:
				case T_FUNCTION:
					$method = new ReflectionMethod($tokenStream, $this->getBroker(), $this);
					$this->methods[$method->getName()] = $method;
					$tokenStream->next();
					break;
				case T_CONST:
					$tokenStream->skipWhitespaces(true);
					while ($tokenStream->is(T_STRING)) {
						$constant = new ReflectionConstant($tokenStream, $this->getBroker(), $this);
						$this->constants[$constant->getName()] = $constant;
						if ($tokenStream->is(',')) {
							$tokenStream->skipWhitespaces(true);
						} else {
							$tokenStream->next();
						}
					}
					break;
				case T_USE:
					$tokenStream->skipWhitespaces(true);

					while (true) {
						$traitName = '';
						$type = $tokenStream->getType();
						while (T_STRING === $type || T_NS_SEPARATOR === $type) {
							$traitName .= $tokenStream->getTokenValue();
							$type = $tokenStream->skipWhitespaces(true)->getType();
						}

						if ('' === trim($traitName, '\\')) {
							throw new Exception\ParseException($this, $tokenStream, 'An empty trait name found.', Exception\ParseException::LOGICAL_ERROR);
						}

						$this->traits[] = Resolver::resolveClassFQN($traitName, $this->aliases, $this->namespaceName);

						if (';' === $type) {
							// End of "use"
							$tokenStream->skipWhitespaces();
							break;
						} elseif (',' === $type) {
							// Next trait name follows
							$tokenStream->skipWhitespaces();
							continue;
						} elseif ('{' !== $type) {
							// Unexpected token
							throw new Exception\ParseException($this, $tokenStream, 'Unexpected token found: "%s".', Exception\ParseException::UNEXPECTED_TOKEN);
						}

						// Aliases definition
						$type = $tokenStream->skipWhitespaces(true)->getType();
						while (true) {
							if ('}' === $type) {
								$tokenStream->skipWhitespaces();
								break 2;
							}

							$leftSide = '';
							$rightSide = array('', null);
							$alias = true;

							while (T_STRING === $type || T_NS_SEPARATOR === $type || T_DOUBLE_COLON === $type) {
								$leftSide .= $tokenStream->getTokenValue();
								$type = $tokenStream->skipWhitespaces(true)->getType();
							}

							if (T_INSTEADOF === $type) {
								$alias = false;
							} elseif (T_AS !== $type) {
								throw new Exception\ParseException($this, $tokenStream, 'Unexpected token found.', Exception\ParseException::UNEXPECTED_TOKEN);
							}

							$type = $tokenStream->skipWhitespaces(true)->getType();

							if (T_PUBLIC === $type || T_PROTECTED === $type || T_PRIVATE === $type) {
								if (!$alias) {
									throw new Exception\ParseException($this, $tokenStream, 'Unexpected token found.', Exception\ParseException::UNEXPECTED_TOKEN);
								}

								switch ($type) {
									case T_PUBLIC:
										$type = InternalReflectionMethod::IS_PUBLIC;
										break;
									case T_PROTECTED:
										$type = InternalReflectionMethod::IS_PROTECTED;
										break;
									case T_PRIVATE:
										$type = InternalReflectionMethod::IS_PRIVATE;
										break;
									default:
										break;
								}

								$rightSide[1] = $type;
								$type = $tokenStream->skipWhitespaces(true)->getType();
							}

							while (T_STRING === $type || (T_NS_SEPARATOR === $type && !$alias)) {
								$rightSide[0] .= $tokenStream->getTokenValue();
								$type = $tokenStream->skipWhitespaces(true)->getType();
							}

							if (empty($leftSide)) {
								throw new Exception\ParseException($this, $tokenStream, 'An empty method name was found.', Exception\ParseException::LOGICAL_ERROR);
							}

							if ($alias) {
								// Alias
								if ($pos = strpos($leftSide, '::')) {
									$methodName = substr($leftSide, $pos + 2);
									$className = Resolver::resolveClassFQN(substr($leftSide, 0, $pos), $this->aliases, $this->namespaceName);
									$leftSide = $className . '::' . $methodName;

									$this->traitAliases[$rightSide[0]] = $leftSide;
								} else {
									$this->traitAliases[$rightSide[0]] = '(null)::' . $leftSide;
								}

								$this->traitImports[$leftSide][] = $rightSide;
							} else {
								// Insteadof
								if ($pos = strpos($leftSide, '::')) {
									$methodName = substr($leftSide, $pos + 2);
								} else {
									throw new Exception\ParseException($this, $tokenStream, 'A T_DOUBLE_COLON has to be present when using T_INSTEADOF.', Exception\ParseException::UNEXPECTED_TOKEN);
								}

								$this->traitImports[Resolver::resolveClassFQN($rightSide[0], $this->aliases, $this->namespaceName) . '::' . $methodName][] = null;
							}

							if (',' === $type) {
								$tokenStream->skipWhitespaces(true);
								continue;
							} elseif (';' !== $type) {
								throw new Exception\ParseException($this, $tokenStream, 'Unexpected token found.', Exception\ParseException::UNEXPECTED_TOKEN);
							}

							$type = $tokenStream->skipWhitespaces()->getType();
						}
					}

					break;
				default:
					$tokenStream->next();
					break;
			}
		}

		return $this;
	}
}
