/bianca/src/main/java/com/clevercloud/bianca/lib/string/StringModule.java
Java | 6494 lines | 4926 code | 974 blank | 594 comment | 1090 complexity | f89a3a083427accd155fc2116ebd73b1 MD5 | raw file
Possible License(s): GPL-2.0, MPL-2.0-no-copyleft-exception
- /*
- * Copyright (c) 1998-2010 Caucho Technology -- all rights reserved
- * Copyright (c) 2011-2012 Clever Cloud SAS -- all rights reserved
- *
- * This file is part of Bianca(R) Open Source
- *
- * Each copy or derived work must preserve the copyright notice and this
- * notice unmodified.
- *
- * Bianca Open Source 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.
- *
- * Bianca Open Source 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, or any warranty
- * of NON-INFRINGEMENT. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Bianca Open Source; if not, write to the
- *
- * Free Software Foundation, Inc.
- * 59 Temple Place, Suite 330
- * Boston, MA 02111-1307 USA
- *
- * @author Scott Ferguson
- * @author Marc-Antoine Perennou <Marc-Antoine@Perennou.com>
- */
- package com.clevercloud.bianca.lib.string;
- import com.clevercloud.bianca.BiancaException;
- import com.clevercloud.bianca.BiancaModuleException;
- import com.clevercloud.bianca.annotation.*;
- import com.clevercloud.bianca.env.*;
- import com.clevercloud.bianca.lib.file.BinaryOutput;
- import com.clevercloud.bianca.lib.file.FileModule;
- import com.clevercloud.bianca.module.AbstractBiancaModule;
- import com.clevercloud.util.*;
- import com.clevercloud.vfs.Path;
- import java.io.IOException;
- import java.io.InputStream;
- import java.math.BigInteger;
- import java.security.MessageDigest;
- import java.text.DecimalFormat;
- import java.text.DecimalFormatSymbols;
- import java.text.NumberFormat;
- import java.util.*;
- import java.util.logging.Level;
- import java.util.logging.Logger;
- /**
- * PHP functions implemented from the string module
- */
- public class StringModule extends AbstractBiancaModule {
- private static final Logger log =
- Logger.getLogger(StringModule.class.getName());
- private static final L10N L = new L10N(StringModule.class);
- public static final int CODESET = 14;
- public static final int CRYPT_SALT_LENGTH = 2;
- public static final int CRYPT_STD_DES = 0;
- public static final int CRYPT_EXT_DES = 0;
- public static final int CRYPT_MD5 = 0;
- public static final int CRYPT_BLOWFISH = 0;
- public static final int CHAR_MAX = 1;
- public static final int LC_CTYPE = 1;
- public static final int LC_NUMERIC = 2;
- public static final int LC_TIME = 3;
- public static final int LC_COLLATE = 4;
- public static final int LC_MONETARY = 5;
- public static final int LC_ALL = 6;
- public static final int LC_MESSAGES = 7;
- public static final int NOEXPR = (10 << 15) + 1;
- public static final int STR_PAD_LEFT = 1;
- public static final int STR_PAD_RIGHT = 0;
- public static final int STR_PAD_BOTH = 2;
- public static final int YESEXPR = 10 << 15;
- public static final int RADIXCHAR = 0x10000;
- public static final int DECIMAL_POINT = RADIXCHAR; // Returns same value as RADIXCHAR
- public static final int THOUSEP = 0x10001;
- public static final int THOUSANDS_SEP = THOUSEP; // Returns same value as THOUSEP
- public static final int ABDAY_1 = 0x20000;
- public static final int ABDAY_2 = 0x20001;
- public static final int ABDAY_3 = 0x20002;
- public static final int ABDAY_4 = 0x20003;
- public static final int ABDAY_5 = 0x20004;
- public static final int ABDAY_6 = 0x20005;
- public static final int ABDAY_7 = 0x20006;
- public static final int DAY_1 = 0x20007;
- public static final int DAY_2 = 0x20008;
- public static final int DAY_3 = 0x20009;
- public static final int DAY_4 = 0x2000a;
- public static final int DAY_5 = 0x2000b;
- public static final int DAY_6 = 0x2000c;
- public static final int DAY_7 = 0x2000d;
- public static final int ABMON_1 = 0x2000e;
- public static final int ABMON_2 = 0x2000f;
- public static final int ABMON_3 = 0x20010;
- public static final int ABMON_4 = 0x20011;
- public static final int ABMON_5 = 0x20012;
- public static final int ABMON_6 = 0x20013;
- public static final int ABMON_7 = 0x20014;
- public static final int ABMON_8 = 0x20015;
- public static final int ABMON_9 = 0x20016;
- public static final int ABMON_10 = 0x20017;
- public static final int ABMON_11 = 0x20018;
- public static final int ABMON_12 = 0x20019;
- public static final int MON_1 = 0x2001a;
- public static final int MON_2 = 0x2001b;
- public static final int MON_3 = 0x2001c;
- public static final int MON_4 = 0x2001d;
- public static final int MON_5 = 0x2001e;
- public static final int MON_6 = 0x2001f;
- public static final int MON_7 = 0x20020;
- public static final int MON_8 = 0x20021;
- public static final int MON_9 = 0x20022;
- public static final int MON_10 = 0x20023;
- public static final int MON_11 = 0x20024;
- public static final int MON_12 = 0x20025;
- public static final int AM_STR = 0x20026;
- public static final int PM_STR = 0x20027;
- public static final int D_T_FMT = 0x20028;
- public static final int D_FMT = 0x20029;
- public static final int T_FMT = 0x2002a;
- public static final int T_FMT_AMPM = 0x2002b;
- public static final int ERA = 0x2002c;
- public static final int ERA_D_FMT = 0x2002e;
- public static final int ERA_D_T_FMT = 0x20030;
- public static final int ERA_T_FMT = 0x20031;
- public static final int CRNCYSTR = 0x4000f;
- private static final DecimalFormatSymbols DEFAULT_DECIMAL_FORMAT_SYMBOLS;
- private static final BigInteger BIG_TEN = new BigInteger("10");
- private static final BigInteger BIG_2_64 = BigInteger.ONE.shiftLeft(64);
- private static final FreeList<MessageDigest> _md5FreeList = new FreeList<MessageDigest>(16);
- private static final int LEVENSHTEIN_MAX_LENGTH = 255;
- /**
- * Escapes a string using C syntax.
- *
- * @param source the source string to convert
- * @param characters the set of characters to convert
- * @return the escaped string
- * @see #stripcslashes
- */
- public static StringValue addcslashes(
- Env env, StringValue source, String characters) {
- if (characters == null) {
- characters = "";
- }
- boolean[] bitmap = parseCharsetBitmap(env, characters);
- int length = source.length();
- StringValue sb = new StringValue();
- for (int i = 0; i < length; i++) {
- char ch = source.charAt(i);
- if (ch >= 256 || !bitmap[ch]) {
- sb.append(ch);
- continue;
- }
- switch (ch) {
- case 0x07:
- sb.append("\\a");
- break;
- case '\b':
- sb.append("\\b");
- break;
- case '\t':
- sb.append("\\t");
- break;
- case '\n':
- sb.append("\\n");
- break;
- case 0xb:
- sb.append("\\v");
- break;
- case '\f':
- sb.append("\\f");
- break;
- case '\r':
- sb.append("\\r");
- break;
- default:
- if (ch < 0x20 || ch >= 0x7f) {
- // save as octal
- sb.append("\\");
- sb.append((char) ('0' + ((ch >> 6) & 7)));
- sb.append((char) ('0' + ((ch >> 3) & 7)));
- sb.append((char) ('0' + ((ch) & 7)));
- break;
- } else {
- sb.append("\\");
- sb.append(ch);
- break;
- }
- }
- }
- return sb;
- }
- /**
- * Parses the cslashes bitmap returning an actual bitmap.
- *
- * @param charset the bitmap string
- * @return the actual bitmap
- */
- private static boolean[] parseCharsetBitmap(Env env, String charset) {
- boolean[] bitmap = new boolean[256];
- int length = charset.length();
- for (int i = 0; i < length; i++) {
- char ch = charset.charAt(i);
- // TODO: the bitmap eventual might need to deal with unicode
- if (ch >= 256) {
- continue;
- }
- bitmap[ch] = true;
- if (length <= i + 3) {
- continue;
- }
- if (charset.charAt(i + 1) != '.' || charset.charAt(i + 2) != '.') {
- continue;
- }
- char last = charset.charAt(i + 3);
- if (last < ch) {
- env.warning(L.l("character set range is invalid: {0}..{1}",
- ch, last));
- continue;
- }
- i += 3;
- for (; ch <= last; ch++) {
- bitmap[ch] = true;
- }
- // TODO: handling of '@'?
- }
- return bitmap;
- }
- /**
- * Escapes a string for db characters.
- *
- * @param source the source string to convert
- * @return the escaped string
- */
- public static StringValue addslashes(StringValue source) {
- StringValue sb = new StringValue();
- int length = source.length();
- for (int i = 0; i < length; i++) {
- char ch = source.charAt(i);
- switch (ch) {
- case 0x0:
- sb.append("\\0");
- break;
- case '\'':
- sb.append("\\'");
- break;
- case '\"':
- sb.append("\\\"");
- break;
- case '\\':
- sb.append("\\\\");
- break;
- default:
- sb.append(ch);
- break;
- }
- }
- return sb;
- }
- /**
- * Converts a binary value to a hex value.
- */
- public static StringValue bin2hex(Env env, InputStream is) {
- try {
- StringValue sb = new StringValue();
- int ch;
- while ((ch = is.read()) >= 0) {
- int d = (ch >> 4) & 0xf;
- if (d < 10) {
- sb.append((char) (d + '0'));
- } else {
- sb.append((char) (d + 'a' - 10));
- }
- d = (ch) & 0xf;
- if (d < 10) {
- sb.append((char) (d + '0'));
- } else {
- sb.append((char) (d + 'a' - 10));
- }
- }
- return sb;
- } catch (IOException e) {
- throw new BiancaModuleException(e);
- }
- }
- /**
- * Alias of rtrim. Removes trailing whitespace.
- *
- * @param env the bianca environment
- * @param str the string to be trimmed
- * @param charset optional set of characters to trim
- * @return the trimmed string
- */
- public static StringValue chop(Env env,
- StringValue str,
- @Optional String charset) {
- return rtrim(env, str, charset);
- }
- /**
- * converts a number to its character equivalent
- *
- * @param value the integer value
- * @return the string equivalent
- */
- public static StringValue chr(Env env, long value) {
- StringValue sb = new StringValue();
- sb.append((char) value);
- return sb;
- }
- /**
- * Splits a string into chunks
- *
- * @param body the body string
- * @param chunkLen the optional chunk length, defaults to 76
- * @param end the optional end value, defaults to "\r\n"
- */
- public static String chunk_split(String body,
- @Optional("76") int chunkLen,
- @Optional("\"\\r\\n\"") String end) {
- if (body == null) {
- body = "";
- }
- if (end == null) {
- end = "";
- }
- if (chunkLen < 1) // TODO: real exn
- {
- throw new IllegalArgumentException(L.l("bad value {0}", chunkLen));
- }
- StringBuilder sb = new StringBuilder();
- int i = 0;
- for (; i + chunkLen <= body.length(); i += chunkLen) {
- sb.append(body.substring(i, i + chunkLen));
- sb.append(end);
- }
- if (i < body.length()) {
- sb.append(body.substring(i));
- sb.append(end);
- }
- return sb.toString();
- }
- /**
- * Converts from one cyrillic set to another.
- * <p/>
- * This implementation does nothing, because bianca stores strings as
- * 16 bit unicode.
- */
- public static String convert_cyr_string(Env env,
- String str,
- String from,
- String to) {
- env.stub("convert_cyr_string");
- return str;
- }
- public static Value convert_uudecode(Env env, StringValue source) {
- int length = source.length();
- if (length == 0) {
- return BooleanValue.FALSE;
- }
- StringBuilder builder = new StringBuilder();
- int i = 0;
- while (i < length) {
- int ch1 = source.charAt(i++);
- if (ch1 == 0x60 || ch1 == 0x20) {
- break;
- } else if (ch1 < 0x20 || 0x5f < ch1) {
- continue;
- }
- int sublen = ch1 - 0x20;
- while (sublen > 0) {
- int code;
- code = ((source.charAt(i++) - 0x20) & 0x3f) << 18;
- code += ((source.charAt(i++) - 0x20) & 0x3f) << 12;
- code += ((source.charAt(i++) - 0x20) & 0x3f) << 6;
- code += ((source.charAt(i++) - 0x20) & 0x3f);
- builder.append(code >> 16);
- if (sublen > 1) {
- builder.append(code >> 8);
- }
- if (sublen > 2) {
- builder.append(code);
- }
- sublen -= 3;
- }
- }
- return env.createString(builder.toString());
- }
- /**
- * uuencode a string.
- */
- public static Value convert_uuencode(StringValue source) {
- if (source.length() == 0) {
- return BooleanValue.FALSE;
- }
- StringValue result = new StringValue();
- int i = 0;
- int length = source.length();
- while (i < length) {
- int sublen = length - i;
- if (45 < sublen) {
- sublen = 45;
- }
- result.append((char) (sublen + 0x20));
- int end = i + sublen;
- while (i < end) {
- int code = source.charAt(i++) << 16;
- if (i < length) {
- code += source.charAt(i++) << 8;
- }
- if (i < length) {
- code += source.charAt(i++);
- }
- result.append(toUUChar(((code >> 18) & 0x3f)));
- result.append(toUUChar(((code >> 12) & 0x3f)));
- result.append(toUUChar(((code >> 6) & 0x3f)));
- result.append(toUUChar(((code) & 0x3f)));
- }
- result.append('\n');
- }
- result.append((char) 0x60);
- result.append('\n');
- return result;
- }
- /**
- * Returns an array of information about the characters.
- */
- public static Value count_chars(StringValue data,
- @Optional("0") int mode) {
- int[] count = new int[256];
- int length = data.length();
- for (int i = 0; i < length; i++) {
- count[data.charAt(i) & 0xff] += 1;
- }
- switch (mode) {
- case 0: {
- ArrayValue result = new ArrayValueImpl();
- for (int i = 0; i < count.length; i++) {
- result.put(LongValue.create(i), LongValue.create(count[i]));
- }
- return result;
- }
- case 1: {
- ArrayValue result = new ArrayValueImpl();
- for (int i = 0; i < count.length; i++) {
- if (count[i] > 0) {
- result.put(LongValue.create(i), LongValue.create(count[i]));
- }
- }
- return result;
- }
- case 2: {
- ArrayValue result = new ArrayValueImpl();
- for (int i = 0; i < count.length; i++) {
- if (count[i] == 0) {
- result.put(LongValue.create(i), LongValue.create(count[i]));
- }
- }
- return result;
- }
- case 3: {
- StringValue sb = new StringValue();
- for (int i = 0; i < count.length; i++) {
- if (count[i] > 0) {
- sb.append((char) i);
- }
- }
- return sb;
- }
- case 4: {
- StringValue sb = new StringValue();
- for (int i = 0; i < count.length; i++) {
- if (count[i] == 0) {
- sb.append((char) i);
- }
- }
- return sb;
- }
- default:
- return BooleanValue.FALSE;
- }
- }
- /**
- * Calculates the crc32 value for a string
- *
- * @param str the string value
- * @return the crc32 hash
- */
- public static long crc32(StringValue str) {
- return str.getCrc32Value();
- }
- public static String crypt(String string, @Optional String salt) {
- if (string == null) {
- string = "";
- }
- if (salt == null || salt.equals("")) {
- salt = ("" + Crypt.resultToChar(RandomUtil.nextInt(0x40))
- + Crypt.resultToChar(RandomUtil.nextInt(0x40)));
- }
- return Crypt.crypt(string, salt);
- }
- // TODO: echo
- /**
- * Explodes a string into an array
- *
- * @param separator the separator string
- * @param string the string to be exploded
- * @param limit the max number of elements
- * @return an array of exploded values
- */
- public static Value explode(Env env,
- StringValue separator,
- StringValue string,
- @Optional("0x7fffffff") long limit) {
- if (separator.length() == 0) {
- env.warning(L.l("Delimiter is empty"));
- return BooleanValue.FALSE;
- }
- int head = 0;
- ArrayValue array = new ArrayValueImpl();
- int separatorLength = separator.length();
- int stringLength = string.length();
- long ulimit;
- if (limit >= 0) {
- ulimit = limit;
- } else {
- ulimit = 0x7fffffff;
- }
- for (int i = 0; i < stringLength; ++i) {
- if (ulimit <= array.getSize() + 1) {
- break;
- }
- if (string.regionMatches(i, separator.toString(), 0)) {
- StringValue chunk = string.substring(head, i);
- array.append(chunk);
- head = i + separatorLength;
- i = head - 1;
- }
- }
- StringValue chunk = string.substring(head);
- array.append(chunk);
- while (array.getSize() > 0 && limit++ < 0) {
- array.pop(env);
- }
- return array;
- }
- /**
- * Use printf style formatting to write a string to a file.
- *
- * @param fd the file to write to
- * @param format the format string
- * @param args the valujes to apply to the format string
- */
- public static Value fprintf(Env env,
- @NotNull BinaryOutput os,
- StringValue format,
- Value[] args) {
- Value value = sprintf(env, format, args);
- return FileModule.fwrite(env, os, value.toInputStream(),
- Integer.MAX_VALUE);
- }
- /**
- * implodes an array into a string
- *
- * @param glueV the separator string
- * @param piecesV the array to be imploded
- * @return a string of imploded values
- */
- public static Value implode(Env env,
- Value glueV,
- @Optional Value piecesV) {
- StringValue glue;
- ArrayValue pieces;
- if ((piecesV.isArray() && glueV.isArray())
- || glueV.isArray()) {
- pieces = glueV.toArrayValue(env);
- glue = piecesV.toStringValue();
- } else if (piecesV.isArray()) {
- pieces = piecesV.toArrayValue(env);
- glue = glueV.toStringValue();
- } else {
- env.warning(L.l("neither argument to implode is an array: {0}, {1}",
- glueV.getClass().getName(), piecesV.getClass().getName()));
- return NullValue.NULL;
- }
- StringValue sb = new StringValue();
- boolean isFirst = true;
- Iterator<Value> iter = pieces.getValueIterator(env);
- while (iter.hasNext()) {
- if (!isFirst) {
- sb = sb.append(glue);
- }
- isFirst = false;
- sb = sb.append(iter.next());
- }
- return sb;
- }
- /**
- * implodes an array into a string
- *
- * @param glueV the separator string
- * @param piecesV the array to be imploded
- * @return a string of imploded values
- */
- public static Value join(Env env,
- Value glueV,
- Value piecesV) {
- return implode(env, glueV, piecesV);
- }
- /**
- * Lowercases the first character
- *
- * @param string the input string
- */
- public static StringValue lcfirst(Env env, StringValue string) {
- if (string == null) {
- return StringValue.EMPTY;
- } else if (string.length() == 0) {
- return string;
- }
- StringValue sb = new StringValue();
- sb = sb.append(Character.toLowerCase(string.charAt(0)));
- sb = sb.append(string, 1, string.length());
- return sb;
- }
- /**
- * Calculate Levenshtein distance between two strings
- *
- * @param str1 first string
- * @param str2 second string
- * @param cost_ins defines the cost of insertion
- * @param cost_rep defines the cost of replacement
- * @param cost_del defines the cost of deletion
- * @return int Levenshtein-Distance between the two argument strings
- */
- public static int levenshtein(Env env, String str1, String str2, @Optional("1") int cost_ins, @Optional("1") int cost_rep, @Optional("1") int cost_del) {
- int distance = -1;
- int i1, i2, c0, c1, c2, l1, l2;
- l1 = str1.length();
- l2 = str2.length();
- int[] p1 = new int[l2 + 1];
- int[] p2 = new int[l2 + 1];
- int[] tmp = new int[l2 + 1];
- if (l1 == 0) {
- return l2 * cost_ins;
- }
- if (l2 == 0) {
- return l1 * cost_del;
- }
- // TODO: keep this limitation ? really ?
- if ((l1 > LEVENSHTEIN_MAX_LENGTH) || (l2 > LEVENSHTEIN_MAX_LENGTH)) {
- distance = -1;
- } else {
- for (i2 = 0; i2 <= l2; i2++) {
- p1[i2] = i2 * cost_ins;
- }
- for (i1 = 0; i1 < l1; i1++) {
- p2[0] = p1[0] + cost_del;
- for (i2 = 0; i2 < l2; i2++) {
- c0 = p1[i2] + ((str1.charAt(i1) == str2.charAt(i2)) ? 0 : cost_rep);
- c1 = p1[i2 + 1] + cost_del;
- if (c1 < c0) {
- c0 = c1;
- }
- c2 = p2[i2] + cost_ins;
- if (c2 < c0) {
- c0 = c2;
- }
- p2[i2 + 1] = c0;
- }
- tmp = p1;
- p1 = p2;
- p2 = tmp;
- }
- c0 = p1[l2];
- distance = c0;
- }
- if (distance < 0) {
- env.warning(L.l("Argument string(s) too long"));
- }
- return distance;
- }
- /**
- * Gets locale-specific symbols.
- * XXX: locale charset
- */
- public static ArrayValue localeconv(Env env) {
- ArrayValueImpl array = new ArrayValueImpl();
- BiancaLocale money = env.getLocaleInfo().getMonetary();
- Locale locale = money.getLocale();
- DecimalFormatSymbols decimal = new DecimalFormatSymbols(locale);
- Currency currency = NumberFormat.getInstance(locale).getCurrency();
- array.put(env.createString("decimal_point"),
- new StringValue(decimal.getDecimalSeparator()));
- array.put(env.createString("thousands_sep"),
- new StringValue(decimal.getGroupingSeparator()));
- //array.put("grouping", "");
- array.put(env.createString("int_curr_symbol"),
- new StringValue(decimal.getInternationalCurrencySymbol()));
- array.put(env.createString("currency_symbol"),
- new StringValue(decimal.getCurrencySymbol()));
- array.put(env.createString("mon_decimal_point"),
- new StringValue(decimal.getMonetaryDecimalSeparator()));
- array.put(env.createString("mon_thousands_sep"),
- new StringValue(decimal.getGroupingSeparator()));
- //array.put("mon_grouping", "");
- array.put(new StringValue("positive_sign"), StringValue.EMPTY);
- array.put(env.createString("negative_sign"),
- new StringValue(decimal.getMinusSign()));
- array.put(env.createString("int_frac_digits"),
- LongValue.create(currency.getDefaultFractionDigits()));
- array.put(env.createString("frac_digits"),
- LongValue.create(currency.getDefaultFractionDigits()));
- //array.put("p_cs_precedes", "");
- //array.put("p_sep_by_space", "");
- //array.put("n_cs_precedes", "");
- //array.put("n_sep_by_space", "");
- //array.put("p_sign_posn", "");
- //array.put("n_sign_posn", "");
- return array;
- }
- /**
- * Removes leading whitespace.
- *
- * @param string the string to be trimmed
- * @param characters optional set of characters to trim
- * @return the trimmed string
- */
- public static StringValue ltrim(Env env,
- StringValue string,
- @Optional String characters) {
- if (characters == null) {
- characters = "";
- }
- boolean[] trim;
- if (characters.equals("")) {
- trim = TRIM_WHITESPACE;
- } else {
- trim = parseCharsetBitmap(env, characters);
- }
- for (int i = 0; i < string.length(); i++) {
- char ch = string.charAt(i);
- if (ch >= 256 || !trim[ch]) {
- if (i == 0) {
- return string;
- } else {
- return string.substring(i);
- }
- }
- }
- return StringValue.EMPTY;
- }
- /**
- * returns the md5 hash
- *
- * @param source the string
- * @param rawOutput if true, return the raw binary
- * @return a string of imploded values
- */
- public static Value md5(Env env,
- InputStream is,
- @Optional boolean rawOutput) {
- try {
- MessageDigest md = _md5FreeList.allocate();
- if (md == null) {
- md = MessageDigest.getInstance("MD5");
- }
- md.reset();
- int ch;
- while ((ch = is.read()) >= 0) {
- md.update((byte) ch);
- }
- byte[] digest = md.digest();
- _md5FreeList.free(md);
- return hashToValue(digest, rawOutput);
- } catch (Exception e) {
- throw new BiancaModuleException(e);
- }
- }
- /**
- * returns the md5 hash
- *
- * @param source the string
- * @param rawOutput if true, return the raw binary
- * @return a string of imploded values
- */
- public static Value md5_file(Env env,
- Path source,
- @Optional boolean rawOutput) {
- try {
- MessageDigest md = MessageDigest.getInstance("MD5");
- InputStream is = null;
- try {
- is = source.openRead();
- int d;
- while ((d = is.read()) >= 0) {
- md.update((byte) d);
- }
- byte[] digest = md.digest();
- return hashToValue(digest, rawOutput);
- } catch (IOException e) {
- log.log(Level.FINE, e.toString(), e);
- return BooleanValue.FALSE;
- } finally {
- try {
- if (is != null) {
- is.close();
- }
- } catch (IOException e) {
- }
- }
- } catch (Exception e) {
- throw new BiancaModuleException(e);
- }
- }
- /**
- * Returns the metaphone of a string.
- * This implementation produces identical results to the php version,
- * which does contain some bugs.
- */
- public static String metaphone(String string) {
- if (string == null) {
- string = "";
- }
- int length = string.length();
- int index = 0;
- char ch = 0;
- // ignore everything up until first letter
- for (; index < length; index++) {
- ch = toUpperCase(string.charAt(index));
- if ('A' <= ch && ch <= 'Z') {
- break;
- }
- }
- if (index == length) {
- return "";
- }
- int lastIndex = length - 1;
- StringBuilder result = new StringBuilder(length);
- // special case first letter
- char nextCh = index < lastIndex
- ? toUpperCase(string.charAt(index + 1))
- : 0;
- switch (ch) {
- case 'A':
- if (nextCh == 'E') {
- result.append('E');
- index += 2;
- } else {
- result.append('A');
- index += 1;
- }
- break;
- case 'E':
- case 'I':
- case 'O':
- case 'U':
- result.append(ch);
- index += 1;
- break;
- case 'G':
- case 'K':
- case 'P':
- if (nextCh == 'N') {
- result.append('N');
- index += 2;
- }
- break;
- case 'W':
- if (nextCh == 'H' || nextCh == 'R') {
- result.append(nextCh);
- index += 2;
- } else {
- switch (nextCh) {
- case 'A':
- case 'E':
- case 'I':
- case 'O':
- case 'U':
- result.append('W');
- index += 2;
- break;
- default:
- break;
- }
- }
- break;
- case 'X':
- result.append('S');
- index += 1;
- break;
- default:
- break;
- }
- // the rest of the letters
- char prevCh;
- for (; index < length; index++) {
- if (index > 0) {
- prevCh = toUpperCase(string.charAt(index - 1));
- } else {
- prevCh = 0;
- }
- ch = toUpperCase(string.charAt(index));
- if (ch < 'A' || ch > 'Z') {
- continue;
- }
- if (ch == prevCh && ch != 'C') {
- continue;
- }
- if (index + 1 < length) {
- nextCh = toUpperCase(string.charAt(index + 1));
- } else {
- nextCh = 0;
- }
- char nextnextCh;
- if (index + 2 < length) {
- nextnextCh = toUpperCase(string.charAt(index + 2));
- } else {
- nextnextCh = 0;
- }
- switch (ch) {
- case 'B':
- if (prevCh != 'M') {
- result.append('B');
- }
- break;
- case 'C':
- switch (nextCh) {
- case 'E':
- case 'I':
- case 'Y':
- // makesoft
- if (nextCh == 'I' && nextnextCh == 'A') {
- result.append('X');
- } else if (prevCh == 'S') {
- } else {
- result.append('S');
- }
- break;
- default:
- if (nextCh == 'H') {
- result.append('X');
- index++;
- } else {
- result.append('K');
- }
- break;
- }
- break;
- case 'D':
- if (nextCh == 'G') {
- switch (nextnextCh) {
- case 'E':
- case 'I':
- case 'Y':
- // makesoft
- result.append('J');
- index++;
- break;
- default:
- result.append('T');
- break;
- }
- } else {
- result.append('T');
- }
- break;
- case 'G':
- if (nextCh == 'H') {
- boolean isSilent = false;
- if (index - 3 >= 0) {
- char prev3Ch = toUpperCase(string.charAt(index - 3));
- switch (prev3Ch) {
- // noghtof
- case 'B':
- case 'D':
- case 'H':
- isSilent = true;
- break;
- default:
- break;
- }
- }
- if (!isSilent) {
- if (index - 4 >= 0) {
- char prev4Ch = toUpperCase(string.charAt(index - 4));
- isSilent = (prev4Ch == 'H');
- }
- }
- if (!isSilent) {
- result.append('F');
- index++;
- }
- } else if (nextCh == 'N') {
- char nextnextnextCh;
- if (index + 3 < length) {
- nextnextnextCh = toUpperCase(string.charAt(index + 3));
- } else {
- nextnextnextCh = 0;
- }
- if (nextnextCh < 'A' || nextnextCh > 'Z') {
- } else if (nextnextCh == 'E' && nextnextnextCh == 'D') {
- } else {
- result.append('K');
- }
- } else if (prevCh == 'G') {
- result.append('K');
- } else {
- switch (nextCh) {
- case 'E':
- case 'I':
- case 'Y':
- // makesoft
- result.append('J');
- break;
- default:
- result.append('K');
- break;
- }
- }
- break;
- case 'H':
- case 'W':
- case 'Y':
- switch (nextCh) {
- case 'A':
- case 'E':
- case 'I':
- case 'O':
- case 'U':
- // followed by a vowel
- if (ch == 'H') {
- switch (prevCh) {
- case 'C':
- case 'G':
- case 'P':
- case 'S':
- case 'T':
- // affecth
- break;
- default:
- result.append('H');
- break;
- }
- } else {
- result.append(ch);
- }
- break;
- default:
- // not followed by a vowel
- break;
- }
- break;
- case 'K':
- if (prevCh != 'C') {
- result.append('K');
- }
- break;
- case 'P':
- if (nextCh == 'H') {
- result.append('F');
- } else {
- result.append('P');
- }
- break;
- case 'Q':
- result.append('K');
- break;
- case 'S':
- if (nextCh == 'I' && (nextnextCh == 'O' || nextnextCh == 'A')) {
- result.append('X');
- } else if (nextCh == 'H') {
- result.append('X');
- index++;
- } else {
- result.append('S');
- }
- break;
- case 'T':
- if (nextCh == 'I' && (nextnextCh == 'O' || nextnextCh == 'A')) {
- result.append('X');
- } else if (nextCh == 'H') {
- result.append('0');
- index++;
- } else {
- result.append('T');
- }
- break;
- case 'V':
- result.append('F');
- break;
- case 'X':
- result.append('K');
- result.append('S');
- break;
- case 'Z':
- result.append('S');
- break;
- case 'F':
- case 'J':
- case 'L':
- case 'M':
- case 'N':
- case 'R':
- result.append(ch);
- break;
- default:
- break;
- }
- }
- return result.toString();
- }
- /**
- * Returns a formatted money value.
- * XXX: locale charset
- *
- * @param format the format
- * @param value the value
- * @return a string of formatted values
- */
- public static String money_format(Env env, String format, double value) {
- BiancaLocale monetary = env.getLocaleInfo().getMonetary();
- Locale locale = monetary.getLocale();
- return NumberFormat.getCurrencyInstance(locale).format(value);
- }
- /**
- * TODO: finish implementation of nl_langinfo
- */
- public static String nl_langinfo(Env env, int item) {
- BiancaLocale money = env.getLocaleInfo().getMonetary();
- Locale locale = money.getLocale();
- DecimalFormatSymbols decimal = new DecimalFormatSymbols(locale);
- Currency currency = NumberFormat.getInstance(locale).getCurrency();
- String result;
- switch (item) {
- default:
- result = Boolean.FALSE.toString();
- break;
- }
- return result;
- }
- /**
- * Returns a formatted number.
- *
- * @param value the value
- * @param decimals the number of decimals
- * @param pointValue the decimal point string
- * @param groupValue the thousands separator
- * @return a string of the formatted number
- */
- public static String number_format(Env env,
- double value,
- @Optional int decimals,
- @Optional Value pointValue,
- @Optional Value groupValue) {
- boolean isGroupDefault = (groupValue instanceof DefaultValue);
- boolean isPointDefault = (pointValue instanceof DefaultValue);
- if (!isPointDefault && isGroupDefault) {
- env.warning(L.l("wrong parameter count"));
- return null;
- }
- String pattern;
- char point = '.';
- if (!pointValue.isNull()) {
- String pointString = pointValue.toString();
- point = (pointString.length() == 0) ? 0 : pointString.charAt(0);
- }
- char group = ',';
- if (!groupValue.isNull()) {
- String groupString = groupValue.toString();
- group = (groupString.length() == 0) ? 0 : groupString.charAt(0);
- }
- if (decimals > 0) {
- StringBuilder patternBuilder = new StringBuilder(6 + decimals);
- patternBuilder.append(group == 0 ? "###0." : "#,##0.");
- for (int i = 0; i < decimals; i++) {
- patternBuilder.append('0');
- }
- pattern = patternBuilder.toString();
- } else {
- pattern = group == 0 ? "###0" : "#,##0";
- }
- DecimalFormatSymbols decimalFormatSymbols;
- if (point == '.' && group == ',') {
- decimalFormatSymbols = DEFAULT_DECIMAL_FORMAT_SYMBOLS;
- } else {
- decimalFormatSymbols = new DecimalFormatSymbols();
- decimalFormatSymbols.setDecimalSeparator(point);
- decimalFormatSymbols.setGroupingSeparator(group);
- decimalFormatSymbols.setZeroDigit('0');
- }
- DecimalFormat format = new DecimalFormat(pattern, decimalFormatSymbols);
- String result = format.format(value);
- if (point == 0 && decimals > 0) {
- // no way to get DecimalFormat to output nothing for the point,
- // so remove it here
- int i = result.lastIndexOf(point);
- return result.substring(0, i) + result.substring(i + 1, result.length());
- } else {
- return result;
- }
- }
- /**
- * Converts the first character to an integer.
- *
- * @param string the string to be converted
- * @return the integer value
- */
- public static long ord(StringValue string) {
- if (string.length() == 0) {
- return 0;
- } else {
- return string.charAt(0);
- }
- }
- /**
- * Parses the string as a query string.
- *
- * @param env the calling environment
- * @param str the query string
- * @param array the optional result array
- */
- @UsesSymbolTable
- public static Value parse_str(Env env, StringValue str,
- @Optional @Reference Value ref) {
- boolean isRef = ref instanceof Var;
- ArrayValue result = null;
- if (isRef) {
- result = new ArrayValueImpl();
- ref.set(result);
- } else if (ref instanceof ArrayValue) {
- result = (ArrayValue) ref;
- isRef = true;
- } else {
- result = new ArrayValueImpl();
- }
- return StringUtility.parseStr(env,
- str,
- result,
- isRef,
- env.getHttpInputEncoding());
- }
- /**
- * Prints the string.
- *
- * @param env the bianca environment
- * @param value the string to print
- */
- public static long print(Env env, Value value) {
- value.print(env);
- return 1;
- }
- /**
- * print to the output with a formatter
- *
- * @param env the bianca environment
- * @param format the format string
- * @param args the format arguments
- * @return the formatted string
- */
- public static int printf(Env env, StringValue format, Value[] args) {
- Value str = sprintf(env, format, args);
- str.print(env);
- return str.length();
- }
- /**
- * Converts a RFC2045 quoted printable string to a string.
- */
- // TODO: i18n
- public static String quoted_printable_decode(String str) {
- if (str == null) {
- str = "";
- }
- StringBuilder sb = new StringBuilder();
- int length = str.length();
- for (int i = 0; i < length; i++) {
- char ch = str.charAt(i);
- if (33 <= ch && ch <= 60) {
- sb.append(ch);
- } else if (62 <= ch && ch <= 126) {
- sb.append(ch);
- } else if (ch == ' ' || ch == '\t') {
- if (i + 1 < str.length()
- && (str.charAt(i + 1) == '\r' || str.charAt(i + 1) == '\n')) {
- sb.append('=');
- sb.append(toUpperHexChar(ch >> 4));
- sb.append(toUpperHexChar(ch));
- } else {
- sb.append(ch);
- }
- } else if (ch == '\r' || ch == '\n') {
- sb.append(ch);
- } else {
- sb.append('=');
- sb.append(toUpperHexChar(ch >> 4));
- sb.append(toUpperHexChar(ch));
- }
- }
- return sb.toString();
- }
- /**
- * Escapes meta characters.
- *
- * @param string the string to be quoted
- * @return the quoted
- */
- public static Value quotemeta(StringValue string) {
- int len = string.length();
- StringValue sb = new StringValue();
- for (int i = 0; i < len; i++) {
- char ch = string.charAt(i);
- switch (ch) {
- case '.':
- case '\\':
- case '+':
- case '*':
- case '?':
- case '[':
- case '^':
- case ']':
- case '(':
- case ')':
- case '$':
- sb.append("\\");
- sb.append(ch);
- break;
- default:
- sb.append(ch);
- break;
- }
- }
- return sb;
- }
- private static final boolean[] TRIM_WHITESPACE = new boolean[256];
- static {
- TRIM_WHITESPACE['\0'] = true;
- TRIM_WHITESPACE['\b'] = true;
- TRIM_WHITESPACE[' '] = true;
- TRIM_WHITESPACE['\t'] = true;
- TRIM_WHITESPACE['\r'] = true;
- TRIM_WHITESPACE['\n'] = true;
- TRIM_WHITESPACE[0x0B] = true;
- }
- /**
- * Removes trailing whitespace.
- *
- * @param env the bianca environment
- * @param string the string to be trimmed
- * @param characters optional set of characters to trim
- * @return the trimmed string
- */
- public static StringValue rtrim(Env env,
- StringValue string,
- @Optional String characters) {
- if (characters == null) {
- characters = "";
- }
- boolean[] trim;
- if (characters.equals("")) {
- trim = TRIM_WHITESPACE;
- } else {
- trim = parseCharsetBitmap(env, characters);
- }
- for (int i = string.length() - 1; i >= 0; i--) {
- char ch = string.charAt(i);
- if (ch >= 256 || !trim[ch]) {
- if (i == string.length()) {
- return string;
- } else {
- return (StringValue) string.subSequence(0, i + 1);
- }
- }
- }
- return StringValue.EMPTY;
- }
- /**
- * Sets locale configuration.
- */
- public static Value setlocale(Env env,
- int category,
- Value localeArg,
- Value[] fallback) {
- LocaleInfo localeInfo = env.getLocaleInfo();
- if (localeArg instanceof ArrayValue) {
- for (Value value : ((ArrayValue) localeArg).values()) {
- BiancaLocale locale = setLocale(localeInfo,
- category,
- value.toString());
- if (locale != null) {
- return env.createString(locale.toString());
- }
- }
- } else {
- BiancaLocale locale = setLocale(localeInfo,
- category,
- localeArg.toString());
- if (locale != null) {
- return env.createString(locale.toString());
- }
- }
- for (int i = 0; i < fallback.length; i++) {
- BiancaLocale locale = setLocale(localeInfo,
- category,
- fallback[i].toString());
- if (locale != null) {
- return env.createString(locale.toString());
- }
- }
- return BooleanValue.FALSE;
- }
- /**
- * Sets locale configuration.
- */
- private static BiancaLocale setLocale(LocaleInfo localeInfo,
- int category,
- String localeName) {
- BiancaLocale locale = findLocale(localeName);
- if (locale == null) {
- return null;
- }
- switch (category) {
- case LC_ALL:
- localeInfo.setAll(locale);
- return localeInfo.getMessages();
- case LC_COLLATE:
- localeInfo.setCollate(locale);
- return localeInfo.getCollate();
- case LC_CTYPE:
- localeInfo.setCtype(locale);
- return localeInfo.getCtype();
- case LC_MONETARY:
- localeInfo.setMonetary(locale);
- return localeInfo.getMonetary();
- case LC_NUMERIC:
- localeInfo.setNumeric(locale);
- return localeInfo.getNumeric();
- case LC_TIME:
- localeInfo.setTime(locale);
- return localeInfo.getTime();
- case LC_MESSAGES:
- localeInfo.setMessages(locale);
- return localeInfo.getMessages();
- default:
- return null;
- }
- }
- /*
- * Example locale: fr_FR.UTF-8@euro, french (on Windows)
- * (French, France, UTF-8, with euro currency support)
- */
- private static BiancaLocale findLocale(String localeName) {
- String language;
- String country;
- String charset = null;
- String variant = null;
- CharBuffer sb = CharBuffer.allocate();
- int len = localeName.length();
- int i = 0;
- char ch = 0;
- while (i < len && (ch = localeName.charAt(i++)) != '-' && ch != '_') {
- sb.append(ch);
- }
- language = sb.toString();
- sb.clear();
- while (i < len && (ch = localeName.charAt(i)) != '.' && ch != '@') {
- sb.append(ch);
- i++;
- }
- if (ch == '.') {
- i++;
- }
- country = sb.toString();
- sb.clear();
- while (i < len && (ch = localeName.charAt(i)) != '@') {
- sb.append(ch);
- i++;
- }
- if (sb.length() > 0) {
- charset = sb.toString();
- }
- if (i + 1 < len) {
- variant = localeName.substring(i + 1);
- }
- Locale locale;
- // java versions >= 1.5 should automatically use the euro sign
- if (variant != null && !variant.equalsIgnoreCase("euro")) {
- locale = new Locale(language, country, variant);
- } else if (country != null) {
- locale = new Locale(language, country);
- } else {
- locale = new Locale(language);
- }
- if (isValidLocale(locale)) {
- return new BiancaLocale(locale, charset);
- } else {
- return null;
- }
- }
- /**
- * Returns true if the locale is supported.
- */
- private static boolean isValidLocale(Locale locale) {
- Locale[] validLocales = Locale.getAvailableLocales();
- for (int i = 0; i < validLocales.length; i++) {
- if (validLocales[i].equals(locale)) {
- return true;
- }
- }
- return false;
- }
- /**
- * returns the md5 hash
- *
- * @param source the string
- * @param rawOutput if true, return the raw binary
- * @return a string of imploded values
- */
- public static Value sha1(Env env,
- String source,
- @Optional boolean rawOutput) {
- if (source == null) {
- source = "";
- }
- try {
- MessageDigest md = MessageDigest.getInstance("SHA1");
- for (int i = 0; i < source.length(); i++) {
- char ch = source.charAt(i);
- md.update((byte) ch);
- }
- byte[] digest = md.digest();
- return hashToValue(digest, rawOutput);
- } catch (Exception e) {
- throw new BiancaException(e);
- }
- }
- /**
- * returns the md5 hash
- *
- * @param source the string
- * @param rawOutput if true, return the raw binary
- * @return a string of imploded values
- */
- public static Value sha1_file(Env env,
- Path source,
- @Optional boolean rawOutput) {
- try {
- MessageDigest md = MessageDigest.getInstance("SHA1");
- InputStream is = null;
- try {
- is = source.openRead();
- int d;
- while ((d = is.read()) >= 0) {
- md.update((byte) d);
- }
- byte[] digest = md.digest();
- return hashToValue(digest, rawOutput);
- } catch (IOException e) {
- log.log(Level.FINE, e.toString(), e);
- return BooleanValue.FALSE;
- } finally {
- try {
- if (is != null) {
- is.close();
- }
- } catch (IOException e) {
- }
- }
- } catch (Exception e) {
- throw new BiancaException(e);
- }
- }
- private static Value hashToValue(byte[] bytes, boolean isBinary) {
- if (isBinary) {
- StringValue v = new StringValue();
- v.append(new String(bytes), 0, bytes.length);
- return v;
- } else {
- StringValue v = new StringValue();
- for (int i = 0; i < bytes.length; i++) {
- int ch = bytes[i];
- int d1 = (ch >> 4) & 0xf;
- int d2 = (ch) & 0xf;
- if (d1 < 10) {
- v.append((char) ('0' + d1));
- } else {
- v.append((char) ('a' + d1 - 10));
- }
- if (d2 < 10) {
- v.append((char) ('0' + d2));
- } else {
- v.append((char) ('a' + d2 - 10));
- }
- }
- return v;
- }
- }
- // TODO: similar_text
- private static final char[] SOUNDEX_VALUES = "01230120022455012623010202".toCharArray();
- public static Value soundex(StringValue string) {
- int length = string.length();
- if (length == 0) {
- return BooleanValue.FALSE;
- }
- StringValue result = new StringValue();
- int count = 0;
- char lastCode = 0;
- for (int i = 0; i < length && count < 4; i++) {
- char ch = toUpperCase(string.charAt(i));
- if ('A' <= ch && ch <= 'Z') {
- char code = SOUNDEX_VALUES[ch - 'A'];
- if (count == 0) {
- result.append(ch);
- count++;
- } else if (code != '0' && code != lastCode) {
- result.append(code);
- count++;
- }
- lastCode = code;
- }
- }
- for (; count < 4; count++) {
- result.append('0');
- }
- return result;
- }
- /**
- * Print to a string with a formatter
- *
- * @param format the format string
- * @param args the format arguments
- * @return the formatted string
- */
- public static Value sprintf(Env env, StringValue format, Value[] args) {
- ArrayList<PrintfSegment> segments = parsePrintfFormat(env, format);
- StringValue sb = new StringValue();
- for (PrintfSegment segment : segments) {
- if (!segment.apply(env, sb, args)) {
- return BooleanValue.FALSE;
- }
- }
- return sb;
- }
- private static ArrayList<PrintfSegment> parsePrintfFormat(Env env,
- StringValue format) {
- ArrayList<PrintfSegment> segments = new ArrayList<PrintfSegment>();
- StringBuilder sb = new StringBuilder();
- StringBuilder flags = new StringBuilder();
- int length = format.length();
- int index = 0;
- for (int i = 0; i < length; i++) {
- char ch = format.charAt(i);
- if (i + 1 < length && ch == '%') {
- // The C printf silently ignores invalid flags, so we need to
- // remove them if present.
- sb.append(ch);
- boolean isLeft = false;
- boolean isAlt = false;
- boolean isShowSign = false;
- int argIndex = -1;
- int leftPadLength = 0;
- int width = 0;
- int padChar = -1;
- flags.setLength(0);
- int j = i + 1;
- loop:
- for (; j < length; j++) {
- ch = format.charAt(j);
- switch (ch) {
- case '-':
- isLeft = true;
- if (j + 1 < length && format.charAt(j + 1) == '0') {
- padChar = '0';
- j++;
- }
- /*
- for (int k = j + 1; k < length; k++) {
- char digit = format.charAt(k);
- if ('0' <= digit && digit <= '9') {
- leftPadLength = leftPadLength * 10 + digit - '0';
- j++;
- }
- else
- break;
- }
- */
- break;
- case '#':
- isAlt = true;
- break;
- case '0':
- if (padChar < 0) {
- padChar = '0';
- } else {
- int value = 0;
- for (int k = j + 1; k < length; k++) {
- char digit = format.charAt(k);
- if ('0' <= digit && digit <= '9') {
- value = value * 10 + digit - '0';
- j++;
- } else {
- break;
- }
- }
- if (j + 1 < length && format.charAt(j + 1) == '$') {
- argIndex = value - 1;
- j++;
- } else {
- width = value;
- }
- }
- break;
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- int value = ch - '0';
- for (int k = j + 1; k < length; k++) {
- char digit = format.charAt(k);
- if ('0' <= digit && digit <= '9') {
- value = value * 10 + digit - '0';
- j++;
- } else {
- break;
- }
- }
- if (j + 1 < length && format.charAt(j + 1) == '$') {
- argIndex = value - 1;
- j++;
- } else {
- width = value;
- }
- break;
- case '\'':
- padChar = format.charAt(j + 1);
- j += 1;
- break;
- case '+':
- isShowSign = true;
- break;
- case ' ':
- case ',':
- case '(':
- flags.append(ch);
- break;
- default:
- break loop;
- }
- }
- int head = j;
- if (argIndex < 0) {
- argIndex = index;
- }
- loop:
- for (; j < length; j++) {
- ch = format.charAt(j);
- switch (ch) {
- case '%':
- i = j;
- segments.add(new TextPrintfSegment(sb));
- sb.setLength(0);
- break loop;
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- case '.':
- case '$':
- break;
- case 's':
- case 'S':
- sb.setLength(sb.length() - 1);
- if (width <= 0 && 0 < leftPadLength) {
- width = leftPadLength;
- }
- index++;
- segments.add(new StringPrintfSegment(
- sb,
- isLeft || isAlt,
- padChar,
- ch == 'S',
- width,
- format.substring(head, j).toString(),
- argIndex));
- sb.setLength(0);
- i = j;
- break loop;
- case 'c':
- case 'C':
- sb.setLength(sb.length() - 1);
- if (width <= 0 && 0 < leftPadLength) {
- width = leftPadLength;
- }
- index++;
- segments.add(new CharPrintfSegment(
- sb,
- isLeft || isAlt,
- padChar,
- ch == 'C',
- width,
- format.substring(head, j).toString(),
- argIndex));
- sb.setLength(0);
- i = j;
- break loop;
- /*
- case 'u':
- sb.setLength(sb.length() - 1);
- if (sb.length() > 0)
- segments.add(new TextPrintfSegment(sb));
- sb.setLength(0);
- if (isLeft)
- sb.append('-');
- if (isAlt)
- sb.append('#');
- sb.append(flags);
- sb.append(format, head, j);
- sb.append(ch);
- //segments.add(UnsignedLongPrintfSegment.create(
- env, sb.toString(), index++));
- sb.setLength(0);
- i = j;
- break loop;
- */
- case 'i':
- ch = 'd';
- case 'd':
- case 'x':
- case 'o':
- case 'X':
- case 'b':
- case 'B':
- case 'u':
- sb.setLength(sb.length() - 1);
- if (sb.length() > 0) {
- segments.add(new TextPrintfSegment(sb));
- }
- sb.setLength(0);
- if (isAlt) {
- sb.append('#');
- }
- if (isShowSign) {
- sb.append('+');
- }
- sb.append(flags);
- if (width > 0) {
- if (isLeft) {
- sb.append('-');
- } else if (padChar == '0') {
- sb.append('0');
- }
- sb.append(width);
- }
- sb.append(format, head, j);
- sb.append(ch);
- index++;
- segments.add(
- LongPrintfSegment.create(env, sb.toString(), argIndex));
- sb.setLength(0);
- i = j;
- break loop;
- case 'e':
- case 'E':
- case 'f':
- case 'g':
- case 'G':
- case 'F':
- BiancaLocale locale = null;
- if (ch == 'F') {
- ch = 'f';
- } else {
- locale = env.getLocaleInfo().getNumeric();
- }
- sb.setLength(sb.length() - 1);
- if (sb.length() > 0) {
- segments.add(new TextPrintfSegment(sb));
- }
- sb.setLength(0);
- if (isAlt) {
- sb.append('#');
- }
- if (isShowSign) {
- sb.append('+');
- }
- sb.append(flags);
- if (width > 0) {
- if (isLeft) {
- sb.append('-');
- } else if (padChar == '0') {
- sb.append('0');
- }
- // '-' and '0' together is not supported by java.util.Formatter
- //if (padChar == '0')
- // sb.append((char) padChar);
- sb.append(width);
- }
- sb.append(format, head, j);
- sb.append(ch);
- index++;
- segments.add(new DoublePrintfSegment(sb.toString(),
- isLeft && padChar == '0',
- argIndex,
- locale));
- sb.setLength(0);
- i = j;
- break loop;
- default:
- if (isLeft) {
- sb.append('-');
- }
- if (isAlt) {
- sb.append('#');
- }
- sb.append(flags);
- sb.append(format, head, j);
- sb.append(ch);
- i = j;
- break loop;
- }
- }
- } else {
- sb.append(ch);
- }
- }
- if (sb.length() > 0) {
- segments.add(new TextPrintfSegment(sb));
- }
- return segments;
- }
- /**
- * scans a string
- *
- * @param format the format string
- * @param args the format arguments
- * @return the formatted string
- */
- public static Value sscanf(Env env,
- StringValue string,
- StringValue format,
- @Optional @Reference Value[] args) {
- ScanfSegment[] formatArray = sscanfParseFormat(env, format);
- int strlen = string.length();
- int sIndex = 0;
- boolean isReturnArray = args.length == 0;
- int argIndex = 0;
- if (strlen == 0) {
- return isReturnArray ? NullValue.NULL : LongValue.MINUS_ONE;
- }
- ArrayValue array = new ArrayValueImpl();
- for (int i = 0; i < formatArray.length; i++) {
- ScanfSegment segment = formatArray[i];
- Value var;
- if (!segment.isAssigned()) {
- var = null;
- } else if (isReturnArray) {
- var = array;
- } else {
- if (argIndex < args.length) {
- var = args[argIndex];
- if (sIndex < strlen) {
- argIndex++;
- }
- } else {
- env.warning(L.l("not enough vars passed in"));
- var = NullValue.NULL;
- }
- }
- sIndex = segment.apply(string,
- strlen,
- sIndex,
- var,
- isReturnArray);
- if (sIndex < 0) {
- if (isReturnArray) {
- return sscanfFillNull(array, formatArray, i);
- } else {
- return LongValue.create(argIndex);
- }
- }
- /*
- else if (sIndex == strlen) {
- if (isReturnArray)
- return sscanfFillNull(array, formatArray, i + 1);
- else
- return LongValue.create(argIndex);
- }
- */
- }
- return sscanfReturn(env, array, args, argIndex, isReturnArray, false);
- }
- private static Value sscanfFillNull(
- ArrayValue array, ScanfSegment[] formatArray, int fIndex) {
- for (; fIndex < formatArray.length; fIndex++) {
- ScanfSegment segment = formatArray[fIndex];
- if (segment.isAssigned()) {
- array.put(NullValue.NULL);
- }
- }
- return array;
- }
- /**
- * scans a string
- *
- * @param format the format string
- * @param args the format arguments
- * @return the formatted string
- */
- private static ScanfSegment[] sscanfParseFormat(Env env,
- StringValue format) {
- int fmtLen = format.length();
- int fIndex = 0;
- ArrayList<ScanfSegment> segmentList = new ArrayList<ScanfSegment>();
- StringBuilder sb = new StringBuilder();
- while (fIndex < fmtLen) {
- char ch = format.charAt(fIndex++);
- if (isWhitespace(ch)) {
- for (;
- (fIndex < fmtLen
- && isWhitespace(ch = format.charAt(fIndex)));
- fIndex++) {
- }
- scanfAddConstant(segmentList, sb);
- segmentList.add(ScanfWhitespace.SEGMENT);
- } else if (ch == '%') {
- int maxLen = -1;
- loop:
- while (fIndex < fmtLen) {
- ch = format.charAt(fIndex++);
- switch (ch) {
- case '%':
- sb.append('%');
- break loop;
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- if (maxLen < 0) {
- maxLen = 0;
- }
- maxLen = 10 * maxLen + ch - '0';
- break;
- case 's': {
- scanfAddConstant(segmentList, sb);
- segmentList.add(new ScanfString(maxLen));
- break loop;
- }
- case 'c': {
- if (maxLen < 0) {
- maxLen = 1;
- }
- scanfAddConstant(segmentList, sb);
- segmentList.add(new ScanfString(maxLen));
- break loop;
- }
- case 'n': {
- scanfAddConstant(segmentList, sb);
- segmentList.add(ScanfStringLength.SEGMENT);
- break loop;
- }
- case 'd': {
- scanfAddConstant(segmentList, sb);
- segmentList.add(new ScanfInteger(maxLen, 10, false));
- break loop;
- }
- case 'u': {
- scanfAddConstant(segmentList, sb);
- segmentList.add(new ScanfInteger(maxLen, 10, true));
- break loop;
- }
- case 'o': {
- scanfAddConstant(segmentList, sb);
- segmentList.add(new ScanfInteger(maxLen, 8, false));
- break loop;
- }
- case 'x':
- case 'X': {
- scanfAddConstant(segmentList, sb);
- segmentList.add(new ScanfHex(maxLen));
- break loop;
- }
- case 'e':
- case 'f': {
- scanfAddConstant(segmentList, sb);
- segmentList.add(new ScanfScientific(maxLen));
- break loop;
- }
- case '[': {
- scanfAddConstant(segmentList, sb);
- if (fmtLen <= fIndex) {
- env.warning(L.l("expected ']', saw end of string"));
- break loop;
- }
- boolean isNegated = false;
- if (fIndex < fmtLen
- && format.charAt(fIndex) == '^') {
- isNegated = true;
- fIndex++;
- }
- IntSet set = new IntSet();
- while (true) {
- if (fIndex == fmtLen) {
- env.warning(L.l("expected ']', saw end of string"));
- break loop;
- }
- char ch2 = format.charAt(fIndex++);
- if (ch2 == ']') {
- break;
- } else {
- set.union(ch2);
- }
- }
- if (isNegated) {
- segmentList.add(new ScanfSetNegated(set));
- } else {
- segmentList.add(new ScanfSet(set));
- }
- break loop;
- }
- default:
- log.fine(L.l("'{0}' is a bad sscanf string.", format));
- env.warning(L.l("'{0}' is a bad sscanf string.", format));
- // TODO:
- //return isAssign ? LongValue.create(argIndex) : array;
- break loop;
- }
- }
- } else {
- sb.append(ch);
- }
- }
- scanfAddConstant(segmentList, sb);
- ScanfSegment[] segmentArray = new ScanfSegment[segmentList.size()];
- return segmentList.toArray(segmentArray);
- }
- private static void scanfAddConstant(
- ArrayList<ScanfSegment> segmentList, StringBuilder sb) {
- if (sb.length() == 0) {
- return;
- }
- segmentList.add(new ScanfConstant(sb.toString()));
- sb.setLength(0);
- }
- /**
- * scans a string
- *
- * @param format the format string
- * @param args the format arguments
- * @return the formatted string
- */
- public static Value sscanfOld(Env env,
- StringValue string,
- StringValue format,
- @Optional @Reference Value[] args) {
- int fmtLen = format.length();
- int strlen = string.length();
- int sIndex = 0;
- int fIndex = 0;
- boolean isAssign = args.length != 0;
- boolean isReturnArray = !isAssign;
- int argIndex = 0;
- if (strlen == 0) {
- return isAssign ? LongValue.MINUS_ONE : NullValue.NULL;
- }
- ArrayValue array = new ArrayValueImpl();
- while (fIndex < fmtLen) {
- char ch = format.charAt(fIndex++);
- if (isWhitespace(ch)) {
- for (;
- (fIndex < fmtLen
- && isWhitespace(ch = format.charAt(fIndex)));
- fIndex++) {
- }
- /*ch = string.charAt(sIndex);
- if (! isWhitespace(ch)) {
- // TODO: return false?
- return sscanfReturn(env, array, args, argIndex, isAssign, true);
- }*/
- for (;
- sIndex < strlen && isWhitespace(string.charAt(sIndex));
- sIndex++) {
- }
- } else if (ch == '%') {
- int maxLen = -1;
- loop:
- while (fIndex < fmtLen) {
- ch = format.charAt(fIndex++);
- if (sIndex >= strlen && ch != 'n') {
- array.append(NullValue.NULL);
- break loop;
- }
- Value obj;
- if (isAssign) {
- if (argIndex < args.length) {
- obj = args[argIndex++];
- } else {
- env.warning(L.l("not enough vars passed in"));
- break loop;
- }
- } else {
- obj = array;
- }
- switch (ch) {
- case '%':
- if (string.charAt(sIndex) != '%') {
- return sscanfReturn(env, array, args, argIndex, isAssign, true);
- } else {
- break loop;
- }
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- if (maxLen < 0) {
- maxLen = 0;
- }
- maxLen = 10 * maxLen + ch - '0';
- break;
- case 's': {
- ScanfSegment seg = new ScanfString(maxLen);
- sIndex = seg.apply(string, strlen, sIndex, obj, isReturnArray);
- break loop;
- }
- case 'c': {
- if (maxLen < 0) {
- maxLen = 1;
- }
- ScanfSegment seg = new ScanfString(maxLen);
- sIndex = seg.apply(string, strlen, sIndex, obj, isReturnArray);
- break loop;
- }
- case 'n': {
- ScanfSegment seg = ScanfStringLength.SEGMENT;
- sIndex = seg.apply(string, strlen, sIndex, obj, isReturnArray);
- break loop;
- }
- case 'd': {
- ScanfSegment seg = new ScanfInteger(maxLen, 10, false);
- sIndex = seg.apply(string, strlen, sIndex, obj, isReturnArray);
- break loop;
- }
- case 'u': {
- ScanfSegment seg = new ScanfInteger(maxLen, 10, true);
- sIndex = seg.apply(string, strlen, sIndex, obj, isReturnArray);
- break loop;
- }
- case 'o': {
- ScanfSegment seg = new ScanfInteger(maxLen, 8, false);
- sIndex = seg.apply(string, strlen, sIndex, obj, isReturnArray);
- break loop;
- }
- case 'x':
- case 'X': {
- ScanfSegment seg = new ScanfHex(maxLen);
- sIndex = seg.apply(string, strlen, sIndex, obj, isReturnArray);
- break loop;
- }
- case 'e':
- case 'f': {
- ScanfSegment seg = new ScanfScientific(maxLen);
- sIndex = seg.apply(string, strlen, sIndex, obj, isReturnArray);
- break loop;
- }
- default:
- log.fine(L.l("'{0}' is a bad sscanf string.", format));
- env.warning(L.l("'{0}' is a bad sscanf string.", format));
- return isAssign ? LongValue.create(argIndex) : array;
- }
- }
- } else if (ch == string.charAt(sIndex)) {
- sIndex++;
- } else {
- return sscanfReturn(env, array, args, argIndex, false, true);
- }
- }
- return sscanfReturn(env, array, args, argIndex, isAssign, false);
- }
- private static Value sscanfReturn(Env env,
- ArrayValue array,
- Value[] args,
- int argIndex,
- boolean isReturnArray,
- boolean isWarn) {
- if (isReturnArray) {
- return array;
- } else {
- if (isWarn && argIndex != args.length) {
- env.warning(
- L.l("{0} vars passed in but saw only {1} '%' args",
- args.length, argIndex));
- }
- return LongValue.create(argIndex);
- }
- }
- /**
- * Scans a string with a given length.
- */
- private static int sscanfString(StringValue string,
- int sIndex,
- int maxLen,
- Value obj,
- boolean isAssignment) {
- int strlen = string.length();
- if (maxLen < 0) {
- maxLen = Integer.MAX_VALUE;
- }
- StringValue sb = new StringValue();
- for (; sIndex < strlen && maxLen-- > 0; sIndex++) {
- char ch = string.charAt(sIndex);
- if (!isWhitespace(ch)) {
- sb.append(ch);
- } else {
- break;
- }
- }
- sscanfPut(obj, sb, isAssignment);
- return sIndex;
- }
- private static void sscanfPut(Value obj, Value val, boolean isAssignment) {
- if (isAssignment) {
- obj.set(val);
- } else {
- obj.put(val);
- }
- }
- /**
- * Scans a integer with a given length.
- */
- private static int sscanfInteger(StringValue string,
- int sIndex,
- int maxLen,
- Value obj,
- boolean isAssign,
- int base,
- boolean isUnsigned) {
- int strlen = string.length();
- if (maxLen < 0) {
- maxLen = Integer.MAX_VALUE;
- }
- int val = 0;
- int sign = 1;
- boolean isNotMatched = true;
- if (sIndex < strlen) {
- char ch = string.charAt(sIndex);
- if (ch == '+') {
- sIndex++;
- maxLen--;
- } else if (ch == '-') {
- sign = -1;
- sIndex++;
- maxLen--;
- }
- }
- int topRange = base + '0';
- for (; sIndex < strlen && maxLen-- > 0; sIndex++) {
- char ch = string.charAt(sIndex);
- if ('0' <= ch && ch < topRange) {
- val = val * base + ch - '0';
- isNotMatched = false;
- } else if (isNotMatched) {
- sscanfPut(obj, NullValue.NULL, isAssign);
- return sIndex;
- } else {
- break;
- }
- }
- if (isUnsigned) {
- if (sign == -1 && val != 0) {
- sscanfPut(obj, StringValue.create(0xFFFFFFFFL - val + 1), isAssign);
- } else {
- sscanfPut(obj, LongValue.create(val), isAssign);
- }
- } else {
- sscanfPut(obj, LongValue.create(val * sign), isAssign);
- }
- return sIndex;
- }
- /**
- * Scans a integer with a given length.
- */
- private static int sscanfHex(StringValue string,
- int sIndex,
- int maxLen,
- Value obj,
- boolean isAssign) {
- int strlen = string.length();
- if (maxLen < 0) {
- maxLen = Integer.MAX_VALUE;
- }
- int val = 0;
- int sign = 1;
- boolean isMatched = false;
- if (sIndex < strlen) {
- char ch = string.charAt(sIndex);
- if (ch == '+') {
- sIndex++;
- maxLen--;
- } else if (ch == '-') {
- sign = -1;
- sIndex++;
- maxLen--;
- }
- }
- for (; sIndex < strlen && maxLen-- > 0; sIndex++) {
- char ch = string.charAt(sIndex);
- if ('0' <= ch && ch <= '9') {
- val = val * 16 + ch - '0';
- isMatched = true;
- } else if ('a' <= ch && ch <= 'f') {
- val = val * 16 + ch - 'a' + 10;
- isMatched = true;
- } else if ('A' <= ch && ch <= 'F') {
- val = val * 16 + ch - 'A' + 10;
- isMatched = true;
- } else if (!isMatched) {
- sscanfPut(obj, NullValue.NULL, isAssign);
- return sIndex;
- } else {
- break;
- }
- }
- sscanfPut(obj, LongValue.create(val * sign), isAssign);
- return sIndex;
- }
- /**
- * Scans a integer with a given length.
- */
- private static int sscanfScientific(StringValue s,
- int i,
- int maxLen,
- Value obj,
- boolean isAssign) {
- if (maxLen < 0) {
- maxLen = Integer.MAX_VALUE;
- }
- int start = i;
- int len = s.length();
- int ch = 0;
- if (i < len && maxLen > 0 && ((ch = s.charAt(i)) == '+' || ch == '-')) {
- i++;
- maxLen--;
- }
- for (; i < len && maxLen > 0
- && '0' <= (ch = s.charAt(i)) && ch <= '9'; i++) {
- maxLen--;
- }
- if (ch == '.') {
- maxLen--;
- for (i++; i < len && maxLen > 0
- && '0' <= (ch = s.charAt(i)) && ch <= '9'; i++) {
- maxLen--;
- }
- }
- if (ch == 'e' || ch == 'E') {
- maxLen--;
- int e = i++;
- if (start == e) {
- sscanfPut(obj, NullValue.NULL, isAssign);
- return start;
- }
- if (i < len && maxLen > 0 && (ch = s.charAt(i)) == '+' || ch == '-') {
- i++;
- maxLen--;
- }
- for (; i < len && maxLen > 0
- && '0' <= (ch = s.charAt(i)) && ch <= '9'; i++) {
- maxLen--;
- }
- if (i == e + 1) {
- i = e;
- }
- }
- double val;
- if (i == 0) {
- val = 0;
- } else {
- val = Double.parseDouble(s.substring(start, i).toString());
- }
- sscanfPut(obj, DoubleValue.create(val), isAssign);
- return i;
- }
- // TODO: str_getcsv
- /**
- * replaces substrings.
- *
- * @param search search string
- * @param replace replacement string
- * @param subject replacement
- * @param count return value
- */
- public static Value str_ireplace(Env env,
- Value search,
- Value replace,
- Value subject,
- @Reference @Optional Value count) {
- return strReplace(env, search, replace, subject, count, true);
- }
- /**
- * Pads strings
- *
- * @param string string
- * @param length length
- * @param pad padding string
- * @param type padding type
- */
- public static StringValue str_pad(StringValue string,
- int length,
- @Optional("' '") String pad,
- @Optional("STR_PAD_RIGHT") int type) {
- int strLen = string.length();
- int padLen = length - strLen;
- if (padLen <= 0) {
- return string;
- }
- if (pad == null || pad.length() == 0) {
- pad = " ";
- }
- int leftPad = 0;
- int rightPad = 0;
- switch (type) {
- case STR_PAD_LEFT:
- leftPad = padLen;
- break;
- case STR_PAD_RIGHT:
- default:
- rightPad = padLen;
- break;
- case STR_PAD_BOTH:
- leftPad = padLen / 2;
- rightPad = padLen - leftPad;
- break;
- }
- int padStringLen = pad.length();
- StringValue sb = new StringValue();
- for (int i = 0; i < leftPad; i++) {
- sb.append(pad.charAt(i % padStringLen));
- }
- sb = sb.append(string);
- for (int i = 0; i < rightPad; i++) {
- sb.append(pad.charAt(i % padStringLen));
- }
- return sb;
- }
- /**
- * repeats a string
- *
- * @param string string to repeat
- * @param count number of times to repeat
- */
- public static Value str_repeat(StringValue string, int count) {
- StringValue sb = new StringValue();
- for (int i = 0; i < count; i++) {
- sb = sb.append(string);
- }
- return sb;
- }
- /**
- * replaces substrings.
- *
- * @param search search string
- * @param replace replacement string
- * @param subject replacement
- * @param count return value
- */
- public static Value str_replace(Env env,
- Value search,
- Value replace,
- Value subject,
- @Reference @Optional Value count) {
- return strReplace(env, search, replace, subject, count, false);
- }
- /**
- * replaces substrings.
- *
- * @param search search string
- * @param replace replacement string
- * @param subject replacement
- * @param count return value
- */
- private static Value strReplace(Env env,
- Value search,
- Value replace,
- Value subject,
- @Reference @Optional Value count,
- boolean isInsensitive) {
- count.set(LongValue.ZERO);
- if (subject.isNull()) {
- return StringValue.EMPTY;
- }
- if (search.isNull()) {
- return subject;
- }
- if (subject instanceof ArrayValue) {
- ArrayValue subjectArray = (ArrayValue) subject;
- ArrayValue resultArray = new ArrayValueImpl();
- for (Map.Entry<Value, Value> entry : subjectArray.entrySet()) {
- if (entry.getValue() instanceof ArrayValue) {
- resultArray.append(entry.getKey(), entry.getValue());
- } else {
- Value result = strReplaceImpl(env,
- search,
- replace,
- entry.getValue().toStringValue(),
- count,
- isInsensitive);
- resultArray.append(entry.getKey(), result);
- }
- }
- return resultArray;
- } else {
- StringValue subjectString = subject.toStringValue();
- if (subjectString.length() == 0) {
- return StringValue.EMPTY;
- }
- return strReplaceImpl(env,
- search,
- replace,
- subjectString,
- count,
- isInsensitive);
- }
- }
- /**
- * replaces substrings.
- *
- * @param search search string
- * @param replace replacement string
- * @param subject replacement
- * @param count return value
- */
- private static Value strReplaceImpl(Env env,
- Value search,
- Value replace,
- StringValue subject,
- Value count,
- boolean isInsensitive) {
- if (!search.isArray()) {
- StringValue searchString = search.toStringValue();
- if (searchString.length() == 0) {
- return subject;
- }
- if (replace instanceof ArrayValue) {
- env.warning(L.l("Array to string conversion"));
- }
- subject = strReplaceImpl(env,
- searchString,
- replace.toStringValue(),
- subject,
- count,
- isInsensitive);
- } else if (replace instanceof ArrayValue) {
- ArrayValue searchArray = (ArrayValue) search;
- ArrayValue replaceArray = (ArrayValue) replace;
- Iterator<Value> searchIter = searchArray.values().iterator();
- Iterator<Value> replaceIter = replaceArray.values().iterator();
- while (searchIter.hasNext()) {
- Value searchItem = searchIter.next();
- Value replaceItem = replaceIter.next();
- if (replaceItem == null) {
- replaceItem = NullValue.NULL;
- }
- subject = strReplaceImpl(env,
- searchItem.toStringValue(),
- replaceItem.toStringValue(),
- subject,
- count,
- isInsensitive);
- }
- } else {
- ArrayValue searchArray = (ArrayValue) search;
- Iterator<Value> searchIter = searchArray.values().iterator();
- while (searchIter.hasNext()) {
- Value searchItem = searchIter.next();
- subject = strReplaceImpl(env,
- searchItem.toStringValue(),
- replace.toStringValue(),
- subject,
- count,
- isInsensitive);
- }
- }
- return subject;
- }
- /**
- * replaces substrings.
- *
- * @param search search string
- * @param replace replacement string
- * @param subject replacement
- * @param countV return value
- */
- private static StringValue strReplaceImpl(Env env,
- StringValue search,
- StringValue replace,
- StringValue subject,
- Value countV,
- boolean isInsensitive) {
- long count = countV.toLong();
- int head = 0;
- int next;
- int searchLen = search.length();
- StringValue result = null;
- while (head <= (next = indexOf(subject, search, head, isInsensitive))) {
- if (result == null) {
- result = new StringValue();
- }
- result = result.append(subject, head, next);
- result = result.append(replace);
- if (head < next + searchLen) {
- head = next + searchLen;
- } else {
- head += 1;
- }
- count++;
- }
- if (count != 0 && result != null) {
- countV.set(LongValue.create(count));
- int subjectLength = subject.length();
- if (head > 0 && head < subjectLength) {
- result = result.append(subject, head, subjectLength);
- }
- return result;
- } else {
- return subject;
- }
- }
- /**
- * Returns the next index.
- */
- private static int indexOf(StringValue subject,
- StringValue match,
- int head,
- boolean isInsensitive) {
- if (!isInsensitive) {
- return subject.indexOf(match, head);
- } else {
- int length = subject.length();
- int matchLen = match.length();
- if (matchLen <= 0) {
- return -1;
- }
- char ch = Character.toLowerCase(match.charAt(0));
- loop:
- for (; head + matchLen <= length; head++) {
- if (ch == Character.toLowerCase(subject.charAt(head))) {
- for (int i = 1; i < matchLen; i++) {
- if (Character.toLowerCase(subject.charAt(head + i))
- != Character.toLowerCase(match.charAt(i))) {
- continue loop;
- }
- }
- return head;
- }
- }
- return -1;
- }
- }
- /**
- * rot13 conversion
- *
- * @param string string to convert
- */
- public static Value str_rot13(StringValue string) {
- if (string == null) {
- return NullValue.NULL;
- }
- StringValue sb = new StringValue();
- int len = string.length();
- for (int i = 0; i < len; i++) {
- char ch = string.charAt(i);
- if ('a' <= ch && ch <= 'z') {
- int off = ch - 'a';
- sb.append((char) ('a' + (off + 13) % 26));
- } else if ('A' <= ch && ch <= 'Z') {
- int off = ch - 'A';
- sb.append((char) ('A' + (off + 13) % 26));
- } else {
- sb.append(ch);
- }
- }
- return sb;
- }
- /**
- * shuffles a string
- */
- public static String str_shuffle(String string) {
- if (string == null) {
- string = "";
- }
- char[] chars = string.toCharArray();
- int length = chars.length;
- for (int i = 0; i < length; i++) {
- int rand = RandomUtil.nextInt(length);
- char temp = chars[rand];
- chars[rand] = chars[i];
- chars[i] = temp;
- }
- return new String(chars);
- }
- /**
- * split into an array
- *
- * @param string string to split
- * @param chunk chunk size
- */
- public static Value str_split(StringValue string,
- @Optional("1") int chunk) {
- ArrayValue array = new ArrayValueImpl();
- if (string.length() == 0) {
- array.put(string);
- return array;
- }
- int strLen = string.length();
- for (int i = 0; i < strLen; i += chunk) {
- Value value;
- if (i + chunk <= strLen) {
- value = string.substring(i, i + chunk);
- } else {
- value = string.substring(i);
- }
- array.put(LongValue.create(i), value);
- }
- return array;
- }
- public static Value str_word_count(StringValue string,
- @Optional int format,
- @Optional String additionalWordCharacters) {
- if (format < 0 || format > 2) {
- return NullValue.NULL;
- }
- int strlen = string.length();
- boolean isAdditionalWordCharacters = false;
- if (additionalWordCharacters != null) {
- isAdditionalWordCharacters = additionalWordCharacters.length() > 0;
- }
- ArrayValueImpl resultArray = null;
- if (format > 0) {
- resultArray = new ArrayValueImpl();
- }
- boolean isBetweenWords = true;
- int wordCount = 0;
- int lastWordStart = 0;
- for (int i = 0; i <= strlen; i++) {
- boolean isWordCharacter;
- if (i < strlen) {
- int ch = string.charAt(i);
- isWordCharacter = Character.isLetter(ch)
- || ch == '-'
- || ch == '\''
- || (isAdditionalWordCharacters
- && additionalWordCharacters.indexOf(ch) > -1);
- } else {
- isWordCharacter = false;
- }
- if (isWordCharacter) {
- if (isBetweenWords) {
- // starting a word
- isBetweenWords = false;
- lastWordStart = i;
- wordCount++;
- }
- } else {
- if (!isBetweenWords) {
- // finished a word
- isBetweenWords = true;
- if (format > 0) {
- StringValue word = string.substring(lastWordStart, i);
- if (format == 1) {
- resultArray.append(word);
- } else if (format == 2) {
- resultArray.put(LongValue.create(lastWordStart), word);
- }
- }
- }
- }
- }
- if (resultArray == null) {
- return LongValue.create(wordCount);
- } else {
- return resultArray;
- }
- }
- /**
- * Case-insensitive comparison
- *
- * @param a left value
- * @param b right value
- * @return -1, 0, or 1
- */
- public static int strcasecmp(StringValue a, StringValue b) {
- int aLen = a.length();
- int bLen = b.length();
- for (int i = 0; i < aLen && i < bLen; i++) {
- char chA = a.charAt(i);
- char chB = b.charAt(i);
- if (chA == chB) {
- continue;
- }
- if (Character.isUpperCase(chA)) {
- chA = Character.toLowerCase(chA);
- }
- if (Character.isUpperCase(chB)) {
- chB = Character.toLowerCase(chB);
- }
- if (chA == chB) {
- continue;
- } else if (chA < chB) {
- return -1;
- } else {
- return 1;
- }
- }
- if (aLen == bLen) {
- return 0;
- } else if (aLen < bLen) {
- return -1;
- } else {
- return 1;
- }
- }
- /**
- * Finds the index of a substring
- *
- * @param env the calling environment
- */
- public static Value strchr(Env env, StringValue haystack, Value needle) {
- return strstr(env, haystack, needle);
- }
- /**
- * Case-sensitive comparison
- *
- * @param a left value
- * @param b right value
- * @return -1, 0, or 1
- */
- public static int strcmp(StringValue a, StringValue b) {
- int aLen = a.length();
- int bLen = b.length();
- for (int i = 0; i < aLen && i < bLen; i++) {
- char chA = a.charAt(i);
- char chB = b.charAt(i);
- if (chA == chB) {
- continue;
- }
- if (chA == chB) {
- continue;
- } else if (chA < chB) {
- return -1;
- } else {
- return 1;
- }
- }
- if (aLen == bLen) {
- return 0;
- } else if (aLen < bLen) {
- return -1;
- } else {
- return 1;
- }
- }
- /**
- * Locale-based comparison
- * XXX: i18n
- *
- * @param a left value
- * @param b right value
- * @return -1, 0, or 1
- */
- public static Value strcoll(String a, String b) {
- if (a == null) {
- a = "";
- }
- if (b == null) {
- b = "";
- }
- int cmp = a.compareTo(b);
- if (cmp == 0) {
- return LongValue.ZERO;
- } else if (cmp < 0) {
- return LongValue.MINUS_ONE;
- } else {
- return LongValue.ONE;
- }
- }
- /**
- * Finds the number of initial characters in <i>string</i> that do not match
- * one of the characters in <i>characters</i>
- *
- * @param string the string to search in
- * @param characters the character set
- * @param offset the starting offset
- * @param length the length
- * @return the length of the match or FALSE if
- * the offset or length are invalid
- */
- public static Value strcspn(StringValue string,
- StringValue characters,
- @Optional("0") int offset,
- @Optional("-2147483648") int length) {
- if (characters.length() == 0) {
- characters = StringValue.create((char) 0);
- }
- return strspnImpl(string, characters, offset, length, false);
- }
- /**
- * Removes tags from a string.
- *
- * @param string the string to remove
- * @param allowTags the allowable tags
- */
- public static StringValue strip_tags(StringValue string,
- @Optional Value allowTags) {
- StringValue result = new StringValue();
- HashSet<StringValue> allowedTagMap = null;
- if (!allowTags.isDefault()) {
- allowedTagMap = getAllowedTags(allowTags.toStringValue());
- }
- int len = string.length();
- for (int i = 0; i < len; i++) {
- char ch = string.charAt(i);
- if (i + 1 >= len || ch != '<') {
- result.append(ch);
- continue;
- }
- ch = string.charAt(i + 1);
- if (Character.isWhitespace(ch)) {
- i++;
- result.append('<');
- result.append(ch);
- continue;
- }
- int tagNameStart = i + 1;
- if (ch == '/') {
- tagNameStart++;
- }
- int j = tagNameStart;
- while (j < len
- && (ch = string.charAt(j)) != '>'
- // && ch != '/'
- && !Character.isWhitespace(ch)) {
- j++;
- }
- StringValue tagName = string.substring(tagNameStart, j);
- int tagEnd = 0;
- if (allowedTagMap != null && allowedTagMap.contains(tagName)) {
- result.append(string, i, Math.min(j + 1, len));
- } else {
- while (j < len && (ch = string.charAt(j)) != '<') {
- if (ch == '>') {
- tagEnd = j;
- }
- j++;
- }
- }
- i = (tagEnd != 0) ? tagEnd : j;
- }
- return result;
- }
- private static HashSet<StringValue> getAllowedTags(StringValue str) {
- int len = str.length();
- HashSet<StringValue> set = new HashSet<StringValue>();
- for (int i = 0; i < len; i++) {
- char ch = str.charAt(i);
- switch (ch) {
- case '<':
- int j = i + 1;
- while (j < len
- && (ch = str.charAt(j)) != '>'
- //&& ch != '/'
- && !Character.isWhitespace(ch)) {
- j++;
- }
- if (ch == '>'
- && i + 1 < j
- && j < len) {
- set.add(str.substring(i + 1, j));
- }
- i = j;
- default:
- continue;
- }
- }
- return set;
- }
- /**
- * Strip out the backslashes, recognizing the escape sequences, octal,
- * and hexadecimal representations.
- *
- * @param source the string to clean
- * @see #addcslashes
- */
- public static String stripcslashes(String source) {
- if (source == null) {
- source = "";
- }
- StringBuilder result = new StringBuilder(source.length());
- int length = source.length();
- for (int i = 0; i < length; i++) {
- int ch = source.charAt(i);
- if (ch == '\\') {
- i++;
- if (i == length) {
- ch = '\\';
- } else {
- ch = source.charAt(i);
- switch (ch) {
- case 'a':
- ch = 0x07;
- break;
- case 'b':
- ch = '\b';
- break;
- case 't':
- ch = '\t';
- break;
- case 'n':
- ch = '\n';
- break;
- case 'v':
- ch = 0xb;
- break;
- case 'f':
- ch = '\f';
- break;
- case 'r':
- ch = '\r';
- break;
- case 'x':
- // up to two digits for a hex number
- if (i + 1 == length) {
- break;
- }
- int digitValue = hexToDigit(source.charAt(i + 1));
- if (digitValue < 0) {
- break;
- }
- ch = digitValue;
- i++;
- if (i + 1 == length) {
- break;
- }
- digitValue = hexToDigit(source.charAt(i + 1));
- if (digitValue < 0) {
- break;
- }
- ch = ((ch << 4) | digitValue);
- i++;
- break;
- default:
- // up to three digits from 0 to 7 for an octal number
- digitValue = octToDigit((char) ch);
- if (digitValue < 0) {
- break;
- }
- ch = digitValue;
- if (i + 1 == length) {
- break;
- }
- digitValue = octToDigit(source.charAt(i + 1));
- if (digitValue < 0) {
- break;
- }
- ch = ((ch << 3) | digitValue);
- i++;
- if (i + 1 == length) {
- break;
- }
- digitValue = octToDigit(source.charAt(i + 1));
- if (digitValue < 0) {
- break;
- }
- ch = ((ch << 3) | digitValue);
- i++;
- }
- }
- } // if ch == '/'
- result.append((char) ch);
- }
- return result.toString();
- }
- /**
- * Returns the position of a substring, testing case insensitive.
- *
- * @param haystack the full argument to check
- * @param needleV the substring argument to check
- * @param offsetV optional starting position
- */
- public static Value stripos(Env env, StringValue haystack,
- Value needleV,
- @Optional int offset) {
- StringValue needle;
- int len = haystack.length();
- if (len < offset) {
- env.warning(L.l("offset cannot exceed string length"));
- return BooleanValue.FALSE;
- }
- if (needleV instanceof StringValue) {
- needle = (StringValue) needleV;
- } else {
- needle = StringValue.create((char) needleV.toInt());
- }
- haystack = haystack.toLowerCase();
- needle = needle.toLowerCase();
- int pos = haystack.indexOf(needle, offset);
- if (pos < 0) {
- return BooleanValue.FALSE;
- } else {
- return LongValue.create(pos);
- }
- }
- /**
- * Strips out the backslashes.
- *
- * @param string the string to clean
- */
- public static StringValue stripslashes(StringValue string) {
- StringValue sb = new StringValue();
- int len = string.length();
- for (int i = 0; i < len; i++) {
- char ch = string.charAt(i);
- if (ch == '\\') {
- if (i + 1 < len) {
- char ch2 = string.charAt(i + 1);
- if (ch2 == '0') {
- ch2 = 0x0;
- }
- sb.append(ch2);
- i++;
- }
- } else {
- sb.append(ch);
- }
- }
- return sb;
- }
- /**
- * Finds the first instance of a substring, testing case insensitively
- *
- * @param haystack the string to search in
- * @param needleV the string to search for
- * @return the trailing match or FALSE
- */
- public static Value stristr(StringValue haystack,
- Value needleV) {
- CharSequence needleLower;
- if (needleV instanceof StringValue) {
- needleLower = ((StringValue) needleV).toLowerCase();
- } else {
- char lower = Character.toLowerCase((char) needleV.toLong());
- needleLower = String.valueOf(lower);
- }
- StringValue haystackLower = haystack.toLowerCase();
- int i = haystackLower.indexOf(needleLower);
- if (i >= 0) {
- return haystack.substring(i);
- } else {
- return BooleanValue.FALSE;
- }
- }
- /**
- * Returns the length of a string.
- *
- * @param value the argument value
- */
- public static Value strlen(Value value) {
- return LongValue.create(value.length());
- }
- /**
- * Case-insensitive comparison
- *
- * @param a left value
- * @param b right value
- * @return -1, 0, or 1
- */
- public static int strnatcasecmp(StringValue a, StringValue b) {
- return naturalOrderCompare(a, b, true);
- }
- /**
- * Case-sensitive comparison
- *
- * @param a left value
- * @param b right value
- * @return -1, 0, or 1
- */
- public static int strnatcmp(StringValue a, StringValue b) {
- return naturalOrderCompare(a, b, false);
- }
- /**
- * http://sourcefrog.net/projects/natsort/
- */
- private static int naturalOrderCompare(StringValue a,
- StringValue b,
- boolean ignoreCase) {
- SimpleStringReader aIn = new SimpleStringReader(a);
- SimpleStringReader bIn = new SimpleStringReader(b);
- int aChar = aIn.read();
- int bChar = bIn.read();
- if (aChar == -1 && bChar >= 0) {
- return -1;
- } else if (aChar >= 0 && bChar == -1) {
- return 1;
- }
- while (true) {
- while (Character.isWhitespace(aChar)) {
- aChar = aIn.read();
- }
- while (Character.isWhitespace(bChar)) {
- bChar = bIn.read();
- }
- if (aChar == -1 && bChar == -1) {
- return 0;
- }
- // leading zeros
- // '01' < '2'
- // '0a' > 'a'
- if (aChar == '0' && bChar == '0') {
- while (true) {
- aChar = aIn.read();
- bChar = bIn.read();
- if (aChar == '0' && bChar == '0') {
- continue;
- } else if (aChar == '0') {
- if ('1' <= bChar && bChar <= '9') {
- return -1;
- } else {
- return 1;
- }
- } else if (bChar == 0) {
- if ('1' <= aChar && aChar <= '9') {
- return 1;
- } else {
- return -1;
- }
- } else {
- break;
- }
- }
- } else if ('0' < aChar && aChar <= '9'
- && '0' < bChar && bChar <= '9') {
- int aInteger = aIn.readInt(aChar);
- int bInteger = bIn.readInt(bChar);
- if (aInteger > bInteger) {
- return 1;
- } else if (aInteger < bInteger) {
- return -1;
- } else {
- aChar = aIn.read();
- bChar = bIn.read();
- }
- }
- if (ignoreCase) {
- aChar = Character.toUpperCase(aChar);
- bChar = Character.toUpperCase(bChar);
- }
- if (aChar > bChar) {
- return 1;
- } else if (aChar < bChar) {
- return -1;
- }
- aChar = aIn.read();
- bChar = bIn.read();
- // trailing spaces
- // "abc " > "abc"
- if (aChar >= 0 && bChar == -1) {
- return 1;
- } else if (aChar == -1 && bChar >= 0) {
- return -1;
- }
- }
- }
- /**
- * Case-insensitive comparison
- *
- * @param a left value
- * @param b right value
- * @return -1, 0, or 1
- */
- public static Value strncasecmp(
- Env env, StringValue a, StringValue b, int length) {
- if (length < 0) {
- env.warning(L.l("strncasecmp() length '{0}' must be non-negative",
- length));
- return BooleanValue.FALSE;
- }
- int aLen = a.length();
- int bLen = b.length();
- for (int i = 0; i < length; i++) {
- if (aLen <= i) {
- return LongValue.MINUS_ONE;
- } else if (bLen <= i) {
- return LongValue.ONE;
- }
- char aChar = Character.toUpperCase(a.charAt(i));
- char bChar = Character.toUpperCase(b.charAt(i));
- if (aChar < bChar) {
- return LongValue.MINUS_ONE;
- } else if (bChar < aChar) {
- return LongValue.ONE;
- }
- }
- return LongValue.ZERO;
- }
- /**
- * Case-sensitive comparison
- *
- * @param a left value
- * @param b right value
- * @return -1, 0, or 1
- */
- public static Value strncmp(Env env, StringValue a, StringValue b, int length) {
- if (length < 0) {
- env.warning(L.l("strncmp() length '{0}' must be non-negative",
- length));
- return BooleanValue.FALSE;
- }
- if (length < a.length()) {
- a = a.substring(0, length);
- }
- if (length < b.length()) {
- b = b.substring(0, length);
- }
- return LongValue.create(strcmp(a, b));
- }
- /**
- * Returns a substring of <i>haystack</i> starting from the earliest
- * occurence of any char in <i>charList</i>
- *
- * @param haystack the string to search in
- * @param charList list of chars that would trigger match
- * @return substring, else FALSE
- */
- public static Value strpbrk(StringValue haystack,
- StringValue charList) {
- int len = haystack.length();
- int sublen = charList.length();
- for (int i = 0; i < len; i++) {
- for (int j = 0; j < sublen; j++) {
- if (haystack.charAt(i) == charList.charAt(j)) {
- return haystack.substring(i);
- }
- }
- }
- return BooleanValue.FALSE;
- }
- /**
- * Returns the position of a substring.
- *
- * @param haystack the string to search in
- * @param needleV the string to search for
- */
- public static Value strpos(Env env,
- StringValue haystack,
- Value needleV,
- @Optional int offset) {
- StringValue needle;
- if (offset > haystack.length()) {
- env.warning(L.l("offset cannot exceed string length"));
- return BooleanValue.FALSE;
- }
- if (needleV.isString()) {
- needle = needleV.toStringValue();
- } else {
- needle = StringValue.create((char) needleV.toInt());
- }
- int pos = haystack.indexOf(needle, offset);
- if (pos < 0) {
- return BooleanValue.FALSE;
- } else {
- return LongValue.create(pos);
- }
- }
- /**
- * Finds the last instance of a substring
- *
- * @param haystack the string to search in
- * @param needleV the string to search for
- * @return the trailing match or FALSE
- */
- public static Value strrchr(StringValue haystack,
- Value needleV) {
- CharSequence needle;
- if (needleV instanceof StringValue) {
- needle = (StringValue) needleV;
- } else {
- needle = String.valueOf((char) needleV.toLong());
- }
- int i = haystack.lastIndexOf(needle);
- if (i > 0) {
- return haystack.substring(i);
- } else {
- return BooleanValue.FALSE;
- }
- }
- /**
- * Reverses a string.
- */
- public static Value strrev(StringValue string) {
- StringValue sb = new StringValue();
- for (int i = string.length() - 1; i >= 0; i--) {
- sb.append(string.charAt(i));
- }
- return sb;
- }
- /**
- * Returns the position of a substring, testing case-insensitive.
- *
- * @param haystack the full string to test
- * @param needleV the substring string to test
- * @param offsetV the optional offset to start searching
- */
- public static Value strripos(Env env,
- String haystack,
- Value needleV,
- @Optional Value offsetV) {
- if (haystack == null) {
- haystack = "";
- }
- String needle;
- if (needleV instanceof StringValue) {
- needle = needleV.toString();
- } else {
- needle = String.valueOf((char) needleV.toInt());
- }
- int offset;
- if (offsetV instanceof DefaultValue) {
- offset = haystack.length();
- } else {
- offset = offsetV.toInt();
- if (haystack.length() < offset) {
- env.warning(L.l("offset cannot exceed string length"));
- return BooleanValue.FALSE;
- }
- }
- haystack = haystack.toLowerCase();
- needle = needle.toLowerCase();
- int pos = haystack.lastIndexOf(needle, offset);
- if (pos < 0) {
- return BooleanValue.FALSE;
- } else {
- return LongValue.create(pos);
- }
- }
- /**
- * Returns the position of a substring.
- *
- * @param haystack the string to search in
- * @param needleV the string to search for
- */
- public static Value strrpos(Env env,
- StringValue haystack,
- Value needleV,
- @Optional Value offsetV) {
- StringValue needle;
- if (needleV instanceof StringValue) {
- needle = needleV.toStringValue();
- } else {
- needle = StringValue.create((char) needleV.toInt());
- }
- int offset = haystack.length() - offsetV.toInt();
- if (offset < 0) {
- env.warning(L.l("offset cannot exceed string length"));
- return BooleanValue.FALSE;
- }
- int pos = haystack.lastIndexOf(needle, offset);
- if (pos < 0) {
- return BooleanValue.FALSE;
- } else {
- return LongValue.create(pos);
- }
- }
- /**
- * Finds the number of initial characters in <i>string</i> that match one of
- * the characters in <i>characters</i>
- *
- * @param string the string to search in
- * @param characters the character set
- * @param offset the starting offset
- * @param length the length
- * @return the length of the match or FALSE
- * if the offset or length are invalid
- */
- public static Value strspn(StringValue string,
- StringValue characters,
- @Optional int offset,
- @Optional("-2147483648") int length) {
- return strspnImpl(string, characters, offset, length, true);
- }
- private static Value strspnImpl(StringValue string,
- StringValue characters,
- int offset,
- int length,
- boolean isMatch) {
- int strlen = string.length();
- // see also strcspn which uses the same procedure for determining
- // effective offset and length
- if (offset < 0) {
- offset += strlen;
- if (offset < 0) {
- offset = 0;
- }
- }
- if (offset > strlen) {
- return BooleanValue.FALSE;
- }
- if (length == -2147483648) {
- length = strlen;
- } else if (length < 0) {
- length += (strlen - offset);
- if (length < 0) {
- length = 0;
- }
- }
- int end = offset + length;
- if (strlen < end) {
- end = strlen;
- }
- int count = 0;
- for (; offset < end; offset++) {
- char ch = string.charAt(offset);
- boolean isPresent = characters.indexOf(ch) > -1;
- if (isPresent == isMatch) {
- count++;
- } else {
- return LongValue.create(count);
- }
- }
- return LongValue.create(count);
- }
- /**
- * Finds the first instance of a needle in haystack and returns
- * the portion of haystack from the beginning of
- * needle to the end of haystack.
- *
- * @param env the calling environment
- * @param haystackV the string to search in
- * @param needleV the string to search for, or the
- * original value of a character
- * @return the trailing match or FALSE if needle is not found
- */
- public static Value strstr(Env env,
- StringValue haystackV,
- Value needleV) {
- if (haystackV == null) {
- haystackV = StringValue.EMPTY;
- }
- String needle;
- if (needleV instanceof StringValue) {
- needle = needleV.toString();
- } else {
- needle = String.valueOf((char) needleV.toLong());
- }
- if (needle.length() == 0) {
- env.warning("empty needle");
- return BooleanValue.FALSE;
- }
- int i = haystackV.indexOf(needle);
- if (i >= 0) {
- return haystackV.substring(i);
- } else {
- return BooleanValue.FALSE;
- }
- }
- /**
- * Split a string into tokens using any character
- * in another string as a delimiter.
- * <p/>
- * The first call establishes the string to
- * search and the characters to use as tokens,
- * the first token is returned:
- * <pre>
- * strtok("hello, world", ", ")
- * => "hello"
- * </pre>
- * <p/>
- * Subsequent calls pass only the token
- * characters, the next token is returned:
- * <pre>
- * strtok("hello, world", ", ")
- * => "hello"
- * strtok(", ")
- * => "world"
- * </pre>
- * <p/>
- * False is returned if there are no more tokens:
- * <pre>
- * strtok("hello, world", ", ")
- * => "hello"
- * strtok(", ")
- * => "world"
- * strtok(", ")
- * => false
- * </pre>
- * <p/>
- * Calls that pass two arguments reset the search string:
- * <pre>
- * strtok("hello, world", ", ")
- * => "hello"
- * strtok("goodbye, world", ", ")
- * => "goodbye"
- * strtok("world")
- * => false
- * strtok(", ")
- * => false
- * </pre>
- */
- public static Value strtok(Env env,
- StringValue string1,
- @Optional Value string2) {
- StringValue string;
- StringValue characters;
- int offset;
- //StringValue savedToken = null;
- if (string2.isNull()) {
- StringValue savedString = (StringValue) env.getSpecialValue("clevercloud.strtok_string");
- Integer savedOffset = (Integer) env.getSpecialValue("clevercloud.strtok_offset");
- //savedToken = (StringValue) env.getSpecialValue("clevercloud.strtok_token");
- string = savedString == null ? StringValue.EMPTY : savedString;
- offset = savedOffset == null ? 0 : savedOffset;
- //savedToken = savedToken == null ? env.getEmptyString() : savedToken;
- characters = string1;
- } else {
- string = string1;
- offset = 0;
- characters = string2.toStringValue();
- env.setSpecialValue("clevercloud.strtok_string", string);
- //env.setSpecialValue("clevercloud.strtok_token", string2);
- }
- int strlen = string.length();
- // skip any at beginning
- for (; offset < strlen; offset++) {
- char ch = string.charAt(offset);
- if (characters.indexOf(ch) < 0) {
- break;
- }
- }
- Value result;
- if (offset == strlen) {
- result = BooleanValue.FALSE;
- } else {
- //if (string2.isNull() && !(string1.eq(savedToken))) {
- // offset = offset + savedToken.length();
- //}
- int start = offset;
- int end = start;
- // find end
- for (; end < strlen; end++) {
- char ch = string.charAt(end);
- if (characters.indexOf(ch) > -1) {
- break;
- }
- }
- for (offset = end; offset < strlen; offset++) {
- char ch = string.charAt(offset);
- if (characters.indexOf(ch) < 0) {
- break;
- }
- }
- result = string.substring(start, end);
- }
- env.setSpecialValue("clevercloud.strtok_offset", offset);
- return result;
- }
- /**
- * Converts to lower case.
- *
- * @param string the input string
- */
- public static StringValue strtolower(StringValue string) {
- return string.toLowerCase();
- }
- /**
- * Converts to upper case.
- *
- * @param string the input string
- */
- public static StringValue strtoupper(StringValue string) {
- return string.toUpperCase();
- }
- /**
- * Translates characters in a string to target values.
- *
- * @param string the source string
- * @param fromV the from characters
- * @param to the to character map
- */
- public static StringValue strtr(Env env,
- StringValue string,
- Value fromV,
- @Optional StringValue to) {
- if (fromV instanceof ArrayValue) {
- return strtrArray(string, (ArrayValue) fromV);
- }
- StringValue from = fromV.toStringValue();
- int len = from.length();
- if (to.length() < len) {
- len = to.length();
- }
- char[] map = new char[256];
- for (int i = len - 1; i >= 0; i--) {
- map[from.charAt(i)] = to.charAt(i);
- }
- StringValue sb = new StringValue();
- len = string.length();
- for (int i = 0; i < len; i++) {
- char ch = string.charAt(i);
- if (map[ch] != 0) {
- sb.append(map[ch]);
- } else {
- sb.append(ch);
- }
- }
- return sb;
- }
- /**
- * Translates characters in a string to target values.
- *
- * @param string the source string
- * @param map the character map
- */
- private static StringValue strtrArray(StringValue string, ArrayValue map) {
- int size = map.getSize();
- StringValue[] fromList = new StringValue[size];
- StringValue[] toList = new StringValue[size];
- Map.Entry<Value, Value>[] entryArray = new Map.Entry[size];
- int i = 0;
- for (Map.Entry<Value, Value> entry : map.entrySet()) {
- entryArray[i++] = entry;
- }
- // sort entries in descending fashion
- Arrays.sort(entryArray, new StrtrComparator<Map.Entry<Value, Value>>());
- boolean[] charSet = new boolean[256];
- for (i = 0; i < size; i++) {
- fromList[i] = entryArray[i].getKey().toStringValue();
- toList[i] = entryArray[i].getValue().toStringValue();
- charSet[fromList[i].charAt(0)] = true;
- }
- StringValue result = new StringValue();
- int len = string.length();
- int head = 0;
- top:
- while (head < len) {
- char ch = string.charAt(head);
- if (charSet.length <= ch || charSet[ch]) {
- fromLoop:
- for (i = 0; i < fromList.length; i++) {
- StringValue from = fromList[i];
- int fromLen = from.length();
- if (head + fromLen > len) {
- continue;
- }
- if (ch != from.charAt(0)) {
- continue;
- }
- for (int j = 0; j < fromLen; j++) {
- if (string.charAt(head + j) != from.charAt(j)) {
- continue fromLoop;
- }
- }
- result = result.append(toList[i]);
- head = head + fromLen;
- continue top;
- }
- }
- result.append(ch);
- head++;
- }
- return result;
- }
- /*
- * Comparator for sorting in descending fashion based on length.
- */
- static class StrtrComparator<T extends Map.Entry<Value, Value>>
- implements Comparator<T> {
- @Override
- public int compare(T a, T b) {
- int lenA = a.getKey().length();
- int lenB = b.getKey().length();
- if (lenA < lenB) {
- return 1;
- } else if (lenA == lenB) {
- return 0;
- } else {
- return -1;
- }
- }
- }
- /**
- * Returns a substring
- *
- * @param env the calling environment
- * @param string the string
- * @param start the start offset
- * @param lenV the optional length
- */
- public static Value substr(Env env,
- StringValue string,
- int start,
- @Optional Value lenV) {
- int len = lenV.toInt();
- int strLen = string.length();
- if (start < 0) {
- start = strLen + start;
- }
- if (start < 0 || start >= strLen) {
- return BooleanValue.FALSE;
- }
- if (lenV.isDefault()) {
- return string.substring(start);
- } else if (len == 0) {
- return StringValue.EMPTY;
- } else {
- int end;
- if (len < 0) {
- end = strLen + len;
- } else {
- end = (strLen < len) ? strLen : start + len;
- }
- if (end <= start) {
- return BooleanValue.FALSE;
- } else if (strLen <= end) {
- return string.substring(start);
- } else {
- return string.substring(start, end);
- }
- }
- }
- public static Value substr_compare(Env env,
- StringValue mainStr,
- StringValue str,
- int offset,
- @Optional Value lenV,
- @Optional boolean isCaseInsensitive) {
- int strLen = mainStr.length();
- int len = lenV.toInt();
- if (!lenV.isDefault() && len == 0) {
- return BooleanValue.FALSE;
- }
- if (strLen < offset) {
- env.warning(L.l("offset can not be greater than length of string"));
- return BooleanValue.FALSE;
- }
- if (len > strLen
- || len + offset > strLen) {
- return BooleanValue.FALSE;
- }
- mainStr = substr(env, mainStr, offset, lenV).toStringValue();
- str = substr(env, str, 0, lenV).toStringValue();
- if (isCaseInsensitive) {
- return LongValue.create(strcasecmp(mainStr, str));
- } else {
- return LongValue.create(strcmp(mainStr, str));
- }
- }
- public static Value substr_count(Env env,
- StringValue haystackV,
- StringValue needleV,
- @Optional int offset,
- @Optional("-1") int length) {
- String haystack = haystackV.toString();
- String needle = needleV.toString();
- if (needle.length() == 0) {
- env.warning(L.l("empty substr"));
- return BooleanValue.FALSE;
- }
- int haystackLength = haystack.length();
- if (offset < 0 || offset > haystackLength) {
- env.warning(L.l("offset cannot exceed string length", offset));
- return BooleanValue.FALSE;
- }
- if (length >= 0) {
- int newLength = offset + length;
- if (newLength < 0 || newLength > haystackLength) {
- env.warning(L.l("length cannot exceed string length", length));
- return BooleanValue.FALSE;
- }
- haystackLength = newLength;
- }
- int needleLength = needle.length();
- int count = 0;
- int end = haystackLength - needleLength + 1;
- int i = offset;
- while (i < end) {
- if (haystack.startsWith(needle, i)) {
- count++;
- i += needleLength;
- } else {
- i++;
- }
- }
- return LongValue.create(count);
- }
- /**
- * Replaces a substring with a replacement
- *
- * @param subjectV a string to modify, or an array of strings to modify
- * @param replacement the replacement string
- * @param startV the start offset
- * @param lengthV the optional length
- */
- public static Value substr_replace(Value subjectV,
- StringValue replacement,
- Value startV,
- @Optional Value lengthV) {
- int start = 0;
- int length = Integer.MAX_VALUE / 2;
- if (!(lengthV.isNull() || lengthV.isArray())) {
- length = lengthV.toInt();
- }
- if (!(startV.isNull() || startV.isArray())) {
- start = startV.toInt();
- }
- Iterator<Value> startIterator =
- startV.isArray()
- ? ((ArrayValue) startV).values().iterator()
- : null;
- Iterator<Value> lengthIterator =
- lengthV.isArray()
- ? ((ArrayValue) lengthV).values().iterator()
- : null;
- if (subjectV.isArray()) {
- ArrayValue resultArray = new ArrayValueImpl();
- ArrayValue subjectArray = (ArrayValue) subjectV;
- for (Value value : subjectArray.values()) {
- if (lengthIterator != null && lengthIterator.hasNext()) {
- length = lengthIterator.next().toInt();
- }
- if (startIterator != null && startIterator.hasNext()) {
- start = startIterator.next().toInt();
- }
- Value result = substrReplaceImpl(
- value.toStringValue(), replacement, start, length);
- resultArray.append(result);
- }
- return resultArray;
- } else {
- if (lengthIterator != null && lengthIterator.hasNext()) {
- length = lengthIterator.next().toInt();
- }
- if (startIterator != null && startIterator.hasNext()) {
- start = startIterator.next().toInt();
- }
- return substrReplaceImpl(
- subjectV.toStringValue(), replacement, start, length);
- }
- }
- private static Value substrReplaceImpl(StringValue string,
- StringValue replacement,
- int start,
- int len) {
- int strLen = string.length();
- if (start > strLen) {
- start = strLen;
- } else if (start < 0) {
- start = Math.max(strLen + start, 0);
- }
- int end;
- if (len < 0) {
- end = Math.max(strLen + len, start);
- } else {
- end = (strLen < len) ? strLen : (start + len);
- }
- StringValue result = new StringValue();
- result = result.append(string.substring(0, start));
- result = result.append(replacement);
- result = result.append(string.substring(end));
- return result;
- }
- /**
- * Removes leading and trailing whitespace.
- *
- * @param string the string to be trimmed
- * @param characters optional set of characters to trim
- * @return the trimmed string
- */
- public static Value trim(Env env,
- StringValue string,
- @Optional String characters) {
- boolean[] trim;
- if (characters == null || characters.equals("")) {
- trim = TRIM_WHITESPACE;
- } else {
- trim = parseCharsetBitmap(env, characters.toString());
- }
- int len = string.length();
- int head = 0;
- for (; head < len; head++) {
- char ch = string.charAt(head);
- if (ch >= 256 || !trim[ch]) {
- break;
- }
- }
- int tail = len - 1;
- for (; tail >= 0; tail--) {
- char ch = string.charAt(tail);
- if (ch >= 256 || !trim[ch]) {
- break;
- }
- }
- if (tail < head) {
- return StringValue.EMPTY;
- } else {
- return (StringValue) string.subSequence(head, tail + 1);
- }
- }
- /**
- * Uppercases the first character
- *
- * @param string the input string
- */
- public static StringValue ucfirst(Env env, StringValue string) {
- if (string == null) {
- return StringValue.EMPTY;
- } else if (string.length() == 0) {
- return string;
- }
- StringValue sb = new StringValue();
- sb = sb.append(Character.toUpperCase(string.charAt(0)));
- sb = sb.append(string, 1, string.length());
- return sb;
- }
- /**
- * Uppercases the first character of each word
- *
- * @param string the input string
- */
- public static String ucwords(String string) {
- if (string == null) {
- string = "";
- }
- int strLen = string.length();
- boolean isStart = true;
- StringBuilder sb = new StringBuilder();
- for (int i = 0; i < strLen; i++) {
- char ch = string.charAt(i);
- switch (ch) {
- case ' ':
- case '\t':
- case '\r':
- case '\n':
- isStart = true;
- sb.append(ch);
- break;
- default:
- if (isStart) {
- sb.append(Character.toUpperCase(ch));
- } else {
- sb.append(ch);
- }
- isStart = false;
- break;
- }
- }
- return sb.toString();
- }
- /**
- * Formatted strings with array arguments
- *
- * @param format the format string
- * @param array the arguments to apply to the format string
- */
- public static int vprintf(Env env,
- StringValue format,
- @NotNull ArrayValue array) {
- Value[] args;
- if (array != null) {
- args = new Value[array.getSize()];
- int i = 0;
- for (Value value : array.values()) {
- args[i++] = value;
- }
- } else {
- args = new Value[0];
- }
- return printf(env, format, args);
- }
- /**
- * Formatted strings with array arguments
- *
- * @param format the format string
- * @param array the arguments to apply to the format string
- */
- public static Value vsprintf(Env env,
- StringValue format,
- @NotNull ArrayValue array) {
- Value[] args;
- if (array != null) {
- args = new Value[array.getSize()];
- int i = 0;
- for (Value value : array.values()) {
- args[i++] = value;
- }
- } else {
- args = new Value[0];
- }
- return sprintf(env, format, args);
- }
- /**
- * Wraps a string to the given number of characters.
- *
- * @param string the input string
- * @param width the width
- * @param breakString the break string
- * @param cut if true, break on exact match
- */
- public static Value wordwrap(Env env,
- @Expect(type = Expect.Type.STRING) Value value,
- @Optional @Expect(type = Expect.Type.NUMERIC) Value widthV,
- @Optional @Expect(type = Expect.Type.STRING) Value breakV,
- @Optional @Expect(type = Expect.Type.BOOLEAN) Value cutV) {
- if (value instanceof UnexpectedValue) {
- env.warning(L.l("word must be a string, but {0} given",
- value.getType()));
- return NullValue.NULL;
- }
- if (widthV instanceof UnexpectedValue) {
- env.warning(L.l("width must be numeric, but {0} given",
- widthV.getType()));
- return NullValue.NULL;
- }
- int width = 0;
- if (widthV.isDefault()) {
- width = 75;
- } else {
- width = widthV.toInt();
- }
- String string = value.toString();
- if (cutV instanceof UnexpectedValue) {
- env.warning(L.l("cut must be a boolean, but {0} given",
- cutV.getType()));
- return NullValue.NULL;
- }
- boolean isCut = cutV.toBoolean();
- if (isCut && width == 0 && string.length() > 0) {
- env.warning(L.l("cannot cut string to width 0"));
- return BooleanValue.FALSE;
- }
- int len = string != null ? string.length() : 0;
- if (breakV instanceof UnexpectedValue) {
- env.warning(L.l("break string must be a string, but {0} given",
- breakV.getType()));
- return NullValue.NULL;
- }
- String breakString = "\n";
- if (!breakV.isDefault()) {
- breakString = breakV.toString();
- }
- if (breakString == null || breakString.length() == 0) {
- env.warning(L.l("break string cannot be empty"));
- return BooleanValue.FALSE;
- }
- int breakLen = breakString.length();
- int breakChar;
- if (breakLen == 0) {
- breakChar = -1;
- } else {
- breakChar = breakString.charAt(0);
- }
- int head = 0;
- int lastSpace = 0;
- StringValue sb = new StringValue();
- for (int i = 0; i < len; i++) {
- char ch = string.charAt(i);
- if (ch == breakChar && string.regionMatches(
- i, breakString, 0, breakLen)) {
- sb.append(string, head, i + breakLen);
- head = i + breakLen;
- } else if (width <= i - head) {
- if (ch == ' ') {
- sb.append(string, head, i);
- sb.append(breakString);
- head = i + 1;
- } else if (head < lastSpace) {
- sb.append(string, head, lastSpace);
- sb.append(breakString);
- head = lastSpace + 1;
- } else if (isCut) {
- sb.append(string, head, i);
- sb.append(breakString);
- head = i;
- }
- } else if (ch == ' ') {
- lastSpace = i;
- }
- }
- if (head < len) {
- sb.append(string, head, len);
- }
- return sb;
- }
- /**
- * Returns true if the character is a whitespace character.
- */
- protected static boolean isWhitespace(char ch) {
- return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r';
- }
- /**
- * Returns the uppercase equivalent of the caharacter
- */
- protected static char toUpperCase(char ch) {
- if (ch >= 'a' && ch <= 'z') {
- return (char) ('A' + (ch - 'a'));
- } else {
- return ch;
- }
- }
- /**
- * Converts an integer digit to a uuencoded char.
- */
- protected static char toUUChar(int d) {
- if (d == 0) {
- return (char) 0x60;
- } else {
- return (char) (0x20 + (d & 0x3f));
- }
- }
- protected static char toHexChar(int d) {
- d &= 0xf;
- if (d < 10) {
- return (char) (d + '0');
- } else {
- return (char) (d - 10 + 'a');
- }
- }
- protected static char toUpperHexChar(int d) {
- d &= 0xf;
- if (d < 10) {
- return (char) (d + '0');
- } else {
- return (char) (d - 10 + 'A');
- }
- }
- protected static int hexToDigit(char ch) {
- if ('0' <= ch && ch <= '9') {
- return ch - '0';
- } else if ('a' <= ch && ch <= 'f') {
- return ch - 'a' + 10;
- } else if ('A' <= ch && ch <= 'F') {
- return ch - 'A' + 10;
- } else {
- return -1;
- }
- }
- protected static int octToDigit(char ch) {
- if ('0' <= ch && ch <= '7') {
- return ch - '0';
- } else {
- return -1;
- }
- }
- abstract static class PrintfSegment {
- abstract public boolean apply(Env env, StringValue sb, Value[] args);
- static boolean hasIndex(String format) {
- return format.indexOf('$') >= 0;
- }
- static int getIndex(String format) {
- int value = 0;
- for (int i = 0; i < format.length(); i++) {
- char ch;
- if ('0' <= (ch = format.charAt(i)) && ch <= '9') {
- value = 10 * value + ch - '0';
- } else {
- break;
- }
- }
- return value - 1;
- }
- static String getIndexFormat(String format) {
- int p = format.indexOf('$');
- return '%' + format.substring(p + 1);
- }
- }
- static class TextPrintfSegment extends PrintfSegment {
- private final char[] _text;
- TextPrintfSegment(StringBuilder text) {
- _text = new char[text.length()];
- text.getChars(0, _text.length, _text, 0);
- }
- @Override
- public boolean apply(Env env, StringValue sb, Value[] args) {
- sb.append(_text, 0, _text.length);
- return true;
- }
- }
- static class LongPrintfSegment extends PrintfSegment {
- private final String _format;
- private final int _index;
- private final BiancaLocale _locale;
- private LongPrintfSegment(String format, int index, BiancaLocale locale) {
- _format = format;
- _index = index;
- _locale = locale;
- }
- static PrintfSegment create(Env env, String format, int index) {
- if (hasIndex(format)) {
- index = getIndex(format);
- format = getIndexFormat(format);
- } else {
- format = '%' + format;
- //index = index;
- }
- // php/115b
- // strip out illegal precision specifier from phpBB vote function
- if (format.length() > 1 && format.charAt(1) == '.') {
- int i;
- for (i = 2; i < format.length(); i++) {
- char ch = format.charAt(i);
- if (!('0' <= ch && ch <= '9')) {
- break;
- }
- }
- format = '%' + format.substring(i);
- }
- if (format.charAt(format.length() - 1) == 'x'
- || format.charAt(format.length() - 1) == 'X') {
- HexPrintfSegment hex = HexPrintfSegment.create(format, index);
- if (hex != null) {
- return hex;
- }
- }
- if (format.charAt(format.length() - 1) == 'b'
- || format.charAt(format.length() - 1) == 'B') {
- BinaryPrintfSegment bin = BinaryPrintfSegment.create(format, index);
- if (bin != null) {
- return bin;
- }
- }
- if (format.charAt(format.length() - 1) == 'u') {
- UnsignedPrintfSegment unsign = UnsignedPrintfSegment.create(format, index);
- if (unsign != null) {
- return unsign;
- }
- }
- return new LongPrintfSegment(format, index,
- env.getLocaleInfo().getNumeric());
- }
- @Override
- public boolean apply(Env env, StringValue sb, Value[] args) {
- long value;
- if (_index < args.length) {
- value = args[_index].toLong();
- } else {
- env.warning(L.l("printf(): not enough arguments to match format."));
- return false;
- }
- sb.append(String.format(_locale.getLocale(), _format, value));
- return true;
- }
- }
- static class HexPrintfSegment extends PrintfSegment {
- private final int _index;
- private final int _min;
- private final char _pad;
- private boolean _isUpper;
- HexPrintfSegment(int index, int min, int pad, boolean isUpper) {
- _index = index;
- _min = min;
- if (pad >= 0) {
- _pad = (char) pad;
- } else {
- _pad = ' ';
- }
- _isUpper = isUpper;
- }
- static HexPrintfSegment create(String format, int index) {
- int length = format.length();
- int offset = 1;
- boolean isUpper = format.charAt(length - 1) == 'X';
- char pad = ' ';
- if (format.charAt(offset) == ' ') {
- pad = ' ';
- offset++;
- } else if (format.charAt(offset) == '0') {
- pad = '0';
- offset++;
- }
- int min = 0;
- for (; offset < length - 1; offset++) {
- char ch = format.charAt(offset);
- if ('0' <= ch && ch <= '9') {
- min = 10 * min + ch - '0';
- } else {
- return null;
- }
- }
- return new HexPrintfSegment(index, min, pad, isUpper);
- }
- @Override
- public boolean apply(Env env, StringValue sb, Value[] args) {
- long value;
- if (_index >= 0 && _index < args.length) {
- value = args[_index].toLong();
- } else {
- env.warning(L.l("printf(): not enough arguments to match format."));
- return false;
- }
- int digits = 0;
- long shift = value;
- for (int i = 0; i < 16; i++) {
- if (shift != 0) {
- digits = i;
- }
- shift = shift >>> 4;
- }
- for (int i = digits + 1; i < _min; i++) {
- sb.append(_pad);
- }
- for (; digits >= 0; digits--) {
- int digit = (int) (value >>> (4 * digits)) & 0xf;
- if (digit <= 9) {
- sb.append((char) ('0' + digit));
- } else if (_isUpper) {
- sb.append((char) ('A' + digit - 10));
- } else {
- sb.append((char) ('a' + digit - 10));
- }
- }
- return true;
- }
- }
- static class UnsignedPrintfSegment extends PrintfSegment {
- private final int _index;
- private final int _min;
- private final char _pad;
- UnsignedPrintfSegment(int index, int min, int pad) {
- _index = index;
- _min = min;
- if (pad >= 0) {
- _pad = (char) pad;
- } else {
- _pad = ' ';
- }
- }
- static UnsignedPrintfSegment create(String format, int index) {
- int length = format.length();
- int offset = 1;
- if (format.charAt(offset) == '+') {
- offset++;
- }
- char pad = ' ';
- if (format.charAt(offset) == ' ') {
- pad = ' ';
- offset++;
- } else if (format.charAt(offset) == '0') {
- pad = '0';
- offset++;
- }
- int min = 0;
- for (; offset < length - 1; offset++) {
- char ch = format.charAt(offset);
- if ('0' <= ch && ch <= '9') {
- min = 10 * min + ch - '0';
- } else {
- return null;
- }
- }
- return new UnsignedPrintfSegment(index, min, pad);
- }
- @Override
- public boolean apply(Env env, StringValue sb, Value[] args) {
- long value;
- if (_index >= 0 && _index < args.length) {
- value = args[_index].toLong();
- } else {
- env.warning(L.l("printf(): not enough arguments to match format."));
- return false;
- }
- char[] buf = new char[32];
- int digits = buf.length;
- if (value == 0) {
- buf[--digits] = '0';
- } else if (value > 0) {
- while (value != 0) {
- int digit = (int) (value % 10);
- buf[--digits] = (char) ('0' + digit);
- value = value / 10;
- }
- } else {
- BigInteger bigInt = new BigInteger(String.valueOf(value));
- bigInt = bigInt.add(BIG_2_64);
- while (bigInt.compareTo(BigInteger.ZERO) != 0) {
- int digit = bigInt.mod(BIG_TEN).intValue();
- buf[--digits] = (char) ('0' + digit);
- bigInt = bigInt.divide(BIG_TEN);
- }
- }
- for (int i = buf.length - digits; i < _min; i++) {
- sb.append(_pad);
- }
- for (; digits < buf.length; digits++) {
- sb.append(buf[digits]);
- }
- return true;
- }
- }
- static class BinaryPrintfSegment extends PrintfSegment {
- private final int _index;
- private final int _min;
- private final char _pad;
- BinaryPrintfSegment(int index, int min, int pad) {
- _index = index;
- _min = min;
- if (pad >= 0) {
- _pad = (char) pad;
- } else {
- _pad = ' ';
- }
- }
- static BinaryPrintfSegment create(String format, int index) {
- int length = format.length();
- int offset = 1;
- char pad = ' ';
- if (format.charAt(offset) == ' ') {
- pad = ' ';
- offset++;
- } else if (format.charAt(offset) == '0') {
- pad = '0';
- offset++;
- }
- int min = 0;
- for (; offset < length - 1; offset++) {
- char ch = format.charAt(offset);
- if ('0' <= ch && ch <= '9') {
- min = 10 * min + ch - '0';
- } else {
- return null;
- }
- }
- return new BinaryPrintfSegment(index, min, pad);
- }
- @Override
- public boolean apply(Env env, StringValue sb, Value[] args) {
- long value;
- if (_index >= 0 && _index < args.length) {
- value = args[_index].toLong();
- } else {
- env.warning(L.l("printf(): not enough arguments to match format."));
- return false;
- }
- int digits = 0;
- long shift = value;
- for (int i = 0; i < 64; i++) {
- if (shift != 0) {
- digits = i;
- }
- shift = shift >>> 1;
- }
- for (int i = digits + 1; i < _min; i++) {
- sb.append(_pad);
- }
- for (; digits >= 0; digits--) {
- int digit = (int) (value >>> (digits)) & 0x1;
- sb.append((char) ('0' + digit));
- }
- return true;
- }
- }
- static class DoublePrintfSegment extends PrintfSegment {
- private final String _format;
- private final boolean _isLeftZero;
- private final int _index;
- private final BiancaLocale _locale;
- DoublePrintfSegment(String format,
- boolean isLeftZero,
- int index,
- BiancaLocale locale) {
- if (hasIndex(format)) {
- _index = getIndex(format);
- _format = getIndexFormat(format);
- } else {
- _format = '%' + format;
- _index = index;
- }
- _isLeftZero = isLeftZero;
- _locale = locale;
- }
- @Override
- public boolean apply(Env env, StringValue sb, Value[] args) {
- double value;
- if (_index < args.length) {
- value = args[_index].toDouble();
- } else {
- env.warning(L.l("printf(): not enough arguments to match format."));
- return false;
- }
- String s;
- if (_locale == null) {
- s = String.format(_format, value);
- } else {
- s = String.format(_locale.getLocale(), _format, value);
- }
- if (_isLeftZero) {
- int len = s.length();
- // php/1174 "-0" not allowed by java formatter
- for (int i = 0; i < len; i++) {
- char ch = s.charAt(i);
- if (ch == ' ') {
- sb.append('0');
- } else {
- sb.append(ch);
- }
- }
- } else {
- sb.append(s);
- }
- return true;
- }
- }
- static class StringPrintfSegment extends PrintfSegment {
- protected final char[] _prefix;
- protected final int _min;
- protected final int _max;
- protected final boolean _isLeft;
- protected final boolean _isUpper;
- protected final char _pad;
- protected final int _index;
- StringPrintfSegment(StringBuilder prefix,
- boolean isLeft, int pad, boolean isUpper,
- int width,
- String format, int index) {
- _prefix = new char[prefix.length()];
- _isLeft = isLeft;
- _isUpper = isUpper;
- if (pad >= 0) {
- _pad = (char) pad;
- } else {
- _pad = ' ';
- }
- prefix.getChars(0, _prefix.length, _prefix, 0);
- if (hasIndex(format)) {
- index = getIndex(format);
- format = getIndexFormat(format);
- }
- int i = 0;
- int len = format.length();
- int min = width;
- int max = Integer.MAX_VALUE;
- char ch = ' ';
- /*
- for (; i < len && '0' <= (ch = format.charAt(i)) && ch <= '9'; i++) {
- min = 10 * min + ch - '0';
- }
- */
- if (0 < len && format.charAt(0) == '.') {
- max = 0;
- for (i++; i < len && '0' <= (ch = format.charAt(i)) && ch <= '9'; i++) {
- max = 10 * max + ch - '0';
- }
- }
- _min = min;
- _max = max;
- _index = index;
- }
- protected String toValue(Value[] args) {
- return args[_index].toString();
- }
- @Override
- public boolean apply(Env env, StringValue sb, Value[] args) {
- sb.append(_prefix, 0, _prefix.length);
- String value;
- if (_index < args.length) {
- value = toValue(args);
- } else {
- env.warning(L.l("printf(): not enough arguments to match format."));
- return false;
- }
- int len = value.length();
- if (_max < len) {
- value = value.substring(0, _max);
- len = _max;
- }
- if (_isUpper) {
- value = value.toUpperCase();
- }
- if (!_isLeft) {
- for (int i = len; i < _min; i++) {
- sb.append(_pad);
- }
- }
- sb.append(value);
- if (_isLeft) {
- for (int i = len; i < _min; i++) {
- sb.append(_pad);
- }
- }
- return true;
- }
- }
- static class CharPrintfSegment extends StringPrintfSegment {
- CharPrintfSegment(StringBuilder prefix,
- boolean isLeft, int pad, boolean isUpper,
- int width,
- String format, int index) {
- super(prefix, isLeft, pad, isUpper, width, format, index);
- }
- @Override
- protected String toValue(Value[] args) {
- if (args.length <= _index) {
- return "";
- }
- Value v = args[_index];
- if (v.isLongConvertible()) {
- return String.valueOf((char) v.toLong());
- } else {
- return v.charValueAt(0).toString();
- }
- }
- }
- static class SimpleStringReader {
- StringValue _str;
- int _length;
- int _index;
- SimpleStringReader(StringValue str) {
- _str = str;
- _length = str.length();
- _index = 0;
- }
- int read() {
- if (_index < _length) {
- return _str.charAt(_index++);
- } else {
- return -1;
- }
- }
- int peek() {
- if (_index < _length) {
- return _str.charAt(_index);
- } else {
- return -1;
- }
- }
- int readInt(int currChar) {
- int number = currChar - '0';
- while (true) {
- currChar = peek();
- if ('0' <= currChar && currChar <= '9') {
- number = number * 10 + currChar - '0';
- _index++;
- } else {
- break;
- }
- }
- return number;
- }
- }
- // sscanf
- abstract static class ScanfSegment {
- abstract public boolean isAssigned();
- abstract public int apply(StringValue string,
- int strlen,
- int sIndex,
- Value var,
- boolean isReturnArray);
- void sscanfPut(Value var, Value val, boolean isReturnArray) {
- if (isReturnArray) {
- var.put(val);
- } else {
- var.set(val);
- }
- }
- }
- static class ScanfConstant extends ScanfSegment {
- private final String _string;
- private final int _strlen;
- private ScanfConstant(String string) {
- _string = string;
- _strlen = string.length();
- }
- @Override
- public boolean isAssigned() {
- return false;
- }
- @Override
- public int apply(StringValue string,
- int strlen,
- int sIndex,
- Value var,
- boolean isReturnArray) {
- int fStrlen = _strlen;
- String fString = _string;
- if (strlen - sIndex < fStrlen) {
- return -1;
- }
- for (int i = 0; i < fStrlen; i++) {
- if (string.charAt(sIndex++) != fString.charAt(i)) {
- return -1;
- }
- }
- return sIndex;
- }
- }
- static class ScanfWhitespace extends ScanfSegment {
- static final ScanfWhitespace SEGMENT = new ScanfWhitespace();
- private ScanfWhitespace() {
- }
- @Override
- public boolean isAssigned() {
- return false;
- }
- @Override
- public int apply(StringValue string,
- int strlen,
- int sIndex,
- Value var,
- boolean isReturnArray) {
- for (;
- sIndex < strlen && isWhitespace(string.charAt(sIndex));
- sIndex++) {
- }
- return sIndex;
- }
- }
- static class ScanfStringLength extends ScanfSegment {
- static final ScanfStringLength SEGMENT = new ScanfStringLength();
- private ScanfStringLength() {
- }
- @Override
- public boolean isAssigned() {
- return true;
- }
- @Override
- public int apply(StringValue string,
- int strlen,
- int sIndex,
- Value var,
- boolean isReturnArray) {
- sscanfPut(var, LongValue.create(sIndex), isReturnArray);
- return sIndex;
- }
- }
- static class ScanfSet extends ScanfSegment {
- private IntSet _set;
- private ScanfSet(IntSet set) {
- _set = set;
- }
- @Override
- public boolean isAssigned() {
- return true;
- }
- @Override
- public int apply(StringValue string,
- int strlen,
- int sIndex,
- Value var,
- boolean isReturnArray) {
- StringValue sb = new StringValue();
- for (; sIndex < strlen; sIndex++) {
- char ch = string.charAt(sIndex);
- if (_set.contains(ch)) {
- sb.append(ch);
- } else {
- break;
- }
- }
- if (sb.length() > 0) {
- sscanfPut(var, sb, isReturnArray);
- } else if (isReturnArray) {
- var.put(NullValue.NULL);
- }
- return sIndex;
- }
- }
- static class ScanfSetNegated extends ScanfSegment {
- private IntSet _set;
- private ScanfSetNegated(IntSet set) {
- _set = set;
- }
- @Override
- public boolean isAssigned() {
- return true;
- }
- @Override
- public int apply(StringValue string,
- int strlen,
- int sIndex,
- Value var,
- boolean isReturnArray) {
- StringValue sb = new StringValue();
- for (; sIndex < strlen; sIndex++) {
- char ch = string.charAt(sIndex);
- if (!_set.contains(ch)) {
- sb.append(ch);
- } else {
- break;
- }
- }
- if (sb.length() > 0) {
- sscanfPut(var, sb, isReturnArray);
- } else if (isReturnArray) {
- var.put(NullValue.NULL);
- }
- return sIndex;
- }
- }
- static class ScanfScientific extends ScanfSegment {
- private final int _maxLen;
- ScanfScientific(int maxLen) {
- if (maxLen < 0) {
- maxLen = Integer.MAX_VALUE;
- }
- _maxLen = maxLen;
- }
- @Override
- public boolean isAssigned() {
- return true;
- }
- @Override
- public int apply(StringValue s,
- int strlen,
- int i,
- Value var,
- boolean isReturnArray) {
- if (i == strlen) {
- if (isReturnArray) {
- var.put(NullValue.NULL);
- }
- return i;
- }
- int start = i;
- int len = strlen;
- int ch = 0;
- int maxLen = _maxLen;
- if (i < len && maxLen > 0 && ((ch = s.charAt(i)) == '+' || ch == '-')) {
- i++;
- maxLen--;
- }
- for (; i < len && maxLen > 0
- && '0' <= (ch = s.charAt(i)) && ch <= '9'; i++) {
- maxLen--;
- }
- if (ch == '.') {
- maxLen--;
- for (i++; i < len && maxLen > 0
- && '0' <= (ch = s.charAt(i)) && ch <= '9'; i++) {
- maxLen--;
- }
- }
- if (ch == 'e' || ch == 'E') {
- maxLen--;
- int e = i++;
- if (start == e) {
- sscanfPut(var, NullValue.NULL, isReturnArray);
- return start;
- }
- if (i < len && maxLen > 0 && (ch = s.charAt(i)) == '+' || ch == '-') {
- i++;
- maxLen--;
- }
- for (; i < len && maxLen > 0
- && '0' <= (ch = s.charAt(i)) && ch <= '9'; i++) {
- maxLen--;
- }
- if (i == e + 1) {
- i = e;
- }
- }
- double val;
- if (i == 0) {
- val = 0;
- } else {
- val = Double.parseDouble(s.substring(start, i).toString());
- }
- sscanfPut(var, DoubleValue.create(val), isReturnArray);
- return i;
- }
- }
- static class ScanfHex extends ScanfSegment {
- private final int _maxLen;
- ;
- ScanfHex(int maxLen) {
- if (maxLen < 0) {
- maxLen = Integer.MAX_VALUE;
- }
- _maxLen = maxLen;
- }
- @Override
- public boolean isAssigned() {
- return true;
- }
- @Override
- public int apply(StringValue string,
- int strlen,
- int sIndex,
- Value var,
- boolean isReturnArray) {
- if (sIndex == strlen) {
- if (isReturnArray) {
- var.put(NullValue.NULL);
- }
- return sIndex;
- }
- int val = 0;
- int sign = 1;
- boolean isMatched = false;
- int maxLen = _maxLen;
- if (sIndex < strlen) {
- char ch = string.charAt(sIndex);
- if (ch == '+') {
- sIndex++;
- maxLen--;
- } else if (ch == '-') {
- sign = -1;
- sIndex++;
- maxLen--;
- }
- }
- for (; sIndex < strlen && maxLen-- > 0; sIndex++) {
- char ch = string.charAt(sIndex);
- if ('0' <= ch && ch <= '9') {
- val = val * 16 + ch - '0';
- isMatched = true;
- } else if ('a' <= ch && ch <= 'f') {
- val = val * 16 + ch - 'a' + 10;
- isMatched = true;
- } else if ('A' <= ch && ch <= 'F') {
- val = val * 16 + ch - 'A' + 10;
- isMatched = true;
- } else if (!isMatched) {
- sscanfPut(var, NullValue.NULL, isReturnArray);
- return sIndex;
- } else {
- break;
- }
- }
- sscanfPut(var, LongValue.create(val * sign), isReturnArray);
- return sIndex;
- }
- }
- static class ScanfInteger extends ScanfSegment {
- private final int _maxLen;
- private final int _base;
- private final boolean _isUnsigned;
- ScanfInteger(int maxLen, int base, boolean isUnsigned) {
- if (maxLen < 0) {
- maxLen = Integer.MAX_VALUE;
- }
- _maxLen = maxLen;
- _base = base;
- _isUnsigned = isUnsigned;
- }
- @Override
- public boolean isAssigned() {
- return true;
- }
- @Override
- public int apply(StringValue string,
- int strlen,
- int sIndex,
- Value var,
- boolean isReturnArray) {
- if (sIndex == strlen) {
- if (isReturnArray) {
- var.put(NullValue.NULL);
- }
- return sIndex;
- }
- // TODO: 32-bit vs 64-bit
- int val = 0;
- int sign = 1;
- boolean isNotMatched = true;
- int maxLen = _maxLen;
- if (sIndex < strlen) {
- char ch = string.charAt(sIndex);
- if (ch == '+') {
- sIndex++;
- maxLen--;
- } else if (ch == '-') {
- sign = -1;
- sIndex++;
- maxLen--;
- }
- }
- int base = _base;
- int topRange = base + '0';
- for (; sIndex < strlen && maxLen-- > 0; sIndex++) {
- char ch = string.charAt(sIndex);
- if ('0' <= ch && ch < topRange) {
- val = val * base + ch - '0';
- isNotMatched = false;
- } else if (isNotMatched) {
- sscanfPut(var, NullValue.NULL, isReturnArray);
- return sIndex;
- } else {
- break;
- }
- }
- if (_isUnsigned) {
- if (sign == -1 && val != 0) {
- sscanfPut(
- var, StringValue.create(0xffffffffL - val + 1), isReturnArray);
- } else {
- sscanfPut(var, LongValue.create(val), isReturnArray);
- }
- } else {
- sscanfPut(var, LongValue.create(val * sign), isReturnArray);
- }
- return sIndex;
- }
- }
- static class ScanfString extends ScanfSegment {
- private final int _maxLen;
- ScanfString(int maxLen) {
- if (maxLen < 0) {
- maxLen = Integer.MAX_VALUE;
- }
- _maxLen = maxLen;
- }
- @Override
- public boolean isAssigned() {
- return true;
- }
- /**
- * Scans a string with a given length.
- */
- @Override
- public int apply(StringValue string,
- int strlen,
- int sIndex,
- Value var,
- boolean isReturnArray) {
- if (sIndex == strlen) {
- if (isReturnArray) {
- var.put(NullValue.NULL);
- }
- return sIndex;
- }
- StringValue sb = new StringValue();
- int maxLen = _maxLen;
- for (; sIndex < strlen && maxLen-- > 0; sIndex++) {
- char ch = string.charAt(sIndex);
- if (isWhitespace(ch)) {
- break;
- }
- sb.append(ch);
- }
- sscanfPut(var, sb, isReturnArray);
- return sIndex;
- }
- }
- static {
- DEFAULT_DECIMAL_FORMAT_SYMBOLS = new DecimalFormatSymbols();
- DEFAULT_DECIMAL_FORMAT_SYMBOLS.setDecimalSeparator('.');
- DEFAULT_DECIMAL_FORMAT_SYMBOLS.setGroupingSeparator(',');
- DEFAULT_DECIMAL_FORMAT_SYMBOLS.setZeroDigit('0');
- }
- }