Passed
Branch master (6b380a)
by Vincent
13:11
created

create(Record)   A

Complexity

Conditions 4

Size

Total Lines 18
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 4

Importance

Changes 0
Metric Value
cc 4
eloc 15
dl 0
loc 18
ccs 13
cts 13
cp 1
crap 4
rs 9.65
c 0
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-2020 Vincent Quatrevieux
18
 */
19
20
package fr.quatrevieux.araknemu.data.world.repository.implementation.sql;
21
22
import fr.arakne.utils.value.Colors;
23
import fr.quatrevieux.araknemu.core.dbal.executor.QueryExecutor;
24
import fr.quatrevieux.araknemu.core.dbal.repository.EntityNotFoundException;
25
import fr.quatrevieux.araknemu.core.dbal.repository.Record;
26
import fr.quatrevieux.araknemu.core.dbal.repository.RepositoryException;
27
import fr.quatrevieux.araknemu.core.dbal.repository.RepositoryUtils;
28
import fr.quatrevieux.araknemu.data.transformer.Transformer;
29
import fr.quatrevieux.araknemu.data.transformer.TransformerException;
30
import fr.quatrevieux.araknemu.data.world.entity.monster.MonsterTemplate;
31
import fr.quatrevieux.araknemu.data.world.repository.monster.MonsterTemplateRepository;
32
import fr.quatrevieux.araknemu.game.world.creature.characteristics.Characteristics;
33
import fr.quatrevieux.araknemu.util.ParseUtils;
34
import org.apache.commons.lang3.StringUtils;
35
import org.checkerframework.checker.index.qual.Positive;
36
import org.checkerframework.checker.index.qual.SameLen;
37
38
import java.sql.ResultSet;
39
import java.sql.SQLException;
40
import java.util.HashMap;
41
import java.util.List;
42
import java.util.Map;
43
44
/**
45
 * SQL implementation for monster template repository
46
 */
