Mailcode_Collection_NestingValidator   A
last analyzed

Complexity

Total Complexity 21

Size/Duplication

Total Lines 204
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 81
c 1
b 0
f 0
dl 0
loc 204
rs 10
wmc 21

10 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 3 1
A log() 0 2 1
A getOpenCommand() 0 19 3
A getCollection() 0 3 1
A validate_Opening() 0 5 1
A validate_Unclosed() 0 15 2
A validate_Closing() 0 26 3
A validate_Sibling() 0 36 3
A validate_Standalone() 0 2 1
A validate() 0 37 5
1
<?php
2
/**
3
 * File containing the {@see Mailcode_Collection} class.
4
 *
5
 * @package Mailcode
6
 * @subpackage Collection
7
 * @see Mailcode_Collection
8
 */
9
10
declare(strict_types=1);
11
12
namespace Mailcode;
13
14
use AppUtils\OperationResult;
15
16
/**
17
 * Commands collection: container for commands.
18
 *
19
 * @package Mailcode
20
 * @subpackage Collection
21
 * @author Sebastian Mordziol <[email protected]>
22
 */
23
class Mailcode_Collection_NestingValidator
24
{
25
    public const ERROR_MISSING_COMMAND_TYPE_METHOD = 49001;
26
    
27
    public const VALIDATION_SIBLING_WITHOUT_PARENT = 49101;
28
    public const VALIDATION_SIBLING_WRONG_PARENT = 49102; 
29
    public const VALIDATION_COMMANDS_ALREADY_CLOSED = 49103;
30
    public const VALIDATION_UNCLOSED_COMMAND = 49104;
31
    public const VALIDATION_CLOSING_NON_OPENING_COMMAND = 49105;
32
    
33
   /**
34
    * @var Mailcode_Collection
35
    */
36
    protected $collection;
37
    
38
   /**
39
    * @var OperationResult
40
    */
41
    protected $validationResult;
42
    
43
   /**
44
    * @var Mailcode_Commands_Command_Type[]
45
    */
46
    protected $stack = array();
47
    
48
    public function __construct(Mailcode_Collection $collection)
49
    {
50
        $this->collection = $collection;
51
    }
52
    
53
    public function getCollection() : Mailcode_Collection
54
    {
55
        return $this->collection;
56
    }
57
58
    /**
59
     * @return OperationResult
60
     * @throws Mailcode_Exception
61
     *
62
     * @see Mailcode_Collection_NestingValidator::validate_Closing()
63
     * @see Mailcode_Collection_NestingValidator::validate_Opening()
64
     * @see Mailcode_Collection_NestingValidator::validate_Sibling()
65
     * @see Mailcode_Collection_NestingValidator::validate_Standalone()
66
     * @see Mailcode_Collection_NestingValidator::validate_Unclosed()
67
     */
68
    public function validate() : OperationResult
69
    {
70
        $this->validationResult = new OperationResult($this);
71
        
72
        $commands = $this->collection->getCommands();
73
        
74
        foreach($commands as $command)
75
        {
76
            $method = 'validate_'.$command->getCommandType();
77
            
78
            if(!method_exists($this, $method))
79
            {
80
                throw new Mailcode_Exception(
81
                    'Unknown command type validation method.',
82
                    sprintf(
83
                        'The method [%s] does not exist in class [%s].',
84
                        $method,
85
                        get_class($this)
86
                    ),
87
                    self::ERROR_MISSING_COMMAND_TYPE_METHOD
88
                );
89
            }
90
            
91
            $this->$method($command);
92
            
93
            if(!$this->validationResult->isValid())
94
            {
95
                break;
96
            }
97
        }
98
        
99
        if($this->validationResult->isValid())
100
        {
101
            $this->validate_Unclosed();
102
        }
103
        
104
        return $this->validationResult;
105
    }
106
107
    protected function validate_Unclosed() : void
108
    {
109
        $leftover = $this->getOpenCommand();
110
        
111
        if($leftover === null)
112
        {
113
            return;
114
        }
115
        
116
        $this->validationResult->makeError(
117
            t(
118
                'The command %1$s was never ended.',
119
                $leftover->getName()
120
            ).' ('.$leftover->getMatchedText().')',
121
            self::VALIDATION_UNCLOSED_COMMAND
122
        );
123
    }
124
    
125
    protected function getOpenCommand() : ?Mailcode_Commands_Command_Type_Opening
126
    {
127
        if(empty($this->stack))
128
        {
129
            return null;
130
        }
131
        
132
        end($this->stack);
133
        $idx = key($this->stack);
134
        reset($this->stack);
135
        
136
        $cmd = $this->stack[$idx];
137
        
138
        if($cmd instanceof Mailcode_Commands_Command_Type_Opening)
139
        {
140
            return $cmd;
141
        }
142
        
143
        return null;
144
    }
145
    
146
    protected function validate_Standalone(Mailcode_Commands_Command_Type_Standalone $command) : void
147
    {
148
        // standalone commands have no nesting issues
149
    }
150
    
151
    protected function validate_Opening(Mailcode_Commands_Command_Type_Opening $command) : void
152
    {
153
        $this->log(sprintf('Opening %s', $command->getName()));
154
        
155
        $this->stack[] = $command;
156
    }
157
    
158
    protected function validate_Sibling(Mailcode_Commands_Command_Type_Sibling $command) : void
159
    {
160
        $parent = $this->getOpenCommand();
161
        
162
        if($parent === null)
163
        {
164
            $this->validationResult->makeError(
165
                t(
166
                    '%1$s command has no parent %2$s command.',
167
                    $command->getName(),
168
                    $command->getParentName()
169
                ),
170
                self::VALIDATION_SIBLING_WITHOUT_PARENT
171
            );
172
            
173
            return;
174
        }
175
        
176
        if($parent->getName() !== $command->getParentName())
177
        {
178
            $this->validationResult->makeError(
179
                t(
180
                    '%1$s command cannot be used as child of a %2$s command.',
181
                    $command->getName(),
182
                    $parent->getName()
183
                ),
184
                self::VALIDATION_SIBLING_WRONG_PARENT
185
            );
186
            
187
            return;
188
        }
189
190
        $command->registerOpening($parent);
191
        $parent->registerSibling($command);
192
193
        $this->log(sprintf('Sibling command %s in %s', $command->getName(), $parent->getName()));
194
    }
195
    
196
    protected function validate_Closing(Mailcode_Commands_Command_Type_Closing $command) : void
197
    {
198
        if(empty($this->stack))
199
        {
200
            $this->validationResult->makeError(
201
                t('All open commands have already been ended.'),
202
                self::VALIDATION_COMMANDS_ALREADY_CLOSED
203
            );
204
            
205
            return;
206
        }
207
        
208
        $openingCommand = array_pop($this->stack);
209
210
        if($openingCommand instanceof Mailcode_Commands_Command_Type_Opening)
211
        {
212
            $this->log(sprintf('Closing command %s', $openingCommand->getName()));
213
214
            $command->registerOpening($openingCommand);
215
            $openingCommand->registerClosing($command);
216
            return;
217
        }
218
219
        $this->validationResult->makeError(
220
            t('Trying to close a non-opening command.'),
221
            self::VALIDATION_CLOSING_NON_OPENING_COMMAND
222
        );
223
    }
224
    
225
    protected function log(string $message) : void
226
    {
227
        //echo $message.PHP_EOL;
228
    }
229
}
230