/**
  * Attempts to dispatch the supplied Route object
  * 
  * @param \THCFrame\Router\Route $route
  * @throws Exception\Module
  * @throws Exception\Controller
  * @throws Exception\Action
  */
 public function dispatch(\THCFrame\Router\Route $route)
 {
     $module = trim($route->getModule());
     $class = trim($route->getController());
     $action = trim($route->getAction());
     $parameters = $route->getMapArguments();
     if ('' === $module) {
         throw new Exception\Module('Module Name not specified');
     } elseif ('' === $class) {
         throw new Exception\Controller('Class Name not specified');
     } elseif ('' === $action) {
         throw new Exception\Action('Method Name not specified');
     }
     $status = $this->loadConfigFromDb($module . 'status');
     if ($status !== null && $status != 1) {
         throw new Exception\Offline('Application is offline');
     }
     $module = str_replace('\\', '', $module);
     preg_match('/^[a-zA-Z0-9_]+$/', $module, $matches);
     if (count($matches) !== 1) {
         throw new Exception\Module(sprintf('Disallowed characters in module name %s', $module));
     }
     $class = str_replace('\\', '', $class);
     preg_match('/^[a-zA-Z0-9_]+$/', $class, $matches);
     if (count($matches) !== 1) {
         throw new Exception\Controller(sprintf('Disallowed characters in class name %s', $class));
     }
     $file_name = strtolower("./modules/{$module}/controller/{$class}.php");
     $class = ucfirst($module) . '_Controller_' . ucfirst($class);
     if (FALSE === file_exists($file_name)) {
         throw new Exception\Controller(sprintf('Class file %s not found', $file_name));
     } else {
         require_once $file_name;
     }
     $this->_activeModule = $module;
     Event::fire('framework.dispatcher.controller.before', array($class, $parameters));
     try {
         $instance = new $class(array('parameters' => $parameters));
         Registry::set('controller', $instance);
     } catch (\Exception $e) {
         throw new Exception\Controller(sprintf('Controller %s error: %s', $class, $e->getMessage()));
     }
     Event::fire('framework.dispatcher.controller.after', array($class, $parameters));
     if (!method_exists($instance, $action)) {
         $instance->willRenderLayoutView = false;
         $instance->willRenderActionView = false;
         throw new Exception\Action(sprintf('Action %s not found', $action));
     }
     $inspector = new Inspector($instance);
     $methodMeta = $inspector->getMethodMeta($action);
     if (!empty($methodMeta['@protected']) || !empty($methodMeta['@private'])) {
         throw new Exception\Action(sprintf('Action %s not found', $action));
     }
     $hooks = function ($meta, $type) use($inspector, $instance) {
         if (isset($meta[$type])) {
             $run = array();
             foreach ($meta[$type] as $method) {
                 $hookMeta = $inspector->getMethodMeta($method);
                 if (in_array($method, $run) && !empty($hookMeta['@once'])) {
                     continue;
                 }
                 $instance->{$method}();
                 $run[] = $method;
             }
         }
     };
     Event::fire('framework.dispatcher.beforehooks.before', array($action, $parameters));
     $hooks($methodMeta, '@before');
     Event::fire('framework.dispatcher.beforehooks.after', array($action, $parameters));
     Event::fire('framework.dispatcher.action.before', array($action, $parameters));
     call_user_func_array(array($instance, $action), is_array($parameters) ? $parameters : array());
     Event::fire('framework.dispatcher.action.after', array($action, $parameters));
     Event::fire('framework.dispatcher.afterhooks.before', array($action, $parameters));
     $hooks($methodMeta, '@after');
     Event::fire('framework.dispatcher.afterhooks.after', array($action, $parameters));
     // unset controller
     Registry::erase('controller');
 }
 /**
  * There are four basic parts to our __call() method: 
  * checking to see that the inspector is set, 
  * handling the getProperty() methods, handling the setProperty() methods and 
  * handling the unsProperty() methods
  * 
  * @param string $name
  * @param string $arguments
  * @return null|\THCFrame\Core\Base
  * @throws Exception
  */
 public function __call($name, $arguments)
 {
     if (empty($this->_inspector)) {
         throw new Exception('Call parent::__construct!');
     }
     $getMatches = StringMethods::match($name, '#^get([a-zA-Z0-9_]+)$#');
     if (count($getMatches) > 0) {
         $normalized = lcfirst($getMatches[0]);
         $property = "_{$normalized}";
         if (property_exists($this, $property)) {
             $meta = $this->_inspector->getPropertyMeta($property);
             if (empty($meta['@readwrite']) && empty($meta['@read'])) {
                 throw $this->_getWriteonlyException($normalized);
             }
             unset($meta);
             if (isset($this->{$property})) {
                 return $this->{$property};
             } else {
                 return null;
             }
         } elseif (array_key_exists($normalized, $this->_dataStore)) {
             return $this->_dataStore[$normalized];
         } else {
             return null;
         }
     }
     unset($getMatches);
     $setMatches = StringMethods::match($name, '#^set([a-zA-Z0-9_]+)$#');
     if (count($setMatches) > 0) {
         $normalized = lcfirst($setMatches[0]);
         $property = "_{$normalized}";
         if (property_exists($this, $property)) {
             $meta = $this->_inspector->getPropertyMeta($property);
             if (empty($meta['@readwrite']) && empty($meta['@write'])) {
                 throw $this->_getReadonlyException($normalized);
             }
             unset($meta);
             $this->{$property} = $arguments[0];
             return $this;
         } else {
             //if variable is not class property its stored into _dataStore array
             $this->_dataStore[$normalized] = $arguments[0];
             return $this;
         }
     }
     unset($setMatches);
     $unsetMatches = StringMethods::match($name, '#^uns([a-zA-Z0-9_]+)$#');
     if (count($unsetMatches) > 0) {
         $normalized = lcfirst($setMatches[0]);
         $property = "_{$normalized}";
         if (property_exists($this, $property)) {
             $meta = $this->_inspector->getPropertyMeta($property);
             if (empty($meta['@readwrite']) && empty($meta['@write'])) {
                 throw $this->_getReadonlyException($normalized);
             }
             unset($meta);
             unset($this->{$property});
             return $this;
         } else {
             unset($this->_dataStore[$normalized]);
             return $this;
         }
     }
     unset($unsetMatches);
     throw $this->_getImplementationException($name);
 }
 /**
  * Method creates an Inspector instance and a utility function ($first) to return the
  * first item in a metadata array. Next, it loops through all the properties in the model, 
  * and sifts out all that have an @column flag. Any other properties are ignored at this point.
  * The column’s @type flag is checked to make sure it is valid, raising a 
  * Model\Exception\Type in the event that it is not. If the column’s type is valid, 
  * it is added to the $_columns property. Every valid $primary column leads to the 
  * incrementing of the $primaries variable, which is checked at the end 
  * of the method to make sure that exactly one primary column has been defined. 
  * In essence, this method takes the User model definition and returns an associative array of column data
  * 
  * @return array
  * @throws Exception\Type
  * @throws Exception\Primary
  */
 public function getColumns()
 {
     if (empty($this->_columns)) {
         $primaries = 0;
         $columns = array();
         $class = get_class($this);
         $types = $this->_types;
         $inspector = new Inspector($this);
         $properties = $inspector->getClassProperties();
         $first = function ($array, $key) {
             if (!empty($array[$key]) && count($array[$key]) == 1) {
                 return $array[$key][0];
             }
             return null;
         };
         foreach ($properties as $property) {
             $propertyMeta = $inspector->getPropertyMeta($property);
             if (!empty($propertyMeta['@column'])) {
                 $name = preg_replace('#^_#', '', $property);
                 $primary = !empty($propertyMeta['@primary']);
                 $type = $first($propertyMeta, '@type');
                 $length = $first($propertyMeta, '@length');
                 $index = !empty($propertyMeta['@index']);
                 $unique = !empty($propertyMeta['@unique']);
                 $readwrite = !empty($propertyMeta['@readwrite']);
                 $read = !empty($propertyMeta['@read']) || $readwrite;
                 $write = !empty($propertyMeta['@write']) || $readwrite;
                 $validate = !empty($propertyMeta['@validate']) ? $propertyMeta['@validate'] : false;
                 $label = $first($propertyMeta, '@label');
                 if (!in_array($type, $types)) {
                     throw new Exception\Type(sprintf('%s is not a valid type', $type));
                 }
                 if ($primary) {
                     $primaries++;
                 }
                 $columns[$name] = array('raw' => $property, 'name' => $name, 'primary' => $primary, 'type' => $type, 'length' => $length, 'index' => $index, 'unique' => $unique, 'read' => $read, 'write' => $write, 'validate' => $validate, 'label' => $label);
             }
         }
         if ($primaries !== 1) {
             throw new Exception\Primary(sprintf('%s must have exactly one @primary column', $primary));
         }
         $this->_columns = $columns;
     }
     return $this->_columns;
 }