5
import play.data.validation.MaxSize;
6
import play.data.validation.Password;
7
import play.data.validation.Required;
8
import play.db.jpa.FileAttachment;
9
import play.db.jpa.JPA;
10
import play.db.jpa.JPASupport;
11
import play.exceptions.TemplateNotFoundException;
12
import play.i18n.Messages;
13
import play.mvc.Before;
14
import play.mvc.Controller;
15
import play.mvc.Router;
17
import javax.persistence.*;
18
import java.lang.annotation.ElementType;
19
import java.lang.annotation.Retention;
20
import java.lang.annotation.RetentionPolicy;
21
import java.lang.annotation.Target;
22
import java.lang.reflect.Field;
23
import java.lang.reflect.Modifier;
24
import java.lang.reflect.ParameterizedType;
27
public abstract class CRUD extends Application {
30
static void addType() {
31
ObjectType type = ObjectType.get(getControllerClass());
32
renderArgs.put("type", type);
35
public static void index() {
38
} catch (TemplateNotFoundException e) {
39
render("CRUD/index.html");
43
public static void autocomplete(String oType, String q, String searchFields) throws ClassNotFoundException {
44
ObjectType type = new ObjectType(oType);
47
String orderBy = null, order = null;
48
List<JPASupport> objects = type.findPage(page, q, searchFields, orderBy, order, (String) request.args.get("where"));
49
Long count = type.count(q, searchFields, (String) request.args.get("where"));
50
Long totalCount = type.count(null, null, (String) request.args.get("where"));
52
render(type, objects, count, totalCount, page, orderBy, order);
53
} catch (TemplateNotFoundException e) {
54
render("CRUD/autocomplete.html", type, objects, count, totalCount, page, orderBy, order);
58
public static void list(int page, String search, String searchFields, String orderBy, String order) {
59
ObjectType type = ObjectType.get(getControllerClass());
64
List<JPASupport> objects = type.findPage(page, search, searchFields, orderBy, order, (String) request.args.get("where"));
65
Long count = type.count(search, searchFields, (String) request.args.get("where"));
66
Long totalCount = type.count(null, null, (String) request.args.get("where"));
68
render(type, objects, count, totalCount, page, orderBy, order);
69
} catch (TemplateNotFoundException e) {
70
render("CRUD/list.html", type, objects, count, totalCount, page, orderBy, order);
74
public static void show(String id) {
75
ObjectType type = ObjectType.get(getControllerClass());
77
JPASupport object = type.findById(id);
80
} catch (TemplateNotFoundException e) {
81
render("CRUD/show.html", type, object);
85
public static void attachment(String id, String field) throws Exception {
86
ObjectType type = ObjectType.get(getControllerClass());
88
JPASupport object = type.findById(id);
89
FileAttachment attachment = (FileAttachment) object.getClass().getField(field).get(object);
90
if (attachment == null) {
93
renderBinary(attachment.get(), attachment.filename);
96
public static void save(String id) throws Exception {
97
ObjectType type = ObjectType.get(getControllerClass());
99
JPASupport object = type.findById(id);
100
object = object.edit("object", params);
101
// Look if we need to deserialize
102
for (ObjectType.ObjectField field : type.getFields()) {
103
if (field.type.equals("serializedText") && params.get("object." + field.name) != null) {
104
Field f = object.getClass().getDeclaredField(field.name);
105
f.set(object, CRUD.collectionDeserializer(params.get("object." + field.name),(Class)((ParameterizedType) f.getGenericType()).getActualTypeArguments()[0]));
109
validation.valid(object);
110
if (validation.hasErrors()) {
111
renderArgs.put("error", Messages.get("crud.hasErrors"));
113
render(request.controller.replace(".", "/") + "/show.html", type, object);
114
} catch (TemplateNotFoundException e) {
115
render("CRUD/show.html", type, object);
119
flash.success(Messages.get("crud.saved", type.modelName, object.getEntityId()));
120
if (params.get("_save") != null) {
121
redirect(request.controller + ".list");
123
redirect(request.controller + ".show", object.getEntityId());
126
public static void blank() {
127
ObjectType type = ObjectType.get(getControllerClass());
128
notFoundIfNull(type);
131
} catch (TemplateNotFoundException e) {
132
render("CRUD/blank.html", type);
136
public static void create() throws Exception {
137
ObjectType type = ObjectType.get(getControllerClass());
138
notFoundIfNull(type);
139
JPASupport object = type.entityClass.newInstance();
140
validation.valid(object.edit("object", params));
141
if (validation.hasErrors()) {
142
renderArgs.put("error", Messages.get("crud.hasErrors"));
144
render(request.controller.replace(".", "/") + "/blank.html", type);
145
} catch (TemplateNotFoundException e) {
146
render("CRUD/blank.html", type);
150
flash.success(Messages.get("crud.created", type.modelName, object.getEntityId()));
151
if (params.get("_save") != null) {
152
redirect(request.controller + ".list");
154
if (params.get("_saveAndAddAnother") != null) {
155
redirect(request.controller + ".blank");
157
redirect(request.controller + ".show", object.getEntityId());
160
public static void delete(String id) {
161
ObjectType type = ObjectType.get(getControllerClass());
162
notFoundIfNull(type);
163
JPASupport object = type.findById(id);
166
} catch (Exception e) {
167
flash.error(Messages.get("crud.delete.error", type.modelName, object.getEntityId()));
168
redirect(request.controller + ".show", object.getEntityId());
170
flash.success(Messages.get("crud.deleted", type.modelName, object.getEntityId()));
171
redirect(request.controller + ".list");
175
@Retention(RetentionPolicy.RUNTIME)
176
@Target(ElementType.TYPE)
177
public @interface For {
183
static int getPageSize() {
184
return Integer.parseInt(Play.configuration.getProperty("crud.pageSize", "30"));
187
public static class ObjectType implements Comparable<ObjectType> {
189
public Class<? extends CRUD> controllerClass;
190
public Class<? extends JPASupport> entityClass;
192
public String modelName;
193
public String controllerName;
195
public ObjectType(Class modelClass) {
196
this.modelName = modelClass.getSimpleName();
197
this.entityClass = modelClass;
200
public ObjectType(String modelClass) throws ClassNotFoundException {
201
this(Play.classloader.loadClass(modelClass));
204
public static ObjectType forClass(String modelClass) throws ClassNotFoundException {
205
return new ObjectType(modelClass);
208
public static ObjectType get(Class controllerClass) {
209
Class entityClass = getEntityClassForController(controllerClass);
210
if (entityClass == null || !JPASupport.class.isAssignableFrom(entityClass)) {
213
ObjectType type = new ObjectType(entityClass);
214
type.name = controllerClass.getSimpleName();
215
type.controllerName = controllerClass.getSimpleName().toLowerCase();
216
type.controllerClass = controllerClass;
220
public static Class getEntityClassForController(Class controllerClass) {
221
if (controllerClass.isAnnotationPresent(For.class)) {
222
return ((For) (controllerClass.getAnnotation(For.class))).value();
224
String name = controllerClass.getSimpleName();
225
name = "models." + name.substring(0, name.length() - 1);
227
return Play.classloader.loadClass(name);
228
} catch (ClassNotFoundException e) {
233
public Object getListAction() {
234
return Router.reverse(controllerClass.getName() + ".list");
237
public Object getBlankAction() {
238
return Router.reverse(controllerClass.getName() + ".blank");
241
public Long count(String search, String searchFields, String where) {
242
String q = "select count(e) from " + entityClass.getName() + " e";
243
if (search != null && !search.equals("")) {
244
String searchQuery = getSearchQuery(searchFields);
245
if (!searchQuery.equals("")) {
246
q += " where (" + searchQuery + ")";
248
q += (where != null ? " and " + where : "");
250
q += (where != null ? " where " + where : "");
252
Query query = JPA.em().createQuery(q);
253
if (search != null && !search.equals("") && q.indexOf("?1") != -1) {
254
query.setParameter(1, "%" + search.toLowerCase() + "%");
256
return Long.decode(query.getSingleResult().toString());
259
public List findPage(int page, String search, String searchFields, String orderBy, String order, String where) {
260
int pageLength = getPageSize();
261
String q = "from " + entityClass.getName();
262
if (search != null && !search.equals("")) {
263
String searchQuery = getSearchQuery(searchFields);
264
if (!searchQuery.equals("")) {
265
q += " where (" + searchQuery + ")";
267
q += (where != null ? " and " + where : "");
269
q += (where != null ? " where " + where : "");
271
if (orderBy == null && order == null) {
275
if (orderBy == null && order != null) {
278
if (order == null || (!order.equals("ASC") && !order.equals("DESC"))) {
281
q += " order by " + orderBy + " " + order;
282
Query query = JPA.em().createQuery(q);
283
if (search != null && !search.equals("") && q.indexOf("?1") != -1) {
284
query.setParameter(1, "%" + search.toLowerCase() + "%");
286
query.setFirstResult((page - 1) * pageLength);
287
query.setMaxResults(pageLength);
288
return query.getResultList();
291
public String getSearchQuery(String searchFields) {
292
List<String> fields = null;
293
if (searchFields != null && !searchFields.equals("")) {
294
fields = Arrays.asList(searchFields.split("[ ]"));
297
for (ObjectField field : getFields()) {
298
if (field.searchable && (fields == null ? true : fields.contains(field.name))) {
302
q += "lower(" + field.name + ") like ?1";
308
public JPASupport findById(Object id) {
309
Query query = JPA.em().createQuery("from " + entityClass.getName() + " where id = ?");
311
query.setParameter(1, play.data.binding.Binder.directBind(id + "", play.db.jpa.JPASupport.findKeyType(entityClass)));
312
} catch (Exception e) {
313
throw new RuntimeException("Something bad with id type ?", e);
315
return (JPASupport) query.getSingleResult();
318
public List<ObjectField> getFields() {
319
List fields = new ArrayList();
320
for (Field f : entityClass.getFields()) {
321
if (Modifier.isTransient(f.getModifiers()) || Modifier.isFinal(f.getModifiers())) {
324
ObjectField of = new ObjectField(f);
325
if (of.type != null) {
332
public ObjectField getField(String name) {
333
for (ObjectField field : getFields()) {
334
if (field.name.equals(name)) {
341
public int compareTo(ObjectType other) {
342
return modelName.compareTo(other.modelName);
345
public static class ObjectField {
347
public String type = "unknown";
349
public String relation;
350
public boolean multiple;
351
public boolean searchable;
352
public Object[] choices;
353
public boolean required;
354
public String serializedValue;
356
public ObjectField(Field field) {
357
if (CharSequence.class.isAssignableFrom(field.getType())) {
360
if (field.isAnnotationPresent(MaxSize.class)) {
361
int maxSize = field.getAnnotation(MaxSize.class).value();
366
if (field.isAnnotationPresent(Password.class)) {
370
if (Number.class.isAssignableFrom(field.getType()) || field.getType().equals(double.class) || field.getType().equals(int.class) || field.getType().equals(long.class)) {
373
if (Boolean.class.isAssignableFrom(field.getType()) || field.getType().equals(boolean.class)) {
376
if (Date.class.isAssignableFrom(field.getType())) {
379
if (byte[].class.isAssignableFrom(field.getType())) {
382
if (FileAttachment.class.isAssignableFrom(field.getType())) {
385
if (JPASupport.class.isAssignableFrom(field.getType())) {
386
if (field.isAnnotationPresent(OneToOne.class)) {
387
if (field.getAnnotation(OneToOne.class).mappedBy().equals("")) {
389
relation = field.getType().getName();
392
if (field.isAnnotationPresent(ManyToOne.class)) {
394
relation = field.getType().getName();
397
if (Collection.class.isAssignableFrom(field.getType())) {
398
Class fieldType = (Class) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0];
399
if (field.isAnnotationPresent(OneToMany.class)) {
400
if (field.getAnnotation(OneToMany.class).mappedBy().equals("")) {
402
relation = fieldType.getName();
406
if (field.isAnnotationPresent(ManyToMany.class)) {
407
if (field.getAnnotation(ManyToMany.class).mappedBy().equals("")) {
409
relation = fieldType.getName();
414
if (Collection.class.isAssignableFrom(field.getType())) {
415
if (!(field.isAnnotationPresent(OneToMany.class) ||
416
(field.isAnnotationPresent(ManyToMany.class)))) {
417
type = "serializedText";
420
if (field.getType().isEnum()) {
422
relation = field.getType().getSimpleName();
423
choices = field.getType().getEnumConstants();
425
if (field.isAnnotationPresent(Id.class)) {
428
if (field.isAnnotationPresent(Transient.class)) {
431
if (field.isAnnotationPresent(Required.class)) {
434
name = field.getName();
437
public Object[] getChoices() {
443
public static String collectionSerializer(Collection<?> coll) {
444
StringBuffer sb = new StringBuffer();
445
for (Object obj : coll) {
446
sb.append("\"" + obj.toString() + "\",");
448
if (sb.length() > 2) {
449
return sb.substring(0, sb.length() - 1);
455
public static String arraySerializer(Object[] coll) {
456
return collectionSerializer(Arrays.asList(coll));
459
public static Collection<?> collectionDeserializer(String target, Class<?> type) {
460
String[] targets = target.trim().split(",");
462
Logger.info("type [" + type + "]");
463
if (List.class.isAssignableFrom(type)) {
464
results = new ArrayList();
466
results = new TreeSet();
468
for (String targ : targets) {
469
if (targ.length() > 1) {
470
targ = targ.substring(1, targ.length() - 1);
473
Object[] constants = type.getEnumConstants();
474
for (Object c : constants) {
475
if (c.toString().equals(targ)) {
479
} else if (CharSequence.class.isAssignableFrom(type)) {
481
} else if (Integer.class.isAssignableFrom(type)) {
482
results.add(Integer.valueOf(targ));
483
} else if (Float.class.isAssignableFrom(type)) {
484
results.add(Float.valueOf(targ));
485
} else if (Boolean.class.isAssignableFrom(type)) {
486
results.add(Boolean.valueOf(targ));
487
} else if (Double.class.isAssignableFrom(type)) {
488
results.add(Double.valueOf(targ));
489
} else if (Long.class.isAssignableFrom(type)) {
490
results.add(Long.valueOf(targ));
491
} else if (Byte.class.isAssignableFrom(type)) {
492
results.add(Byte.valueOf(targ));