1 package xuml.tools.model.compiler.runtime.query;
2
3 import java.io.ByteArrayOutputStream;
4 import java.io.PrintStream;
5 import java.util.HashMap;
6 import java.util.List;
7 import java.util.Map;
8 import java.util.Map.Entry;
9 import java.util.concurrent.atomic.AtomicInteger;
10
11 import javax.persistence.EntityManager;
12 import javax.persistence.TypedQuery;
13
14 import com.google.common.annotations.VisibleForTesting;
15 import com.google.common.base.Optional;
16 import com.google.common.base.Preconditions;
17 import com.google.common.collect.Maps;
18
19 import xuml.tools.model.compiler.runtime.Entity;
20 import xuml.tools.model.compiler.runtime.Info;
21
22 public class SelectBuilder<T extends Entity<T>> {
23
24 private final BooleanExpression<T> e;
25 private Info info;
26 private Class<T> entityClass;
27 private final AtomicInteger parameterNo = new AtomicInteger(0);
28
29 public SelectBuilder(BooleanExpression<T> e) {
30 this.e = e;
31 }
32
33 public static <R extends Entity<R>> SelectBuilder<R> builder(BooleanExpression<R> e) {
34 return new SelectBuilder<R>(e);
35 }
36
37 public SelectBuilder<T> select(BooleanExpression<T> exp) {
38 return new SelectBuilder<T>(e.and(exp));
39 }
40
41 public SelectBuilder<T> entityClass(Class<T> cls) {
42 entityClass = cls;
43 return this;
44 }
45
46 public SelectBuilder<T> info(Info info) {
47 Preconditions.checkNotNull(info,
48 "thread local Info not available. You need to set the Context EntityManagerFactory before create a SelectBuilder");
49 this.info = info;
50 return this;
51 }
52
53
54
55
56
57
58
59
60 public Optional<T> one() {
61 return one(info.getCurrentEntityManager());
62 }
63
64
65
66
67
68
69
70
71
72 public Optional<T> one(EntityManager em) {
73 List<T> list = many(em);
74 int size = list.size();
75 if (size == 1)
76 return Optional.of(list.get(0));
77 else if (size == 0)
78 return Optional.absent();
79 else
80 throw new RuntimeException("expected 0 or 1 but found " + size);
81 }
82
83 public Optional<T> any(EntityManager em) {
84 List<T> list = many(em);
85 if (list.size() >= 1)
86 return Optional.of(list.get(0));
87 else
88 return Optional.absent();
89 }
90
91 public Optional<T> any() {
92 return any(info.getCurrentEntityManager());
93 }
94
95 public List<T> many() {
96 return many(info.getCurrentEntityManager());
97 }
98
99 public List<T> many(EntityManager em) {
100 Preconditions.checkNotNull(em, "entity manager is null!");
101 ClauseAndParameters c = getClauseAndParameters();
102 String sql = getSql(entityClass, c.clause);
103 TypedQuery<T> query = em.createQuery(sql, entityClass);
104 for (Entry<String, Object> p : c.parameters.entrySet())
105 query = query.setParameter(p.getKey(), p.getValue());
106 return query.getResultList();
107 }
108
109 private ClauseAndParameters getClauseAndParameters() {
110 return getClauseAndParameters(e);
111 }
112
113 @VisibleForTesting
114 static String getSql(Class<?> entityClass, String clause) {
115 String prefix = "select e from " + entityClass.getSimpleName() + " e";
116 String sql;
117 if (clause.length() > 0)
118 sql = prefix + " where " + clause;
119 else
120 sql = prefix;
121 return sql;
122 }
123
124 @VisibleForTesting
125 String getClause() {
126 return getClauseAndParameters(e).clause;
127 }
128
129 private ClauseAndParameters getClauseAndParameters(BooleanExpression<T> e) {
130 if (e == null)
131 return new ClauseAndParameters("", new HashMap<String, Object>());
132 Map<String, Object> parameters = Maps.newHashMap();
133 ByteArrayOutputStream bytes = new ByteArrayOutputStream();
134 PrintStream out = new PrintStream(bytes);
135 if (e instanceof Not) {
136 Not<T> not = (Not<T>) e;
137 ClauseAndParameters c = getClauseAndParameters(not.getExpression());
138 parameters.putAll(c.parameters);
139 out.print("!(" + c.clause + ")");
140 } else if (e instanceof NumericComparison) {
141 NumericComparison<T> c = (NumericComparison<T>) e;
142 ClauseAndParameters c1 = getClauseAndParameters(c.getExpression1());
143 ClauseAndParameters c2 = getClauseAndParameters(c.getExpression2());
144 parameters.putAll(c1.parameters);
145 parameters.putAll(c2.parameters);
146 out.print("(" + c1.clause + " " + getOperator(c.getOperator()) + " " + c2.clause + ")");
147 } else if (e instanceof BinaryBooleanExpression) {
148 BinaryBooleanExpression<T> c = (BinaryBooleanExpression<T>) e;
149 ClauseAndParameters c1 = getClauseAndParameters(c.getExpression1());
150 ClauseAndParameters c2 = getClauseAndParameters(c.getExpression2());
151 parameters.putAll(c1.parameters);
152 parameters.putAll(c2.parameters);
153 out.print("(" + c1.clause + " " + getOperator(c.getOperator()) + " " + c2.clause + ")");
154 } else if (e instanceof StringComparison) {
155 StringComparison<T> c = (StringComparison<T>) e;
156 ClauseAndParameters c1 = getClauseAndParameters(c.getExpression1());
157 ClauseAndParameters c2 = getClauseAndParameters(c.getExpression2());
158 parameters.putAll(c1.parameters);
159 parameters.putAll(c2.parameters);
160 out.print("(" + c1.clause + " " + getOperator(c.getOperator()) + " " + c2.clause + ")");
161 } else if (e instanceof DateComparison) {
162 DateComparison<T> c = (DateComparison<T>) e;
163 ClauseAndParameters c1 = getClauseAndParameters(c.getExpression1());
164 ClauseAndParameters c2 = getClauseAndParameters(c.getExpression2());
165 parameters.putAll(c1.parameters);
166 parameters.putAll(c2.parameters);
167 out.print("(" + c1.clause + " " + getOperator(c.getOperator()) + " " + c2.clause + ")");
168 }
169 out.close();
170 return new ClauseAndParameters(bytes.toString(), parameters);
171 }
172
173 private String getOperator(DateComparisonOperator op) {
174 if (op == DateComparisonOperator.EQ)
175 return "=";
176 else if (op == DateComparisonOperator.NEQ)
177 return "!=";
178 else if (op == DateComparisonOperator.LT)
179 return "<";
180 else if (op == DateComparisonOperator.GT)
181 return ">";
182 else if (op == DateComparisonOperator.LTE)
183 return "<=";
184 else if (op == DateComparisonOperator.GTE)
185 return ">=";
186 else
187 throw new RuntimeException("not implemented " + op);
188 }
189
190 private ClauseAndParameters getClauseAndParameters(DateExpression<T> e) {
191 Map<String, Object> parameters = Maps.newHashMap();
192 ByteArrayOutputStream bytes = new ByteArrayOutputStream();
193 PrintStream out = new PrintStream(bytes);
194 if (e instanceof DateConstant) {
195 DateConstant<T> c = (DateConstant<T>) e;
196 addToParameters(parameters, out, c.getValue());
197 } else if (e instanceof IsNullDate) {
198 IsNullDate<T> n = (IsNullDate<T>) e;
199 ClauseAndParameters c = getClauseAndParameters(n.getExpression());
200 out.print(c.clause + " is null");
201 } else if (e instanceof DateExpressionField) {
202 DateExpressionField<T> f = (DateExpressionField<T>) e;
203 out.print("e." + f.getField().getName());
204 }
205 out.close();
206 return new ClauseAndParameters(bytes.toString(), parameters);
207 }
208
209 private String getOperator(StringComparisonOperator op) {
210 if (op == StringComparisonOperator.EQ)
211 return "=";
212 else if (op == StringComparisonOperator.NEQ)
213 return "!=";
214 else if (op == StringComparisonOperator.GT)
215 return ">";
216 else if (op == StringComparisonOperator.GTE)
217 return ">=";
218 else if (op == StringComparisonOperator.LT)
219 return "<";
220 else if (op == StringComparisonOperator.LTE)
221 return "<=";
222 else if (op == StringComparisonOperator.LIKE)
223 return "like";
224 else
225 throw new RuntimeException("not implemented " + op);
226 }
227
228 private ClauseAndParameters getClauseAndParameters(StringExpression<T> e) {
229 Map<String, Object> parameters = Maps.newHashMap();
230 ByteArrayOutputStream bytes = new ByteArrayOutputStream();
231 PrintStream out = new PrintStream(bytes);
232 if (e instanceof BinaryStringExpression) {
233 BinaryStringExpression<T> c = (BinaryStringExpression<T>) e;
234 ClauseAndParameters c1 = getClauseAndParameters(c.getExpression1());
235 ClauseAndParameters c2 = getClauseAndParameters(c.getExpression2());
236 parameters.putAll(c1.parameters);
237 parameters.putAll(c2.parameters);
238 out.print("(" + c1.clause + " " + getOperator(c.getOperator()) + " " + c2.clause + ")");
239 } else if (e instanceof StringConstant) {
240 StringConstant<T> c = (StringConstant<T>) e;
241 addToParameters(parameters, out, c.getValue());
242 } else if (e instanceof IsNullString) {
243 IsNullString<T> n = (IsNullString<T>) e;
244 ClauseAndParameters c = getClauseAndParameters(n.getExpression());
245 out.print(c.clause + " is null");
246 } else if (e instanceof StringExpressionField) {
247 StringExpressionField<T> f = (StringExpressionField<T>) e;
248 out.print("e." + f.getField().getName());
249 }
250 out.close();
251 return new ClauseAndParameters(bytes.toString(), parameters);
252 }
253
254 private void addToParameters(Map<String, Object> parameters, PrintStream out, Object object) {
255 int index = parameterNo.incrementAndGet();
256 String parameterName = "_p" + index;
257 parameters.put(parameterName, object);
258 out.print(":" + parameterName);
259 }
260
261 private String getOperator(BinaryStringOperator op) {
262 if (op == BinaryStringOperator.PLUS)
263 return "+";
264 else
265 throw new RuntimeException("not implemented " + op);
266 }
267
268 private String getOperator(BinaryBooleanOperator op) {
269 if (op == BinaryBooleanOperator.AND)
270 return "and";
271 else if (op == BinaryBooleanOperator.OR)
272 return "or";
273 else
274 throw new RuntimeException("not implemented " + op);
275 }
276
277 private String getOperator(NumericComparisonOperator c) {
278 String op;
279 if (c == NumericComparisonOperator.EQ)
280 op = "=";
281 else if (c == NumericComparisonOperator.NEQ)
282 op = "!=";
283 else if (c == NumericComparisonOperator.LT)
284 op = "<";
285 else if (c == NumericComparisonOperator.GT)
286 op = ">";
287 else if (c == NumericComparisonOperator.LTE)
288 op = "<=";
289 else if (c == NumericComparisonOperator.GTE)
290 op = ">=";
291 else
292 throw new RuntimeException("unimplemented operator " + c);
293 return op;
294 }
295
296 private ClauseAndParameters getClauseAndParameters(NumericExpression<T> e) {
297 Map<String, Object> parameters = Maps.newHashMap();
298 ByteArrayOutputStream bytes = new ByteArrayOutputStream();
299 PrintStream out = new PrintStream(bytes);
300 if (e instanceof BinaryNumericExpression) {
301 BinaryNumericExpression<T> c = (BinaryNumericExpression<T>) e;
302 ClauseAndParameters c1 = getClauseAndParameters(c.getExpression1());
303 ClauseAndParameters c2 = getClauseAndParameters(c.getExpression2());
304 parameters.putAll(c1.parameters);
305 parameters.putAll(c2.parameters);
306 out.print("(" + c1.clause + " " + getOperator(c.getOperator()) + " " + c2.clause + ")");
307 } else if (e instanceof NumericConstant) {
308 NumericConstant<T> c = (NumericConstant<T>) e;
309 addToParameters(parameters, out, c.getValue());
310 } else if (e instanceof IsNullNumeric) {
311 IsNullNumeric<T> n = (IsNullNumeric<T>) e;
312 ClauseAndParameters c = getClauseAndParameters(n.getExpression());
313 out.print(c.clause + " is null");
314 } else if (e instanceof NumericExpressionField) {
315 NumericExpressionField<T> f = (NumericExpressionField<T>) e;
316 out.print("e." + f.getField().getName());
317 }
318 out.close();
319 return new ClauseAndParameters(bytes.toString(), parameters);
320 }
321
322 private String getOperator(BinaryNumericOperator op) {
323
324 if (op == BinaryNumericOperator.DIVIDE)
325 return "/";
326 else if (op == BinaryNumericOperator.MINUS)
327 return "-";
328 else if (op == BinaryNumericOperator.PLUS)
329 return "+";
330 else if (op == BinaryNumericOperator.TIMES)
331 return "*";
332 else
333 throw new RuntimeException("not implemented " + op);
334 }
335
336 private static class ClauseAndParameters {
337 String clause;
338 Map<String, Object> parameters = Maps.newHashMap();
339
340 ClauseAndParameters(String clause, Map<String, Object> parameters) {
341 super();
342 this.clause = clause;
343 this.parameters = parameters;
344 }
345 }
346
347 }