Completed
Push — master ( 4036d4...da82e8 )
by Fran
03:44
created

GeneratorService   B

Complexity

Total Complexity 47

Size/Duplication

Total Lines 404
Duplicated Lines 11.63 %

Coupling/Cohesion

Components 1
Dependencies 6

Importance

Changes 15
Bugs 1 Features 0
Metric Value
c 15
b 1
f 0
dl 47
loc 404
rs 8.439
wmc 47
lcom 1
cbo 6

20 Methods

Rating   Name   Duplication   Size   Complexity  
B findTranslations() 0 30 6
A createStructureModule() 0 12 1
A createModulePath() 0 7 1
A createModulePathTree() 0 14 3
A createModuleBaseFiles() 0 11 1
A createModuleModels() 0 14 1
A generateControllerTemplate() 10 10 1
B generateBaseApiTemplate() 0 17 6
A generateConfigTemplate() 0 6 1
A generatePublicTemplates() 0 8 2
A generateServiceTemplate() 9 9 1
A genereateAutoloaderTemplate() 0 9 1
A generateSchemaTemplate() 10 10 1
A generatePropertiesTemplate() 0 13 1
A generateIndexTemplate() 9 9 1
A writeTemplateToFile() 0 14 4
A createApiBaseFile() 0 9 1
A createApi() 9 9 1
B copyResources() 0 15 7
B copyr() 0 14 6

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 GeneratorService 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 GeneratorService, and based on these observations, apply Extract Interface, too.

1
<?php
2
    namespace PSFS\Services;
3
4
    use PSFS\base\Cache;
5
    use PSFS\base\config\Config;
6
    use PSFS\base\exception\ConfigException;
7
    use PSFS\base\Service;
