mirror of
https://github.com/gnosygnu/xowa.git
synced 2026-03-02 03:49:30 +00:00
v2.5.4.1
This commit is contained in:
@@ -50,8 +50,8 @@ class GfmlParse_fxt {
|
||||
public void tst_Err(String raw, UsrMsg_mok... expdErrs) {
|
||||
bldr.ThrowErrors_set(false);
|
||||
GfmlDoc actlDoc = bldr.XtoGfmlDoc(raw);
|
||||
ListAdp expd = ListAdp_.new_(), actl = actlDoc.UsrMsgs();
|
||||
expd.AddMany((Object[])expdErrs);
|
||||
List_adp expd = List_adp_.new_(), actl = actlDoc.UsrMsgs();
|
||||
expd.Add_many((Object[])expdErrs);
|
||||
TfdsTstr_fxt tstr = TfdsTstr_fxt.new_();
|
||||
int max = tstr.List_Max(expd, actl);
|
||||
for (int i = 0; i < max; i++) {
|
||||
@@ -60,14 +60,14 @@ class GfmlParse_fxt {
|
||||
UsrMsg_mok actlUmm = UsrMsg_mok.new_(actlUm);
|
||||
tstr.Eq_str(expdUm.Main(), actlUmm.Main(), "main");
|
||||
for (int j = 0; j < expdUm.Args().Count(); j++) {
|
||||
KeyVal expdKv = (KeyVal)expdUm.Args().FetchAt(j);
|
||||
KeyVal actlKv = (KeyVal)actlUmm.Args().Fetch(expdKv.Key());
|
||||
KeyVal expdKv = (KeyVal)expdUm.Args().Get_at(j);
|
||||
KeyVal actlKv = (KeyVal)actlUmm.Args().Get_by(expdKv.Key());
|
||||
Object actlVal = actlKv == null ? String_.Null_mark : actlKv.Val();
|
||||
tstr.Eq_str(expdKv.Val(), actlVal, expdKv.Key());
|
||||
}
|
||||
for (int j = 0; j < expdUm.Required().Count(); j++) {
|
||||
String expdKv = (String)expdUm.Required().FetchAt(j);
|
||||
KeyVal actlKv = (KeyVal)actlUmm.Args().Fetch(expdKv);
|
||||
String expdKv = (String)expdUm.Required().Get_at(j);
|
||||
KeyVal actlKv = (KeyVal)actlUmm.Args().Get_by(expdKv);
|
||||
Object actlVal = actlKv == null ? String_.Null_mark : actlKv.Val();
|
||||
Object actlValV = actlKv == null ? "<<REQD>>" : actlKv.Val();
|
||||
tstr.Eq_str(actlValV, actlVal, expdKv);
|
||||
|
||||
@@ -21,7 +21,7 @@ class GfmlTypeCompiler_fxt {
|
||||
public GfmlNde_mok nde_() {return GfmlNde_mok.new_();}
|
||||
public GfmlFld_mok fld_() {return GfmlFld_mok.new_();}
|
||||
public GfmlTypRegy Regy() {return typBldr.TypeRegy();}
|
||||
public void ini_Typ(ListAdp typs, GfmlTyp_mok typ) {typs.Add(typ);}
|
||||
public void ini_Typ(List_adp typs, GfmlTyp_mok typ) {typs.Add(typ);}
|
||||
@gplx.Internal protected void run_InitPragma(GfmlTypRegy regy, GfmlPragma pragma) {
|
||||
GfmlTypeMakr makr = GfmlTypeMakr.new_();
|
||||
GfmlType[] typeAry = pragma.MakePragmaTypes(makr);
|
||||
@@ -34,7 +34,7 @@ class GfmlTypeCompiler_fxt {
|
||||
}
|
||||
public void tst_Compile(GfmlNde_mok nde, GfmlTyp_mok expd) {
|
||||
GfmlNde gnde = run_Resolve(this.Regy(), "_type/type", nde);
|
||||
OrderedHash list = OrderedHash_.new_();
|
||||
Ordered_hash list = Ordered_hash_.new_();
|
||||
GfmlType actlType = GfmlTypeCompiler.Compile(gnde, GfmlType_.Root, this.Regy(), list);
|
||||
GfmlTyp_mok actl = GfmlTyp_mok.type_(actlType);
|
||||
TfdsTstr_fxt tstr = TfdsTstr_fxt.new_();
|
||||
|
||||
@@ -50,7 +50,7 @@ class GfmlNde_mok implements GfmlItm_mok {
|
||||
public boolean KeyedSubObj() {return keyed;}
|
||||
public GfmlNde_mok KeyedSubObj_() {return KeyedSubObj_(true);}
|
||||
public GfmlNde_mok KeyedSubObj_(boolean v) {keyed = v; return this;} private boolean keyed;
|
||||
public ListAdp Subs() {return subs;}
|
||||
public List_adp Subs() {return subs;}
|
||||
public String XtoStrStub() {
|
||||
String_bldr sb = String_bldr_.new_();
|
||||
sb.Add_kv("key=", key).Add_kv("hnd=", hnd).Add_kv("typ=", typ).Add_kv("subs=", Int_.Xto_str(subs.Count()));
|
||||
@@ -60,7 +60,7 @@ class GfmlNde_mok implements GfmlItm_mok {
|
||||
for (GfmlItm_mok itm : ary)
|
||||
subs.Add(itm);
|
||||
return this;
|
||||
} ListAdp subs = ListAdp_.new_();
|
||||
} List_adp subs = List_adp_.new_();
|
||||
|
||||
public GfmlNde_mok Atrk_(String k, String v) {subs.Add(GfmlAtr_mok.new_(k, v)); return this;}
|
||||
public GfmlNde_mok Atru_(String v) {subs.Add(GfmlAtr_mok.new_(GfmlTkn_.NullVal, v)); return this;}
|
||||
@@ -78,7 +78,7 @@ class GfmlNde_mok implements GfmlItm_mok {
|
||||
if (ownerMok.keyed) rv.KeyedSubObj_(ownerMok.keyed);
|
||||
if (ownerMok.key != null) rv.KeyTkn_set(GfmlTkn_.val_(ownerMok.key));
|
||||
for (int i = 0; i < ownerMok.subs.Count(); i++) {
|
||||
GfmlItm_mok itm = (GfmlItm_mok)ownerMok.subs.FetchAt(i);
|
||||
GfmlItm_mok itm = (GfmlItm_mok)ownerMok.subs.Get_at(i);
|
||||
if (itm.ObjType() == GfmlObj_.Type_nde) {
|
||||
GfmlNde_mok itmMok = (GfmlNde_mok)itm;
|
||||
rv.SubObjs_Add(itmMok.XtoGfmlItm(regy));
|
||||
@@ -103,7 +103,7 @@ class GfmlNde_mok implements GfmlItm_mok {
|
||||
rv.chainId = nde.ChainId();
|
||||
if (nde.Key() != null) rv.key = nde.Key();
|
||||
for (int i = 0; i < nde.SubKeys().Count(); i++) {
|
||||
GfmlItm subItm = (GfmlItm)nde.SubKeys().FetchAt(i);
|
||||
GfmlItm subItm = (GfmlItm)nde.SubKeys().Get_at(i);
|
||||
if (subItm.ObjType() == GfmlObj_.Type_atr) {
|
||||
GfmlAtr subAtr = (GfmlAtr)subItm;
|
||||
GfmlAtr_mok mokAtr = GfmlAtr_mok.new_(subAtr.Key(), subAtr.DatTkn().Val());
|
||||
@@ -117,7 +117,7 @@ class GfmlNde_mok implements GfmlItm_mok {
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < nde.SubHnds().Count(); i++) {
|
||||
GfmlNde subNde = (GfmlNde)nde.SubHnds().FetchAt(i);
|
||||
GfmlNde subNde = (GfmlNde)nde.SubHnds().Get_at(i);
|
||||
GfmlNde_mok mokNde = InitNde(subNde);
|
||||
rv.subs.Add(mokNde);
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
package gplx.gfml; import gplx.*;
|
||||
class GfmlTkn_mok {
|
||||
public String Raw() {return raw;} public GfmlTkn_mok Raw_(String v) {raw = v; return this;} private String raw;
|
||||
public ListAdp Subs() {return list;} ListAdp list = ListAdp_.new_();
|
||||
public List_adp Subs() {return list;} List_adp list = List_adp_.new_();
|
||||
public GfmlTkn_mok Subs_(GfmlTkn_mok... ary) {
|
||||
for (GfmlTkn_mok itm : ary)
|
||||
list.Add(itm);
|
||||
|
||||
@@ -43,7 +43,7 @@ class GfmlFld_mok {
|
||||
class GfmlTyp_mok {
|
||||
public String Name() {return name;} public GfmlTyp_mok Name_(String v) {name = v; return this;} private String name;
|
||||
public String Key() {return key;} public GfmlTyp_mok Key_(String v) {key = v; return this;} private String key;
|
||||
public ListAdp Subs() {return subFlds;} ListAdp subFlds = ListAdp_.new_();
|
||||
public List_adp Subs() {return subFlds;} List_adp subFlds = List_adp_.new_();
|
||||
public GfmlTyp_mok Atrs_(String... ary) {
|
||||
for (String itm : ary)
|
||||
subFlds.Add(GfmlFld_mok.new_().ini_atr_(itm));
|
||||
@@ -57,7 +57,7 @@ class GfmlTyp_mok {
|
||||
public GfmlType XtoGfmlType() {
|
||||
GfmlType rv = GfmlType_.new_(key, name); // all types in tests are top-level
|
||||
for (int i = 0; i < subFlds.Count(); i++) {
|
||||
GfmlFld_mok fld = (GfmlFld_mok)subFlds.FetchAt(i);
|
||||
GfmlFld_mok fld = (GfmlFld_mok)subFlds.Get_at(i);
|
||||
rv.SubFlds().Add(fld.XtoGfmlFld());
|
||||
}
|
||||
return rv;
|
||||
@@ -74,7 +74,7 @@ class GfmlTyp_mok {
|
||||
GfmlTyp_mok rv = new GfmlTyp_mok();
|
||||
rv.key = typ.Key(); rv.name = typ.NdeName();
|
||||
for (int i = 0; i < typ.SubFlds().Count(); i++) {
|
||||
GfmlFld fld = (GfmlFld)typ.SubFlds().FetchAt(i);
|
||||
GfmlFld fld = (GfmlFld)typ.SubFlds().Get_at(i);
|
||||
GfmlFld_mok mkFld = GfmlFld_mok.new_().ini_ndk_(fld.Name(), fld.TypeKey()).DefaultTkn_(fld.DefaultTkn());
|
||||
rv.subFlds.Add(mkFld);
|
||||
}
|
||||
|
||||
@@ -20,14 +20,14 @@ class UsrMsg_mok {
|
||||
public String Main() {return main;} public UsrMsg_mok Main_(String v) {main = v; return this;} private String main;
|
||||
public UsrMsg_mok Add_(String k, Object o) {hash.Add(k, KeyVal_.new_(k, o)); return this;}
|
||||
public UsrMsg_mok Require_(String k) {required.Add(k, k); return this;}
|
||||
public OrderedHash Args() {return hash;} OrderedHash hash = OrderedHash_.new_();
|
||||
public OrderedHash Required() {return required;} OrderedHash required = OrderedHash_.new_();
|
||||
public Ordered_hash Args() {return hash;} Ordered_hash hash = Ordered_hash_.new_();
|
||||
public Ordered_hash Required() {return required;} Ordered_hash required = Ordered_hash_.new_();
|
||||
public static UsrMsg_mok new_(UsrMsg um) {
|
||||
UsrMsg_mok rv = new UsrMsg_mok();
|
||||
if (um != null) {
|
||||
rv.main = um.Hdr();
|
||||
for (int i = 0; i < um.Args().Count(); i++) {
|
||||
KeyVal kv = (KeyVal)um.Args().FetchAt(i);
|
||||
KeyVal kv = (KeyVal)um.Args().Get_at(i);
|
||||
rv.Add_(kv.Key(), kv.Val());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,6 +58,6 @@ public class z011_IntObjHash_tst {
|
||||
tst_Fetch(17, null); // works
|
||||
tst_Fetch(16, null); // used to fail
|
||||
}
|
||||
void tst_Fetch(int key, Object expd) {Tfds.Eq(expd, hash.Fetch(key));}
|
||||
void tst_Fetch(int key, Object expd) {Tfds.Eq(expd, hash.Get_by(key));}
|
||||
void tst_Count(int expd) {Tfds.Eq(expd, hash.Count());}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ public class z016_GfmlScopeList_tst {
|
||||
list.Add(itm);
|
||||
}
|
||||
void tst_Itm(GfmlScopeList list, GfmlDocPos pos, String expd) {
|
||||
GfmlVarItm itm = (GfmlVarItm)list.Fetch(pos);
|
||||
GfmlVarItm itm = (GfmlVarItm)list.Get_by(pos);
|
||||
String actl = itm == null ? null : itm.TknVal();
|
||||
Tfds.Eq(expd, actl);
|
||||
}
|
||||
|
||||
@@ -54,14 +54,14 @@ public class z091_GfmlLxr_basic_tst {
|
||||
}
|
||||
GfmlTkn tst_Fetch(String raw, String... expd) {
|
||||
CharStream stream = CharStream.pos0_(raw);
|
||||
ListAdp list = ListAdp_.new_();
|
||||
List_adp list = List_adp_.new_();
|
||||
GfmlTkn tkn = null;
|
||||
while (true) {
|
||||
tkn = rootLxr.MakeTkn(stream, 0);
|
||||
if (tkn == GfmlTkn_.EndOfStream) break;
|
||||
list.Add(tkn.Raw());
|
||||
}
|
||||
String[] actl = (String[])list.Xto_ary(String.class);
|
||||
String[] actl = (String[])list.To_ary(String.class);
|
||||
Tfds.Eq_ary(expd, actl);
|
||||
return tkn;
|
||||
}
|
||||
|
||||
@@ -25,9 +25,9 @@ class GfmlDefaultItem_fxt {
|
||||
Tfds.Eq(GfmlFld_mok.XtoRaw(expd.Val()), GfmlFld_mok.XtoRaw(actl.Val()));
|
||||
Tfds.Eq(Val_to_str_or_null(expd.ValPrev()), Val_to_str_or_null(actl.ValPrev()));
|
||||
}
|
||||
@gplx.Internal protected void tst_List(ListAdp list, GfmlDefaultItem... expdAry) {
|
||||
@gplx.Internal protected void tst_List(List_adp list, GfmlDefaultItem... expdAry) {
|
||||
for (int i = 0; i < expdAry.length; i++) {
|
||||
GfmlDefaultItem actl = (GfmlDefaultItem)list.FetchAt(i);
|
||||
GfmlDefaultItem actl = (GfmlDefaultItem)list.Get_at(i);
|
||||
GfmlDefaultItem expd = expdAry[i];
|
||||
tst_Item(actl, expd);
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ public class z451_dflts_compile_tst {
|
||||
( fx_typ.nde_().Atrs_("x", "10")
|
||||
, fx_typ.nde_().Atrs_("y", "20")
|
||||
));
|
||||
ListAdp list = ListAdp_.new_();
|
||||
List_adp list = List_adp_.new_();
|
||||
pragma.CompileSubNde(gnde, list);
|
||||
fx.tst_List(list
|
||||
, fx.make_("point", "x", "10")
|
||||
|
||||
@@ -67,23 +67,23 @@ public class z452_dflts_exec_tst {
|
||||
GfmlVarCtx evalContext = GfmlVarCtx.new_("testContext");
|
||||
GfmlTkn valTkn = GfmlTkn_.raw_(text);
|
||||
GfmlVarItm varItem = GfmlVarItm.new_("varKey", valTkn, "testContext");
|
||||
evalContext.AddReplace(varItem);
|
||||
evalContext.Add_if_dupe_use_nth(varItem);
|
||||
GfmlVarTkn rv = new GfmlVarTkn("eval", GfmlTknAry_.ary_(valTkn), evalContext, "varKey");
|
||||
return new Object[] {rv, varItem};
|
||||
}
|
||||
void ini_SubFldDefault_add(GfmlType type, String subFldKey, String defaultVal) {
|
||||
GfmlFld subFld = type.SubFlds().Fetch(subFldKey);
|
||||
GfmlFld subFld = type.SubFlds().Get_by(subFldKey);
|
||||
GfmlTkn defaultTkn = GfmlTkn_.raw_(defaultVal);;
|
||||
subFld.DefaultTkn_(defaultTkn);
|
||||
}
|
||||
void tst_SubFldDefault(GfmlType type, String subFldKey, String expdDefaultVal) {
|
||||
GfmlFld subFld = type.SubFlds().Fetch(subFldKey);
|
||||
GfmlFld subFld = type.SubFlds().Get_by(subFldKey);
|
||||
GfmlTkn defaultTkn = GfmlTkn_.as_(subFld.DefaultTkn());
|
||||
String actlDefaultVal = defaultTkn == null || defaultTkn == GfmlTkn_.Null ? null : defaultTkn.Val();
|
||||
Tfds.Eq(expdDefaultVal, actlDefaultVal);
|
||||
}
|
||||
void tst_SubFldExists(GfmlType type, String subFldKey, boolean expd) {
|
||||
GfmlFld subFld = type.SubFlds().Fetch(subFldKey);
|
||||
GfmlFld subFld = type.SubFlds().Get_by(subFldKey);
|
||||
Tfds.Eq(expd, subFld != null);
|
||||
}
|
||||
GfmlType make_(String key, String name, String... atrs) {
|
||||
|
||||
@@ -30,7 +30,7 @@ public class z455_dflts_scope_tst {
|
||||
tst_FetchOrNullByPos(regy, "point", rootPos, "point", "x", "y");
|
||||
tst_FetchOrNullByPos(regy, "point", currPos, "point", "x", "y");
|
||||
|
||||
ListAdp list = ListAdp_.new_();
|
||||
List_adp list = List_adp_.new_();
|
||||
list.Add(GfmlDefaultItem.new_("point", "z", GfmlTkn_.raw_("0")));
|
||||
GfmlDefaultPragma_bgnCmd.ExecList(regy, currPos, list);
|
||||
|
||||
@@ -42,7 +42,7 @@ public class z455_dflts_scope_tst {
|
||||
Tfds.Eq(expdTypeKey, actl.Key());
|
||||
String[] actlSubs = new String[actl.SubFlds().Count()];
|
||||
for (int i = 0; i < actlSubs.length; i++)
|
||||
actlSubs[i] = actl.SubFlds().FetchAt(i).Name();
|
||||
actlSubs[i] = actl.SubFlds().Get_at(i).Name();
|
||||
Tfds.Eq_ary(expdSubs, actlSubs);
|
||||
return actl;
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ public class z601_edit_atr_tst {
|
||||
fx .Raw_("a='1';").Update_(fx.atr_().NdeIdxs_(0).Atr_("b", "2"))
|
||||
.tst_("a='1' b='2';");
|
||||
}
|
||||
@Test public void AddMany() {
|
||||
@Test public void Add_many() {
|
||||
fx .Raw_("a='1';").Update_(fx.atr_().NdeIdxs_(0).Atr_("b", "2")).Update_(fx.atr_().NdeIdxs_(0).Atr_("c", "3"))
|
||||
.tst_("a='1' b='2' c='3';");
|
||||
}
|
||||
@@ -86,7 +86,7 @@ class GfmlUpdateCmd_atr implements GfmlUpdateCmd {
|
||||
public void Exec(GfmlDoc gdoc) {
|
||||
GfmlNde nde = GetNde(ndeIdxs, gdoc.RootNde());
|
||||
nde.UpdateAtr(atrKey, atrVal);
|
||||
// GfmlAtr atr = atrIdx != -1 ? GfmlAtr.as_(nde.SubKeys().FetchAt(atrIdx)) : GfmlAtr.as_(nde.SubKeys().Fetch(atrKey));
|
||||
// GfmlAtr atr = atrIdx != -1 ? GfmlAtr.as_(nde.SubKeys().Get_at(atrIdx)) : GfmlAtr.as_(nde.SubKeys().Get_by(atrKey));
|
||||
// atr.UpdateAtr(atrKey, atrVal);
|
||||
atrKey = ""; atrVal = "";//atrIdx = -1;
|
||||
}
|
||||
@@ -94,7 +94,7 @@ class GfmlUpdateCmd_atr implements GfmlUpdateCmd {
|
||||
GfmlNde nde = owner;
|
||||
for (int i = 0; i < ndeIdxs.length; i++) {
|
||||
int ndeIdx = ndeIdxs[i];
|
||||
nde = (GfmlNde)owner.SubHnds().FetchAt(ndeIdx);
|
||||
nde = (GfmlNde)owner.SubHnds().Get_at(ndeIdx);
|
||||
}
|
||||
return nde;
|
||||
}
|
||||
@@ -114,11 +114,11 @@ class GfmlUpdateFx {
|
||||
public GfmlUpdateCmd_atr atr_() {return GfmlUpdateCmd_atr.new_();}
|
||||
public GfmlUpdateCmd_nde nde_() {return GfmlUpdateCmd_nde.new_();}
|
||||
public String Raw() {return raw;} public GfmlUpdateFx Raw_(String v) {raw = v; return this;} private String raw;
|
||||
public GfmlUpdateFx Update_(GfmlUpdateCmd cmd) {cmds.Add(cmd); return this;} ListAdp cmds = ListAdp_.new_();
|
||||
public GfmlUpdateFx Update_(GfmlUpdateCmd cmd) {cmds.Add(cmd); return this;} List_adp cmds = List_adp_.new_();
|
||||
public GfmlUpdateFx tst_(String expd) {
|
||||
GfmlDoc actlDoc = GfmlDataNde.new_any_eol_(raw).Doc();
|
||||
for (int i = 0; i < cmds.Count(); i++) {
|
||||
GfmlUpdateCmd cmd = (GfmlUpdateCmd)cmds.FetchAt(i);
|
||||
GfmlUpdateCmd cmd = (GfmlUpdateCmd)cmds.Get_at(i);
|
||||
cmd.Exec(actlDoc);
|
||||
}
|
||||
String actl = actlDoc.RootNde().XtoStr();
|
||||
|
||||
@@ -56,7 +56,7 @@ public class z803_useCase_KbdKeyboard_tst {
|
||||
}
|
||||
@Test public void Load_Smoke() {
|
||||
Io_url url = Tfds.RscDir.GenSubFil_nest("110_gfml", "cfgs_archive", "gfui-keyboard-ui.cfg.gfml");
|
||||
raw = Io_mgr._.LoadFilStr(url);
|
||||
raw = Io_mgr.I.LoadFilStr(url);
|
||||
gdoc = GfmlDoc_.parse_any_eol_(raw);
|
||||
// Tfds.Write(gdoc.RootNde().XtoStr());
|
||||
}
|
||||
|
||||
@@ -39,12 +39,12 @@ public class z811_useCase_GfmlIoSql_tst {
|
||||
Tfds.Eq_ary_str(XtoStr(expd.Nde()), XtoStr(actl));
|
||||
}
|
||||
String[] XtoStr(GfmlNde nde) {
|
||||
ListAdp list = ListAdp_.new_();
|
||||
List_adp list = List_adp_.new_();
|
||||
for (int i = 0; i < nde.SubObjs_Count(); i++) {
|
||||
GfmlAtr atr = (GfmlAtr)nde.SubObjs_GetAt(i);
|
||||
list.Add(atr.Key() + "=" + atr.DatTkn().Raw());
|
||||
}
|
||||
return list.XtoStrAry();
|
||||
return list.To_str_ary();
|
||||
}
|
||||
GfmlNdeWrapper nde_(String name) {return GfmlNdeWrapper.new_().Name_(name);}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ public class z901_perf_tst {
|
||||
// @Test
|
||||
public void Long() {
|
||||
// String longText = String_.Repeat("a", 30 * 1000 * 1000);
|
||||
String longText = Io_mgr._.LoadFilStr(Io_url_.new_any_("C:\\core_weekly.temp.gfio"));
|
||||
String longText = Io_mgr.I.LoadFilStr(Io_url_.new_any_("C:\\core_weekly.temp.gfio"));
|
||||
// String_bldr sbXml = String_bldr_.new_();
|
||||
// sbXml.Add("<");
|
||||
// sbXml.Add(longText);
|
||||
|
||||
Reference in New Issue
Block a user