· 8 years ago · Feb 03, 2018, 03:20 PM
1import java.lang.reflect.Field;
2import java.lang.reflect.Modifier;
3import java.util.*;
4import java.util.stream.Collectors;
5
6public class SQLiteHelper implements SQLiteHelperInterface {
7
8 private static final Map<String, String> CLASS_NAMES_TO_SQL_TYPES = mapOf(
9 "int", "INTEGER",
10 "long", "INTEGER",
11 "java.lang.Integer", "INTEGER",
12 "java.lang.Long", "INTEGER",
13 "float", "REAL",
14 "double", "REAL",
15 "java.lang.Float", "REAL",
16 "java.lang.Double", "REAL",
17 "java.lang.String", "TEXT",
18 "boolean", "INTEGER",
19 "java.lang.Boolean", "INTEGER"
20 );
21
22 @Override
23 public String createTable(final Object o) {
24 StringBuilder sql = new StringBuilder("CREATE TABLE IF NOT EXISTS " + o.getClass().getName() + "(");
25 List<RowData<String, String, Object>> rowDataList = loadClassFields(o.getClass(), o);
26 // data processing
27 rowDataList.stream().filter(it -> it.getValue() != null)
28 .forEach(rowData -> sql.append(rowData.getKey()).append(" ").append(rowData.getValue()).append(", "));
29
30 // foreign keys processing
31 rowDataList.stream().filter(it -> it.getForeignKeyPayload() != null).
32 map(RowData::getForeignKeyPayload).
33 forEach(payload -> sql.append(payload).append(", "));
34
35 // Close create table
36 sql.append(");");
37
38 // index payload
39 Map<String, List<IndexData>> indexData = rowDataList.stream().filter(it -> it.getIndexData() != null).
40 map(RowData::getIndexData).
41 collect(Collectors.groupingBy(IndexData::getIndexName));
42
43 for (List<IndexData> indexDataList : indexData.values()) {
44 String indexPayload = "\nCREATE" + (indexDataList.get(0).isUnique() ? " UNIQUE INDEX" : " INDEX");
45 indexPayload += String.format(" %s ON %s (", indexDataList.get(0).getIndexName(), indexDataList.get(0).getTableName());
46
47 for (IndexData indexData1 : indexDataList) {
48 indexPayload += indexData1.getColumn() + ", ";
49 }
50
51 indexPayload += ");";
52 sql.append(indexPayload);
53 }
54
55 return sql.toString().replaceAll(", \\);", ");");
56 }
57
58 @Override
59 public String insert(final Object o) {
60 StringBuilder sql = new StringBuilder("INSERT INTO " + o.getClass().getName() + "(");
61 List<RowData<String, String, Object>> rowData = loadClassFields(o.getClass(), o);
62
63 boolean shouldUpdate = rowData
64 .stream().
65 filter(it -> it.isPrimaryKey)
66 .anyMatch(it -> !it.getBalast().equals(0));
67
68 if (shouldUpdate) {
69 return update(o);
70 } else {
71 StringBuilder finalSql = sql;
72 rowData.stream().
73 filter(rd -> !rd.isAutoincrement).
74 map(RowData::getKey).
75 forEach(it -> finalSql.append(it).append(","));
76
77 sql = finalSql;
78 sql.append(")VALUES (");
79 sql = new StringBuilder(sql.toString().replaceAll(",\\)", "\\)"));
80
81 StringBuilder finalSql1 = sql;
82 rowData.stream().
83 filter(rd -> !rd.isAutoincrement)
84 .forEach(it -> {
85 if (it.getValue() != null) {
86 if (it.getBalast() == null) {
87 finalSql1.append("null").append(", ");
88 } else {
89 String balast = it.getBalast().toString();
90 balast = processBoolean(balast);
91 balast = processStrings(it, balast);
92 balast = processAlphaNumeric(balast);
93 finalSql1.append(balast).append(", ");
94 }
95 }
96 });
97
98 sql = finalSql1;
99 sql.append(");");
100 return sql.toString().replaceAll(", \\);", ");");
101 }
102 }
103
104 private String update(final Object o) {
105 String tableName = o.getClass().getName();
106 String sql = "UPDATE " + tableName + " SET ";
107
108 List<RowData<String, String, Object>> rowData = loadClassFields(o.getClass(), o);
109
110 rowData = rowData.stream().filter(rd -> !rd.isAutoincrement).collect(Collectors.toList());
111 for (RowData it : rowData) {
112 if (it.getValue() != null) {
113 if (it.getBalast() == null) {
114 sql += it.getKey() + "=null" + ", ";
115 } else {
116 String balast = it.getBalast().toString();
117 balast = processBoolean(balast);
118 balast = processStrings(it, balast);
119 balast = processAlphaNumeric(balast);
120 sql += it.getKey() + "=" + balast + ", ";
121 }
122 }
123 }
124
125 sql = sql.substring(0, sql.length() - 2);
126
127 Optional<RowData<String, String, Object>> pkey = loadClassFields(o.getClass(), o).stream().filter(it -> it.isPrimaryKey).findFirst();
128
129 if (pkey.isPresent()) {
130 sql += " WHERE " + pkey.get().getKey() + "=" + pkey.get().getBalast();
131 }
132
133 return sql + ";";
134 }
135
136 private String processAlphaNumeric(String balast) {
137 if (balast.contains(".")) {
138 balast = "\"" + balast.replaceAll("\\.", ",") + "\"";
139 }
140 return balast;
141 }
142
143 private String processStrings(RowData<String, String, Object> it, String balast) {
144 if (it.getBalast() instanceof String) {
145 balast = "\"" + balast + "\"";
146 }
147 return balast;
148 }
149
150 private String processBoolean(String balast) {
151 if ("true".equals(balast)) {
152 balast = "1";
153 } else if ("false".equals(balast)) {
154 balast = "0";
155 }
156 return balast;
157 }
158
159 private List<RowData<String, String, Object>> loadClassFields(final Class<?> clazz, final Object inst) {
160 return Arrays
161 .stream(clazz.getFields()).
162 filter(it -> Modifier.isPublic(it.getModifiers())).
163 map(it -> getRow(inst, it))
164 .collect(Collectors.toList());
165 }
166
167 private RowData<String, String, Object> getRow(Object inst, Field it) {
168 try {
169
170 String name = it.getName();
171 String sqlType = getSqlType(it);
172 Object fieldValue = it.get(inst);
173
174 String foreignKeyPayload = null;
175 ForeignKeyAnnotation foreignKeyAnnotation = it.getAnnotation(ForeignKeyAnnotation.class);
176 if (foreignKeyAnnotation != null) {
177 foreignKeyPayload = String.format("CONSTRAINT %s FOREIGN KEY(%s) REFERENCES %s(%s)", foreignKeyAnnotation.foreignKeyName(), foreignKeyAnnotation.foreignKeyName(), foreignKeyAnnotation.foreignTableName(), foreignKeyAnnotation.foreignColumnName());
178 }
179
180 IndexData indexData = null;
181 IndexAnnotation indexAnnotation = it.getAnnotation(IndexAnnotation.class);
182 if (indexAnnotation != null) {
183 indexData = new IndexData(indexAnnotation.isUnique(), indexAnnotation.indexName(), inst.getClass().getName(), name);
184 }
185
186 RowData<String, String, Object> rowData = RowData.of(name, sqlType, fieldValue, foreignKeyPayload, indexData);
187
188 KeyAnnotation keyAnnotation = it.getAnnotation(KeyAnnotation.class);
189 if (keyAnnotation != null) {
190 if (keyAnnotation.autoIncrement()) {
191 rowData.isAutoincrement = true;
192 }
193 rowData.isPrimaryKey = true;
194 }
195
196 return rowData;
197 } catch (IllegalAccessException e) {
198 // ig
199 }
200 return null;
201 }
202
203 private String getSqlType(Field it) {
204 String base = CLASS_NAMES_TO_SQL_TYPES.get(it.getType().getName());
205 base = processKeyAnnotation(it, base);
206 return base;
207 }
208
209 private String processKeyAnnotation(Field it, String base) {
210 KeyAnnotation keyAnnotation = it.getAnnotation(KeyAnnotation.class);
211 if (keyAnnotation != null) {
212 base += keyAnnotation.autoIncrement() ? " PRIMARY KEY AUTOINCREMENT" : " NOT NULL PRIMARY KEY";
213 }
214 return base;
215 }
216
217 @SuppressWarnings("unchecked")
218 private static <T, R> Map<T, R> mapOf(final Object... args) {
219 Map<T, R> out = new HashMap<>();
220 for (int i = 0; i < args.length; i += 2) {
221 out.put((T) args[i], (R) args[i + 1]);
222 }
223 return out;
224 }
225}
226
227class RowData<L, R, G> {
228 public boolean isAutoincrement;
229 public boolean isPrimaryKey;
230 private L key;
231 private R value;
232 private G balast;
233 private String foreignKeyPayload;
234 private IndexData indexData;
235
236 public String getForeignKeyPayload() {
237 return foreignKeyPayload;
238 }
239
240 public IndexData getIndexData() {
241 return indexData;
242 }
243
244 private RowData(final L key, final R value, final G balast, final String foreignKeyPayload, final IndexData indexData) {
245 this.key = key;
246 this.value = value;
247 this.balast = balast;
248 this.foreignKeyPayload = foreignKeyPayload;
249 this.indexData = indexData;
250 }
251
252 public static <L, R, G> RowData<L, R, G> of(final L left, final R right, final G balast, final String foreignKeyPayload, final IndexData indexData) {
253 return new RowData<>(left, right, balast, foreignKeyPayload, indexData);
254 }
255
256 public L getKey() {
257 return key;
258 }
259
260 public R getValue() {
261 return value;
262 }
263
264 public G getBalast() {
265 return balast;
266 }
267}
268
269class IndexData {
270 private boolean isUnique;
271 private String indexName;
272 private String tableName;
273 private String column;
274
275 public IndexData(boolean isUnique, String indexName, String tableName, String column) {
276 this.isUnique = isUnique;
277 this.indexName = indexName;
278 this.tableName = tableName;
279 this.column = column;
280 }
281
282 public boolean isUnique() {
283 return isUnique;
284 }
285
286 public String getIndexName() {
287 return indexName;
288 }
289
290 public String getTableName() {
291 return tableName;
292 }
293
294 public String getColumn() {
295 return column;
296 }
297}