Completed
Push — master ( 71521f...d8e7c5 )
by Vitaly
02:34
created

Builder   C

Complexity

Total Complexity 55

Size/Duplication

Total Lines 520
Duplicated Lines 3.85 %

Coupling/Cohesion

Components 1
Dependencies 4

Test Coverage

Coverage 96.5%

Importance

Changes 0
Metric Value
wmc 55
lcom 1
cbo 4
dl 20
loc 520
ccs 193
cts 200
cp 0.965
rs 6.8
c 0
b 0
f 0

14 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A buildResolverCondition() 0 11 3
A getValidClassMethodsMetadata() 0 13 4
A getValidClassPropertiesMetadata() 0 13 3
A buildResolvingClassDeclaration() 0 4 1
B buildConstructorDependencies() 0 29 5
A build() 0 54 4
A processClassMetadata() 0 13 3
A buildDependencyResolver() 0 14 1
C generateConditions() 0 91 12
B buildResolverArgument() 0 21 7
B buildReflectionClass() 20 34 5
B buildResolverPropertyDeclaration() 0 32 2
B buildResolverMethodDeclaration() 0 38 4

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Builder often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Builder, and based on these observations, apply Extract Interface, too.

1
<?php declare(strict_types = 1);
2
/**
3
 * Created by PhpStorm.
4
 * User: root
5
 * Date: 02.08.16
6
 * Time: 0:46.
7
 */
8
namespace samsonframework\container;
9
10
use samsonframework\container\metadata\ClassMetadata;
11
use samsonframework\container\metadata\MethodMetadata;
12
use samsonframework\container\metadata\PropertyMetadata;
13
use samsonframework\di\Container;
14
use samsonphp\generator\Generator;
15
16
/**
17
 * Container builder.
18
 *
19
 * @author Vitaly Egorov <[email protected]>
20
 */
