web/lib/Zend/CodeGenerator/Php/File.php
author Nicolas Sauret <nicolas.sauret@iri.centrepompidou.fr>
Thu, 12 Jun 2014 12:52:01 +0200
changeset 1130 393bb7c2bc1d
parent 807 877f952ae2bd
child 1230 68c69c656a2c
permissions -rw-r--r--
Added tag v02.96 for changeset d586369f8c46

<?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_CodeGenerator
 * @subpackage PHP
 * @copyright  Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 * @version    $Id: File.php 24593 2012-01-05 20:35:02Z matthew $
 */

/**
 * @see Zend_CodeGenerator_Php_Abstract
 */
require_once 'Zend/CodeGenerator/Php/Abstract.php';

/**
 * @see Zend_CodeGenerator_Php_Class
 */
require_once 'Zend/CodeGenerator/Php/Class.php';

/**
 * @category   Zend
 * @package    Zend_CodeGenerator
 * @copyright  Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 */
class Zend_CodeGenerator_Php_File extends Zend_CodeGenerator_Php_Abstract
{

    /**
     * @var array Array of Zend_CodeGenerator_Php_File
     */
    protected static $_fileCodeGenerators = array();

    /**#@+
     * @var string
     */
    protected static $_markerDocblock = '/* Zend_CodeGenerator_Php_File-DocblockMarker */';
    protected static $_markerRequire = '/* Zend_CodeGenerator_Php_File-RequireMarker: {?} */';
    protected static $_markerClass = '/* Zend_CodeGenerator_Php_File-ClassMarker: {?} */';
    /**#@-*/

    /**
     * @var string
     */
    protected $_filename = null;

    /**
     * @var Zend_CodeGenerator_Php_Docblock
     */
    protected $_docblock = null;

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

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

    /**
     * @var string
     */
    protected $_body = null;

    public static function registerFileCodeGenerator(Zend_CodeGenerator_Php_File $fileCodeGenerator, $fileName = null)
    {
        if ($fileName == null) {
            $fileName = $fileCodeGenerator->getFilename();
        }

        if ($fileName == '') {
            require_once 'Zend/CodeGenerator/Php/Exception.php';
            throw new Zend_CodeGenerator_Php_Exception('FileName does not exist.');
        }

        // cannot use realpath since the file might not exist, but we do need to have the index
        // in the same DIRECTORY_SEPARATOR that realpath would use:
        $fileName = str_replace(array('\\', '/'), DIRECTORY_SEPARATOR, $fileName);

        self::$_fileCodeGenerators[$fileName] = $fileCodeGenerator;

    }

    /**
     * fromReflectedFileName() - use this if you intend on generating code generation objects based on the same file.
     * This will keep previous changes to the file in tact during the same PHP process
     *
     * @param string $filePath
     * @param bool $usePreviousCodeGeneratorIfItExists
     * @param bool $includeIfNotAlreadyIncluded
     * @return Zend_CodeGenerator_Php_File
     */
    public static function fromReflectedFileName($filePath, $usePreviousCodeGeneratorIfItExists = true, $includeIfNotAlreadyIncluded = true)
    {
        $realpath = realpath($filePath);

        if ($realpath === false) {
            if ( ($realpath = Zend_Reflection_File::findRealpathInIncludePath($filePath)) === false) {
                require_once 'Zend/CodeGenerator/Php/Exception.php';
                throw new Zend_CodeGenerator_Php_Exception('No file for ' . $realpath . ' was found.');
            }
        }

        if ($usePreviousCodeGeneratorIfItExists && isset(self::$_fileCodeGenerators[$realpath])) {
            return self::$_fileCodeGenerators[$realpath];
        }

        if ($includeIfNotAlreadyIncluded && !in_array($realpath, get_included_files())) {
            include $realpath;
        }

        $codeGenerator = self::fromReflection(($fileReflector = new Zend_Reflection_File($realpath)));

        if (!isset(self::$_fileCodeGenerators[$fileReflector->getFileName()])) {
            self::$_fileCodeGenerators[$fileReflector->getFileName()] = $codeGenerator;
        }

        return $codeGenerator;
    }

