web/lib/Zend/Application/Bootstrap/BootstrapAbstract.php
author Yves-Marie Haussonne <1218002+ymph@users.noreply.github.com>
Tue, 09 Aug 2011 12:41:37 +0200
changeset 245 4c953ca2aa1d
parent 207 621fa6caec0c
child 807 877f952ae2bd
permissions -rw-r--r--
commit .project

<?php
/**
 * Zend Framework
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://framework.zend.com/license/new-bsd
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to license@zend.com so we can send you a copy immediately.
 *
 * @category   Zend
 * @package    Zend_Application
 * @subpackage Bootstrap
 * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 * @version    $Id: BootstrapAbstract.php 23278 2010-10-30 12:50:21Z ramon $
 */

/**
 * Abstract base class for bootstrap classes
 *
 * @uses       Zend_Application_Bootstrap_Bootstrapper
 * @uses       Zend_Application_Bootstrap_ResourceBootstrapper
 * @category   Zend
 * @package    Zend_Application
 * @subpackage Bootstrap
 * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 */
abstract class Zend_Application_Bootstrap_BootstrapAbstract
    implements Zend_Application_Bootstrap_Bootstrapper,
               Zend_Application_Bootstrap_ResourceBootstrapper
{
    /**
     * @var Zend_Application|Zend_Application_Bootstrap_Bootstrapper
     */
    protected $_application;

    /**
     * @var array Internal resource methods (resource/method pairs)
     */
    protected $_classResources;

    /**
     * @var object Resource container
     */
    protected $_container;

    /**
     * @var string
     */
    protected $_environment;

    /**
     * Flattened (lowercase) option keys used for lookups
     *
     * @var array
     */
    protected $_optionKeys = array();

    /**
     * @var array
     */
    protected $_options = array();

    /**
     * @var Zend_Loader_PluginLoader_Interface
     */
    protected $_pluginLoader;

    /**
     * @var array Class-based resource plugins
     */
    protected $_pluginResources = array();

    /**
     * @var array Initializers that have been run
     */
    protected $_run = array();

    /**
     * @var array Initializers that have been started but not yet completed (circular dependency detection)
     */
    protected $_started = array();

    /**
     * Constructor
     *
     * Sets application object, initializes options, and prepares list of
     * initializer methods.
     *
     * @param  Zend_Application|Zend_Application_Bootstrap_Bootstrapper $application
     * @return void
     * @throws Zend_Application_Bootstrap_Exception When invalid application is provided
     */
    public function __construct($application)
    {
        $this->setApplication($application);
        $options = $application->getOptions();
        $this->setOptions($options);
    }

    /**
     * Set class state
     *
     * @param  array $options
     * @return Zend_Application_Bootstrap_BootstrapAbstract
     */
    public function setOptions(array $options)
    {
        $this->_options = $this->mergeOptions($this->_options, $options);

        $options = array_change_key_case($options, CASE_LOWER);
        $this->_optionKeys = array_merge($this->_optionKeys, array_keys($options));

        $methods = get_class_methods($this);
        foreach ($methods as $key => $method) {
            $methods[$key] = strtolower($method);
        }

        if (array_key_exists('pluginpaths', $options)) {
            $pluginLoader = $this->getPluginLoader();

            foreach ($options['pluginpaths'] as $prefix => $path) {
                $pluginLoader->addPrefixPath($prefix, $path);
            }
            unset($options['pluginpaths']);
        }

        foreach ($options as $key => $value) {
            $method = 'set' . strtolower($key);

            if (in_array($method, $methods)) {
                $this->$method($value);
            } elseif ('resources' == $key) {
                foreach ($value as $resource => $resourceOptions) {
                    $this->registerPluginResource($resource, $resourceOptions);
                }
            }
        }
        return $this;
    }

    /**
     * Get current options from bootstrap
     *
     * @return array
     */
    public function getOptions()
    {
        return $this->_options;
    }

    /**
     * Is an option present?
     *
     * @param  string $key
     * @return bool
     */
    public function hasOption($key)
    {
        return in_array(strtolower($key), $this->_optionKeys);
    }

    /**
     * Retrieve a single option
     *
     * @param  string $key
     * @return mixed
     */
    public function getOption($key)
    {
        if ($this->hasOption($key)) {
            $options = $this->getOptions();
            $options = array_change_key_case($options, CASE_LOWER);
            return $options[strtolower($key)];
        }
        return null;
    }

    /**
     * Merge options recursively
     *
     * @param  array $array1
     * @param  mixed $array2
     * @return array
     */
    public function mergeOptions(array $array1, $array2 = null)
    {
        if (is_array($array2)) {
            foreach ($array2 as $key => $val) {
                if (is_array($array2[$key])) {
                    $array1[$key] = (array_key_exists($key, $array1) && is_array($array1[$key]))
                                  ? $this->mergeOptions($array1[$key], $array2[$key])
                                  : $array2[$key];
                } else {
                    $array1[$key] = $val;
                }
            }
        }
        return $array1;
    }

    /**
     * Get class resources (as resource/method pairs)
     *
     * Uses get_class_methods() by default, reflection on prior to 5.2.6,
     * as a bug prevents the usage of get_class_methods() there.
     *
     * @return array
     */
    public function getClassResources()
    {
        if (null === $this->_classResources) {
            if (version_compare(PHP_VERSION, '5.2.6') === -1) {
                $class        = new ReflectionObject($this);
                $classMethods = $class->getMethods();
                $methodNames  = array();

                foreach ($classMethods as $method) {
                    $methodNames[] = $method->getName();
                }
            } else {
                $methodNames = get_class_methods($this);
            }

            $this->_classResources = array();
            foreach ($methodNames as $method) {
                if (5 < strlen($method) && '_init' === substr($method, 0, 5)) {
                    $this->_classResources[strtolower(substr($method, 5))] = $method;
                }
            }
        }

        return $this->_classResources;
    }

    /**
     * Get class resource names
     *
     * @return array
     */
    public function getClassResourceNames()
    {
        $resources = $this->getClassResources();
        return array_keys($resources);
    }

    /**
     * Register a new resource plugin
     *
     * @param  string|Zend_Application_Resource_Resource $resource
     * @param  mixed  $options
     * @return Zend_Application_Bootstrap_BootstrapAbstract
     * @throws Zend_Application_Bootstrap_Exception When invalid resource is provided
     */
    public function registerPluginResource($resource, $options = null)
    {
        if ($resource instanceof Zend_Application_Resource_Resource) {
            $resource->setBootstrap($this);
            $pluginName = $this->_resolvePluginResourceName($resource);
            $this->_pluginResources[$pluginName] = $resource;
            return $this;
        }

        if (!is_string($resource)) {
            throw new Zend_Application_Bootstrap_Exception('Invalid resource provided to ' . __METHOD__);
        }

        $this->_pluginResources[$resource] = $options;
        return $this;
    }

    /**
     * Unregister a resource from the bootstrap
     *
     * @param  string|Zend_Application_Resource_Resource $resource
     * @return Zend_Application_Bootstrap_BootstrapAbstract
     * @throws Zend_Application_Bootstrap_Exception When unknown resource type is provided
     */
    public function unregisterPluginResource($resource)
    {
        if ($resource instanceof Zend_Application_Resource_Resource) {
            if ($index = array_search($resource, $this->_pluginResources, true)) {
                unset($this->_pluginResources[$index]);
            }
            return $this;
        }

        if (!is_string($resource)) {
            throw new Zend_Application_Bootstrap_Exception('Unknown resource type provided to ' . __METHOD__);
        }

        $resource = strtolower($resource);
        if (array_key_exists($resource, $this->_pluginResources)) {
            unset($this->_pluginResources[$resource]);
        }

        return $this;
    }

    /**
     * Is the requested plugin resource registered?
     *
     * @param  string $resource
     * @return bool
     */
    public function hasPluginResource($resource)
    {
        return (null !== $this->getPluginResource($resource));
    }

    /**
     * Get a registered plugin resource
     *
     * @param  string $resourceName
     * @return Zend_Application_Resource_Resource
     */
    public function getPluginResource($resource)
    {
        if (array_key_exists(strtolower($resource), $this->_pluginResources)) {
            $resource = strtolower($resource);
            if (!$this->_pluginResources[$resource] instanceof Zend_Application_Resource_Resource) {
                $resourceName = $this->_loadPluginResource($resource, $this->_pluginResources[$resource]);
                if (!$resourceName) {
                    throw new Zend_Application_Bootstrap_Exception(sprintf('Unable to resolve plugin "%s"; no corresponding plugin with that name', $resource));
                }
                $resource = $resourceName;
            }
            return $this->_pluginResources[$resource];
        }

        foreach ($this->_pluginResources as $plugin => $spec) {
            if ($spec instanceof Zend_Application_Resource_Resource) {
                $pluginName = $this->_resolvePluginResourceName($spec);
                if (0 === strcasecmp($resource, $pluginName)) {
                    unset($this->_pluginResources[$plugin]);
                    $this->_pluginResources[$pluginName] = $spec;
                    return $spec;
                }
                continue;
            }

            if (false !== $pluginName = $this->_loadPluginResource($plugin, $spec)) {
                if (0 === strcasecmp($resource, $pluginName)) {
                    return $this->_pluginResources[$pluginName];
                }
                continue;
            }

            if (class_exists($plugin)) { //@SEE ZF-7550
                $spec = (array) $spec;
                $spec['bootstrap'] = $this;
                $instance = new $plugin($spec);
                $pluginName = $this->_resolvePluginResourceName($instance);
                unset($this->_pluginResources[$plugin]);
                $this->_pluginResources[$pluginName] = $instance;

                if (0 === strcasecmp($resource, $pluginName)) {
                    return $instance;
                }
            }
        }

        return null;
    }

    /**
     * Retrieve all plugin resources
     *
     * @return array
     */
    public function getPluginResources()
    {
        foreach (array_keys($this->_pluginResources) as $resource) {
            $this->getPluginResource($resource);
        }
        return $this->_pluginResources;
    }

    /**
     * Retrieve plugin resource names
     *
     * @return array
     */
    public function getPluginResourceNames()
    {
        $this->getPluginResources();
        return array_keys($this->_pluginResources);
    }

    /**
     * Set plugin loader for loading resources
     *
     * @param  Zend_Loader_PluginLoader_Interface $loader
     * @return Zend_Application_Bootstrap_BootstrapAbstract
     */
    public function setPluginLoader(Zend_Loader_PluginLoader_Interface $loader)
    {
        $this->_pluginLoader = $loader;
        return $this;
    }

    /**
     * Get the plugin loader for resources
     *
     * @return Zend_Loader_PluginLoader_Interface
     */
    public function getPluginLoader()
    {
        if ($this->_pluginLoader === null) {
            $options = array(
                'Zend_Application_Resource' => 'Zend/Application/Resource'
            );

            $this->_pluginLoader = new Zend_Loader_PluginLoader($options);
        }

        return $this->_pluginLoader;
    }

    /**
     * Set application/parent bootstrap
     *
     * @param  Zend_Application|Zend_Application_Bootstrap_Bootstrapper $application
     * @return Zend_Application_Bootstrap_BootstrapAbstract
     */
    public function setApplication($application)
    {
        if (($application instanceof Zend_Application)
            || ($application instanceof Zend_Application_Bootstrap_Bootstrapper)
        ) {
            if ($application === $this) {
                throw new Zend_Application_Bootstrap_Exception('Cannot set application to same object; creates recursion');
            }
            $this->_application = $application;
        } else {
            throw new Zend_Application_Bootstrap_Exception('Invalid application provided to bootstrap constructor (received "' . get_class($application) . '" instance)');
        }
        return $this;
    }

    /**
     * Retrieve parent application instance
     *
     * @return Zend_Application|Zend_Application_Bootstrap_Bootstrapper
     */
    public function getApplication()
    {
        return $this->_application;
    }

    /**
     * Retrieve application environment
     *
     * @return string
     */
    public function getEnvironment()
    {
        if (null === $this->_environment) {
            $this->_environment = $this->getApplication()->getEnvironment();
        }
        return $this->_environment;
    }

    /**
     * Set resource container
     *
     * By default, if a resource callback has a non-null return value, this
     * value will be stored in a container using the resource name as the
     * key.
     *
     * Containers must be objects, and must allow setting public properties.
     *
     * @param  object $container
     * @return Zend_Application_Bootstrap_BootstrapAbstract
     */
    public function setContainer($container)
    {
        if (!is_object($container)) {
            throw new Zend_Application_Bootstrap_Exception('Resource containers must be objects');
        }
        $this->_container = $container;
        return $this;
    }

    /**
     * Retrieve resource container
     *
     * @return object
     */
    public function getContainer()
    {
        if (null === $this->_container) {
            $this->setContainer(new Zend_Registry());
        }
        return $this->_container;
    }

    /**
     * Determine if a resource has been stored in the container
     *
     * During bootstrap resource initialization, you may return a value. If
     * you do, it will be stored in the {@link setContainer() container}.
     * You can use this method to determine if a value was stored.
     *
     * @param  string $name
     * @return bool
     */
    public function hasResource($name)
    {
        $resource  = strtolower($name);
        $container = $this->getContainer();
        return isset($container->{$resource});
    }

    /**
     * Retrieve a resource from the container
     *
     * During bootstrap resource initialization, you may return a value. If
     * you do, it will be stored in the {@link setContainer() container}.
     * You can use this method to retrieve that value.
     *
     * If no value was returned, this will return a null value.
     *
     * @param  string $name
     * @return null|mixed
     */
    public function getResource($name)
    {
        $resource  = strtolower($name);
        $container = $this->getContainer();
        if ($this->hasResource($resource)) {
            return $container->{$resource};
        }
        return null;
    }

    /**
     * Implement PHP's magic to retrieve a ressource
     * in the bootstrap
     *
     * @param string $prop
     * @return null|mixed
     */
    public function __get($prop)
    {
        return $this->getResource($prop);
    }

    /**
     * Implement PHP's magic to ask for the
     * existence of a ressource in the bootstrap
     *
     * @param string $prop
     * @return bool
     */
    public function __isset($prop)
    {
        return $this->hasResource($prop);
    }

    /**
     * Bootstrap individual, all, or multiple resources
     *
     * Marked as final to prevent issues when subclassing and naming the
     * child class 'Bootstrap' (in which case, overriding this method
     * would result in it being treated as a constructor).
     *
     * If you need to override this functionality, override the
     * {@link _bootstrap()} method.
     *
     * @param  null|string|array $resource
     * @return Zend_Application_Bootstrap_BootstrapAbstract
     * @throws Zend_Application_Bootstrap_Exception When invalid argument was passed
     */
    final public function bootstrap($resource = null)
    {
        $this->_bootstrap($resource);
        return $this;
    }

    /**
     * Overloading: intercept calls to bootstrap<resourcename>() methods
     *
     * @param  string $method
     * @param  array  $args
     * @return void
     * @throws Zend_Application_Bootstrap_Exception On invalid method name
     */
    public function __call($method, $args)
    {
        if (9 < strlen($method) && 'bootstrap' === substr($method, 0, 9)) {
            $resource = substr($method, 9);
            return $this->bootstrap($resource);
        }

        throw new Zend_Application_Bootstrap_Exception('Invalid method "' . $method . '"');
    }

    /**
     * Bootstrap implementation
     *
     * This method may be overridden to provide custom bootstrapping logic.
     * It is the sole method called by {@link bootstrap()}.
     *
     * @param  null|string|array $resource
     * @return void
     * @throws Zend_Application_Bootstrap_Exception When invalid argument was passed
     */
    protected function _bootstrap($resource = null)
    {
        if (null === $resource) {
            foreach ($this->getClassResourceNames() as $resource) {
                $this->_executeResource($resource);
            }

            foreach ($this->getPluginResourceNames() as $resource) {
                $this->_executeResource($resource);
            }
        } elseif (is_string($resource)) {
            $this->_executeResource($resource);
        } elseif (is_array($resource)) {
            foreach ($resource as $r) {
                $this->_executeResource($r);
            }
        } else {
            throw new Zend_Application_Bootstrap_Exception('Invalid argument passed to ' . __METHOD__);
        }
    }

    /**
     * Execute a resource
     *
     * Checks to see if the resource has already been run. If not, it searches
     * first to see if a local method matches the resource, and executes that.
     * If not, it checks to see if a plugin resource matches, and executes that
     * if found.
     *
     * Finally, if not found, it throws an exception.
     *
     * @param  string $resource
     * @return void
     * @throws Zend_Application_Bootstrap_Exception When resource not found
     */
    protected function _executeResource($resource)
    {
        $resourceName = strtolower($resource);

        if (in_array($resourceName, $this->_run)) {
            return;
        }

        if (isset($this->_started[$resourceName]) && $this->_started[$resourceName]) {
            throw new Zend_Application_Bootstrap_Exception('Circular resource dependency detected');
        }

        $classResources = $this->getClassResources();
        if (array_key_exists($resourceName, $classResources)) {
            $this->_started[$resourceName] = true;
            $method = $classResources[$resourceName];
            $return = $this->$method();
            unset($this->_started[$resourceName]);
            $this->_markRun($resourceName);

            if (null !== $return) {
                $this->getContainer()->{$resourceName} = $return;
            }

            return;
        }

        if ($this->hasPluginResource($resource)) {
            $this->_started[$resourceName] = true;
            $plugin = $this->getPluginResource($resource);
            $return = $plugin->init();
            unset($this->_started[$resourceName]);
            $this->_markRun($resourceName);

            if (null !== $return) {
                $this->getContainer()->{$resourceName} = $return;
            }

            return;
        }

        throw new Zend_Application_Bootstrap_Exception('Resource matching "' . $resource . '" not found');
    }

    /**
     * Load a plugin resource
     *
     * @param  string $resource
     * @param  array|object|null $options
     * @return string|false
     */
    protected function _loadPluginResource($resource, $options)
    {
        $options   = (array) $options;
        $options['bootstrap'] = $this;
        $className = $this->getPluginLoader()->load(strtolower($resource), false);

        if (!$className) {
            return false;
        }

        $instance = new $className($options);

        unset($this->_pluginResources[$resource]);

        if (isset($instance->_explicitType)) {
            $resource = $instance->_explicitType;
        }
        $resource = strtolower($resource);
        $this->_pluginResources[$resource] = $instance;

        return $resource;
    }

    /**
     * Mark a resource as having run
     *
     * @param  string $resource
     * @return void
     */
    protected function _markRun($resource)
    {
        if (!in_array($resource, $this->_run)) {
            $this->_run[] = $resource;
        }
    }

    /**
     * Resolve a plugin resource name
     *
     * Uses, in order of preference
     * - $_explicitType property of resource
     * - Short name of resource (if a matching prefix path is found)
     * - class name (if none of the above are true)
     *
     * The name is then cast to lowercase.
     *
     * @param  Zend_Application_Resource_Resource $resource
     * @return string
     */
    protected function _resolvePluginResourceName($resource)
    {
        if (isset($resource->_explicitType)) {
            $pluginName = $resource->_explicitType;
        } else  {
            $className  = get_class($resource);
            $pluginName = $className;
            $loader     = $this->getPluginLoader();
            foreach ($loader->getPaths() as $prefix => $paths) {
                if (0 === strpos($className, $prefix)) {
                    $pluginName = substr($className, strlen($prefix));
                    $pluginName = trim($pluginName, '_');
                    break;
                }
            }
        }
        $pluginName = strtolower($pluginName);
        return $pluginName;
    }
}
󪯰p8zUTT/(~d?B 0}U6ʴU!LnEHV2TX=**!W0d]O1ZU*px(@?eId>0b,%` +PEhd0jVC!`¬$gUdN*eYV  W¬]V\U`.C!j\_8tH:+h3U`e35Ux*`b, PʭC!,X=!U?0/Q0|^nuY\kJjĦuE5UY8ҚSp0e]eACPP\L:"b,U*Y2C!~z*}"V 2E0`?%!?,?_<CT IeUC"`CsxNEWdq~~V*낮W`Unhlle*9AUnA2U9{tR- C2y U,"L2E%ՙ  b # 0`C\DYPW"V`2Gt"20h4044?|7Uv˼{)@-AU.:CjV]#{)ؐHv 0`D:ݫ.?3,X3)W9ͷ79Vʮ*r@ j?[*`!V E*ZVU` P)48- 20` UjZVʬ8V)5C!ʪj"20`UEX``W?40d=GCC0`jeZi)US*bR*G%O|ħ8t0a*ZT?H 7CO0`/0V  IQAC'ix0` λq $YaߑC8yW里7I)ř֡Uu8Ed]!C(l[pjCz)."tC!!tį`VPJbʲ*U 'VX0`ʪe 0eUd0`M` T0CXUU_7 SV )5H -kok]!IJio^i$U'i2xgC!c/91 ÜEG{v<ʾUU`8[p 0`w 5E} X1IV [E+P"V RlhhV CƅVW9 _ >K+;d0` 0` 0` 0` 0` 0` 0` 0`į}zBK]HUjO:%x4 0`VPx«/ BCdq}U^YUllw U2ClhhhhhhhhhwGAU}ILHe#*t*]*%2VEC*Ud45 E(`UY 44$zqCCjT:D+""Q'F }]#z6m蔔.-H5TtTIsԕ8qb.Fq7UW9qzdᱱP"ʭ F8d2,d! Cf(d n0xnr;0d6;p68#y4=GA`tFUeWZ*Wd\ Cp626: Qq#;CCc6: ##Cx  #`taYU֫* *U#cCc#CC69=bVJU^7J d5 ZCpʫpȕ d[4W{WW۠`js33#?xAdU>o+U\]a2 Ԕ r5^UZʸ qV ! u[R|ƆC0d47U8EjE5 p5C\Ak- .sT2Bc =j^d<1`5 W p>aUUX444ʭUux 0{UheVXj1+PI}5U􌇰+}3^,P!di¼ajh_=9KAچ%Y" IEEC!ʇxheV"ʭ \hsۈxE \^c0`Urޗsʹ\rp9VU~xx(}cU_Si|ZRF#Uh;PQ| ``GzV`8īsC"%/YEUbO#*ʴCY *!iTOܯ(~0sG#}/D?*/0`{D]!~U ?hdU *߮`tuҭo{7]9$/U ?C̆Za.!%sHjeW2!*Gb٫*l`hbvU%4 * V >aS *K_އC  tD2"b, W:  U}9cr2Uz{ *X 0aTd>W†<`m`lp5X20{Uְj`v ,0` 0` 0e{P9 pH2CC5Vԋ!WZʸU>F Hdvʲ VWAe_|o[_[ϐ:Y"ʫC-[Cr7UjvÑvr!QnU\sU֫r;FagQ:R,܍qH0~d2 ")>0`֪檼bW R_lJCcc)WL I_)^(o@<PF @ 0;>fJRDPU]>($ P  UP!!*BP (!JD!*@R"%@PDR @IATP$H((HB$T& F#Tħɦd2h#L h4d p4dhd# 5HL LL& &&L j@MM4h!a j{J=C@OFmP RBi0!i'  cfBK~lߧgǜ=b.69ޭk߾9#*,Ulu>[?JLE!fJ!J"~k^7ו{t~:yF =|#緭=1Gp5rA ?xgLG3 6l1A:q 5d?U5~a[Pq/c8f/1pC{ ðlz  ݏ֮;ކڭ:1;mUlӇvC'/UݪMUVDf&n؛ocg_1g[_ jq1zߴv+j0uQ èj~*G#?ʪ el}x#Á=C%{C:G<eUgK3cY^#I;E%RmdQ}d:v}׏UWes*۫|ƝxC_\UZX#02VCUn40`z:C=^zVuv h:|C*\U^Cpawʭ p u 90 꼪WqVy [UY !CP7*u[*#0`ʰ8U{ ʰ0=xU`ʮ*8  tU-SVU{p`^ a:q$VU```0@>h4*Av 7JlnUnlX@@VA]5W;ä;CC2VC<EUllnXspAhlhpʫPTF !U{ Ul V%1V*W)~%\}?sF~*nx#ccccc(d! Cn jE 1Tts 2+d:PEnjC?<9.aY j8 C!=!.e$&XJaB"{ 4( <240b-C0r0v=aqr=ǘwEh`;cr8:4?;u"H0d9A{C0{FCAl{Q`>Cvp;pqcz:?xzǰ9PC`qs #>CCGQ>^0e1ht1C;QqFX=Ccw 0hzt1`J}c0{C`CczaE8t8x: !!0hy7`b.F hp0t A:P cr9#ut:}pp8@r; C:P5V4 h4`w0xv s@<l `~p@r zP>A[4Ps sa9`8#w~C`` =FQr5 ` =Gq: pd0tǼ~ut!;C:Gq9#x0` w!t=t`lh{ø{G#HG#}:!x4;Cqs #>CC740eAhvqà`d; 4=à86*0u4=G=CFzaE8t8:;`4>cclj1#Chu EuE`0ltC8FC4?hjC`8#^06Z\?HdV`/hhr:G0~!-C2%p6? 442>hhc*X>ѡWAiJ̙f,V!FF CIj+e$ X1K"2e*!6C󎃰;d] * D)0Ux*T*` EeXBdQ]yƪֆk43YfhfPZhfC5kXCZֵ ZPcW>уCv*(t#CC 0`h`^*F"򆆇p8ơ` 0` 0`8p8a45 wqv14;a0` 0`  0` 0` 0` 0`Hhhjx`p8L440` 0`C84?~C:CCCQ+CCP6;c{ C!  0u=k=,`(ZCp:0`!+0w`jC =C=cxC`h`p0`׍ =Fqn0` aW 990`qccccp;(e}ڪG!b5 hd4? JMUYCګǰX0`X9=lr844<9 5u0`_2E&C!{ x 0n+WW,A40hh`a*Úa4; [8PkPp0`C>ʪw櫑 Pڿ?Ñhhz,+9~ll G#CCC9440`ݚ0` 0` 0` 0` 0` 0` 0` 0` 0` 0` *0AW\f|`R:qc7: 0`aFǼ{p?|:hq~ T>pc0` W2P: 0` 0`cllllxaccccp;T8P:CH F᱃C.#ld;!668 a"0"r0`p0`苁5 49660tUA 0` 0` 0`n ۯ 0~x|_ qT 0` 0` 0` 0` 0` 0` ĭ 5Xkٌ 0` CCC 0` 0`G>bC .444=aU` 0` ` |4446;xh9G#jEއxz333333^U[a 0`骭 jCZZjh5WooV}90C`yË_;]j C_1WsU?'ӷ]}òp*KUT_eV d:!UV49QqW IVFX0V*^U  B!IK `CCCb IPGȿ2,"e{w?VUEYֆ =] CUW0}aqFCN0d hzpF Ur40a+0`2}@x!WҫX:HHzu~ʼ&_><85ByU`UUU`~S`ʭCP0` 0` 0}CC>!UT2^ᡪFPʬ2Ud9?80`CChhd?4< 0`? CCP*VU U,j P;z.Z3r1zujcxC/<AsU9HiF0p69S!UV0eS@hUȫhV_ʸʱV5 U6: ;Cn0lv*eWhxCwUjhtU8>uWxC21uCq+n0hhhhhhj*Q0t9Eyp; ǠCQ=Èld9; ucccch`86;llpJb/php;;Ghh{cxcCCz"{4:~v `h06 P4 =PAs `{p]ÀrZ*; 42#CcC60j"Cc90u!vG8C#(60{!`nc6:a6qCp!0vP Gw0l`p;FǨsP`vC48cx "lhhz p\p=#ҭVu`:`A0n0AhC9mTʰ244<z huǸ`p6;r/1VeffffYffYX̽!PnGqHjw P#hhhj*m  T4: ;C:!y )445"x=iW0d7C*n1Q`y*U}eN% $NC+!!8UyP}p~h~Qc`z C**0`ʽU{8  U *Ư PwJ2T?]C<SZuUe R᳌b,CCUQCcHa+*U$YhnCBd0d,Jl*īv}e[{C%`[SWeQ]N2 eUn%e+:WWUC`0`nA`΋`JUu ۫\P0d!r0j_Ԓ^UC"V""Ud>1Z懵Uʬ  =2C,PC0d0`ʫU`ʬUhsab,C!d;]ႫP5_‘L*JQzcahn7dʪf88%eVUV qTE!`tCcjo|Cp#u[ƆHe = UYVUT*"CIe $X#,}qU*'cad "%YCqVIrp=jS8iW!YCP~~ffY~`ֳ>WUՠʴh4h2-Ub/[:QY2   C*+U~hqP{]}ֆCc4\V`6lH*7pFArCp;AC C8v  hd42j*68C;ô;!wCc)pr22C訟*V*p EC C!S/=|VWQb4¯)l - MRiV`%2.G=h?$<Jȕ}\D]n J`Uu%:*Щە_=U `1VM ެUYV6 7461*Up8pHUGU**pT`9mC1+܇5q Wqs{5YV 43r 5C+PEoZj/!6JҫUYU4:cphhd,֭kZ1AV b,tVUj GAr5 %n"hq C|qȧ VWcW"vU$v; CUd̆\y0s\Na\|q*J[U*}R!E*Ud2e c(-BC!q3jUUiVA!d)ڊ©d2Y h&jBLW*pʰ7Ubʌ ܫG[?h*pl{#j"ʇ_C* ;ʇ EE\G\R}WZ42U`YUPJXUZ-ʰ1)TUZCW=jP`=H 2)I)22.J_@```Xz5HU\4AP5 Y  Pʣ7ڭJs*j2 40eV1W60j0p0eVR5 i}r`  CwC!ʬ UYC?5 !d;P 9Uë*e^U($Y'QXCC UyC!!VP+Cu2ʦ 0}"b-jPe nxF YELUڑd00/5U0,?{CCnt䧗ҫEdW~F~7UaVV2ѱΫsUV|Cad*Vjn Vǚ?b94=ȸ@ >샪UOp=bSX?#![WHuE]``````n1@E^r 2xtðd4h1VAޟ$꼪U`9ECjVuAh|_WQeVU`U`tP[UЩ;*Ⱥ3 C3VThh4Ad!VA UYHC!C 2UjU`|A>a I4CCC 2E!! 0hhh`ʬUUV"0p1I Pj"20`5!~qUʭä>TCU`ʫy?L;P" %:)*KR>!)UR3Jmf %X2C~H~}#0` Jp9UJ %`e9K%`{h```ʆD|(j 2PP>,4d-P*d* TO϶}-Zc4m }mrso}5Mۺ7nڭUèܗÕ]Cj·ncccu[$ث M nCwJp2`(q͚YA6c6?H\QlbcC;C}tzÀbtE֙9Q-P$4ZE)e#leSJd9bRU*2!#X2#lz  Eu0j)64; =#[֬`<@!eVCht9la+Cʡqjv]d*)=UjO <ǝsPEUr8Ք+c)\J2*HکWG{< 4>W;.`s *è֤Y cCCCCCCCCCC*wϭJQST>ؕpm^HVC"ʫUZ41 CPVUjUjDC LVw7*r*%VNG [IeTU8]a|09W@zyuknL՟cW{UOӮ x:hS*:8k }WUVNp6UlYꩃC UK iV`1PYP`,@WҮ +dH 5 Cp:n#;CCc7clr8CtGA`d<e.`08zLQlkY#anC<ơ:: c r0r:xj.`06Vhut="VC2nC!2VVC6U-nUdT W# Cj ! *8>ccC>#VC" «\T!QvlhԋT5UeC 2 ln2xp8 Ƒne C!kt9EX0`VR/!0`+J#hqUchp48 0`FCC0`p֪;HyxTV W.47BJyPXzQھf |Qd1UX! T=GA|XuE|@KO*UXV*I/"*jBZ5U!ŨUt %x`{ " 0`"C2Q;л"1YXO} U1W JX9Uޯ^`UґЫdI"W0`+j`28fwUNOTSUWUaCC*02BY gI?w*xJ}_P.}SW 1)%8¿hc2I({U^c0`NSU\үU 0?LJ8 RR2 ǕCD0`*,`jJV' **'VCCj  "ʬYVY`}CV *eW?61 hyU];в20aTd=heUo ƭ ?ӡX0`Z`eV yZfXVC6P`eYUPʰ65VE[:bnVn4C5Sc!V Ca%1W UIX0!U %sV ۴8 \}j LAU2|?o|`C:G#cUqWup@2Yaj*⮕nU]as!8qC62/  J~[K},j8˅\q\*㍍ozj+|2* rR> ⭃}JCC1AY&SYGـTP?P:Hbh0F!=J~Ѧ~4TQ1`C# R"OHdMLi4H I0:Rt9eL-iel?řUQuU2d?Xbb_:ޮgt ˝fo*UuǔvGB8a;іL!W}c~dA D'M֟'-'#z-D7Ų͂-&0a0;Qy+[UYkms.U\5Sj$-#A>/XI9N"m:4K~H)8N AD;> }j'OcC9U\FDTWT { RTC xYLȧt ˪hSn%%yOJ &C))GV\SKG_IZUiJL9IITrI6