/**********************************************************************************
* nWordPress is an automated migration of WordPress 2.5.1 performed by Numiton.
*
* copyright : (C) 2008 Numiton - www.numiton.com
* email : numiton@users.sourceforge.net
*
* $Id: gettext_reader.java,v 1.2 2008/10/03 18:45:30 numiton Exp $
*
**********************************************************************************/
/**********************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
**********************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
package org.numiton.nwp.wp_includes;
import static com.numiton.VarHandling.*;
import java.io.IOException;
import java.io.Serializable;
import org.apache.log4j.Logger;
import org.numiton.nwp.GlobalConsts;
import org.numiton.nwp.GlobalVars;
import com.numiton.FunctionHandling;
import com.numiton.Math;
import com.numiton.RegExPosix;
import com.numiton.array.Array;
import com.numiton.generic.*;
import com.numiton.ntile.til.libraries.php.quercus.QMisc;
import com.numiton.ntile.til.libraries.php.quercus.QRegExPerl;
import com.numiton.ntile.til.libraries.php.quercus.QVarHandling;
import com.numiton.string.Strings;
/**
* PHP-Gettext External Library: gettext_reader class
*
* @package External
* @subpackage PHP-gettext
*
* @internal
Copyright (c) 2003 Danilo Segan <danilo@kvota.net>.
Copyright (c) 2005 Nico Kaiser <nico@siriux.net>
This file is part of PHP-gettext.
PHP-gettext is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
PHP-gettext is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with PHP-gettext; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/**
* Provides a simple gettext replacement that works independently from
* the system's gettext abilities.
* It can read MO files and use them for translating strings.
* The files are passed to gettext_reader as a Stream (see streams.php)
*
* This version has the ability to cache all strings and translations to
* speed up the string lookup.
* While the cache is enabled by default, it can be switched off with the
* second parameter in the constructor (e.g. whenusing very large MO files
* that you don't want to keep in memory)
*/
public class gettext_reader implements ContextCarrierInterface, Serializable, Cloneable {
protected static final Logger LOG = Logger.getLogger(gettext_reader.class.getName());
public GlobalConsts gConsts;
public GlobalVars gVars;
//public:
public int error = 0; // public variable that holds error code (0 if no error)
//private:
public int BYTEORDER = 0; // 0: low endian, 1: big endian
public StreamReader STREAM = null;
public boolean short_circuit = false;
public boolean enable_cache = false;
public int originals = 0; // offset of original table
public int translations = 0; // offset of translation table
public String pluralheader; // cache header field for plural forms
public String select_string_function = null; // cache function, which chooses plural forms
public int total = 0; // total string count
public Array<Integer> table_originals; /*Initialized in code*/ // table for original strings (offsets)
public Array<Integer> table_translations; /* Initialized in code */ // table for translated strings (offsets)
public Array<String> cache_translations; /* Initialized in code */ // original -> translation mapping
/* Methods */
/**
* Reads a 32bit Integer from the Stream
*
* @access private
* @return Integer from the Stream
*/
public int readint() {
Ref<Array<Integer>> low_end = new Ref<Array<Integer>>();
Ref<Array<Integer>> big_end = new Ref<Array<Integer>>();
if (equal(this.BYTEORDER, 0)) {
// low endian
low_end.value = QMisc.unpack("V", this.STREAM.read(4));
return Array.array_shift(low_end);
}
else {
// big endian
big_end.value = QMisc.unpack("N", this.STREAM.read(4));
return Array.array_shift(big_end);
}
}
/**
* Reads an array of Integers from the Stream
* @param int count How many elements should be read
* @return Array of Integers
*/
public Array<Integer> readintarray(int count) {
if (equal(this.BYTEORDER, 0)) {
// low endian
return QMisc.unpack("V" + strval(count), this.STREAM.read(4 * count));
}
else {
// big endian
return QMisc.unpack("N" + strval(count), this.STREAM.read(4 * count));
}
}
public gettext_reader(GlobalVars javaGlobalVariables, GlobalConsts javaGlobalConstants, StreamReader Reader) {
this(javaGlobalVariables, javaGlobalConstants, Reader, true);
}
/**
* Constructor
* @param object Reader the StreamReader object
* @param boolean enable_cache Enable or disable caching of strings (default
* on)
*/
public gettext_reader(GlobalVars javaGlobalVariables, GlobalConsts javaGlobalConstants, StreamReader Reader, boolean enable_cache) {
setContext(javaGlobalVariables, javaGlobalConstants);
int MAGIC1 = 0;
int MAGIC2 = 0;
int MAGIC3 = 0;
Object magic = null;
Object revision = null;
// If there isn't a StreamReader, turn on short circuit mode.
if (!booleanval(Reader))
/*Commented by Numiton || isset(Reader.error)*/
{
this.short_circuit = true;
return;
}
// Caching can be turned off
this.enable_cache = enable_cache;
// $MAGIC1 = (int)0x950412de; //bug in PHP 5.0.2, see https://savannah.nongnu.org/bugs/?func=detailitem&item_id=10565
MAGIC1 = -1794895138;
// $MAGIC2 = (int)0xde120495; //bug
MAGIC2 = -569244523;
// 64-bit fix
MAGIC3 = intval(2500072158L);
this.STREAM = Reader;
magic = this.readint();
if (equal(magic, MAGIC1) || equal(magic, MAGIC3)) { // to make sure it works for 64-bit platforms
this.BYTEORDER = 0;
}
else
if (equal(magic, MAGIC2 & 4294967295L)) {
this.BYTEORDER = 1;
}
else {
this.error = 1; // not MO file
return;
}
// FIXME: Do we care about revision? We should.
revision = this.readint();
this.total = this.readint();
this.originals = this.readint();
this.translations = this.readint();
}
/**
* Loads the translation tables from the MO file into the cache If caching
* is enabled, also loads all strings into a cache to speed up translation
* lookups
* @access private
*/
public void load_tables() {
int i = 0;
String original = null;
String translation = null;
if (is_array(this.cache_translations) && is_array(this.table_originals) && is_array(this.table_translations)) {
return;
}
/* get original and translations tables */
this.STREAM.seekto(this.originals);
this.table_originals = this.readintarray(this.total * 2);
this.STREAM.seekto(this.translations);
this.table_translations = this.readintarray(this.total * 2);
if (this.enable_cache) {
this.cache_translations = new Array<String>();
/* read all strings in the cache */
for (i = 0; i < this.total; i++) {
this.STREAM.seekto(this.table_originals.getValue(i * 2 + 2));
original = this.STREAM.read(this.table_originals.getValue(i * 2 + 1));
this.STREAM.seekto(this.table_translations.getValue(i * 2 + 2));
translation = this.STREAM.read(this.table_translations.getValue(i * 2 + 1));
this.cache_translations.putValue(original, translation);
}
}
}
/**
* Returns a string from the "originals" table
* @access private
* @param int num Offset number of original string
* @return string Requested string if found, otherwise ''
*/
public String get_original_string(int num) {
int length = 0;
int offset = 0;
String data = null;
length = this.table_originals.getValue(num * 2 + 1);
offset = this.table_originals.getValue(num * 2 + 2);
if (!booleanval(length)) {
return "";
}
this.STREAM.seekto(offset);
data = this.STREAM.read(length);
return data;
}
/**
* Returns a string from the "translations" table
* @access private
* @param int num Offset number of original string
* @return string Requested string if found, otherwise ''
*/
public String get_translation_string(int num) {
int length = 0;
int offset = 0;
String data = null;
length = this.table_translations.getValue(num * 2 + 1);
offset = this.table_translations.getValue(num * 2 + 2);
if (!booleanval(length)) {
return "";
}
this.STREAM.seekto(offset);
data = this.STREAM.read(length);
return data;
}
public int find_string(String string) {
return find_string(string, -1, -1);
}
/**
* Binary search for string
* @access private
* @param string string
* @param int start (internally used in recursive function)
* @param int end (internally used in recursive function)
* @return int string number (offset in originals table)
*/
public int find_string(String string, int start, int end) {
String txt;
int half;
int cmp = 0;
if (equal(start, -1) || equal(end, -1)) {
// find_string is called with only one parameter, set start end end
start = 0;
end = this.total;
}
if (Math.abs(start - end) <= 1) {
// We're done, now we either found the string, or it doesn't exist
txt = this.get_original_string(start);
if (equal(string, txt)) {
return start;
}
else
return -1;
}
else
if (start > end) {
// start > end -> turn around and start over
return this.find_string(string, end, start);
}
else {
// Divide table in two parts
half = intval(floatval(start + end) / floatval(2));
cmp = Strings.strcmp(string, this.get_original_string(half));
if (equal(cmp, 0)) {
// string is exactly in the middle => return it
return half;
}
else
if (cmp < 0) {
// The string is in the upper half
return this.find_string(string, start, half);
}
else {
// The string is in the lower half
return this.find_string(string, half, end);
}
}
}
/**
* Translates a string
* @access public
* @param string string to be translated
* @return string translated string (or original, if not found)
*/
public String translate(String string) {
int num = 0;
if (this.short_circuit) {
return string;
}
this.load_tables();
if (this.enable_cache) {
// Caching enabled, get translated string from cache
if (Array.array_key_exists(string, this.cache_translations)) {
return this.cache_translations.getValue(string);
}
else
return string;
}
else {
// Caching not enabled, try to find string
num = this.find_string(string);
if (equal(num, -1)) {
return string;
}
else
return this.get_translation_string(num);
}
}
/**
* Get possible plural forms from MO header
* @access private
* @return string plural form header
*/
public String get_plural_forms() {
String header = null;
Array<Object> regs = new Array<Object>();
String expr = null;
String res = null;
int p = 0;
Object ch = null;
int i = 0;
// lets assume message number 0 is header
// this is true, right?
this.load_tables();
// cache header field for plural forms
if (!is_string(this.pluralheader)) {
if (this.enable_cache) {
header = this.cache_translations.getValue("");
}
else {
header = this.get_translation_string(0);
}
header = header + "\n"; //make sure our regex matches
if (booleanval(RegExPosix.eregi("plural-forms: ([^\n]*)\n", header, regs))) {
expr = strval(regs.getValue(1));
}
else
expr = "nplurals=2; plural=n == 1 ? 0 : 1;";
// add parentheses
// important since PHP's ternary evaluates from left to right
expr = expr + ";";
res = "";
p = 0;
for (i = 0; i < Strings.strlen(expr); i++) {
ch = Strings.getCharAt(expr, i);
{
int javaSwitchSelector69 = 0;
if (equal(ch, "?"))
javaSwitchSelector69 = 1;
if (equal(ch, ":"))
javaSwitchSelector69 = 2;
if (equal(ch, ";"))
javaSwitchSelector69 = 3;
switch (javaSwitchSelector69) {
case 1: {
res = res + " ? (";
p++;
break;
}
case 2: {
res = res + ") : (";
break;
}
case 3: {
res = res + Strings.str_repeat(")", p) + ";";
p = 0;
break;
}
default: {
res = res + strval(ch);
}
}
}
}
this.pluralheader = res;
}
return this.pluralheader;
}
/**
* Detects which plural form to take
* @access private
* @param n count
* @return int array index of the right plural form
*/
public int select_string(int n) {
String string = null;
Array<Object> matches = new Array<Object>();
if (is_null(this.select_string_function)) {
string = this.get_plural_forms();
if (QRegExPerl.preg_match("/nplurals\\s*=\\s*(\\d+)\\s*\\;\\s*plural\\s*=\\s*(.*?)\\;+/", string, matches)) {
nplurals = intval(matches.getValue(1));
expression = strval(matches.getValue(2));
expression = Strings.str_replace("n", "$n", expression);
}
else {
nplurals = 2;
expression = " $n == 1 ? 0 : 1 ";
}
this.select_string_function = "function created";
}
return intval(FunctionHandling.call_user_func(new Callback("createFunction_select_string_function", this), n));
}
protected int nplurals;
protected String expression;
// TODO Eliminate error-prone dynamic code
public Object createFunction_select_string_function(String n) {
String evalStr = "$expression=\"" + expression + "\";\n" + "$n = " + n + ";\n" + "$plural = ($expression);\n" + "echo(($plural <= $nplurals)? $plural : $plural - 1;)";
LOG.debug("Evaluated string: " + evalStr);
try {
return QVarHandling.eval(evalStr);
}
catch (IOException ex) {
LOG.warn(ex, ex);
return null;
}
}
/**
* Plural version of gettext
* @access public
* @param string single
* @param string plural
* @param string number
* @return translated plural form
*/
public Object ngettext(String single, String plural, int number) {
int select = 0;
String key = null;
String result = null;
Array<String> list = new Array<String>();
int num = 0;
if (this.short_circuit) {
if (!equal(number, 1)) {
return plural;
}
else
return single;
}
// find out the appropriate form
select = this.select_string(number);
// this should contains all strings separated by NULLs
key = single + Strings.chr(0) + plural;
if (this.enable_cache) {
if (!Array.array_key_exists(key, this.cache_translations)) {
return !equal(number, 1) ? plural : single;
}
else {
result = this.cache_translations.getValue(key);
list = Strings.explode(Strings.chr(0), result);
return list.getValue(select);
}
}
else {
num = this.find_string(key);
if (equal(num, -1)) {
return !equal(number, 1) ? plural : single;
}
else {
result = this.get_translation_string(num);
list = Strings.explode(Strings.chr(0), result);
return list.getValue(select);
}
}
}
public void setContext(GlobalVariablesContainer javaGlobalVariables, GlobalConstantsInterface javaGlobalConstants) {
gConsts = (GlobalConsts) javaGlobalConstants;
gVars = (GlobalVars) javaGlobalVariables;
gVars.gConsts = gConsts;
}
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
public GlobalVariablesContainer getGlobalVars() {
return gVars;
}
}