package nl.amis.jsf.model;
import java.lang.reflect.Field;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.ResourceBundle;
import java.util.Set;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.DateTimeConverter;
import javax.faces.convert.NumberConverter;
import javax.faces.model.SelectItem;
import oracle.adf.view.rich.model.AttributeDescriptor;
import oracle.adf.view.rich.model.ColumnDescriptor;
import static nl.amis.jsf.model.EntityLovModelUtil.*;
public class EntityAttributeDescriptor<E> extends ColumnDescriptor {
private Class entityClass;
private Field field;
private AttributeDescriptor.ComponentType componentType;
private List<SelectItem> selectItems;
private Object[] defaultValues;
private String valueExpression;
private boolean required = false;
private boolean readOnly = false;
private boolean removable = false;
private int columnWidth = 0;
class EntityOperator extends AttributeDescriptor.Operator {
protected EntityOperatorType type;
private Object value;
public EntityOperator(EntityOperatorType type) {
this.type = type;
}
public String getLabel() {
String label = getResourceBundle().getString(type.getLabel());
if (null == label || "".equals(label)) {
return type.getLabel();
} else {
return label;
}
}
public Object getValue() {
if (null == value) {
value = type.getValue();
}
return value;
}
public void setValue(Object value) {
this.value = value;
}
public int getOperandCount() {
return type.getOperandCount();
}
public EntityOperatorType getType() {
return type;
}
public String formatJpql(String attrName, List<Object> values) {
Object[] params = new Object[values.size() + 1];
params[0] = attrName;
if (type.getOperandCount() == 1) {
if (values.size() > 1) {
params[1] = createCommaSeparatedList(values);
} else {
if (values.get(0) instanceof List) {
params[1] = createCommaSeparatedList((List)values.get(0));
} else {
params[1] = values.get(0);
}
}
} else {
for (int i = 0; i < values.size(); i++) {
params[i+1] = values.get(i);
}
}
MessageFormat mf = new MessageFormat(type.getJpql());
String result = mf.format(params);
return result;
}
private String createCommaSeparatedList(List objects) {
StringBuilder array = new StringBuilder();
for (int i = 0; i < objects.size(); i++) {
Object object = objects.get(i);
if (object instanceof String) {
array.append("'");
}
array.append(object);
if (object instanceof String) {
array.append("'");
}
if (i < (objects.size() - 1)) {
array.append(",");
}
}
return array.toString();
}
@Override
public boolean equals(Object object) {
if (this == object) {
return true;
}
if (!(object instanceof EntityAttributeDescriptor.EntityOperator)) {
return false;
}
final EntityAttributeDescriptor.EntityOperator other = (EntityAttributeDescriptor.EntityOperator)object;
if (!(type == null ? other.type == null : type.equals(other.type))) {
return false;
}
return true;
}
@Override
public int hashCode() {
final int PRIME = 37;
int result = super.hashCode();
result = PRIME * result + ((type == null) ? 0 : type.hashCode());
return result;
}
}
// Operators for the Number dataType
private final AttributeDescriptor.Operator DEFAULT_NUMBER_OPERATOR = new EntityOperator(EntityOperatorType.EQUALS_NUMBER);
private final Set<AttributeDescriptor.Operator> NUMBER_OPERATORS = new LinkedHashSet<AttributeDescriptor.Operator>();
{
NUMBER_OPERATORS.add(new EntityOperator(EntityOperatorType.EQUALS_NUMBER));
NUMBER_OPERATORS.add(new EntityOperator(EntityOperatorType.NOT_EQUALS_NUMBER));
NUMBER_OPERATORS.add(new EntityOperator(EntityOperatorType.GREATER_THAN_NUMBER));
NUMBER_OPERATORS.add(new EntityOperator(EntityOperatorType.LESS_THAN_NUMBER));
NUMBER_OPERATORS.add(new EntityOperator(EntityOperatorType.GREATER_THAN_OR_EQUAL_NUMBER));
NUMBER_OPERATORS.add(new EntityOperator(EntityOperatorType.LESS_THAN_OR_EQUAL_NUMBER));
NUMBER_OPERATORS.add(new EntityOperator(EntityOperatorType.BETWEEN_NUMBER));
}
private final AttributeDescriptor.Operator DEFAULT_NUMBER_OPERATOR_READ_ONLY = new EntityOperator(EntityOperatorType.EQUALS_NUMBER);
private final Set<AttributeDescriptor.Operator> NUMBER_OPERATORS_READ_ONLY = new LinkedHashSet<AttributeDescriptor.Operator>();
{
NUMBER_OPERATORS_READ_ONLY.add(new EntityOperator(EntityOperatorType.EQUALS_NUMBER));
}
// Operators for the Date dataType
private final AttributeDescriptor.Operator DEFAULT_DATE_OPERATOR = new EntityOperator(EntityOperatorType.BETWEEN_DATE);
private final Set<AttributeDescriptor.Operator> DATE_OPERATORS = new LinkedHashSet<AttributeDescriptor.Operator>();
{
DATE_OPERATORS.add(new EntityOperator(EntityOperatorType.EQUALS_DATE));
DATE_OPERATORS.add(new EntityOperator(EntityOperatorType.NOT_EQUALS_DATE));
DATE_OPERATORS.add(new EntityOperator(EntityOperatorType.BETWEEN_DATE));
DATE_OPERATORS.add(new EntityOperator(EntityOperatorType.GREATER_THAN_DATE));
DATE_OPERATORS.add(new EntityOperator(EntityOperatorType.GREATER_THAN_OR_EQUAL_DATE));
DATE_OPERATORS.add(new EntityOperator(EntityOperatorType.LESS_THAN_DATE));
DATE_OPERATORS.add(new EntityOperator(EntityOperatorType.LESS_THAN_OR_EQUAL_DATE));
}
private final AttributeDescriptor.Operator DEFAULT_DATE_OPERATOR_READ_ONLY = new EntityOperator(EntityOperatorType.EQUALS_DATE);
private final Set<AttributeDescriptor.Operator> DATE_OPERATORS_READ_ONLY = new LinkedHashSet<AttributeDescriptor.Operator>();
{
DATE_OPERATORS_READ_ONLY.add(new EntityOperator(EntityOperatorType.EQUALS_DATE));
}
// Operators for the String dataType
private final AttributeDescriptor.Operator DEFAULT_STRING_OPERATOR = new EntityOperator(EntityOperatorType.CONTAINS);
private final Set<AttributeDescriptor.Operator> STRING_OPERATORS = new LinkedHashSet<AttributeDescriptor.Operator>();
{
STRING_OPERATORS.add(new EntityOperator(EntityOperatorType.EQUALS_STRING));
STRING_OPERATORS.add(new EntityOperator(EntityOperatorType.NOT_EQUALS_STRING));
STRING_OPERATORS.add(new EntityOperator(EntityOperatorType.STARTS_WITH));
STRING_OPERATORS.add(new EntityOperator(EntityOperatorType.ENDS_WITH));
STRING_OPERATORS.add(new EntityOperator(EntityOperatorType.CONTAINS));
STRING_OPERATORS.add(new EntityOperator(EntityOperatorType.NOT_CONTAINS));
}
private final AttributeDescriptor.Operator DEFAULT_STRING_OPERATOR_READ_ONLY = new EntityOperator(EntityOperatorType.EQUALS_STRING);
private final Set<AttributeDescriptor.Operator> STRING_OPERATORS_READ_ONLY = new LinkedHashSet<AttributeDescriptor.Operator>();
{
STRING_OPERATORS_READ_ONLY.add(new EntityOperator(EntityOperatorType.EQUALS_STRING));
}
// Operators for the String dataType
private final AttributeDescriptor.Operator DEFAULT_SELECT_ONE_OPERATOR = new EntityOperator(EntityOperatorType.EQUALS_STRING);
private final Set<AttributeDescriptor.Operator> SELECT_ONE_OPERATORS = new LinkedHashSet<AttributeDescriptor.Operator>();
{
SELECT_ONE_OPERATORS.add(new EntityOperator(EntityOperatorType.EQUALS_NUMBER));
SELECT_ONE_OPERATORS.add(new EntityOperator(EntityOperatorType.NOT_EQUALS_NUMBER));
// SELECT_ONE_OPERATORS.add(new EntityOperator(EntityOperatorType.EQUALS_STRING));
// SELECT_ONE_OPERATORS.add(new EntityOperator(EntityOperatorType.NOT_EQUALS_STRING));
}
// Operators for the String dataType
private final AttributeDescriptor.Operator DEFAULT_SELECT_MANY_OPERATOR = new EntityOperator(EntityOperatorType.IN);
private final Set<AttributeDescriptor.Operator> SELECT_MANY_OPERATORS = new LinkedHashSet<AttributeDescriptor.Operator>();
{
SELECT_MANY_OPERATORS.add(new EntityOperator(EntityOperatorType.IN));
SELECT_MANY_OPERATORS.add(new EntityOperator(EntityOperatorType.NOT_IN));
}
// TODO: hebben we deze eigenlijk wel nodig?
// Operators for the Boolean dataType
private final AttributeDescriptor.Operator DEFAULT_BOOLEAN_OPERATOR = new EntityOperator(EntityOperatorType.EQUALS_NUMBER);
private final Set<AttributeDescriptor.Operator> BOOLEAN_OPERATORS = new LinkedHashSet<AttributeDescriptor.Operator>();
{
BOOLEAN_OPERATORS.add(new EntityOperator(EntityOperatorType.EQUALS_NUMBER));
BOOLEAN_OPERATORS.add(new EntityOperator(EntityOperatorType.NOT_EQUALS_NUMBER));
}
// Operators for the other dataTypes
private final AttributeDescriptor.Operator DEFAULT_NO_OPERATOR = new EntityOperator(EntityOperatorType.NO_OPERATOR);
private final Set<AttributeDescriptor.Operator> NO_OPERATORS = new LinkedHashSet<AttributeDescriptor.Operator>();
{
NO_OPERATORS.add(new EntityOperator(EntityOperatorType.NO_OPERATOR));
}
public EntityAttributeDescriptor(Class entityClass, Field field) {
super();
this.entityClass = entityClass;
this.field = field;
}
public EntityAttributeDescriptor(Class entityClass, String name) throws NoSuchFieldException {
this(entityClass, entityClass.getDeclaredField(name));
}
public static EntityAttributeDescriptor getInstance(Class entityClass, String name) throws NoSuchFieldException {
EntityAttributeDescriptor result = new EntityAttributeDescriptor(entityClass, name);
return result;
}
public AttributeDescriptor.ComponentType getComponentType() {
if (readOnly) {
return AttributeDescriptor.ComponentType.selectOneChoice;
}
if (null != componentType) {
return componentType;
}
if (EntityLovModelUtil.isDate(getType())) {
return AttributeDescriptor.ComponentType.inputDate;
}
return AttributeDescriptor.ComponentType.inputText;
}
public void setComponentType(AttributeDescriptor.ComponentType componentType) {
this.componentType = componentType;
}
@Override
public Converter getConverter() {
if (null != selectItems) {
return new Converter() {
public Object getAsObject(FacesContext facesContext, UIComponent uiComponent, String string) {
if (null != selectItems) {
for (SelectItem item : selectItems) {
if (item.getLabel().equals(string)) {
return item.getValue();
}
}
}
return string;
}
public String getAsString(FacesContext facesContext, UIComponent uiComponent, Object object) {
if (null != selectItems) {
for (SelectItem item : selectItems) {
if (item.getValue().equals(object)) {
return item.getLabel();
}
}
}
return object.toString();
}
};
} else if (isNumericType()){
return new NumberConverter();
} else if (isDateType()) {
return new DateTimeConverter();
} else {
return new Converter() {
public Object getAsObject(FacesContext facesContext, UIComponent uiComponent, String string) {
return string;
}
public String getAsString(FacesContext facesContext, UIComponent uiComponent, Object object) {
return object.toString();
}
};
}
}
@Override
public boolean hasDefaultConverter() {
return !( getComponentType() == AttributeDescriptor.ComponentType.selectManyChoice
|| getComponentType() == AttributeDescriptor.ComponentType.selectOneChoice
|| getComponentType() == AttributeDescriptor.ComponentType.selectOneListbox
|| getComponentType() == AttributeDescriptor.ComponentType.selectOneRadio);
}
public String getDescription() {
String label = getResourceBundle().getString(getResourceKey("_TOOLTIP"));
if (null == label || "".equals(label)) {
return field.getName();
} else {
return label;
}
}
public String getFormat() {
return null;
}
public String getLabel() {
String label = getResourceBundle().getString(getResourceKey("_LABEL"));
if (null == label || "".equals(label)) {
return field.getName();
} else {
return label;
}
}
private ResourceBundle getResourceBundle() {
FacesContext context = FacesContext.getCurrentInstance();
return context.getApplication().getResourceBundle(context, "resourceBundle");
}
private String getResourceKey(String append) {
StringBuilder key = new StringBuilder();
key.append(field.getDeclaringClass().getSimpleName());
key.append(".");
key.append(field.getName());
key.append(append);
return key.toString();
}
public void setColumnWidth(int columnWidth) {
this.columnWidth = columnWidth;
}
public int getColumnWidth() {
return columnWidth;
}
public int getWidth() {
return getColumnWidth();
}
public String getAlign() {
if (isNumericType() || isDateType()) {
return "right";
} else {
return "left";
}
}
public int getLength() {
// TODO: @Length annotatie opzoeken?
return 0;
}
public int getMaximumLength() {
// TODO: @Length annotatie opzoeken?
return 0;
}
Object[] getDefaultValues() {
return defaultValues;
}
void setDefaultValues(Object ... defaultValues) {
this.defaultValues = defaultValues;
}
String getValueExpression() {
return valueExpression;
}
void setValueExpression(String expression) {
this.valueExpression = expression;
}
/**
* Returns the model object that represents the data for the component.
*
* For inputListOfValues/inputComboboxListOfValues, it's a ListOfValuesModel
* For inputNumberSpinbox, it's a NumberRange object
* For selectXyz components, it must be List<SelectItem>
* Undefined for other types
*/
public Object getModel() {
if (readOnly) {
return selectItems;
}
if ( getComponentType().equals(AttributeDescriptor.ComponentType.selectOneListbox) ||
getComponentType().equals(AttributeDescriptor.ComponentType.selectManyChoice) ||
getComponentType().equals(AttributeDescriptor.ComponentType.selectOneChoice) ||
getComponentType().equals(AttributeDescriptor.ComponentType.selectOneRadio)) {
return selectItems;
} else {
return null;
}
}
public void setModel(List<SelectItem> selectItems) {
this.selectItems = selectItems;
}
public String getName() {
return field.getName();
}
public AttributeDescriptor.Operator getOperatorByType(EntityOperatorType type) {
System.out.println("base: "+type.toString());
Set<AttributeDescriptor.Operator> operators = getSupportedOperators();
for (AttributeDescriptor.Operator operator : operators) {
System.out.println("compare: "+((EntityOperator)operator).getType().toString());
if (type.equals(((EntityOperator)operator).getType())) {
return operator;
}
}
throw new IllegalArgumentException("Operator " + type + " not valid for attribute of type " + getType());
}
public Set<AttributeDescriptor.Operator> getSupportedOperators() {
if (!this.readOnly && getComponentType().equals(AttributeDescriptor.ComponentType.selectManyChoice)) {
return SELECT_MANY_OPERATORS;
} else if ( !this.readOnly
&& ( getComponentType().equals(AttributeDescriptor.ComponentType.selectOneListbox)
|| getComponentType().equals(AttributeDescriptor.ComponentType.selectOneChoice)
|| getComponentType().equals(AttributeDescriptor.ComponentType.selectOneRadio))) {
return SELECT_ONE_OPERATORS;
} else if (getType().equals(Boolean.class)) {
return BOOLEAN_OPERATORS;
} else if (EntityLovModelUtil.isNumber(getType())) {
return this.readOnly ? NUMBER_OPERATORS_READ_ONLY : NUMBER_OPERATORS;
} else if (getType().equals(String.class)) {
return this.readOnly ? STRING_OPERATORS_READ_ONLY : STRING_OPERATORS;
} else if (EntityLovModelUtil.isDate(getType())) {
return this.readOnly ? DATE_OPERATORS_READ_ONLY : DATE_OPERATORS;
}
return NO_OPERATORS;
}
AttributeDescriptor.Operator getDefaultOperator() {
if (!this.readOnly && getComponentType().equals(AttributeDescriptor.ComponentType.selectManyChoice)) {
return DEFAULT_SELECT_MANY_OPERATOR;
} else if ( !this.readOnly
&& ( getComponentType().equals(AttributeDescriptor.ComponentType.selectOneListbox)
|| getComponentType().equals(AttributeDescriptor.ComponentType.selectOneChoice)
|| getComponentType().equals(AttributeDescriptor.ComponentType.selectOneRadio))) {
return DEFAULT_SELECT_ONE_OPERATOR;
} else if (getType().equals(Boolean.class)) {
return DEFAULT_BOOLEAN_OPERATOR;
} else if (EntityLovModelUtil.isNumber(getType())) {
return this.readOnly ? DEFAULT_NUMBER_OPERATOR_READ_ONLY : DEFAULT_NUMBER_OPERATOR;
} else if (getType().equals(String.class)) {
return this.readOnly ? DEFAULT_STRING_OPERATOR_READ_ONLY : DEFAULT_STRING_OPERATOR;
} else if (EntityLovModelUtil.isDate(getType())) {
return this.readOnly ? DEFAULT_DATE_OPERATOR_READ_ONLY : DEFAULT_DATE_OPERATOR;
}
return DEFAULT_NO_OPERATOR;
}
public boolean isNumericType() {
return EntityLovModelUtil.isNumber(getType());
}
public boolean isDateType() {
return EntityLovModelUtil.isDate(getType());
}
public Class getType() {
return field.getType();
}
// TODO: deze methode wordt blijkbaar niet aangeroepen, daardoor blijft
// het veld editable, ook al wordt readOnly op true gezet. Uitzoeken
// of er een work around is.
public boolean isReadOnly() {
return readOnly;
}
public void setReadOnly(boolean readOnly, Object ... values) {
if (readOnly) {
if (null == values) {
throw new IllegalArgumentException("Value cannot be null if setting attribute to read only.");
}
selectItems = new ArrayList<SelectItem>();
for (Object value : values) {
selectItems.add(new SelectItem(value, "" + value));
}
}
this.readOnly = readOnly;
}
public boolean isRequired() {
return required || readOnly;
}
public void setRequired(boolean required) {
this.required = required;
}
public void setRemovable(boolean removable) {
this.removable = removable;
}
public boolean isRemovable() {
return removable;
}
@Override
public boolean equals(Object object) {
if (this == object) {
return true;
}
if (!(object instanceof EntityAttributeDescriptor)) {
return false;
}
final EntityAttributeDescriptor other = (EntityAttributeDescriptor)object;
if (!(entityClass == null ? other.entityClass == null : entityClass.getName().equals(other.entityClass.getName()))) {
return false;
}
if (!(field == null ? other.field == null : field.equals(other.field))) {
return false;
}
return true;
}
@Override
public int hashCode() {
final int PRIME = 37;
int result = super.hashCode();
result = PRIME * result + ((entityClass == null) ? 0 : entityClass.getName().hashCode());
result = PRIME * result + ((field == null) ? 0 : field.hashCode());
return result;
}
}