package com.venky.swf.views.controls.model;
import java.io.InputStream;
import java.io.Reader;
import java.lang.reflect.Method;
import java.sql.Date;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.StringTokenizer;
import com.venky.core.collections.IgnoreCaseList;
import com.venky.core.collections.UpperCaseStringCache;
import com.venky.core.string.StringUtil;
import com.venky.core.util.ObjectUtil;
import com.venky.swf.controller.Controller;
import com.venky.swf.controller.annotations.SingleRecordAction;
import com.venky.swf.controller.reflection.ControllerReflector;
import com.venky.swf.db.Database;
import com.venky.swf.db.JdbcTypeHelper.TypeConverter;
import com.venky.swf.db.annotations.column.ui.OnLookupSelect;
import com.venky.swf.db.annotations.column.ui.PROTECTION.Kind;
import com.venky.swf.db.annotations.column.ui.TOOLTIP;
import com.venky.swf.db.annotations.column.ui.WATERMARK;
import com.venky.swf.db.annotations.column.validations.Enumeration;
import com.venky.swf.db.model.Model;
import com.venky.swf.db.model.reflection.ModelReflector;
import com.venky.swf.path.Path;
import com.venky.swf.views.controls.Control;
import com.venky.swf.views.controls.page.Link;
import com.venky.swf.views.controls.page.layout.Div;
import com.venky.swf.views.controls.page.layout.Glyphicon;
import com.venky.swf.views.controls.page.text.AutoCompleteText;
import com.venky.swf.views.controls.page.text.CheckBox;
import com.venky.swf.views.controls.page.text.DateBox;
import com.venky.swf.views.controls.page.text.FileTextBox;
import com.venky.swf.views.controls.page.text.OptionCreator;
import com.venky.swf.views.controls.page.text.PasswordText;
import com.venky.swf.views.controls.page.text.RadioGroup;
import com.venky.swf.views.controls.page.text.Select;
import com.venky.swf.views.controls.page.text.TextArea;
import com.venky.swf.views.controls.page.text.TextBox;
import com.venky.swf.views.model.FieldUIMetaProvider;
public class ModelAwareness implements FieldUIMetaProvider{
public ModelAwareness(Path path, String[] includeFields) {
this.path = path;
this.orderby = new OrderBy();
this.includedFields.addAll(getReflector().getFields());
if (includeFields != null && includeFields.length > 0){
this.includedFields.retainAll(Arrays.asList(includeFields));
}
}
private List<String> includedFields = new IgnoreCaseList(false);
public List<String> getIncludedFields(){
return includedFields;
}
private Path path = null;
public Path getPath(){
return path;
}
@SuppressWarnings("unchecked")
public <M extends Model> ModelReflector<M> getReflector(){
return (ModelReflector<M>) ModelReflector.instance(getPath().getModelClass());
}
/* Model Awareness */
public String getFieldLiteral(String fieldName){
String fieldLiteral = getLiteral(StringUtil.camelize(fieldName));
Method parentModelgetter = getReflector().getReferredModelGetterFor(getReflector().getFieldGetter(fieldName));
if (parentModelgetter != null) {
fieldLiteral = getLiteral(parentModelgetter.getName().substring("get".length())) ;
}
return fieldLiteral;
}
public String getLiteral(String camel){
List<Integer> upper = new ArrayList<Integer>();
byte[] bytes = camel.getBytes();
for (int i = 0; i < bytes.length; i++) {
byte b = bytes[i];
if (b < 97 || b > 122) {
upper.add(i);
}
}
StringBuilder b = new StringBuilder(camel);
for (int i = upper.size() - 1; i >= 0; i--) {
Integer index = upper.get(i);
if (index != 0)
b.insert(index, " ");
}
return b.toString();
}
@SuppressWarnings({ "rawtypes", "unchecked" })
public <M extends Model> Control getInputControl(String controlName, String fieldName, M record, FieldUIMetaProvider metaprovider) {
if (metaprovider == null){
metaprovider = this;
}
ModelReflector<M> reflector = getReflector();
Method getter = reflector.getFieldGetter(fieldName);
Object value = null;
try {
value = getter.invoke(record);
} catch (Exception e) {
throw new RuntimeException(e);
}
Class<?> returnType = getter.getReturnType();
TypeConverter<?> converter = Database.getJdbcTypeHelper(reflector.getPool()).getTypeRef(returnType).getTypeConverter();
Control control = null;
if (!reflector.isFieldSettable(fieldName) && (String.class.isAssignableFrom(returnType) || Reader.class.isAssignableFrom(returnType))){
control = new Div();
control.setText(converter.toString(value));
}else if (boolean.class.isAssignableFrom(returnType) || Boolean.class.isAssignableFrom(returnType)) {
CheckBox cb = new CheckBox();
cb.setChecked(converter.toString(value));
control = cb;
}else if (reflector.isFieldValueALongText(fieldName,value)){
TextArea txtArea = new TextArea();
txtArea.setText(converter.toString(value));
if (ObjectUtil.isVoid(value)){
int maxRows = Math.min(10,reflector.getMaxDataLength(fieldName)/txtArea.getCols());
txtArea.setRows(maxRows);
}
control = txtArea;
}else if (InputStream.class.isAssignableFrom(returnType)){
FileTextBox ftb = new FileTextBox();
String contentType = reflector.getContentType(record, fieldName);
if (!ObjectUtil.isVoid(contentType)){
ftb.setContentType(contentType);
}
if (reflector.getContentSize(record, fieldName) != 0 && !record.getRawRecord().isNewRecord()){
ftb.setStreamUrl(path.controllerPath()+"/view/"+record.getId(),reflector.getContentName(record, fieldName));
}
control = ftb;
}else {
Method parentModelGetter = reflector.getReferredModelGetterFor(getter);
if (parentModelGetter != null){
control = new AutoCompleteText(reflector.getReferredModelClass(parentModelGetter),path.controllerPath()+"/autocomplete");
if (reflector.isAnnotationPresent(getter,OnLookupSelect.class)){
((AutoCompleteText)control).setOnAutoCompleteSelectProcessingUrl(path.controllerPath()+"/onAutoCompleteSelect");
}
}else if (Date.class.isAssignableFrom(returnType)){
control = new DateBox();
}else if (reflector.isFieldPassword(fieldName)){
control = new PasswordText();
}else if (reflector.isFieldEnumeration(fieldName)){
//Select select = new Select();
Enumeration enumeration = reflector.getAnnotation(reflector.getFieldGetter(fieldName),Enumeration.class) ;
OptionCreator select = null;
if (enumeration.showAs() == null || enumeration.showAs().equals(Select.class.getSimpleName())){
select = new Select();
}else {
select = new RadioGroup();
}
StringTokenizer allowedValues = new StringTokenizer(enumeration.value(),",");
while (allowedValues.hasMoreTokens()){
String nextAllowedValue = allowedValues.nextToken();
select.createOption(nextAllowedValue, nextAllowedValue);
}
control = select;
}else{
control = new TextBox();
}
if (!ObjectUtil.isVoid(value)){
control.setValue(converter.toString(value));
}
}
control.setName(controlName);
Kind protectionKind = metaprovider.getFieldProtection(fieldName);
switch(protectionKind){
case DISABLED:
control.setEnabled(false);
break;
case NON_EDITABLE:
control.setReadOnly(true);
break;
default:
if (getReflector().isFieldSettable(fieldName)) {
control.setReadOnly(false);
}else {
control.setReadOnly(true);
}
break;
}
if (!metaprovider.isFieldVisible(fieldName)){
control.setVisible(false);
}
if (control.isEnabled() && !control.isReadOnly()){
WATERMARK watermark = getReflector().getAnnotation(getter, WATERMARK.class);
if (watermark != null){
control.setWaterMark(watermark.value());
}
TOOLTIP tooltip = getReflector().getAnnotation(getter, TOOLTIP.class);
if (tooltip != null){
control.setToolTip(tooltip.value());
}
}else {
control.setWaterMark(null);
control.setToolTip(null);
}
if (control.isEnabled() && getReflector().isFieldSettable(fieldName)){
if (hashFieldValue.length() > 0){
hashFieldValue.append(",");
}
String fieldValue = control.getUnescapedValue();
if (control instanceof CheckBox){
fieldValue = StringUtil.valueOf(((CheckBox)control).isChecked());
}else if (control instanceof AutoCompleteText){
AutoCompleteText ac = (AutoCompleteText)control;
Control hiddenIdControl = ac.getHiddenIdControl();
hashFieldValue.append(hiddenIdControl.getName());
hashFieldValue.append("=");
hashFieldValue.append(StringUtil.valueOf(hiddenIdControl.getValue()));
hashFieldValue.append(",");
}
hashFieldValue.append(control.getName());
hashFieldValue.append("=");
hashFieldValue.append(StringUtil.valueOf(fieldValue));
}
return control;
}
private StringBuilder hashFieldValue = new StringBuilder();
public StringBuilder getHashFieldValue(){
return hashFieldValue;
}
public String getParentDescription(Method parentIdGetter, Model record){
Method parentModelGetter = getReflector().getReferredModelGetterFor(parentIdGetter);
if (parentModelGetter != null){
try {
Model parentModel = (Model)parentModelGetter.invoke(record);
if (parentModel != null){
@SuppressWarnings("unchecked")
Class<? extends Model> parentModelClass = (Class<? extends Model>)parentModelGetter.getReturnType();
ModelReflector<? extends Model> parentModelReflector = ModelReflector.instance(parentModelClass);
String descriptionColumn = parentModelReflector.getDescriptionField();
Method descGetter = parentModelReflector.getFieldGetter(descriptionColumn);
Object descValue = descGetter.invoke(parentModel);
return Database.getJdbcTypeHelper(parentModelReflector.getPool()).getTypeRef(descGetter.getReturnType()).getTypeConverter().toString(descValue);
}
}catch( Exception ex){
throw new RuntimeException(ex);
}
}
return null;
}
public List<Method> getSingleRecordActions(){
return getPath().getControllerReflector().getSingleRecordActionMethods();
}
public <M extends Model> Link createSingleRecordActionLink(Method m, M record){
String actionName = m.getName();
SingleRecordAction sra = getPath().getControllerReflector().getAnnotation(m,SingleRecordAction.class);
if (sra == null){
return null;
}
boolean canAccessAction = record.getId() > 0 && getPath().canAccessControllerAction(actionName,String.valueOf(record.getId()));
if (!canAccessAction){
return null;
}
Link actionLink = new Link();
String icon = "glyphicon-asterisk" ;
String tooltip = StringUtil.camelize(actionName);
if (sra != null) {
if (!ObjectUtil.isVoid(sra.icon())){
icon = sra.icon();
}
if (!ObjectUtil.isVoid(sra.tooltip())){
tooltip = sra.tooltip();
}
}
StringBuilder sAction = new StringBuilder();
if ("search".equals(getPath().action())){
sAction.append(getPath().controllerPath()).append("/").append(getPath().action()).append("/").append(StringUtil.valueOf(getPath().getFormFields().get("q")));
}else {
sAction.append(getPath().controllerPath()).append("/").append(getPath().action());
if (!ObjectUtil.isVoid(getPath().parameter())){
sAction.append("/").append(getPath().parameter());
}
}
sAction.append("/").append(getPath().controllerPathElement());
sAction.append("/").append(actionName).append("/").append(record.getId());
actionLink.setUrl(sAction.toString());
actionLink.addControl(new Glyphicon(icon,tooltip));
//actionLink.addClass("btn");
return actionLink;
}
public ControllerReflector<? extends Controller> getControllerReflector() {
return ControllerReflector.instance(getPath().getControllerClass());
}
private OrderBy orderby = null ;
public class OrderBy {
int sortDirection(){
if (StringUtil.equals(ascOrDesc, "ASC")){
return 0;
}else {
return 1;
}
}
String field = null;
String ascOrDesc = "ASC";
public OrderBy(){
String orderBy = new StringTokenizer(getReflector().getOrderBy(), ",").nextToken();
StringTokenizer tok = new StringTokenizer(orderBy);
this.field = tok.nextToken();
if (tok.hasMoreTokens()){
this.ascOrDesc = UpperCaseStringCache.instance().get(tok.nextToken());
}
}
}
public OrderBy getOrderBy(){
return orderby;
}
@Override
public boolean isFieldVisible(String fieldName) {
return getReflector().isFieldVisible(fieldName);
}
@Override
public boolean isFieldEditable(String fieldName) {
return getReflector().isFieldEditable(fieldName);
}
@Override
public Kind getFieldProtection(String fieldName) {
return getReflector().getFieldProtection(fieldName);
}
}