1 | /* |
||
2 | * This file is part of Swf Map Loader. |
||
3 | * |
||
4 | * Swf Map Loader 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 | * Swf Map Loader 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 Swf Map Loader. If not, see <https://www.gnu.org/licenses/>. |
||
16 | * |
||
17 | * Copyright (c) 2020-2020 Vincent Quatrevieux |
||
18 | */ |
||
19 | |||
20 | package fr.arakne.swfmaploader.cache; |
||
21 | |||
22 | import fr.arakne.swfmaploader.swf.SwfMapStructure; |
||
23 | |||
24 | import java.io.File; |
||
25 | import java.sql.Connection; |
||
26 | import java.sql.DriverManager; |
||
27 | import java.sql.PreparedStatement; |
||
28 | import java.sql.ResultSet; |
||
29 | import java.sql.SQLException; |
||
30 | import java.sql.Statement; |
||
31 | import java.util.Optional; |
||
32 | |||
33 | /** |
||
34 | * SQL implementation for the cache system |
||
35 | */ |
||
36 | final public class SqlMapStructureCache implements MapStructureCache { |
||
37 | final static public int SCHEMA_VERSION = 1; |
||
38 | |||
39 | final private Connection connection; |
||
40 | |||
41 | 1 | public SqlMapStructureCache(Connection connection) throws SQLException { |
|
42 | 1 | this.connection = connection; |
|
43 | |||
44 | 1 | initDatabase(); |
|
45 | 1 | } |
|
46 | |||
47 | @Override |
||
48 | public Optional<SwfMapStructure> retrieve(int id) { |
||
49 | 1 | try (PreparedStatement stmt = connection.prepareStatement("SELECT * FROM `MAP_CACHE` WHERE `ID` = ?")) { |
|
50 | 1 | stmt.setInt(1, id); |
|
51 | |||
52 | 1 | try (ResultSet rs = stmt.executeQuery()) { |
|
53 | 1 | if (!rs.next()) { |
|
54 | 1 | return Optional.empty(); |
|
55 | } |
||
56 | |||
57 | 1 | SwfMapStructure structure = new SwfMapStructure(); |
|
58 | |||
59 | 1 | structure.setId(rs.getInt("ID")); |
|
60 | 1 | structure.setWidth(rs.getInt("WIDTH")); |
|
61 | 1 | structure.setHeight(rs.getInt("HEIGHT")); |
|
62 | 1 | structure.setBackgroundNum(rs.getInt("BACKGROUND_NUM")); |
|
63 | 1 | structure.setAmbianceId(rs.getInt("AMBIANCE_ID")); |
|
64 | 1 | structure.setMusicId(rs.getInt("MUSIC_ID")); |
|
65 | 1 | structure.setOutdoor(rs.getInt("OUTDOOR") == 1); |
|
66 | 1 | structure.setCapabilities(rs.getInt("CAPABILITIES")); |
|
67 | 1 | structure.setMapData(rs.getString("MAP_DATA")); |
|
68 | 1 | structure.setVersion(rs.getString("VERSION")); |
|
69 | |||
70 | 1 | return Optional.of(structure); |
|
71 | } |
||
72 | } catch (SQLException e) { |
||
73 | throw new RuntimeException("Cannot read map " + id + " from cache", e); |
||
0 ignored issues
–
show
Best Practice
introduced
by
![]() |
|||
74 | } |
||
75 | } |
||
76 | |||
77 | @Override |
||
78 | public void store(SwfMapStructure structure) { |
||
79 | 1 | try (PreparedStatement stmt = connection.prepareStatement("REPLACE INTO `MAP_CACHE` (`ID`, `WIDTH`, `HEIGHT`, `BACKGROUND_NUM`, `AMBIANCE_ID`, `MUSIC_ID`, `OUTDOOR`, `CAPABILITIES`, `MAP_DATA`, `VERSION`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")) { |
|
80 | 1 | stmt.setInt(1, structure.id()); |
|
81 | 1 | stmt.setInt(2, structure.width()); |
|
0 ignored issues
–
show
|
|||
82 | 1 | stmt.setInt(3, structure.height()); |
|
0 ignored issues
–
show
|
|||
83 | 1 | stmt.setInt(4, structure.backgroundNum()); |
|
0 ignored issues
–
show
|
|||
84 | 1 | stmt.setInt(5, structure.ambianceId()); |
|
0 ignored issues
–
show
|
|||
85 | 1 | stmt.setInt(6, structure.musicId()); |
|
0 ignored issues
–
show
|
|||
86 | 1 | stmt.setInt(7, structure.isOutdoor() ? 1 : 0); |
|
0 ignored issues
–
show
|
|||
87 | 1 | stmt.setInt(8, structure.capabilities()); |
|
0 ignored issues
–
show
|
|||
88 | 1 | stmt.setString(9, structure.mapData()); |
|
0 ignored issues
–
show
|
|||
89 | 1 | stmt.setString(10, structure.version()); |
|
0 ignored issues
–
show
|
|||
90 | |||
91 | 1 | stmt.executeUpdate(); |
|
92 | } catch (SQLException e) { |
||
93 | throw new RuntimeException("Cannot store map " + structure.id() + " in cache", e); |
||
0 ignored issues
–
show
|
|||
94 | 1 | } |
|
95 | 1 | } |
|
96 | |||
97 | @Override |
||
98 | public void clear() { |
||
99 | try { |
||
100 | 1 | executeQuery("DELETE FROM `MAP_CACHE`"); |
|
101 | } catch (SQLException e) { |
||
102 | throw new RuntimeException("Cannot clear map cache", e); |
||
0 ignored issues
–
show
|
|||
103 | 1 | } |
|
104 | 1 | } |
|
105 | |||
106 | /** |
||
107 | * Initialize the SQL database |
||
108 | * |
||
109 | * @throws SQLException |
||
110 | */ |
||
111 | private void initDatabase() throws SQLException { |
||
112 | 1 | boolean hasTable = hasTable("MAP_CACHE"); |
|
113 | 1 | boolean checkVersion = checkSchemaVersion(); |
|
114 | |||
115 | 1 | if (hasTable && !checkVersion) { |
|
116 | 1 | executeQuery("DROP TABLE `MAP_CACHE`"); |
|
117 | 1 | hasTable = false; |
|
118 | } |
||
119 | |||
120 | 1 | if (!hasTable) { |
|
121 | 1 | createCacheTable(); |
|
122 | } |
||
123 | |||
124 | 1 | if (!checkVersion) { |
|
125 | 1 | updateSchemaVersion(); |
|
126 | } |
||
127 | 1 | } |
|
128 | |||
129 | /** |
||
130 | * Check if the schema version match |
||
131 | */ |
||
132 | private boolean checkSchemaVersion() throws SQLException { |
||
133 | 1 | if (!hasTable("SCHEMA_VERSION")) { |
|
134 | 1 | executeQuery("CREATE TABLE `SCHEMA_VERSION` (`VERSION` INTEGER PRIMARY KEY)"); |
|
135 | |||
136 | 1 | return false; |
|
137 | } |
||
138 | |||
139 | 1 | try (PreparedStatement stmt = connection.prepareStatement("SELECT `VERSION` FROM `SCHEMA_VERSION` WHERE `VERSION` = ?")) { |
|
140 | 1 | stmt.setInt(1, SCHEMA_VERSION); |
|
141 | |||
142 | 1 | try (ResultSet rs = stmt.executeQuery()) { |
|
143 | 1 | return rs.next(); |
|
144 | } |
||
145 | } |
||
146 | } |
||
147 | |||
148 | /** |
||
149 | * Check if a table exists |
||
150 | * |
||
151 | * @param tableName The table name |
||
152 | * |
||
153 | * @return true if exists |
||
154 | */ |
||
155 | private boolean hasTable(String tableName) throws SQLException { |
||
156 | 1 | try (PreparedStatement stmt = connection.prepareStatement("SELECT `name` FROM `sqlite_master` WHERE `type` = 'table' AND `name` = ?")) { |
|
157 | 1 | stmt.setString(1, tableName); |
|
158 | |||
159 | 1 | try (ResultSet rs = stmt.executeQuery()) { |
|
160 | 1 | return rs.next(); |
|
161 | } |
||
162 | } |
||
163 | } |
||
164 | |||
165 | /** |
||
166 | * Set the schema version |
||
167 | */ |
||
168 | private void updateSchemaVersion() throws SQLException { |
||
169 | 1 | try (PreparedStatement stmt = connection.prepareStatement("INSERT INTO `SCHEMA_VERSION` VALUES (?)")) { |
|
170 | 1 | stmt.setInt(1, SCHEMA_VERSION); |
|
171 | 1 | stmt.executeUpdate(); |
|
172 | } |
||
173 | 1 | } |
|
174 | |||
175 | /** |
||
176 | * Create the table |
||
177 | */ |
||
178 | private void createCacheTable() throws SQLException { |
||
179 | 1 | executeQuery( |
|
180 | "CREATE TABLE `MAP_CACHE` (" + |
||
181 | "`ID` INTEGER PRIMARY KEY, " + |
||
182 | "`VERSION` VARCHAR(32), " + |
||
183 | "`WIDTH` INTEGER, " + |
||
184 | "`HEIGHT` INTEGER, " + |
||
185 | "`BACKGROUND_NUM` INTEGER, " + |
||
186 | "`AMBIANCE_ID` INTEGER, " + |
||
187 | "`MUSIC_ID` INTEGER, " + |
||
188 | "`OUTDOOR` INTEGER, " + |
||
189 | "`CAPABILITIES` INTEGER, " + |
||
190 | "`MAP_DATA` BLOB" + |
||
191 | ")" |
||
192 | ); |
||
193 | 1 | } |
|
194 | |||
195 | /** |
||
196 | * Execute a simple SQL query |
||
197 | */ |
||
198 | private void executeQuery(String query) throws SQLException { |
||
199 | 1 | try (Statement stmt = connection.createStatement()) { |
|
200 | 1 | stmt.execute(query); |
|
201 | } |
||
202 | 1 | } |
|
203 | |||
204 | /** |
||
205 | * Create a new cache instance for a SQLite file database |
||
206 | * |
||
207 | * @param filename The sqlite filename |
||
208 | * |
||
209 | * @return The cache instance |
||
210 | */ |
||
211 | static public SqlMapStructureCache createBySqliteFile(String filename) throws SQLException { |
||
212 | 1 | File file = new File(filename); |
|
213 | |||
214 | 1 | if (!file.exists() && !file.getParentFile().exists()) { |
|
215 | 1 | file.getParentFile().mkdirs(); |
|
216 | } |
||
217 | |||
218 | 1 | return new SqlMapStructureCache(DriverManager.getConnection("jdbc:sqlite:" + filename)); |
|
219 | } |
||
220 | } |
||
221 |