Overview

Namespaces

  • None
  • PHP
  • picon

Classes

  • AbstractAjaxBehaviour
  • AbstractAssociatedMarkupSource
  • AbstractBehaviour
  • AbstractCallableOption
  • AbstractChoice
  • AbstractColumn
  • AbstractContextLoader
  • AbstractDatabaseDriver
  • AbstractInjectedDataProvider
  • AbstractJQueryBehaviour
  • AbstractJQueryUIBehaviour
  • AbstractLink
  • AbstractMarkupSource
  • AbstractMultipleChoice
  • AbstractOption
  • AbstractPageClassAuthorisationStrategy
  • AbstractRepeater
  • AbstractSingleChoice
  • AbstractTextComponent
  • AbstractToolbar
  • AbstractValidator
  • AjaxButton
  • AjaxEventBehaviour
  • AjaxFormComponentUpdateBehavior
  • AjaxFormSubmitBehavior
  • AjaxLink
  • AjaxRequestTarget
  • AllowAllAuthorisationStrategy
  • AnnotationRule
  • ApplicationConfigLoadListener
  • ApplicationContext
  • ApplicationContextLoadListener
  • ApplicationInitializer
  • ApplicationInitializerConfigLoadListenerCollection
  • ApplicationInitializerContextLoadListenerCollection
  • ApplicationProfile
  • Args
  • ArrayModel
  • ArrayOption
  • AttributeAppender
  • AttributeModifier
  • AutoContextLoader
  • AutoLoader
  • BasicModel
  • BooleanModel
  • BooleanOption
  • Border
  • BorderMarkupSourcingStratagy
  • BorderResolver
  • Button
  • CacheManager
  • CallbackAjaxCallDecorator
  • CallbackColumn
  • CallbackFunctionOption
  • CallbackOption
  • CallbackRowMapper
  • Check
  • CheckBox
  • CheckBoxGroup
  • CheckChoice
  • ChoiceRenderer
  • ClassNameRule
  • ClassNamespaceRule
  • ClassScanner
  • Comment
  • ComonDomainBase
  • Component
  • ComponentAfterRenderListenerCollection
  • ComponentAuthorisationListener
  • ComponentBeforeRenderListenerCollection
  • ComponentInitializationListenerCollection
  • ComponentInjector
  • ComponentInstantiationListenerCollection
  • ComponentRenderHeadListenerCollection
  • ComponentResolverHelper
  • ComponentTag
  • CompoundPropertyModel
  • Config
  • ConfigLoader
  • ContextLoaderFactory
  • DaoSupport
  • DataBaseTemplate
  • DataGridView
  • DataSource
  • DataSourceConfig
  • DataSourceFactory
  • DataSourceType
  • DataTable
  • DateField
  • DatePickerBehaviour
  • DefaultDataTable
  • DefaultJQueryUIBehaviour
  • DefaultMarkupSource
  • DefaultNotAuthorisedListener
  • DialogBehavior
  • DirectToPageComponentNotAuthorisedListener
  • DraggableBehaviour
  • DropDown
  • EmailAddressValidator
  • EmptyPanel
  • Enum
  • ExceptionPageRequestTarget
  • ExtendResolver
  • FeedbackMessage
  • FeedbackModel
  • FeedbackPanel
  • FileModel
  • FileUploadField
  • Form
  • FormComponent
  • FormComponentLabel
  • FormComponentPanel
  • FunctionOption
  • GridItem
  • GridView
  • HeaderContainer
  • HeaderPartContainer
  • HeaderResolver
  • HeaderResponse
  • HeaderToolbar
  • IdenticalValueValidator
  • Identifier
  • Injector
  • JQueryRenderHeadListener
  • JQueryUIRenderHeadListener
  • Label
  • LabeledMarkupContainer
  • Link
  • ListenerCollection
  • ListenerRequestResolver
  • ListenerRequestTarget
  • ListItem
  • ListMultiple
  • ListView
  • Localizer
  • ManualContextLoader
  • MarkupContainer
  • MarkupElement
  • MarkupLoader
  • MarkupParser
  • MarkupUtils
  • MaximumLengthValidator
  • MaximumValidator
  • MinimumLengthValidator
  • MinimumValidator
  • ModalWindow
  • MySqlDriver
  • MySqliDriver
  • NavigationLink
  • NavigationToolbar
  • Navigator
  • NumbericOption
  • NumericValidator
  • Objects
  • Options
  • PageInstanceRequestResolver
  • PageInstanceRequestTarget
  • PageMap
  • PageMapInitializationListenerCollection
  • PageNotFoundPage
  • PageNotFoundRequestTarget
  • PageRequestResolver
  • PageRequestTarget
  • PageRequestWithListenerTarget
  • PaginatingGridView
  • Panel
  • PanelMarkupSource
  • PanelResolver
  • PasswordField
  • PatternValidator
  • PiconApplication
  • PiconErrorHandler
  • PiconSerializer
  • PiconTag
  • PopupSettings
  • PropertyColumn
  • PropertyModel
  • PropertyOption
  • PropertyResolver
  • Radio
  • RadioChoice
  • RadioGroup
  • RangeLengthValidator
  • RangeValidator
  • RedirectRequestTarget
  • RepeatingView
  • RequestCycle
  • RequestResolverCollection
  • RequiredTextField
  • ResourceReference
  • ResourceRequestResolver
  • ResourceRequestTarget
  • SerializableClosure
  • SortableBehavior
  • StaticTabPanel
  • StringValidator
  • SubClassRule
  • Tab
  • TabCollection
  • TabPanel
  • TextArea
  • TextElement
  • TextField
  • TransparentMarkupContainer
  • ValidatableFormComponentWrapper
  • ValidationResponse
  • WebApplicationSecuritySettings
  • WebComponent
  • WebPage
  • WebRequest
  • WebResponse
  • WrappedCompoundModel
  • XMLDocument
  • XMLParser
  • XMLTag
  • XmlTagType