    /**
     * fromReflection()
     *
     * @param Zend_Reflection_File $reflectionFile
     * @return Zend_CodeGenerator_Php_File
     */
    public static function fromReflection(Zend_Reflection_File $reflectionFile)
    {
        $file = new self();

        $file->setSourceContent($reflectionFile->getContents());
        $file->setSourceDirty(false);

        $body = $reflectionFile->getContents();

        // @todo this whole area needs to be reworked with respect to how body lines are processed
        foreach ($reflectionFile->getClasses() as $class) {
            $file->setClass(Zend_CodeGenerator_Php_Class::fromReflection($class));
            $classStartLine = $class->getStartLine(true);
            $classEndLine = $class->getEndLine();

            $bodyLines = explode("\n", $body);
            $bodyReturn = array();
            for ($lineNum = 1; $lineNum <= count($bodyLines); $lineNum++) {
                if ($lineNum == $classStartLine) {
                    $bodyReturn[] = str_replace('?', $class->getName(), self::$_markerClass);  //'/* Zend_CodeGenerator_Php_File-ClassMarker: {' . $class->getName() . '} */';
                    $lineNum = $classEndLine;
                } else {
                    $bodyReturn[] = $bodyLines[$lineNum - 1]; // adjust for index -> line conversion
                }
            }
            $body = implode("\n", $bodyReturn);
            unset($bodyLines, $bodyReturn, $classStartLine, $classEndLine);
        }

        if (($reflectionFile->getDocComment() != '')) {
            $docblock = $reflectionFile->getDocblock();
            $file->setDocblock(Zend_CodeGenerator_Php_Docblock::fromReflection($docblock));

            $bodyLines = explode("\n", $body);
            $bodyReturn = array();
            for ($lineNum = 1; $lineNum <= count($bodyLines); $lineNum++) {
                if ($lineNum == $docblock->getStartLine()) {
                    $bodyReturn[] = str_replace('?', $class->getName(), self::$_markerDocblock);  //'/* Zend_CodeGenerator_Php_File-ClassMarker: {' . $class->getName() . '} */';
                    $lineNum = $docblock->getEndLine();
                } else {
                    $bodyReturn[] = $bodyLines[$lineNum - 1]; // adjust for index -> line conversion
                }
            }
            $body = implode("\n", $bodyReturn);
            unset($bodyLines, $bodyReturn, $classStartLine, $classEndLine);
        }

        $file->setBody($body);

        return $file;
    }

    /**
     * setDocblock() Set the docblock
     *
     * @param Zend_CodeGenerator_Php_Docblock|array|string $docblock
     * @return Zend_CodeGenerator_Php_File
     */
    public function setDocblock($docblock)
    {
        if (is_string($docblock)) {
            $docblock = array('shortDescription' => $docblock);
        }

        if (is_array($docblock)) {
            $docblock = new Zend_CodeGenerator_Php_Docblock($docblock);
        } elseif (!$docblock instanceof Zend_CodeGenerator_Php_Docblock) {
            require_once 'Zend/CodeGenerator/Php/Exception.php';
            throw new Zend_CodeGenerator_Php_Exception('setDocblock() is expecting either a string, array or an instance of Zend_CodeGenerator_Php_Docblock');
        }

        $this->_docblock = $docblock;
        return $this;
    }

    /**
     * Get docblock
     *
     * @return Zend_CodeGenerator_Php_Docblock
     */
    public function getDocblock()
    {
        return $this->_docblock;
    }

    /**
     * setRequiredFiles
     *
     * @param array $requiredFiles
     * @return Zend_CodeGenerator_Php_File
     */
    public function setRequiredFiles($requiredFiles)
    {
        $this->_requiredFiles = $requiredFiles;
        return $this;
    }

    /**
     * getRequiredFiles()
     *
     * @return array
     */
    public function getRequiredFiles()
    {
        return $this->_requiredFiles;
    }