21
class Builder implements ContainerBuilderInterface
22
{
23
    /** Controller classes scope name */
24
    const SCOPE_CONTROLLER = 'controllers';
25
26
    /** Service classes scope name */
27
    const SCOPE_SERVICES = 'service';
28
29
    /** Generated resolving function name prefix */
30
    const DI_FUNCTION_PREFIX = 'container';
31
32
    /** Generated resolving function service static collection name */
33
    const DI_FUNCTION_SERVICES = self::SCOPE_SERVICES . 'Instances';
34
35
    /** @var string[] Collection of available container scopes */
36
    protected $scopes = [
37
        self::SCOPE_CONTROLLER => [],
38
        self::SCOPE_SERVICES => []
39
    ];
40
41
    /** @var ClassMetadata[] Collection of classes metadata */
42
    protected $classesMetadata = [];
43
44
    /** @var array Collection of dependencies aliases */
45
    protected $classAliases = [];
46
47
    /**
48
     * @var Generator
49
     * @Injectable
50
     */
51
    protected $generator;
52
53
    /** @var string Resolver function name */
54
    protected $resolverFunction;
55
56
    /**
57
     * Container builder constructor.
58
     *
59
     * @param Generator       $generator     PHP code generator
60
     * @param ClassMetadata[] $classMetadata Collection of classes metadata for container
0 ignored issues
show
Bug introduced by
There is no parameter named $classMetadata. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
61
     */
62 2
    public function __construct(Generator $generator)
63
    {
64 2
        $this->generator = $generator;
65 2
    }
66
67
    /**
68
     * Build container class.
69
     *
70
     * @param array       $classesMetadata
71
     * @param string|null $containerClass Container class name
72
     * @param string      $namespace      Name space
73
     *
74
     * @return string Generated Container class code
75
     */
76 2
    public function build(array $classesMetadata, $containerClass = 'Container', $namespace = '')
77
    {
78 2
        $this->classesMetadata = $classesMetadata;
79 2
        $this->processClassMetadata($classesMetadata);
80
81
        // Build dependency injection container function name
82 2
        $this->resolverFunction = uniqid(self::DI_FUNCTION_PREFIX);
83
84 2
        $containerDependencies = [];
85 2
        foreach ($classesMetadata as $classMetadata) {
86 2
            $className = $classMetadata->className;
87
            // Store inner dependencies
88 2
            if (array_key_exists('__construct', $classMetadata->methodsMetadata)) {
89 2
                $containerDependencies[$className] = array_values($classMetadata->methodsMetadata['__construct']->dependencies ?? []);
90
            }
91
        }
92
93 2
        $this->generator
94 2
            ->text('<?php declare(strict_types = 1);')
95 2
            ->newLine()
96 2
            ->defNamespace($namespace)
97 2
            ->multiComment(['Application container'])
98 2
            ->defClass($containerClass, '\\' . Container::class)
99 2
            ->multiComment(['@var array Collection of service instances'])
100 2
            ->defClassFunction('__construct', 'public', [], ['Container constructor'])
101 2
            ->newLine('$this->dependencies = ')->arrayValue($containerDependencies)->text(';')
102 2
            ->newLine('$this->aliases = ')->arrayValue($this->classAliases)->text(';')
103 2
            ->newLine('$this->scopes = ')->arrayValue($this->scopes)->text(';')
104 2
            ->newLine('$this->services = ')->arrayValue($this->scopes[self::SCOPE_SERVICES])->text(';')
0 ignored issues
show
Documentation introduced by
$this->scopes[self::SCOPE_SERVICES] is of type string, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
105 2
            ->endClassFunction()
106 2
            ->defClassFunction('logic', 'protected', ['$dependency'], ['{@inheritdoc}'])
107 2
            ->newLine('return $this->' . $this->resolverFunction . '($dependency);')
108 2
            ->endClassFunction();
109
110 2
        foreach ($classesMetadata as $classMetadata) {
111 2
            $className = $classMetadata->className;
112 2
            $dependencyName = $classMetadata->name ?? $className;
113
114
            // Generate camel case getter method
115 2
            $camelMethodName = 'get' . str_replace(' ', '', ucwords(ucfirst(str_replace(['\\', '_'], ' ', $dependencyName))));
116
117 2
            $this->generator
118 2
                ->defClassFunction($camelMethodName, 'public', [], ['@return ' . '\\'.ltrim($className, '\\') . ' Get ' . $dependencyName . ' instance'])
119 2
                ->newLine('return $this->' . $this->resolverFunction . '(\'' . $dependencyName . '\');')
120 2
                ->endClassFunction();
121
        }
122
123
        // Build di container function and add to container class and return class code
124 2
        $this->buildDependencyResolver($this->resolverFunction);
125
126 2
        return $this->generator
127 2
            ->endClass()
128 2
            ->flush();
129
    }
130
131
    /**
132
     * Read class metadata and fill internal collections.
133
     *
134
     * @param ClassMetadata[] $classesMetadata
135
     */
136 2
    public function processClassMetadata(array $classesMetadata)
137
    {
138
        // Read all classes in given file
139 2
        foreach ($classesMetadata as $classMetadata) {
140
            // Store by metadata name as alias
141 2
            $this->classAliases[$classMetadata->name] = $classMetadata->className;
142
143
            // Store class in defined scopes
144 2
            foreach ($classMetadata->scopes as $scope) {
145 2
                $this->scopes[$scope][$classMetadata->name] = $classMetadata->className;
146
            }
147
        }
148 2
    }
149
150
    /**
151
     * Build dependency resolving function.
152
     *
153
     * @param string $functionName Function name
154
     *
155
     * @throws \InvalidArgumentException
156
     */
157 2
    protected function buildDependencyResolver($functionName)
158
    {
159 2
        $inputVariable = '$aliasOrClassName';
160 2
        $this->generator
161 2
            ->defClassFunction($functionName, 'protected', [$inputVariable], ['Dependency resolving function'])
162
            //->defVar('static ' . self::DI_FUNCTION_SERVICES . ' = []')
163 2
            ->newLine();
164
165
        // Generate all container and delegate conditions
166 2
        $this->generateConditions($inputVariable, false);
167
168
        // Add method not found
169 2
        $this->generator->endIfCondition()->endFunction();
170 2
    }
171
172
    /**
173
     * Generate logic conditions and their implementation for container and its delegates.
174
     *
175
     * @param string     $inputVariable Input condition parameter variable name
176
     * @param bool|false $started       Flag if condition branching has been started
177
     */
178 2
    public function generateConditions($inputVariable = '$alias', $started = false)
179
    {
180
        // Iterate all container dependencies
181 2
        foreach ($this->classesMetadata as $classMetadata) {
182 2
            $className = $classMetadata->className;
183
            // Generate condition statement to define if this class is needed
184 2
            $conditionFunc = !$started ? 'defIfCondition' : 'defElseIfCondition';
185
186
            // Output condition branch
187 2
            $this->generator->$conditionFunc(
188 2
                $this->buildResolverCondition($inputVariable, $className, $classMetadata->name)
189
            );
190
191
            // Define if this class has service scope
192 2
            $isService = in_array($className, $this->scopes[self::SCOPE_SERVICES], true);
193
194
            /** @var MethodMetadata[] Gather only valid method for container */
195 2
            $classValidMethods = $this->getValidClassMethodsMetadata($classMetadata->methodsMetadata);
196
197
            /** @var PropertyMetadata[] Gather only valid property for container */
198 2
            $classValidProperties = $this->getValidClassPropertiesMetadata($classMetadata->propertiesMetadata);
199
200
            // Define class or service variable
201 2
            $staticContainerName = $isService
202 2
                ? '$this->' . self::DI_FUNCTION_SERVICES . '[\'' . $classMetadata->name . '\']'
203 2
                : '$temp';
204
205 2
            if ($isService) {
206
                // Check if dependency was instantiated
207 2
                $this->generator->defIfCondition('!array_key_exists(\'' . $classMetadata->name . '\', $this->' . self::DI_FUNCTION_SERVICES . ')');
208
            }
209
210 2
            if (count($classValidMethods) || count($classValidProperties)) {
211 2
                $this->generator->newLine($staticContainerName . ' = ');
212 2
                $this->buildResolvingClassDeclaration($className);
213 2
                $this->buildConstructorDependencies($classMetadata->methodsMetadata);
214
215
                // Internal scope reflection variable
216 2
                $reflectionVariable = '$reflectionClass';
217
218 2
                $this->buildReflectionClass($className, $classValidProperties, $classValidMethods, $reflectionVariable);
219
220
                // Process class properties
221 2
                foreach ($classValidProperties as $property) {
222
                    // If such property has the dependency
223 2
                    if ($property->dependency) {
224
                        // Set value via refection
225 2
                        $this->buildResolverPropertyDeclaration(
226 2
                            $property->name,
227 2
                            $property->dependency,
228
                            $staticContainerName,
229
                            $reflectionVariable,
230 2
                            $property->isPublic
231
                        );
232
                    }
233
                }
234
235
                /** @var MethodMetadata $methodMetadata */
236 2
                foreach ($classValidMethods as $methodName => $methodMetadata) {
237 2
                    $this->buildResolverMethodDeclaration(
238 2
                        $methodMetadata->dependencies,
239
                        $methodName,
240
                        $staticContainerName,
241
                        $reflectionVariable,
242 2
                        $methodMetadata->isPublic
243
                    );
244
                }
245
246 2
                if ($isService) {
247 2
                    $this->generator->endIfCondition();
248
                }
249
250 2
                $this->generator->newLine()->newLine('return ' . $staticContainerName . ';');
251
            } else {
252 2
                if ($isService) {
253
                    $this->generator->newLine($staticContainerName.' = ');
254
                    $this->buildResolvingClassDeclaration($className);
255
                    $this->buildConstructorDependencies($classMetadata->methodsMetadata);
256
                    $this->generator->endIfCondition()->newLine('return ' . $staticContainerName . ';');
257
                } else {
258 2
                    $this->generator->newLine('return ');
259 2
                    $this->buildResolvingClassDeclaration($className);
260 2
                    $this->buildConstructorDependencies($classMetadata->methodsMetadata);
261
                }
262
0 ignored issues
show
Coding Style introduced by
Blank line found at end of control structure
Loading history...
263
            }
264
265
            // Set flag that condition is started
266 2
            $started = true;
267
        }
268 2
    }
269
270
    /**
271
     * Build resolving function condition.
272
     *
273
     * @param string      $inputVariable Condition variable
274
     * @param string      $className
275
     * @param string|null $alias
276
     *
277
     * @return string Condition code
278
     */
279 2
    protected function buildResolverCondition(string $inputVariable, string $className, string $alias = null) : string
280
    {
281
        // Create condition branch
282 2
        $condition = $inputVariable . ' === \'' . $className . '\'';
283
284 2
        if ($alias !== null && $alias !== $className) {
285 2
            $condition .= '||' . $this->buildResolverCondition($inputVariable, $alias);
286
        }
287
288 2
        return $condition;
289
    }
290
291
    /**
292
     * Get valid class methods metadata.
293
     *
294
     * @param MethodMetadata[] $classMethodsMetadata All class methods metadata
295
     *
296
     * @return array Valid class methods metadata
297
     */
298 2
    protected function getValidClassMethodsMetadata(array $classMethodsMetadata)
299
    {
300
        /** @var MethodMetadata[] Gather only valid method for container */
301 2
        $classValidMethods = [];
302 2
        foreach ($classMethodsMetadata as $methodName => $methodMetadata) {
303
            // Skip constructor method and empty dependencies
304 2
            if ($methodName !== '__construct' && count($methodMetadata->dependencies) > 0) {
0 ignored issues
show
Unused Code Bug introduced by
The strict comparison !== seems to always evaluate to true as the types of $methodName (integer) and '__construct' (string) can never be identical. Maybe you want to use a loose comparison != instead?
Loading history...
305 2
                $classValidMethods[$methodName] = $methodMetadata;
306
            }
307
        }
308
309 2
        return $classValidMethods;
310
    }
311
312
    /**
313
     * Get valid class properties metadata.
314
     *
315
     * @param PropertyMetadata[] $classPropertiesMetadata All class properties metadata
316
     *
317
     * @return array Valid class properties metadata
318
     */
319 2
    protected function getValidClassPropertiesMetadata(array $classPropertiesMetadata)
320
    {
321
        /** @var PropertyMetadata[] Gather only valid property for container */
322 2
        $classValidProperties = [];
323 2
        foreach ($classPropertiesMetadata as $propertyName => $propertyMetadata) {
324
            // Skip constructor method and empty dependencies
325 2
            if ($propertyMetadata->dependency) {
326 2
                $classValidProperties[$propertyName] = $propertyMetadata;
327
            }
328
        }
329
330 2
        return $classValidProperties;
331
    }
332
333
    /**
334
     * Build resolving function class block.
335
     *
336
     * @param string $className Class name for new instance creation
337
     */
338 2
    protected function buildResolvingClassDeclaration(string $className)
339
    {
340 2
        $this->generator->text('new \\' . ltrim($className, '\\') . '(');
341 2
    }
342
343
    /**
344
     * Build constructor arguments injection.
345
     *
346
     * @param MethodMetadata[] $methodsMetaData
347
     */
348 2
    protected function buildConstructorDependencies(array $methodsMetaData)
349
    {
350
        // Process constructor dependencies
351 2
        $argumentsCount = 0;
352 2
        if (array_key_exists('__construct', $methodsMetaData)) {
353 2
            $constructorArguments = $methodsMetaData['__construct']->dependencies ?? [];
354 2
            $argumentsCount = count($constructorArguments);
355 2
            $i = 0;
356
357
            // Add indentation to move declaration arguments
358 2
            $this->generator->tabs++;
359
360
            // Process constructor arguments
361 2
            foreach ($constructorArguments as $argument => $dependency) {
362 2
                $this->buildResolverArgument($dependency);
363
364
                // Add comma if this is not last dependency
365 2
                if (++$i < $argumentsCount) {
366 2
                    $this->generator->text(',');
367
                }
368
            }
369
370
            // Restore indentation
371 2
            $this->generator->tabs--;
372
        }
373
374
        // Close declaration block, multiline if we have dependencies
375 2
        $argumentsCount ? $this->generator->newLine(');') : $this->generator->text(');');
376 2
    }
377
378
    /**
379
     * Build resolving function dependency argument.
380
     *
381
     * @param mixed $argument Dependency argument
382
     *
383
     * @throws \InvalidArgumentException On invalid argument type
384
     */
385 2
    protected function buildResolverArgument($argument, $textFunction = 'newLine')
386
    {
387
        // This is a dependency which invokes resolving function
388 2
        if (is_string($argument)) {
389 2
            if (array_key_exists($argument, $this->classesMetadata)) {
390
                // Call container logic for this dependency
391 2
                $this->generator->$textFunction('$this->' . $this->resolverFunction . '(\'' . $argument . '\')');
392 2
            } elseif (array_key_exists($argument, $this->classAliases)) {
393
                // Call container logic for this dependency
394
                $this->generator->$textFunction('$this->' . $this->resolverFunction . '(\'' . $argument . '\')');
395 2
            } elseif (class_exists($argument)) { // If this argument is existing class
396
                throw new \InvalidArgumentException($argument.' class metadata is not defined');
397 2
            } elseif (interface_exists($argument)) { // If this argument is existing interface
398
                throw new \InvalidArgumentException($argument.' - interface dependency not resolvable');
399
            } else { // String variable
400 2
                $this->generator->$textFunction()->stringValue($argument);
401
            }
402 2
        } elseif (is_array($argument)) { // Dependency value is array
403 2
            $this->generator->$textFunction()->arrayValue($argument);
404
        }
405 2
    }
406
407
    /**
408
     * Generate reflection class for private/protected methods or properties
409
     * in current scope.
410
     *
411
     * @param string             $className          Reflection class source class name
412
     * @param PropertyMetadata[] $propertiesMetadata Properties metadata
413
     * @param MethodMetadata[]   $methodsMetadata    Methods metadata
414
     * @param string             $reflectionVariable Reflection class variable name
415
     */
416 2
    protected function buildReflectionClass(string $className, array $propertiesMetadata, array $methodsMetadata, string $reflectionVariable)
417
    {
418
        /**
419
         * Iterate all properties and create internal scope reflection class instance if
420
         * at least one property in not public
421
         */
422 2 View Code Duplication
        foreach ($propertiesMetadata as $propertyMetadata) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
423 2
            if (!$propertyMetadata->isPublic) {
424 2
                $this->generator
425 2
                    ->comment('Create reflection class for injecting private/protected properties and methods')
426 2
                    ->newLine($reflectionVariable . ' = new \ReflectionClass(\'' . $className . '\');')
427 2
                    ->newLine();
428
429 2
                return true;
430
            }
431
        }
432
433
        /**
434
         * Iterate all properties and create internal scope reflection class instance if
435
         * at least one property in not public
436
         */
437 2 View Code Duplication
        foreach ($methodsMetadata as $methodMetadata) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
438 2
            if (!$methodMetadata->isPublic) {
439 2
                $this->generator
440 2
                    ->comment('Create reflection class for injecting private/protected properties and methods')
441 2
                    ->newLine($reflectionVariable . ' = new \ReflectionClass(\'' . $className . '\');')
442 2
                    ->newLine();
443
444 2
                return true;
445
            }
446
        }
