WebGsdbTools.java
package eu.javaexperience.gsdbrpc;
import eu.javaexperience.query.F;
import hu.ddsi.java.database.FieldData;
import hu.ddsi.java.database.GenericStorable;
import hu.ddsi.java.database.GenericStorage;
import hu.ddsi.java.database.GenericStorageMappingData;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.teavm.jso.JSObject;
import org.teavm.jso.dom.events.Event;
import org.teavm.jso.dom.events.EventListener;
import org.teavm.jso.dom.html.HTMLElement;
import org.teavm.jso.dom.html.HTMLInputElement;
import org.teavm.jso.json.JSON;
import eu.javaexperience.arrays.ArrayTools;
import eu.javaexperience.classes.ClassDescriptor;
import eu.javaexperience.classes.ClassDescriptorTools;
import eu.javaexperience.classes.ClassFieldDescriptor;
import eu.javaexperience.collection.CollectionTools;
import eu.javaexperience.collection.enumerations.EnumLike;
import eu.javaexperience.collection.enumerations.EnumManager;
import eu.javaexperience.collection.map.ConcurrentMapTools;
import eu.javaexperience.datareprez.DataArray;
import eu.javaexperience.datareprez.DataReprezTools;
import eu.javaexperience.gsdbrpc.api.ModelManager;
import eu.javaexperience.interfaces.simple.getBy.GetBy1;
import eu.javaexperience.interfaces.simple.getBy.GetBy2;
import eu.javaexperience.interfaces.simple.publish.SimplePublish1;
import eu.javaexperience.log.JavaExperienceLoggingFacility;
import eu.javaexperience.log.LogLevel;
import eu.javaexperience.log.Loggable;
import eu.javaexperience.log.Logger;
import eu.javaexperience.log.LoggingTools;
import eu.javaexperience.reflect.CastTo;
import eu.javaexperience.reflect.Mirror;
import eu.javaexperience.semantic.references.MayNull;
import eu.javaexperience.teavm.datareprez.DataArrayTeaVMImpl;
import eu.javaexperience.text.Format;
import eu.javaexperience.text.StringTools;
import eu.javaexperience.webgsdb.commons.FieldExtraAttributes;
import eu.javaexperience.webgsdb.commons.FrontendFieldEditContext;
import eu.javaexperience.webgsdb.commons.FrontendFieldManager;
import eu.javaexperience.webgsdb.commons.ModelData;
import eu.javaexperience.webgsdb.frontend.modellayer.WebModel;
import eu.jvx.js.lib.ImpTools;
import eu.jvx.js.lib.bindings.H;
import eu.jvx.js.lib.bindings.VanillaTools;
import eu.jvx.js.lib.ui.component.func.HtmlDataContainer;
import eu.jvx.js.lib.ui.component.func.HtmlDataContainerTools;
import eu.jvx.js.lib.ui.component.func.HtmlListContainerUserEditable;
import eu.jvx.js.lib.ui.component.input.SelectList;
import eu.jvx.js.lib.ui.component.input.SelectOption;
import eu.jvx.js.tbs.ui.TbsLayoutTools.SimpleFormRow;
public class WebGsdbTools
{
public static final Logger LOG = JavaExperienceLoggingFacility.getLogger(new Loggable("WebGsdbTools"));
public static <E extends EnumLike<E>> HTMLElement generateEnumChoose(EnumManager<E> es, @MayNull String noneLabel, String name, String value)
{
LoggingTools.tryLogFormat(LOG, LogLevel.DEBUG, "generateEnumChoose(enumManager: %s, noneLabel: %s, name: %s, value: %s)", es, noneLabel, name, value);
SelectList l = new SelectList();
if(null != noneLabel)
{
l.addOption(null, noneLabel);
}
for(Object s:es.getValues())
{
EnumLike e = (EnumLike) s;
String n = e.getName();
SelectOption so = l.addOption(n, n);
LoggingTools.tryLogFormat(LOG, LogLevel.DEBUG, "generateEnumChoose: add option (%s)", n);
if(null != value && value.equals(n))
{
LoggingTools.tryLogFormat(LOG, LogLevel.DEBUG, "generateEnumChoose: selecting (%s)", n);
so.selectThis();
}
}
if(null != name)
{
l.getHtml().setAttribute("name", name);
}
VanillaTools.stringAttrs((HTMLElement) l.getHtml(), "class", "form-control");
return (HTMLElement) l.getHtml();
}
public static <T extends WebDbModel> SimpleFormRow createModelFieldInputRow
(
String fieldLabel,
FrontendFieldEditContext<T> ctx,
@MayNull FrontendFieldManager<T> mngr
)
{
if(null == mngr)
{
mngr = DEFAULT_FRONTEND_FIELD_MANAGER;
}
return new SimpleFormRow(ctx.idLabelFor, fieldLabel+": ", mngr.toEditable(ctx));
}
public static SimpleFormRow createModelFieldInputRow
(
ModelManager<WebDbModel> sma,
String id,
String trname,
ClassFieldDescriptor fd,
final Object value
)
{
FrontendFieldEditContext<WebDbModel> ctx = new FrontendFieldEditContext<WebDbModel>();
return createModelFieldInputRow(trname, ctx, DEFAULT_FRONTEND_FIELD_MANAGER);
}
public static final FrontendFieldManager DEFAULT_FRONTEND_FIELD_MANAGER = new FrontendFieldManager<WebDbModel>()
{
@Override
public String toString(WebDbModel model, String field)
{
Object ret = model.get(field);
if(!(ret instanceof Object))
{
return "";
}
if(ret instanceof Date)
{
return Format.SQL_TIMESTAMP.format((Date) ret);
}
if(ret.getClass().isArray())
{
return ArrayTools.toString((Object[])ret);
}
return String.valueOf(ret);
}
@Override
public HTMLElement toEditable(FrontendFieldEditContext<WebDbModel> ctx)
{
return generateEditableOfType(ctx, ctx.field.getName(), ctx.field.getType(), ctx.value);
}
};
protected static Set<String> collect(String... args)
{
return Collections.unmodifiableSet(CollectionTools.inlineAdd(new HashSet<>(), args));
}
public static final Set<String> CLS_BOOLEAN = collect("java.lang.Boolean", "boolean");
public static final Set<String> CLS_NUMBER = collect
(
"java.lang.Byte", "byte",
"java.lang.Short", "short",
"java.lang.Integer", "int",
"java.lang.Long", "long",
"java.lang.Float", "float",
"java.lang.Double", "double",
"java.lang.Number",
"java.math.BigDecimal", "java.math.BigInteger"
);
public static final Set<String> CLS_DATE = collect("java.util.Date", "java.sql.Date");
protected static HTMLElement generateModelEditable
(
FrontendFieldEditContext<WebDbModel> ctx,
String name,
ClassDescriptor type,
Object value
)
{
LoggingTools.tryLogFormat(LOG, LogLevel.DEBUG, "generateModelEditable type is GenericStorable");
//TODO teavm: anonymus class method has been not overridden.
final SelectList sl = new SelectList();
List<WebDbModel> a = ctx.acc.select
(
type,
F.gt.is("do", -1)
);
Long selected = (Long) CastTo.Long.cast(value);
for(WebDbModel d:a)
{
String id = String.valueOf(CastTo.Long.cast(d.getModelId()));
SelectOption so = sl.addOption(id, d.toString());
LoggingTools.tryLogFormat(LOG, LogLevel.DEBUG, "generateModelEditable - GenericStorable: add option: %s -> %s", id, d.toString());
Long check = (Long) CastTo.Long.cast(id);
if(LOG.mayLog(LogLevel.TRACE))
{
LoggingTools.tryLogFormat(LOG, LogLevel.TRACE, "generateModelEditable - isSelected? search: %s actual: %s", selected, check);
}
if(null != selected && selected.equals(check))
{
LoggingTools.tryLogFormat(LOG, LogLevel.DEBUG, "generateModelEditable - GenericStorable: set selected: %s -> %s", selected, check);
so.selectThis();
}
}
return new H((HTMLElement)sl.getHtml()).attrs("name", name, "class", "form-control").getHtml();
}
protected static HTMLElement generateEditableOfPrimitiveType
(
FrontendFieldEditContext<WebDbModel> ctx,
String name,
ClassDescriptor type,
Object value
)
{
String fType = "text";
if(CLS_BOOLEAN.contains(type.getClassName()))
{
fType = "checkbox";
}
else if(CLS_NUMBER.contains(type.getClassName()))
{
fType = "number";
}
H elem = new H("input").attrs("type", fType, "name", name, "class", "form-control");
final HTMLInputElement e = (HTMLInputElement) elem.getHtml();
HtmlDataContainer<String> dc = null;
if(CLS_DATE.contains(type.getClassName()))
{
elem.attrs("type", "datetime-local");
dc = HtmlDataContainerTools.browserDatetimeLocal((HTMLInputElement) e);
}
else
{
dc = HtmlDataContainerTools.wrapInput(e);
}
ImpTools.appendImp(e, dc);
if(null != value && value instanceof Object)
{
dc.setData(value.toString());
}
return e;
}
protected static HTMLElement generateEditableOfType
(
FrontendFieldEditContext<WebDbModel> ctx,
String name,
ClassDescriptor type,
Object value
)
{
if(type.isArray())
{
return generateMultiOf(name, type.getComponentType(), ctx, value);
}
LoggingTools.tryLogFormat(LOG, LogLevel.DEBUG, "generateEditableOfType(name: %s, type: %s, value: %s)", name, type.getClassName(), value);
FieldExtraAttributes fa = FieldExtraAttributes.parse(ctx.field);
if(null != fa)
{
if(null != fa.frontendFieldManager)
{
try
{
LoggingTools.tryLogFormat(LOG, LogLevel.DEBUG, "generateEditableOfType(fa.frontendFieldManager: %s)", fa.frontendFieldManager);
return ((FrontendFieldManager)Class.forName(fa.frontendFieldManager).newInstance()).toEditable(ctx);
}
catch(Exception e)
{
LoggingTools.tryLogFormatException(LOG, LogLevel.ERROR, e, "generateEditableOfType: ");
Mirror.propagateAnyway(e);
}
}
}
boolean gs = false;
List<ClassDescriptor> sups = new ArrayList<>();
ClassDescriptorTools.collectAllSuperClass(sups, type);
for(ClassDescriptor sup: sups)
{
if("hu.ddsi.java.database.GenericStorable".equals(sup.getClassName()))
{
gs = true;
break;
}
}
if(gs)
{
return generateModelEditable(ctx, name, type, value);
}
else if(type.isEnum())
{
return new H(generateEnumChoose(type.getEnumManager(), null, name, String.valueOf(value))).getHtml();
}
else
{
return generateEditableOfPrimitiveType(ctx, name, type, value);
}
}
public static HTMLElement generateMultiOf
(
String masterName,
ClassDescriptor type,
FrontendFieldEditContext ctx,
Object value,
GetBy2<HtmlDataContainer<String>, FrontendFieldEditContext, ClassDescriptor> unitCreator
)
{
final HtmlListContainerUserEditable ret = new HtmlListContainerUserEditable()
{
@Override
protected HtmlDataContainer<String> create()
{
return unitCreator.getBy(ctx, type);
}
};
LoggingTools.tryLogFormat(LOG, LogLevel.DEBUG, "generateMultiOf(name: %s, type: %s, value: %s)", masterName, type.getClassName(), value);
final H input = new H("input").attrs("type", "hidden", "name", masterName);
input.on("serialize", new EventListener<Event>()
{
@Override
public void handleEvent(Event arg0)
{
((HTMLInputElement)input.getHtml()).setValue(ret.getData());
}
});
new H((HTMLElement)ret.getHtml()).addChilds(input);
if(null != value)
{
if(value.getClass().isArray())
{
DataArray arr = new DataArrayTeaVMImpl();
Object[] os = (Object[]) value;
for(int i=0;i<os.length;++i)
{
Object o = os[i];
if(o instanceof GenericStorable)
{
o = GenericStorage.getID((GenericStorable) o);
}
DataReprezTools.put(arr, i, o);
}
value = JSON.stringify((JSObject)arr.getImpl());
}
else if(value instanceof DataArray)
{
value = JSON.stringify((JSObject)((DataArray)value).getImpl());
}
try
{
LoggingTools.tryLogFormat(LOG, LogLevel.DEBUG, "generateMultiOf - setting container value: %s", value);
ret.setData(String.valueOf(value));
}
catch(Exception e)
{
LoggingTools.tryLogFormatException(LOG, LogLevel.ERROR, e, "value: %s", value);
}
}
return (HTMLElement) ret.getHtml();
}
public static HTMLElement generateMultiOf
(
String masterName,
ClassDescriptor type,
FrontendFieldEditContext ctx,
Object value
)
{
return generateMultiOf(masterName, type, ctx, value, new GetBy2<HtmlDataContainer<String>, FrontendFieldEditContext, ClassDescriptor>()
{
@Override
public HtmlDataContainer<String> getBy(FrontendFieldEditContext ctx, ClassDescriptor type)
{
boolean embedded = ctx.isEmbedded;
ctx.isEmbedded = true;
HTMLElement cre = generateEditableOfType(ctx, null, type, null);
ctx.isEmbedded = embedded;
return ImpTools.getImp(cre);
}
});
}
public static final String GSDB_FIELD_EXTRA_ATTRIBUTES_KEY = "GSDB_FIELD_EXTRA_ATTRIBUTES";
public static FieldExtraAttributes tryGetModelFieldExtraAttributes(FieldData fd)
{
return (FieldExtraAttributes)fd.getExtraDataMap().get(GSDB_FIELD_EXTRA_ATTRIBUTES_KEY);
}
protected static final ConcurrentMap<Class, ModelData> MODEL_EXTRA_DATA = new ConcurrentHashMap<>();
protected static final GetBy1<ModelData, Class> CREATE_MODEL_DATA_STORAGE = (cls) -> new ModelData(cls);
public static ModelData getModelData(Class<? extends WebModel> cls)
{
return ConcurrentMapTools.getOrCreate(MODEL_EXTRA_DATA, cls, CREATE_MODEL_DATA_STORAGE);
}
public static void putFieldAttributes(Class<? extends WebDbModel> cls, Object... name_FieldExtraAttributes)
{
Map<String, FieldExtraAttributes> fieldDatas = new HashMap<>();
for(int i=0;i<name_FieldExtraAttributes.length;i+=2)
{
fieldDatas.put
(
(String) name_FieldExtraAttributes[i],
(FieldExtraAttributes) name_FieldExtraAttributes[i+1]
);
}
ModelData md = new ModelData(cls);
FieldData[] fds = GenericStorageMappingData.getOrCollectAllClassData(cls).allFd;
for(FieldData fd:fds)
{
if(!RemoteGsdbTools.isWebgsdbUseField(fd.f))
{
continue;
}
FieldExtraAttributes dat = fieldDatas.get(fd.f.getName());
if(null == dat)
{
System.out.println("No field metadata attached: "+cls.getName()+"."+fd.f.getName());
continue;
}
dat.owner = md;
if(null != dat)
{
fd.getExtraDataMap().put(GSDB_FIELD_EXTRA_ATTRIBUTES_KEY, dat);
}
}
}
public static <D extends WebDbModel> FrontendFieldManager<D> getFrontendFieldManager(D model, ClassFieldDescriptor fd, FieldExtraAttributes fa)
{
FrontendFieldManager<D> ffm = null;
LoggingTools.tryLogFormat(LOG, LogLevel.DEBUG, "getFrontendFieldManager(model: %s, field: %s, extra: %s)", model, fd, fa);
if(null != fa && null != fa.frontendFieldManager)
{
try
{
LoggingTools.tryLogFormat(LOG, LogLevel.DEBUG, "getFrontendFieldManager - creating new instance of custom frontendFieldManager: %s", fa.frontendFieldManager);
ffm = (FrontendFieldManager<D>) Class.forName(fa.frontendFieldManager).newInstance();
}
catch(Exception e)
{
LoggingTools.tryLogFormatException(LOG, LogLevel.ERROR, e, "getFrontendFieldManager - Can't initialize FrontendFieldManager for: %s : %s | %s ", model.getClass(), fd.getName(), fa.frontendFieldManager);
e.printStackTrace();
}
}
if(null == ffm)
{
LoggingTools.tryLogFormat(LOG, LogLevel.DEBUG, "getFrontendFieldManager - using default frontendFieldManager");
ffm = WebGsdbTools.DEFAULT_FRONTEND_FIELD_MANAGER;
}
return ffm;
}
public static String translate(String trname)
{
if(null != trname && trname.contains("."))
{
trname = StringTools.getSubstringAfterLastString(trname, ".", trname);
}
return trname;
}
public static void generateModelEditor(SimplePublish1<SimpleFormRow> builder, ClassDescriptor cls, WebDbModel model, FrontendFieldEditContext baseCtx)
{
generateModelEditor
(
builder,
(List<ClassFieldDescriptor> ) cls.getAllField(),
fd->translate(fd.getTranslationSymbol()),
model,
baseCtx
);
}
public static void generateModelEditor
(
SimplePublish1<SimpleFormRow> builder,
List<ClassFieldDescriptor> fields,
GetBy1<String, ClassFieldDescriptor> getLabel,
WebDbModel model,
FrontendFieldEditContext baseCtx
)
{
String ns = StringTools.randomString(10);
LoggingTools.tryLogFormat(LOG, LogLevel.DEBUG, "generateModelEditor(cls: %s)", model.getModelClass());
for(ClassFieldDescriptor fd:fields)
{
if(!fd.isUserAccessible())
{
continue;
}
String fieldName = fd.getName();
FieldExtraAttributes fa = FieldExtraAttributes.parse(fd);
if(null == fa || (fa.userCanModify && fa.userMaySee))
{
String trname = getLabel.getBy(fd);
Object val = null;
if(null != model)
{
Object o = model.get(fd.getName());
if(o instanceof WebDbModel)
{
val = String.valueOf(CastTo.Long.cast(((WebDbModel)o).get("do")));
}
else
{
val = model.get(fieldName);
}
}
FrontendFieldEditContext<WebDbModel> ctx = baseCtx.clone();
ctx.field = fd;
ctx.idLabelFor = ns+fieldName;
ctx.model = model;
ctx.value = val;
HTMLElement edit = generateEditableOfType(ctx, fieldName, fd.getType(), val);
builder.publish(new SimpleFormRow(ctx.idLabelFor, trname+": ", edit));
/* SimpleFormRow row = null;
FrontendFieldManager ffm = WebGsdbTools.getFrontendFieldManager(model, fd, fa);
if(null != ffm)
{
FrontendFieldEditContext ctx = baseCtx.clone();
ctx.field = fd;
ctx.gdb = GenericStorage.getOwnerDatabase(model);
ctx.idLabelFor = ns+fieldName;
ctx.model = model;
ctx.value = val;
row = WebGsdbTools.createModelFieldInputRow(fieldName, ctx, ffm);
}
else
{
row = WebGsdbTools.createModelFieldInputRow(null, ns+fieldName, trname, fd, val);
}
builder.publish(row);*/
}
}
}
}