    /**
     * setClasses()
     *
     * @param array $classes
     * @return Zend_CodeGenerator_Php_File
     */
    public function setClasses(Array $classes)
    {
        foreach ($classes as $class) {
            $this->setClass($class);
        }
        return $this;
    }

    /**
     * getClass()
     *
     * @param string $name
     * @return Zend_CodeGenerator_Php_Class
     */
    public function getClass($name = null)
    {
        if ($name == null) {
            reset($this->_classes);
            return current($this->_classes);
        }

        return $this->_classes[$name];
    }

    /**
     * setClass()
     *
     * @param Zend_CodeGenerator_Php_Class|array $class
     * @return Zend_CodeGenerator_Php_File
     */
    public function setClass($class)
    {
        if (is_array($class)) {
            $class = new Zend_CodeGenerator_Php_Class($class);
            $className = $class->getName();
        } elseif ($class instanceof Zend_CodeGenerator_Php_Class) {
            $className = $class->getName();
        } else {
            require_once 'Zend/CodeGenerator/Php/Exception.php';
            throw new Zend_CodeGenerator_Php_Exception('Expecting either an array or an instance of Zend_CodeGenerator_Php_Class');
        }

        // @todo check for dup here

        $this->_classes[$className] = $class;
        return $this;
    }

    /**
     * setFilename()
     *
     * @param string $filename
     * @return Zend_CodeGenerator_Php_File
     */
    public function setFilename($filename)
    {
        $this->_filename = $filename;
        return $this;
    }

    /**
     * getFilename()
     *
     * @return string
     */
    public function getFilename()
    {
        return $this->_filename;
    }

    /**
     * getClasses()
     *
     * @return array Array of Zend_CodeGenerator_Php_Class
     */
    public function getClasses()
    {
        return $this->_classes;
    }

    /**
     * setBody()
     *
     * @param string $body
     * @return Zend_CodeGenerator_Php_File
     */
    public function setBody($body)
    {
        $this->_body = $body;
        return $this;
    }

    /**
     * getBody()
     *
     * @return string
     */
    public function getBody()
    {
        return $this->_body;
    }

    /**
     * isSourceDirty()
     *
     * @return bool
     */
    public function isSourceDirty()
    {
        if (($docblock = $this->getDocblock()) && $docblock->isSourceDirty()) {
            return true;
        }

        foreach ($this->_classes as $class) {
            if ($class->isSourceDirty()) {
                return true;
            }
        }

        return parent::isSourceDirty();
    }

    /**
     * generate()
     *
     * @return string
     */
    public function generate()
    {
        if ($this->isSourceDirty() === false) {
            return $this->_sourceContent;
        }

        $output = '';

        // start with the body (if there), or open tag
        if (preg_match('#(?:\s*)<\?php#', $this->getBody()) == false) {
            $output = '<?php' . self::LINE_FEED;
        }

        // if there are markers, put the body into the output
        $body = $this->getBody();
        if (preg_match('#/\* Zend_CodeGenerator_Php_File-(.*?)Marker#', $body)) {
            $output .= $body;
            $body    = '';
        }

        // Add file docblock, if any
        if (null !== ($docblock = $this->getDocblock())) {
            $docblock->setIndentation('');
            $regex = preg_quote(self::$_markerDocblock, '#');
            if (preg_match('#'.$regex.'#', $output)) {
                $output  = preg_replace('#'.$regex.'#', $docblock->generate(), $output, 1);
            } else {
                $output .= $docblock->generate() . self::LINE_FEED;
            }
        }

        // newline
        $output .= self::LINE_FEED;

        // process required files
        // @todo marker replacement for required files
        $requiredFiles = $this->getRequiredFiles();
        if (!empty($requiredFiles)) {
            foreach ($requiredFiles as $requiredFile) {
                $output .= 'require_once \'' . $requiredFile . '\';' . self::LINE_FEED;
            }

            $output .= self::LINE_FEED;
        }

        // process classes
        $classes = $this->getClasses();
        if (!empty($classes)) {
            foreach ($classes as $class) {
                if($this->getDocblock() == $class->getDocblock()) {
                    $class->setDocblock(null);
                }                   
                $regex = str_replace('?', $class->getName(), self::$_markerClass);
                $regex = preg_quote($regex, '#');
                if (preg_match('#'.$regex.'#', $output)) {
                    $output = preg_replace('#'.$regex.'#', $class->generate(), $output, 1);
                } else {
                    $output .= $class->generate() . self::LINE_FEED;
                }
            }

        }

        if (!empty($body)) {

            // add an extra space betwee clsses and
            if (!empty($classes)) {
                $output .= self::LINE_FEED;
            }

            $output .= $body;
        }

        return $output;
    }

