Skip to content

Commit

Permalink
[playframework#265] Adding the possibility to define an own ObjectType
Browse files Browse the repository at this point in the history
by Annotation.
  • Loading branch information
opensource21 authored and guillaumebort committed Dec 7, 2010
1 parent c156923 commit 377ea40
Show file tree
Hide file tree
Showing 16 changed files with 618 additions and 2 deletions.
56 changes: 56 additions & 0 deletions documentation/manual/crud.textile
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,62 @@ public class Account extends Model {

This is shown as: "myEnumId1","myEnumId2" for the contentTypes and "string1","string2" for the user names. Per definition, this is what you should first customize in your CRUD module.

h2. <a>Customize the show and the blank view in a generic way<a>

A central influence on the behavior of the CRUD-views has the "**ObjectType**". So if you want to
take influence on the behavior of CRUD in a generic way (for example make fields with annotation @Version hidden), you can create you own ObjectType-class.
You must furthermore declare a static method in your controller or a super-class of your controller.

bc. protected static ObjectType createObjectType(Class<? extends Model> entityClass) {
return new VersionObjectType(entityClass);
}

Here a complete example:

bc. public class CustomAdminCompany extends CRUD {
protected static ObjectType createObjectType(Class<? extends Model> entityClass) {
return new VersionObjectType(entityClass);
}

public static class VersionObjectType extends ObjectType {

private final String versionColumn;

public VersionObjectType(Class<? extends Model> modelClass) {
super(modelClass);
versionColumn = getVersionColumnName(modelClass);
}
private String getVersionColumnName(Class modelClass) {
Class c = modelClass;
try {
while (!c.equals(Object.class)) {
for (Field field : c.getDeclaredFields()) {
if (field.isAnnotationPresent(Version.class)) {
return field.getName();
}
}
c = c.getSuperclass();
}
} catch (Exception e) {
throw new UnexpectedException("Error while determining the object @Version for an object of type " + modelClass);
}
return null;
}
@Override
public List<ObjectField> getFields() {
List<ObjectField> result = super.getFields();
for (ObjectField objectField : result) {
if (objectField.name.equals(versionColumn)) {
objectField.type = "hidden";
}
}
return result;
}
}
}

But this isn't the end. You can customize findPage and other. Have simple a look at the source code.

h2. <a>Limitation</a>

The CRUD module will show the relationships that are unidirectional in one entity: the one that does not have the **mappedBy** attribute.
2 changes: 2 additions & 0 deletions framework/src/play/utils/Java.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
Expand Down Expand Up @@ -84,6 +85,7 @@ public static Method findActionMethod(String name, Class clazz) {
return null;
}


/**
* Invoke a static method
* @param clazz The class
Expand Down
15 changes: 13 additions & 2 deletions modules/crud/app/controllers/CRUD.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import play.*;
import play.data.binding.*;
import play.mvc.*;
import play.utils.Java;
import play.db.Model;
import play.data.validation.*;
import play.exceptions.*;
Expand Down Expand Up @@ -158,14 +159,18 @@ public static void delete(String id) {
flash.success(Messages.get("crud.deleted", type.modelName));
redirect(request.controller + ".list");
}

protected static ObjectType createObjectType(Class<? extends Model> entityClass) {
return new ObjectType(entityClass);
}

// ~~~~~~~~~~~~~
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface For {
Class<? extends Model> value();
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Exclude {}
Expand Down Expand Up @@ -208,7 +213,13 @@ public static ObjectType get(Class<? extends Controller> controllerClass) {
if (entityClass == null || !Model.class.isAssignableFrom(entityClass)) {
return null;
}
ObjectType type = new ObjectType(entityClass);
ObjectType type;
try {
type = (ObjectType) Java.invokeStaticOrParent(controllerClass, "createObjectType", entityClass);
} catch (Exception e) {
Logger.error(e, "Couldn't create an ObjectType. Use default one.");
type = new ObjectType(entityClass);
}
type.name = controllerClass.getSimpleName().replace("$", "");
type.controllerName = controllerClass.getSimpleName().toLowerCase().replace("$", "");
type.controllerClass = controllerClass;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package controllers;

import java.util.List;

import models.MyBook;
import play.mvc.Controller;

public class MyBookApplication extends Controller {

public static void index() {
List<MyBook> testEntries = MyBook.all().fetch();
render(testEntries);
}

public static void edit(Long id) {
MyBook testObj = MyBook.findById(id);
notFoundIfNull(testObj);
render(testObj);
}


public static void save(long id, MyBook testObj) {
if (!testObj.isPersistent()) {
notFound("The object Test with id "+ id + " wasn't found anymore!");
}
if (testObj.validateAndSave()) {
index();
} else {
render("Application/edit.html", testObj);
}
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package controllers;

import play.db.Model;
import controllers.CRUD.ObjectType;
import controllers.OptimisticLockingCRUD.CustomizableObjectType;
import models.MyBook;

@CRUD.For(MyBook.class)
public class MyBookCustomCrud extends OptimisticLockingCRUD {
public static CustomizableObjectType createObjectType(Class<? extends Model> entityClass) {
final CustomizableObjectType type = OptimisticLockingCRUD.createObjectType(entityClass);
type.defineBlankFields("excludedProperty", "text", "version");
type.defineShowFields("excludedProperty", "text", "version");
return type;
}
}
14 changes: 14 additions & 0 deletions samples-and-tests/just-test-cases/app/controllers/MyBooks.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package controllers;

import play.db.Model;
import controllers.CRUD.ObjectType;
import controllers.OptimisticLockingCRUD.CustomizableObjectType;
import models.MyBook;

public class MyBooks extends OptimisticLockingCRUD {
public static CustomizableObjectType createObjectType(Class<? extends Model> entityClass) {
final CustomizableObjectType type = OptimisticLockingCRUD.createObjectType(entityClass);
type.addExcludedFields("excludedProperty", "notFound");
return type;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
package controllers;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import play.Logger;
import play.db.Model;
import play.mvc.Http.Request;

public class OptimisticLockingCRUD extends CRUD {

protected static CustomizableObjectType createObjectType(Class<? extends Model> entityClass) {
return new CustomizableObjectType(entityClass);
}


public static class CustomizableObjectType extends ObjectType {

private Set<String> hiddenFieldNames = new HashSet<String>();
private Set<String> excludedFieldNames = new HashSet<String>();

private String[] showFieldNames = null;
private String[] blankFieldNames = null;

private Map<String, ObjectField> fieldMap = new HashMap<String, ObjectField>();


public CustomizableObjectType(Class<? extends Model> modelClass) {
super(modelClass);
hiddenFieldNames.add("version");
}

public CustomizableObjectType(String modelClass)
throws ClassNotFoundException {
super(modelClass);
}

public void addHiddenFields(String... hiddenFields) {
for (String hiddenField : hiddenFields) {
hiddenFieldNames.add(hiddenField);
}
}

public void addExcludedFields(String... excludedFields) {
for (String excludedField : excludedFields) {
excludedFieldNames.add(excludedField);
}
}

public void defineShowFields(String... showFields) {
showFieldNames = showFields;
}

public void defineBlankFields(String... blankFields) {
blankFieldNames = blankFields;
}

@Override
public List<ObjectField> getFields() {
String methodName = Request.current().actionMethod;
if ("blank".equals(methodName) && blankFieldNames != null) {
return getFields(blankFieldNames);
} else if ("show".equals(methodName) && showFieldNames != null) {
return getFields(showFieldNames);
} else {
return getFieldsCommon();
}
}



private List <ObjectField> getFields(String[] fieldnameList) {
if (fieldMap.isEmpty()) {
final List <ObjectField> fields = getFieldsCommon();
for (ObjectField objectField : fields) {
fieldMap.put(objectField.name, objectField);
}
}
final List<ObjectField> result = new ArrayList<ObjectField>(fieldnameList.length);
for (String fieldname : fieldnameList) {
if (fieldMap.containsKey(fieldname)) {
result.add(fieldMap.get(fieldname));
} else {
Logger.warn("Unknown field with name >%s<", fieldname);
}

}
return result;
}

private List<ObjectField> getFieldsCommon() {
final List <ObjectField> fields = super.getFields();
final Set<String> hiddenFieldsNotChanged = new HashSet<String>(hiddenFieldNames);
final Set<String> excludedFieldsNotChanged = new HashSet<String>(excludedFieldNames);
final List<ObjectField> hiddenFields = new ArrayList<ObjectField>();
final List<ObjectField> normalFields = new ArrayList<ObjectField>();
for (Iterator iterator = fields.iterator(); iterator.hasNext() &&
(!hiddenFieldsNotChanged.isEmpty() || !excludedFieldsNotChanged.isEmpty());) {
final ObjectField field = (ObjectField) iterator.next();
if (excludedFieldsNotChanged.remove(field.name)) {
Logger.debug("ignore " + field.name);
//Ignore this field.
} else if (hiddenFieldsNotChanged.remove(field.name)) {
Logger.debug("hidden " + field.name);
field.type = "hidden";
hiddenFields.add(field);
} else {
Logger.debug("normal " + field.name);
normalFields.add(field);
}

}
hiddenFields.addAll(normalFields);
if (!hiddenFieldsNotChanged.isEmpty()) {
final StringBuilder message = new StringBuilder(
"Not all hidden fields was found in model: ");
for (String hiddenFieldName : hiddenFieldsNotChanged) {
message.append(hiddenFieldName).append(", ");
}
Logger.warn(message.toString());
}
if (!excludedFieldsNotChanged.isEmpty()) {
final StringBuilder message = new StringBuilder(
"Not all excluded fields was found in model: ");
for (String excludedField : excludedFieldsNotChanged) {
message.append(excludedField).append(", ");
}
Logger.warn(message.toString());
}
return hiddenFields;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package controllers.admin;

import java.lang.reflect.Field;
import java.util.List;

import javax.persistence.Version;

import models.Company;
import play.db.Model;
import play.exceptions.UnexpectedException;
import controllers.CRUD;

@CRUD.For(Company.class)
public class CustomAdminCompany extends CRUD {
protected static ObjectType createObjectType(Class<? extends Model> entityClass) {
return new VersionObjectType(entityClass);
}

public static class VersionObjectType extends ObjectType {

private final String versionColumn;

public VersionObjectType(Class<? extends Model> modelClass) {
super(modelClass);
versionColumn = getVersionColumnName(modelClass);
}

private String getVersionColumnName(Class modelClass) {
Class c = modelClass;
try {
while (!c.equals(Object.class)) {
for (Field field : c.getDeclaredFields()) {
if (field.isAnnotationPresent(Version.class)) {
return field.getName();
}
}
c = c.getSuperclass();
}
} catch (Exception e) {
throw new UnexpectedException("Error while determining the object @Version for an object of type " + modelClass);
}
return null;
}

@Override
public List<ObjectField> getFields() {
List<ObjectField> result = super.getFields();
for (ObjectField objectField : result) {
if (objectField.name.equals(versionColumn)) {
objectField.type = "hidden";
}
}
return result;
}

}

}

Loading

0 comments on commit 377ea40

Please sign in to comment.