diff --git a/gplx.xowa.mediawiki/src/gplx/core/lists/HashByInt.java b/gplx.xowa.mediawiki/src/gplx/core/lists/HashByInt.java index bd952dd71..0df272b00 100644 --- a/gplx.xowa.mediawiki/src/gplx/core/lists/HashByInt.java +++ b/gplx.xowa.mediawiki/src/gplx/core/lists/HashByInt.java @@ -50,7 +50,7 @@ public class HashByInt { int len = hash.Len(); for (int i = 0; i < len; i++) { HashByIntItem item = (HashByIntItem)hash.Get_at(i); - rv.Add(item.key, item); + rv.Add(item.key, item.val); } return rv; } 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 index e6262841c..5d97d40cc 100644 --- a/gplx.xowa.mediawiki/src/gplx/xowa/mediawiki/includes/parsers/XomwLinkHolderArray.java +++ b/gplx.xowa.mediawiki/src/gplx/xowa/mediawiki/includes/parsers/XomwLinkHolderArray.java @@ -17,15 +17,19 @@ 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.*; +import gplx.xowa.mediawiki.includes.htmls.*; +import gplx.xowa.mediawiki.includes.linkers.*; /** * 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(); + private final XomwLinkHolderList internals = new XomwLinkHolderList(); // public $interwikis = []; -// public $size = 0; -// + // private int size = 0; + + private final Bry_bfr tmp = Bry_bfr_.New(); + private final Xomw_atr_mgr extraAtrs = new Xomw_atr_mgr(); + private final Xomw_qry_mgr query = new Xomw_qry_mgr(); /** * @var Parser */ @@ -39,72 +43,72 @@ public class XomwLinkHolderArray { 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 ); -// } -// + /** + * 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; +// this.size += count( $entries ); +// if ( !isset( this.internals[$ns] ) ) { +// this.internals[$ns] = $entries; // } else { -// $this->internals[$ns] += $entries; +// this.internals[$ns] += $entries; // } // } -// $this->interwikis += $other->interwikis; +// this.interwikis += $other->interwikis; // } // // /** @@ -120,14 +124,14 @@ public class XomwLinkHolderArray { // * @return array // */ // public function mergeForeign( $other, $texts ) { -// $this->tempIdOffset = $idOffset = $this->parent->nextLinkID(); +// 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; +// this.internals[$ns][$newKey] = $entry; // $maxId = $newKey > $maxId ? $newKey : $maxId; // } // } @@ -137,15 +141,15 @@ public class XomwLinkHolderArray { // # Renumber interwiki links // foreach ( $other->interwikis as $key => $entry ) { // $newKey = $idOffset + $key; -// $this->interwikis[$newKey] = $entry; +// 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; +// this.parent->setLinkID( $maxId + 1 ); +// this.tempIdOffset = null; // return $texts; // } // @@ -154,7 +158,7 @@ public class XomwLinkHolderArray { // * @return String // */ // protected function mergeForeignCallback( $m ) { -// return $m[1] . ( $m[2] + $this->tempIdOffset ) . $m[3]; +// return $m[1] . ( $m[2] + this.tempIdOffset ) . $m[3]; // } // // /** @@ -164,7 +168,7 @@ public class XomwLinkHolderArray { // * @return LinkHolderArray // */ // public function getSubArray( $text ) { -// $sub = new LinkHolderArray( $this->parent ); +// $sub = new LinkHolderArray( this.parent ); // // # Internal links // $pos = 0; @@ -176,7 +180,7 @@ public class XomwLinkHolderArray { // } // $ns = $m[1][0]; // $key = $m[2][0]; -// $sub->internals[$ns][$key] = $this->internals[$ns][$key]; +// $sub->internals[$ns][$key] = this.internals[$ns][$key]; // $pos = $m[0][1] + strlen( $m[0][0] ); // } // @@ -187,7 +191,7 @@ public class XomwLinkHolderArray { // break; // } // $key = $m[1][0]; -// $sub->interwikis[$key] = $this->interwikis[$key]; +// $sub->interwikis[$key] = this.interwikis[$key]; // $pos = $m[0][1] + strlen( $m[0][0] ); // } // return $sub; @@ -199,19 +203,19 @@ public class XomwLinkHolderArray { // */ // 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; +// 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 void clear() { + this.internals.Clear();// +// 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 @@ -235,15 +239,15 @@ public class XomwLinkHolderArray { // list( $inside, $trail ) = Linker::splitTrail( $trail ); byte[] inside = Bry_.Empty; - Xomw_link_holder_item entry = new Xomw_link_holder_item + XomwLinkHolderItem entry = new XomwLinkHolderItem ( 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; +// $key = this.parent->nextLinkID(); +// this.interwikis[$key] = $entry; // $retVal = "{$trail}"; } else { @@ -253,37 +257,38 @@ public class XomwLinkHolderArray { } } } -// -// /** -// * Replace link placeholders with actual links, in the buffer -// * -// * @param String $text -// */ -// public function replace( &$text ) { -// $this->replaceInternal( $text ); + + /** + * Replace link placeholders with actual links, in the buffer + * + * @param String $text + */ + public void Replace(Xomw_parser_ctx pctx, Xomw_parser_bfr pbfr) { + this.replaceInternal(pbfr); // $this->replaceInterwiki( $text ); -// } -// -// /** -// * Replace @gplx.Internal protected links -// * @param String $text -// */ -// protected function replaceInternal( &$text ) { -// if ( !$this->internals ) { -// return; -// } -// + } + + /** + * Replace @gplx.Internal protected links + * @param String $text + */ + private void replaceInternal(Xomw_parser_bfr pbfr) { + if (internals.Len() == 0) { + return; + } + + // SKIP:Replace_internals does db lookup to identify redlinks; // global $wgContLang; // // $colours = []; // $linkCache = LinkCache::singleton(); -// $output = $this->parent->getOutput(); -// $linkRenderer = $this->parent->getLinkRenderer(); +// $output = this.parent->getOutput(); + Xomw_link_renderer linkRenderer = this.parent.getLinkRenderer(); // // $dbr = wfGetDB( DB_REPLICA ); // // # Sort by namespace -// ksort( $this->internals ); +// ksort( this.internals ); // // $linkcolour_ids = []; // @@ -291,6 +296,378 @@ public class XomwLinkHolderArray { // $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 + Bry_bfr src_bfr = pbfr.Src(); + byte[] src = src_bfr.Bfr(); + int src_bgn = 0; + int src_end = src_bfr.Len(); + Bry_bfr bfr = pbfr.Trg(); + pbfr.Switch(); + + int cur = src_bgn; + int prv = 0; + while (true) { + int link_bgn = Bry_find_.Find_fwd(src, Bry__link__bgn, cur, src_end); + if (link_bgn == Bry_find_.Not_found) { + bfr.Add_mid(src, prv, src_end); + break; + } + int key_bgn = link_bgn + Bry__link__bgn.length; + int key_end = Bry_find_.Find_fwd_while_num(src, key_bgn, src_end); + int link_key = Bry_.To_int_or(src, key_bgn, key_end, -1); + XomwLinkHolderItem item = internals.Get_by(link_key); + +// $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; +// } +// } + bfr.Add_mid(src, prv, link_bgn); + linkRenderer.Make_preloaded_link(bfr, item.Title(), item.Text(), Bry_.Empty, extraAtrs, query.Clear()); + cur = key_end + Gfh_tag_.Comm_end_len; + prv = cur; + } +// $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 void Replace_internal__db() { +// // Generate query +// $lb = new LinkBatch(); +// $lb->setCaller( __METHOD__ ); +// // foreach ( $this->internals as $ns => $entries ) { // foreach ( $entries as $entry ) { // /** @var Title $title */ @@ -358,281 +735,42 @@ public class XomwLinkHolderArray { // 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]; // } + public void Test__add(XomwTitle ttl, byte[] capt) { + int key = parent.nextLinkID(); + XomwLinkHolderItem item = new XomwLinkHolderItem(ttl, capt, Bry_.Ary_empty); + internals.Add(key, item); + } private static final byte[] Bry__link__bgn = Bry_.new_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); - - boolean is_external = false; // $nt->isExternal() - if (is_external) { - // Use a globally unique ID to keep the objects mergable -// $key = $this->parent->nextLinkID(); -// $this->interwikis[$key] = $entry; -// $retVal = "{$trail}"; - } - else { - int key = link_id++; - internals.Add(key, entry); - bfr.Add(Bry__link__bgn).Add_int_variable(key).Add(Gfh_tag_.Comm_end).Add(trail); // "{$trail}"; - } - } - } - public void Test__add(XomwTitle ttl, byte[] capt) { - int key = link_id++; - Xomw_link_holder_item item = new Xomw_link_holder_item(ttl, capt, Bry_.Ary_empty); - internals.Add(key, item); - } - public void Replace(Xomw_parser_ctx pctx, Xomw_parser_bfr pbfr) { - this.Replace_internal(pbfr); -// $this->replaceInterwiki( $text ); - } - private void Replace_internal(Xomw_parser_bfr pbfr) { - if (internals.Len() == 0) - return; - -// $colours = []; -// $linkCache = LinkCache::singleton(); -// $output = $this->parent->getOutput(); -// $linkRenderer = $this->parent->getLinkRenderer(); - -// $linkcolour_ids = []; - - // SKIP:Replace_internals does db lookup to identify redlinks; - - // Construct search and replace arrays - Bry_bfr src_bfr = pbfr.Src(); - byte[] src = src_bfr.Bfr(); - int src_bgn = 0; - int src_end = src_bfr.Len(); - Bry_bfr bfr = pbfr.Trg(); - pbfr.Switch(); - - int cur = src_bgn; - int prv = 0; - while (true) { - int link_bgn = Bry_find_.Find_fwd(src, Bry__link__bgn, cur, src_end); - if (link_bgn == Bry_find_.Not_found) { - bfr.Add_mid(src, prv, src_end); - break; - } - int key_bgn = link_bgn + Bry__link__bgn.length; - int key_end = Bry_find_.Find_fwd_while_num(src, key_bgn, src_end); - int link_key = Bry_.To_int_or(src, key_bgn, key_end, -1); - Xomw_link_holder_item item = internals.Get_by(link_key); - -// $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 -// ); -// } - - bfr.Add_mid(src, prv, link_bgn); - link_renderer.Make_preloaded_link(bfr, item.Title(), item.Text(), Bry_.Empty, extra_atrs, query.Clear()); - cur = key_end + Gfh_tag_.Comm_end_len; - prv = cur; - } - } -// private void Replace_internal__db() { -// // 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 ); -// } -// } - - private static final byte[] Bry__link__bgn = Bry_.new_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; +// +// XomwLinkHolderItem entry = new XomwLinkHolderItem(nt, tmp.Add_bry_many(prefix, text, inside).To_bry_and_clear(), query); +// +// boolean is_external = false; // $nt->isExternal() +// if (is_external) { +// // Use a globally unique ID to keep the objects mergable +//// $key = $this->parent->nextLinkID(); +//// $this->interwikis[$key] = $entry; +//// $retVal = "{$trail}"; +// } +// else { +// int key = link_id++; +// internals.Add(key, entry); +// bfr.Add(Bry__link__bgn).Add_int_variable(key).Add(Gfh_tag_.Comm_end).Add(trail); // "{$trail}"; +// } +// } +// } +// public void Test__add(XomwTitle ttl, byte[] capt) { +// int key = link_id++; +// XomwLinkHolderItem item = new XomwLinkHolderItem(ttl, capt, Bry_.Ary_empty); +// internals.Add(key, item); +// } +// public void Replace(Xomw_parser_ctx pctx, Xomw_parser_bfr pbfr) { +// this.Replace_internal(pbfr); +//// $this->replaceInterwiki( $text ); +// } +// private void Replace_internal(Xomw_parser_bfr pbfr) { +// if (internals.Len() == 0) +// return; +// +//// $colours = []; +//// $linkCache = LinkCache::singleton(); +//// $output = $this->parent->getOutput(); +//// $linkRenderer = $this->parent->getLinkRenderer(); +// +//// $linkcolour_ids = []; +// +// // SKIP:Replace_internals does db lookup to identify redlinks; +// +// // Construct search and replace arrays +// Bry_bfr src_bfr = pbfr.Src(); +// byte[] src = src_bfr.Bfr(); +// int src_bgn = 0; +// int src_end = src_bfr.Len(); +// Bry_bfr bfr = pbfr.Trg(); +// pbfr.Switch(); +// +// int cur = src_bgn; +// int prv = 0; +// while (true) { +// int link_bgn = Bry_find_.Find_fwd(src, Bry__link__bgn, cur, src_end); +// if (link_bgn == Bry_find_.Not_found) { +// bfr.Add_mid(src, prv, src_end); +// break; +// } +// int key_bgn = link_bgn + Bry__link__bgn.length; +// int key_end = Bry_find_.Find_fwd_while_num(src, key_bgn, src_end); +// int link_key = Bry_.To_int_or(src, key_bgn, key_end, -1); +// XomwLinkHolderItem item = internals.Get_by(link_key); +// +//// $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 +//// ); +//// } +// +// bfr.Add_mid(src, prv, link_bgn); +// link_renderer.Make_preloaded_link(bfr, item.Title(), item.Text(), Bry_.Empty, extra_atrs, query.Clear()); +// cur = key_end + Gfh_tag_.Comm_end_len; +// prv = cur; +// } +// } +// +// private static final byte[] Bry__link__bgn = Bry_.new_a7("