47
final class SqlMonsterTemplateRepository implements MonsterTemplateRepository {
48
    private final QueryExecutor executor;
49
    private final RepositoryUtils<MonsterTemplate> utils;
50
    private final Transformer<Colors> colorsTransformer;
51
    private final Transformer<Characteristics> characteristicsTransformer;
52
53 1
    public SqlMonsterTemplateRepository(QueryExecutor executor, Transformer<Colors> colorsTransformer, Transformer<Characteristics> characteristicsTransformer) {
54 1
        this.executor = executor;
55
56 1
        this.colorsTransformer = colorsTransformer;
57 1
        this.characteristicsTransformer = characteristicsTransformer;
58
59 1
        utils = new RepositoryUtils<>(this.executor, new SqlMonsterTemplateRepository.Loader());
60 1
    }
61
62
    @Override
63
    public void initialize() throws RepositoryException {
64
        try {
65 1
            executor.query(
66
                "CREATE TABLE `MONSTER_TEMPLATE` (" +
67
                    "  `MONSTER_ID` INTEGER PRIMARY KEY," +
68
                    "  `MONSTER_NAME` VARCHAR(100)," +
69
                    "  `GFXID` INTEGER," +
70
                    "  `COLORS` VARCHAR(30)," +
71
                    "  `AI` VARCHAR(12)," +
72
                    "  `CHARACTERISTICS` TEXT," +
73
                    "  `LIFE_POINTS` VARCHAR(200)," +
74
                    "  `INITIATIVES` VARCHAR(200)," +
75
                    "  `SPELLS` TEXT" +
76
                ")"
77
            );
78
        } catch (SQLException e) {
79
            throw new RepositoryException(e);
80 1
        }
81 1
    }
82
83
    @Override
84
    public void destroy() throws RepositoryException {
85
        try {
86 1
            executor.query("DROP TABLE MONSTER_TEMPLATE");
87
        } catch (SQLException e) {
88
            throw new RepositoryException(e);
89 1
        }
90 1
    }
91
92
    @Override
93
    public MonsterTemplate get(MonsterTemplate entity) throws RepositoryException {
94 1
        return get(entity.id());
95
    }
96
97
    @Override
98
    public MonsterTemplate get(int id) {
99
        try {
100 1
            return utils.findOne(
101
                "SELECT * FROM MONSTER_TEMPLATE WHERE MONSTER_ID = ?",
102 1
                stmt -> stmt.setInt(1, id)
103
            );
104 1
        } catch (EntityNotFoundException e) {
105 1
            throw new EntityNotFoundException("Monster " + id + " is not found");
106
        }
107
    }
108
109
    @Override
110
    public boolean has(MonsterTemplate entity) throws RepositoryException {
111 1
        return utils.aggregate(
112
            "SELECT COUNT(*) FROM MONSTER_TEMPLATE WHERE MONSTER_ID = ?",
113 1
            stmt -> stmt.setInt(1, entity.id())
114
        ) > 0;
115
    }
116
117
    @Override
118
    public List<MonsterTemplate> all() {
119 1
        return utils.findAll("SELECT * FROM MONSTER_TEMPLATE");
120
    }
121
122 1
    private class Loader implements RepositoryUtils.Loader<MonsterTemplate> {
0 ignored issues
show
Comprehensibility introduced by
Class or interface names should not shadow other classes or interfaces. In general, shadowing is a bad practice as it makes code harder to understand. Consider renaming this class.
Loading history...
123
        @Override
124
        public MonsterTemplate create(Record record) throws SQLException {
125 1
            final String[] characteristics = record.getCsvArray("CHARACTERISTICS", '|');
126 1
            final String[] lifePoints = record.getCsvArray("LIFE_POINTS", '|');
127 1
            final String[] initiatives = record.getCsvArray("INITIATIVES", '|');
128 1
            final String[] spells = record.getCsvArray("SPELLS", '|');
129
130 1
            if (characteristics.length != lifePoints.length || characteristics.length != initiatives.length || characteristics.length != spells.length) {
131 1
                throw new IllegalArgumentException("All grade characteristics must have the same length");
132
            }
133
134 1
            return new MonsterTemplate(
135 1
                record.getInt("MONSTER_ID"),
136 1
                record.getString("MONSTER_NAME"),
137 1
                record.getInt("GFXID"),
138 1
                record.unserialize("COLORS", colorsTransformer),
139 1
                record.getString("AI"),
140 1
                parseGrades(characteristics, lifePoints, initiatives, spells)
141
            );
142
        }
143
144
        @Override
145
        public MonsterTemplate fillKeys(MonsterTemplate entity, ResultSet keys) {
146
            throw new RepositoryException("Read-only entity");
147
        }
148
149
        private MonsterTemplate.Grade[] parseGrades(String @SameLen({"#2", "#3", "#4"}) [] characteristics, String @SameLen({"#1", "#3", "#4"}) [] lifePoints, String @SameLen({"#1", "#2", "#4"}) [] initiatives, String @SameLen({"#1", "#2", "#3"}) [] spells) {
150 1
            final MonsterTemplate.Grade[] grades = new MonsterTemplate.Grade[characteristics.length];
151
152 1
            for (int i = 0; i < characteristics.length; ++i) {
153 1
                final String[] grade = StringUtils.splitPreserveAllTokens(characteristics[i], "@", 2);
154
155 1
                if (grade.length != 2) {
156
                    throw new TransformerException("Invalid grade '" + grades[i] + "'");
157
                }
158
159 1
                final Map<Integer, @Positive Integer> gradeSpells = new HashMap<>();
160
161 1
                for (String spell : StringUtils.split(spells[i], ";")) {
162 1
                    final String[] data = StringUtils.split(spell, "@", 2);
163
164 1
                    if (data.length != 2) {
165
                        throw new TransformerException("Invalid spell list '" + spells[i] + "'");
166
                    }
167
168 1
                    gradeSpells.put(
169 1
                        Integer.parseInt(data[0]),
170 1
                        ParseUtils.parsePositiveInt(data[1])
171
                    );
172
                }
173
174 1
                grades[i] = new MonsterTemplate.Grade(
175 1
                    ParseUtils.parsePositiveInt(grade[0]),
176 1
                    ParseUtils.parsePositiveInt(lifePoints[i]),
177 1
                    Integer.parseInt(initiatives[i]),
178 1
                    characteristicsTransformer.unserialize(grade[1]),
179
                    gradeSpells
180
                );
181
            }
182
183 1
            return grades;
184
        }
185
    }
186
}
187