8
9
    class GeneratorService extends Service{
10
        /**
11
         * @Inyectable
12
         * @var \PSFS\base\config\Config Servicio de configuración
13
         */
14
        protected $config;
15
        /**
16
         * @Inyectable
17
         * @var \PSFS\base\Security Servicio de autenticación
18
         */
19
        protected $security;
20
        /**
21
         * @Inyectable
22
         * @var \PSFS\base\Template Servicio de gestión de plantillas
23
         */
24
        protected $tpl;
25
26
        /**
27
         * Método que revisa las traducciones directorio a directorio
28
         * @param $path
29
         * @param $locale
30
         * @return array
31
         */
32
        public static function findTranslations($path, $locale)
33
        {
34
            $locale_path = realpath(BASE_DIR . DIRECTORY_SEPARATOR . 'locale');
35
            $locale_path .= DIRECTORY_SEPARATOR . $locale . DIRECTORY_SEPARATOR . 'LC_MESSAGES' . DIRECTORY_SEPARATOR;
36
37
            $translations = array();
38
            if(file_exists($path))
39
            {
40
                $d = dir($path);
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $d. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
41
                while(false !== ($dir = $d->read()))
42
                {
43
                    Config::createDir($locale_path);
44
                    if(!file_exists($locale_path . 'translations.po')) {
45
                        file_put_contents($locale_path . 'translations.po', '');
46
                    }
47
                    $inspect_path = realpath($path.DIRECTORY_SEPARATOR.$dir);
48
                    $cmd_php = "export PATH=\$PATH:/opt/local/bin; xgettext ". $inspect_path . DIRECTORY_SEPARATOR ."*.php --from-code=UTF-8 -j -L PHP --debug --force-po -o {$locale_path}translations.po";
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 204 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
49
                    if(is_dir($path.DIRECTORY_SEPARATOR.$dir) && preg_match('/^\./',$dir) == 0)
50
                    {
51
                        $res = _('Revisando directorio: ') . $inspect_path;
52
                        $res .= _('Comando ejecutado: '). $cmd_php;
53
                        $res .= shell_exec($cmd_php);
54
                        usleep(10);
55
                        $translations[] = $res;
56
                        $translations = array_merge($translations, self::findTranslations($inspect_path, $locale));
57
                    }
58
                }
59
            }
60
            return $translations;
61
        }
62
63
        /**
64
         * Servicio que genera la estructura de un módulo o lo actualiza en caso de ser necesario
65
         * @param string $module
66
         * @param boolean $force
67
         * @param string $type
68
         * @return mixed
69
         */
70
        public function createStructureModule($module, $force = false, $type = "")
71
        {
72
            $mod_path = CORE_DIR . DIRECTORY_SEPARATOR;
73
            $module = ucfirst($module);
74
            $this->createModulePath($module, $mod_path);
75
            $this->createModulePathTree($module, $mod_path);
76
            $this->createModuleBaseFiles($module, $mod_path, $force, $type);
77
            $this->createModuleModels($module);
78
            $this->generateBaseApiTemplate($module, $mod_path);
79
            //Redireccionamos al home definido
80
            $this->log->infoLog("Módulo generado correctamente");
81
        }
82
83
        /**
84
         * Servicio que genera el path base del módulo
85
         * @param string $module
86
         * @param string $mod_path
87
         */
88
        private function createModulePath($module, $mod_path)
89
        {
90
            //Creamos el directorio base de los módulos
91
            Config::createDir($mod_path);
92
            //Creamos la carpeta del módulo
93
            Config::createDir($mod_path . $module);
94
        }
95
96
        /**
97
         * Servicio que genera la estructura base
98
         * @param $module
99
         * @param $mod_path
100
         */
101
        private function createModulePathTree($module, $mod_path)
102
        {
103
            //Creamos las carpetas CORE del módulo
104
            $this->log->infoLog("Generamos la estructura");
105
            $paths = array("Api", "Api/base", "Config", "Controller", "Form", "Models", "Public", "Templates", "Services", "Test");
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 131 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
106
            foreach($paths as $path) {
107
                Config::createDir($mod_path . $module . DIRECTORY_SEPARATOR . $path);
108
            }
109
            //Creamos las carpetas de los assets
110
            $htmlPaths = array("css", "js", "img", "media", "font");
111
            foreach($htmlPaths as $path) {
112
                Config::createDir($mod_path . $module . DIRECTORY_SEPARATOR . "Public" . DIRECTORY_SEPARATOR . $path);
113
            }
114
        }
115
116
        /**
117
         * Servicio que genera las plantillas básicas de ficheros del módulo
118
         * @param string $module
119
         * @param string $mod_path
120
         * @param boolean $force
121
         * @param string $controllerType
122
         */
123
        private function createModuleBaseFiles($module, $mod_path, $force = false, $controllerType = '')
124
        {
125
            $this->generateControllerTemplate($module, $mod_path, $force, $controllerType);
126
            $this->generateServiceTemplate($module, $mod_path, $force);
127
            $this->genereateAutoloaderTemplate($module, $mod_path, $force);
128
            $this->generateSchemaTemplate($module, $mod_path, $force);
129
            $this->generatePropertiesTemplate($module, $mod_path, $force);
130
            $this->generateConfigTemplate($module, $mod_path, $force);
131
            $this->generateIndexTemplate($module, $mod_path, $force);
132
            $this->generatePublicTemplates($module, $mod_path, $force);
133
        }
134
135
        /**
136
         * Servicio que ejecuta Propel y genera el modelo de datos
137
         * @param string $module
138
         */
139
        private function createModuleModels($module)
140
        {
141
            //Generamos las clases de propel y la configuración
142
            $exec = "export PATH=\$PATH:/opt/local/bin; " . BASE_DIR . DIRECTORY_SEPARATOR . "vendor" . DIRECTORY_SEPARATOR . "bin" . DIRECTORY_SEPARATOR . "propel ";
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 166 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
143
            $schemaOpt = " --schema-dir=" . CORE_DIR . DIRECTORY_SEPARATOR . $module . DIRECTORY_SEPARATOR . "Config";
144
            $opt = " --config-dir=" . CORE_DIR . DIRECTORY_SEPARATOR . $module . DIRECTORY_SEPARATOR . "Config --output-dir=" . CORE_DIR . " --verbose";
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 152 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
145
            $ret = shell_exec($exec . "build" . $opt . $schemaOpt);
146
147
            $this->log->infoLog("Generamos clases invocando a propel:\n $ret");
148
            $ret = shell_exec($exec . "sql:build" . $opt . " --output-dir=" . CORE_DIR . DIRECTORY_SEPARATOR . $module . DIRECTORY_SEPARATOR . "Config" . $schemaOpt);
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 166 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
149
            $this->log->infoLog("Generamos sql invocando a propel:\n $ret");
150
            $ret = shell_exec($exec . "config:convert" . $opt . " --output-dir=" . CORE_DIR . DIRECTORY_SEPARATOR . $module . DIRECTORY_SEPARATOR . "Config");
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 158 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
151
            $this->log->infoLog("Generamos configuración invocando a propel:\n $ret");
152
        }
153
154
        /**
155
         * @param string $module
156
         * @param string $mod_path
157
         * @param boolean $force
158
         * @param string $controllerType
159
         * @return boolean
160
         */
161 View Code Duplication
        private function generateControllerTemplate($module, $mod_path, $force = false, $controllerType = "")
162
        {
163
            //Generamos el controlador base
164
            $this->log->infoLog("Generamos el controlador BASE");
165
            $controller = $this->tpl->dump("generator/controller.template.twig", array(
166
                "module" => $module,
167
                "controllerType" => $controllerType,
168
            ));
169
            return $this->writeTemplateToFile($controller, $mod_path . $module . DIRECTORY_SEPARATOR . "Controller" . DIRECTORY_SEPARATOR . "{$module}Controller.php", $force);
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 175 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
170
        }
171
172
        /**
173
         * @param string $module
174
         * @param string $mod_path
175
         * @param boolean $force
176
         * @param string $controllerType
177
         * @return boolean
178
         */
179
        private function generateBaseApiTemplate($module, $mod_path, $force = false, $controllerType = "")
180
        {
181
            $created = true;
182
            $modelPath = $mod_path . $module . DIRECTORY_SEPARATOR . 'Models';
183
            if(file_exists($modelPath)) {
184
                $dir = dir($modelPath);
185
                while($file = $dir->read()) {
186
                    if(!in_array($file, array('.', '..')) && !preg_match('/Query\.php$/i', $file) && preg_match('/\.php$/i', $file)) {
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 134 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
187
                        $filename = str_replace(".php", "", $file);
188
                        $this->log->infoLog("Generamos Api BASES para {$filename}");
189
                        $this->createApiBaseFile($module, $mod_path, $filename);
190
                        $this->createApi($module, $mod_path, $force, $filename);
191
                    }
192
                }
193
            }
194
            return $created;
195
        }
196
197
        /**
198
         * @param string $module
199
         * @param string $mod_path
200
         * @param boolean $force
201
         * @return boolean
202
         */
203
        private function generateConfigTemplate($module, $mod_path, $force = false)
204
        {
205
            //Generamos el fichero de configuración
206
            $this->log->infoLog("Generamos fichero vacío de configuración");
207
            return $this->writeTemplateToFile("<?php\n\t", $mod_path . $module . DIRECTORY_SEPARATOR . "Config" . DIRECTORY_SEPARATOR . "config.php", $force);
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 158 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
208
        }
209
210
        /**
211
         * @param string $module
212
         * @param string $mod_path
213
         * @param boolean $force
214
         * @return boolean
215
         */
216
        private function generatePublicTemplates($module, $mod_path, $force = false)
0 ignored issues
show
Unused Code introduced by
The parameter $force is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
217
        {
218
            //Generamos el fichero de configuración
219
            $this->log->infoLog("Generamos ficheros para assets base");
220
            $css = $this->writeTemplateToFile("/* CSS3 STYLES */\n\n", $mod_path . $module . DIRECTORY_SEPARATOR . "Public" . DIRECTORY_SEPARATOR . "css" . DIRECTORY_SEPARATOR . "styles.css", false);
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 199 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
221
            $js = $this->writeTemplateToFile("/* APP MODULE JS */\n\n(function() {\n\t'use strict';\n})();", $mod_path . $module . DIRECTORY_SEPARATOR . "Public" . DIRECTORY_SEPARATOR . "js" . DIRECTORY_SEPARATOR . "app.js", false);
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $js. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 232 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
222
            return ($css && $js);
223
        }
224
225
        /**
226
         * @param string $module
227
         * @param string $mod_path
228
         * @param boolean $force
229
         * @return boolean
230
         */
231 View Code Duplication
        private function generateServiceTemplate($module, $mod_path, $force = false)
232
        {
233
            //Generamos el controlador base
234
            $this->log->infoLog("Generamos el servicio BASE");
235
            $controller = $this->tpl->dump("generator/service.template.twig", array(
236
                "module" => $module,
237
            ));
238
            return $this->writeTemplateToFile($controller, $mod_path . $module . DIRECTORY_SEPARATOR . "Services" . DIRECTORY_SEPARATOR . "{$module}Service.php", $force);
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 170 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
239
        }
240
241
        /**
242
         * @param string $module
243
         * @param string $mod_path
244
         * @param boolean $force
245
         * @return boolean
246
         */
247
        private function genereateAutoloaderTemplate($module, $mod_path, $force = false)
248
        {
249
            //Generamos el autoloader del módulo
250
            $this->log->infoLog("Generamos el autoloader");
251
            $autoloader = $this->tpl->dump("generator/autoloader.template.twig", array(
252
                "module" => $module,
253
            ));
254
            return $this->writeTemplateToFile($autoloader, $mod_path . $module . DIRECTORY_SEPARATOR . "autoload.php", $force);
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 127 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
255
        }
256
257
        /**
258
         * @param string $module
259
         * @param string $mod_path
260
         * @param boolean $force
261
         * @return boolean
262
         */
263 View Code Duplication
        private function generateSchemaTemplate($module, $mod_path, $force = false)
264
        {
265
            //Generamos el autoloader del módulo
266
            $this->log->infoLog("Generamos el schema");
267
            $schema = $this->tpl->dump("generator/schema.propel.twig", array(
268
                "module" => $module,
269
                "db"     => $this->config->get("db_name"),
270
            ));
271
            return $this->writeTemplateToFile($schema, $mod_path . $module . DIRECTORY_SEPARATOR . "Config" . DIRECTORY_SEPARATOR . "schema.xml", $force);
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 154 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
272
        }
273
274
        /**
275
         * @param string $module
276
         * @param string $mod_path
277
         * @param boolean $force
278
         * @return boolean
279
         */
280
        private function generatePropertiesTemplate($module, $mod_path, $force = false)
281
        {
282
            $this->log->infoLog("Generamos la configuración de Propel");
283
            $build_properties = $this->tpl->dump("generator/build.properties.twig", array(
284
                "module" => $module,
285
                "host"   => $this->config->get("db_host"),
286
                "port"   => $this->config->get("db_port"),
287
                "user"   => $this->config->get("db_user"),
288
                "pass"   => $this->config->get("db_password"),
289
                "db"     => $this->config->get("db_name"),
290
            ));
291
            return $this->writeTemplateToFile($build_properties, $mod_path . $module . DIRECTORY_SEPARATOR . "Config" . DIRECTORY_SEPARATOR . "propel.yml", $force);
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 164 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
292
        }
293
294
        /**
295
         * @param string $module
296
         * @param string $mod_path
297
         * @param boolean $force
298
         * @return boolean
299
         */
300 View Code Duplication
        private function generateIndexTemplate($module, $mod_path, $force = false)
301
        {
302
            //Generamos la plantilla de index
303
            $this->log->infoLog("Generamos una plantilla base por defecto");
304
            $index = $this->tpl->dump("generator/index.template.twig", array(
305
                "module" => $module,
306
            ));
307
            return $this->writeTemplateToFile($index, $mod_path . $module . DIRECTORY_SEPARATOR . "Templates" . DIRECTORY_SEPARATOR . "index.html.twig", $force);
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 161 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
308
        }
309
310
        /**
311
         * Método que graba el contenido de una plantilla en un fichero
312
         * @param string $fileContent
313
         * @param string $filename
314
         * @param boolean $force
315
         * @return boolean
316
         */
317
        private function writeTemplateToFile($fileContent, $filename, $force = false) {
318
            $created = false;
319
            if ($force || !file_exists($filename)) {
320
                try {
321
                    $this->cache->storeData($filename, $fileContent, Cache::TEXT,true);
322
                    $created = true;
323
                } catch(\Exception $e) {
324
                    $this->log->errorLog($e->getMessage());
325
                }
326
            } else{
327
                $this->log->errorLog($filename . _(' not exists or cant write'));
328
            }
329
            return $created;
330
        }
331
332
        /**
333
         * Create ApiBase
334
         * @param string $module
335
         * @param string $mod_path
336
         * @param string $api
337
         *
338
         * @return bool
339
         */
340
        private function createApiBaseFile($module, $mod_path, $api)
341
        {
342
            $controller = $this->tpl->dump("generator/api.base.template.twig", array(
343
                "module"         => $module,
344
                "api"            => $api
345
            ));
346
347
            return $this->writeTemplateToFile($controller, $mod_path . $module . DIRECTORY_SEPARATOR . "Api" . DIRECTORY_SEPARATOR . 'base' . DIRECTORY_SEPARATOR . "{$api}BaseApi.php", true);
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 191 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
348
        }
349
350
        /**
351
         * Create Api
352
         * @param string $module
353
         * @param string $mod_path
354
         * @param bool $force
355
         * @param string $api
356
         *
357
         * @return bool
358
         */
359 View Code Duplication
        private function createApi($module, $mod_path, $force, $api)
360
        {
361
            $controller = $this->tpl->dump("generator/api.template.twig", array(
362
                "module"         => $module,
363
                "api"            => $api
364
            ));
365
366
            return $this->writeTemplateToFile($controller, $mod_path . $module . DIRECTORY_SEPARATOR . "Api" . DIRECTORY_SEPARATOR . "{$api}.php", $force);
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 155 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
367
        }
368
369
        /**
370
         * Method that copy resources recursively
371
         * @param string $dest
372
         * @param boolean $force
373
         * @param $filename_path
374
         * @param boolean $debug
375
         */
376
        public static function copyResources($dest, $force, $filename_path, $debug)
377
        {
378
            if (file_exists($filename_path)) {
379
                $destfolder = basename($filename_path);
380
                if (!file_exists(WEB_DIR.$dest.DIRECTORY_SEPARATOR.$destfolder) || $debug || $force) {
381
                    if (is_dir($filename_path)) {
382
                        self::copyr($filename_path, WEB_DIR.$dest.DIRECTORY_SEPARATOR.$destfolder);
383
                    }else {
384
                        if (@copy($filename_path, WEB_DIR.$dest.DIRECTORY_SEPARATOR.$destfolder) === FALSE) {
385
                            throw new ConfigException("Can't copy ".$filename_path." to ".WEB_DIR.$dest.DIRECTORY_SEPARATOR.$destfolder);
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 137 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
386
                        }
387
                    }
388
                }
389
            }
390
        }
391
392
        /**
393
         * Method that copy a resource
394
         * @param string $src
395
         * @param string $dst
396
         * @throws ConfigException
397
         */
398
        public static function copyr($src, $dst) {
399
            $dir = opendir($src);
400
            Config::createDir($dst);
401
            while (false !== ($file = readdir($dir))) {
402
                if (($file != '.') && ($file != '..')) {
403
                    if (is_dir($src.'/'.$file)) {
404
                        self::copyr($src.'/'.$file, $dst.'/'.$file);
405
                    } elseif (@copy($src.'/'.$file, $dst.'/'.$file) === false) {
406
                        throw new ConfigException("Can't copy ".$src." to ".$dst);
407
                    }
408
                }
409
            }
410
            closedir($dir);
411
        }
412
    }
413