From f9cf49c160c9f02f3a4e44020abbe21293c28bc9 Mon Sep 17 00:00:00 2001 From: gnosygnu Date: Mon, 4 May 2020 08:29:37 -0400 Subject: [PATCH] XOMW: Start XomwMessage [#632] --- .../src/gplx/xowa/mediawiki/XophpArray.java | 19 + .../src/gplx/xowa/mediawiki/XophpArray_.java | 15 +- .../XophpInvalidArgumentException.java | 11 +- .../includes/XomwGlobalFunctions.java | 15 +- .../xowa/mediawiki/includes/XomwLinker.java | 5 +- .../xowa/mediawiki/includes/XomwMessage.java | 2823 ++++++++--------- .../mediawiki/includes/XomwMessageMgr.java | 18 +- .../mediawiki/includes/XomwMessageOld.java | 1416 +++++++++ .../xowa/mediawiki/includes/XomwTitleOld.java | 3 +- .../includes/parsers/XomwParser.java | 36 +- .../lnkis/Xomw_lnki_wkr__file__tst.java | 2 +- .../mediawiki/includes/site/XomwSiteList.java | 35 +- .../gplx/xowa/mediawiki/XophpArray__tst.java | 18 + 13 files changed, 2946 insertions(+), 1470 deletions(-) create mode 100644 400_xowa/src/gplx/xowa/mediawiki/includes/XomwMessageOld.java diff --git a/400_xowa/src/gplx/xowa/mediawiki/XophpArray.java b/400_xowa/src/gplx/xowa/mediawiki/XophpArray.java index eb8bc9e00..a604d57b3 100644 --- a/400_xowa/src/gplx/xowa/mediawiki/XophpArray.java +++ b/400_xowa/src/gplx/xowa/mediawiki/XophpArray.java @@ -196,6 +196,7 @@ public class XophpArray implements Bry_bfr_able { itm += v; this.Set(idx, itm); } + public XophpArray Clone() { XophpArray rv = new XophpArray(); int len = hash.Len(); @@ -240,5 +241,23 @@ public class XophpArray implements Bry_bfr_able { rv.Add(val); return rv; } + + // REF.PHP:https://www.php.net/manual/en/function.reset.php + private int internalPointerIndex = 0; + private Object internalPointerAdd(int v) { + internalPointerIndex += v; + return internalPointerGetOrNull(); + } + private Object internalPointerReset() { + internalPointerIndex = 0; + return internalPointerGetOrNull(); + } + private Object internalPointerGetOrNull() {return hash.Len() == 0 ? null : this.Get_at(internalPointerIndex);} + public static Object reset(XophpArray array) {return array.internalPointerReset();} + public static Object current(XophpArray array) {return array.internalPointerGetOrNull();} + public static Object next(XophpArray array) { + return array.internalPointerAdd(1); + } + public static final XophpArray False = null; // handles code like "if ($var === false)" where var is an Object; } diff --git a/400_xowa/src/gplx/xowa/mediawiki/XophpArray_.java b/400_xowa/src/gplx/xowa/mediawiki/XophpArray_.java index 62aeae6c9..ca19c545f 100644 --- a/400_xowa/src/gplx/xowa/mediawiki/XophpArray_.java +++ b/400_xowa/src/gplx/xowa/mediawiki/XophpArray_.java @@ -129,6 +129,16 @@ public class XophpArray_ { return rv; } + public static XophpArray array_values(XophpArray array) { + XophpArray rv = XophpArray.New(); + int len = array.count(); + for (int i = 0; i < len; i++) { + XophpArrayItm itm = array.Get_at_itm(i); + rv.Add(itm.Val()); + } + return rv; + } + // DEPRECATE:use XophpArray public static boolean popBoolOrN(List_adp list) {return Bool_.Cast(List_adp_.Pop_or(list, false));} public static byte[] popBryOrNull(List_adp list) {return (byte[])List_adp_.Pop_or(list, null);} @@ -151,7 +161,10 @@ public class XophpArray_ { public static boolean array_key_exists(int key, Ordered_hash array) {return array.Has(key);} public static boolean array_key_exists(String key, Ordered_hash array) {return array.Has(key);} public static boolean array_key_exists(byte[] key, Ordered_hash array) {return array.Has(key);} - public static boolean array_is_empty(Ordered_hash array) { + public static boolean empty(Ordered_hash array) { + return array.Len() == 0; + } + public static boolean empty(XophpArray array) { return array.Len() == 0; } diff --git a/400_xowa/src/gplx/xowa/mediawiki/XophpInvalidArgumentException.java b/400_xowa/src/gplx/xowa/mediawiki/XophpInvalidArgumentException.java index 5b105f99b..2c6fc87e9 100644 --- a/400_xowa/src/gplx/xowa/mediawiki/XophpInvalidArgumentException.java +++ b/400_xowa/src/gplx/xowa/mediawiki/XophpInvalidArgumentException.java @@ -1,6 +1,6 @@ /* XOWA: the XOWA Offline Wiki Application -Copyright (C) 2012-2017 gnosygnu@gmail.com +Copyright (C) 2012-2020 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. @@ -13,6 +13,9 @@ 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; import gplx.*; import gplx.xowa.*; -public class XophpInvalidArgumentException extends Err { public XophpInvalidArgumentException(String text) {super(false, "", text, text);} -} +package gplx.xowa.mediawiki; import gplx.*; import gplx.xowa.*; +public class XophpInvalidArgumentException extends Err { + public XophpInvalidArgumentException(String fmt, Object... args) { + super(true, "", "", String_.Format(fmt, args)); + } +} diff --git a/400_xowa/src/gplx/xowa/mediawiki/includes/XomwGlobalFunctions.java b/400_xowa/src/gplx/xowa/mediawiki/includes/XomwGlobalFunctions.java index 86b3b7d13..8572644a3 100644 --- a/400_xowa/src/gplx/xowa/mediawiki/includes/XomwGlobalFunctions.java +++ b/400_xowa/src/gplx/xowa/mediawiki/includes/XomwGlobalFunctions.java @@ -17,6 +17,7 @@ package gplx.xowa.mediawiki.includes; import gplx.Gfo_log_; import gplx.xowa.mediawiki.XomwEnv; +import gplx.xowa.mediawiki.XophpObject_; import gplx.xowa.mediawiki.XophpString_; // MW.SRC:1.33.1 @@ -1283,7 +1284,7 @@ public class XomwGlobalFunctions { * * @see Message::__construct */ - public static XomwMessage wfMessage(XomwEnv env, String key, String... params) { + public static XomwMessageOld wfMessageOld(XomwEnv env, String key, String... params) { // $message = new Message($key); // // // We call Message::params() to reduce code duplication @@ -1292,7 +1293,17 @@ public class XomwGlobalFunctions { // } // // return $message; - return env.Message_mgr().Get_by_str(key); + return env.Message_mgr().Old_get_by_str(key); + } + public static XomwMessage wfMessage(XomwEnv env, String key, String... params) { + XomwMessage message = new XomwMessage(key); + + // We call Message::params() to reduce code duplication + if (XophpObject_.is_true(params)) { +// message.params(...$params); + } + + return message; } // /** diff --git a/400_xowa/src/gplx/xowa/mediawiki/includes/XomwLinker.java b/400_xowa/src/gplx/xowa/mediawiki/includes/XomwLinker.java index 4596e5158..314482fc9 100644 --- a/400_xowa/src/gplx/xowa/mediawiki/includes/XomwLinker.java +++ b/400_xowa/src/gplx/xowa/mediawiki/includes/XomwLinker.java @@ -13,7 +13,8 @@ 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; import gplx.*; import gplx.xowa.*; import gplx.xowa.mediawiki.*; +package gplx.xowa.mediawiki.includes; import gplx.*; +import gplx.xowa.mediawiki.*; import gplx.core.btries.*; import gplx.langs.htmls.*; import gplx.xowa.mediawiki.includes.xohtml.*; import gplx.xowa.mediawiki.includes.linkers.*; import gplx.xowa.mediawiki.includes.parsers.*; @@ -650,7 +651,7 @@ public class XomwLinker { , tmp_attribs.Clear() .Add(Gfh_atr_.Bry__href , url) .Add(Gfh_atr_.Bry__class, Class__internal) - .Add(Gfh_atr_.Bry__title, XomwGlobalFunctions.wfMessage(env, "thumbnail-more").text()) + .Add(Gfh_atr_.Bry__title, XomwGlobalFunctions.wfMessageOld(env, "thumbnail-more").text()) , Bry_.Empty); byte[] zoom_anch = tmp.To_bry_and_clear(); XomwHtml.rawElement(tmp, htmlTemp, Gfh_tag_.Bry__div, tmp_attribs.Clear().Add(Gfh_atr_.Bry__class, Class__magnify), zoom_anch); diff --git a/400_xowa/src/gplx/xowa/mediawiki/includes/XomwMessage.java b/400_xowa/src/gplx/xowa/mediawiki/includes/XomwMessage.java index 01f3ce4f5..0afdd0ad6 100644 --- a/400_xowa/src/gplx/xowa/mediawiki/includes/XomwMessage.java +++ b/400_xowa/src/gplx/xowa/mediawiki/includes/XomwMessage.java @@ -1,1416 +1,1407 @@ -/* -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; import gplx.*; import gplx.xowa.*; import gplx.xowa.mediawiki.*; -import gplx.xowa.mediawiki.languages.*; -import gplx.xowa.mediawiki.includes.content.*; -/** -* The Message cls provides methods which fulfil two basic services: -* - fetching interfaceIsUserLang messages -* - processing messages into a variety of formats -* -* First implemented with MediaWiki 1.17, the Message cls is intended to -* replace the old wfMsg* functions that over time grew unusable. -* @see https://www.mediawiki.org/wiki/Manual:Messages_API for equivalences -* between old and new functions. -* -* You should use the wfMessage() global function which acts as a wrapper for -* the Message cls. The wrapper let you pass parameters as arguments. -* -* The most basic usage cases would be: -* -* @code -* // Initialize a Message Object using the 'some_key' message key -* $message = wfMessage('some_key'); -* -* // Using two parameters those values are strings 'value1' and 'value2': -* $message = wfMessage('some_key', -* 'value1', 'value2' -* ); -* @endcode -* -* @section message_global_fn Global function wrapper: -* -* Since wfMessage() returns a Message instance, you can chain its call with -* 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 interfaceIsUserLang message: -* -* @code -* $button = Xml::button( -* wfMessage('submit')->text() -* ); -* @endcode -* -* A Message instance can be passed parameters after it has been constructed, -* use the prms() method to do so: -* -* @code -* wfMessage('welcome-to') -* ->prmsVar($wgSitename) -* ->text(); -* @endcode -* -* {{GRAMMAR}} and friends work correctly: -* -* @code -* wfMessage('are-friends', -* $user, $friend -* ); -* wfMessage('bad-message') -* ->rawParams('') -* ->escaped(); -* @endcode -* -* @section message_language Changing language: -* -* Messages can be requested in a different language or in whatever current -* content language is being used. The methods are: -* - Message->inContentLanguage() -* - Message->inLanguage() -* -* Sometimes the message text ends up in the database, so content language is -* needed: -* -* @code -* wfMessage('file-log', -* $user, $filename -* )->inContentLanguage()->text(); -* @endcode -* -* Checking whether a message exists: -* -* @code -* wfMessage('mysterious-message')->exists() -* // returns a boolean whether the 'mysterious-message' key exist. -* @endcode -* -* If you want to use a different language: -* -* @code -* $userLanguage = $user->getOption('language'); -* wfMessage('email-header') -* ->inLanguage($userLanguage) -* ->plain(); -* @endcode -* -* @note You can parse the text only in the content or interfaceIsUserLang languages -* -* @section message_compare_old Comparison with old wfMsg* functions: -* -* Use full parsing: -* -* @code -* // old style: -* wfMsgExt('key', [ 'parseinline' ], 'apple'); -* // new style: -* wfMessage('key', 'apple')->parse(); -* @endcode -* -* Parseinline is used because it is more useful when pre-building HTML. -* In normal use it is better to use OutputPage::(add|wrap)WikiMsg. -* -* Places where HTML cannot be used. {{-transformation is done. -* @code -* // old style: -* wfMsgExt('key', [ 'parsemag' ], 'apple', 'pear'); -* // new style: -* wfMessage('key', 'apple', 'pear')->text(); -* @endcode -* -* Shortcut for escaping the message too, similar to wfMsgHTML(), but -* parameters are not replaced after escaping by default. -* @code -* $escaped = wfMessage('key') -* ->rawParams('apple') -* ->escaped(); -* @endcode -* -* @section message_appendix Appendix: -* -* @todo -* - test, can we have tests? -* - this documentation needs to be extended -* -* @see https://www.mediawiki.org/wiki/WfMessage() -* @see https://www.mediawiki.org/wiki/New_messages_API -* @see https://www.mediawiki.org/wiki/Localisation -* -* @since 1.17 -*/ -public class XomwMessage { -// /** Use message text as-is */ - 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'; - - private static final int FORMAT_NULL = 5; - - private static final int PRM_TID_BEFORE = 1; // "before" - private static final int PRM_TID_AFTER = 2; // "after" - - /** - * 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 byte[] 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 XomwTitleOld title = null; - - /** - * @var Content Content Object representing the message. - */ -// private XomwContent content = null; - - /** - * @var String - */ - private byte[] message; - - public void CompilerAppeasement() { - 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); - this.extractParam(null, null, 0); - } - public XomwMessage(byte[] textBry, XomwLanguage language) { - this.textBry = textBry; - this.language = language; - } - 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 -// * message keys to try and use the first non-empty message for, or a -// * MessageSpecifier to copy from. -// * @param array $prmsVar Message parameters. -// * @param Language $language [optional] Language to use (defaults to current user language). -// * @throws InvalidArgumentException -// */ -// public function __construct(key, $prmsVar = [], Language $language = null) { -// if (key instanceof MessageSpecifier) { -// if ($prmsVar) { -// throw new InvalidArgumentException( -// '$prmsVar must be empty if key is a MessageSpecifier' -// ); -// } -// $prmsVar = key->getParams(); -// key = key->getKey(); -// } -// -// if (!is_string(key) && !is_array(key)) { -// throw new InvalidArgumentException('key must be a String or an array'); -// } -// -// this.keysToTry = (array)key; -// -// if (empty(this.keysToTry)) { -// throw new InvalidArgumentException('key must not be an empty list'); -// } -// -// this.key = reset(this.keysToTry); -// -// this.parameters = array_values($prmsVar); -// // User language is only resolved in getLanguage(). This helps preserve the -// // semantic intent of "user language" across serialize() and unserialize(). -// this.language = $language ?: false; -// } - - // /** - // * @see Serializable::serialize() - // * @since 1.26 - // * @return String - // */ - // public function serialize() { - // return serialize([ - // 'interfaceIsUserLang' => this.interfaceIsUserLang, - // 'language' => this.language ? this.language->getCode() : false, - // 'key' => this.key, - // 'keysToTry' => this.keysToTry, - // 'parameters' => this.parameters, - // 'format' => this.format, - // 'useDatabase' => this.useDatabase, - // 'title' => this.title, - // ]); - // } - // - // /** - // * @see Serializable::unserialize() - // * @since 1.26 - // * @param String $serialized - // */ - // public function unserialize($serialized) { - // $data = unserialize($serialized); - // this.interfaceIsUserLang = $data['interfaceIsUserLang']; - // this.key = $data['key']; - // this.keysToTry = $data['keysToTry']; - // this.parameters = $data['parameters']; - // this.format = $data['format']; - // this.useDatabase = $data['useDatabase']; - // this.language = $data['language'] ? Language::factory($data['language']) : false; - // this.title = $data['title']; - // } - -// /** -// * @since 1.24 -// * -// * @return boolean True if this is a multi-key message, that is, if the key provided to the -// * constructor was a fallback list of keys to try. -// */ -// public function isMultiKey() { -// return count(this.keysToTry) > 1; -// } -// -// /** -// * @since 1.24 -// * -// * @return String[] The list of keys to try when fetching the message text, -// * in order of preference. -// */ -// public function getKeysToTry() { -// return this.keysToTry; -// } - - /** - * Returns the message key. - * - * If a list of multiple possible keys was supplied to the constructor, this method may - * return any of these keys. After the message has been fetched, this method will return - * the key that was actually used to fetch the message. - * - * @since 1.21 - * - * @return String - */ - public byte[] getKey() { - return this.key; - } - -// /** -// * Returns the message parameters. -// * -// * @since 1.21 -// * -// * @return array -// */ -// public function getParams() { -// return this.parameters; -// } -// -// /** -// * Returns the message format. -// * -// * @since 1.21 -// * -// * @return String -// * @deprecated since 1.29 formatting is not stateful -// */ -// public function getFormat() { -// wfDeprecated(__METHOD__, '1.29'); -// return this.format; -// } - - /** - * Returns the Language of the Message. - * - * @since 1.23 - * - * @return Language - */ - public XomwLanguage getLanguage() { - // Defaults to false which means current user language - return this.language; - } - -// /** -// * Factory function that is just wrapper for the real constructor. It is -// * intended to be used instead of the real constructor, because it allows -// * chaining method calls, while new objects don't. -// * -// * @since 1.17 -// * -// * @param String|String[]|MessageSpecifier key -// * @param mixed $param,... Parameters as strings. -// * -// * @return Message -// */ -// public static function newFromKey(key /*...*/) { -// $prmsVar = func_get_args(); -// array_shift($prmsVar); -// return new self(key, $prmsVar); -// } -// -// /** -// * Transform a MessageSpecifier or a primitive value used interchangeably with -// * specifiers (a message key String, or a key + prmsVar array) into a proper Message. -// * -// * Also accepts a MessageSpecifier inside an array: that's not considered a valid format -// * but is an easy error to make due to how StatusValue stores messages internally. -// * Further array elements are ignored in that case. -// * -// * @param String|array|MessageSpecifier $value -// * @return Message -// * @throws InvalidArgumentException -// * @since 1.27 -// */ -// public static function newFromSpecifier($value) { -// $prmsVar = []; -// if (is_array($value)) { -// $prmsVar = $value; -// $value = array_shift($prmsVar); -// } -// -// if ($value instanceof Message) { // Message, RawMessage, ApiMessage, etc -// $message = clone($value); -// } elseif ($value instanceof MessageSpecifier) { -// $message = new Message($value); -// } elseif (is_string($value)) { -// $message = new Message($value, $prmsVar); -// } else { -// throw new InvalidArgumentException(__METHOD__ . ': invalid argument type ' -// . gettype($value)); -// } -// -// return $message; -// } -// -// /** -// * Factory function accepting multiple message keys and returning a message instance -// * for the first message which is non-empty. If all messages are empty then an -// * instance of the first message key is returned. -// * -// * @since 1.18 -// * -// * @param String|String[] $keys,... Message keys, or first argument as an array of all the -// * message keys. -// * -// * @return Message -// */ -// public static function newFallbackSequence(/*...*/) { -// $keys = func_get_args(); -// if (func_num_args() == 1) { -// if (is_array($keys[0])) { -// // Allow an array to be passed as the first argument instead -// $keys = array_values($keys[0]); -// } else { -// // Optimize a single String to not need special fallback handling -// $keys = $keys[0]; -// } -// } -// return new self($keys); -// } -// -// /** -// * Get a title Object for a mediawiki message, where it can be found in the mediawiki namespace. -// * The title will be for the current language, if the message key is in -// * $wgForceUIMsgAsContentMsg it will be append with the language code (except content -// * language), because Message::inContentLanguage will also return in user language. -// * -// * @see $wgForceUIMsgAsContentMsg -// * @return Title -// * @since 1.26 -// */ -// public function getTitle() { -// global $wgContLang, $wgForceUIMsgAsContentMsg; -// -// title = this.key; -// if ( -// !this.language->equals($wgContLang) -// && in_array(this.key, (array)$wgForceUIMsgAsContentMsg) -// ) { -// $code = this.language->getCode(); -// title .= '/' . $code; -// } -// -// return Title::makeTitle(NS_MEDIAWIKI, $wgContLang->ucfirst(strtr(title, ' ', '_'))); -// } -// -// /** -// * Adds parameters to the parameter list of this message. -// * -// * @since 1.17 -// * -// * @param mixed ... Parameters as strings or arrays from -// * Message::numParam() and the like, or a single array of parameters. -// * -// * @return Message this -// */ -// public function prmsVar(/*...*/) { -// $args = func_get_args(); -// -// // If $args has only one entry and it's an array, then it's either a -// // non-varargs call or it happens to be a call with just a single -// // "special" parameter. Since the "special" parameters don't have any -// // numeric keys, we'll test that to differentiate the cases. -// if (count($args) === 1 && isset($args[0]) && is_array($args[0])) { -// if ($args[0] === []) { -// $args = []; -// } else { -// foreach ($args[0] as key => $value) { -// if (is_int(key)) { -// $args = $args[0]; -// break; -// } -// } -// } -// } -// -// this.parameters = array_merge(this.parameters, array_values($args)); -// return this; -// } - - /** - * Add parameters that are substituted after parsing or escaping. - * In other words the parsing process cannot access the contents - * of this type of parameter, and you need to make sure it is - * sanitized beforehand. The parser will see "$n", instead. - * - * @since 1.17 - * - * @param mixed $prmsVar,... Raw parameters as strings, or a single argument that is - * an array of raw parameters. - * - * @return Message this - */ - public XomwMessage rawParams(byte[]... prmsAry) { - for (byte[] prmsVar : prmsAry) { - this.parameters.Add(XomwMessage.rawParam(prmsVar)); - } - return this; - } - - /** - * Add parameters that are numeric and will be passed through - * Language::formatNum before substitution - * - * @since 1.18 - * - * @param mixed $param,... Numeric parameters, or a single argument that is - * an array of numeric parameters. - * - * @return Message this - */ - public XomwMessage numParams(int... prmsAry) { - for (int prmsVar : prmsAry) { - this.parameters.Add(XomwMessage.numParam(prmsVar)); - } - return this; - } - - /** - * Add parameters that are durations of time and will be passed through - * Language::formatDuration before substitution - * - * @since 1.22 - * - * @param int|int[] $param,... Duration parameters, or a single argument that is - * an array of duration parameters. - * - * @return Message this - */ - public XomwMessage durationParams(int... prmsAry) { - for (int prmsVar : prmsAry) { - this.parameters.Add(XomwMessage.durationParam(prmsVar)); - } - return this; - } - - /** - * Add parameters that are expiration times and will be passed through - * Language::formatExpiry before substitution - * - * @since 1.22 - * - * @param String|String[] $param,... Expiry parameters, or a single argument that is - * an array of expiry parameters. - * - * @return Message this - */ - public XomwMessage expiryParams(byte[]... prmsAry) { - for (byte[] prmsVar : prmsAry) { - this.parameters.Add(XomwMessage.expiryParam(prmsVar)); - } - return this; - } - - /** - * Add parameters that are time periods and will be passed through - * Language::formatTimePeriod before substitution - * - * @since 1.22 - * - * @param int|int[] $param,... Time period parameters, or a single argument that is - * an array of time period parameters. - * - * @return Message this - */ - public XomwMessage timeperiodParams(int... prmsAry) { - for (int prmsVar : prmsAry) { - this.parameters.Add(XomwMessage.timeperiodParam(prmsVar)); - } - return this; - } - - /** - * Add parameters that are file sizes and will be passed through - * Language::formatSize before substitution - * - * @since 1.22 - * - * @param int|int[] $param,... Size parameters, or a single argument that is - * an array of size parameters. - * - * @return Message this - */ - public XomwMessage sizeParams(int... prmsAry) { - for (int prmsVar : prmsAry) { - this.parameters.Add(XomwMessage.sizeParam(prmsVar)); - } - return this; - } - - /** - * Add parameters that are bitrates and will be passed through - * Language::formatBitrate before substitution - * - * @since 1.22 - * - * @param int|int[] $param,... Bit rate parameters, or a single argument that is - * an array of bit rate parameters. - * - * @return Message this - */ - public XomwMessage bitrateParams(int... prmsAry) { - for (int prmsVar : prmsAry) { - this.parameters.Add(XomwMessage.bitrateParam(prmsVar)); - } - return this; - } - - /** - * Add parameters that are plaintext and will be passed through without - * the content being evaluated. Plaintext parameters are not valid as - * arguments to parser functions. This differs from XomwMessage.rawParams in - * that the Message cls handles escaping to match the output format. - * - * @since 1.25 - * - * @param String|String[] $param,... plaintext parameters, or a single argument that is - * an array of plaintext parameters. - * - * @return Message this - */ - public XomwMessage plaintextParams(byte[]... prmsAry) { - for (byte[] prmsVar : prmsAry) { - this.parameters.Add(XomwMessage.plaintextParam(prmsVar)); - } - return this; - } - -// /** -// * Set the language and the title from a context Object -// * -// * @since 1.19 -// * -// * @param IContextSource $context -// * -// * @return Message this -// */ -// public function setContext(IContextSource $context) { -// this.inLanguage($context->getLanguage()); -// this.title($context->getTitle()); -// this.interfaceIsUserLang = true; -// -// return this; -// } -// -// /** -// * Request the message in any language that is supported. -// * -// * As a side effect interfaceIsUserLang message status is unconditionally -// * turned off. -// * -// * @since 1.17 -// * @param Language|String $lang Language code or Language Object. -// * @return Message this -// * @throws MWException -// */ -// public function inLanguage($lang) { -// if ($lang instanceof Language) { -// this.language = $lang; -// } elseif (is_string($lang)) { -// if (!this.language instanceof Language || this.language->getCode() != $lang) { -// this.language = Language::factory($lang); -// } -// } elseif ($lang instanceof StubUserLang) { -// this.language = false; -// } else { -// $type = gettype($lang); -// throw new MWException(__METHOD__ . " must be " -// . "passed a String or Language Object; $type given" -// ); -// } -// this.message = null; -// this.interfaceIsUserLang = false; -// return this; -// } - - /** - * Request the message in the wiki's content language, - * unless it is disabled for this message. - * - * @since 1.17 - * @see $wgForceUIMsgAsContentMsg - * - * @return Message this - */ - public XomwMessage inContentLanguage() { -// global $wgForceUIMsgAsContentMsg; -// if (in_array(this.key, (array)$wgForceUIMsgAsContentMsg)) { -// return this; -// } -// -// global $wgContLang; -// this.inLanguage($wgContLang); -// return this; - return this; - } - -// /** -// * Allows manipulating the interfaceIsUserLang message flag directly. -// * Can be used to restore the flag after setting a language. -// * -// * @since 1.20 -// * -// * @param boolean $interfaceIsUserLang -// * -// * @return Message this -// */ -// public function setInterfaceMessageFlag($interfaceIsUserLang) { -// this.interfaceIsUserLang = (boolean)$interfaceIsUserLang; -// return this; -// } -// -// /** -// * Enable or disable database use. -// * -// * @since 1.17 -// * -// * @param boolean $useDatabase -// * -// * @return Message this -// */ -// public function useDatabase($useDatabase) { -// this.useDatabase = (boolean)$useDatabase; -// this.message = null; -// return this; -// } -// -// /** -// * Set the Title Object to use as context when transforming the message -// * -// * @since 1.18 -// * -// * @param Title title -// * -// * @return Message this -// */ -// public function title(title) { -// this.title = title; -// return this; -// } - - /** - * Returns the message as a Content Object. - * - * @return Content - */ - public XomwContent contentFunc() { -// if (this.content == null) { -// this.content = new XomwMessageContent(this); -// } -// -// return this.content; - throw Err_.new_unimplemented(); - } - - /** - * Returns the message parsed from wikitext to HTML. - * - * @since 1.17 - * - * @param String|null $format One of the FORMAT_* constants. Null means use whatever was used - * the last time (this is for B/C and should be avoided). - * - * @return String HTML - */ - - // NOTE: causes issues in C# source; E2A7BC; http://www.fileformat.info/info/unicode/char/29fc/index.htm - private static final byte[] LeftPointingCurvedAngleBracket = gplx.core.encoders.Hex_utl_.Parse_hex_to_bry("E2A7BC"); - public byte[] toString(int format) { - if (format == FORMAT_NULL) { - Gfo_usr_dlg_.Instance.Warn_many("", "", "toString import implicit.*; format=~{0} key=~{1}", format, key); - format = this.format; - } -// byte[] s = this.fetchMessage(); - byte[] s = Bry_.Empty; - - if (s == null) { - // Err on the side of safety, ensure that the output - // is always html safe in the event the message key is - // missing, since in that case its highly likely the - // message key is user-controlled. - // LeftPointingCurvedAngleBracket is used instead of '<' to side-step any - // double-escaping issues. - // (Keep synchronised with mw.Message#toString in JS.) - return Bry_.Escape_html(Bry_.Add(LeftPointingCurvedAngleBracket, key, LeftPointingCurvedAngleBracket)); - } - - // Replace $* with a list of parameters for &uselang=qqx. -// if (XophpString_.strpos(s, "$*") != false) { -// String paramlist = ""; -// if (this.parameters != []) { -// paramlist = ": $" . implode(", $", range(1, count(this.parameters))); -// } -// s = str_replace("$*", paramlist, s); -// } - -// // Replace parameters before text parsing -// s = this.replaceParameters(s, "before", format); -// -// // Maybe transform using the full parser -// if (format == XomwMessage.FORMAT_PARSE) { -// s = this.parseText(s); -// s = Parser::stripOuterParagraph(s); -// } elseif (format == XomwMessage.FORMAT_BLOCK_PARSE) { -// s = this.parseText(s); -// } elseif (format == XomwMessage.FORMAT_TEXT) { -// s = this.transformText(s); -// } elseif (format == XomwMessage.FORMAT_ESCAPED) { -// s = this.transformText(s); -// s = htmlspecialchars(s, ENT_QUOTES, "UTF-8", false); -// } -// -// // Raw parameter replacement -// s = this.replaceParameters(s, "after", format); - - return s; - } - -// /** -// * Magic method implementation of the above (for PHP >= 5.2.0), so we can do, eg: -// * $foo = new Message(key); -// * $String = "$foo"; -// * -// * @since 1.18 -// * -// * @return String -// */ -// public function __toString() { -// // PHP doesn't allow __toString to throw exceptions and will -// // trigger a fatal error if it does. So, catch any exceptions. -// -// try { -// return this.toString(XomwMessage.FORMAT_PARSE); -// } catch (Exception $ex) { -// try { -// trigger_error("Exception caught in " . __METHOD__ . " (message " . this.key . "): " -// . $ex, E_USER_WARNING); -// } catch (Exception $ex) { -// // Doh! Cause a fatal error after all? -// } -// -// return '⧼' . htmlspecialchars(this.key) . '⧽'; -// } -// } -// -// /** -// * Fully parse the text from wikitext to HTML. -// * -// * @since 1.17 -// * -// * @return String Parsed HTML. -// */ -// public function parse() { -// this.format = XomwMessage.FORMAT_PARSE; -// return this.toString(XomwMessage.FORMAT_PARSE); -// } - - /** - * Returns the message text. {{-transformation is done. - * - * @since 1.17 - * - * @return String Unescaped message text. - */ - public byte[] textMw() { - this.format = XomwMessage.FORMAT_TEXT; - return this.toString(XomwMessage.FORMAT_TEXT); - } - -// /** -// * Returns the message text as-is, only parameters are substituted. -// * -// * @since 1.17 -// * -// * @return String Unescaped untransformed message text. -// */ -// public function plain() { -// this.format = XomwMessage.FORMAT_PLAIN; -// return this.toString(XomwMessage.FORMAT_PLAIN); -// } -// -// /** -// * Returns the parsed message text which is always surrounded by a block element. -// * -// * @since 1.17 -// * -// * @return String HTML -// */ -// public function parseAsBlock() { -// this.format = XomwMessage.FORMAT_BLOCK_PARSE; -// return this.toString(XomwMessage.FORMAT_BLOCK_PARSE); -// } -// -// /** -// * Returns the message text. {{-transformation is done and the result -// * is escaped excluding any raw parameters. -// * -// * @since 1.17 -// * -// * @return String Escaped message text. -// */ -// public function escaped() { -// this.format = XomwMessage.FORMAT_ESCAPED; -// return this.toString(XomwMessage.FORMAT_ESCAPED); -// } -// -// /** -// * Check whether a message key has been defined currently. -// * -// * @since 1.17 -// * -// * @return boolean -// */ -// public function exists() { -// return this.fetchMessage() !== false; -// } -// -// /** -// * Check whether a message does not exist, or is an empty String -// * -// * @since 1.18 -// * @todo FIXME: Merge with isDisabled()? -// * -// * @return boolean -// */ -// public function isBlank() { -// $message = this.fetchMessage(); -// return $message === false || $message === ''; -// } -// -// /** -// * Check whether a message does not exist, is an empty String, or is "-". -// * -// * @since 1.18 -// * -// * @return boolean -// */ -// public function isDisabled() { -// $message = this.fetchMessage(); -// return $message === false || $message === '' || $message === '-'; -// } - - /** - * @since 1.17 - * - * @param mixed $raw - * - * @return array Array with a single "raw" key. - */ - private static XomwMessagePrm_raw rawParam(byte[] raw) { - return new XomwMessagePrm_raw(raw); - } - - /** - * @since 1.18 - * - * @param mixed $num - * - * @return array Array with a single "num" key. - */ - private static XomwMessagePrm_num numParam(int num) { - return new XomwMessagePrm_num(num); - } - - /** - * @since 1.22 - * - * @param int $duration - * - * @return int[] Array with a single "duration" key. - */ - private static XomwMessagePrm_duration durationParam(int duration) { - return new XomwMessagePrm_duration(duration); - } - - /** - * @since 1.22 - * - * @param String $expiry - * - * @return String[] Array with a single "expiry" key. - */ - private static XomwMessagePrm_expiry expiryParam(byte[] expiry) { - return new XomwMessagePrm_expiry(expiry); - } - - /** - * @since 1.22 - * - * @param int $period - * - * @return int[] Array with a single "period" key. - */ - private static XomwMessagePrm_period timeperiodParam(int period) { - return new XomwMessagePrm_period(period); - } - - /** - * @since 1.22 - * - * @param int $size - * - * @return int[] Array with a single "size" key. - */ - private static XomwMessagePrm_size sizeParam(int size) { - return new XomwMessagePrm_size(size); - } - - /** - * @since 1.22 - * - * @param int $bitrate - * - * @return int[] Array with a single "bitrate" key. - */ - private static XomwMessagePrm_bitrate bitrateParam(int bitrate) { - return new XomwMessagePrm_bitrate(bitrate); - } - - /** - * @since 1.25 - * - * @param String $plaintext - * - * @return String[] Array with a single "plaintext" key. - */ - private static XomwMessagePrm_plaintext plaintextParam(byte[] plaintext) { - return new XomwMessagePrm_plaintext(plaintext); - } - - /** - * @since 1.29 - * - * @param array $list - * @param String $type 'comma', 'semicolon', 'pipe', 'text' - * @return array Array with "list" and "type" keys. - */ -// private static XomwMessagePrm_list listParam(byte[][] list, String type) { // array $list, $type = 'text' -// if (!listTypeMap.Has(type)) { -// throw new XophpInvalidArgumentException( -// "Invalid type " + type -// ); -// } -// return new XomwMessagePrm_list(list, type); -// } - - /** - * Substitutes any parameters into the message text. - * - * @since 1.17 - * - * @param String $message The message text. - * @param String $type Either "before" or "after". - * @param String $format One of the FORMAT_* constants. - * - * @return String - */ - // DFLT:type="before" -// private byte[] replaceParameters(byte[] message, int type, int format) { -// $replacementKeys = []; -// foreach (this.parameters as $n => $param) { -// list($paramType, $value) = this.extractParam($param, $format); -// if ($type === $paramType) { -// $replacementKeys['$' . ($n + 1)] = $value; -// } -// } -// $message = strtr($message, $replacementKeys); -// return $message; -// return null; -// } - - /** - * Extracts the parameter type and preprocessed the value if needed. - * - * @since 1.18 - * - * @param mixed $param Parameter as defined in this cls. - * @param String $format One of the FORMAT_* constants. - * - * @return array Array with the parameter type (either "before" or "after") and the value. - */ - private void extractParam(XomwMessageVal rv, Object param, int format) { - if (Type_.Is_assignable_from_by_obj(param, XomwMessagePrm.class)) { - XomwMessagePrm prm = (XomwMessagePrm)param; - switch (prm.Tid()) { - case XomwMessagePrm.Tid__raw: - rv.Set(PRM_TID_AFTER, ((XomwMessagePrm_raw)prm).raw); - break; - case XomwMessagePrm.Tid__num: - // Replace number prmsVar always in before step for now. - // No support for combined raw and num prmsVar - rv.Set(PRM_TID_BEFORE, this.getLanguage().formatNum(((XomwMessagePrm_num)prm).numAsBry())); - break; -// case XomwMessagePrm.Tid__duration: -// return [PRM_TID_BEFORE, this.getLanguage()->formatDuration($param['duration']) ]; -// break; -// case XomwMessagePrm.Tid__expiry: -// return [PRM_TID_BEFORE, this.getLanguage()->formatExpiry($param['expiry']) ]; -// break; -// case XomwMessagePrm.Tid__period: -// return [PRM_TID_BEFORE, this.getLanguage()->formatTimePeriod($param['period']) ]; -// break; -// case XomwMessagePrm.Tid__size: -// return [PRM_TID_BEFORE, this.getLanguage()->formatSize($param['size']) ]; -// break; -// case XomwMessagePrm.Tid__bitrate: -// return [PRM_TID_BEFORE, this.getLanguage()->formatBitrate($param['bitrate']) ]; -// break; -// case XomwMessagePrm.Tid__plaintext: -// return [PRM_TID_AFTER, this.formatPlaintext($param['plaintext'], $format) ]; -// break; -// case XomwMessagePrm.Tid__list: -// return this.formatListParam($param['list'], $param['type'], $format); -// break; - default: - String warning = "Invalid parameter for message '" + this.getKey() + "': " + - prm.toString(); - Gfo_usr_dlg_.Instance.Warn_many("", "", warning); - - rv.Set(PRM_TID_BEFORE, Bry_.new_a7("[INVALID]")); - break; - } -// } -// else if ($param instanceof Message) { -// // Match language, flags, etc. to the current message. -// $msg = clone $param; -// if ($msg->language !== this.language || $msg->useDatabase !== this.useDatabase) { -// // Cache depends on these parameters -// $msg->message = null; -// } -// $msg->interfaceIsUserLang = this.interfaceIsUserLang; -// $msg->language = this.language; -// $msg->useDatabase = this.useDatabase; -// $msg->title = this.title; -// -// // DWIM -// if ($format === 'block-parse') { -// $format = 'parse'; -// } -// $msg->format = $format; -// -// // Message objects should not be before parameters because -// // then they'll get double escaped. If the message needs to be -// // escaped, it'll happen right here when we call toString(). -// return [PRM_TID_AFTER, $msg->toString($format) ]; -// } -// else { -// rv.Set(PRM_TID_BEFORE, param); - } - } - -// /** -// * Wrapper for what ever method we use to parse wikitext. -// * -// * @since 1.17 -// * -// * @param String $String Wikitext message contents. -// * -// * @return String Wikitext parsed into HTML. -// */ -// protected function parseText($String) { -// $out = MessageCache::singleton()->parse( -// $String, -// this.title, -// /*linestart*/true, -// this.interfaceIsUserLang, -// this.getLanguage() -// ); -// -// return $out instanceof ParserOutput ? $out->getText() : $out; -// } -// -// /** -// * Wrapper for what ever method we use to {{-transform wikitext. -// * -// * @since 1.17 -// * -// * @param String $String Wikitext message contents. -// * -// * @return String Wikitext with {{-constructs replaced with their values. -// */ -// protected function transformText($String) { -// return MessageCache::singleton()->transform( -// $String, -// this.interfaceIsUserLang, -// this.getLanguage(), -// this.title -// ); -// } -// -// /** -// * Wrapper for what ever method we use to get message contents. -// * -// * @since 1.17 -// * -// * @return String -// * @throws MWException If message key array is empty. -// */ -// protected function fetchMessage() { -// if (this.message === null) { -// $cache = MessageCache::singleton(); -// -// 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; -// this.message = $message; -// } -// return this.message; -// } -// -// /** -// * Formats a message parameter wrapped with 'plaintext'. Ensures that -// * the entire String is displayed unchanged when displayed in the output -// * format. -// * -// * @since 1.25 -// * -// * @param String $plaintext String to ensure plaintext output of -// * @param String $format One of the FORMAT_* constants. -// * -// * @return String Input plaintext encoded for output to $format -// */ -// protected function formatPlaintext($plaintext, $format) { -// switch ($format) { -// case XomwMessage.FORMAT_TEXT: -// case XomwMessage.FORMAT_PLAIN: -// return $plaintext; -// -// case XomwMessage.FORMAT_PARSE: -// case XomwMessage.FORMAT_BLOCK_PARSE: -// case XomwMessage.FORMAT_ESCAPED: -// default: -// return htmlspecialchars($plaintext, ENT_QUOTES); -// -// } -// } -// -// /** -// * Formats a list of parameters as a concatenated String. -// * @since 1.29 -// * @param array $prmsVar -// * @param String $listType -// * @param String $format One of the FORMAT_* constants. -// * @return array Array with the parameter type (either "before" or "after") and the value. -// */ -// protected function formatListParam(array $prmsVar, $listType, $format) { -// if (!isset(XomwMessage.$listTypeMap[$listType])) { -// $warning = 'Invalid list type for message "' . this.getKey() . '": ' -// . htmlspecialchars($listType) -// . ' (prmsVar are ' . htmlspecialchars(serialize($prmsVar)) . ')'; -// trigger_error($warning, E_USER_WARNING); -// $e = new Exception; -// wfDebugLog('Bug58676', $warning . "\n" . $e->getTraceAsString()); -// return [PRM_TID_BEFORE, '[INVALID]' ]; -// } -// $func = XomwMessage.$listTypeMap[$listType]; -// -// // Handle an empty list sensibly -// if (!$prmsVar) { -// return [PRM_TID_BEFORE, this.getLanguage()->$func([]) ]; -// } -// -// // First, determine what kinds of list items we have -// $types = []; -// $vars = []; -// $list = []; -// foreach ($prmsVar as $n => $p) { -// list($type, $value) = this.extractParam($p, $format); -// $types[$type] = true; -// $list[] = $value; -// $vars[] = '$' . ($n + 1); -// } -// -// // Easy case: all are 'before' or 'after', so just join the -// // values and use the same type. -// if (count($types) === 1) { -// return [ key($types), this.getLanguage()->$func($list) ]; -// } -// -// // Hard case: We need to process each value per its type, then -// // return the concatenated values as 'after'. We handle this by turning -// // the list into a RawMessage and processing that as a parameter. -// $vars = this.getLanguage()->$func($vars); -// return this.extractParam(new RawMessage($vars, $prmsVar), $format); -// } -} -class XomwMessageVal { - public int tid; - public byte[] val; - public void Set(int tid, byte[] val) { - this.tid = tid; - this.val = val; - } -} -abstract class XomwMessagePrm { - public XomwMessagePrm(int tid) {this.tid = tid;} - public int Tid() {return tid;} private int tid; - public static final int - Tid__raw = 0 - , Tid__num = 1 - , Tid__duration = 2 - , Tid__expiry = 3 - , Tid__period = 4 - , Tid__size = 5 - , Tid__bitrate = 6 - , Tid__plaintext = 7 - , Tid__list = 8 - ; -} -class XomwMessagePrm_raw extends XomwMessagePrm { public byte[] raw; - public XomwMessagePrm_raw(byte[] raw) {super(Tid__raw); - this.raw = raw; - } -} -class XomwMessagePrm_num extends XomwMessagePrm { public int num; - public byte[] numAsBry() {return Int_.To_bry(num);} - public XomwMessagePrm_num(int num) {super(Tid__num); - this.num = num; - } -} -class XomwMessagePrm_duration extends XomwMessagePrm { public int duration; - public XomwMessagePrm_duration(int duration) {super(Tid__duration); - this.duration = duration; - } -} -class XomwMessagePrm_expiry extends XomwMessagePrm { public byte[] expiry; - public XomwMessagePrm_expiry(byte[] expiry) {super(Tid__expiry); - this.expiry = expiry; - } -} -class XomwMessagePrm_period extends XomwMessagePrm { public int period; - public XomwMessagePrm_period(int period) {super(Tid__period); - this.period = period; - } -} -class XomwMessagePrm_size extends XomwMessagePrm { public int size; - public XomwMessagePrm_size(int size) {super(Tid__size); - this.size = size; - } -} -class XomwMessagePrm_bitrate extends XomwMessagePrm { public int bitrate; - public XomwMessagePrm_bitrate(int bitrate) {super(Tid__bitrate); - this.bitrate = bitrate; - } -} -class XomwMessagePrm_plaintext extends XomwMessagePrm { public byte[] plaintext; - public XomwMessagePrm_plaintext(byte[] plaintext) {super(Tid__plaintext); - this.plaintext = plaintext; - } -} -class XomwMessagePrm_list extends XomwMessagePrm { public byte[][] list; - public String type; - public XomwMessagePrm_list(byte[][] list, String type) {super(Tid__list); - this.list = list; - this.type = type; - } -} +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012-2020 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; + +import gplx.String_; +import gplx.xowa.mediawiki.XophpArray; +import gplx.xowa.mediawiki.XophpArray_; +import gplx.xowa.mediawiki.XophpInvalidArgumentException; +import gplx.xowa.mediawiki.XophpObject_; +import gplx.xowa.mediawiki.XophpString_; +import gplx.xowa.mediawiki.includes.content.XomwContent; +import gplx.xowa.mediawiki.languages.XomwLanguage; + +// MW.SRC:1.33.1 +/** + * The Message class provides methods which fulfil two basic services: + * - fetching interface messages + * - processing messages into a variety of formats + * + * First implemented with MediaWiki 1.17, the Message class is intended to + * replace the old wfMsg* functions that over time grew unusable. + * @see https://www.mediawiki.org/wiki/Manual:Messages_API for equivalences + * between old and new functions. + * + * You should use the wfMessage() global function which acts as a wrapper for + * the Message class. The wrapper let you pass parameters as arguments. + * + * The most basic usage cases would be: + * + * @code + * // Initialize a Message object using the 'some_key' message key + * $message = wfMessage('some_key'); + * + * // Using two parameters those values are strings 'value1' and 'value2': + * $message = wfMessage('some_key', + * 'value1', 'value2' + * ); + * @endcode + * + * @section message_global_fn Global function wrapper: + * + * Since wfMessage() returns a Message instance, you can chain its call with + * 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: + * + * @code + * $button = Xml::button( + * wfMessage('submit').text() + * ); + * @endcode + * + * A Message instance can be passed parameters after it has been constructed, + * use the params() method to do so: + * + * @code + * wfMessage('welcome-to') + * .params($wgSitename) + * .text(); + * @endcode + * + * {{GRAMMAR}} and friends work correctly: + * + * @code + * wfMessage('are-friends', + * $user, $friend + * ); + * wfMessage('bad-message') + * .rawParams('') + * .escaped(); + * @endcode + * + * @section message_language Changing language: + * + * Messages can be requested in a different language or in whatever current + * content language is being used. The methods are: + * - Message.inContentLanguage() + * - Message.inLanguage() + * + * Sometimes the message text ends up in the database, so content language is + * needed: + * + * @code + * wfMessage('file-log', + * $user, $filename + * ).inContentLanguage().text(); + * @endcode + * + * Checking whether a message exists: + * + * @code + * wfMessage('mysterious-message').exists() + * // returns a boolean whether the 'mysterious-message' key exist. + * @endcode + * + * If you want to use a different language: + * + * @code + * $userLanguage = $user.getOption('language'); + * wfMessage('email-header') + * .inLanguage($userLanguage) + * .plain(); + * @endcode + * + * @note You can parse the text only in the content or interface languages + * + * @section message_compare_old Comparison with old wfMsg* functions: + * + * Use full parsing: + * + * @code + * // old style: + * wfMsgExt('key', [ 'parseinline' ], 'apple'); + * // new style: + * wfMessage('key', 'apple').parse(); + * @endcode + * + * Parseinline is used because it is more useful when pre-building HTML. + * In normal use it is better to use OutputPage::(add|wrap)WikiMsg. + * + * Places where HTML cannot be used. {{-transformation is done. + * @code + * // old style: + * wfMsgExt('key', [ 'parsemag' ], 'apple', 'pear'); + * // new style: + * wfMessage('key', 'apple', 'pear').text(); + * @endcode + * + * Shortcut for escaping the message too, similar to wfMsgHTML(), but + * parameters are not replaced after escaping by default. + * @code + * $escaped = wfMessage('key') + * .rawParams('apple') + * .escaped(); + * @endcode + * + * @section message_appendix Appendix: + * + * @todo + * - test, can we have tests? + * - this documentation needs to be extended + * + * @see https://www.mediawiki.org/wiki/WfMessage() + * @see https://www.mediawiki.org/wiki/New_messages_API + * @see https://www.mediawiki.org/wiki/Localisation + * + * @since 1.17 + */ +class XomwMessage { // implements MessageSpecifier, Serializable + /** Use message text as-is */ + static final String FORMAT_PLAIN = "plain"; + /** Use normal wikitext . HTML parsing (the result will be wrapped in a block-level HTML tag) */ + static final String FORMAT_BLOCK_PARSE = "block-parse"; + /** Use normal wikitext . HTML parsing but strip the block-level wrapper */ + static final String FORMAT_PARSE = "parse"; + /** Transform {{..}} constructs but don't transform to HTML */ + static final String FORMAT_TEXT = "text"; + /** Transform {{..}} constructs, HTML-escape the result */ + static final String FORMAT_ESCAPED = "escaped"; + + /** + * Mapping from Message::listParam() types to Language methods. + * @var array + */ + protected static XophpArray listTypeMap = XophpArray.New() + .Add("comma", "commaList") + .Add("semicolon", "semicolonList") + .Add("pipe", "pipeList") + .Add("text", "listToText") + ; + + /** + * In which language to get this message. True, which is the default, + * means the current user language, false content language. + * + * @var bool + */ + protected boolean interface_var = true; + + /** + * In which language to get this message. Overrides the $interface setting. + * + * @var Language|bool Explicit language object, or false for user language + */ + protected XomwLanguage language = null; + + /** + * @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 String key; + + /** + * @var string[] List of keys to try when fetching the message. + */ + protected XophpArray keysToTry; + + /** + * @var array List of parameters which will be substituted into the message. + */ + protected XophpArray parameters = XophpArray.New(); + + /** + * @var string + * @deprecated + */ + protected String format = "parse"; + + /** + * @var bool Whether database can be used. + */ + protected boolean useDatabase = true; + + /** + * @var Title Title object to use as context. + */ + protected XomwTitle title = null; + + /** + * @var Content Content object representing the message. + */ + protected XomwContent content = null; + + /** + * @var string + */ + protected String message; + + /** + * @since 1.17 + * @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|null $language [optional] Language to use (defaults to current user language). + * @throws InvalidArgumentException + */ + public XomwMessage(String key) {this(key, XophpArray.New(), null);} + public XomwMessage(Object key, XophpArray params, XomwLanguage language) { +// if ($key instanceof MessageSpecifier) { +// if ($params) { +// throw new XomwInvalidArgumentException( +// '$params must be empty if $key is a MessageSpecifier' +// ); +// } +// $params = $key.getParams(); +// $key = $key.getKey(); +// } + + // XOMW.TYPE.BGN + XophpArray keysToTry = null; + if (XophpString_.is_string(key)) { + this.keysToTry = XophpArray.New(key); + } + else if (XophpArray_.is_array(key)) { + this.keysToTry = (XophpArray)key; + } + else { + throw new XophpInvalidArgumentException("{0} must be a string or an array", key); + } + // XOMW.TYPE.END + + if (XophpArray_.empty(this.keysToTry)) { + throw new XophpInvalidArgumentException("{0} must not be an empty list", key); + } + + this.key = (String)XophpArray.reset(this.keysToTry); + + this.parameters = XophpArray_.array_values(params); + // User language is only resolved in getLanguage(). This helps preserve the + // semantic intent of "user language" across serialize() and unserialize(). + this.language = XophpObject_.is_true(language) ? language : null; + } + +// /** +// * @see Serializable::serialize() +// * @since 1.26 +// * @return string +// */ +// public function serialize() { +// return serialize([ +// 'interface' => this.interface, +// 'language' => this.language ? this.language.getCode() : false, +// 'key' => this.key, +// 'keysToTry' => this.keysToTry, +// 'parameters' => this.parameters, +// 'format' => this.format, +// 'useDatabase' => this.useDatabase, +// 'titlestr' => this.title ? this.title.getFullText() : null, +// ]); +// } +// +// /** +// * @see Serializable::unserialize() +// * @since 1.26 +// * @param string $serialized +// */ +// public function unserialize($serialized) { +// $data = unserialize($serialized); +// if (!is_array($data)) { +// throw new InvalidArgumentException(__METHOD__ . ': Invalid serialized data'); +// } +// +// this.interface = $data['interface']; +// this.key = $data['key']; +// this.keysToTry = $data['keysToTry']; +// this.parameters = $data['parameters']; +// this.format = $data['format']; +// this.useDatabase = $data['useDatabase']; +// this.language = $data['language'] ? Language::factory($data['language']) : false; +// +// if (isset($data['titlestr'])) { +// this.title = Title::newFromText($data['titlestr']); +// } elseif (isset($data['title']) && $data['title'] instanceof Title) { +// // Old serializations from before December 2018 +// this.title = $data['title']; +// } else { +// this.title = null; // Explicit for sanity +// } +// } +// +// /** +// * @since 1.24 +// * +// * @return bool True if this is a multi-key message, that is, if the key provided to the +// * constructor was a fallback list of keys to try. +// */ +// public function isMultiKey() { +// return count(this.keysToTry) > 1; +// } +// +// /** +// * @since 1.24 +// * +// * @return string[] The list of keys to try when fetching the message text, +// * in order of preference. +// */ +// public function getKeysToTry() { +// return this.keysToTry; +// } +// +// /** +// * Returns the message key. +// * +// * If a list of multiple possible keys was supplied to the constructor, this method may +// * return any of these keys. After the message has been fetched, this method will return +// * the key that was actually used to fetch the message. +// * +// * @since 1.21 +// * +// * @return string +// */ +// public function getKey() { +// return this.key; +// } +// +// /** +// * Returns the message parameters. +// * +// * @since 1.21 +// * +// * @return array +// */ +// public function getParams() { +// return this.parameters; +// } +// +// /** +// * Returns the message format. +// * +// * @since 1.21 +// * +// * @return string +// * @deprecated since 1.29 formatting is not stateful +// */ +// public function getFormat() { +// wfDeprecated(__METHOD__, '1.29'); +// return this.format; +// } +// +// /** +// * Returns the Language of the Message. +// * +// * @since 1.23 +// * +// * @return Language +// */ +// public function getLanguage() { +// // Defaults to false which means current user language +// return this.language ?: RequestContext::getMain().getLanguage(); +// } +// +// /** +// * Factory function that is just wrapper for the real constructor. It is +// * intended to be used instead of the real constructor, because it allows +// * chaining method calls, while new objects don't. +// * +// * @since 1.17 +// * +// * @param string|string[]|MessageSpecifier $key +// * @param mixed $param,... Parameters as strings. +// * +// * @return Message +// */ +// public static function newFromKey($key /*...*/) { +// $params = func_get_args(); +// array_shift($params); +// return new self($key, $params); +// } +// +// /** +// * Transform a MessageSpecifier or a primitive value used interchangeably with +// * specifiers (a message key string, or a key + params array) into a proper Message. +// * +// * Also accepts a MessageSpecifier inside an array: that's not considered a valid format +// * but is an easy error to make due to how StatusValue stores messages internally. +// * Further array elements are ignored in that case. +// * +// * @param string|array|MessageSpecifier $value +// * @return Message +// * @throws InvalidArgumentException +// * @since 1.27 +// */ +// public static function newFromSpecifier($value) { +// $params = []; +// if (is_array($value)) { +// $params = $value; +// $value = array_shift($params); +// } +// +// if ($value instanceof Message) { // Message, RawMessage, ApiMessage, etc +// $message = clone $value; +// } elseif ($value instanceof MessageSpecifier) { +// $message = new Message($value); +// } elseif (is_string($value)) { +// $message = new Message($value, $params); +// } else { +// throw new InvalidArgumentException(__METHOD__ . ': invalid argument type ' +// . gettype($value)); +// } +// +// return $message; +// } +// +// /** +// * Factory function accepting multiple message keys and returning a message instance +// * for the first message which is non-empty. If all messages are empty then an +// * instance of the first message key is returned. +// * +// * @since 1.18 +// * +// * @param string|string[] $keys,... Message keys, or first argument as an array of all the +// * message keys. +// * +// * @return Message +// */ +// public static function newFallbackSequence(/*...*/) { +// $keys = func_get_args(); +// if (func_num_args() == 1) { +// if (is_array($keys[0])) { +// // Allow an array to be passed as the first argument instead +// $keys = array_values($keys[0]); +// } else { +// // Optimize a single string to not need special fallback handling +// $keys = $keys[0]; +// } +// } +// return new self($keys); +// } +// +// /** +// * Get a title object for a mediawiki message, where it can be found in the mediawiki namespace. +// * The title will be for the current language, if the message key is in +// * $wgForceUIMsgAsContentMsg it will be append with the language code (except content +// * language), because Message::inContentLanguage will also return in user language. +// * +// * @see $wgForceUIMsgAsContentMsg +// * @return Title +// * @since 1.26 +// */ +// public function getTitle() { +// global $wgForceUIMsgAsContentMsg; +// +// $contLang = MediaWikiServices::getInstance().getContentLanguage(); +// $lang = this.getLanguage(); +// $title = this.key; +// if ( +// !$lang.equals($contLang) +// && in_array(this.key, (array)$wgForceUIMsgAsContentMsg) +// ) { +// $title .= '/' . $lang.getCode(); +// } +// +// return Title::makeTitle( +// NS_MEDIAWIKI, $contLang.ucfirst(strtr($title, ' ', '_'))); +// } +// +// /** +// * Adds parameters to the parameter list of this message. +// * +// * @since 1.17 +// * +// * @param mixed $args,... Parameters as strings or arrays from +// * Message::numParam() and the like, or a single array of parameters. +// * +// * @return Message $this +// */ +// public function params(/*...*/) { +// $args = func_get_args(); +// +// // If $args has only one entry and it's an array, then it's either a +// // non-varargs call or it happens to be a call with just a single +// // "special" parameter. Since the "special" parameters don't have any +// // numeric keys, we'll test that to differentiate the cases. +// if (count($args) === 1 && isset($args[0]) && is_array($args[0])) { +// if ($args[0] === []) { +// $args = []; +// } else { +// foreach ($args[0] as $key => $value) { +// if (is_int($key)) { +// $args = $args[0]; +// break; +// } +// } +// } +// } +// +// this.parameters = array_merge(this.parameters, array_values($args)); +// return $this; +// } +// +// /** +// * Add parameters that are substituted after parsing or escaping. +// * In other words the parsing process cannot access the contents +// * of this type of parameter, and you need to make sure it is +// * sanitized beforehand. The parser will see "$n", instead. +// * +// * @since 1.17 +// * +// * @param mixed $params,... Raw parameters as strings, or a single argument that is +// * an array of raw parameters. +// * +// * @return Message $this +// */ +// public function rawParams(/*...*/) { +// $params = func_get_args(); +// if (isset($params[0]) && is_array($params[0])) { +// $params = $params[0]; +// } +// foreach ($params as $param) { +// this.parameters[] = self::rawParam($param); +// } +// return $this; +// } +// +// /** +// * Add parameters that are numeric and will be passed through +// * Language::formatNum before substitution +// * +// * @since 1.18 +// * +// * @param mixed $param,... Numeric parameters, or a single argument that is +// * an array of numeric parameters. +// * +// * @return Message $this +// */ +// public function numParams(/*...*/) { +// $params = func_get_args(); +// if (isset($params[0]) && is_array($params[0])) { +// $params = $params[0]; +// } +// foreach ($params as $param) { +// this.parameters[] = self::numParam($param); +// } +// return $this; +// } +// +// /** +// * Add parameters that are durations of time and will be passed through +// * Language::formatDuration before substitution +// * +// * @since 1.22 +// * +// * @param int|int[] $param,... Duration parameters, or a single argument that is +// * an array of duration parameters. +// * +// * @return Message $this +// */ +// public function durationParams(/*...*/) { +// $params = func_get_args(); +// if (isset($params[0]) && is_array($params[0])) { +// $params = $params[0]; +// } +// foreach ($params as $param) { +// this.parameters[] = self::durationParam($param); +// } +// return $this; +// } +// +// /** +// * Add parameters that are expiration times and will be passed through +// * Language::formatExpiry before substitution +// * +// * @since 1.22 +// * +// * @param string|string[] $param,... Expiry parameters, or a single argument that is +// * an array of expiry parameters. +// * +// * @return Message $this +// */ +// public function expiryParams(/*...*/) { +// $params = func_get_args(); +// if (isset($params[0]) && is_array($params[0])) { +// $params = $params[0]; +// } +// foreach ($params as $param) { +// this.parameters[] = self::expiryParam($param); +// } +// return $this; +// } +// +// /** +// * Add parameters that are time periods and will be passed through +// * Language::formatTimePeriod before substitution +// * +// * @since 1.22 +// * +// * @param int|int[] $param,... Time period parameters, or a single argument that is +// * an array of time period parameters. +// * +// * @return Message $this +// */ +// public function timeperiodParams(/*...*/) { +// $params = func_get_args(); +// if (isset($params[0]) && is_array($params[0])) { +// $params = $params[0]; +// } +// foreach ($params as $param) { +// this.parameters[] = self::timeperiodParam($param); +// } +// return $this; +// } +// +// /** +// * Add parameters that are file sizes and will be passed through +// * Language::formatSize before substitution +// * +// * @since 1.22 +// * +// * @param int|int[] $param,... Size parameters, or a single argument that is +// * an array of size parameters. +// * +// * @return Message $this +// */ +// public function sizeParams(/*...*/) { +// $params = func_get_args(); +// if (isset($params[0]) && is_array($params[0])) { +// $params = $params[0]; +// } +// foreach ($params as $param) { +// this.parameters[] = self::sizeParam($param); +// } +// return $this; +// } +// +// /** +// * Add parameters that are bitrates and will be passed through +// * Language::formatBitrate before substitution +// * +// * @since 1.22 +// * +// * @param int|int[] $param,... Bit rate parameters, or a single argument that is +// * an array of bit rate parameters. +// * +// * @return Message $this +// */ +// public function bitrateParams(/*...*/) { +// $params = func_get_args(); +// if (isset($params[0]) && is_array($params[0])) { +// $params = $params[0]; +// } +// foreach ($params as $param) { +// this.parameters[] = self::bitrateParam($param); +// } +// return $this; +// } +// +// /** +// * Add parameters that are plaintext and will be passed through without +// * the content being evaluated. Plaintext parameters are not valid as +// * arguments to parser functions. This differs from self::rawParams in +// * that the Message class handles escaping to match the output format. +// * +// * @since 1.25 +// * +// * @param string|string[] $param,... plaintext parameters, or a single argument that is +// * an array of plaintext parameters. +// * +// * @return Message $this +// */ +// public function plaintextParams(/*...*/) { +// $params = func_get_args(); +// if (isset($params[0]) && is_array($params[0])) { +// $params = $params[0]; +// } +// foreach ($params as $param) { +// this.parameters[] = self::plaintextParam($param); +// } +// return $this; +// } +// +// /** +// * Set the language and the title from a context object +// * +// * @since 1.19 +// * +// * @param IContextSource $context +// * +// * @return Message $this +// */ +// public function setContext(IContextSource $context) { +// this.inLanguage($context.getLanguage()); +// this.title($context.getTitle()); +// this.interface = true; +// +// return $this; +// } +// +// /** +// * Request the message in any language that is supported. +// * +// * As a side effect interface message status is unconditionally +// * turned off. +// * +// * @since 1.17 +// * @param Language|string $lang Language code or Language object. +// * @return Message $this +// * @throws MWException +// */ +// public function inLanguage($lang) { +// $previousLanguage = this.language; +// +// if ($lang instanceof Language) { +// this.language = $lang; +// } elseif (is_string($lang)) { +// if (!this.language instanceof Language || this.language.getCode() != $lang) { +// this.language = Language::factory($lang); +// } +// } elseif ($lang instanceof StubUserLang) { +// this.language = false; +// } else { +// $type = gettype($lang); +// throw new MWException(__METHOD__ . " must be " +// . "passed a String or Language object; $type given" +// ); +// } +// +// if (this.language !== $previousLanguage) { +// // The language has changed. Clear the message cache. +// this.message = null; +// } +// this.interface = false; +// return $this; +// } +// +// /** +// * Request the message in the wiki's content language, +// * unless it is disabled for this message. +// * +// * @since 1.17 +// * @see $wgForceUIMsgAsContentMsg +// * +// * @return Message $this +// */ +// public function inContentLanguage() { +// global $wgForceUIMsgAsContentMsg; +// if (in_array(this.key, (array)$wgForceUIMsgAsContentMsg)) { +// return $this; +// } +// +// this.inLanguage(MediaWikiServices::getInstance().getContentLanguage()); +// return $this; +// } +// +// /** +// * Allows manipulating the interface message flag directly. +// * Can be used to restore the flag after setting a language. +// * +// * @since 1.20 +// * +// * @param bool $interface +// * +// * @return Message $this +// */ +// public function setInterfaceMessageFlag($interface) { +// this.interface = (bool)$interface; +// return $this; +// } +// +// /** +// * Enable or disable database use. +// * +// * @since 1.17 +// * +// * @param bool $useDatabase +// * +// * @return Message $this +// */ +// public function useDatabase($useDatabase) { +// this.useDatabase = (bool)$useDatabase; +// this.message = null; +// return $this; +// } +// +// /** +// * Set the Title object to use as context when transforming the message +// * +// * @since 1.18 +// * +// * @param Title $title +// * +// * @return Message $this +// */ +// public function title($title) { +// this.title = $title; +// return $this; +// } +// +// /** +// * Returns the message as a Content object. +// * +// * @return Content +// */ +// public function content() { +// if (!this.content) { +// this.content = new MessageContent($this); +// } +// +// return this.content; +// } +// +// /** +// * Returns the message parsed from wikitext to HTML. +// * +// * @since 1.17 +// * +// * @param string|null $format One of the FORMAT_* constants. Null means use whatever was used +// * the last time (this is for B/C and should be avoided). +// * +// * @return string HTML +// * @suppress SecurityCheck-DoubleEscaped phan false positive +// */ +// public function toString($format = null) { +// if ($format === null) { +// $ex = new LogicException(__METHOD__ . ' using implicit format: ' . this.format); +// \MediaWiki\Logger\LoggerFactory::getInstance('message-format').warning( +// $ex.getMessage(), [ 'exception' => $ex, 'format' => this.format, 'key' => this.key ]); +// $format = this.format; +// } +// $string = this.fetchMessage(); +// +// if ($string === false) { +// // Err on the side of safety, ensure that the output +// // is always html safe in the event the message key is +// // missing, since in that case its highly likely the +// // message key is user-controlled. +// // '⧼' is used instead of '<' to side-step any +// // double-escaping issues. +// // (Keep synchronised with mw.Message#toString in JS.) +// return '⧼' . htmlspecialchars(this.key) . '⧽'; +// } +// +// # Replace $* with a list of parameters for &uselang=qqx. +// if (strpos($string, '$*') !== false) { +// $paramlist = ''; +// if (this.parameters !== []) { +// $paramlist = ': $' . implode(', $', range(1, count(this.parameters))); +// } +// $string = str_replace('$*', $paramlist, $string); +// } +// +// # Replace parameters before text parsing +// $string = this.replaceParameters($string, 'before', $format); +// +// # Maybe transform using the full parser +// if ($format === self::FORMAT_PARSE) { +// $string = this.parseText($string); +// $string = Parser::stripOuterParagraph($string); +// } elseif ($format === self::FORMAT_BLOCK_PARSE) { +// $string = this.parseText($string); +// } elseif ($format === self::FORMAT_TEXT) { +// $string = this.transformText($string); +// } elseif ($format === self::FORMAT_ESCAPED) { +// $string = this.transformText($string); +// $string = htmlspecialchars($string, ENT_QUOTES, 'UTF-8', false); +// } +// +// # Raw parameter replacement +// $string = this.replaceParameters($string, 'after', $format); +// +// return $string; +// } +// +// /** +// * Magic method implementation of the above (for PHP >= 5.2.0), so we can do, eg: +// * $foo = new Message($key); +// * $string = "$foo"; +// * +// * @since 1.18 +// * +// * @return string +// */ +// public function __toString() { +// // PHP doesn't allow __toString to throw exceptions and will +// // trigger a fatal error if it does. So, catch any exceptions. +// +// try { +// return this.toString(self::FORMAT_PARSE); +// } catch (Exception $ex) { +// try { +// trigger_error("Exception caught in " . __METHOD__ . " (message " . this.key . "): " +// . $ex, E_USER_WARNING); +// } catch (Exception $ex) { +// // Doh! Cause a fatal error after all? +// } +// +// return '⧼' . htmlspecialchars(this.key) . '⧽'; +// } +// } +// +// /** +// * Fully parse the text from wikitext to HTML. +// * +// * @since 1.17 +// * +// * @return string Parsed HTML. +// */ +// public function parse() { +// this.format = self::FORMAT_PARSE; +// return this.toString(self::FORMAT_PARSE); +// } +// +// /** +// * Returns the message text. {{-transformation is done. +// * +// * @since 1.17 +// * +// * @return string Unescaped message text. +// */ +// public function text() { +// this.format = self::FORMAT_TEXT; +// return this.toString(self::FORMAT_TEXT); +// } +// +// /** +// * Returns the message text as-is, only parameters are substituted. +// * +// * @since 1.17 +// * +// * @return string Unescaped untransformed message text. +// */ +// public function plain() { +// this.format = self::FORMAT_PLAIN; +// return this.toString(self::FORMAT_PLAIN); +// } +// +// /** +// * Returns the parsed message text which is always surrounded by a block element. +// * +// * @since 1.17 +// * +// * @return string HTML +// */ +// public function parseAsBlock() { +// this.format = self::FORMAT_BLOCK_PARSE; +// return this.toString(self::FORMAT_BLOCK_PARSE); +// } +// +// /** +// * Returns the message text. {{-transformation is done and the result +// * is escaped excluding any raw parameters. +// * +// * @since 1.17 +// * +// * @return string Escaped message text. +// */ +// public function escaped() { +// this.format = self::FORMAT_ESCAPED; +// return this.toString(self::FORMAT_ESCAPED); +// } +// +// /** +// * Check whether a message key has been defined currently. +// * +// * @since 1.17 +// * +// * @return bool +// */ +// public function exists() { +// return this.fetchMessage() !== false; +// } +// +// /** +// * Check whether a message does not exist, or is an empty string +// * +// * @since 1.18 +// * @todo FIXME: Merge with isDisabled()? +// * +// * @return bool +// */ +// public function isBlank() { +// $message = this.fetchMessage(); +// return $message === false || $message === ''; +// } +// +// /** +// * Check whether a message does not exist, is an empty string, or is "-". +// * +// * @since 1.18 +// * +// * @return bool +// */ +// public function isDisabled() { +// $message = this.fetchMessage(); +// return $message === false || $message === '' || $message === '-'; +// } +// +// /** +// * @since 1.17 +// * +// * @param mixed $raw +// * +// * @return array Array with a single "raw" key. +// */ +// public static function rawParam($raw) { +// return [ 'raw' => $raw ]; +// } +// +// /** +// * @since 1.18 +// * +// * @param mixed $num +// * +// * @return array Array with a single "num" key. +// */ +// public static function numParam($num) { +// return [ 'num' => $num ]; +// } +// +// /** +// * @since 1.22 +// * +// * @param int $duration +// * +// * @return int[] Array with a single "duration" key. +// */ +// public static function durationParam($duration) { +// return [ 'duration' => $duration ]; +// } +// +// /** +// * @since 1.22 +// * +// * @param string $expiry +// * +// * @return string[] Array with a single "expiry" key. +// */ +// public static function expiryParam($expiry) { +// return [ 'expiry' => $expiry ]; +// } +// +// /** +// * @since 1.22 +// * +// * @param int $period +// * +// * @return int[] Array with a single "period" key. +// */ +// public static function timeperiodParam($period) { +// return [ 'period' => $period ]; +// } +// +// /** +// * @since 1.22 +// * +// * @param int $size +// * +// * @return int[] Array with a single "size" key. +// */ +// public static function sizeParam($size) { +// return [ 'size' => $size ]; +// } +// +// /** +// * @since 1.22 +// * +// * @param int $bitrate +// * +// * @return int[] Array with a single "bitrate" key. +// */ +// public static function bitrateParam($bitrate) { +// return [ 'bitrate' => $bitrate ]; +// } +// +// /** +// * @since 1.25 +// * +// * @param string $plaintext +// * +// * @return string[] Array with a single "plaintext" key. +// */ +// public static function plaintextParam($plaintext) { +// return [ 'plaintext' => $plaintext ]; +// } +// +// /** +// * @since 1.29 +// * +// * @param array $list +// * @param string $type 'comma', 'semicolon', 'pipe', 'text' +// * @return array Array with "list" and "type" keys. +// */ +// public static function listParam(array $list, $type = 'text') { +// if (!isset(self::$listTypeMap[$type])) { +// throw new InvalidArgumentException( +// "Invalid type '$type'. Known types are: " . implode(', ', array_keys(self::$listTypeMap)) +// ); +// } +// return [ 'list' => $list, 'type' => $type ]; +// } +// +// /** +// * Substitutes any parameters into the message text. +// * +// * @since 1.17 +// * +// * @param string $message The message text. +// * @param string $type Either "before" or "after". +// * @param string $format One of the FORMAT_* constants. +// * +// * @return string +// */ +// protected function replaceParameters($message, $type, $format) { +// // A temporary marker for $1 parameters that is only valid +// // in non-attribute contexts. However if the entire message is escaped +// // then we don't want to use it because it will be mangled in all contexts +// // and its unnessary as .escaped() messages aren't html. +// $marker = $format === self::FORMAT_ESCAPED ? '$' : '$\'"'; +// $replacementKeys = []; +// foreach (this.parameters as $n => $param) { +// list($paramType, $value) = this.extractParam($param, $format); +// if ($type === 'before') { +// if ($paramType === 'before') { +// $replacementKeys['$' . ($n + 1)] = $value; +// } else /* $paramType === 'after' */ { +// // To protect against XSS from replacing parameters +// // inside html attributes, we convert $1 to $'"1. +// // In the event that one of the parameters ends up +// // in an attribute, either the ' or the " will be +// // escaped, breaking the replacement and avoiding XSS. +// $replacementKeys['$' . ($n + 1)] = $marker . ($n + 1); +// } +// } elseif ($paramType === 'after') { +// $replacementKeys[$marker . ($n + 1)] = $value; +// } +// } +// return strtr($message, $replacementKeys); +// } +// +// /** +// * Extracts the parameter type and preprocessed the value if needed. +// * +// * @since 1.18 +// * +// * @param mixed $param Parameter as defined in this class. +// * @param string $format One of the FORMAT_* constants. +// * +// * @return array Array with the parameter type (either "before" or "after") and the value. +// */ +// protected function extractParam($param, $format) { +// if (is_array($param)) { +// if (isset($param['raw'])) { +// return [ 'after', $param['raw'] ]; +// } elseif (isset($param['num'])) { +// // Replace number params always in before step for now. +// // No support for combined raw and num params +// return [ 'before', this.getLanguage().formatNum($param['num']) ]; +// } elseif (isset($param['duration'])) { +// return [ 'before', this.getLanguage().formatDuration($param['duration']) ]; +// } elseif (isset($param['expiry'])) { +// return [ 'before', this.getLanguage().formatExpiry($param['expiry']) ]; +// } elseif (isset($param['period'])) { +// return [ 'before', this.getLanguage().formatTimePeriod($param['period']) ]; +// } elseif (isset($param['size'])) { +// return [ 'before', this.getLanguage().formatSize($param['size']) ]; +// } elseif (isset($param['bitrate'])) { +// return [ 'before', this.getLanguage().formatBitrate($param['bitrate']) ]; +// } elseif (isset($param['plaintext'])) { +// return [ 'after', this.formatPlaintext($param['plaintext'], $format) ]; +// } elseif (isset($param['list'])) { +// return this.formatListParam($param['list'], $param['type'], $format); +// } else { +// if (!is_scalar($param)) { +// $param = serialize($param); +// } +// \MediaWiki\Logger\LoggerFactory::getInstance('Bug58676').warning( +// 'Invalid parameter for message "{msgkey}": {param}', +// [ +// 'exception' => new Exception, +// 'msgkey' => this.getKey(), +// 'param' => htmlspecialchars($param), +// ] +// ); +// +// return [ 'before', '[INVALID]' ]; +// } +// } elseif ($param instanceof Message) { +// // Match language, flags, etc. to the current message. +// $msg = clone $param; +// if ($msg.language !== this.language || $msg.useDatabase !== this.useDatabase) { +// // Cache depends on these parameters +// $msg.message = null; +// } +// $msg.interface = this.interface; +// $msg.language = this.language; +// $msg.useDatabase = this.useDatabase; +// $msg.title = this.title; +// +// // DWIM +// if ($format === 'block-parse') { +// $format = 'parse'; +// } +// $msg.format = $format; +// +// // Message objects should not be before parameters because +// // then they'll get double escaped. If the message needs to be +// // escaped, it'll happen right here when we call toString(). +// return [ 'after', $msg.toString($format) ]; +// } else { +// return [ 'before', $param ]; +// } +// } +// +// /** +// * Wrapper for what ever method we use to parse wikitext. +// * +// * @since 1.17 +// * +// * @param string $string Wikitext message contents. +// * +// * @return string Wikitext parsed into HTML. +// */ +// protected function parseText($string) { +// $out = MessageCache::singleton().parse( +// $string, +// this.title, +// /*linestart*/true, +// this.interface, +// this.getLanguage() +// ); +// +// return $out instanceof ParserOutput +// ? $out.getText([ +// 'enableSectionEditLinks' => false, +// // Wrapping messages in an extra
is probably not expected. If +// // they're outside the content area they probably shouldn't be +// // targeted by CSS that's targeting the parser output, and if +// // they're inside they already are from the outer div. +// 'unwrap' => true, +// ]) +// : $out; +// } +// +// /** +// * Wrapper for what ever method we use to {{-transform wikitext. +// * +// * @since 1.17 +// * +// * @param string $string Wikitext message contents. +// * +// * @return string Wikitext with {{-constructs replaced with their values. +// */ +// protected function transformText($string) { +// return MessageCache::singleton().transform( +// $string, +// this.interface, +// this.getLanguage(), +// this.title +// ); +// } +// +// /** +// * Wrapper for what ever method we use to get message contents. +// * +// * @since 1.17 +// * +// * @return string +// * @throws MWException If message key array is empty. +// */ +// protected function fetchMessage() { +// if (this.message === null) { +// $cache = MessageCache::singleton(); +// +// 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; +// this.message = $message; +// } +// return this.message; +// } +// +// /** +// * Formats a message parameter wrapped with 'plaintext'. Ensures that +// * the entire string is displayed unchanged when displayed in the output +// * format. +// * +// * @since 1.25 +// * +// * @param string $plaintext String to ensure plaintext output of +// * @param string $format One of the FORMAT_* constants. +// * +// * @return string Input plaintext encoded for output to $format +// */ +// protected function formatPlaintext($plaintext, $format) { +// switch ($format) { +// case self::FORMAT_TEXT: +// case self::FORMAT_PLAIN: +// return $plaintext; +// +// case self::FORMAT_PARSE: +// case self::FORMAT_BLOCK_PARSE: +// case self::FORMAT_ESCAPED: +// default: +// return htmlspecialchars($plaintext, ENT_QUOTES); +// } +// } +// +// /** +// * Formats a list of parameters as a concatenated string. +// * @since 1.29 +// * @param array $params +// * @param string $listType +// * @param string $format One of the FORMAT_* constants. +// * @return array Array with the parameter type (either "before" or "after") and the value. +// */ +// protected function formatListParam(array $params, $listType, $format) { +// if (!isset(self::$listTypeMap[$listType])) { +// $warning = 'Invalid list type for message "' . this.getKey() . '": ' +// . htmlspecialchars($listType) +// . ' (params are ' . htmlspecialchars(serialize($params)) . ')'; +// trigger_error($warning, E_USER_WARNING); +// $e = new Exception; +// wfDebugLog('Bug58676', $warning . "\n" . $e.getTraceAsString()); +// return [ 'before', '[INVALID]' ]; +// } +// $func = self::$listTypeMap[$listType]; +// +// // Handle an empty list sensibly +// if (!$params) { +// return [ 'before', this.getLanguage().$func([]) ]; +// } +// +// // First, determine what kinds of list items we have +// $types = []; +// $vars = []; +// $list = []; +// foreach ($params as $n => $p) { +// list($type, $value) = this.extractParam($p, $format); +// $types[$type] = true; +// $list[] = $value; +// $vars[] = '$' . ($n + 1); +// } +// +// // Easy case: all are 'before' or 'after', so just join the +// // values and use the same type. +// if (count($types) === 1) { +// return [ key($types), this.getLanguage().$func($list) ]; +// } +// +// // Hard case: We need to process each value per its type, then +// // return the concatenated values as 'after'. We handle this by turning +// // the list into a RawMessage and processing that as a parameter. +// $vars = this.getLanguage().$func($vars); +// return this.extractParam(new RawMessage($vars, $params), $format); +// } +} diff --git a/400_xowa/src/gplx/xowa/mediawiki/includes/XomwMessageMgr.java b/400_xowa/src/gplx/xowa/mediawiki/includes/XomwMessageMgr.java index 4994f7cd8..6fd9af26c 100644 --- a/400_xowa/src/gplx/xowa/mediawiki/includes/XomwMessageMgr.java +++ b/400_xowa/src/gplx/xowa/mediawiki/includes/XomwMessageMgr.java @@ -13,12 +13,12 @@ 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; import gplx.*; import gplx.xowa.*; import gplx.xowa.mediawiki.*; -import gplx.xowa.mediawiki.languages.*; -public class XomwMessageMgr { - private final Hash_adp hash = Hash_adp_.New(); - public void Add(String key, String val, XomwLanguage language) { - hash.Add(key, new XomwMessage(Bry_.new_u8(val), language)); - } - public XomwMessage Get_by_str(String key) {return (XomwMessage)hash.Get_by(key);} -} +package gplx.xowa.mediawiki.includes; import gplx.*; +import gplx.xowa.mediawiki.languages.*; +public class XomwMessageMgr { + private final Hash_adp hash = Hash_adp_.New(); + public void Old_add(String key, String val, XomwLanguage language) { + hash.Add(key, new XomwMessageOld(Bry_.new_u8(val), language)); + } + public XomwMessageOld Old_get_by_str(String key) {return (XomwMessageOld)hash.Get_by(key);} +} diff --git a/400_xowa/src/gplx/xowa/mediawiki/includes/XomwMessageOld.java b/400_xowa/src/gplx/xowa/mediawiki/includes/XomwMessageOld.java new file mode 100644 index 000000000..ccc67e9ed --- /dev/null +++ b/400_xowa/src/gplx/xowa/mediawiki/includes/XomwMessageOld.java @@ -0,0 +1,1416 @@ +/* +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; import gplx.*; +import gplx.xowa.mediawiki.languages.*; +import gplx.xowa.mediawiki.includes.content.*; +/** +* The Message cls provides methods which fulfil two basic services: +* - fetching interfaceIsUserLang messages +* - processing messages into a variety of formats +* +* First implemented with MediaWiki 1.17, the Message cls is intended to +* replace the old wfMsg* functions that over time grew unusable. +* @see https://www.mediawiki.org/wiki/Manual:Messages_API for equivalences +* between old and new functions. +* +* You should use the wfMessage() global function which acts as a wrapper for +* the Message cls. The wrapper let you pass parameters as arguments. +* +* The most basic usage cases would be: +* +* @code +* // Initialize a Message Object using the 'some_key' message key +* $message = wfMessage('some_key'); +* +* // Using two parameters those values are strings 'value1' and 'value2': +* $message = wfMessage('some_key', +* 'value1', 'value2' +* ); +* @endcode +* +* @section message_global_fn Global function wrapper: +* +* Since wfMessage() returns a Message instance, you can chain its call with +* 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 interfaceIsUserLang message: +* +* @code +* $button = Xml::button( +* wfMessage('submit')->text() +* ); +* @endcode +* +* A Message instance can be passed parameters after it has been constructed, +* use the prms() method to do so: +* +* @code +* wfMessage('welcome-to') +* ->prmsVar($wgSitename) +* ->text(); +* @endcode +* +* {{GRAMMAR}} and friends work correctly: +* +* @code +* wfMessage('are-friends', +* $user, $friend +* ); +* wfMessage('bad-message') +* ->rawParams('') +* ->escaped(); +* @endcode +* +* @section message_language Changing language: +* +* Messages can be requested in a different language or in whatever current +* content language is being used. The methods are: +* - Message->inContentLanguage() +* - Message->inLanguage() +* +* Sometimes the message text ends up in the database, so content language is +* needed: +* +* @code +* wfMessage('file-log', +* $user, $filename +* )->inContentLanguage()->text(); +* @endcode +* +* Checking whether a message exists: +* +* @code +* wfMessage('mysterious-message')->exists() +* // returns a boolean whether the 'mysterious-message' key exist. +* @endcode +* +* If you want to use a different language: +* +* @code +* $userLanguage = $user->getOption('language'); +* wfMessage('email-header') +* ->inLanguage($userLanguage) +* ->plain(); +* @endcode +* +* @note You can parse the text only in the content or interfaceIsUserLang languages +* +* @section message_compare_old Comparison with old wfMsg* functions: +* +* Use full parsing: +* +* @code +* // old style: +* wfMsgExt('key', [ 'parseinline' ], 'apple'); +* // new style: +* wfMessage('key', 'apple')->parse(); +* @endcode +* +* Parseinline is used because it is more useful when pre-building HTML. +* In normal use it is better to use OutputPage::(add|wrap)WikiMsg. +* +* Places where HTML cannot be used. {{-transformation is done. +* @code +* // old style: +* wfMsgExt('key', [ 'parsemag' ], 'apple', 'pear'); +* // new style: +* wfMessage('key', 'apple', 'pear')->text(); +* @endcode +* +* Shortcut for escaping the message too, similar to wfMsgHTML(), but +* parameters are not replaced after escaping by default. +* @code +* $escaped = wfMessage('key') +* ->rawParams('apple') +* ->escaped(); +* @endcode +* +* @section message_appendix Appendix: +* +* @todo +* - test, can we have tests? +* - this documentation needs to be extended +* +* @see https://www.mediawiki.org/wiki/WfMessage() +* @see https://www.mediawiki.org/wiki/New_messages_API +* @see https://www.mediawiki.org/wiki/Localisation +* +* @since 1.17 +*/ +public class XomwMessageOld { +// /** Use message text as-is */ + 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'; + + private static final int FORMAT_NULL = 5; + + private static final int PRM_TID_BEFORE = 1; // "before" + private static final int PRM_TID_AFTER = 2; // "after" + + /** + * 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 byte[] 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 XomwTitleOld title = null; + + /** + * @var Content Content Object representing the message. + */ +// private XomwContent content = null; + + /** + * @var String + */ + private byte[] message; + + public void CompilerAppeasement() { + 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); + this.extractParam(null, null, 0); + } + public XomwMessageOld(byte[] textBry, XomwLanguage language) { + this.textBry = textBry; + this.language = language; + } + 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 +// * message keys to try and use the first non-empty message for, or a +// * MessageSpecifier to copy from. +// * @param array $prmsVar Message parameters. +// * @param Language $language [optional] Language to use (defaults to current user language). +// * @throws InvalidArgumentException +// */ +// public function __construct(key, $prmsVar = [], Language $language = null) { +// if (key instanceof MessageSpecifier) { +// if ($prmsVar) { +// throw new InvalidArgumentException( +// '$prmsVar must be empty if key is a MessageSpecifier' +// ); +// } +// $prmsVar = key->getParams(); +// key = key->getKey(); +// } +// +// if (!is_string(key) && !is_array(key)) { +// throw new InvalidArgumentException('key must be a String or an array'); +// } +// +// this.keysToTry = (array)key; +// +// if (empty(this.keysToTry)) { +// throw new InvalidArgumentException('key must not be an empty list'); +// } +// +// this.key = reset(this.keysToTry); +// +// this.parameters = array_values($prmsVar); +// // User language is only resolved in getLanguage(). This helps preserve the +// // semantic intent of "user language" across serialize() and unserialize(). +// this.language = $language ?: false; +// } + + // /** + // * @see Serializable::serialize() + // * @since 1.26 + // * @return String + // */ + // public function serialize() { + // return serialize([ + // 'interfaceIsUserLang' => this.interfaceIsUserLang, + // 'language' => this.language ? this.language->getCode() : false, + // 'key' => this.key, + // 'keysToTry' => this.keysToTry, + // 'parameters' => this.parameters, + // 'format' => this.format, + // 'useDatabase' => this.useDatabase, + // 'title' => this.title, + // ]); + // } + // + // /** + // * @see Serializable::unserialize() + // * @since 1.26 + // * @param String $serialized + // */ + // public function unserialize($serialized) { + // $data = unserialize($serialized); + // this.interfaceIsUserLang = $data['interfaceIsUserLang']; + // this.key = $data['key']; + // this.keysToTry = $data['keysToTry']; + // this.parameters = $data['parameters']; + // this.format = $data['format']; + // this.useDatabase = $data['useDatabase']; + // this.language = $data['language'] ? Language::factory($data['language']) : false; + // this.title = $data['title']; + // } + +// /** +// * @since 1.24 +// * +// * @return boolean True if this is a multi-key message, that is, if the key provided to the +// * constructor was a fallback list of keys to try. +// */ +// public function isMultiKey() { +// return count(this.keysToTry) > 1; +// } +// +// /** +// * @since 1.24 +// * +// * @return String[] The list of keys to try when fetching the message text, +// * in order of preference. +// */ +// public function getKeysToTry() { +// return this.keysToTry; +// } + + /** + * Returns the message key. + * + * If a list of multiple possible keys was supplied to the constructor, this method may + * return any of these keys. After the message has been fetched, this method will return + * the key that was actually used to fetch the message. + * + * @since 1.21 + * + * @return String + */ + public byte[] getKey() { + return this.key; + } + +// /** +// * Returns the message parameters. +// * +// * @since 1.21 +// * +// * @return array +// */ +// public function getParams() { +// return this.parameters; +// } +// +// /** +// * Returns the message format. +// * +// * @since 1.21 +// * +// * @return String +// * @deprecated since 1.29 formatting is not stateful +// */ +// public function getFormat() { +// wfDeprecated(__METHOD__, '1.29'); +// return this.format; +// } + + /** + * Returns the Language of the Message. + * + * @since 1.23 + * + * @return Language + */ + public XomwLanguage getLanguage() { + // Defaults to false which means current user language + return this.language; + } + +// /** +// * Factory function that is just wrapper for the real constructor. It is +// * intended to be used instead of the real constructor, because it allows +// * chaining method calls, while new objects don't. +// * +// * @since 1.17 +// * +// * @param String|String[]|MessageSpecifier key +// * @param mixed $param,... Parameters as strings. +// * +// * @return Message +// */ +// public static function newFromKey(key /*...*/) { +// $prmsVar = func_get_args(); +// array_shift($prmsVar); +// return new self(key, $prmsVar); +// } +// +// /** +// * Transform a MessageSpecifier or a primitive value used interchangeably with +// * specifiers (a message key String, or a key + prmsVar array) into a proper Message. +// * +// * Also accepts a MessageSpecifier inside an array: that's not considered a valid format +// * but is an easy error to make due to how StatusValue stores messages internally. +// * Further array elements are ignored in that case. +// * +// * @param String|array|MessageSpecifier $value +// * @return Message +// * @throws InvalidArgumentException +// * @since 1.27 +// */ +// public static function newFromSpecifier($value) { +// $prmsVar = []; +// if (is_array($value)) { +// $prmsVar = $value; +// $value = array_shift($prmsVar); +// } +// +// if ($value instanceof Message) { // Message, RawMessage, ApiMessage, etc +// $message = clone($value); +// } elseif ($value instanceof MessageSpecifier) { +// $message = new Message($value); +// } elseif (is_string($value)) { +// $message = new Message($value, $prmsVar); +// } else { +// throw new InvalidArgumentException(__METHOD__ . ': invalid argument type ' +// . gettype($value)); +// } +// +// return $message; +// } +// +// /** +// * Factory function accepting multiple message keys and returning a message instance +// * for the first message which is non-empty. If all messages are empty then an +// * instance of the first message key is returned. +// * +// * @since 1.18 +// * +// * @param String|String[] $keys,... Message keys, or first argument as an array of all the +// * message keys. +// * +// * @return Message +// */ +// public static function newFallbackSequence(/*...*/) { +// $keys = func_get_args(); +// if (func_num_args() == 1) { +// if (is_array($keys[0])) { +// // Allow an array to be passed as the first argument instead +// $keys = array_values($keys[0]); +// } else { +// // Optimize a single String to not need special fallback handling +// $keys = $keys[0]; +// } +// } +// return new self($keys); +// } +// +// /** +// * Get a title Object for a mediawiki message, where it can be found in the mediawiki namespace. +// * The title will be for the current language, if the message key is in +// * $wgForceUIMsgAsContentMsg it will be append with the language code (except content +// * language), because Message::inContentLanguage will also return in user language. +// * +// * @see $wgForceUIMsgAsContentMsg +// * @return Title +// * @since 1.26 +// */ +// public function getTitle() { +// global $wgContLang, $wgForceUIMsgAsContentMsg; +// +// title = this.key; +// if ( +// !this.language->equals($wgContLang) +// && in_array(this.key, (array)$wgForceUIMsgAsContentMsg) +// ) { +// $code = this.language->getCode(); +// title .= '/' . $code; +// } +// +// return Title::makeTitle(NS_MEDIAWIKI, $wgContLang->ucfirst(strtr(title, ' ', '_'))); +// } +// +// /** +// * Adds parameters to the parameter list of this message. +// * +// * @since 1.17 +// * +// * @param mixed ... Parameters as strings or arrays from +// * Message::numParam() and the like, or a single array of parameters. +// * +// * @return Message this +// */ +// public function prmsVar(/*...*/) { +// $args = func_get_args(); +// +// // If $args has only one entry and it's an array, then it's either a +// // non-varargs call or it happens to be a call with just a single +// // "special" parameter. Since the "special" parameters don't have any +// // numeric keys, we'll test that to differentiate the cases. +// if (count($args) === 1 && isset($args[0]) && is_array($args[0])) { +// if ($args[0] === []) { +// $args = []; +// } else { +// foreach ($args[0] as key => $value) { +// if (is_int(key)) { +// $args = $args[0]; +// break; +// } +// } +// } +// } +// +// this.parameters = array_merge(this.parameters, array_values($args)); +// return this; +// } + + /** + * Add parameters that are substituted after parsing or escaping. + * In other words the parsing process cannot access the contents + * of this type of parameter, and you need to make sure it is + * sanitized beforehand. The parser will see "$n", instead. + * + * @since 1.17 + * + * @param mixed $prmsVar,... Raw parameters as strings, or a single argument that is + * an array of raw parameters. + * + * @return Message this + */ + public XomwMessageOld rawParams(byte[]... prmsAry) { + for (byte[] prmsVar : prmsAry) { + this.parameters.Add(XomwMessageOld.rawParam(prmsVar)); + } + return this; + } + + /** + * Add parameters that are numeric and will be passed through + * Language::formatNum before substitution + * + * @since 1.18 + * + * @param mixed $param,... Numeric parameters, or a single argument that is + * an array of numeric parameters. + * + * @return Message this + */ + public XomwMessageOld numParams(int... prmsAry) { + for (int prmsVar : prmsAry) { + this.parameters.Add(XomwMessageOld.numParam(prmsVar)); + } + return this; + } + + /** + * Add parameters that are durations of time and will be passed through + * Language::formatDuration before substitution + * + * @since 1.22 + * + * @param int|int[] $param,... Duration parameters, or a single argument that is + * an array of duration parameters. + * + * @return Message this + */ + public XomwMessageOld durationParams(int... prmsAry) { + for (int prmsVar : prmsAry) { + this.parameters.Add(XomwMessageOld.durationParam(prmsVar)); + } + return this; + } + + /** + * Add parameters that are expiration times and will be passed through + * Language::formatExpiry before substitution + * + * @since 1.22 + * + * @param String|String[] $param,... Expiry parameters, or a single argument that is + * an array of expiry parameters. + * + * @return Message this + */ + public XomwMessageOld expiryParams(byte[]... prmsAry) { + for (byte[] prmsVar : prmsAry) { + this.parameters.Add(XomwMessageOld.expiryParam(prmsVar)); + } + return this; + } + + /** + * Add parameters that are time periods and will be passed through + * Language::formatTimePeriod before substitution + * + * @since 1.22 + * + * @param int|int[] $param,... Time period parameters, or a single argument that is + * an array of time period parameters. + * + * @return Message this + */ + public XomwMessageOld timeperiodParams(int... prmsAry) { + for (int prmsVar : prmsAry) { + this.parameters.Add(XomwMessageOld.timeperiodParam(prmsVar)); + } + return this; + } + + /** + * Add parameters that are file sizes and will be passed through + * Language::formatSize before substitution + * + * @since 1.22 + * + * @param int|int[] $param,... Size parameters, or a single argument that is + * an array of size parameters. + * + * @return Message this + */ + public XomwMessageOld sizeParams(int... prmsAry) { + for (int prmsVar : prmsAry) { + this.parameters.Add(XomwMessageOld.sizeParam(prmsVar)); + } + return this; + } + + /** + * Add parameters that are bitrates and will be passed through + * Language::formatBitrate before substitution + * + * @since 1.22 + * + * @param int|int[] $param,... Bit rate parameters, or a single argument that is + * an array of bit rate parameters. + * + * @return Message this + */ + public XomwMessageOld bitrateParams(int... prmsAry) { + for (int prmsVar : prmsAry) { + this.parameters.Add(XomwMessageOld.bitrateParam(prmsVar)); + } + return this; + } + + /** + * Add parameters that are plaintext and will be passed through without + * the content being evaluated. Plaintext parameters are not valid as + * arguments to parser functions. This differs from XomwMessage.rawParams in + * that the Message cls handles escaping to match the output format. + * + * @since 1.25 + * + * @param String|String[] $param,... plaintext parameters, or a single argument that is + * an array of plaintext parameters. + * + * @return Message this + */ + public XomwMessageOld plaintextParams(byte[]... prmsAry) { + for (byte[] prmsVar : prmsAry) { + this.parameters.Add(XomwMessageOld.plaintextParam(prmsVar)); + } + return this; + } + +// /** +// * Set the language and the title from a context Object +// * +// * @since 1.19 +// * +// * @param IContextSource $context +// * +// * @return Message this +// */ +// public function setContext(IContextSource $context) { +// this.inLanguage($context->getLanguage()); +// this.title($context->getTitle()); +// this.interfaceIsUserLang = true; +// +// return this; +// } +// +// /** +// * Request the message in any language that is supported. +// * +// * As a side effect interfaceIsUserLang message status is unconditionally +// * turned off. +// * +// * @since 1.17 +// * @param Language|String $lang Language code or Language Object. +// * @return Message this +// * @throws MWException +// */ +// public function inLanguage($lang) { +// if ($lang instanceof Language) { +// this.language = $lang; +// } elseif (is_string($lang)) { +// if (!this.language instanceof Language || this.language->getCode() != $lang) { +// this.language = Language::factory($lang); +// } +// } elseif ($lang instanceof StubUserLang) { +// this.language = false; +// } else { +// $type = gettype($lang); +// throw new MWException(__METHOD__ . " must be " +// . "passed a String or Language Object; $type given" +// ); +// } +// this.message = null; +// this.interfaceIsUserLang = false; +// return this; +// } + + /** + * Request the message in the wiki's content language, + * unless it is disabled for this message. + * + * @since 1.17 + * @see $wgForceUIMsgAsContentMsg + * + * @return Message this + */ + public XomwMessageOld inContentLanguage() { +// global $wgForceUIMsgAsContentMsg; +// if (in_array(this.key, (array)$wgForceUIMsgAsContentMsg)) { +// return this; +// } +// +// global $wgContLang; +// this.inLanguage($wgContLang); +// return this; + return this; + } + +// /** +// * Allows manipulating the interfaceIsUserLang message flag directly. +// * Can be used to restore the flag after setting a language. +// * +// * @since 1.20 +// * +// * @param boolean $interfaceIsUserLang +// * +// * @return Message this +// */ +// public function setInterfaceMessageFlag($interfaceIsUserLang) { +// this.interfaceIsUserLang = (boolean)$interfaceIsUserLang; +// return this; +// } +// +// /** +// * Enable or disable database use. +// * +// * @since 1.17 +// * +// * @param boolean $useDatabase +// * +// * @return Message this +// */ +// public function useDatabase($useDatabase) { +// this.useDatabase = (boolean)$useDatabase; +// this.message = null; +// return this; +// } +// +// /** +// * Set the Title Object to use as context when transforming the message +// * +// * @since 1.18 +// * +// * @param Title title +// * +// * @return Message this +// */ +// public function title(title) { +// this.title = title; +// return this; +// } + + /** + * Returns the message as a Content Object. + * + * @return Content + */ + public XomwContent contentFunc() { +// if (this.content == null) { +// this.content = new XomwMessageContent(this); +// } +// +// return this.content; + throw Err_.new_unimplemented(); + } + + /** + * Returns the message parsed from wikitext to HTML. + * + * @since 1.17 + * + * @param String|null $format One of the FORMAT_* constants. Null means use whatever was used + * the last time (this is for B/C and should be avoided). + * + * @return String HTML + */ + + // NOTE: causes issues in C# source; E2A7BC; http://www.fileformat.info/info/unicode/char/29fc/index.htm + private static final byte[] LeftPointingCurvedAngleBracket = gplx.core.encoders.Hex_utl_.Parse_hex_to_bry("E2A7BC"); + public byte[] toString(int format) { + if (format == FORMAT_NULL) { + Gfo_usr_dlg_.Instance.Warn_many("", "", "toString import implicit.*; format=~{0} key=~{1}", format, key); + format = this.format; + } +// byte[] s = this.fetchMessage(); + byte[] s = Bry_.Empty; + + if (s == null) { + // Err on the side of safety, ensure that the output + // is always html safe in the event the message key is + // missing, since in that case its highly likely the + // message key is user-controlled. + // LeftPointingCurvedAngleBracket is used instead of '<' to side-step any + // double-escaping issues. + // (Keep synchronised with mw.Message#toString in JS.) + return Bry_.Escape_html(Bry_.Add(LeftPointingCurvedAngleBracket, key, LeftPointingCurvedAngleBracket)); + } + + // Replace $* with a list of parameters for &uselang=qqx. +// if (XophpString_.strpos(s, "$*") != false) { +// String paramlist = ""; +// if (this.parameters != []) { +// paramlist = ": $" . implode(", $", range(1, count(this.parameters))); +// } +// s = str_replace("$*", paramlist, s); +// } + +// // Replace parameters before text parsing +// s = this.replaceParameters(s, "before", format); +// +// // Maybe transform using the full parser +// if (format == XomwMessage.FORMAT_PARSE) { +// s = this.parseText(s); +// s = Parser::stripOuterParagraph(s); +// } elseif (format == XomwMessage.FORMAT_BLOCK_PARSE) { +// s = this.parseText(s); +// } elseif (format == XomwMessage.FORMAT_TEXT) { +// s = this.transformText(s); +// } elseif (format == XomwMessage.FORMAT_ESCAPED) { +// s = this.transformText(s); +// s = htmlspecialchars(s, ENT_QUOTES, "UTF-8", false); +// } +// +// // Raw parameter replacement +// s = this.replaceParameters(s, "after", format); + + return s; + } + +// /** +// * Magic method implementation of the above (for PHP >= 5.2.0), so we can do, eg: +// * $foo = new Message(key); +// * $String = "$foo"; +// * +// * @since 1.18 +// * +// * @return String +// */ +// public function __toString() { +// // PHP doesn't allow __toString to throw exceptions and will +// // trigger a fatal error if it does. So, catch any exceptions. +// +// try { +// return this.toString(XomwMessage.FORMAT_PARSE); +// } catch (Exception $ex) { +// try { +// trigger_error("Exception caught in " . __METHOD__ . " (message " . this.key . "): " +// . $ex, E_USER_WARNING); +// } catch (Exception $ex) { +// // Doh! Cause a fatal error after all? +// } +// +// return '⧼' . htmlspecialchars(this.key) . '⧽'; +// } +// } +// +// /** +// * Fully parse the text from wikitext to HTML. +// * +// * @since 1.17 +// * +// * @return String Parsed HTML. +// */ +// public function parse() { +// this.format = XomwMessage.FORMAT_PARSE; +// return this.toString(XomwMessage.FORMAT_PARSE); +// } + + /** + * Returns the message text. {{-transformation is done. + * + * @since 1.17 + * + * @return String Unescaped message text. + */ + public byte[] textMw() { + this.format = XomwMessageOld.FORMAT_TEXT; + return this.toString(XomwMessageOld.FORMAT_TEXT); + } + +// /** +// * Returns the message text as-is, only parameters are substituted. +// * +// * @since 1.17 +// * +// * @return String Unescaped untransformed message text. +// */ +// public function plain() { +// this.format = XomwMessage.FORMAT_PLAIN; +// return this.toString(XomwMessage.FORMAT_PLAIN); +// } +// +// /** +// * Returns the parsed message text which is always surrounded by a block element. +// * +// * @since 1.17 +// * +// * @return String HTML +// */ +// public function parseAsBlock() { +// this.format = XomwMessage.FORMAT_BLOCK_PARSE; +// return this.toString(XomwMessage.FORMAT_BLOCK_PARSE); +// } +// +// /** +// * Returns the message text. {{-transformation is done and the result +// * is escaped excluding any raw parameters. +// * +// * @since 1.17 +// * +// * @return String Escaped message text. +// */ +// public function escaped() { +// this.format = XomwMessage.FORMAT_ESCAPED; +// return this.toString(XomwMessage.FORMAT_ESCAPED); +// } +// +// /** +// * Check whether a message key has been defined currently. +// * +// * @since 1.17 +// * +// * @return boolean +// */ +// public function exists() { +// return this.fetchMessage() !== false; +// } +// +// /** +// * Check whether a message does not exist, or is an empty String +// * +// * @since 1.18 +// * @todo FIXME: Merge with isDisabled()? +// * +// * @return boolean +// */ +// public function isBlank() { +// $message = this.fetchMessage(); +// return $message === false || $message === ''; +// } +// +// /** +// * Check whether a message does not exist, is an empty String, or is "-". +// * +// * @since 1.18 +// * +// * @return boolean +// */ +// public function isDisabled() { +// $message = this.fetchMessage(); +// return $message === false || $message === '' || $message === '-'; +// } + + /** + * @since 1.17 + * + * @param mixed $raw + * + * @return array Array with a single "raw" key. + */ + private static XomwMessagePrm_raw rawParam(byte[] raw) { + return new XomwMessagePrm_raw(raw); + } + + /** + * @since 1.18 + * + * @param mixed $num + * + * @return array Array with a single "num" key. + */ + private static XomwMessagePrm_num numParam(int num) { + return new XomwMessagePrm_num(num); + } + + /** + * @since 1.22 + * + * @param int $duration + * + * @return int[] Array with a single "duration" key. + */ + private static XomwMessagePrm_duration durationParam(int duration) { + return new XomwMessagePrm_duration(duration); + } + + /** + * @since 1.22 + * + * @param String $expiry + * + * @return String[] Array with a single "expiry" key. + */ + private static XomwMessagePrm_expiry expiryParam(byte[] expiry) { + return new XomwMessagePrm_expiry(expiry); + } + + /** + * @since 1.22 + * + * @param int $period + * + * @return int[] Array with a single "period" key. + */ + private static XomwMessagePrm_period timeperiodParam(int period) { + return new XomwMessagePrm_period(period); + } + + /** + * @since 1.22 + * + * @param int $size + * + * @return int[] Array with a single "size" key. + */ + private static XomwMessagePrm_size sizeParam(int size) { + return new XomwMessagePrm_size(size); + } + + /** + * @since 1.22 + * + * @param int $bitrate + * + * @return int[] Array with a single "bitrate" key. + */ + private static XomwMessagePrm_bitrate bitrateParam(int bitrate) { + return new XomwMessagePrm_bitrate(bitrate); + } + + /** + * @since 1.25 + * + * @param String $plaintext + * + * @return String[] Array with a single "plaintext" key. + */ + private static XomwMessagePrm_plaintext plaintextParam(byte[] plaintext) { + return new XomwMessagePrm_plaintext(plaintext); + } + + /** + * @since 1.29 + * + * @param array $list + * @param String $type 'comma', 'semicolon', 'pipe', 'text' + * @return array Array with "list" and "type" keys. + */ +// private static XomwMessagePrm_list listParam(byte[][] list, String type) { // array $list, $type = 'text' +// if (!listTypeMap.Has(type)) { +// throw new XophpInvalidArgumentException( +// "Invalid type " + type +// ); +// } +// return new XomwMessagePrm_list(list, type); +// } + + /** + * Substitutes any parameters into the message text. + * + * @since 1.17 + * + * @param String $message The message text. + * @param String $type Either "before" or "after". + * @param String $format One of the FORMAT_* constants. + * + * @return String + */ + // DFLT:type="before" +// private byte[] replaceParameters(byte[] message, int type, int format) { +// $replacementKeys = []; +// foreach (this.parameters as $n => $param) { +// list($paramType, $value) = this.extractParam($param, $format); +// if ($type === $paramType) { +// $replacementKeys['$' . ($n + 1)] = $value; +// } +// } +// $message = strtr($message, $replacementKeys); +// return $message; +// return null; +// } + + /** + * Extracts the parameter type and preprocessed the value if needed. + * + * @since 1.18 + * + * @param mixed $param Parameter as defined in this cls. + * @param String $format One of the FORMAT_* constants. + * + * @return array Array with the parameter type (either "before" or "after") and the value. + */ + private void extractParam(XomwMessageVal rv, Object param, int format) { + if (Type_.Is_assignable_from_by_obj(param, XomwMessagePrm.class)) { + XomwMessagePrm prm = (XomwMessagePrm)param; + switch (prm.Tid()) { + case XomwMessagePrm.Tid__raw: + rv.Set(PRM_TID_AFTER, ((XomwMessagePrm_raw)prm).raw); + break; + case XomwMessagePrm.Tid__num: + // Replace number prmsVar always in before step for now. + // No support for combined raw and num prmsVar + rv.Set(PRM_TID_BEFORE, this.getLanguage().formatNum(((XomwMessagePrm_num)prm).numAsBry())); + break; +// case XomwMessagePrm.Tid__duration: +// return [PRM_TID_BEFORE, this.getLanguage()->formatDuration($param['duration']) ]; +// break; +// case XomwMessagePrm.Tid__expiry: +// return [PRM_TID_BEFORE, this.getLanguage()->formatExpiry($param['expiry']) ]; +// break; +// case XomwMessagePrm.Tid__period: +// return [PRM_TID_BEFORE, this.getLanguage()->formatTimePeriod($param['period']) ]; +// break; +// case XomwMessagePrm.Tid__size: +// return [PRM_TID_BEFORE, this.getLanguage()->formatSize($param['size']) ]; +// break; +// case XomwMessagePrm.Tid__bitrate: +// return [PRM_TID_BEFORE, this.getLanguage()->formatBitrate($param['bitrate']) ]; +// break; +// case XomwMessagePrm.Tid__plaintext: +// return [PRM_TID_AFTER, this.formatPlaintext($param['plaintext'], $format) ]; +// break; +// case XomwMessagePrm.Tid__list: +// return this.formatListParam($param['list'], $param['type'], $format); +// break; + default: + String warning = "Invalid parameter for message '" + this.getKey() + "': " + + prm.toString(); + Gfo_usr_dlg_.Instance.Warn_many("", "", warning); + + rv.Set(PRM_TID_BEFORE, Bry_.new_a7("[INVALID]")); + break; + } +// } +// else if ($param instanceof Message) { +// // Match language, flags, etc. to the current message. +// $msg = clone $param; +// if ($msg->language !== this.language || $msg->useDatabase !== this.useDatabase) { +// // Cache depends on these parameters +// $msg->message = null; +// } +// $msg->interfaceIsUserLang = this.interfaceIsUserLang; +// $msg->language = this.language; +// $msg->useDatabase = this.useDatabase; +// $msg->title = this.title; +// +// // DWIM +// if ($format === 'block-parse') { +// $format = 'parse'; +// } +// $msg->format = $format; +// +// // Message objects should not be before parameters because +// // then they'll get double escaped. If the message needs to be +// // escaped, it'll happen right here when we call toString(). +// return [PRM_TID_AFTER, $msg->toString($format) ]; +// } +// else { +// rv.Set(PRM_TID_BEFORE, param); + } + } + +// /** +// * Wrapper for what ever method we use to parse wikitext. +// * +// * @since 1.17 +// * +// * @param String $String Wikitext message contents. +// * +// * @return String Wikitext parsed into HTML. +// */ +// protected function parseText($String) { +// $out = MessageCache::singleton()->parse( +// $String, +// this.title, +// /*linestart*/true, +// this.interfaceIsUserLang, +// this.getLanguage() +// ); +// +// return $out instanceof ParserOutput ? $out->getText() : $out; +// } +// +// /** +// * Wrapper for what ever method we use to {{-transform wikitext. +// * +// * @since 1.17 +// * +// * @param String $String Wikitext message contents. +// * +// * @return String Wikitext with {{-constructs replaced with their values. +// */ +// protected function transformText($String) { +// return MessageCache::singleton()->transform( +// $String, +// this.interfaceIsUserLang, +// this.getLanguage(), +// this.title +// ); +// } +// +// /** +// * Wrapper for what ever method we use to get message contents. +// * +// * @since 1.17 +// * +// * @return String +// * @throws MWException If message key array is empty. +// */ +// protected function fetchMessage() { +// if (this.message === null) { +// $cache = MessageCache::singleton(); +// +// 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; +// this.message = $message; +// } +// return this.message; +// } +// +// /** +// * Formats a message parameter wrapped with 'plaintext'. Ensures that +// * the entire String is displayed unchanged when displayed in the output +// * format. +// * +// * @since 1.25 +// * +// * @param String $plaintext String to ensure plaintext output of +// * @param String $format One of the FORMAT_* constants. +// * +// * @return String Input plaintext encoded for output to $format +// */ +// protected function formatPlaintext($plaintext, $format) { +// switch ($format) { +// case XomwMessage.FORMAT_TEXT: +// case XomwMessage.FORMAT_PLAIN: +// return $plaintext; +// +// case XomwMessage.FORMAT_PARSE: +// case XomwMessage.FORMAT_BLOCK_PARSE: +// case XomwMessage.FORMAT_ESCAPED: +// default: +// return htmlspecialchars($plaintext, ENT_QUOTES); +// +// } +// } +// +// /** +// * Formats a list of parameters as a concatenated String. +// * @since 1.29 +// * @param array $prmsVar +// * @param String $listType +// * @param String $format One of the FORMAT_* constants. +// * @return array Array with the parameter type (either "before" or "after") and the value. +// */ +// protected function formatListParam(array $prmsVar, $listType, $format) { +// if (!isset(XomwMessage.$listTypeMap[$listType])) { +// $warning = 'Invalid list type for message "' . this.getKey() . '": ' +// . htmlspecialchars($listType) +// . ' (prmsVar are ' . htmlspecialchars(serialize($prmsVar)) . ')'; +// trigger_error($warning, E_USER_WARNING); +// $e = new Exception; +// wfDebugLog('Bug58676', $warning . "\n" . $e->getTraceAsString()); +// return [PRM_TID_BEFORE, '[INVALID]' ]; +// } +// $func = XomwMessage.$listTypeMap[$listType]; +// +// // Handle an empty list sensibly +// if (!$prmsVar) { +// return [PRM_TID_BEFORE, this.getLanguage()->$func([]) ]; +// } +// +// // First, determine what kinds of list items we have +// $types = []; +// $vars = []; +// $list = []; +// foreach ($prmsVar as $n => $p) { +// list($type, $value) = this.extractParam($p, $format); +// $types[$type] = true; +// $list[] = $value; +// $vars[] = '$' . ($n + 1); +// } +// +// // Easy case: all are 'before' or 'after', so just join the +// // values and use the same type. +// if (count($types) === 1) { +// return [ key($types), this.getLanguage()->$func($list) ]; +// } +// +// // Hard case: We need to process each value per its type, then +// // return the concatenated values as 'after'. We handle this by turning +// // the list into a RawMessage and processing that as a parameter. +// $vars = this.getLanguage()->$func($vars); +// return this.extractParam(new RawMessage($vars, $prmsVar), $format); +// } +} +class XomwMessageVal { + public int tid; + public byte[] val; + public void Set(int tid, byte[] val) { + this.tid = tid; + this.val = val; + } +} +abstract class XomwMessagePrm { + public XomwMessagePrm(int tid) {this.tid = tid;} + public int Tid() {return tid;} private int tid; + public static final int + Tid__raw = 0 + , Tid__num = 1 + , Tid__duration = 2 + , Tid__expiry = 3 + , Tid__period = 4 + , Tid__size = 5 + , Tid__bitrate = 6 + , Tid__plaintext = 7 + , Tid__list = 8 + ; +} +class XomwMessagePrm_raw extends XomwMessagePrm { public byte[] raw; + public XomwMessagePrm_raw(byte[] raw) {super(Tid__raw); + this.raw = raw; + } +} +class XomwMessagePrm_num extends XomwMessagePrm { public int num; + public byte[] numAsBry() {return Int_.To_bry(num);} + public XomwMessagePrm_num(int num) {super(Tid__num); + this.num = num; + } +} +class XomwMessagePrm_duration extends XomwMessagePrm { public int duration; + public XomwMessagePrm_duration(int duration) {super(Tid__duration); + this.duration = duration; + } +} +class XomwMessagePrm_expiry extends XomwMessagePrm { public byte[] expiry; + public XomwMessagePrm_expiry(byte[] expiry) {super(Tid__expiry); + this.expiry = expiry; + } +} +class XomwMessagePrm_period extends XomwMessagePrm { public int period; + public XomwMessagePrm_period(int period) {super(Tid__period); + this.period = period; + } +} +class XomwMessagePrm_size extends XomwMessagePrm { public int size; + public XomwMessagePrm_size(int size) {super(Tid__size); + this.size = size; + } +} +class XomwMessagePrm_bitrate extends XomwMessagePrm { public int bitrate; + public XomwMessagePrm_bitrate(int bitrate) {super(Tid__bitrate); + this.bitrate = bitrate; + } +} +class XomwMessagePrm_plaintext extends XomwMessagePrm { public byte[] plaintext; + public XomwMessagePrm_plaintext(byte[] plaintext) {super(Tid__plaintext); + this.plaintext = plaintext; + } +} +class XomwMessagePrm_list extends XomwMessagePrm { public byte[][] list; + public String type; + public XomwMessagePrm_list(byte[][] list, String type) {super(Tid__list); + this.list = list; + this.type = type; + } +} diff --git a/400_xowa/src/gplx/xowa/mediawiki/includes/XomwTitleOld.java b/400_xowa/src/gplx/xowa/mediawiki/includes/XomwTitleOld.java index b4abab604..f3e350ae6 100644 --- a/400_xowa/src/gplx/xowa/mediawiki/includes/XomwTitleOld.java +++ b/400_xowa/src/gplx/xowa/mediawiki/includes/XomwTitleOld.java @@ -15,7 +15,6 @@ Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt */ package gplx.xowa.mediawiki.includes; import gplx.*; import gplx.xowa.mediawiki.*; -import gplx.xowa.mediawiki.includes.linkers.XomwLinkTarget; import gplx.xowa.mediawiki.includes.title.*; /** * Represents a title within MediaWiki. @@ -558,7 +557,7 @@ public class XomwTitleOld { // implements XomwLinkTarget * @return Title The new Object */ public static XomwTitleOld newMainPage(XomwEnv env) { - XomwTitleOld title = XomwTitleOld.newFromText(env, XomwGlobalFunctions.wfMessage(env, "mainpage").inContentLanguage().text()); + XomwTitleOld title = XomwTitleOld.newFromText(env, XomwGlobalFunctions.wfMessageOld(env, "mainpage").inContentLanguage().text()); // Don't give fatal errors if the message is broken if (title == null) { title = XomwTitleOld.newFromText(env, Bry_.new_a7("Main Page")); diff --git a/400_xowa/src/gplx/xowa/mediawiki/includes/parsers/XomwParser.java b/400_xowa/src/gplx/xowa/mediawiki/includes/parsers/XomwParser.java index 6aaf26484..ec213cd81 100644 --- a/400_xowa/src/gplx/xowa/mediawiki/includes/parsers/XomwParser.java +++ b/400_xowa/src/gplx/xowa/mediawiki/includes/parsers/XomwParser.java @@ -13,7 +13,11 @@ 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.parsers; import gplx.*; import gplx.xowa.*; import gplx.xowa.mediawiki.*; import gplx.xowa.mediawiki.includes.*; +package gplx.xowa.mediawiki.includes.parsers; import gplx.*; +import gplx.core.primitives.Bool_obj_ref; +import gplx.core.primitives.Int_obj_ref; +import gplx.core.primitives.String_obj_ref; +import gplx.xowa.*; import gplx.xowa.mediawiki.*; import gplx.xowa.mediawiki.includes.*; import gplx.core.btries.*; import gplx.core.net.*; import gplx.xowa.mediawiki.includes.content.XomwContent; @@ -4507,20 +4511,20 @@ Tfds.Write(nowiki, isHTML, forceRawInterwiki, isChildObj, isLocalObj, titleText, * @return array */ public static XophpArray statelessFetchTemplate(XomwTitle title, XomwParser parser) { // $parser = false - String text = XophpString_.False; - boolean skip = false; + String_obj_ref text = String_obj_ref.new_(XophpString_.False); + Bool_obj_ref skip = Bool_obj_ref.n_(); XomwTitle finalTitle = title; XophpArray deps = XophpArray.New(); // Loop to fetch the article, with up to 1 redirect for (int i = 0; i < 2 && XophpObject_.is_object(title); i++) { // Give extensions a chance to select the revision instead - int id = XophpInt_.False; // Assume current -// Hooks::run('BeforeParserFetchTemplateAndtitle', -// [ parser, title, &skip, &id ]); + Int_obj_ref id = Int_obj_ref.New(XophpInt_.False); // Assume current + XomwHooks.run("BeforeParserFetchTemplateAndtitle", + XophpArray.New(parser, title, skip, id)); - if (skip) { - text = XophpString_.False; + if (skip.Val()) { + text = text.Val_(XophpString_.False); deps.Add(XophpArray.New() .Add("title", title) .Add("page_id", title.getArticleID()) @@ -4530,8 +4534,8 @@ Tfds.Write(nowiki, isHTML, forceRawInterwiki, isChildObj, isLocalObj, titleText, } // Get the revision XomwRevision rev = null; - if (XophpInt_.is_true(id)) { - rev = XomwRevision.newFromId(id); + if (XophpInt_.is_true(id.Val())) { + rev = XomwRevision.newFromId(id.Val()); } else if (XophpObject_.is_true(parser)) { // rev = parser.fetchCurrentRevisionOfTitle(title); } else { @@ -4539,7 +4543,7 @@ Tfds.Write(nowiki, isHTML, forceRawInterwiki, isChildObj, isLocalObj, titleText, } int rev_id = XophpObject_.is_true(rev) ? rev.getId() : 0; // If there is no current revision, there is no page - if (XophpInt_.is_false(id) && !XophpObject_.is_true(rev)) { + if (XophpInt_.is_false(id.Val()) && !XophpObject_.is_true(rev)) { // linkCache = MediaWikiServices::getInstance().getLinkCache(); // linkCache.addBadLinkObj(title); } @@ -4559,12 +4563,12 @@ Tfds.Write(nowiki, isHTML, forceRawInterwiki, isChildObj, isLocalObj, titleText, XomwContent content; if (XophpObject_.is_true(rev)) { content = rev.getContent(); - text = XophpObject_.is_true(content) ? content.getWikitextForTransclusion() : null; + text = text.Val_(XophpObject_.is_true(content) ? content.getWikitextForTransclusion() : null); -// Hooks::run('ParserFetchTemplate', -// [ parser, title, rev, &text, &deps ]); - if (XophpString_.is_false(text) || XophpString_.is_null(text)) { - text = XophpString_.False; + XomwHooks.run("ParserFetchTemplate", + XophpArray.New(parser, title, rev, text, deps)); // NOTE: was &deps + if (XophpString_.is_false(text.Val()) || XophpString_.is_null(text.Val())) { + text = text.Val_(XophpString_.False); break; } } else if (title.getNamespace() == XomwDefines.NS_MEDIAWIKI) { diff --git a/400_xowa/src/gplx/xowa/mediawiki/includes/parsers/lnkis/Xomw_lnki_wkr__file__tst.java b/400_xowa/src/gplx/xowa/mediawiki/includes/parsers/lnkis/Xomw_lnki_wkr__file__tst.java index b289da4c0..26a84a2e1 100644 --- a/400_xowa/src/gplx/xowa/mediawiki/includes/parsers/lnkis/Xomw_lnki_wkr__file__tst.java +++ b/400_xowa/src/gplx/xowa/mediawiki/includes/parsers/lnkis/Xomw_lnki_wkr__file__tst.java @@ -91,7 +91,7 @@ class Xomw_lnki_wkr__fxt { env.File_finder_(file_finder); env.Magic_word_mgr().Add(Bry_.new_u8("img_thumbnail"), Bool_.Y, Bry_.Ary("thumb")); env.Magic_word_mgr().Add(Bry_.new_u8("img_width"), Bool_.Y, Bry_.Ary("$1px")); - env.Message_mgr().Add("thumbnail-more", "enlarge", env.Language()); + env.Message_mgr().Old_add("thumbnail-more", "enlarge", env.Language()); parser.Init_by_wiki(wiki); // ctx diff --git a/400_xowa/src/gplx/xowa/mediawiki/includes/site/XomwSiteList.java b/400_xowa/src/gplx/xowa/mediawiki/includes/site/XomwSiteList.java index 71627c98d..0582f0504 100644 --- a/400_xowa/src/gplx/xowa/mediawiki/includes/site/XomwSiteList.java +++ b/400_xowa/src/gplx/xowa/mediawiki/includes/site/XomwSiteList.java @@ -1,19 +1,20 @@ -/* -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.site; import gplx.*; import gplx.xowa.*; import gplx.xowa.mediawiki.*; import gplx.xowa.mediawiki.includes.*; +/* +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.site; import gplx.*; +import gplx.xowa.mediawiki.*; import gplx.xowa.mediawiki.includes.libs.*; /** * Collection of Site objects. @@ -174,7 +175,7 @@ public class XomwSiteList extends XomwGenericArrayObject { public int Len() {ret * @return boolean */ @Override public boolean isEmpty() { - return XophpArray_.array_is_empty(this.byGlobalId); + return XophpArray_.empty(this.byGlobalId); } /** diff --git a/400_xowa/tst/gplx/xowa/mediawiki/XophpArray__tst.java b/400_xowa/tst/gplx/xowa/mediawiki/XophpArray__tst.java index d45a4c3f6..13a2434a0 100644 --- a/400_xowa/tst/gplx/xowa/mediawiki/XophpArray__tst.java +++ b/400_xowa/tst/gplx/xowa/mediawiki/XophpArray__tst.java @@ -371,6 +371,24 @@ public class XophpArray__tst { , XophpArray_.array_filter(array, callbackOwner.NewCallback("array_filter_both"), XophpArray_.ARRAY_FILTER_USE_BOTH) ); } + @Test public void reset() { + // PHP examples + // Example #1 reset() example + XophpArray array; + array = XophpArray.New("step one", "step two", "step three", "step four"); + + // by default, the pointer is on the first element + Gftest.Eq__str("step one", (String)XophpArray.current(array)); + + // skip two steps + XophpArray.next(array); + XophpArray.next(array); + Gftest.Eq__str("step three", (String)XophpArray.current(array)); + + // reset pointer, start again on step one + XophpArray.reset(array); + Gftest.Eq__str("step one", (String)XophpArray.current(array)); + } } class XophpArray__fxt { public XophpArray Make() {return new XophpArray();}