Passed
Pull Request — master (#176)
by Vincent
10:21
created

instantiate(Container,Class)   A

Complexity

Conditions 3

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 3.072

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 6
dl 0
loc 11
ccs 4
cts 5
cp 0.8
crap 3.072
rs 10
c 1
b 0
f 0
1
/*
2
 * This file is part of Araknemu.
3
 *
4
 * Araknemu is free software: you can redistribute it and/or modify
5
 * it under the terms of the GNU Lesser General Public License as published by
6
 * the Free Software Foundation, either version 3 of the License, or
7
 * (at your option) any later version.
8
 *
9
 * Araknemu is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 * GNU Lesser General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU Lesser General Public License
15
 * along with Araknemu.  If not, see <https://www.gnu.org/licenses/>.
16
 *
17
 * Copyright (c) 2017-2021 Vincent Quatrevieux
18
 */
19
20
package fr.quatrevieux.araknemu.game.admin.script;
21
22
import fr.quatrevieux.araknemu.core.di.Container;
23
import fr.quatrevieux.araknemu.game.admin.Command;
24
import fr.quatrevieux.araknemu.game.admin.context.AbstractContextConfigurator;
25
import fr.quatrevieux.araknemu.game.admin.context.Context;
26
import groovy.util.GroovyScriptEngine;
27
import org.apache.logging.log4j.Logger;
28
29
import java.io.IOException;
30
import java.lang.reflect.Constructor;
31
import java.lang.reflect.InvocationTargetException;
32
import java.net.MalformedURLException;
33
import java.net.URL;
34
import java.nio.file.Files;
35
import java.nio.file.Path;
36
import java.util.function.Function;
37
38
/**
39
 * Load commands from groovy scripts
40
 *
41
 * @param <C> The context type
42
 */
43
public final class ScriptLoaderContextConfigurator<C extends Context> extends AbstractContextConfigurator<C> {
44
    private final Path path;
45
    private final Function<C, Container> containerResolver;
46
    private final Logger logger;
47
48
    private final GroovyScriptEngine engine;
49
50 1
    public ScriptLoaderContextConfigurator(Path path, Function<C, Container> containerResolver, Logger logger) {
51 1
        this.path = path;
52 1
        this.containerResolver = containerResolver;
53 1
        this.logger = logger;
54
55
        try {
56 1
            engine = new GroovyScriptEngine(new URL[] {path.toUri().toURL()});
57
        } catch (MalformedURLException e) {
58
            // Should not occurs
59
            throw new RuntimeException();
0 ignored issues
show
Best Practice introduced by
Dedicated exceptions should be preferred over throwing the generic Exception.
Loading history...
60 1
        }
61 1
    }
62
63
    @Override
64
    public void configure(C context) {
65 1
        if (!Files.isDirectory(path)) {
66 1
            return;
67
        }
68
69 1
        final Container container = containerResolver.apply(context);
70
71
        try {
72 1
            Files.list(path).forEach(file -> {
0 ignored issues
show
Security Bug introduced by
Use try-with-resources or close this "Stream" in a "finally" clause.

You may want to use try {} ... finally {} to close the resource or use the (relatively) new try-with-resources capability.

Loading history...
73 1
                logger.debug("Load command script {}", file.toAbsolutePath().toString());
74
75
                try {
76 1
                    final Class type = engine.loadScriptByName(file.getFileName().toString());
77
78 1
                    if (Command.class.isAssignableFrom(type)) {
79 1
                        logger.debug("Find command {}", type.getSimpleName());
80 1
                        add(instantiate(container, type));
81
                    }
82 1
                } catch (Exception e) {
83 1
                    logger.error("Fail to load command script", e);
84 1
                }
85 1
            });
86
        } catch (IOException e) {
87
            logger.error("Fail to open commands scripts directory", e);
88 1
        }
89 1
    }
90
91
    private Command instantiate(Container container, Class<? extends Command> type) throws InstantiationException, IllegalAccessException, InvocationTargetException {
92 1
        for (Constructor constructor : type.getConstructors()) {
93 1
            final Object[] parameters = resolveArguments(container, constructor);
94
95 1
            if (parameters != null) {
96 1
                return (Command) constructor.newInstance(parameters);
97
            }
98
        }
99
100
        // No constructor found : try to instantiate without constructor
101
        return type.newInstance();
102
    }
103
104
    private Object[] resolveArguments(Container container, Constructor constructor) {
105 1
        final Object[] parameters = new Object[constructor.getParameterCount()];
106 1
        final Class[] parametersTypes = constructor.getParameterTypes();
107
108 1
        for (int i = 0; i < parameters.length; ++i) {
109 1
            if (!container.has(parametersTypes[i])) {
110
                return null;
0 ignored issues
show
Code Smell introduced by
When the calling method is expecting an Array it is (null-)safer to return an empty array instead of null.
Loading history...
111
            }
112
113 1
            parameters[i] = container.get(parametersTypes[i]);
114
        }
115
116 1
        return parameters;
117
    }
118
}
119