Interfaces

  • AjaxCallDecorator
  • ApplicationInitializerConfigLoadListener
  • ApplicationInitializerContextLoadListener
  • AuthorisationStrategy
  • Behaviour
  • BehaviourListener
  • CallDecoratorWrapper
  • ChoiceGroup
  • ClassScannerRule
  • Column
  • ComponentAfterRenderListener
  • ComponentAwareModel
  • ComponentBeforeRenderListener
  • ComponentInheritedModel
  • ComponentInitializationListener
  • ComponentInstantiationListener
  • ComponentNotAuthorisedListener
  • ComponentRenderHeadListener
  • ComponentResolver
  • CompoundModel
  • DatabaseDriver
  • DataBaseOperations
  • DataProvider
  • Detachable
  • Equalable
  • FormSubmitListener
  • FormSubmitter
  • Identifiable
  • InitializingBean
  • InjectOnWakeup
  • LinkListener
  • Listener
  • MarkupSource
  • Model
  • Pageable
  • PageMapInitializationListener
  • Request
  • RequestablePage
  • RequestResolver
  • RequestTarget
  • Response
  • RowMapper
  • Validatable
  • Validator
  • XmlElement

Exceptions

  • ConfigException
  • ConversionException
  • RestartRequestOnPageException
  • SQLException
  • UnAuthorisdeException
  • Overview
  • Namespace
  • Class
  • Tree
  • Deprecated
  • Todo
   1: <?php
   2: 
   3: /**
   4:  * Picon Framework
   5:  * http://code.google.com/p/picon-framework/
   6:  *
   7:  * Copyright (C) 2011-2012 Martin Cassidy <martin.cassidy@webquub.com>
   8: 
   9:  * Picon Framework is free software: you can redistribute it and/or modify
  10:  * it under the terms of the GNU General Public License as published by
  11:  * the Free Software Foundation, either version 3 of the License, or
  12:  * (at your option) any later version.
  13: 
  14:  * Picon Framework is distributed in the hope that it will be useful,
  15:  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16:  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17:  *  General Public License for more details.
  18: 
  19:  * You should have received a copy of the GNU General Public License
  20:  * along with Picon Framework.  If not, see <http://www.gnu.org/licenses/>.
  21:  * */
  22: 
  23: namespace picon;
  24: 
  25: /**
  26:  * Component sersvices as the hightest and most abstract super class for all
  27:  * components. 
  28:  * 
  29:  * Component supports complex serialisation @see PiconSerializer
  30:  * Component automatically injects resources on instantiation @see Injector
  31:  * 
  32:  * A component must have a unique ID that is passed in the constructor. The ID 
  33:  * need only be unique amoung sibling component in the component hierarchy.
  34:  * 
  35:  * Components are organised into a simple hierachy. With the exception of the 
  36:  * ultimate parent at the top of the hierarchy, a component will always have a parent.
  37:  * 
  38:  * If the component is an instance of MarkupContainer it can have children added to it.
  39:  * 
  40:  * @author Martin Cassidy
  41:  * @package web
  42:  * @todo finish adding state flags so that checks can be run to ensure overriden methods are calling
  43:  * the parent implementation
  44:  */
  45: abstract class Component implements InjectOnWakeup, Identifiable, Detachable
  46: {
  47:     const TYPE_STRING = 'string';
  48:     const TYPE_FLOAT = 'float';
  49:     const TYPE_BOOL = 'boolean';
  50:     const TYPE_DOUBLE = 'double';
  51:     const TYPE_INT = 'integer';
  52:     const TYPE_ARRAY = 'array';
  53:     
  54:     
  55:     const VISITOR_CONTINUE_TRAVERSAL = 1;
  56:     const VISITOR_STOP_TRAVERSAL = 2;
  57:     const VISITOR_CONTINUE_TRAVERSAL_NO_DEEPER = 3;
  58:     
  59:     /**
  60:      * @var String the ID of this component
  61:      */
  62:     private $id;
  63:     
  64:     /**
  65:      * @var Component the parent of this component in the hierarchy
  66:      */
  67:     private $parent;
  68:     
  69:     /**
  70:      * @var Array The behaviours that have been added to this component
  71:      */
  72:     private $behaviours = array();
  73:     
  74:     /**
  75:      * @var mixed MarkupElement or array of MarkupElements. The markup associated with this component
  76:      */
  77:     private $markup;
  78:     
  79:     /**
  80:      * @var boolean true if this component has been rendered
  81:      */
  82:     private $rendered = false;
  83:     
  84:     /**
  85:      * @var boolean true if this component has been rendered
  86:      */
  87:     private $initialized = false;
  88:     
  89:     /**
  90:      * @var boolean true if the parent::onInitialize was called by all overriding
  91:      * implementations 
  92:      */
  93:     private $flagInitializeParentCall = false;
  94:     
  95:     
  96:     private $beforePageRendered = false;
  97:     
  98:     private $model;
  99: 
 100:     /**
 101:      * @var boolean whether only the body of this component is to be rendered
 102:      */
 103:     private $renderBodyOnly;
 104:     
 105:     /**
 106:      * @var boolean true if this component is in the hierarchy
 107:      */
 108:     protected $added = false;
 109:     
 110:     private $markupSource = null;
 111:     
 112:     private $markupId;
 113:     
 114:     private $outputMarkupId = false;
 115:     
 116:     private $visible = true;
 117:     
 118:     private static $nextId = 0;
 119:     
 120:     /**
 121:      * @var boolean whether the component was added automatically
 122:      */
 123:     private $auto = false;
 124:     
 125:     private $beforePageRenderCallback;
 126:     private $afterPageRenderCallback;
 127:     private $beforeComponentRenderCallback;
 128:     private $afterComponentRenderCallback;
 129:     private $onComponentTagCallback;
 130:     private $onComponentTagBodyCallback;
 131:     private $renderHeadCallback;
 132:     
 133:     const PATH_SEPERATOR = ':';
 134:     
 135:     /**
 136:      * Create a new component. Any overrides of the constructor must call the super.
 137:      * @param string $id the ID of this component
 138:      */
 139:     public function __construct($id, Model $model = null)
 140:     {
 141:         Args::isString($id, 'id');
 142:         $this->id = $id;
 143:         $this->model = $model;
 144:         PiconApplication::get()->getComponentInstantiationListener()->onInstantiate($this);
 145:     }
 146: 
 147:     /**
 148:      * Called when the component hierarchy above this compoent is complete
 149:      * If overriding this method you MUST call parent::onInitialize()
 150:      */
 151:     protected function onInitialize()
 152:     {
 153:         $this->flagInitializeParentCall = true;
 154:         
 155:         foreach($this->behaviours as $behaviour)
 156:         {
 157:             $behaviour->bind($this);
 158:         }
 159:         
 160:         PiconApplication::get()->getComponentInitializationListener()->onInitialize($this);
 161:     }
 162:     
 163:     protected final function fireInitialize()
 164:     {
 165:         if($this->isInitialized())
 166:         { 
 167:             return;
 168:         }
 169:         $this->initialized = true;
 170:         $this->flagInitializeParentCall = false;
 171:         $this->onInitialize();
 172:         if(!$this->flagInitializeParentCall)
 173:         {
 174:             throw new \IllegalStateException(sprintf("Parent implementation of onInitialize for component %s was not called", $this->id));
 175:         }
 176:     }
 177:     
 178:     public function internalInitialize()
 179:     {
 180:         $this->fireInitialize();
 181:     }
 182:     
 183:     public function add(&$object)
 184:     {
 185:         if($object instanceof Behaviour)
 186:         {
 187:             $this->addBehaviour($object);
 188:         }
 189:         else
 190:         {
 191:             throw new \InvalidArgumentException(sprintf("Argument %s was not a valid type for this method", gettype($object)));
 192:         }
 193:     }
 194:     
 195:     protected final function addBehaviour(Behaviour &$behaviour)
 196:     {
 197:         $this->behaviours['behaviour_'.$this->getNextComponentId()] = $behaviour;
 198:         if($this->isInitialized())
 199:         {
 200:             $behaviour->bind($this);
 201:         }
 202:     }
 203:     
 204:     /**
 205:      * Gets the markup for this component
 206:      */
 207:     public function getMarkup()
 208:     {
 209:         if($this->markup!=null)
 210:         {
 211:             return $this->markup;
 212:         }
 213:         else
 214:         {
 215:             if($this->parent==null)
 216:             {
 217:                 if($this instanceof MarkupContainer)
 218:                 {
 219:                     $this->markup = $this->loadAssociatedMarkup();
 220:                     return $this->markup;
 221:                 }
 222:                 else
 223:                 {
 224:                     throw new \MarkupNotFoundException(sprintf("Component %s has no associated markup and no parent to get markup from", $this->id));
 225:                 }
 226:                 
 227:             }
 228:             else
 229:             {
 230:                 $this->markup = $this->parent->getMarkupForChild($this);
 231:                 return $this->markup;
 232:             }
 233:         }
 234:     }
 235:     
 236:     /**
 237:      * Called just before the page is rendered for all of its components
 238:      */
 239:     public function beforePageRender()
 240:     {
 241:         if($this->beforePageRenderCallback!=null)
 242:         {
 243:             $callable = $this->beforePageRenderCallback;
 244:             $callable($this);
 245:         }
 246:         $this->beforePageRendered = true;
 247:     }
 248:     
 249:     /**
 250:      * Called just after a page is rendered
 251:      */
 252:     public function afterPageRender()
 253:     {
 254:         if($this->afterPageRenderCallback!=null)
 255:         {
 256:             $callable = $this->afterPageRenderCallback;
 257:             $callable($this);
 258:         }
 259:     }
 260:     
 261:     /**
 262:      * Called just before a component is rendered
 263:      */
 264:     public function beforeComponentRender()
 265:     {
 266:         PiconApplication::get()->getComponentBeforeRenderListener()->onBeforeRender($this);
 267:         $this->notifyBehavioursBeforeRender();
 268:         if($this->beforeComponentRenderCallback!=null)
 269:         {
 270:             $callable = $this->beforeComponentRenderCallback;
 271:             $callable($this);
 272:         }
 273:     }
 274:     
 275:     public function isInitialized()
 276:     {
 277:         return $this->initialized;
 278:     }
 279:     
 280:     public function isBeforePageRender()
 281:     {
 282:         return $this->beforePageRendered;
 283:     }
 284:     
 285:     /**
 286:      * Called just after the component is rendered
 287:      */
 288:     public function afterComponentRender()
 289:     {
 290:         if($this->afterComponentRenderCallback!=null)
 291:         {
 292:             $callable = $this->afterComponentRender;
 293:             $callable($this);
 294:         }
 295:         $this->rendered = true;
 296:         PiconApplication::get()->getComponentAfterRenderListenersr()->onAfterRender($this);
 297:         $this->notifyBehavioursAfterRender();
 298:            
 299:         if($this instanceof MarkupContainer && $this->visible)
 300:         {
 301:             foreach($this->getChildren() as $child)
 302:             {
 303:                 /**
 304:                  * If the child component has not been rendered it must
 305:                  * not have been present in the markup
 306:                  * @todo move this into another non component parent calling method
 307:                  * so that all children may have internalAfterRender
 308:                  */
 309:                 if(!$child->rendered)
 310:                 {
 311:                     throw new \RuntimeException(sprintf("Component %s was not rendered because there was no corrisponding picon:id in the markup.", $child->id));
 312:                 }
 313:             }
 314:         }
 315:     }
 316:     
 317:     public function render()
 318:     {
 319:         $exception = null;
 320:         
 321:         try
 322:         {
 323:             if($this->getParent()==null)
 324:             {
 325:                 $this->beforePageRender();
 326:             }
 327:             $this->beforeComponentRender();
 328:             $this->internalRender();
 329:         }
 330:         catch(Exception $ex)
 331:         {
 332:             $exception = $ex;
 333:         }
 334:         try
 335:         {
 336:             $this->afterComponentRender();
 337:             if($this->getParent()==null)
 338:             {
 339:                 $this->afterPageRender();
 340:             }
 341:         }
 342:         catch(Exception $ex)
 343:         {
 344:              if($exception==null)
 345:              {
 346:                 $exception = $ex;
 347:              }
 348:         }
 349:         
 350:         if($exception!=null)
 351:         {
 352:             throw $exception;
 353:         }
 354:     }
 355:     
 356:     private function internalRender()
 357:     {
 358:         $markup = $this->getMarkup();
 359:         
 360:         if($markup==null)
 361:         {
 362:             throw new \MarkupNotFoundException(sprintf("Markup not found for component %s.", $this->id));
 363:         }
 364:         $this->onRender();
 365:     }
 366:     
 367:     /**
 368:      * Renders this component
 369:      */
 370:     protected final function internalRenderComponent()
 371:     {
 372:         $markup = $this->getMarkup();
 373:         
 374:         if($markup==null)
 375:         {
 376:             throw new \MarkupNotFoundException(sprintf("Markup not found for component %s.", $this->id));
 377:         }
 378:         /* @todo this cloning is a quick fix, markup should be imutable until 
 379:          * this point were a mutable version is created for use by the component 
 380:          * to render with for only this request
 381:          */
 382:         $markup = clone $markup;
 383:         
 384:         //@todo add ajax placeholder to set display:none on invisible components
 385:         if(!$this->visible)
 386:         {
 387:             return;
 388:         }
 389:         
 390:         if($this->renderBodyOnly)
 391:         {
 392:             $this->onComponentTagBody($markup);
 393:         }
 394:         else
 395:         {
 396:             $this->onComponentTag($markup);
 397:             $this->renderElementStart($markup);
 398:             $this->onComponentTagBody($markup);
 399:             $this->renderElementEnd($markup);
 400:         }
 401:     }
 402:     
 403:     /**
 404:      * Renders the start tag for the passed element
 405:      * @param MarkupElement The markup element to be rendered
 406:      */
 407:     private final function renderElementStart(MarkupElement $element)
 408:     {
 409:         $this->getResponse()->write('<'.$element->getName());
 410:         $this->renderAttributes($element->getAttributes());
 411:         if($element->isOpenClose())
 412:         {
 413:             $this->getResponse()->write(' /');
 414:         }
 415:         $this->getResponse()->write('>');
 416:     }
 417:     
 418:     /**
 419:      * Renders the close tag (if needed) for the element
 420:      * @param MarkupElement The markup element to be rendered 
 421:      */
 422:     private final function renderElementEnd(MarkupElement $element)
 423:     {        
 424:         if($element->isOpen())
 425:         {
 426:             $this->getResponse()->write('</'.$element->getName().'>');
 427:         }
 428:     }
 429:     
 430:     /**
 431:      * Renders the attributes
 432:      * @param Array the array of attributes
 433:      */
 434:     private function renderAttributes($attributes)
 435:     {
 436:         foreach($attributes as $name => $value)
 437:         {
 438:             $this->getResponse()->write(' '.$name.'="'.$value.'"');
 439:         }
 440:     }
 441:     
 442:     public function renderElement(MarkupElement $element)
 443:     {
 444:         $this->renderElementStart($element);
 445:         if($element->hasChildren())
 446:         {
 447:             $this->renderAll($element->getChildren());
 448:         }
 449:         $this->renderElementEnd($element);
 450:     }
 451:     
 452:     /**
 453:      * Render all of the markup elements in the array
 454:      * @param Array An array of markup elements
 455:      */
 456:     protected function renderAll($markup = null)
 457:     {
 458:         if($markup==null)
 459:         {
 460:             $markup = $this->getMarkup();
 461: 
 462:             if($markup==null)
 463:             {
 464:                 throw new \MarkupNotFoundException(sprintf("Markup not found for component %s.", $this->id));
 465:             }
 466:         }
 467:         
 468:         foreach($markup as &$element)
 469:         {
 470:             if($element instanceof ComponentTag)
 471:             {
 472:                 if($this instanceof MarkupContainer)
 473:                 {
 474:                     $child = $this->get($element->getComponentTagId());
 475:                     
 476:                     if($child==null)
 477:                     {
 478:                         $child = ComponentResolverHelper::resolve($this, $element);
 479:                         
 480:                         if($child!=null && $child->getParent()==null)
 481:                         {
 482:                             $child->setAuto();
 483:                             $this->addComponent($child);
 484:                         }
 485:                         if($child!=null)
 486:                         {
 487:                             $child->setMarkup($element);
 488:                         }
 489:                     }
 490:                     
 491:                     if($child!=null)
 492:                     { 
 493:                         $child->render();
 494:                     }
 495:                     else
 496:                     {
 497:                         throw new \RuntimeException(sprintf("A component was not found for element with picon:id %s. This may be because you have forgotten to create it in your code or the hierarchy is wrong", $element->getComponentTagId()));
 498:                     }
 499:                     
 500:                 }
 501:                 else
 502:                 {
 503:                     throw new \InvalidMarkupException(sprintf("Markup element %s may not contain a child with a picon:id as the component %s cannot not have any child components", $element->getName(), $this->id));
 504:                 }
 505:             }
 506:             elseif($element instanceof TextElement)
 507:             {
 508:                 $this->getResponse()->write($element->getContent());
 509:             }
 510:             else
 511:             {
 512:                 $this->renderElement($element);
 513:             }
 514:         }
 515:     }
 516:     
 517:     protected abstract function onRender();
 518:     
 519:     /**
 520:      * This is called imediatly before the tag is written to the output
 521:      * This method allows direct manipulation of the object representing the 
 522:      * actual markup element that is to be rendered.
 523:      * 
 524:      * When overriding this method you must remember to call the super.
 525:      * @param ComponentTag The tag being rendered
 526:      */
 527:     protected function onComponentTag(ComponentTag $tag)
 528:     {
 529:         if($this->onComponentTagCallback!=null)
 530:         {
 531:             $callable = $this->onComponentTagCallback;
 532:             $callable($this, $tag);
 533:         }
 534:         $this->getMarkUpSource()->onComponentTag($this, $tag);
 535:         if($this instanceof MarkupContainer && $this->hasChildren())
 536:         {
 537:             $tag->setTagType(new XmlTagType(XmlTagType::OPEN));
 538:         }
 539:         
 540:         if($this->outputMarkupId)
 541:         {
 542:             $tag->put('id', $this->getMarkupId());
 543:         }
 544:         $this->notifyBehavioursComponentTag($tag);
 545:     }
 546:     
 547:     /**
 548:      * Generates and returns a markup id for this component
 549:      * @param type $generate 
 550:      */
 551:     public function getMarkupId()
 552:     {
 553:         if(!isset($this->markupId))
 554:         {
 555:             $this->markupId = $this->id.$this->getNextComponentId();
 556:         }
 557:         return $this->markupId;
 558:     }
 559:     
 560:     /**
 561:      * Manually set the markup id. Note, using this makes it your
 562:      * responsability to ensure the id is unique
 563:      * @param string $id 
 564:      */
 565:     public function setMarkupId($id)
 566:     {
 567:         $this->markupId = $id;
 568:     }
 569:     
 570:     /**
 571:      *
 572:      * @param boolean $output 
 573:      */
 574:     public function setOutputMarkupId($output)
 575:     {
 576:         Args::isBoolean($output, 'output');
 577:         $this->outputMarkupId = $output;
 578:     }
 579:     
 580:     /**
 581:      * Render the body of the component
 582:      * @param ComponentTag $tag 
 583:      */
 584:     protected function onComponentTagBody(ComponentTag $tag)
 585:     {
 586:         if($this->onComponentTagBodyCallback!=null)
 587:         {
 588:             $callable = $this->onComponentTagBodyCallback;
 589:             $callable($this, $tag);
 590:         }
 591:         $this->getMarkUpSource()->onComponentTagBody($this, $tag);
 592:     }
 593:     
 594:     /**
 595:      * Checks that a component tag is a tag of the required name
 596:      * Throws an IllegalStateException if it is not
 597:      * @param ComponentTag $tag The tag to check
 598:      * @param String $tagName The tag name that should match
 599:      */
 600:     protected function checkComponentTag(ComponentTag $tag, $tagName)
 601:     {
 602:         if($tag->getName()!=$tagName)
 603:         {
 604:             throw new \IllegalStateException(sprintf("An %s component can only be added to the HTML element %s", get_called_class(), $tagName));
 605:         }
 606:     }
 607:     
 608:     /**
 609:      * Checks that a component tag as an attribute and that the attribute has the required value
 610:      * Throws an IllegalStateException if it is not
 611:      * @param ComponentTag $tag The tag to check
 612:      * @param String $attribute The attribute to find
 613:      * @param String $value The value the attribute will have
 614:      */
 615:     protected function checkComponentTagAttribute(ComponentTag $tag, $attribute, $value)
 616:     {
 617:         $attributes = $tag->getAttributes();
 618:         
 619:         if(!array_key_exists($attribute, $attributes) || $attributes[$attribute] != $value)
 620:         {
 621:             throw new \IllegalStateException(sprintf("An %s component can only be added to a tag with a %s of %s", get_called_class(), $attribute, $value));
 622:         }
 623:     }
 624:     
 625:     /**
 626:      * Visit all the parent components of this components and execute
 627:      * a callback on each
 628:      * @param Identifier $identifier The identifier of the parent to look for
 629:      * @param closure $callback The callback to run
 630:      */
 631:     public function visitParents(Identifier $identifier, $callback)
 632:     {
 633:         Args::callBackArgs($callback, 1, 'callback');
 634:         $this->internalVisitParents($identifier, $this->parent, $callback);
 635:     }
 636:     
 637:     private function internalVisitParents(Identifier $identifier, $component, $callback)
 638:     {
 639:         if($component!=null)
 640:         {
 641:             $response = self::VISITOR_CONTINUE_TRAVERSAL;
 642:             if($component::getIdentifier()->of($identifier))
 643:             {
 644:                 $response = $callback($component);
 645:             }
 646:             if($response==self::VISITOR_CONTINUE_TRAVERSAL)
 647:             {
 648:                 $this->internalVisitParents($identifier, $component->parent, $callback);
 649:             }
 650:         }
 651:     }
 652:     
 653:     public function getPage()
 654:     {
 655:         $current = $this;
 656:         while($current!=null)
 657:         {
 658:             if($current instanceof WebPage)
 659:             {
 660:                 return $current;
 661:             }
 662:             $current = $current->getParent();
 663:         }
 664:         return null;
 665:     }
 666:     
 667:     protected function setParent($parent)
 668:     {
 669:         $this->parent = $parent;
 670:     }
 671:     
 672:     public final function getApplication()
 673:     {
 674:         return $GLOBALS['application'];
 675:     }
 676:     
 677:     public final function getRequestCycle()
 678:     {
 679:         return $GLOBALS['requestCycle'];
 680:     }
 681:     
 682:     public final function getRequest()
 683:     {
 684:         return $this->getRequestCycle()->getRequest();
 685:     }
 686:     
 687:     public final function getResponse()
 688:     {
 689:         return $this->getRequestCycle()->getResponse();
 690:     }
 691:     
 692:     public function getId()
 693:     {
 694:         return $this->id;
 695:     }
 696:     
 697:     public static function getIdentifier()
 698:     {
 699:         return Identifier::forName(get_called_class());
 700:     }
 701:     
 702:     /**
 703:      * Gets whether or not this component is stateless
 704:      * @return boolean
 705:      */
 706:     public function isStateless()
 707:     {
 708:         foreach($this->behaviours as $behaviour)
 709:         {
 710:             if(!$behaviour->isStateless())
 711:             {
 712:                 return false;
 713:             }
 714:         }
 715:         return true;
 716:     }
 717:     
 718:     public function get($id)
 719:     {
 720:         if(empty($id))
 721:         {
 722:             return $this;
 723:         }
 724:        
 725:         throw new \InvalidArgumentException("This component is not a container and does not have any children.");
 726:     }
 727:     
 728:     /**
 729:      * Generate a URL for a particular action:
 730:      * 
 731:      * WebPage Identifier - Generates a URL for the web page
 732:      * WebPage Instance - Generate a URL for the page instance
 733:      * 
 734:      * @param mixed $for
 735:      * @return type 
 736:      */
 737:     public function generateUrlFor($for)
 738:     {
 739:         if($for instanceof Listener)
 740:         {
 741:             return $this->urlForListener($for);
 742:         }
 743:         else if($for instanceof Identifier)
 744:         {
 745:             return $this->urlForPage($page);
 746:         }
 747:         else if($for instanceof WebPage)
 748:         {
 749:             return $this->urlForPageInstance($for);
 750:         }
 751:         else
 752:         {
 753:             throw new \InvalidArgumentException(sprintf("generateUrlFor expected argment of type Identifier or Listener or WebPage, actual %s", get_class($for)));
 754:         }
 755:     }
 756:     
 757:     /**
 758:      * @todo this should use a request target
 759:      * @param Identifier $page
 760:      * @return type 
 761:      */
 762:     public function urlForPage(Identifier $page)
 763:     {
 764:         if(is_subclass_of($page->getFullyQualifiedName(), WebPage::getIdentifier()->getFullyQualifiedName()))
 765:         {
 766:             return $this->getRequest()->getRootPath().$page->getFullyQualifiedName();
 767:         }
 768:         throw new \InvalidArgumentException(sprintf("Expected identifier of a web page, actual %s", $page->getFullyQualifiedName()));
 769:     }
 770:     
 771:     
 772:     public function urlForPageInstance(WebPage $pageInstance)
 773:     {
 774:         
 775:     }
 776:     
 777:     /**
 778:      * @todo this should use a request target
 779:      * @param Identifier $page
 780:      * @return type 
 781:      */
 782:     public function urlForListener(Listener $listener)
 783:     {
 784:         $target;
 785:         $page = $this->getPage();
 786:         
 787:         $behaviour = null;
 788:         
 789:         if($listener instanceof Behaviour)
 790:         {
 791:             $behaviour = $listener->getBehaviourId();
 792:         }
 793:         
 794:         if($page->isPageStateless())
 795:         {
 796:             $target = new PageRequestWithListenerTarget($page::getIdentifier(), $this->getComponentPath(), $behaviour);
 797:         }
 798:         else
 799:         {
 800:             $target = new ListenerRequestTarget($this->getPage(), $this->getComponentPath(), $behaviour);
 801:         }
 802:         return $this->getRequestCycle()->generateUrl($target);
 803:     }
 804:     
 805:     public function getComponentPath()
 806:     {
 807:         $page = $this->getPage();
 808:         if($page==null)
 809:         {
 810:             throw new \IllegalStateException(sprintf("Unable to generate a path for component %s as it has an incomplete hierarchy.", $this->id));
 811:         }
 812:         
 813:         $path = $this->getId();
 814:         
 815:         $callback = function($component) use (&$path)
 816:         {
 817:             if(!($component instanceof WebPage))
 818:             {
 819:                 $path = $component->getId().Component::PATH_SEPERATOR.$path;
 820:                 return Component::VISITOR_CONTINUE_TRAVERSAL;
 821:             }
 822:             return Component::VISITOR_STOP_TRAVERSAL;
 823:         };
 824:         $this->visitParents(Component::getIdentifier(), $callback);
 825: 
 826:         return str_replace(self::PATH_SEPERATOR.self::PATH_SEPERATOR, '', $path.self::PATH_SEPERATOR);
 827:     }
 828:     
 829:     protected function newMarkupSource()
 830:     {
 831:         return new DefaultMarkupSource();
 832:     }
 833:     
 834:     /**
 835:      * Set the current page
 836:      * @param mixed $page An instance of web page or an Identifier for a web page
 837:      * @todo add page params
 838:      */
 839:     public function setPage($page)
 840:     {
 841:         Args::notNull($page, 'page');
 842:         if($page instanceof Identifier)
 843:         {
 844:             if($page->of(WebPage::getIdentifier()))
 845:             {
 846:                 $target = new PageRequestTarget($page);
 847:             }
 848:             else
 849:             {
 850:                 throw new \InvalidArgumentException("Expected identifier to be for a web page");
 851:             }
 852:         }
 853:         else if($page instanceof WebPage)
 854:         {
 855:             PageMap::get()->addOrUpdate($page);
 856:             $target = new PageInstanceRequestTarget($page);
 857:         }
 858:         else
 859:         {
 860:             throw new \InvalidArgumentException(sprintf("setPage expects an identifier for a web page or an instance of a web page and not a %s", get_class($page)));
 861:         }
 862:         
 863:         if($this->getRequestCycle()->containsTarget(ListenerRequestTarget::getIdentifier()))
 864:         {
 865:             $url = $this->getRequestCycle()->generateUrl($target);
 866:             $this->getRequestCycle()->addTarget(new RedirectRequestTarget($url));
 867:         }
 868:         else
 869:         {
 870:             $this->getRequestCycle()->addTarget($target);
 871:         }
 872:     }
 873:     
 874:     /**
 875:      * @todo add support for model inheritence (compound models)
 876:      * @return Model The model for this component
 877:      */
 878:     public function getModel()
 879:     {
 880:         if($this->model==null)
 881:         {
 882:             $model = null;
 883:             $current = $this->getParent();
 884:             while($current!=null)
 885:             {
 886:                 if($current->model!=null && $current->model instanceof ComponentInheritedModel)
 887:                 {
 888:                     $model = $current->model;
 889:                     break;
 890:                 }
 891:                 $current = $current->getParent();
 892:             }
 893:             
 894:             if($model!=null)
 895:             {
 896:                 $model = $model->onInherit($this);
 897:                 
 898:                 if($model!=null)
 899:                 {
 900:                     $this->model = $model;
 901:                     $this->model->bind($this);
 902:                 }
 903:             }
 904:         }
 905:         return $this->model;
 906:     }
 907:     
 908:     public function setMarkup(MarkupElement $markup)
 909:     {
 910:         $this->markup = $markup;
 911:     }
 912:     
 913:     public function setModel(Model &$model)
 914:     {
 915:         $this->model = $model;
 916:     }
 917:     
 918:     protected function getMarkUpSource()
 919:     {
 920:         if($this->markupSource==null)
 921:         {
 922:             $this->markupSource = $this->newMarkupSource();
 923:         }
 924:         return $this->markupSource;
 925:     }
 926:     
 927:     public function getParent()
 928:     {
 929:         return $this->parent;
 930:     }
 931:     
 932:     public function setModelObject(&$object)
 933:     {
 934:         if($this->getModel()!=null)
 935:         {
 936:             $this->getModel()->setModelObject($object);
 937:         }
 938:     }
 939:     
 940:     public function getModelObject()
 941:     {
 942:         if($this->getModel()!=null)
 943:         {
 944:             return $this->getModel()->getModelObject();
 945:         }
 946:         return null;
 947:     }
 948:     
 949:     private static function getNextComponentId()
 950:     {
 951:         self::$nextId++;
 952:         return dechex(self::$nextId);
 953:     }
 954:     
 955:     /**
 956:      * @todo should really create converters for primatives
 957:      * @return string a representation of the model object as a string
 958:      */
 959:     public function getModelObjectAsString()
 960:     {
 961:         $object = $this->getModelObject();
 962:         if(is_object($object))
 963:         {
 964:             $converter = $this->getApplication()->getConverter(get_class($object));
 965:             
 966:             if($converter==null)
 967:             {
 968:                 throw new \RuntimeException(sprintf("Unable to find converter for type %s", get_class($object)));
 969:             }
 970:             $string = $converter->convertToString($object);
 971:             
 972:             if(!is_string($string))
 973:             {
 974:                 throw new \RuntimeException("Convert did not correctly convert to string");
 975:             }
 976:             return $string;
 977:         }
 978:         else if(is_array($object))
 979:         {
 980:             throw new \RuntimeException("getModelObjectAsString() does not support array");
 981:         }
 982:         else if(is_bool($object))
 983:         {
 984:             return $object ? 'true':'false';
 985:         }
 986:         else
 987:         {
 988:             settype($object, 'string');
 989:             return $object;
 990:         }
 991:     }
 992:     
 993:     public function fatel($message)
 994:     {
 995:         FeedbackModel::get()->addMessage(new FeedbackMessage(FeedbackMessage::FEEDBACK_MEESAGE_FATEL, $message, $this));
 996:     }
 997:     
 998:     public function error($message)
 999:     {
1000:         FeedbackModel::get()->addMessage(new FeedbackMessage(FeedbackMessage::FEEDBACK_MEESAGE_ERROR, $message, $this));
1001:     }
1002:     
1003:     public function warning($message)
1004:     {
1005:         FeedbackModel::get()->addMessage(new FeedbackMessage(FeedbackMessage::FEEDBACK_MEESAGE_WARNING, $message, $this));
1006:     }
1007:     
1008:     public function info($message)
1009:     {
1010:         FeedbackModel::get()->addMessage(new FeedbackMessage(FeedbackMessage::FEEDBACK_MEESAGE_INFO, $message, $this));
1011:     }
1012:     
1013:     public function success($message)
1014:     {
1015:         FeedbackModel::get()->addMessage(new FeedbackMessage(FeedbackMessage::FEEDBACK_MEESAGE_SUCCESS, $message, $this));
1016:     }
1017:     
1018:     public function hasMessage($level = null)
1019:     {
1020:         return FeedbackModel::get()->hasMessages($this, $level);
1021:     }
1022:     
1023:     public function hasErrorMessage()
1024:     {
1025:         return FeedbackModel::get()->hasMessages($this, FeedbackMessage::FEEDBACK_MEESAGE_ERROR);
1026:     }
1027:     
1028:     private function notifyBehavioursBeforeRender()
1029:     {
1030:         foreach($this->behaviours as $behaviour)
1031:         {
1032:             $behaviour->beforeRender($this);
1033:         }
1034:     }
1035:     
1036:     private function notifyBehavioursAfterRender()
1037:     {
1038:         foreach($this->behaviours as $behaviour)
1039:         {
1040:             $behaviour->afterRender($this);
1041:         }
1042:     }
1043:     
1044:     private function notifyBehavioursComponentTag(ComponentTag $tag)
1045:     {
1046:         foreach($this->behaviours as $behaviour)
1047:         {
1048:             $behaviour->onComponentTag($this, $tag);
1049:         }
1050:     }
1051: 
1052:     /**
1053:      * Called by the header container when the HTML <head> is rendering
1054:      * @param HeaderContainer $container
1055:      * @param HeaderResponse $headerResponse 
1056:      */
1057:     public final function renderHeadContainer(HeaderContainer $container, HeaderResponse $headerResponse)
1058:     {
1059:         $this->getMarkUpSource()->renderHead($this, $container, $headerResponse);
1060:         $this->renderHead($headerResponse);
1061:         
1062:         foreach($this->behaviours as $behaviour)
1063:         {
1064:             $behaviour->renderHead($this, $container, $headerResponse);
1065:         }
1066:     }
1067:     
1068:     /**
1069:      * Called for each component when the HTML <head> is rendering.
1070:      * @param HeaderResponse $headerResponse The response to write to
1071:      */
1072:     public function renderHead(HeaderResponse $headerResponse)
1073:     {
1074:         if($this->renderHeadCallback!=null)
1075:         {
1076:             $callable = $this->renderHeadCallback;
1077:             $callable($this, $headerResponse);
1078:         }
1079:     }
1080:     
1081:     /**
1082:      * Sets whether this component will render its open and close
1083:      * tags
1084:      * @param boolean $renderBodyOnly 
1085:      */
1086:     public function setRenderBodyOnly($renderBodyOnly)
1087:     {
1088:         Args::isBoolean($renderBodyOnly, 'renderBodyOnly');
1089:         $this->renderBodyOnly = $renderBodyOnly;
1090:     }
1091:     
1092:     public function setVisible($visible)
1093:     {
1094:         Args::isBoolean($visible, 'visible');
1095:         $this->visible = $visible;
1096:     }
1097:     
1098:     public function getBehaviours()
1099:     {
1100:         return $this->behaviours;
1101:     }
1102:     
1103:     public function getBehaviourById($id)
1104:     {
1105:         if(array_key_exists($id, $this->behaviours))
1106:         {
1107:             return $this->behaviours[$id];
1108:         }
1109:         return null;
1110:     }
1111:     
1112:     private function setAuto()
1113:     {
1114:         $this->auto = true;
1115:     }
1116: 
1117: 
1118:     public function isAuto()
1119:     {
1120:         return $this->auto;
1121:     }
1122:     
1123:     public function detach()
1124:     {
1125:         
1126:     }
1127:     
1128:     public function getLocalizer()
1129:     {
1130:         return Localizer::get($this);
1131:     }
1132:     
1133:     public function getComponentKey($suffix)
1134:     {
1135:         return sprintf("%s.%s.%s", get_class($this->getPage()), $this->id, $suffix);
1136:     }
1137:     
1138:     public function isRendered()
1139:     {
1140:         return $this->rendered;
1141:     }
1142:     
1143:     public function setBeforePageRenderCallback($beforePageRenderCallback)
1144:     {
1145:         Args::callBackArgs($beforePageRenderCallback, 1, 'beforePageRenderCallback');
1146:         $this->beforePageRenderCallback = $beforePageRenderCallback;
1147:     }
1148:     
1149:     public function setBeforeComponentRenderCallback($beforeComponentRenderCallback)
1150:     {
1151:         Args::callBackArgs($beforeComponentRenderCallback, 1, 'beforeComponentRenderCallback');
1152:         $this->beforeComponentRenderCallback = $beforeComponentRenderCallback;
1153:     }
1154:     
1155:     public function setAfterPageRenderCallback($afterPageRenderCallback)
1156:     {
1157:         Args::callBackArgs($afterPageRenderCallback, 1, 'afterPageRenderCallback');
1158:         $this->afterPageRenderCallback = $afterPageRenderCallback;
1159:     }
1160:     
1161:     public function setAfterComponentRenderCallback($afterComponentRenderCallback)
1162:     {
1163:         Args::callBackArgs($afterComponentRenderCallback, 1, 'afterComponentRenderCallback');
1164:         $this->afterComponentRenderCallback = $afterComponentRenderCallback;
1165:     }
1166:     
1167:     public function setOnComponentTagCallback($onComponentTagCallback)
1168:     {
1169:         Args::callBackArgs($onComponentTagCallback, 2, 'onComponentTagCallback');
1170:         $this->onComponentTagCallback = $onComponentTagCallback;
1171:     }
1172:     
1173:     public function setOnComponentTagBodyCallback($onComponentTagBodyCallback)
1174:     {
1175:         Args::callBackArgs($onComponentTagBodyCallback, 2, 'onComponentTagBodyCallback');
1176:         $this->onComponentTagBodyCallback = $onComponentTagBodyCallback;
1177:     }
1178:     
1179:     public function setRenderHeadCallback($renderHeadCallback)
1180:     {
1181:         Args::callBackArgs($renderHeadCallback, 2, 'renderHeadCallback');
1182:         $this->renderHeadCallback = $renderHeadCallback;
1183:     }
1184: }
1185: 
1186: ?>
1187: 
Picon Framework API documentation generated by ApiGen 2.7.0