From 341d2e13a43bb91e40b0c0204a750a5f56780a26 Mon Sep 17 00:00:00 2001 From: gnosygnu Date: Sun, 24 Jun 2018 08:36:34 -0400 Subject: [PATCH] Wikibase: Add getReferencedEntityId; refactor some wikibase code --- ...ta_doc_cache.java => Wbase_doc_cache.java} | 4 +- .../gplx/xowa/apps/caches/Xoa_cache_mgr.java | 2 +- .../libs/Referenced_entity_lookup_wkr.java | 121 ++++++++++++++++++ .../scribunto/libs/Scrib_lib_wikibase.java | 35 +++-- .../libs/Scrib_lib_wikibase_tst.java | 15 +++ .../gplx/xowa/xtns/wbases/Wbase_doc_mgr.java | 23 ++-- .../xowa/xtns/wbases/Wbase_doc_mgr__tst.java | 2 +- .../xtns/wbases/claims/Wbase_claim_grp.java | 32 ++++- .../wbases/claims/Wbase_claim_grp_tst.java | 71 ++++++++++ 9 files changed, 280 insertions(+), 25 deletions(-) rename 400_xowa/src/gplx/xowa/apps/caches/{Wdata_doc_cache.java => Wbase_doc_cache.java} (90%) create mode 100644 400_xowa/src/gplx/xowa/xtns/scribunto/libs/Referenced_entity_lookup_wkr.java create mode 100644 400_xowa/src/gplx/xowa/xtns/wbases/claims/Wbase_claim_grp_tst.java diff --git a/400_xowa/src/gplx/xowa/apps/caches/Wdata_doc_cache.java b/400_xowa/src/gplx/xowa/apps/caches/Wbase_doc_cache.java similarity index 90% rename from 400_xowa/src/gplx/xowa/apps/caches/Wdata_doc_cache.java rename to 400_xowa/src/gplx/xowa/apps/caches/Wbase_doc_cache.java index 685d8d203..7b1e5a639 100644 --- a/400_xowa/src/gplx/xowa/apps/caches/Wdata_doc_cache.java +++ b/400_xowa/src/gplx/xowa/apps/caches/Wbase_doc_cache.java @@ -15,8 +15,8 @@ Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt */ package gplx.xowa.apps.caches; import gplx.*; import gplx.xowa.*; import gplx.xowa.apps.*; import gplx.xowa.xtns.wbases.*; -public class Wdata_doc_cache { - private Hash_adp_bry hash = Hash_adp_bry.cs(); +public class Wbase_doc_cache { + private final Hash_adp_bry hash = Hash_adp_bry.cs(); public void Add(byte[] qid, Wdata_doc doc) {hash.Add(qid, doc);} public Wdata_doc Get_or_null(byte[] qid) {return (Wdata_doc)hash.Get_by_bry(qid);} public void Free_mem_all() {this.Clear();} diff --git a/400_xowa/src/gplx/xowa/apps/caches/Xoa_cache_mgr.java b/400_xowa/src/gplx/xowa/apps/caches/Xoa_cache_mgr.java index e4faa9888..5883484be 100644 --- a/400_xowa/src/gplx/xowa/apps/caches/Xoa_cache_mgr.java +++ b/400_xowa/src/gplx/xowa/apps/caches/Xoa_cache_mgr.java @@ -15,7 +15,7 @@ Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt */ package gplx.xowa.apps.caches; import gplx.*; import gplx.xowa.*; import gplx.xowa.apps.*; public class Xoa_cache_mgr { - public Wdata_doc_cache Doc_cache() {return doc_cache;} private Wdata_doc_cache doc_cache = new Wdata_doc_cache(); + public Wbase_doc_cache Doc_cache() {return doc_cache;} private Wbase_doc_cache doc_cache = new Wbase_doc_cache(); public void Free_mem_all() { doc_cache.Free_mem_all(); } diff --git a/400_xowa/src/gplx/xowa/xtns/scribunto/libs/Referenced_entity_lookup_wkr.java b/400_xowa/src/gplx/xowa/xtns/scribunto/libs/Referenced_entity_lookup_wkr.java new file mode 100644 index 000000000..9d3adf060 --- /dev/null +++ b/400_xowa/src/gplx/xowa/xtns/scribunto/libs/Referenced_entity_lookup_wkr.java @@ -0,0 +1,121 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012-2017 gnosygnu@gmail.com + +XOWA is licensed under the terms of the General Public License (GPL) Version 3, +or alternatively under the terms of the Apache License Version 2.0. + +You may use XOWA according to either of these licenses as is most appropriate +for your project on a case-by-case basis. + +The terms of each license can be found in the source code repository: + +GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt +Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt +*/ +package gplx.xowa.xtns.scribunto.libs; import gplx.*; import gplx.xowa.*; import gplx.xowa.xtns.*; import gplx.xowa.xtns.scribunto.*; +import gplx.xowa.xtns.wbases.*; import gplx.xowa.xtns.wbases.claims.*; import gplx.xowa.xtns.wbases.claims.itms.*; import gplx.xowa.xtns.wbases.claims.enums.*; +// REF: https://github.com/wmde/WikibaseDataModelServices/blob/master/src/Lookup/EntityRetrievingClosestReferencedEntityIdLookup.php +class Referenced_entity_lookup_wkr { + private final Wbase_doc_mgr entity_mgr; + private final int maxDepth; + private final int maxEntityVisits; + private final Xoa_url url; + private final byte[] fromId; + private final int propertyId; + private final Ordered_hash toIds; + private final Ordered_hash alreadyVisited = Ordered_hash_.New_bry(); + private final List_adp tmp_snak_list = List_adp_.New(); + public Referenced_entity_lookup_wkr(int maxDepth, int maxEntityVisits, Wbase_doc_mgr entity_mgr, Xoa_url url, byte[] fromId, int propertyId, Ordered_hash toIds) { + this.maxDepth = maxDepth; + this.maxEntityVisits = maxEntityVisits; + this.entity_mgr = entity_mgr; + this.url = url; + this.fromId = fromId; + this.propertyId = propertyId; + this.toIds = toIds; + } + public byte[] Get_referenced_entity() { + if (toIds == null) + return null; + + int steps = this.maxDepth + 1;// Add one as checking $fromId already is a step + Ordered_hash toVisit = Ordered_hash_.New_bry(); + toVisit.Add_as_key_and_val(fromId); + + while (steps-- > 0) { + // $this->entityPrefetcher->prefetch( $toVisit ); + Ordered_hash toVisitNext = Ordered_hash_.New_bry(); + int toVisitLen = toVisit.Len(); + for (int i = 0; i < toVisitLen; i++) { + byte[] curId = (byte[])toVisit.Get_at(i); + byte[] result = processEntityById(alreadyVisited, curId, fromId, propertyId, toIds, toVisitNext); + if (result != null) + return result; + } + // Remove already visited entities + toVisit = merge(toVisitNext, alreadyVisited); + if (toVisit.Len() == 0) + return null; + } + // Exhausted the max. depth without finding anything. + throw newErr(true); + } + private Err newErr(boolean isMaxDepthOrMaxEntities) { + return Err_.new_wo_type("max exceeded", "type", isMaxDepthOrMaxEntities ? "depth" : "entities", "url", url.To_bry(true, false), "fromId", fromId, "propertyId", propertyId, "toIds", toString(toIds)); + } + private static String toString(Ordered_hash hash) { + Bry_bfr bfr = Bry_bfr_.New(); + int len = hash.Len(); + for (int i = 0; i < len; i++) { + bfr.Add_str_u8(Object_.Xto_str_strict_or_null_mark(hash.Get_at(i))).Add_byte_pipe(); + } + return bfr.To_str_and_clear(); + } + private byte[] processEntityById(Ordered_hash alreadyVisited, byte[] id, byte[] fromId, int propertyId, Ordered_hash toIds, Ordered_hash toVisit) { + Wdata_doc entity = getEntity(alreadyVisited, id, fromId, propertyId, toIds); + if (entity == null) + return null; + Wbase_claim_base[] mainSnaks = getMainSnaks(entity, propertyId); + for (Wbase_claim_base mainSnak : mainSnaks) { + byte[] result = processSnak(mainSnak, toVisit, toIds); + if (result != null) + return result; + } + return null; + } + private Wdata_doc getEntity(Ordered_hash alreadyVisited, byte[] id, byte[] fromId, int propertyId, Ordered_hash toIds) { + if (alreadyVisited.Has(id)) { + Gfo_usr_dlg_.Instance.Warn_many("", "", "Entity " + String_.new_u8(id) + " already visited"); + return null; + } + alreadyVisited.Add_as_key_and_val(id); + if (alreadyVisited.Len() > maxEntityVisits) + throw newErr(false); + return entity_mgr.Get_by_bry_or_null(id); + } + private Wbase_claim_base[] getMainSnaks(Wdata_doc entity, int propertyId) { + Wbase_claim_grp claims = entity.Claim_list_get(propertyId); + return claims.Get_best(tmp_snak_list); + } + private byte[] processSnak(Wbase_claim_base snak, Ordered_hash toVisit, Ordered_hash toIds) { + if (snak.Val_tid() != Wbase_claim_type_.Tid__entity) + return null; + Wbase_claim_entity snakEntity = (Wbase_claim_entity)snak; + byte[] entityId = snakEntity.Page_ttl_db(); + if (toIds.Has(entityId)) + return entityId; + toVisit.Add_as_key_and_val(entityId); + return null; + } + + private Ordered_hash merge(Ordered_hash toVisitNext, Ordered_hash alreadyVisited) { + int len = alreadyVisited.Len(); + for (int i = 0; i < len; i++) { + byte[] bry = (byte[])alreadyVisited.Get_at(i); + if (toVisitNext.Has(bry)) + toVisitNext.Del(bry); + } + return toVisitNext; + } +} diff --git a/400_xowa/src/gplx/xowa/xtns/scribunto/libs/Scrib_lib_wikibase.java b/400_xowa/src/gplx/xowa/xtns/scribunto/libs/Scrib_lib_wikibase.java index 47a112a69..eaa4c099b 100644 --- a/400_xowa/src/gplx/xowa/xtns/scribunto/libs/Scrib_lib_wikibase.java +++ b/400_xowa/src/gplx/xowa/xtns/scribunto/libs/Scrib_lib_wikibase.java @@ -20,6 +20,7 @@ import gplx.xowa.xtns.scribunto.procs.*; import gplx.xowa.xtns.wbases.mediawiki.client.includes.*; import gplx.xowa.xtns.wbases.mediawiki.client.includes.dataAccess.scribunto.*; public class Scrib_lib_wikibase implements Scrib_lib { private final Scrib_core core; + private Wbase_doc_mgr entity_mgr; private Wbase_entity_accessor entity_accessor; private WikibaseLanguageIndependentLuaBindings wikibaseLanguageIndependentLuaBindings; private Scrib_lua_proc notify_page_changed_fnc; @@ -28,9 +29,9 @@ public class Scrib_lib_wikibase implements Scrib_lib { public Scrib_proc_mgr Procs() {return procs;} private final Scrib_proc_mgr procs = new Scrib_proc_mgr(); public Scrib_lib Init() { procs.Init_by_lib(this, Proc_names); - Wbase_doc_mgr entityMgr = core.App().Wiki_mgr().Wdata_mgr().Doc_mgr; - this.entity_accessor = new Wbase_entity_accessor(entityMgr); - this.wikibaseLanguageIndependentLuaBindings = new WikibaseLanguageIndependentLuaBindings(entityMgr); + this.entity_mgr = core.App().Wiki_mgr().Wdata_mgr().Doc_mgr; + this.entity_accessor = new Wbase_entity_accessor(entity_mgr); + this.wikibaseLanguageIndependentLuaBindings = new WikibaseLanguageIndependentLuaBindings(entity_mgr); return this; } public Scrib_lib Clone_lib(Scrib_core core) {return new Scrib_lib_wikibase(core);} @@ -97,11 +98,14 @@ public class Scrib_lib_wikibase implements Scrib_lib { entityId = Bry_.Mid(entityId, colonPos + 1); } + boolean valid = checkEntityIdOrNull(entityId) != null; + return rslt.Init_obj(valid); + } + private static byte[] checkEntityIdOrNull(byte[] entityId) { /* REF: https://github.com/wmde/WikibaseDataModel/tree/master/src/Entity/ PropertyId.php.PATTERN: '/^P[1-9]\d{0,9}\z/i'; ItemId.php.PATTERN : '/^Q[1-9]\d{0,9}\z/i'; */ - boolean valid = false; if (entityId.length > 0) { switch (entityId[0]) { case Byte_ascii.Ltr_P: @@ -122,14 +126,14 @@ public class Scrib_lib_wikibase implements Scrib_lib { } } if (numeric) - valid = true; + return entityId; break; } } break; } } - return rslt.Init_obj(valid); + return null; } public boolean GetEntityId(Scrib_proc_args args, Scrib_proc_rslt rslt) { byte[] ttl_bry = args.Pull_bry(0); @@ -138,8 +142,21 @@ public class Scrib_lib_wikibase implements Scrib_lib { byte[] rv = wiki.Appe().Wiki_mgr().Wdata_mgr().Qid_mgr.Get_or_null(wiki, ttl); if (rv == null) rv = Bry_.Empty; return rslt.Init_obj(rv); } + // REF: https://github.com/wikimedia/mediawiki-extensions-Wikibase/blob/master/client/config/WikibaseClient.default.php + private static final int ReferencedEntityIdMaxDepth = 4, ReferencedEntityIdMaxReferencedEntityVisits = 50; + // private static final int ReferencedEntityIdAccessLimit = 3; // max # of calls per page? public boolean GetReferencedEntityId(Scrib_proc_args args, Scrib_proc_rslt rslt) { - throw Err_.new_unimplemented(); + // get fromId, propertyId, and toIds + byte[] fromId = checkEntityIdOrNull(args.Pull_bry(0)); + byte[] propertyIdBry = checkEntityIdOrNull(args.Pull_bry(1)); + int propertyId = Bry_.To_int(Bry_.Mid(propertyIdBry, 1)); + Keyval[] toIdsAry = args.Pull_kv_ary_safe(2); + Ordered_hash toIds = Ordered_hash_.New_bry(); + for (Keyval kv : toIdsAry) { + toIds.Add_as_key_and_val(checkEntityIdOrNull(Bry_.new_u8((String)kv.Val()))); + } + Referenced_entity_lookup_wkr wkr = new Referenced_entity_lookup_wkr(ReferencedEntityIdMaxDepth, ReferencedEntityIdMaxReferencedEntityVisits, entity_mgr, core.Page().Url(), fromId, propertyId, toIds); + return rslt.Init_obj(wkr.Get_referenced_entity()); } public boolean EntityExists(Scrib_proc_args args, Scrib_proc_rslt rslt) { Wdata_doc wdoc = Get_wdoc_or_null(args, core, false); @@ -238,12 +255,12 @@ public function formatValues( $snaksSerialization ) { public boolean IncrementExpensiveFunctionCount(Scrib_proc_args args, Scrib_proc_rslt rslt) { return rslt.Init_obj(Keyval_.Ary_empty); // NOTE: for now, always return null (XOWA does not care about expensive parser functions) } - private static Wdata_doc Get_wdoc_or_null(Scrib_proc_args args, Scrib_core core, boolean logMissing) { + public Wdata_doc Get_wdoc_or_null(Scrib_proc_args args, Scrib_core core, boolean logMissing) { // get qid / pid from scrib_arg[0]; if none, return null; byte[] xid_bry = args.Pull_bry(0); if (Bry_.Len_eq_0(xid_bry)) return null; // NOTE: some Modules do not pass in an argument; return early, else spurious warning "invalid qid for ttl" (since ttl is blank); EX:w:Module:Authority_control; DATE:2013-10-27 // get wdoc - Wdata_doc wdoc = core.Wiki().Appe().Wiki_mgr().Wdata_mgr().Doc_mgr.Get_by_xid_or_null(xid_bry); // NOTE: by_xid b/c Module passes just "p1" not "Property:P1" + Wdata_doc wdoc = entity_mgr.Get_by_xid_or_null(xid_bry); // NOTE: by_xid b/c Module passes just "p1" not "Property:P1" if (wdoc == null && logMissing) Wdata_wiki_mgr.Log_missing_qid(core.Ctx(), xid_bry); return wdoc; } diff --git a/400_xowa/src/gplx/xowa/xtns/scribunto/libs/Scrib_lib_wikibase_tst.java b/400_xowa/src/gplx/xowa/xtns/scribunto/libs/Scrib_lib_wikibase_tst.java index bd9a6ac6f..4b5c66bfd 100644 --- a/400_xowa/src/gplx/xowa/xtns/scribunto/libs/Scrib_lib_wikibase_tst.java +++ b/400_xowa/src/gplx/xowa/xtns/scribunto/libs/Scrib_lib_wikibase_tst.java @@ -54,6 +54,21 @@ public class Scrib_lib_wikibase_tst { fxt.Test_scrib_proc_str(lib, Scrib_lib_wikibase.Invk_getEntityId, Object_.Ary("Earth" ), "q2"); fxt.Test_scrib_proc_str(lib, Scrib_lib_wikibase.Invk_getEntityId, Object_.Ary("missing_page" ), ""); } + @Test public void GetReferencedEntityId() { + /* + wdata_fxt.Init__docs__add(wdata_fxt.Wdoc_bldr("q1").Xto_wdoc()); + wdata_fxt.Init__docs__add(wdata_fxt.Wdoc_bldr("q2").Add_claims(wdata_fxt.Make_claim_entity_qid(1, 1)).Xto_wdoc()); + wdata_fxt.Init__docs__add(wdata_fxt.Wdoc_bldr("q3").Add_claims(wdata_fxt.Make_claim_entity_qid(1, 2)).Xto_wdoc()); + fxt.Test_scrib_proc_str(lib, Scrib_lib_wikibase.Invk_getReferencedEntityId, Object_.Ary("Q3", "P1", NewToIds("Q1")), "Q1"); + */ + } + private static Keyval[] NewToIds(String... toIds) { + int len = toIds.length; + Keyval[] rv = new Keyval[len]; + for (int i = 0; i < len; i++) + rv[i] = Keyval_.int_(i, toIds[i]); + return rv; + } @Test public void GetLabel__cur() { wdata_fxt.Init__docs__add(wdata_fxt.Wdoc_bldr("q2").Add_label("zh-hans", "s").Add_label("zh-hant", "t").Xto_wdoc()); fxt.Test_scrib_proc_str(lib, Scrib_lib_wikibase.Invk_getLabel, Object_.Ary("q2"), "s"); // do not get fallback diff --git a/400_xowa/src/gplx/xowa/xtns/wbases/Wbase_doc_mgr.java b/400_xowa/src/gplx/xowa/xtns/wbases/Wbase_doc_mgr.java index a841f74fa..d66286c30 100644 --- a/400_xowa/src/gplx/xowa/xtns/wbases/Wbase_doc_mgr.java +++ b/400_xowa/src/gplx/xowa/xtns/wbases/Wbase_doc_mgr.java @@ -16,25 +16,26 @@ Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt package gplx.xowa.xtns.wbases; import gplx.*; import gplx.xowa.*; import gplx.xowa.xtns.*; import gplx.langs.jsons.*; import gplx.xowa.wikis.pages.*; +import gplx.xowa.apps.caches.*; public class Wbase_doc_mgr { private final Xoae_app app; private final Wdata_wiki_mgr wbase_mgr; private final Wbase_qid_mgr qid_mgr; - private final gplx.xowa.apps.caches.Wdata_doc_cache hash; + private final Wbase_doc_cache doc_cache; public Wbase_doc_mgr(Xoae_app app, Wdata_wiki_mgr wbase_mgr, Wbase_qid_mgr qid_mgr) { this.app = app; this.wbase_mgr = wbase_mgr; this.qid_mgr = qid_mgr; - this.hash = app.Cache_mgr().Doc_cache(); + this.doc_cache = app.Cache_mgr().Doc_cache(); } public void Enabled_(boolean v) {this.enabled = v;} private boolean enabled; public void Clear() { - synchronized (hash) { // LOCK:app-level - hash.Clear(); + synchronized (doc_cache) { // LOCK:app-level + doc_cache.Clear(); } } public void Add(byte[] full_db, Wdata_doc page) { // TEST: - synchronized (hash) { // LOCK:app-level - if (hash.Get_or_null(full_db) == null) - hash.Add(full_db, page); + synchronized (doc_cache) { // LOCK:app-level + if (doc_cache.Get_or_null(full_db) == null) + doc_cache.Add(full_db, page); } } public Wdata_doc Get_by_ttl_or_null(Xowe_wiki wiki, Xoa_ttl ttl) { @@ -43,18 +44,18 @@ public class Wbase_doc_mgr { } public Wdata_doc Get_by_xid_or_null(byte[] xid) {return Get_by_bry_or_null(Prepend_property_if_needed(xid));}// scribunto passes either p1 or q1; convert p1 to "Property:P1" public Wdata_doc Get_by_bry_or_null(byte[] ttl_bry) {// must be correct format; EX:"Q2" or "Property:P1" - Wdata_doc rv = hash.Get_or_null(ttl_bry); + Wdata_doc rv = doc_cache.Get_or_null(ttl_bry); if (rv == null) { - synchronized (hash) { // LOCK:app-level; hash; + synchronized (doc_cache) { // LOCK:app-level; doc_cache; rv = Load_wdoc_or_null(ttl_bry); if (rv == null) return null; // page not found Add(ttl_bry, rv);// NOTE: use ttl_bry, not rv.Qid; allows subsequent lookups to skip this redirect cycle } } return rv; } - public Wdata_doc Load_wdoc_or_null(byte[] src_ttl_bry) { + private Wdata_doc Load_wdoc_or_null(byte[] src_ttl_bry) { if (!enabled) return null; - synchronized (hash) { // LOCK:app-level; jdoc_parser; moved synchronized higher up; DATE:2016-09-03 + synchronized (doc_cache) { // LOCK:app-level; jdoc_parser; moved synchronized higher up; DATE:2016-09-03 byte[] cur_ttl_bry = src_ttl_bry; int load_count = -1; while (load_count < 2) { // limit to 2 tries (i.e.: 1 redirect) diff --git a/400_xowa/src/gplx/xowa/xtns/wbases/Wbase_doc_mgr__tst.java b/400_xowa/src/gplx/xowa/xtns/wbases/Wbase_doc_mgr__tst.java index 5f4e1b354..5d3a7326b 100644 --- a/400_xowa/src/gplx/xowa/xtns/wbases/Wbase_doc_mgr__tst.java +++ b/400_xowa/src/gplx/xowa/xtns/wbases/Wbase_doc_mgr__tst.java @@ -25,7 +25,7 @@ public class Wbase_doc_mgr__tst { fxt.Parser_fxt().Init_page_create(wbase_mgr.Wdata_wiki(), "Q2", Json_doc.Make_str_by_apos("{'entity':'q2','links':{'enwiki':'q2_en','dewiki':'q2_de'}}")); // fetch Q1; assert Q2 comes back - Wdata_doc actl = wbase_mgr.Doc_mgr.Load_wdoc_or_null(Bry_.new_u8("Q1")); + Wdata_doc actl = wbase_mgr.Doc_mgr.Get_by_bry_or_null(Bry_.new_u8("Q1")); Gftest.Eq__str("Q2", String_.new_u8(actl.Qid())); } } diff --git a/400_xowa/src/gplx/xowa/xtns/wbases/claims/Wbase_claim_grp.java b/400_xowa/src/gplx/xowa/xtns/wbases/claims/Wbase_claim_grp.java index c2310b2c5..cd3f29bf4 100644 --- a/400_xowa/src/gplx/xowa/xtns/wbases/claims/Wbase_claim_grp.java +++ b/400_xowa/src/gplx/xowa/xtns/wbases/claims/Wbase_claim_grp.java @@ -15,7 +15,7 @@ Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt */ package gplx.xowa.xtns.wbases.claims; import gplx.*; import gplx.xowa.*; import gplx.xowa.xtns.*; import gplx.xowa.xtns.wbases.*; import gplx.core.primitives.*; -import gplx.xowa.xtns.wbases.claims.itms.*; +import gplx.xowa.xtns.wbases.claims.enums.*; import gplx.xowa.xtns.wbases.claims.itms.*; public class Wbase_claim_grp { public Wbase_claim_grp(Int_obj_ref id_ref, Wbase_claim_base[] itms) {this.id_ref = id_ref; this.itms = itms;} public Int_obj_ref Id_ref() {return id_ref;} private final Int_obj_ref id_ref; @@ -23,6 +23,36 @@ public class Wbase_claim_grp { public String Id_str() {if (id_str == null) id_str = "P" + Int_.To_str(id_ref.Val()); return id_str;} private String id_str; public int Len() {return itms.length;} private Wbase_claim_base[] itms; public Wbase_claim_base Get_at(int i) {return itms[i];} + + /* + Returns the so called "best statements". + If there are preferred statements, then this is all the preferred statements. + If there are no preferred statements, then this is all normal statements. + */ + public Wbase_claim_base[] Get_best(List_adp tmp_snak_list) { + int len = itms.length; + boolean preferred_found = false; + for (int i = 0; i < len; i++) { + Wbase_claim_base itm = itms[i]; + switch (itm.Rank_tid()) { + case Wbase_claim_rank_.Tid__preferred: + if (!preferred_found) { + if (tmp_snak_list.Len() > 0) + tmp_snak_list.Clear(); + preferred_found = true; + } + tmp_snak_list.Add(itm); + break; + case Wbase_claim_rank_.Tid__normal: + if (!preferred_found) + tmp_snak_list.Add(itm); + break; + } + } + return tmp_snak_list.Count() == 0 ? Empty_array : (Wbase_claim_base[])tmp_snak_list.To_ary_and_clear(Wbase_claim_base.class); + } + private static final Wbase_claim_base[] Empty_array = new Wbase_claim_base[0]; + public static List_adp Xto_list(Ordered_hash hash) { int len = hash.Count(); List_adp rv = List_adp_.New(); diff --git a/400_xowa/src/gplx/xowa/xtns/wbases/claims/Wbase_claim_grp_tst.java b/400_xowa/src/gplx/xowa/xtns/wbases/claims/Wbase_claim_grp_tst.java new file mode 100644 index 000000000..2b2077c99 --- /dev/null +++ b/400_xowa/src/gplx/xowa/xtns/wbases/claims/Wbase_claim_grp_tst.java @@ -0,0 +1,71 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012-2017 gnosygnu@gmail.com + +XOWA is licensed under the terms of the General Public License (GPL) Version 3, +or alternatively under the terms of the Apache License Version 2.0. + +You may use XOWA according to either of these licenses as is most appropriate +for your project on a case-by-case basis. + +The terms of each license can be found in the source code repository: + +GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt +Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt +*/ +package gplx.xowa.xtns.wbases.claims; import gplx.*; import gplx.xowa.*; import gplx.xowa.xtns.*; import gplx.xowa.xtns.wbases.*; +import org.junit.*; import gplx.core.tests.*; import gplx.core.primitives.*; +import gplx.xowa.xtns.wbases.claims.enums.*; import gplx.xowa.xtns.wbases.claims.itms.*; +public class Wbase_claim_grp_tst { + @Test public void Get_best__preferred() { + Wbase_claim_grp_bldr bldr = new Wbase_claim_grp_bldr(123); + bldr.Add("P1", Wbase_claim_rank_.Tid__preferred); + bldr.Add("N1", Wbase_claim_rank_.Tid__normal); + bldr.Add("P2", Wbase_claim_rank_.Tid__preferred); + bldr.Add("N2", Wbase_claim_rank_.Tid__normal); + bldr.Test__Get_best("P1", "P2"); + } + @Test public void Get_best__normal_if_no_preferred() { + Wbase_claim_grp_bldr bldr = new Wbase_claim_grp_bldr(123); + bldr.Add("D1", Wbase_claim_rank_.Tid__deprecated); + bldr.Add("D2", Wbase_claim_rank_.Tid__deprecated); + bldr.Add("N1", Wbase_claim_rank_.Tid__normal); + bldr.Add("N2", Wbase_claim_rank_.Tid__normal); + bldr.Test__Get_best("N1", "N2"); + } + @Test public void Get_best__preferred_after_normal() { + Wbase_claim_grp_bldr bldr = new Wbase_claim_grp_bldr(123); + bldr.Add("N1", Wbase_claim_rank_.Tid__normal); + bldr.Add("N2", Wbase_claim_rank_.Tid__normal); + bldr.Add("P1", Wbase_claim_rank_.Tid__preferred); + bldr.Add("P2", Wbase_claim_rank_.Tid__preferred); + bldr.Test__Get_best("P1", "P2"); + } +} +class Wbase_claim_grp_bldr { + private final int pid; + private final List_adp list = List_adp_.New(); + public Wbase_claim_grp_bldr(int pid) { + this.pid = pid; + } + public void Add(String val, byte rank_tid) { + Wbase_claim_string claim = new Wbase_claim_string(pid, Wbase_claim_value_type_.Tid__value, Bry_.new_u8(val)); + claim.Rank_tid_(rank_tid); + list.Add(claim); + } + public void Test__Get_best(String... expd) { + Wbase_claim_grp grp = new Wbase_claim_grp(Int_obj_ref.New(pid), (Wbase_claim_base[])list.To_ary_and_clear(Wbase_claim_base.class)); + + List_adp tmp_list = List_adp_.New(); + Gftest.Eq__ary(expd, To_string(grp.Get_best(tmp_list))); + } + private String[] To_string(Wbase_claim_base[] items) { + int len = items.length; + String[] rv = new String[len]; + for (int i = 0; i < len; i++) { + Wbase_claim_string claim = (Wbase_claim_string)items[i]; + rv[i] = String_.new_u8(claim.Val_bry()); + } + return rv; + } +}