447
448 2
        return false;
449
    }
450
451
    /**
452
     * Build resolving property injection declaration.
453
     *
454
     * @param string $propertyName       Target property name
455
     * @param string $dependency         Dependency class name
456
     * @param string $containerVariable  Container declaration variable name
457
     * @param string $reflectionVariable Reflection class variable name
458
     * @param bool   $isPublic           Flag if property is public
459
     */
460 2
    protected function buildResolverPropertyDeclaration(
461
        string $propertyName,
462
        string $dependency,
463
        string $containerVariable,
464
        string $reflectionVariable,
465
        bool $isPublic
466
    )
467
    {
0 ignored issues
show
Coding Style introduced by
The closing parenthesis and the opening brace of a multi-line function declaration must be on the same line
Loading history...
468 2
        if ($isPublic) {
469 2
            $this->generator
470 2
                ->comment('Inject public dependency for $' . $propertyName)
471 2
                ->newLine($containerVariable . '->' . $propertyName . ' = ');
472 2
            $this->buildResolverArgument($dependency, 'text');
473 2
            $this->generator->text(';');
474
        } else {
475 2
            $this->generator
476 2
                ->comment('Inject private dependency for $' . $propertyName)
477 2
                ->newLine('$property = ' . $reflectionVariable . '->getProperty(\'' . $propertyName . '\');')
478 2
                ->newLine('$property->setAccessible(true);')
479 2
                ->newLine('$property->setValue(')
480 2
                ->increaseIndentation()
481 2
                ->newLine($containerVariable . ',');
482
483 2
            $this->buildResolverArgument($dependency);
484
485 2
            $this->generator
486 2
                ->decreaseIndentation()
487 2
                ->newLine(');')
488 2
                ->newLine('$property->setAccessible(false);')
489 2
                ->newLine();
490
        }
491 2
    }
