diff --git a/400_xowa/src/gplx/core/lists/hashs/Hash_adp__int.java b/400_xowa/src/gplx/core/lists/hashs/Hash_adp__int.java index d3b9a1dfc..2b9316468 100644 --- a/400_xowa/src/gplx/core/lists/hashs/Hash_adp__int.java +++ b/400_xowa/src/gplx/core/lists/hashs/Hash_adp__int.java @@ -18,7 +18,7 @@ along with this program. If not, see . package gplx.core.lists.hashs; import gplx.*; import gplx.core.*; import gplx.core.lists.*; import gplx.core.primitives.*; public class Hash_adp__int { - private final Ordered_hash hash = Ordered_hash_.New(); + private final Hash_adp hash = Hash_adp_.New(); private final Int_obj_ref tmp_key = Int_obj_ref.New_neg1(); public void Clear() {hash.Clear();} public int Len() {return hash.Count();} diff --git a/gplx.xowa.mediawiki/src/gplx/core/lists/HashByInt.java b/gplx.xowa.mediawiki/src/gplx/core/lists/HashByInt.java new file mode 100644 index 000000000..bd952dd71 --- /dev/null +++ b/gplx.xowa.mediawiki/src/gplx/core/lists/HashByInt.java @@ -0,0 +1,65 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 of the +License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.core.lists; import gplx.*; import gplx.core.*; +import gplx.core.primitives.*; +public class HashByInt { + private final Ordered_hash hash = Ordered_hash_.New(); + private final Int_obj_ref tmp_key = Int_obj_ref.New_neg1(); + public void Clear() { + hash.Clear(); + } + public int Len() { + return hash.Len(); + } + public Object Get_by_or_fail(int key) { + synchronized (tmp_key) { + HashByIntItem item = (HashByIntItem)hash.Get_by_or_fail(tmp_key.Val_(key)); + return item.val; + } + } + public Object Get_by_or_null(int key) { + synchronized (tmp_key) { + HashByIntItem item = (HashByIntItem)hash.Get_by(tmp_key.Val_(key)); + return item == null ? null : item.val; + } + } + public HashByInt Add_as_bry(int key, String val) {return Add(key, Bry_.new_u8(val));} + public HashByInt Add(int key, Object val) { + HashByIntItem item = new HashByIntItem(key, val); + hash.Add(Int_obj_ref.New(key), item); + return this; + } + public HashByInt Clone() { + HashByInt rv = new HashByInt(); + + int len = hash.Len(); + for (int i = 0; i < len; i++) { + HashByIntItem item = (HashByIntItem)hash.Get_at(i); + rv.Add(item.key, item); + } + return rv; + } +} +class HashByIntItem { + public final int key; + public final Object val; + public HashByIntItem(int key, Object val) { + this.key = key; + this.val = val; + } +} diff --git a/gplx.xowa.mediawiki/src/gplx/xowa/mediawiki/includes/XomwNamespace.java b/gplx.xowa.mediawiki/src/gplx/xowa/mediawiki/includes/XomwNamespace.java index f1392e036..4160603a3 100644 --- a/gplx.xowa.mediawiki/src/gplx/xowa/mediawiki/includes/XomwNamespace.java +++ b/gplx.xowa.mediawiki/src/gplx/xowa/mediawiki/includes/XomwNamespace.java @@ -16,9 +16,8 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ package gplx.xowa.mediawiki.includes; import gplx.*; import gplx.xowa.*; import gplx.xowa.mediawiki.*; -import gplx.core.lists.hashs.*; +import gplx.core.lists.*; public class XomwNamespace { -// // /** // * These namespaces should always be first-letter capitalized, now and // * forevermore. Historically, they could've probably been lowercased too, @@ -192,12 +191,12 @@ public class XomwNamespace { * @return array * @since 1.17 */ - private static Hash_adp__int namespaces = null; - public static Hash_adp__int getCanonicalNamespaces() {return getCanonicalNamespaces(false);} - public static Hash_adp__int getCanonicalNamespaces(boolean rebuild) { + private static HashByInt namespaces = null; + public static HashByInt getCanonicalNamespaces() {return getCanonicalNamespaces(false);} + public static HashByInt getCanonicalNamespaces(boolean rebuild) { if (namespaces == null || rebuild) { // global $wgExtraNamespaces, $wgCanonicalNamespaceNames; - namespaces = XomwSetup.wgCanonicalNamespaceNames; + namespaces = XomwSetup.wgCanonicalNamespaceNames.Clone(); namespaces.Add_as_bry(XomwDefines.NS_MAIN, ""); // // Add extension namespaces diff --git a/gplx.xowa.mediawiki/src/gplx/xowa/mediawiki/includes/XomwSetup.java b/gplx.xowa.mediawiki/src/gplx/xowa/mediawiki/includes/XomwSetup.java index 3ce27987e..f1f7f4e6a 100644 --- a/gplx.xowa.mediawiki/src/gplx/xowa/mediawiki/includes/XomwSetup.java +++ b/gplx.xowa.mediawiki/src/gplx/xowa/mediawiki/includes/XomwSetup.java @@ -16,7 +16,7 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ package gplx.xowa.mediawiki.includes; import gplx.*; import gplx.xowa.*; import gplx.xowa.mediawiki.*; -import gplx.core.lists.hashs.*; +import gplx.core.lists.*; /** * Include most things that are needed to make MediaWiki work. * @@ -395,7 +395,7 @@ public class XomwSetup { * Definitions of the NS_ constants are in Defines.php * @private */ - public static Hash_adp__int wgCanonicalNamespaceNames = new Hash_adp__int() + public static HashByInt wgCanonicalNamespaceNames = new HashByInt() .Add_as_bry(XomwDefines.NS_MEDIA , "Media") .Add_as_bry(XomwDefines.NS_SPECIAL , "Special") .Add_as_bry(XomwDefines.NS_TALK , "Talk") diff --git a/gplx.xowa.mediawiki/src/gplx/xowa/mediawiki/includes/XomwTitle.java b/gplx.xowa.mediawiki/src/gplx/xowa/mediawiki/includes/XomwTitle.java index 4c8811f29..55f0b2292 100644 --- a/gplx.xowa.mediawiki/src/gplx/xowa/mediawiki/includes/XomwTitle.java +++ b/gplx.xowa.mediawiki/src/gplx/xowa/mediawiki/includes/XomwTitle.java @@ -204,7 +204,7 @@ public class XomwTitle { // try { // t.secureAndSplit(); // return t; -// } catch (MalformedTitleException $ex) { +// } catch (XomwMalformedTitleException $ex) { // return null; // } // } @@ -266,14 +266,14 @@ public class XomwTitle { try { return XomwTitle.newFromTextThrow(text, defaultNamespace); - } catch (MalformedTitleException ex) { + } catch (XomwMalformedTitleException ex) { Err_.Noop(ex); return null; } } /** - * Like Title::newFromText(), but throws MalformedTitleException when the title is invalid, + * Like Title::newFromText(), but throws XomwMalformedTitleException when the title is invalid, * rather than returning null. * * The exception subclasses encode detailed information about why the title is invalid. @@ -283,7 +283,7 @@ public class XomwTitle { * @since 1.25 * @param String $text Title text to check * @param int $defaultNamespace - * @throws MalformedTitleException If the title is invalid + * @throws XomwMalformedTitleException If the title is invalid * @return Title */ public static XomwTitle newFromTextThrow(byte[] text, int defaultNamespace) { @@ -351,7 +351,7 @@ public class XomwTitle { // try { // t.secureAndSplit(); // return t; -// } catch (MalformedTitleException $ex) { +// } catch (XomwMalformedTitleException $ex) { // return null; // } // } @@ -543,7 +543,7 @@ public class XomwTitle { // try { // t.secureAndSplit(); // return t; -// } catch (MalformedTitleException $ex) { +// } catch (XomwMalformedTitleException $ex) { // return null; // } // } @@ -988,7 +988,7 @@ public class XomwTitle { } // try { - XomwMediaWikiTitleCodec formatter = getTitleFormatter(); + XomwTitleFormatter formatter = getTitleFormatter(); return formatter.getNamespaceName(this.mNamespace, this.mDbkeyform); // } catch (InvalidArgumentException $ex) { // wfDebug(__METHOD__ . ': ' . $ex.getMessage() . "\n"); @@ -3351,7 +3351,7 @@ public class XomwTitle { * namespace prefixes, sets the other forms, and canonicalizes * everything. * - * @throws MalformedTitleException On invalid titles + * @throws XomwMalformedTitleException On invalid titles * @return boolean True on success */ private boolean secureAndSplit() { @@ -3368,7 +3368,7 @@ public class XomwTitle { // @note: getTitleParser() returns a TitleParser implementation which does not have a // splitTitleString method, but the only implementation (MediaWikiTitleCodec) does XomwMediaWikiTitleCodec titleCodec = XomwMediaWikiServices.getInstance().getTitleParser(); - // MalformedTitleException can be thrown here + // XomwMalformedTitleException can be thrown here XomwMediaWikiTitleCodecParts parts = titleCodec.splitTitleString(dbkey, this.getDefaultNamespace()); // Fill fields diff --git a/gplx.xowa.mediawiki/src/gplx/xowa/mediawiki/includes/parsers/XomwLinkHolderArray.java b/gplx.xowa.mediawiki/src/gplx/xowa/mediawiki/includes/parsers/XomwLinkHolderArray.java new file mode 100644 index 000000000..e6262841c --- /dev/null +++ b/gplx.xowa.mediawiki/src/gplx/xowa/mediawiki/includes/parsers/XomwLinkHolderArray.java @@ -0,0 +1,638 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012 gnosygnu@gmail.com + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 of the +License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ +package gplx.xowa.mediawiki.includes.parsers; import gplx.*; import gplx.xowa.*; import gplx.xowa.mediawiki.*; import gplx.xowa.mediawiki.includes.*; +import gplx.langs.htmls.*; +/** +* Holder of replacement pairs for wiki links +*/ +public class XomwLinkHolderArray { + private final Bry_bfr tmp = Bry_bfr_.New(); + private final Xomw_link_holder_list internals = new Xomw_link_holder_list(); +// public $interwikis = []; +// public $size = 0; +// + /** + * @var Parser + */ + private final Xomw_parser parent; +// protected $tempIdOffset; + + /** + * @param Parser $parent + */ + public XomwLinkHolderArray(Xomw_parser parent) { + this.parent = parent; + } + +// /** +// * Reduce memory usage to reduce the impact of circular references +// */ +// public function __destruct() { +// foreach ( $this as $name => $value ) { +// unset( $this->$name ); +// } +// } +// +// /** +// * Don't serialize the parent Object, it is big, and not needed when it is +// * a parameter to mergeForeign(), which is the only application of +// * serializing at present. +// * +// * Compact the titles, only serialize the text form. +// * @return array +// */ +// public function __sleep() { +// foreach ( $this->internals as &$nsLinks ) { +// foreach ( $nsLinks as &$entry ) { +// unset( $entry['title'] ); +// } +// } +// unset( $nsLinks ); +// unset( $entry ); +// +// foreach ( $this->interwikis as &$entry ) { +// unset( $entry['title'] ); +// } +// unset( $entry ); +// +// return [ 'internals', 'interwikis', 'size' ]; +// } +// +// /** +// * Recreate the Title objects +// */ +// public function __wakeup() { +// foreach ( $this->internals as &$nsLinks ) { +// foreach ( $nsLinks as &$entry ) { +// $entry['title'] = Title::newFromText( $entry['pdbk'] ); +// } +// } +// unset( $nsLinks ); +// unset( $entry ); +// +// foreach ( $this->interwikis as &$entry ) { +// $entry['title'] = Title::newFromText( $entry['pdbk'] ); +// } +// unset( $entry ); +// } +// +// /** +// * Merge another LinkHolderArray into this one +// * @param LinkHolderArray $other +// */ +// public function merge( $other ) { +// foreach ( $other->internals as $ns => $entries ) { +// $this->size += count( $entries ); +// if ( !isset( $this->internals[$ns] ) ) { +// $this->internals[$ns] = $entries; +// } else { +// $this->internals[$ns] += $entries; +// } +// } +// $this->interwikis += $other->interwikis; +// } +// +// /** +// * Merge a LinkHolderArray from another parser instance into this one. The +// * keys will not be preserved. Any text which went with the old +// * LinkHolderArray and needs to work with the new one should be passed in +// * the $texts array. The strings in this array will have their link holders +// * converted for use in the destination link holder. The resulting array of +// * strings will be returned. +// * +// * @param LinkHolderArray $other +// * @param array $texts Array of strings +// * @return array +// */ +// public function mergeForeign( $other, $texts ) { +// $this->tempIdOffset = $idOffset = $this->parent->nextLinkID(); +// $maxId = 0; +// +// # Renumber @gplx.Internal protected links +// foreach ( $other->internals as $ns => $nsLinks ) { +// foreach ( $nsLinks as $key => $entry ) { +// $newKey = $idOffset + $key; +// $this->internals[$ns][$newKey] = $entry; +// $maxId = $newKey > $maxId ? $newKey : $maxId; +// } +// } +// $texts = preg_replace_callback( '/()/', +// [ $this, 'mergeForeignCallback' ], $texts ); +// +// # Renumber interwiki links +// foreach ( $other->interwikis as $key => $entry ) { +// $newKey = $idOffset + $key; +// $this->interwikis[$newKey] = $entry; +// $maxId = $newKey > $maxId ? $newKey : $maxId; +// } +// $texts = preg_replace_callback( '/()/', +// [ $this, 'mergeForeignCallback' ], $texts ); +// +// # Set the parent link ID to be beyond the highest used ID +// $this->parent->setLinkID( $maxId + 1 ); +// $this->tempIdOffset = null; +// return $texts; +// } +// +// /** +// * @param array $m +// * @return String +// */ +// protected function mergeForeignCallback( $m ) { +// return $m[1] . ( $m[2] + $this->tempIdOffset ) . $m[3]; +// } +// +// /** +// * Get a subset of the current LinkHolderArray which is sufficient to +// * interpret the given text. +// * @param String $text +// * @return LinkHolderArray +// */ +// public function getSubArray( $text ) { +// $sub = new LinkHolderArray( $this->parent ); +// +// # Internal links +// $pos = 0; +// while ( $pos < strlen( $text ) ) { +// if ( !preg_match( '//', +// $text, $m, PREG_OFFSET_CAPTURE, $pos ) +// ) { +// break; +// } +// $ns = $m[1][0]; +// $key = $m[2][0]; +// $sub->internals[$ns][$key] = $this->internals[$ns][$key]; +// $pos = $m[0][1] + strlen( $m[0][0] ); +// } +// +// # Interwiki links +// $pos = 0; +// while ( $pos < strlen( $text ) ) { +// if ( !preg_match( '//', $text, $m, PREG_OFFSET_CAPTURE, $pos ) ) { +// break; +// } +// $key = $m[1][0]; +// $sub->interwikis[$key] = $this->interwikis[$key]; +// $pos = $m[0][1] + strlen( $m[0][0] ); +// } +// return $sub; +// } +// +// /** +// * Returns true if the memory requirements of this Object are getting large +// * @return boolean +// */ +// public function isBig() { +// global $wgLinkHolderBatchSize; +// return $this->size > $wgLinkHolderBatchSize; +// } +// +// /** +// * Clear all stored link holders. +// * Make sure you don't have any text left using these link holders, before you call this +// */ +// public function clear() { +// $this->internals = []; +// $this->interwikis = []; +// $this->size = 0; +// } + + /** + * Make a link placeholder. The text returned can be later resolved to a real link with + * replaceLinkHolders(). This is done for two reasons: firstly to avoid further + * parsing of interwiki links, and secondly to allow all existence checks and + * article length checks (for stub links) to be bundled into a single query. + * + * @param Title $nt + * @param String $text + * @param array $query [optional] + * @param String $trail [optional] + * @param String $prefix [optional] + * @return String + */ + public void makeHolder(Bry_bfr bfr, XomwTitle nt, byte[] text, byte[][] query, byte[] trail, byte[] prefix) { + if (nt == null) { + // Fail gracefully + bfr.Add_str_a7("").Add(prefix).Add(text).Add(trail); + } + else { + // Separate the link trail from the rest of the link +// list( $inside, $trail ) = Linker::splitTrail( $trail ); + byte[] inside = Bry_.Empty; + + Xomw_link_holder_item entry = new Xomw_link_holder_item + ( nt + , tmp.Add_bry_many(prefix, text, inside).To_bry_and_clear() + , query); + + if (nt.isExternal()) { + // Use a globally unique ID to keep the objects mergable +// $key = $this->parent->nextLinkID(); +// $this->interwikis[$key] = $entry; +// $retVal = "{$trail}"; + } + else { + int key = this.parent.nextLinkID(); + this.internals.Add(key, entry); + bfr.Add(Bry__link__bgn).Add_int_variable(key).Add(Gfh_tag_.Comm_end).Add(trail); // "{$trail}"; + } + } + } +// +// /** +// * Replace link placeholders with actual links, in the buffer +// * +// * @param String $text +// */ +// public function replace( &$text ) { +// $this->replaceInternal( $text ); +// $this->replaceInterwiki( $text ); +// } +// +// /** +// * Replace @gplx.Internal protected links +// * @param String $text +// */ +// protected function replaceInternal( &$text ) { +// if ( !$this->internals ) { +// return; +// } +// +// global $wgContLang; +// +// $colours = []; +// $linkCache = LinkCache::singleton(); +// $output = $this->parent->getOutput(); +// $linkRenderer = $this->parent->getLinkRenderer(); +// +// $dbr = wfGetDB( DB_REPLICA ); +// +// # Sort by namespace +// ksort( $this->internals ); +// +// $linkcolour_ids = []; +// +// # Generate query +// $lb = new LinkBatch(); +// $lb->setCaller( __METHOD__ ); +// +// foreach ( $this->internals as $ns => $entries ) { +// foreach ( $entries as $entry ) { +// /** @var Title $title */ +// $title = $entry['title']; +// $pdbk = $entry['pdbk']; +// +// # Skip invalid entries. +// # Result will be ugly, but prevents crash. +// if ( is_null( $title ) ) { +// continue; +// } +// +// # Check if it's a static known link, e.g. interwiki +// if ( $title->isAlwaysKnown() ) { +// $colours[$pdbk] = ''; +// } elseif ( $ns == NS_SPECIAL ) { +// $colours[$pdbk] = 'new'; +// } else { +// $id = $linkCache->getGoodLinkID( $pdbk ); +// if ( $id != 0 ) { +// $colours[$pdbk] = $linkRenderer->getLinkClasses( $title ); +// $output->addLink( $title, $id ); +// $linkcolour_ids[$id] = $pdbk; +// } elseif ( $linkCache->isBadLink( $pdbk ) ) { +// $colours[$pdbk] = 'new'; +// } else { +// # Not in the link cache, add it to the query +// $lb->addObj( $title ); +// } +// } +// } +// } +// if ( !$lb->isEmpty() ) { +// $fields = array_merge( +// LinkCache::getSelectFields(), +// [ 'page_namespace', 'page_title' ] +// ); +// +// $res = $dbr->select( +// 'page', +// $fields, +// $lb->constructSet( 'page', $dbr ), +// __METHOD__ +// ); +// +// # Fetch data and form into an associative array +// # non-existent = broken +// foreach ( $res as $s ) { +// $title = Title::makeTitle( $s->page_namespace, $s->page_title ); +// $pdbk = $title->getPrefixedDBkey(); +// $linkCache->addGoodLinkObjFromRow( $title, $s ); +// $output->addLink( $title, $s->page_id ); +// $colours[$pdbk] = $linkRenderer->getLinkClasses( $title ); +// // add id to the extension todolist +// $linkcolour_ids[$s->page_id] = $pdbk; +// } +// unset( $res ); +// } +// if ( count( $linkcolour_ids ) ) { +// // pass an array of page_ids to an extension +// Hooks::run( 'GetLinkColours', [ $linkcolour_ids, &$colours ] ); +// } +// +// # Do a second query for different language variants of links and categories +// if ( $wgContLang->hasVariants() ) { +// $this->doVariants( $colours ); +// } +// +// # Construct search and replace arrays +// $replacePairs = []; +// foreach ( $this->internals as $ns => $entries ) { +// foreach ( $entries as $index => $entry ) { +// $pdbk = $entry['pdbk']; +// $title = $entry['title']; +// $query = isset( $entry['query'] ) ? $entry['query'] : []; +// $key = "$ns:$index"; +// $searchkey = ""; +// $displayText = $entry['text']; +// if ( isset( $entry['selflink'] ) ) { +// $replacePairs[$searchkey] = Linker::makeSelfLinkObj( $title, $displayText, $query ); +// continue; +// } +// if ( $displayText === '' ) { +// $displayText = null; +// } else { +// $displayText = new HtmlArmor( $displayText ); +// } +// if ( !isset( $colours[$pdbk] ) ) { +// $colours[$pdbk] = 'new'; +// } +// $attribs = []; +// if ( $colours[$pdbk] == 'new' ) { +// $linkCache->addBadLinkObj( $title ); +// $output->addLink( $title, 0 ); +// $link = $linkRenderer->makeBrokenLink( +// $title, $displayText, $attribs, $query +// ); +// } else { +// $link = $linkRenderer->makePreloadedLink( +// $title, $displayText, $colours[$pdbk], $attribs, $query +// ); +// } +// +// $replacePairs[$searchkey] = $link; +// } +// } +// $replacer = new HashtableReplacer( $replacePairs, 1 ); +// +// # Do the thing +// $text = preg_replace_callback( +// '/()/', +// $replacer->cb(), +// $text +// ); +// } +// +// /** +// * Replace interwiki links +// * @param String $text +// */ +// protected function replaceInterwiki( &$text ) { +// if ( empty( $this->interwikis ) ) { +// return; +// } +// +// # Make interwiki link HTML +// $output = $this->parent->getOutput(); +// $replacePairs = []; +// $linkRenderer = $this->parent->getLinkRenderer(); +// foreach ( $this->interwikis as $key => $link ) { +// $replacePairs[$key] = $linkRenderer->makeLink( +// $link['title'], +// new HtmlArmor( $link['text'] ) +// ); +// $output->addInterwikiLink( $link['title'] ); +// } +// $replacer = new HashtableReplacer( $replacePairs, 1 ); +// +// $text = preg_replace_callback( +// '//', +// $replacer->cb(), +// $text ); +// } +// +// /** +// * Modify $this->internals and $colours according to language variant linking rules +// * @param array $colours +// */ +// protected function doVariants( &$colours ) { +// global $wgContLang; +// $linkBatch = new LinkBatch(); +// $variantMap = []; // maps $pdbkey_Variant => $keys (of link holders) +// $output = $this->parent->getOutput(); +// $linkCache = LinkCache::singleton(); +// $titlesToBeConverted = ''; +// $titlesAttrs = []; +// +// // Concatenate titles to a single String, thus we only need auto convert the +// // single String to all variants. This would improve parser's performance +// // significantly. +// foreach ( $this->internals as $ns => $entries ) { +// if ( $ns == NS_SPECIAL ) { +// continue; +// } +// foreach ( $entries as $index => $entry ) { +// $pdbk = $entry['pdbk']; +// // we only deal with new links (in its first query) +// if ( !isset( $colours[$pdbk] ) || $colours[$pdbk] === 'new' ) { +// $titlesAttrs[] = [ $index, $entry['title'] ]; +// // separate titles with \0 because it would never appears +// // in a valid title +// $titlesToBeConverted .= $entry['title']->getText() . "\0"; +// } +// } +// } +// +// // Now do the conversion and explode String to text of titles +// $titlesAllVariants = $wgContLang->autoConvertToAllVariants( rtrim( $titlesToBeConverted, "\0" ) ); +// $allVariantsName = array_keys( $titlesAllVariants ); +// foreach ( $titlesAllVariants as &$titlesVariant ) { +// $titlesVariant = explode( "\0", $titlesVariant ); +// } +// +// // Then add variants of links to link batch +// $parentTitle = $this->parent->getTitle(); +// foreach ( $titlesAttrs as $i => $attrs ) { +// /** @var Title $title */ +// list( $index, $title ) = $attrs; +// $ns = $title->getNamespace(); +// $text = $title->getText(); +// +// foreach ( $allVariantsName as $variantName ) { +// $textVariant = $titlesAllVariants[$variantName][$i]; +// if ( $textVariant === $text ) { +// continue; +// } +// +// $variantTitle = Title::makeTitle( $ns, $textVariant ); +// +// // Self-link checking for mixed/different variant titles. At this point, we +// // already know the exact title does not exist, so the link cannot be to a +// // variant of the current title that exists as a separate page. +// if ( $variantTitle->equals( $parentTitle ) && !$title->hasFragment() ) { +// $this->internals[$ns][$index]['selflink'] = true; +// continue 2; +// } +// +// $linkBatch->addObj( $variantTitle ); +// $variantMap[$variantTitle->getPrefixedDBkey()][] = "$ns:$index"; +// } +// } +// +// // process categories, check if a category exists in some variant +// $categoryMap = []; // maps $category_variant => $category (dbkeys) +// $varCategories = []; // category replacements oldDBkey => newDBkey +// foreach ( $output->getCategoryLinks() as $category ) { +// $categoryTitle = Title::makeTitleSafe( NS_CATEGORY, $category ); +// $linkBatch->addObj( $categoryTitle ); +// $variants = $wgContLang->autoConvertToAllVariants( $category ); +// foreach ( $variants as $variant ) { +// if ( $variant !== $category ) { +// $variantTitle = Title::makeTitleSafe( NS_CATEGORY, $variant ); +// if ( is_null( $variantTitle ) ) { +// continue; +// } +// $linkBatch->addObj( $variantTitle ); +// $categoryMap[$variant] = [ $category, $categoryTitle ]; +// } +// } +// } +// +// if ( !$linkBatch->isEmpty() ) { +// // construct query +// $dbr = wfGetDB( DB_REPLICA ); +// $fields = array_merge( +// LinkCache::getSelectFields(), +// [ 'page_namespace', 'page_title' ] +// ); +// +// $varRes = $dbr->select( 'page', +// $fields, +// $linkBatch->constructSet( 'page', $dbr ), +// __METHOD__ +// ); +// +// $linkcolour_ids = []; +// $linkRenderer = $this->parent->getLinkRenderer(); +// +// // for each found variants, figure out link holders and replace +// foreach ( $varRes as $s ) { +// $variantTitle = Title::makeTitle( $s->page_namespace, $s->page_title ); +// $varPdbk = $variantTitle->getPrefixedDBkey(); +// $vardbk = $variantTitle->getDBkey(); +// +// $holderKeys = []; +// if ( isset( $variantMap[$varPdbk] ) ) { +// $holderKeys = $variantMap[$varPdbk]; +// $linkCache->addGoodLinkObjFromRow( $variantTitle, $s ); +// $output->addLink( $variantTitle, $s->page_id ); +// } +// +// // loop over link holders +// foreach ( $holderKeys as $key ) { +// list( $ns, $index ) = explode( ':', $key, 2 ); +// $entry =& $this->internals[$ns][$index]; +// $pdbk = $entry['pdbk']; +// +// if ( !isset( $colours[$pdbk] ) || $colours[$pdbk] === 'new' ) { +// // found link in some of the variants, replace the link holder data +// $entry['title'] = $variantTitle; +// $entry['pdbk'] = $varPdbk; +// +// // set pdbk and colour +// $colours[$varPdbk] = $linkRenderer->getLinkClasses( $variantTitle ); +// $linkcolour_ids[$s->page_id] = $pdbk; +// } +// } +// +// // check if the Object is a variant of a category +// if ( isset( $categoryMap[$vardbk] ) ) { +// list( $oldkey, $oldtitle ) = $categoryMap[$vardbk]; +// if ( !isset( $varCategories[$oldkey] ) && !$oldtitle->exists() ) { +// $varCategories[$oldkey] = $vardbk; +// } +// } +// } +// Hooks::run( 'GetLinkColours', [ $linkcolour_ids, &$colours ] ); +// +// // rebuild the categories in original order (if there are replacements) +// if ( count( $varCategories ) > 0 ) { +// $newCats = []; +// $originalCats = $output->getCategories(); +// foreach ( $originalCats as $cat => $sortkey ) { +// // make the replacement +// if ( array_key_exists( $cat, $varCategories ) ) { +// $newCats[$varCategories[$cat]] = $sortkey; +// } else { +// $newCats[$cat] = $sortkey; +// } +// } +// $output->setCategoryLinks( $newCats ); +// } +// } +// } +// +// /** +// * Replace link placeholders with plain text of links +// * (not HTML-formatted). +// * +// * @param String $text +// * @return String +// */ +// public function replaceText( $text ) { +// $text = preg_replace_callback( +// '//', +// [ &$this, 'replaceTextCallback' ], +// $text ); +// +// return $text; +// } +// +// /** +// * Callback for replaceText() +// * +// * @param array $matches +// * @return String +// * @private +// */ +// public function replaceTextCallback( $matches ) { +// $type = $matches[1]; +// $key = $matches[2]; +// if ( $type == 'LINK' ) { +// list( $ns, $index ) = explode( ':', $key, 2 ); +// if ( isset( $this->internals[$ns][$index]['text'] ) ) { +// return $this->internals[$ns][$index]['text']; +// } +// } elseif ( $type == 'IWLINK' ) { +// if ( isset( $this->interwikis[$key]['text'] ) ) { +// return $this->interwikis[$key]['text']; +// } +// } +// return $matches[0]; +// } + private static final byte[] Bry__link__bgn = Bry_.new_a7("