diff --git a/gplx.xowa.mediawiki/src/gplx/xowa/mediawiki/XophpString.java b/gplx.xowa.mediawiki/src/gplx/xowa/mediawiki/XophpString.java index 329c45164..2618fb19c 100644 --- a/gplx.xowa.mediawiki/src/gplx/xowa/mediawiki/XophpString.java +++ b/gplx.xowa.mediawiki/src/gplx/xowa/mediawiki/XophpString.java @@ -153,4 +153,9 @@ public class XophpString { public static byte[] str_replace(byte[] find, byte[] repl, byte[] src) { return Bry_.Replace(src, find, repl); } + public static byte[] strstr(byte[] src, byte[] find) { + int pos = Bry_find_.Find_fwd(src, find); + return pos == Bry_find_.Not_found ? null : Bry_.Mid(src, pos, src.length); + } + public static int strlen(byte[] src) {return src.length;} } diff --git a/gplx.xowa.mediawiki/src/gplx/xowa/mediawiki/includes/XomwMessage.java b/gplx.xowa.mediawiki/src/gplx/xowa/mediawiki/includes/XomwMessage.java index 6be2e6b46..6e103c060 100644 --- a/gplx.xowa.mediawiki/src/gplx/xowa/mediawiki/includes/XomwMessage.java +++ b/gplx.xowa.mediawiki/src/gplx/xowa/mediawiki/includes/XomwMessage.java @@ -14,9 +14,10 @@ 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.mediawiki.includes; import gplx.*; import gplx.xowa.*; import gplx.xowa.mediawiki.*; +import gplx.xowa.mediawiki.languages.*; /** * The Message cls provides methods which fulfil two basic services: -* - fetching interface messages +* - fetching interfaceIsUserLang messages * - processing messages into a variety of formats * * First implemented with MediaWiki 1.17, the Message cls is intended to @@ -45,7 +46,7 @@ package gplx.xowa.mediawiki.includes; import gplx.*; import gplx.xowa.*; import * a method. Some of them return a Message instance too so you can chain them. * You will find below several examples of wfMessage() usage. * -* Fetching a message text for interface message: +* Fetching a message text for interfaceIsUserLang message: * * @code * $button = Xml::button( @@ -105,7 +106,7 @@ package gplx.xowa.mediawiki.includes; import gplx.*; import gplx.xowa.*; import * ->plain(); * @endcode * -* @note You can parse the text only in the content or interface languages +* @note You can parse the text only in the content or interfaceIsUserLang languages * * @section message_compare_old Comparison with old wfMsg* functions: * @@ -151,112 +152,123 @@ package gplx.xowa.mediawiki.includes; import gplx.*; import gplx.xowa.*; import */ public class XomwMessage { // /** Use message text as-is */ -// static final FORMAT_PLAIN = 'plain'; -// /** Use normal wikitext -> HTML parsing (the result will be wrapped in a block-level HTML tag) */ -// static final FORMAT_BLOCK_PARSE = 'block-parse'; -// /** Use normal wikitext -> HTML parsing but strip the block-level wrapper */ -// static final FORMAT_PARSE = 'parse'; -// /** Transform {{..}} constructs but don't transform to HTML */ -// static final FORMAT_TEXT = 'text'; -// /** Transform {{..}} constructs, HTML-escape the result */ -// static final FORMAT_ESCAPED = 'escaped'; -// -// /** -// * Mapping from Message::listParam() types to Language methods. -// * @var array -// */ -// protected static $listTypeMap = [ -// 'comma' => 'commaList', -// 'semicolon' => 'semicolonList', -// 'pipe' => 'pipeList', -// 'text' => 'listToText', -// ]; -// -// /** -// * In which language to get this message. True, which is the default, -// * means the current user language, false content language. -// * -// * @var boolean -// */ -// protected $interface = true; -// -// /** -// * In which language to get this message. Overrides the $interface setting. -// * -// * @var Language|boolean Explicit language Object, or false for user language -// */ -// protected $language = false; -// -// /** -// * @var String The message key. If $keysToTry has more than one element, -// * this may change to one of the keys to try when fetching the message text. -// */ -// protected $key; -// -// /** -// * @var String... List of keys to try when fetching the message. -// */ -// protected $keysToTry; -// -// /** -// * @var array List of parameters which will be substituted into the message. -// */ -// protected $parameters = []; -// -// /** -// * @var String -// * @deprecated -// */ -// protected $format = 'parse'; -// -// /** -// * @var boolean Whether database can be used. -// */ -// protected $useDatabase = true; -// -// /** -// * @var Title Title Object to use as context. -// */ -// protected $title = null; -// -// /** -// * @var Content Content Object representing the message. -// */ + private static final int FORMAT_PLAIN = 0; // 'plain'; + /** Use normal wikitext -> HTML parsing (the result will be wrapped in a block-level HTML tag) */ + private static final int FORMAT_BLOCK_PARSE = 1; // 'block-parse'; + /** Use normal wikitext -> HTML parsing but strip the block-level wrapper */ + private static final int FORMAT_PARSE = 2; // 'parse'; + /** Transform {{..}} constructs but don't transform to HTML */ + private static final int FORMAT_TEXT = 3; // 'text'; + /** Transform {{..}} constructs, HTML-escape the result */ + private static final int FORMAT_ESCAPED = 4; // 'escaped'; + + /** + * Mapping from Message::listParam() types to Language methods. + * @var array + */ + private static final Hash_adp listTypeMap = Hash_adp_.New() + .Add_and_more("comma", "commaList") + .Add_and_more("semicolon", "semicolonList") + .Add_and_more("pipe", "pipeList") + .Add_and_more("text", "listToText") + ; + + /** + * In which language to get this message. True, which is the default, + * means the current user language, false content language. + * + * @var boolean + */ + private boolean interfaceIsUserLang = true; + + /** + * In which language to get this message. Overrides the $interfaceIsUserLang setting. + * + * @var Language|boolean Explicit language Object, or false for user language + */ + private XomwLanguage language; + + /** + * @var String The message key. If $keysToTry has more than one element, + * this may change to one of the keys to try when fetching the message text. + */ + private String key; + + /** + * @var String... List of keys to try when fetching the message. + */ + private String[] keysToTry; + + /** + * @var array List of parameters which will be substituted into the message. + */ + private List_adp parameters = List_adp_.New(); + + /** + * @var String + * @deprecated + */ + private int format = FORMAT_PARSE; + + /** + * @var boolean Whether database can be used. + */ + private boolean useDatabase = true; + + /** + * @var Title Title Object to use as context. + */ + private XomwTitle title = null; + + /** + * @var Content Content Object representing the message. + */ // protected $content = null; -// -// /** -// * @var String -// */ -// protected $message; -// + + /** + * @var String + */ + private byte[] message; + + public void CompilerAppeasement() { + this.language = null; this.key = null; this.keysToTry = null; this.message = null; + Tfds.Write(interfaceIsUserLang, language, key, keysToTry, format, useDatabase, title, message, listTypeMap, parameters); + Tfds.Write(FORMAT_BLOCK_PARSE, FORMAT_ESCAPED, FORMAT_PARSE, FORMAT_PLAIN, FORMAT_TEXT); + } + public XomwMessage(byte[] textBry) { + this.textBry = textBry; + } + public byte[] text() {return textBry;} private byte[] textBry; + public byte[] escaped() {throw Err_.new_unimplemented();} + // /** // * @since 1.17 -// * @param String|String[]|MessageSpecifier $key Message key, or array of +// * @param String|String[]|MessageSpecifier key Message key, or array of // * message keys to try and use the first non-empty message for, or a // * MessageSpecifier to copy from. // * @param array $params Message parameters. // * @param Language $language [optional] Language to use (defaults to current user language). // * @throws InvalidArgumentException // */ -// public function __construct( $key, $params = [], Language $language = null ) { -// if ( $key instanceof MessageSpecifier ) { +// public function __construct( key, $params = [], Language $language = null ) { +// if ( key instanceof MessageSpecifier ) { // if ( $params ) { // throw new InvalidArgumentException( -// '$params must be empty if $key is a MessageSpecifier' +// '$params must be empty if key is a MessageSpecifier' // ); // } -// $params = $key->getParams(); -// $key = $key->getKey(); +// $params = key->getParams(); +// key = key->getKey(); // } // -// if ( !is_string( $key ) && !is_array( $key ) ) { -// throw new InvalidArgumentException( '$key must be a String or an array' ); +// if ( !is_string( key ) && !is_array( key ) ) { +// throw new InvalidArgumentException( 'key must be a String or an array' ); // } // -// $this->keysToTry = (array)$key; +// $this->keysToTry = (array)key; // // if ( empty( $this->keysToTry ) ) { -// throw new InvalidArgumentException( '$key must not be an empty list' ); +// throw new InvalidArgumentException( 'key must not be an empty list' ); // } // // $this->key = reset( $this->keysToTry ); @@ -274,7 +286,7 @@ public class XomwMessage { // */ // public function serialize() { // return serialize( [ -// 'interface' => $this->interface, +// 'interfaceIsUserLang' => $this->interfaceIsUserLang, // 'language' => $this->language ? $this->language->getCode() : false, // 'key' => $this->key, // 'keysToTry' => $this->keysToTry, @@ -292,7 +304,7 @@ public class XomwMessage { // */ // public function unserialize( $serialized ) { // $data = unserialize( $serialized ); -// $this->interface = $data['interface']; +// $this->interfaceIsUserLang = $data['interfaceIsUserLang']; // $this->key = $data['key']; // $this->keysToTry = $data['keysToTry']; // $this->parameters = $data['parameters']; @@ -380,15 +392,15 @@ public class XomwMessage { // * // * @since 1.17 // * -// * @param String|String[]|MessageSpecifier $key +// * @param String|String[]|MessageSpecifier key // * @param mixed $param,... Parameters as strings. // * // * @return Message // */ -// public static function newFromKey( $key /*...*/ ) { +// public static function newFromKey( key /*...*/ ) { // $params = func_get_args(); // array_shift( $params ); -// return new self( $key, $params ); +// return new self( key, $params ); // } // // /** @@ -464,16 +476,16 @@ public class XomwMessage { // public function getTitle() { // global $wgContLang, $wgForceUIMsgAsContentMsg; // -// $title = $this->key; +// title = $this->key; // if ( // !$this->language->equals( $wgContLang ) // && in_array( $this->key, (array)$wgForceUIMsgAsContentMsg ) // ) { // $code = $this->language->getCode(); -// $title .= '/' . $code; +// title .= '/' . $code; // } // -// return Title::makeTitle( NS_MEDIAWIKI, $wgContLang->ucfirst( strtr( $title, ' ', '_' ) ) ); +// return Title::makeTitle( NS_MEDIAWIKI, $wgContLang->ucfirst( strtr( title, ' ', '_' ) ) ); // } // // /** @@ -497,8 +509,8 @@ public class XomwMessage { // if ( $args[0] === [] ) { // $args = []; // } else { -// foreach ( $args[0] as $key => $value ) { -// if ( is_int( $key ) ) { +// foreach ( $args[0] as key => $value ) { +// if ( is_int( key ) ) { // $args = $args[0]; // break; // } @@ -702,7 +714,7 @@ public class XomwMessage { // public function setContext( IContextSource $context ) { // $this->inLanguage( $context->getLanguage() ); // $this->title( $context->getTitle() ); -// $this->interface = true; +// $this->interfaceIsUserLang = true; // // return $this; // } @@ -710,7 +722,7 @@ public class XomwMessage { // /** // * Request the message in any language that is supported. // * -// * As a side effect interface message status is unconditionally +// * As a side effect interfaceIsUserLang message status is unconditionally // * turned off. // * // * @since 1.17 @@ -734,7 +746,7 @@ public class XomwMessage { // ); // } // $this->message = null; -// $this->interface = false; +// $this->interfaceIsUserLang = false; // return $this; // } @@ -760,17 +772,17 @@ public class XomwMessage { } // /** -// * Allows manipulating the interface message flag directly. +// * Allows manipulating the interfaceIsUserLang message flag directly. // * Can be used to restore the flag after setting a language. // * // * @since 1.20 // * -// * @param boolean $interface +// * @param boolean $interfaceIsUserLang // * // * @return Message $this // */ -// public function setInterfaceMessageFlag( $interface ) { -// $this->interface = (boolean)$interface; +// public function setInterfaceMessageFlag( $interfaceIsUserLang ) { +// $this->interfaceIsUserLang = (boolean)$interfaceIsUserLang; // return $this; // } // @@ -794,12 +806,12 @@ public class XomwMessage { // * // * @since 1.18 // * -// * @param Title $title +// * @param Title title // * // * @return Message $this // */ -// public function title( $title ) { -// $this->title = $title; +// public function title( title ) { +// $this->title = title; // return $this; // } // @@ -879,7 +891,7 @@ public class XomwMessage { // // /** // * Magic method implementation of the above (for PHP >= 5.2.0), so we can do, eg: -// * $foo = new Message( $key ); +// * $foo = new Message( key ); // * $String = "$foo"; // * // * @since 1.18 @@ -1176,7 +1188,7 @@ public class XomwMessage { // // Cache depends on these parameters // $msg->message = null; // } -// $msg->interface = $this->interface; +// $msg->interfaceIsUserLang = $this->interfaceIsUserLang; // $msg->language = $this->language; // $msg->useDatabase = $this->useDatabase; // $msg->title = $this->title; @@ -1210,7 +1222,7 @@ public class XomwMessage { // $String, // $this->title, // /*linestart*/true, -// $this->interface, +// $this->interfaceIsUserLang, // $this->getLanguage() // ); // @@ -1229,7 +1241,7 @@ public class XomwMessage { // protected function transformText( $String ) { // return MessageCache::singleton()->transform( // $String, -// $this->interface, +// $this->interfaceIsUserLang, // $this->getLanguage(), // $this->title // ); @@ -1247,16 +1259,16 @@ public class XomwMessage { // if ( $this->message === null ) { // $cache = MessageCache::singleton(); // -// foreach ( $this->keysToTry as $key ) { -// $message = $cache->get( $key, $this->useDatabase, $this->getLanguage() ); +// foreach ( $this->keysToTry as key ) { +// $message = $cache->get( key, $this->useDatabase, $this->getLanguage() ); // if ( $message !== false && $message !== '' ) { // break; // } // } // // // NOTE: The constructor makes sure keysToTry isn't empty, -// // so we know that $key and $message are initialized. -// $this->key = $key; +// // so we know that key and $message are initialized. +// $this->key = key; // $this->message = $message; // } // return $this->message; @@ -1337,9 +1349,4 @@ public class XomwMessage { // $vars = $this->getLanguage()->$func( $vars ); // return $this->extractParam( new RawMessage( $vars, $params ), $format ); // } - public XomwMessage(byte[] textBry) { - this.textBry = textBry; - } - public byte[] text() {return textBry;} private byte[] textBry; - public byte[] escaped() {throw Err_.new_unimplemented();} } diff --git a/gplx.xowa.mediawiki/src/gplx/xowa/mediawiki/includes/content/XomwAbstractContent.java b/gplx.xowa.mediawiki/src/gplx/xowa/mediawiki/includes/content/XomwAbstractContent.java new file mode 100644 index 000000000..180dffc30 --- /dev/null +++ b/gplx.xowa.mediawiki/src/gplx/xowa/mediawiki/includes/content/XomwAbstractContent.java @@ -0,0 +1,526 @@ +/* +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.mediawiki.includes.content; import gplx.*; import gplx.xowa.*; import gplx.xowa.mediawiki.*; import gplx.xowa.mediawiki.includes.*; +// model_id = $modelId; +// } +// +// /** +// * @since 1.21 +// * +// * @see Content::getModel +// */ +// public function getModel() { +// return $this->model_id; +// } +// +// /** +// * @since 1.21 +// * +// * @param String $modelId The model to check +// * +// * @throws MWException If the provided ID is not the ID of the content model supported by this +// * Content Object. +// */ +// protected function checkModelID( $modelId ) { +// if ( $modelId !== $this->model_id ) { +// throw new MWException( +// "Bad content model: " . +// "expected {$this->model_id} " . +// "but got $modelId." +// ); +// } +// } +// +// /** +// * @since 1.21 +// * +// * @see Content::getContentHandler +// */ +// public function getContentHandler() { +// return ContentHandler::getForContent( $this ); +// } +// +// /** +// * @since 1.21 +// * +// * @see Content::getDefaultFormat +// */ +// public function getDefaultFormat() { +// return $this->getContentHandler()->getDefaultFormat(); +// } +// +// /** +// * @since 1.21 +// * +// * @see Content::getSupportedFormats +// */ +// public function getSupportedFormats() { +// return $this->getContentHandler()->getSupportedFormats(); +// } +// +// /** +// * @since 1.21 +// * +// * @param String $format +// * +// * @return boolean +// * +// * @see Content::isSupportedFormat +// */ +// public function isSupportedFormat( $format ) { +// if ( !$format ) { +// return true; // this means "use the default" +// } +// +// return $this->getContentHandler()->isSupportedFormat( $format ); +// } +// +// /** +// * @since 1.21 +// * +// * @param String $format The serialization format to check. +// * +// * @throws MWException If the format is not supported by this content handler. +// */ +// protected function checkFormat( $format ) { +// if ( !$this->isSupportedFormat( $format ) ) { +// throw new MWException( +// "Format $format is not supported for content model " . +// $this->getModel() +// ); +// } +// } +// +// /** +// * @since 1.21 +// * +// * @param String $format +// * +// * @return String +// * +// * @see Content::serialize +// */ +// public function serialize( $format = null ) { +// return $this->getContentHandler()->serializeContent( $this, $format ); +// } +// +// /** +// * @since 1.21 +// * +// * @return boolean +// * +// * @see Content::isEmpty +// */ +// public function isEmpty() { +// return $this->getSize() === 0; +// } +// +// /** +// * Subclasses may override this to implement (light weight) validation. +// * +// * @since 1.21 +// * +// * @return boolean Always true. +// * +// * @see Content::isValid +// */ +// public function isValid() { +// return true; +// } +// +// /** +// * @since 1.21 +// * +// * @param Content $that +// * +// * @return boolean +// * +// * @see Content::equals +// */ +// public function equals( Content $that = null ) { +// if ( is_null( $that ) ) { +// return false; +// } +// +// if ( $that === $this ) { +// return true; +// } +// +// if ( $that->getModel() !== $this->getModel() ) { +// return false; +// } +// +// return $this->getNativeData() === $that->getNativeData(); +// } +// +// /** +// * Returns a list of DataUpdate objects for recording information about this +// * Content in some secondary data store. +// * +// * This default implementation returns a LinksUpdate Object and calls the +// * SecondaryDataUpdates hook. +// * +// * Subclasses may override this to determine the secondary data updates more +// * efficiently, preferably without the need to generate a parser output Object. +// * They should however make sure to call SecondaryDataUpdates to give extensions +// * a chance to inject additional updates. +// * +// * @since 1.21 +// * +// * @param Title $title +// * @param Content $old +// * @param boolean $recursive +// * @param ParserOutput $parserOutput +// * +// * @return DataUpdate[] +// * +// * @see Content::getSecondaryDataUpdates() +// */ +// public function getSecondaryDataUpdates( Title $title, Content $old = null, +// $recursive = true, ParserOutput $parserOutput = null +// ) { +// if ( $parserOutput === null ) { +// $parserOutput = $this->getParserOutput( $title, null, null, false ); +// } +// +// $updates = [ +// new LinksUpdate( $title, $parserOutput, $recursive ) +// ]; +// +// Hooks::run( 'SecondaryDataUpdates', [ $title, $old, $recursive, $parserOutput, &$updates ] ); +// +// return $updates; +// } +// +// /** +// * @since 1.21 +// * +// * @return Title[]|null +// * +// * @see Content::getRedirectChain +// */ +// public function getRedirectChain() { +// global $wgMaxRedirects; +// $title = $this->getRedirectTarget(); +// if ( is_null( $title ) ) { +// return null; +// } +// // recursive check to follow double redirects +// $recurse = $wgMaxRedirects; +// $titles = [ $title ]; +// while ( --$recurse > 0 ) { +// if ( $title->isRedirect() ) { +// $page = WikiPage::factory( $title ); +// $newtitle = $page->getRedirectTarget(); +// } else { +// break; +// } +// // Redirects to some special pages are not permitted +// if ( $newtitle instanceof Title && $newtitle->isValidRedirectTarget() ) { +// // The new title passes the checks, so make that our current +// // title so that further recursion can be checked +// $title = $newtitle; +// $titles[] = $newtitle; +// } else { +// break; +// } +// } +// +// return $titles; +// } +// +// /** +// * Subclasses that implement redirects should override this. +// * +// * @since 1.21 +// * +// * @return Title|null +// * +// * @see Content::getRedirectTarget +// */ +// public function getRedirectTarget() { +// return null; +// } +// +// /** +// * @note Migrated here from Title::newFromRedirectRecurse. +// * +// * @since 1.21 +// * +// * @return Title|null +// * +// * @see Content::getUltimateRedirectTarget +// */ +// public function getUltimateRedirectTarget() { +// $titles = $this->getRedirectChain(); +// +// return $titles ? array_pop( $titles ) : null; +// } +// +// /** +// * @since 1.21 +// * +// * @return boolean +// * +// * @see Content::isRedirect +// */ +// public function isRedirect() { +// return $this->getRedirectTarget() !== null; +// } +// +// /** +// * This default implementation always returns $this. +// * Subclasses that implement redirects should override this. +// * +// * @since 1.21 +// * +// * @param Title $target +// * +// * @return Content $this +// * +// * @see Content::updateRedirect +// */ +// public function updateRedirect( Title $target ) { +// return $this; +// } +// +// /** +// * @since 1.21 +// * +// * @return null +// * +// * @see Content::getSection +// */ +// public function getSection( $sectionId ) { +// return null; +// } +// +// /** +// * @since 1.21 +// * +// * @return null +// * +// * @see Content::replaceSection +// */ +// public function replaceSection( $sectionId, Content $with, $sectionTitle = '' ) { +// return null; +// } +// +// /** +// * @since 1.21 +// * +// * @return Content $this +// * +// * @see Content::preSaveTransform +// */ +// public function preSaveTransform( Title $title, User $user, ParserOptions $popts ) { +// return $this; +// } +// +// /** +// * @since 1.21 +// * +// * @return Content $this +// * +// * @see Content::addSectionHeader +// */ +// public function addSectionHeader( $header ) { +// return $this; +// } +// +// /** +// * @since 1.21 +// * +// * @return Content $this +// * +// * @see Content::preloadTransform +// */ +// public function preloadTransform( Title $title, ParserOptions $popts, $params = [] ) { +// return $this; +// } +// +// /** +// * @since 1.21 +// * +// * @return Status +// * +// * @see Content::prepareSave +// */ +// public function prepareSave( WikiPage $page, $flags, $parentRevId, User $user ) { +// if ( $this->isValid() ) { +// return Status::newGood(); +// } else { +// return Status::newFatal( "invalid-content-data" ); +// } +// } +// +// /** +// * @since 1.21 +// * +// * @param WikiPage $page +// * @param ParserOutput $parserOutput +// * +// * @return LinksDeletionUpdate[] +// * +// * @see Content::getDeletionUpdates +// */ +// public function getDeletionUpdates( WikiPage $page, ParserOutput $parserOutput = null ) { +// return [ +// new LinksDeletionUpdate( $page ), +// ]; +// } +// +// /** +// * This default implementation always returns false. Subclasses may override +// * this to supply matching logic. +// * +// * @since 1.21 +// * +// * @param MagicWord $word +// * +// * @return boolean Always false. +// * +// * @see Content::matchMagicWord +// */ +// public function matchMagicWord( MagicWord $word ) { +// return false; +// } +// +// /** +// * This super implementation calls the hook ConvertContent to enable custom conversions. +// * Subclasses may override this to implement conversion for "their" content model. +// * +// * @param String $toModel +// * @param String $lossy +// * +// * @return Content|boolean +// * +// * @see Content::convert() +// */ +// public function convert( $toModel, $lossy = '' ) { +// if ( $this->getModel() === $toModel ) { +// // nothing to do, shorten out. +// return $this; +// } +// +// $lossy = ( $lossy === 'lossy' ); // String flag, convert to boolean for convenience +// $result = false; +// +// Hooks::run( 'ConvertContent', [ $this, $toModel, $lossy, &$result ] ); +// +// return $result; +// } +// +// /** +// * Returns a ParserOutput Object containing information derived from this content. +// * Most importantly, unless $generateHtml was false, the return value contains an +// * HTML representation of the content. +// * +// * Subclasses that want to control the parser output may override this, but it is +// * preferred to override fillParserOutput() instead. +// * +// * Subclasses that override getParserOutput() itself should take care to call the +// * ContentGetParserOutput hook. +// * +// * @since 1.24 +// * +// * @param Title $title Context title for parsing +// * @param int|null $revId Revision ID (for {{REVISIONID}}) +// * @param ParserOptions|null $options Parser options +// * @param boolean $generateHtml Whether or not to generate HTML +// * +// * @return ParserOutput Containing information derived from this content. +// */ +// public function getParserOutput( Title $title, $revId = null, +// ParserOptions $options = null, $generateHtml = true +// ) { +// if ( $options === null ) { +// $options = $this->getContentHandler()->makeParserOptions( 'canonical' ); +// } +// +// $po = new ParserOutput(); +// +// if ( Hooks::run( 'ContentGetParserOutput', +// [ $this, $title, $revId, $options, $generateHtml, &$po ] ) ) { +// +// // Save and restore the old value, just in case something is reusing +// // the ParserOptions Object in some weird way. +// $oldRedir = $options->getRedirectTarget(); +// $options->setRedirectTarget( $this->getRedirectTarget() ); +// $this->fillParserOutput( $title, $revId, $options, $generateHtml, $po ); +// $options->setRedirectTarget( $oldRedir ); +// } +// +// Hooks::run( 'ContentAlterParserOutput', [ $this, $title, $po ] ); +// +// return $po; +// } +// +// /** +// * Fills the provided ParserOutput with information derived from the content. +// * Unless $generateHtml was false, this includes an HTML representation of the content. +// * +// * This is called by getParserOutput() after consulting the ContentGetParserOutput hook. +// * Subclasses are expected to override this method (or getParserOutput(), if need be). +// * Subclasses of TextContent should generally override getHtml() instead. +// * +// * This placeholder implementation always throws an exception. +// * +// * @since 1.24 +// * +// * @param Title $title Context title for parsing +// * @param int|null $revId Revision ID (for {{REVISIONID}}) +// * @param ParserOptions $options Parser options +// * @param boolean $generateHtml Whether or not to generate HTML +// * @param ParserOutput &$output The output Object to fill (reference). +// * +// * @throws MWException +// */ +// protected function fillParserOutput( Title $title, $revId, +// ParserOptions $options, $generateHtml, ParserOutput &$output +// ) { +// // Don't make abstract, so subclasses that override getParserOutput() directly don't fail. +// throw new MWException( 'Subclasses of AbstractContent must override fillParserOutput!' ); +// } +// } diff --git a/gplx.xowa.mediawiki/src/gplx/xowa/mediawiki/includes/content/XomwContent.java b/gplx.xowa.mediawiki/src/gplx/xowa/mediawiki/includes/content/XomwContent.java new file mode 100644 index 000000000..d078b7050 --- /dev/null +++ b/gplx.xowa.mediawiki/src/gplx/xowa/mediawiki/includes/content/XomwContent.java @@ -0,0 +1,519 @@ +/* +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.mediawiki.includes.content; import gplx.*; import gplx.xowa.*; import gplx.xowa.mediawiki.*; import gplx.xowa.mediawiki.includes.*; +// /** +// * A content Object represents page content, e.g. the text to show on a page. +// * Content objects have no knowledge about how they relate to wiki pages. +// */ +// +// /** +// * Base interface for content objects. +// * +// * @ingroup Content +// */ +// interface Content { +// +// /** +// * @since 1.21 +// * +// * @return String A String representing the content in a way useful for +// * building a full text search index. If no useful representation exists, +// * this method returns an empty String. +// * +// * @todo Test that this actually works +// * @todo Make sure this also works with LuceneSearch / WikiSearch +// */ +// public function getTextForSearchIndex(); +// +// /** +// * @since 1.21 +// * +// * @return String|boolean The wikitext to include when another page includes this +// * content, or false if the content is not includable in a wikitext page. +// * +// * @todo Allow native handling, bypassing wikitext representation, like +// * for includable special pages. +// * @todo Allow transclusion into other content models than Wikitext! +// * @todo Used in WikiPage and MessageCache to get message text. Not so +// * nice. What should we use instead?! +// */ +// public function getWikitextForTransclusion(); +// +// /** +// * Returns a textual representation of the content suitable for use in edit +// * summaries and log messages. +// * +// * @since 1.21 +// * +// * @param int $maxLength Maximum length of the summary text. +// * +// * @return String The summary text. +// */ +// public function getTextForSummary( $maxLength = 250 ); +// +// /** +// * Returns native representation of the data. Interpretation depends on +// * the data model used, as given by getDataModel(). +// * +// * @since 1.21 +// * +// * @return mixed The native representation of the content. Could be a +// * String, a nested array structure, an Object, a binary blob... +// * anything, really. +// * +// * @note Caller must be aware of content model! +// */ +// public function getNativeData(); +// +// /** +// * Returns the content's nominal size in "bogo-bytes". +// * +// * @return int +// */ +// public function getSize(); +// +// /** +// * Returns the ID of the content model used by this Content Object. +// * Corresponds to the CONTENT_MODEL_XXX constants. +// * +// * @since 1.21 +// * +// * @return String The model id +// */ +// public function getModel(); +// +// /** +// * Convenience method that returns the ContentHandler singleton for handling +// * the content model that this Content Object uses. +// * +// * Shorthand for ContentHandler::getForContent( $this ) +// * +// * @since 1.21 +// * +// * @return ContentHandler +// */ +// public function getContentHandler(); +// +// /** +// * Convenience method that returns the default serialization format for the +// * content model that this Content Object uses. +// * +// * Shorthand for $this->getContentHandler()->getDefaultFormat() +// * +// * @since 1.21 +// * +// * @return String +// */ +// public function getDefaultFormat(); +// +// /** +// * Convenience method that returns the list of serialization formats +// * supported for the content model that this Content Object uses. +// * +// * Shorthand for $this->getContentHandler()->getSupportedFormats() +// * +// * @since 1.21 +// * +// * @return String[] List of supported serialization formats +// */ +// public function getSupportedFormats(); +// +// /** +// * Returns true if $format is a supported serialization format for this +// * Content Object, false if it isn't. +// * +// * Note that this should always return true if $format is null, because null +// * stands for the default serialization. +// * +// * Shorthand for $this->getContentHandler()->isSupportedFormat( $format ) +// * +// * @since 1.21 +// * +// * @param String $format The serialization format to check. +// * +// * @return boolean Whether the format is supported +// */ +// public function isSupportedFormat( $format ); +// +// /** +// * Convenience method for serializing this Content Object. +// * +// * Shorthand for $this->getContentHandler()->serializeContent( $this, $format ) +// * +// * @since 1.21 +// * +// * @param String $format The desired serialization format, or null for the default format. +// * +// * @return String Serialized form of this Content Object. +// */ +// public function serialize( $format = null ); +// +// /** +// * Returns true if this Content Object represents empty content. +// * +// * @since 1.21 +// * +// * @return boolean Whether this Content Object is empty +// */ +// public function isEmpty(); +// +// /** +// * Returns whether the content is valid. This is intended for local validity +// * checks, not considering global consistency. +// * +// * Content needs to be valid before it can be saved. +// * +// * This default implementation always returns true. +// * +// * @since 1.21 +// * +// * @return boolean +// */ +// public function isValid(); +// +// /** +// * Returns true if this Content objects is conceptually equivalent to the +// * given Content Object. +// * +// * Contract: +// * +// * - Will return false if $that is null. +// * - Will return true if $that === $this. +// * - Will return false if $that->getModel() != $this->getModel(). +// * - Will return false if $that->getNativeData() is not equal to $this->getNativeData(), +// * where the meaning of "equal" depends on the actual data model. +// * +// * Implementations should be careful to make equals() transitive and reflexive: +// * +// * - $a->equals( $b ) <=> $b->equals( $a ) +// * - $a->equals( $b ) && $b->equals( $c ) ==> $a->equals( $c ) +// * +// * @since 1.21 +// * +// * @param Content $that The Content Object to compare to. +// * +// * @return boolean True if this Content Object is equal to $that, false otherwise. +// */ +// public function equals( Content $that = null ); +// +// /** +// * Return a copy of this Content Object. The following must be true for the +// * Object returned: +// * +// * if $copy = $original->copy() +// * +// * - get_class($original) === get_class($copy) +// * - $original->getModel() === $copy->getModel() +// * - $original->equals( $copy ) +// * +// * If and only if the Content Object is immutable, the copy() method can and +// * should return $this. That is, $copy === $original may be true, but only +// * for immutable content objects. +// * +// * @since 1.21 +// * +// * @return Content A copy of this Object +// */ +// public function copy(); +// +// /** +// * Returns true if this content is countable as a "real" wiki page, provided +// * that it's also in a countable location (e.g. a current revision in the +// * main namespace). +// * +// * @since 1.21 +// * +// * @param boolean|null $hasLinks If it is known whether this content contains +// * links, provide this information here, to avoid redundant parsing to +// * find out. +// * +// * @return boolean +// */ +// public function isCountable( $hasLinks = null ); +// +// /** +// * Parse the Content Object and generate a ParserOutput from the result. +// * $result->getText() can be used to obtain the generated HTML. If no HTML +// * is needed, $generateHtml can be set to false; in that case, +// * $result->getText() may return null. +// * +// * @note To control which options are used in the cache key for the +// * generated parser output, implementations of this method +// * may call ParserOutput::recordOption() on the output Object. +// * +// * @param Title $title The page title to use as a context for rendering. +// * @param int $revId Optional revision ID being rendered. +// * @param ParserOptions $options Any parser options. +// * @param boolean $generateHtml Whether to generate HTML (default: true). If false, +// * the result of calling getText() on the ParserOutput Object returned by +// * this method is undefined. +// * +// * @since 1.21 +// * +// * @return ParserOutput +// */ +// public function getParserOutput( Title $title, $revId = null, +// ParserOptions $options = null, $generateHtml = true ); +// +// // TODO: make RenderOutput and RenderOptions super classes +// +// /** +// * Returns a list of DataUpdate objects for recording information about this +// * Content in some secondary data store. If the optional second argument, +// * $old, is given, the updates may model only the changes that need to be +// * made to replace information about the old content with information about +// * the new content. +// * +// * This default implementation calls +// * $this->getParserOutput( $content, $title, null, null, false ), +// * and then calls getSecondaryDataUpdates( $title, $recursive ) on the +// * resulting ParserOutput Object. +// * +// * Subclasses may implement this to determine the necessary updates more +// * efficiently, or make use of information about the old content. +// * +// * @note Implementations should call the SecondaryDataUpdates hook, like +// * AbstractContent does. +// * +// * @param Title $title The context for determining the necessary updates +// * @param Content $old An optional Content Object representing the +// * previous content, i.e. the content being replaced by this Content +// * Object. +// * @param boolean $recursive Whether to include recursive updates (default: +// * false). +// * @param ParserOutput $parserOutput Optional ParserOutput Object. +// * Provide if you have one handy, to avoid re-parsing of the content. +// * +// * @return DataUpdate[] A list of DataUpdate objects for putting information +// * about this content Object somewhere. +// * +// * @since 1.21 +// */ +// public function getSecondaryDataUpdates( Title $title, Content $old = null, +// $recursive = true, ParserOutput $parserOutput = null ); +// +// /** +// * Construct the redirect destination from this content and return an +// * array of Titles, or null if this content doesn't represent a redirect. +// * The last element in the array is the final destination after all redirects +// * have been resolved (up to $wgMaxRedirects times). +// * +// * @since 1.21 +// * +// * @return Title[]|null List of Titles, with the destination last. +// */ +// public function getRedirectChain(); +// +// /** +// * Construct the redirect destination from this content and return a Title, +// * or null if this content doesn't represent a redirect. +// * This will only return the immediate redirect target, useful for +// * the redirect table and other checks that don't need full recursion. +// * +// * @since 1.21 +// * +// * @return Title|null The corresponding Title. +// */ +// public function getRedirectTarget(); +// +// /** +// * Construct the redirect destination from this content and return the +// * Title, or null if this content doesn't represent a redirect. +// * +// * This will recurse down $wgMaxRedirects times or until a non-redirect +// * target is hit in order to provide (hopefully) the Title of the final +// * destination instead of another redirect. +// * +// * There is usually no need to override the default behavior, subclasses that +// * want to implement redirects should override getRedirectTarget(). +// * +// * @since 1.21 +// * +// * @return Title|null +// */ +// public function getUltimateRedirectTarget(); +// +// /** +// * Returns whether this Content represents a redirect. +// * Shorthand for getRedirectTarget() !== null. +// * +// * @since 1.21 +// * +// * @return boolean +// */ +// public function isRedirect(); +// +// /** +// * If this Content Object is a redirect, this method updates the redirect target. +// * Otherwise, it does nothing. +// * +// * @since 1.21 +// * +// * @param Title $target The new redirect target +// * +// * @return Content A new Content Object with the updated redirect (or $this +// * if this Content Object isn't a redirect) +// */ +// public function updateRedirect( Title $target ); +// +// /** +// * Returns the section with the given ID. +// * +// * @since 1.21 +// * +// * @param String|int $sectionId Section identifier as a number or String +// * (e.g. 0, 1 or 'T-1'). The ID "0" retrieves the section before the first heading, "1" the +// * text between the first heading (included) and the second heading (excluded), etc. +// * +// * @return Content|boolean|null The section, or false if no such section +// * exist, or null if sections are not supported. +// */ +// public function getSection( $sectionId ); +// +// /** +// * Replaces a section of the content and returns a Content Object with the +// * section replaced. +// * +// * @since 1.21 +// * +// * @param String|int|null|boolean $sectionId Section identifier as a number or String +// * (e.g. 0, 1 or 'T-1'), null/false or an empty String for the whole page +// * or 'new' for a new section. +// * @param Content $with New content of the section +// * @param String $sectionTitle New section's subject, only if $section is 'new' +// * +// * @return String|null Complete article text, or null if error +// */ +// public function replaceSection( $sectionId, Content $with, $sectionTitle = '' ); +// +// /** +// * Returns a Content Object with pre-save transformations applied (or this +// * Object if no transformations apply). +// * +// * @since 1.21 +// * +// * @param Title $title +// * @param User $user +// * @param ParserOptions $parserOptions +// * +// * @return Content +// */ +// public function preSaveTransform( Title $title, User $user, ParserOptions $parserOptions ); +// +// /** +// * Returns a new WikitextContent Object with the given section heading +// * prepended, if supported. The default implementation just returns this +// * Content Object unmodified, ignoring the section header. +// * +// * @since 1.21 +// * +// * @param String $header +// * +// * @return Content +// */ +// public function addSectionHeader( $header ); +// +// /** +// * Returns a Content Object with preload transformations applied (or this +// * Object if no transformations apply). +// * +// * @since 1.21 +// * +// * @param Title $title +// * @param ParserOptions $parserOptions +// * @param array $params +// * +// * @return Content +// */ +// public function preloadTransform( Title $title, ParserOptions $parserOptions, $params = [] ); +// +// /** +// * Prepare Content for saving. Called before Content is saved by WikiPage::doEditContent() and in +// * similar places. +// * +// * This may be used to check the content's consistency with global state. This function should +// * NOT write any information to the database. +// * +// * Note that this method will usually be called inside the same transaction +// * bracket that will be used to save the new revision. +// * +// * Note that this method is called before any update to the page table is +// * performed. This means that $page may not yet know a page ID. +// * +// * @since 1.21 +// * +// * @param WikiPage $page The page to be saved. +// * @param int $flags Bitfield for use with EDIT_XXX constants, see WikiPage::doEditContent() +// * @param int $parentRevId The ID of the current revision +// * @param User $user +// * +// * @return Status A status Object indicating whether the content was +// * successfully prepared for saving. If the returned status indicates +// * an error, a rollback will be performed and the transaction aborted. +// * +// * @see WikiPage::doEditContent() +// */ +// public function prepareSave( WikiPage $page, $flags, $parentRevId, User $user ); +// +// /** +// * Returns a list of updates to perform when this content is deleted. +// * The necessary updates may be taken from the Content Object, or depend on +// * the current state of the database. +// * +// * @since 1.21 +// * +// * @param WikiPage $page The deleted page +// * @param ParserOutput $parserOutput Optional parser output Object +// * for efficient access to meta-information about the content Object. +// * Provide if you have one handy. +// * +// * @return DataUpdate[] A list of DataUpdate instances that will clean up the +// * database after deletion. +// */ +// public function getDeletionUpdates( WikiPage $page, +// ParserOutput $parserOutput = null ); +// +// /** +// * Returns true if this Content Object matches the given magic word. +// * +// * @since 1.21 +// * +// * @param MagicWord $word The magic word to match +// * +// * @return boolean Whether this Content Object matches the given magic word. +// */ +// public function matchMagicWord( MagicWord $word ); +// +// /** +// * Converts this content Object into another content Object with the given content model, +// * if that is possible. +// * +// * @param String $toModel The desired content model, use the CONTENT_MODEL_XXX flags. +// * @param String $lossy Optional flag, set to "lossy" to allow lossy conversion. If lossy +// * conversion is not allowed, full round-trip conversion is expected to work without losing +// * information. +// * +// * @return Content|boolean A content Object with the content model $toModel, or false if +// * that conversion is not supported. +// */ +// public function convert( $toModel, $lossy = '' ); +// // @todo ImagePage and CategoryPage interfere with per-content action handlers +// // @todo nice&sane integration of GeSHi syntax highlighting +// // [11:59] Hooks are ugly; make CodeHighlighter interface and a +// // config to set the class which handles syntax highlighting +// // [12:00] And default it to a DummyHighlighter +// +// } diff --git a/gplx.xowa.mediawiki/src/gplx/xowa/mediawiki/includes/content/XomwMessageContent.java b/gplx.xowa.mediawiki/src/gplx/xowa/mediawiki/includes/content/XomwMessageContent.java new file mode 100644 index 000000000..8cd077220 --- /dev/null +++ b/gplx.xowa.mediawiki/src/gplx/xowa/mediawiki/includes/content/XomwMessageContent.java @@ -0,0 +1,168 @@ +/* +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.mediawiki.includes.content; import gplx.*; import gplx.xowa.*; import gplx.xowa.mediawiki.*; import gplx.xowa.mediawiki.includes.*; +// mMessage = wfMessage( $msg ); +// } else { +// $this->mMessage = clone $msg; +// } +// +// if ( $params ) { +// $this->mMessage = $this->mMessage->params( $params ); +// } +// } +// +// /** +// * Fully parse the text from wikitext to HTML. +// * +// * @return String Parsed HTML. +// */ +// public function getHtml() { +// return $this->mMessage->parse(); +// } +// +// /** +// * Returns the message text. {{-transformation is done. +// * +// * @return String Unescaped message text. +// */ +// public function getWikitext() { +// return $this->mMessage->text(); +// } +// +// /** +// * Returns the message Object, with any parameters already substituted. +// * +// * @return Message The message Object. +// */ +// public function getNativeData() { +// // NOTE: Message objects are mutable. Cloning here makes MessageContent immutable. +// return clone $this->mMessage; +// } +// +// /** +// * @return String +// * +// * @see Content::getTextForSearchIndex +// */ +// public function getTextForSearchIndex() { +// return $this->mMessage->plain(); +// } +// +// /** +// * @return String +// * +// * @see Content::getWikitextForTransclusion +// */ +// public function getWikitextForTransclusion() { +// return $this->getWikitext(); +// } +// +// /** +// * @param int $maxlength Maximum length of the summary text, defaults to 250. +// * +// * @return String The summary text. +// * +// * @see Content::getTextForSummary +// */ +// public function getTextForSummary( $maxlength = 250 ) { +// return substr( $this->mMessage->plain(), 0, $maxlength ); +// } +// +// /** +// * @return int +// * +// * @see Content::getSize +// */ +// public function getSize() { +// return strlen( $this->mMessage->plain() ); +// } +// +// /** +// * @return Content A copy of this Object +// * +// * @see Content::copy +// */ +// public function copy() { +// // MessageContent is immutable (because getNativeData() returns a clone of the Message Object) +// return $this; +// } +// +// /** +// * @param boolean|null $hasLinks +// * +// * @return boolean Always false. +// * +// * @see Content::isCountable +// */ +// public function isCountable( $hasLinks = null ) { +// return false; +// } +// +// /** +// * @param Title $title Unused. +// * @param int $revId Unused. +// * @param ParserOptions $options Unused. +// * @param boolean $generateHtml Whether to generate HTML (default: true). +// * +// * @return ParserOutput +// * +// * @see Content::getParserOutput +// */ +// public function getParserOutput( Title $title, $revId = null, +// ParserOptions $options = null, $generateHtml = true ) { +// if ( $generateHtml ) { +// $html = $this->getHtml(); +// } else { +// $html = ''; +// } +// +// $po = new ParserOutput( $html ); +// // Message objects are in the user language. +// $po->recordOption( 'userlang' ); +// +// return $po; +// } +// +// } diff --git a/gplx.xowa.mediawiki/src/gplx/xowa/mediawiki/includes/title/XomwMediaWikiTitleCodec.java b/gplx.xowa.mediawiki/src/gplx/xowa/mediawiki/includes/title/XomwMediaWikiTitleCodec.java index 041035cb1..c3a5e923d 100644 --- a/gplx.xowa.mediawiki/src/gplx/xowa/mediawiki/includes/title/XomwMediaWikiTitleCodec.java +++ b/gplx.xowa.mediawiki/src/gplx/xowa/mediawiki/includes/title/XomwMediaWikiTitleCodec.java @@ -348,14 +348,14 @@ public class XomwMediaWikiTitleCodec implements XomwTitleFormatter { break; } while (true); -// $fragment = strstr(dbkey, '#'); -// if (false !== $fragment) { -// parts['fragment'] = str_replace('_', ' ', substr($fragment, 1)); -// dbkey = substr(dbkey, 0, strlen(dbkey) - strlen($fragment)); -// // remove whitespace again: prevents "Foo_bar_#" -// // becoming "Foo_bar_" -// dbkey = preg_replace('/_*$/', '', dbkey); -// } + byte[] fragment = XophpString.strstr(dbkey, Byte_ascii.Hash_bry); + if (null != fragment) { + parts.fragment = XophpString.str_replace(Byte_ascii.Underline, Byte_ascii.Space, XophpString.substr(fragment, 1)); + dbkey = XophpString.substr(dbkey, 0, XophpString.strlen(dbkey) - XophpString.strlen(fragment)); + // remove whitespace again: prevents "Foo_bar_#" + // becoming "Foo_bar_" + dbkey = Bry_.Replace(dbkey, Byte_ascii.Underline_bry, Bry_.Empty); + } // Reject illegal characters. // $rxTc = self::getTitleInvalidRegex(); @@ -382,7 +382,7 @@ public class XomwMediaWikiTitleCodec implements XomwTitleFormatter { // throw new XomwMalformedTitleException('title-invalid-relative', text); // } - // Magic tilde sequences? Nu-uh! + // Magic tilde sequences? // if (strpos(dbkey, '~~~') !== false) { // throw new XomwMalformedTitleException('title-invalid-magic-tilde', text); // }