    public function write()
    {
        if ($this->_filename == '' || !is_writable(dirname($this->_filename))) {
            require_once 'Zend/CodeGenerator/Php/Exception.php';
            throw new Zend_CodeGenerator_Php_Exception('This code generator object is not writable.');
        }
        file_put_contents($this->_filename, $this->generate());
        return $this;
    }

}
$+eXE ],jlT )&-U2H Jq >ֽ : !Tj4!fNP˽jQTd_eL3]u{KE[$ ->"X,JJ'Ǐ%K+۴WƲU5XUZZffffffffJ؉X[dɏ=",ŔXXު~C12N=,,<ZOpi~e¥TZJJ&}d0c*^v \Zg,ee| #*`)!a Hܐ$"ĝN z?s|R}-*VI*iD2o?"ő_k͌i|Xlchjj}>GI>*񏂋HO!&<Ҭ ,{2m-YbՐ(7w!yˏLLQ[[ю0ƑJQ%m|dGޗe.f[զ_a!GSXE|+$Tʕ̵TUU>洙ԕX~R#r/}0n7|:L+,SM2ٚe[i-/-+$Y0A/Y*X@!k 6w 䬁J Q)$,1²OYf bp7518֮.9LGRʊbrGw ^8[WLdf0`ZMv6 Tfc7*JyIeIJ㴉9<)F;I4W'6HjY$ic[4\kSv,Zb.զ21eO|d$?)R6~J?=yyWш""LD@AkW6gьc1c1n5YRaX0Qe0^y6&JT)R܏!?z~j;$A !>Ucx1jLcP)M2RU[zHxyקޟ%?K bHVBĕ!XUpokR*bɌEDeyuukʟ(īYYe`S"*"wtO2ERn,2Db",dI`bԕ`? > ?Q.]iQax+_VwD%$*a*NxzꩥiG7Ɨ*KF1qi[#D< Z?ᩒ''a?>cid6i13Hkcܵ>;B$Ë"^U52UZ*M*Ɣ4D$=JLN<=uAsJ7ӭ^Uj,i'D"y" 䞘j4:J}ic1cUVnEvﹾ2dɓ&VV/]kqLŋX*s + ,D{p] Q*qrX̔*ʊO_O'xcM~UGV$(4iI9`&A {IL 0(IP 9wkMj UiY:4kuTyw*4 H79ᠰzYn ,9PS@dԟ݆ &b Txmk;5"M혌Q66Un $@ L#D# HE9I)[Fpilm8_,Iɦ$+=ymuLRRu#J,w["mvΎU{F%*& eXge KaJpBkM ]B$u:z' Xv<x*,<uxd0,y{䌹NHb8ZP҂òe9!鐋aIFG:s !浠W$(wPlXd|ؐ JmFWWrI4ެGkǶ%J,JU*QL.7<[' po$CA&U cn@D&p4͉ :lqZH] 1dIȘT9,1'&V 0 0 *8VDUsg4mdDҞ 2 9tbTYծ1<\9%F3EhI&I7@pZQ%D#XKK2M<*;n7 [t{̆p֮ZvqW54Zz, )vĺH.&m4H0.pP 2hbn; \,nml Y6R%%[J%%,%RJҒJJYJJVmrFqzHy&K,=pM6ec 31e-S:4rI'0ԲʎS$sZK,J)RRU)Qe UNNTӤ3cNH8,z0ͶQ$8g;8OHp{ϽO+1c1xyq?Ot 9OURؼz{lb#REқTmcы5LֵK}荑J Hw]ʙie8vԼQ,UUkdnYC9jf $*̞s554pm^VZɵ]Ѵ|SܩVU%M=O<DAǀ?S#"/زf1Gɖ^SHQXXU*q<`V 4UFH%HaGՊaB˦wHNyODD8.$!'S'GXN$:'2$v?i;s*'N3djJ&~ⶔWfH!U7dZbO\&ҴTN6PIKlkjfݻ7]]̘ddDD&T5 #GTw?-WY1raQR*ʬT0CId猳> 4hӓlqm|3obh/^:8fw{̤^`  _9@@ Aa˵r3c24KAŖD~vS4e*" TNͳ6pP̉J 0ӳ2S\뮽rۗg{4ٷw.ͻN;,&)bQZLiO.*lppWURPXUXR*2I9C"m>F'f;$rPFT" "ۅa)EK,aI,'$LUeaJ[H~Yb!bQ [ N60DkdҲ Mj`ny=t͈T'WAI wBBY'R?" ՃÓaL|.&PDP{ԹU+&)$%b)hiF!RgBz(4rb a邴y:c.5꽊Ag&H}eƠB셱i$}6Tv9dt[{idЙ҉T<&<4Xz9%{),FZkle|<߮[liiT3s-kjmfh1-hljUoc V2ڷe8żv]\MU;6]J.u9Q"95uT{e‘[LXV,^,1c,|{OTSM6<UVj'4 c$y1e>ګW/.U1$tyuԼVlJ4]:NY]T]6SUK&+ $T&2U+ *0XQ$|eiDBIt>El=_` 2!YhvLHؐa>ɩuo33V~~RJIe'*^V1d{:-$)p%7..,uE*]弓MR2,Yae&K2c128DAcxM&B$Ң$0L”G FBwCH*h\[2.`v[<6e*ifmke(m[ZaN8ЖXe;OBm:Jو*Rdp%Ƒ$UHMLj&(H19pݶDRbJ%"L,I1Y>Ey%w#*M,F+z]3'X78&?:v:D$x0Yg=౯/ye*B3V Z45ttG ر꠪,V4>+y[νĿ-˰(OCNJ= ;&PD !r'4EF%HAؒ $)RE,1+l%[vƨj||aVDG"G}4ymP-V̋ƚ4ԕ# a1*UU샓P9a|UOwZj$sf'ƘW O$>*P??,d>0QeY>,XbQBIȡVcUJRezwݪLoe2W3*Vj5Md?*$d*UIJ&+X'1JS&U{mc!cDDLc,c;uUڙ0eZ+8Um uZYTK*Z)O9+}4T'୍ ci= B/PO6+12jhԴ 3(El d|5 źRkRFZɵ+s1& JY1K%cI$rY$yD񇕞qgIR$O?V-<蝻,;xIO Bۅ`, -z>* Ge>'XZTrj'T1MyvWY+iew}lcC՗6TBdwVL*`bU+J{AjO'"lD܄*J?n;K;ִzBzv]/Yk%*Tbc*Iil^Sr,e&$ŤD .}1ƭ2:*e12,06j6/W[ř,ϰ#{+RueK-IE-NX:;SիtEF)cX&WWەsu]c,JLU5U;L,++(c02 B"HJVdRZ[2Ԋnf7MaeUy7nd\IM&&L*ԅ\e|)sm˖NU4LiuJŒab'np˥dr哕i4tvp̜Γt;ImuZVS/5a-XF,Uu6XDEF" DA )T>',DV$e,b/Y!! 2XP%HxϬ{ȫaRx=_U'8Bdwʑ>pxcde,+DOO嬊Z?,33 )/G EV tdM3dFfe$]Z R®2RY2MNmSnZp & 6݄ITK2i5Je[LyPȇ3%Q"Q5DS%3rƛQ8m5<Z;L9J#pvRH_Iiaf ,eaPiO {/tDpOp=8=̊*[*Jd&KiVx|5d.,*tVHVRdT02FBȃF1WIxym/OЩ&OX)7W|&1#K 9$j2Y0ݐ܏aH$%GpxiˡI]*jpʶۖ2b]ѵ6f~§ ,ǦұXȵXaԔjA5e%eܖB+P?3SM552Y7MB+%dⷖю\M=m˙]Td\,)GjU4L|dGdxbj3dR̻5N/eUz^ԟbZi4ڋUckjZFki36dR[6,n_-F9nȱQ=,{?Rbi*fNS1l"G\Dh_Qs3330&L0 BP%%%RRRRQ%QRPDBRRRP IBQ%%%%%%ATJJJJ)2RQ Id$J$JJJJJf.ݚ꧄Z!/Hnn^R/+"ɑj5a𪾺cxV8$$>)TXħ{%xb{*XzMTFI)w$wRp8!G ym:ܹXcM4ŻhlҰy$Րk4j+UVRÇW2$JJ*mi4ܒ*2ÍYnjqdpZ#h48ȒIX'EbX)đQZ#&Fc ,;F M.CS )Ti̤$LJtᩅb*m"T92U8-݁m8m>բ?R>ecOjԲenV WvN%ds%1Ѧ1Udjբ7M2d,8,]hᆤDE:LUkNuHj%YZ]H?qs'@%M!पe"ǪDD|hD{$J&tNlt1+U1aJ4Af6V-,7diXFTXN^I.%Na,oW i21?n-dԨNX)eXm4d-5upi̸ޅ-\Wmji,UTIdչI*b*2"ҏ)eA)Q1ML':#Y֚%2W*zEtlc+,(eQ2b,aed_YCɊ h#!*LQ;$"  ,Ty5شWĩ$nImEE"*Q4HXY VU`X3 Sh谵K*( Sw'ٌc1c1ݺݻ3337]z,JT%*Oa'U x/x+%5UC&D1,!0mUkkqZHY!\ķd ,EeeJ3\m[jSZ1U 8̥jMdPŪ8Y>BhIĚb+Utmjh[ŗѭꚚHV+FK%Y,YGױBuQJ[%I!Xe%RJlbhO5UR?UFuHCN' $u{?,OYN ӛwv9p4niRDhؗӭjac2'kTՕU}d+Ū^Nbtx%$ =RiՌ}%/`T,%%*IXClc+*x&"CTՖaiiIz>EO%$DJ_Dp}K,>>䔟\O>0rvA֪wce~`(Zf3WDND,%bp1SIz, dBLX3MRlȨB>dp)jTHщTJIX#fIJK$n_"j#vj~{Õ&y%M,g 'mɓeex~YLpƬ홖Rē[e-}U-T144b$i•91){^Nb}h) ;*!Y$T'/ꕊbtt"3%N{-hQiKmiF,lJՔ4֊ Q-?)R,c蛍,a'"{Wt 6e_DI+I4KwկzU%h2d|M|kcYQUJQEU%%W? ;(0Ixs,YV%ziK&24c6ƙqf/^%zUƷ_ṵH\68Xª*EX%*-ԁ*JS+%Y"jeM۵5v~Kzs!a%.C<;#>ٞe#>:%$vXFdMՑ- VO:>%_nLUQ,*YYe;'t'??1R%2}CeYRTUI}i$ =Qbz'Qf&ͣ's | ~N×1x?PUWKbUЎ_'lI,RJR<Ow{1e2~j%Xhն1elڙjfQ"Z!Qt ڟEJO Yǽ2DA'D͌H>%prM>s2&Oˍ.?[ d!z{C,J/UIǡ9,RF,cXu&yϋ OP,?"d'FRII&κ%ISTۭm)rܒEMH~*IeW^]-zuRgLIՉ>jD8YS,Ը•RL$ ĈܲF~f"~: CS\WZc*SdGw(}UUUWyOjTj˖i4L6bH&a>$eyM~yČHȏ؞JdOtI Ēͪ tǭTVHz $wd~; =UU~3cN4uyGnfJ/HfK*Kyޕb8tɵlA[lYEl$'#639KV@,B)%mڏ[ RdJIh܈%vTiڛ5d*B.[M$I| $HtYSakO*TÅ(IEyE:)|dRijȩ>zN:'IbDY,ȢX&1dGWa9,~-i Jd`%b,]J[Juu)Z60& EEKYjۼzu٫fa YvɸI͑Hehb 0a%J,#*d"L$A8$"qޖjT6k6Y+i2'Ɨp& bfˌOrMRͮt'$1#薜"-Ƀq˚%(I1t_g)69VL09ps,QnpvlV"ߕQHt5}VpњyjZDgG5!5j啩`%Ȝ+!-ڴʫ1m2&1[9eskuqy%ڵYe^Zڑ"gftX;*O4v[JoY%y;Nр ^U^eUUW>^hgORu2CodI$I$I$I$I$I$I$EWRao&P{'H4'⹷M,c,'nݮ+9zcI#LOdYk=HhK Щ;ϾxŖ4ұ{ti!G@:M ;^륫1cćDaئɩ!de*!șKUe~i MqyfIOҸƃ%b"6+6=7xk01[m46a6A %dn]7]efe뮅nMm܏2'ARU!%N<b=U,u9:U{x##$RBiϭl'$~ ;R'GGHv)URlK\Ri O;z5 "̏ :hbΏUSUFX KVFIDE*=fҼO Eɧ}OivtoN8kQk# ۳;p0AYI7V "R3QW$4I6AE hr  A$ATE(H= *;Ӳ] z?zV*ʲX\nh&zH?Qb5I&׻=f.\fXaK YQYJV*1K-+/ݺL5,VH+ "du)-eɆ#dVdNks-2H /ZUB*"/C 7{VA-?|"BZ%[dIM4\*ۥmR-f!>OjbԱZ!d$RcRlٷRK)VC2VbYfJTU)❂Aܩ!' $[$M=O%Ri=UXNPGUUUUU]d!]{Vdt^ѓAUc,eg+l򟊶ord'+u{kjբEԔg2mz,E%u^%VUVUƜ&G n^V'G.Zqp'WI%Xim˻ ȳɷRT.tdu8|^Ô;I %I m OڒBR$e!dXT)ElmV͙IJҶZSZT̵VKH'C9*S^iIWaoFL*I.1YTH> VQhZSH4TrIHӆ%' VnVgp}/sXO*IHWRHQRROpU *;1I$*]WReEm+vL#?JFd(cZ[:D9@q(=^uO!1~3b?YZ=\XbCR4_ޮ̌Ec$|Jhmsl ͫ榚xD. ͦ&LGG{ʸI4rHS )JRmr6nK8i@,aXW#L`]!J5s tvhڜDN93L1wNΑnN:O76;,?b7FMi>}iG hȒmktmG<䬗-oE8xnd;% nd. ΃Pue'xvrN\<9wrˋؕDs, TqnN\urkj=W}7YWM<$N<*Y+/ic4'~ӧYםw0Nn$$TT0O$#iԛJN#W(d$ R\`OFLLN7PQBDU%]kJIl2VB?.,);ʪ?>69u?" ȕ$=AQ, ?)Ϩ*((/:`hAMiɀ T"=T4 hM()z 4i&"$ BiO4Q!q!r$ "CHstsel0RdCJ` Hf$4'ᢥbw$$xFCVZ嘐7cBUTúym$9"CsفWRI6{8uMYlH$7;Eh©^ВXHhH݊S=T2u&Yn2)N!$0暝Tclb1x-HeH[yn XC(fE#jzpsƄ92 UUW;U k UUz@Uw &bi 1d:I^˒d{Fg*!S`@j6mXnlz<r䪽6TIzbŸk`mw5֪[6X޶4 :(g$-6wy:V$s[g~j#ܑN$:$