PrototypedBuilder.class.php

Go to the documentation of this file.
00001 <?php
00002 /***************************************************************************
00003  *   Copyright (C) 2007 by Ivan Y. Khvostishkov                            *
00004  *                                                                         *
00005  *   This program is free software; you can redistribute it and/or modify  *
00006  *   it under the terms of the GNU Lesser General Public License as        *
00007  *   published by the Free Software Foundation; either version 3 of the    *
00008  *   License, or (at your option) any later version.                       *
00009  *                                                                         *
00010  ***************************************************************************/
00011 
00012     abstract class PrototypedBuilder
00013     {
00014         protected $proto        = null;
00015         
00016         private $limitedPropertiesList  = null;
00017         
00018         abstract protected function createEmpty();
00019         
00023         abstract protected function getGetter($object);
00024         
00028         abstract protected function getSetter(&$object);
00029         
00030         public function __construct(EntityProto $proto)
00031         {
00032             $this->proto = $proto;
00033         }
00034         
00035         public function setLimitedPropertiesList($list)
00036         {
00037             if ($list !== null)
00038                 Assert::isArray($list);
00039             
00040             $mapping = $this->proto->getFullFormMapping();
00041             
00042             foreach ($list as $key => $inner)
00043                 Assert::isIndexExists($mapping, $key);
00044                 
00045             $this->limitedPropertiesList = $list;
00046             
00047             return $this;
00048         }
00049         
00053         public function cloneBuilder(EntityProto $proto)
00054         {
00055             Assert::isTrue(
00056                 $this->proto->isInstanceOf($proto)
00057                 || $proto->isInstanceOf($this->proto),
00058                 
00059                 Assert::dumpArgument($proto)
00060             );
00061             
00062             $result = new $this($proto);
00063             
00064             $result->limitedPropertiesList = $this->limitedPropertiesList;
00065             
00066             return $result;
00067         }
00068         
00069         public function cloneInnerBuilder($property)
00070         {
00071             $mapping = $this->getFormMapping();
00072             
00073             Assert::isIndexExists($mapping, $property);
00074             
00075             $primitive = $mapping[$property];
00076             
00077             Assert::isInstance($primitive, 'PrimitiveForm');
00078             
00079             $result = new $this($primitive->getProto());
00080             
00081             if (isset($this->limitedPropertiesList[$primitive->getName()])) {
00082                 $result->setLimitedPropertiesList(
00083                     $this->limitedPropertiesList[$primitive->getName()]
00084                 );
00085             }
00086             
00087             return $result;
00088         }
00089 
00090         public function makeListItemBuilder($object)
00091         {
00092             return $this;
00093         }
00094         
00098         public function makeReverseBuilder()
00099         {
00100             throw new UnimplementedFeatureException(
00101                 'reverse builder is not provided yet'
00102             );
00103         }
00104 
00109         public function make($object, $recursive = true)
00110         {
00111             // FIXME: make entityProto() non-static, problem with forms here
00112             if (
00113                 ($object instanceof PrototypedEntity)
00114                 || ($object instanceof Form)
00115             ) {
00116                 $proto = $this->proto;
00117                 
00118                 if ($object instanceof Form) {
00119                     $objectProto = $object->getProto();
00120                 } else
00121                     $objectProto = $object->entityProto();
00122                 
00123                 if (
00124                     $objectProto
00125                     && !ClassUtils::isInstanceOf($proto, $objectProto)
00126                 ) {
00127                     if (!$objectProto->isInstanceOf($proto))
00128                         throw new WrongArgumentException(
00129                             'target proto '.get_class($objectProto)
00130                             .' is not a child of '.get_class($proto)
00131                         );
00132                     
00133                     $proto = $objectProto;
00134                     
00135                     return $this->cloneBuilder($proto)->
00136                         make($object);
00137                 }
00138             }
00139             
00140             if ($this->proto->isAbstract())
00141                 throw new WrongArgumentException(
00142                     'cannot make from abstract proto '
00143                     .get_class($this->proto)
00144                 );
00145             
00146             return $this->compile($object, $recursive);
00147         }
00148         
00149         public function compile($object, $recursive = true)
00150         {
00151             $result = $this->createEmpty();
00152 
00153             $this->initialize($object, $result);
00154 
00155             if ($recursive)
00156                 $result = $this->upperMake($object, $result);
00157             else
00158                 $result = $this->fillOwn($object, $result);
00159             
00160             return $result;
00161         }
00162         
00163         public function upperMake($object, &$result)
00164         {
00165             if ($this->proto->baseProto()) {
00166                 $this->cloneBuilder($this->proto->baseProto())->
00167                     upperMake($object, $result);
00168             }
00169             
00170             return $this->fillOwn($object, $result);
00171         }
00172         
00173         public function makeList($objectsList, $recursive = true)
00174         {
00175             if ($objectsList === null)
00176                 return null;
00177             
00178             Assert::isArray($objectsList);
00179             
00180             $result = array();
00181             
00182             foreach ($objectsList as $id => $object) {
00183                 $result[$id] = $this->makeListItemBuilder($object)->
00184                     make($object, $recursive);
00185             }
00186             
00187             return $result;
00188         }
00189         
00193         public function makeOwn($object, &$result)
00194         {
00195             return $this->fillOwn($object, $result);
00196         }
00197         
00198         public function upperFill($object, &$result)
00199         {
00200             if ($this->proto->baseProto()) {
00201                 $this->cloneBuilder($this->proto->baseProto())->
00202                     upperFill($object, $result);
00203             }
00204             
00205             return $this->fillOwn($object, $result);
00206         }
00207         
00208         public function fillOwn($object, &$result)
00209         {
00210             if ($object === null)
00211                 return $result;
00212             
00213             $getter = $this->getGetter($object);
00214             $setter = $this->getSetter($result);
00215             
00216             foreach ($this->getFormMapping() as $id => $primitive) {
00217 
00218                 $value = $getter->get($id);
00219                 
00220                 if ($primitive instanceof PrimitiveFormsList) {
00221                         
00222                     $setter->set(
00223                         $id,
00224                         $this->cloneInnerBuilder($id)->
00225                             makeList($value)
00226                     );
00227                     
00228                 } elseif ($primitive instanceof PrimitiveForm) {
00229                     
00230                     if (
00231                         $primitive->isComposite()
00232                         && ($previousValue = $setter->getGetter()->get($id))
00233                     ) {
00234                         
00235                         $this->cloneInnerBuilder($id)->
00236                             upperFill($value, $previousValue);
00237                         
00238                     } elseif ($value !== null || $primitive->isRequired()) {
00239                         
00240                         $setter->set(
00241                             $id,
00242                             $this->cloneInnerBuilder($id)->
00243                                 make($value)
00244                         );
00245                     }
00246                 
00247                 } else {
00248                     $setter->set($id, $value);
00249                 }
00250             }
00251             
00252             return $result;
00253         }
00254         
00255         protected function initialize($object, &$result)
00256         {
00257             return $this;
00258         }
00259 
00260         protected function getFormMapping()
00261         {
00262             $protoMapping = $this->proto->getFormMapping();
00263             
00264             if ($this->limitedPropertiesList === null)
00265                 return $protoMapping;
00266             
00267             $result = array();
00268             
00269             foreach ($protoMapping as $id => $value) {
00270                 if (!isset($this->limitedPropertiesList[$id]))
00271                     continue;
00272                 
00273                 $result[$id] = $value;
00274             }
00275             
00276             return $result;
00277         }
00278     }
00279 ?>