| <?php |
| /** |
| * File containing the ezcWorkflowNode class. |
| * |
| * This class provides basic facilities for workflow nodes |
| * such as constraints for in and output nodes and methods |
| * for identifying and execution of the node. |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| * @package Workflow |
| * @version //autogen// |
| * @copyright Copyright (C) 2005-2010 eZ Systems AS. All rights reserved. |
| * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0 |
| */ |
| |
| /** |
| * Abstract base class for workflow nodes. |
| * |
| * All workflow nodes must extend this class. |
| * |
| * @package Workflow |
| * @version //autogen// |
| * @mainclass |
| */ |
| abstract class ezcWorkflowNode implements ezcWorkflowVisitable |
| { |
| /** |
| * The node is waiting to be activated. |
| */ |
| const WAITING_FOR_ACTIVATION = 0; |
| |
| /** |
| * The node is activated and waiting to be executed. |
| */ |
| const WAITING_FOR_EXECUTION = 1; |
| |
| /** |
| * Unique ID of this node. |
| * |
| * Only available when the workflow this node belongs to has been loaded |
| * from or saved to the data storage. |
| * |
| * @var integer |
| */ |
| protected $id = false; |
| |
| /** |
| * The incoming nodes of this node. |
| * |
| * @var array( int => ezcWorkflowNode ) |
| */ |
| protected $inNodes = array(); |
| |
| /** |
| * The outgoing nodes of this node. |
| * |
| * @var array( int => ezcWorkflowNode ) |
| */ |
| protected $outNodes = array(); |
| |
| /** |
| * Constraint: The minimum number of incoming nodes this node has to have |
| * to be valid. Set to false to disable this constraint. |
| * |
| * @var integer |
| */ |
| protected $minInNodes = 1; |
| |
| /** |
| * Constraint: The maximum number of incoming nodes this node has to have |
| * to be valid. Set to false to disable this constraint. |
| * |
| * @var integer |
| */ |
| protected $maxInNodes = 1; |
| |
| /** |
| * Constraint: The minimum number of outgoing nodes this node has to have |
| * to be valid. Set to false to disable this constraint. |
| * |
| * @var integer |
| */ |
| protected $minOutNodes = 1; |
| |
| /** |
| * Constraint: The maximum number of outgoing nodes this node has to have |
| * to be valid. Set to false to disable this constraint. |
| * |
| * @var integer |
| */ |
| protected $maxOutNodes = 1; |
| |
| /** |
| * The number of incoming nodes. |
| * |
| * @var integer |
| */ |
| protected $numInNodes = 0; |
| |
| /** |
| * The number of outgoing nodes. |
| * |
| * @var integer |
| */ |
| protected $numOutNodes = 0; |
| |
| /** |
| * The configuration of this node. |
| * |
| * The configuration is a structured (hash) array with the |
| * various options of the implemented node. |
| * |
| * This functionality is implemented as an array to make it possible |
| * to have the storage engines unaware of the node classes. |
| * |
| * @var array( config key => config value ) |
| */ |
| protected $configuration; |
| |
| /** |
| * The state of this node. |
| * |
| * @var integer |
| */ |
| protected $activationState; |
| |
| /** |
| * The node(s) that activated this node. |
| * |
| * @var ezcWorkflowNode[] |
| */ |
| protected $activatedFrom = array(); |
| |
| /** |
| * The state of this node. |
| * |
| * @var mixed |
| */ |
| protected $state = null; |
| |
| /** |
| * The id of the thread this node is executing in. |
| * |
| * @var integer |
| */ |
| protected $threadId = null; |
| |
| /** |
| * Flag that indicates whether an add*Node() or remove*Node() |
| * call is internal. This is necessary to avoid unlimited loops. |
| * |
| * @var boolean |
| */ |
| protected static $internalCall = false; |
| |
| /** |
| * Constructs a new node with the configuration $configuration. |
| * |
| * The configuration is a structured (hash) array. Implementations |
| * must pass their complete configuration on to this object. We have |
| * chosen to use structured arrays for the configuration since it |
| * simplifies the process of creating new node types and storing workflows. |
| * |
| * @param mixed $configuration |
| */ |
| public function __construct( $configuration = null ) |
| { |
| if ( $configuration !== null ) |
| { |
| $this->configuration = $configuration; |
| } |
| |
| $this->initState(); |
| } |
| |
| /** |
| * Adds a node to the incoming nodes of this node. |
| * |
| * Automatically adds $node to the workflow and adds |
| * this node as an out node of $node. |
| * |
| * @param ezcWorkflowNode $node The node that is to be added as incoming node. |
| * @throws ezcWorkflowInvalidWorkflowException if the operation violates the constraints of the nodes involved. |
| * @return ezcWorkflowNode |
| */ |
| public function addInNode( ezcWorkflowNode $node ) |
| { |
| // Check whether the node is already an incoming node of this node. |
| if ( ezcWorkflowUtil::findObject( $this->inNodes, $node ) === false ) |
| { |
| // Add this node as an outgoing node to the other node. |
| if ( !self::$internalCall ) |
| { |
| self::$internalCall = true; |
| $node->addOutNode( $this ); |
| } |
| else |
| { |
| self::$internalCall = false; |
| } |
| |
| // Add the other node as an incoming node to this node. |
| $this->inNodes[] = $node; |
| $this->numInNodes++; |
| } |
| |
| return $this; |
| } |
| |
| /** |
| * Removes a node from the incoming nodes of this node. |
| * |
| * Automatically removes $this as an out node of $node. |
| * |
| * @param ezcWorkflowNode $node The node that is to be removed as incoming node. |
| * @throws ezcWorkflowInvalidWorkflowException if the operation violates the constraints of the nodes involved. |
| * @return boolean |
| */ |
| public function removeInNode( ezcWorkflowNode $node ) |
| { |
| $index = ezcWorkflowUtil::findObject( $this->inNodes, $node ); |
| |
| if ( $index !== false ) |
| { |
| // Remove this node as an outgoing node from the other node. |
| if ( !self::$internalCall ) |
| { |
| self::$internalCall = true; |
| $node->removeOutNode( $this ); |
| } |
| else |
| { |
| self::$internalCall = false; |
| } |
| |
| unset( $this->inNodes[$index] ); |
| $this->numInNodes--; |
| |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /** |
| * Adds a node to the outgoing nodes of this node. |
| * |
| * Automatically adds $node to the workflow and adds |
| * this node as an in node of $node. |
| * |
| * @param ezcWorkflowNode $node The node that is to be added as outgoing node. |
| * @throws ezcWorkflowInvalidWorkflowException if the operation violates the constraints of the nodes involved. |
| * @return ezcWorkflowNode |
| */ |
| public function addOutNode( ezcWorkflowNode $node ) |
| { |
| // Check whether the other node is already an outgoing node of this node. |
| if ( ezcWorkflowUtil::findObject( $this->outNodes, $node ) === false ) |
| { |
| // Add this node as an incoming node to the other node. |
| if ( !self::$internalCall ) |
| { |
| self::$internalCall = true; |
| $node->addInNode( $this ); |
| } |
| else |
| { |
| self::$internalCall = false; |
| } |
| |
| // Add the other node as an outgoing node to this node. |
| $this->outNodes[] = $node; |
| $this->numOutNodes++; |
| } |
| |
| return $this; |
| } |
| |
| /** |
| * Removes a node from the outgoing nodes of this node. |
| * |
| * Automatically removes $this as an in node of $node. |
| * |
| * @param ezcWorkflowNode $node The node that is to be removed as outgoing node. |
| * @throws ezcWorkflowInvalidWorkflowException if the operation violates the constraints of the nodes involved. |
| * @return boolean |
| */ |
| public function removeOutNode( ezcWorkflowNode $node ) |
| { |
| $index = ezcWorkflowUtil::findObject( $this->outNodes, $node ); |
| |
| if ( $index !== false ) |
| { |
| // Remove this node as an incoming node from the other node. |
| if ( !self::$internalCall ) |
| { |
| self::$internalCall = true; |
| $node->removeInNode( $this ); |
| } |
| else |
| { |
| self::$internalCall = false; |
| } |
| |
| unset( $this->outNodes[$index] ); |
| $this->numOutNodes--; |
| |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /** |
| * Returns the Id of this node. |
| * |
| * @return integer |
| * @ignore |
| */ |
| public function getId() |
| { |
| return $this->id; |
| } |
| |
| /** |
| * Sets the Id of this node. |
| * |
| * @param int $id |
| * @ignore |
| */ |
| public function setId( $id ) |
| { |
| $this->id = $id; |
| } |
| |
| /** |
| * Sets the activation state for this node. |
| * |
| * One of ezcWorkflowNode::WAITING_FOR_ACTIVATION or |
| * ezcWorkflowNode::WAITING_FOR_EXECUTION. |
| * |
| * @param int $activationState |
| * @ignore |
| */ |
| public function setActivationState( $activationState ) |
| { |
| if ( $activationState == self::WAITING_FOR_ACTIVATION || |
| $activationState == self::WAITING_FOR_EXECUTION ) |
| { |
| $this->activationState = $activationState; |
| } |
| } |
| |
| /** |
| * Returns the incoming nodes of this node. |
| * |
| * @return ezcWorkflowNode[] |
| */ |
| public function getInNodes() |
| { |
| return $this->inNodes; |
| } |
| |
| /** |
| * Returns the outgoing nodes of this node. |
| * |
| * @return ezcWorkflowNode[] |
| */ |
| public function getOutNodes() |
| { |
| return $this->outNodes; |
| } |
| |
| /** |
| * Returns the configuration of this node. |
| * |
| * @return mixed |
| */ |
| public function getConfiguration() |
| { |
| return $this->configuration; |
| } |
| |
| /** |
| * Returns the state of this node. |
| * |
| * @return mixed |
| * @ignore |
| */ |
| public function getState() |
| { |
| return $this->state; |
| } |
| |
| /** |
| * Sets the state of this node. |
| * |
| * @param mixed $state |
| * @ignore |
| */ |
| public function setState( $state ) |
| { |
| $this->state = $state; |
| } |
| |
| /** |
| * Returns the node(s) that activated this node. |
| * |
| * @return array |
| * @ignore |
| */ |
| public function getActivatedFrom() |
| { |
| return $this->activatedFrom; |
| } |
| |
| /** |
| * Sets the node(s) that activated this node. |
| * |
| * @param array $activatedFrom |
| * @ignore |
| */ |
| public function setActivatedFrom( array $activatedFrom ) |
| { |
| $this->activatedFrom = $activatedFrom; |
| } |
| |
| /** |
| * Returns the id of the thread this node is executing in. |
| * |
| * @return integer |
| * @ignore |
| */ |
| public function getThreadId() |
| { |
| return $this->threadId; |
| } |
| |
| /** |
| * Sets the id of the thread this node is executing in. |
| * |
| * @param int $threadId |
| * @ignore |
| */ |
| public function setThreadId( $threadId ) |
| { |
| $this->threadId = $threadId; |
| } |
| |
| /** |
| * Checks this node's constraints. |
| * |
| * The constraints checked are the minimum in nodes |
| * maximum in nodes, minimum out nodes and maximum |
| * out nodes. |
| * |
| * @throws ezcWorkflowInvalidWorkflowException if the constraints of this node are not met. |
| */ |
| public function verify() |
| { |
| $type = str_replace( 'ezcWorkflowNode', '', get_class( $this ) ); |
| |
| if ( $this->minInNodes !== false && $this->numInNodes < $this->minInNodes ) |
| { |
| throw new ezcWorkflowInvalidWorkflowException( |
| sprintf( |
| 'Node of type "%s" has less incoming nodes than required.', |
| $type |
| ) |
| ); |
| } |
| |
| if ( $this->maxInNodes !== false && $this->numInNodes > $this->maxInNodes ) |
| { |
| throw new ezcWorkflowInvalidWorkflowException( |
| sprintf( |
| 'Node of type "%s" has more incoming nodes than allowed.', |
| $type |
| ) |
| ); |
| } |
| |
| if ( $this->minOutNodes !== false && $this->numOutNodes < $this->minOutNodes ) |
| { |
| throw new ezcWorkflowInvalidWorkflowException( |
| sprintf( |
| 'Node of type "%s" has less outgoing nodes than required.', |
| $type |
| ) |
| ); |
| } |
| |
| if ( $this->maxOutNodes !== false && $this->numOutNodes > $this->maxOutNodes ) |
| { |
| throw new ezcWorkflowInvalidWorkflowException( |
| sprintf( |
| 'Node of type "%s" has more outgoing nodes than allowed.', |
| $type |
| ) |
| ); |
| } |
| } |
| |
| /** |
| * Reimplementation of accept() calls accept on all out nodes. |
| * |
| * @param ezcWorkflowVisitor $visitor |
| */ |
| public function accept( ezcWorkflowVisitor $visitor ) |
| { |
| if ( $visitor->visit( $this ) ) |
| { |
| foreach ( $this->outNodes as $outNode ) |
| { |
| $outNode->accept( $visitor ); |
| } |
| } |
| } |
| |
| /** |
| * Activate this node in the execution environment $execution. |
| * |
| * $activatedFrom is the node that activated this node and $threadId is |
| * threadId of the thread the node should be activated in. |
| * |
| * This method is called by other nodes and/or the execution environment |
| * depending on the workflow. |
| * |
| * @param ezcWorkflowExecution $execution |
| * @param ezcWorkflowNode $activatedFrom |
| * @param int $threadId |
| * @ignore |
| */ |
| public function activate( ezcWorkflowExecution $execution, ezcWorkflowNode $activatedFrom = null, $threadId = 0 ) |
| { |
| if ( $this->activationState === self::WAITING_FOR_ACTIVATION ) |
| { |
| $this->activationState = self::WAITING_FOR_EXECUTION; |
| $this->setThreadId( $threadId ); |
| |
| if ( $activatedFrom !== null ) |
| { |
| $this->activatedFrom[] = get_class( $activatedFrom ); |
| } |
| |
| $execution->activate( $this ); |
| } |
| } |
| |
| /** |
| * Convenience method for activating an (outgoing) node. |
| * |
| * @param ezcWorkflowExecution $execution |
| * @param ezcWorkflowNode $node |
| */ |
| protected function activateNode( ezcWorkflowExecution $execution, ezcWorkflowNode $node ) |
| { |
| $node->activate( $execution, $this, $this->getThreadId() ); |
| } |
| |
| /** |
| * Returns true if this node is ready for execution |
| * and false if it is not. |
| * |
| * @return boolean |
| * @ignore |
| */ |
| public function isExecutable() |
| { |
| return $this->activationState === self::WAITING_FOR_EXECUTION; |
| } |
| |
| /** |
| * Executes and performs the workflow duties of this node |
| * and returns true if the node completed execution. |
| * |
| * Implementations of ezcWorkflowNode should reimplement this method. |
| * |
| * This method is called automatically by the workflow execution |
| * environment and should not be called directly. |
| * |
| * The default implementation resets the activation state of the |
| * node. |
| * |
| * @param ezcWorkflowExecution $execution |
| * @return boolean true when the node finished execution, |
| * and false otherwise |
| * @ignore |
| */ |
| public function execute( ezcWorkflowExecution $execution ) |
| { |
| $this->activationState = self::WAITING_FOR_ACTIVATION; |
| $this->activatedFrom = array(); |
| $this->threadId = null; |
| |
| return true; |
| } |
| |
| /** |
| * Generate node configuration from XML representation. |
| * |
| * @param DOMElement $element |
| * @ignore |
| */ |
| public static function configurationFromXML( DOMElement $element ) |
| { |
| } |
| |
| /** |
| * Generate XML representation of this node's configuration. |
| * |
| * @param DOMElement $element |
| * @ignore |
| */ |
| public function configurationToXML( DOMElement $element ) |
| { |
| } |
| |
| /** |
| * Returns a textual representation of this node. |
| * |
| * @return string |
| * @ignore |
| */ |
| public function __toString() |
| { |
| $type = str_replace( 'ezcWorkflowNode', '', get_class( $this ) ); |
| $max = strlen( $type ); |
| $string = ''; |
| |
| for ( $i = 0; $i < $max; $i++ ) |
| { |
| if ( $i > 0 && ord( $type[$i] ) >= 65 && ord( $type[$i] ) <= 90 ) |
| { |
| $string .= ' '; |
| } |
| |
| $string .= $type[$i]; |
| } |
| |
| return $string; |
| } |
| |
| /** |
| * Initializes the state of this node. |
| * |
| * @ignore |
| */ |
| public function initState() |
| { |
| $this->activatedFrom = array(); |
| $this->state = null; |
| $this->threadId = null; |
| |
| $this->setActivationState( self::WAITING_FOR_ACTIVATION ); |
| } |
| } |
| ?> |