492
493
    /**
494
     * Build resolving method injection declaration.
495
     *
496
     * @param array  $dependencies       Collection of method dependencies
497
     * @param string $methodName         Method name
498
     * @param string $containerVariable  Container declaration variable name
499
     * @param string $reflectionVariable Reflection class variable name
500
     * @param bool   $isPublic           Flag if method is public
501
     */
502 2
    protected function buildResolverMethodDeclaration(
503
        array $dependencies,
504
        string $methodName,
505
        string $containerVariable,
506
        string $reflectionVariable,
507
        bool $isPublic
508
    )
509
    {
0 ignored issues
show
Coding Style introduced by
The closing parenthesis and the opening brace of a multi-line function declaration must be on the same line
Loading history...
510
        // Get method arguments
511 2
        $argumentsCount = count($dependencies);
512
513 2
        $this->generator->comment('Invoke ' . $methodName . '() and pass dependencies(y)');
514
515 2
        if ($isPublic) {
516 2
            $this->generator->newLine($containerVariable . '->' . $methodName . '(')->increaseIndentation();
517
        } else {
518 2
            $this->generator
519 2
                ->newLine('$method = ' . $reflectionVariable . '->getMethod(\'' . $methodName . '\');')
520 2
                ->newLine('$method->setAccessible(true);')
521 2
                ->newLine('$method->invoke(')
522 2
                ->increaseIndentation()
523 2
                ->newLine($containerVariable . ',');
524
        }
525
526 2
        $i = 0;
527
        // Iterate method arguments
528 2
        foreach ($dependencies as $argument => $dependency) {
529
            // Add dependencies
530 2
            $this->buildResolverArgument($dependency);
531
532
            // Add comma if this is not last dependency
533 2
            if (++$i < $argumentsCount) {
534 2
                $this->generator->text(',');
535
            }
536
        }
537
538 2
        $this->generator->decreaseIndentation()->newLine(');');
539 2
    }
540
}
541