You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
gnosygnu_xowa/100_core/src/gplx/String_.java

560 lines
22 KiB

/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012-2017 gnosygnu@gmail.com
XOWA is licensed under the terms of the General Public License (GPL) Version 3,
or alternatively under the terms of the Apache License Version 2.0.
You may use XOWA according to either of these licenses as is most appropriate
for your project on a case-by-case basis.
The terms of each license can be found in the source code repository:
GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt
Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt
*/
package gplx;
import gplx.core.envs.Op_sys;
import gplx.core.strings.String_bldr;
import gplx.core.strings.String_bldr_;
import gplx.objects.arrays.ArrayUtl;
import gplx.objects.lists.CompareAbleUtl;
public class String_ {
// -------- BASELIB_COPY --------
public static final Class<?> Cls_ref_type = String.class;
public static final String Cls_val_name = "str" + "ing";
public static final int Find_none = -1, Pos_neg1 = -1;
public static final String Empty = "", Null_mark = "<<NULL>>", Tab = "\t", Lf = "\n", CrLf = "\r\n", Nl = "\n";
public static boolean Eq(String lhs, String rhs) {return lhs == null ? rhs == null : lhs.equals(rhs);}
public static int Len(String s) {return s.length();}
public static char CharAt(String s, int i) {return s.charAt(i);}
public static String new_u8(byte[] v) {return v == null ? null : new_u8(v, 0, v.length);}
public static String new_u8(byte[] v, int bgn, int end) {
try {
return v == null
? null
: new String(v, bgn, end - bgn, "UTF-8");
}
catch (Exception e) {Err_.Noop(e); throw Err_.new_("core", "unsupported encoding", "bgn", bgn, "end", end);}
}
// use C# flavor ("a {0}") rather than Java format ("a %s"); also: (a) don't fail on format errors; (b) escape brackets by doubling
private static final char FORMAT_ITM_LHS = '{', FORMAT_ITM_RHS = '}';
public static String Format(String fmt, Object... args) {
// method vars
int args_len = ArrayUtl.LenObjAry(args);
if (args_len == 0) return fmt; // nothing to format
int fmt_len = Len(fmt);
// loop vars
int pos = 0; String arg_idx_str = ""; boolean inside_brackets = false;
String_bldr bfr = String_bldr_.new_();
while (pos < fmt_len) { // loop over every char; NOTE: UT8-SAFE b/c only checking for "{"; "}"
char c = CharAt(fmt, pos);
if (inside_brackets) {
if (c == FORMAT_ITM_LHS) { // first FORMAT_ITM_LHS is fake; add FORMAT_ITM_LHS and whatever is in arg_idx_str
bfr.Add(FORMAT_ITM_LHS).Add(arg_idx_str);
arg_idx_str = "";
}
else if (c == FORMAT_ITM_RHS) { // itm completed
int args_idx = Int_.Parse_or(arg_idx_str, Int_.Min_value);
String itm = args_idx != Int_.Min_value && Int_.Between(args_idx, 0, args_len - 1) // check (a) args_idx is num; (b) args_idx is in bounds
? Object_.Xto_str_strict_or_empty(args[args_idx]) // valid; add itm
: String_.Concat_any(FORMAT_ITM_LHS, arg_idx_str, FORMAT_ITM_RHS); // not valid; just add String
bfr.Add(itm);
inside_brackets = false;
arg_idx_str = "";
}
else
arg_idx_str += c;
}
else {
if (c == FORMAT_ITM_LHS || c == FORMAT_ITM_RHS) {
boolean pos_is_end = pos == fmt_len - 1;
if (pos_is_end) // last char is "{" or "}" (and not inside_brackets); ignore and just ad
bfr.Add(c);
else {
char next = CharAt(fmt, pos + 1);
if (next == c) { // "{{" or "}}": escape by doubling
bfr.Add(c);
pos++;
}
else
inside_brackets = true;
}
}
else
bfr.Add(c);
}
pos++;
}
if (Len(arg_idx_str) > 0) // unclosed bracket; add FORMAT_ITM_LHS and whatever is in arg_idx_str; ex: "{0"
bfr.Add(FORMAT_ITM_LHS).Add(arg_idx_str);
return bfr.To_str();
}
// -------- TO_MIGRATE --------
public static String cast(Object v) {return (String)v;}
public static String as_(Object obj) {return obj instanceof String ? (String)obj : null;}
public static String new_a7(byte[] v) {return v == null ? null : new_a7(v, 0, v.length);}
public static String new_a7(byte[] v, int bgn, int end) {
try {
return v == null
? null
: new String(v, bgn, end - bgn, "ASCII");
}
catch (Exception e) {throw Err_.new_exc(e, "core", "unsupported encoding");}
}
public static String new_u8__by_len(byte[] v, int bgn, int len) {
int v_len = v.length;
if (bgn + len > v_len) len = v_len - bgn;
return new_u8(v, bgn, bgn + len);
}
public static String[] Ary_add(String[]... arys) {
if (arys == null) return String_.Ary_empty;
int arys_len = arys.length;
int rv_len = 0;
for (int i = 0; i < arys_len; i++) {
String[] ary = arys[i];
rv_len += ary.length;
}
int rv_idx = 0;
String[] rv = new String[rv_len];
for (int i = 0; i < arys_len; i++) {
String[] ary = arys[i];
int ary_len = ary.length;
for (int j = 0; j < ary_len; j++)
rv[rv_idx++] = ary[j];
}
return rv;
}
public static boolean Len_gt_0(String s) {return s != null && s.length() > 0;}
public static boolean Len_eq_0(String s) {return s == null || s.length() == 0;}
public static String Lower(String s) {return s.toLowerCase();}
public static String Upper(String s) {return s.toUpperCase();}
public static String CaseNormalize(boolean caseSensitive, String s) {return caseSensitive ? s : String_.Lower(s);}
public static String Trim(String s) {return s.trim();}
public static String Mid(String s, int bgn) {return s.substring(bgn);}
public static String Replace(String s, String find, String replace) {return s.replace(find, replace);}
public static char[] XtoCharAry(String s) {return s.toCharArray();}
public static int CodePointAt(String s, int i) {return s.codePointAt(i);}
public static boolean Has(String s, String find) {return s.indexOf(find) != String_.Find_none;}
public static boolean Has_at_bgn(String s, String v) {return s.startsWith(v);}
public static boolean Has_at_end(String s, String v) {return s.endsWith(v);}
public static int FindFwd(String s, String find) {return s.indexOf(find);}
public static int FindFwd(String s, String find, int pos) {return s.indexOf(find, pos);}
public static int FindFwd(String s, String find, int bgn, int end) {
int rv = FindFwd(s, find, bgn);
return rv > end ? String_.Find_none : rv;
}
public static int FindBwdOr(String s, String find, int or) {int pos = FindBwd(s, find); return pos == Pos_neg1 ? or : pos;}
public static int FindBwd(String s, String find) {return s.lastIndexOf(find);}
public static int FindBwd(String s, String find, int pos) {return s.lastIndexOf(find, pos);}
public static int FindBetween(String s, String find, int bgn, int end) {
int rv = FindFwd(s, find, bgn);
return (rv > end) ? String_.Find_none : rv;
}
public static int FindAfter(String s, String find, int bgn) {
int rv = FindFwd(s, find, bgn);
return rv == String_.Find_none ? String_.Find_none : rv + Len(find);
}
public static int FindAfterRev(String s, String find, int pos) {
int rv = FindBwd(s, find, pos);
return rv == String_.Find_none ? String_.Find_none : rv + Len(find);
}
public static int Count(String s, String part) {
int count = 0, pos = -1; // -1 b/c first pass must be 0 (see pos + 1 below)
do {
pos = FindFwd(s, part, pos + 1);
if (pos == String_.Find_none) break;
count++;
} while (true);
return count;
}
public static boolean EqAny(String lhs, String... rhsAry) {
for (int i = 0; i < rhsAry.length; i++)
if (Eq(lhs, rhsAry[i])) return true;
return false;
}
public static boolean EqNot(String lhs, String rhs) {return !Object_.Eq(lhs, rhs);}
public static boolean EqEmpty(String lhs) {return lhs.equals("");}
public static String IfNullOrEmpty(String s, String or) {return s == null || s.length() == 0 ? or : s;}
public static int Compare_as_ordinals(String lhs, String rhs) {return lhs.compareTo(rhs);}
public static int Compare_ignoreCase(String lhs, String rhs) {
if (lhs == null && rhs != null) return CompareAbleUtl.Less;
else if (lhs != null && rhs == null) return CompareAbleUtl.More;
else if (lhs == null && rhs == null) return CompareAbleUtl.Same;
else return lhs.compareToIgnoreCase(rhs);
//#-
/*
if (lhs == null && rhs != null) return CompareAble_.Less;
else if (lhs != null && rhs == null) return CompareAble_.More;
else if (lhs == null && rhs == null) return CompareAble_.Same;
else return lhs.compareToIgnoreCase(rhs);
*/
}
public static int Compare(String lhs, String rhs) {
int compare = lhs.compareTo(rhs);
if (compare == CompareAbleUtl.Same) return CompareAbleUtl.Same;
else if (compare < CompareAbleUtl.Same) return CompareAbleUtl.Less;
else /* (compare > CompareAble_.Same) */ return CompareAbleUtl.More;
}
public static int Compare_byteAry(String lhs, String rhs) {
int lhsLen = lhs.length(), rhsLen = rhs.length();
int aryLen = lhsLen < rhsLen ? lhsLen : rhsLen;
int[] lhsAry = XtoIntAry(lhs, aryLen), rhsAry = XtoIntAry(rhs, aryLen);
for (int i = 0; i < aryLen; i++) {
int comp = Int_.Compare(lhsAry[i], rhsAry[i]);
if (comp != CompareAbleUtl.Same) return comp;
}
return Int_.Compare(lhsLen, rhsLen);
}
public static int[] XtoIntAry(String s, int len) {
int[] rv = new int[len];
for (int i = 0; i < len; i++)
rv[i] = (int)s.charAt(i);
return rv;
}
public static String Coalesce(String s, String alt) {return Len_eq_0(s) ? alt : s;}
public static boolean In(String s, String... ary) {
for (String itm : ary)
if (String_.Eq(s, itm)) return true;
return false;
}
public static String new_charAry_(char[] ary, int bgn, int len) {return new String(ary, bgn, len);}
public static String Mid(String s, int bgn, int end) {
try {return Mid_lang(s, bgn, end - bgn);}
catch (Exception e) {
int len = s == null ? 0 : Len(s);
String msg = "";
if (s == null) msg = "s is null";
else if (bgn > end) msg = "@bgn > @end";
else if (bgn < 0 || bgn >= len) msg = "@bgn is invalid";
else if (end < 0 || end > len) msg = "@end is invalid";
throw Err_.new_exc(e, "core", msg, "s", s, "bgn", bgn, "end", end, "len", len);
}
}
public static String MidByLenSafe(String s, int bgn, int len) {
if (bgn + len >= Len(s)) len = Len(s) - bgn;
return Mid_lang(s, bgn, len);
}
public static String MidByLen(String s, int bgn, int len) {return Mid_lang(s, bgn, len);}
public static String GetStrBefore(String s, String spr) {
int sprPos = String_.FindFwd(s, spr); if (sprPos == String_.Find_none) throw Err_.new_wo_type("could not find spr", "s", s, "spr", spr);
return Mid(s, 0, sprPos);
}
public static String GetStrAfter(String s, String spr) {
int sprPos = String_.FindFwd(s, spr); if (sprPos == String_.Find_none) throw Err_.new_wo_type("could not find spr", "s", s, "spr", spr);
return Mid(s, sprPos + 1);
}
public static String LimitToFirst(String s, int len) {
if (len < 0) throw Err_.new_invalid_arg("< 0", "len", len);
int sLen = Len(s); if (len > sLen) return s;
return Mid_lang(s, 0, len);
}
public static String LimitToLast(String s, int len) {
if (len < 0) throw Err_.new_invalid_arg("< 0", "len", len);
int sLen = Len(s); if (len > sLen) return s;
return Mid_lang(s, sLen - len, len);
}
public static String DelBgn(String s, int count) {
if (count < 0) throw Err_.new_invalid_arg("< 0", "count", count);
if (s == null) throw Err_.new_null();
int len = Len(s); if (count > len) throw Err_.new_invalid_arg("> @len", "count", count, "len", len);
return String_.Mid(s, count);
}
public static String DelBgnIf(String s, String find) {
if (s == null) throw Err_.new_null();
if (find == null) throw Err_.new_null();
return Has_at_bgn(s, find) ? String_.Mid(s, Len(find)) : s;
}
public static String DelEnd(String s, int count) {
if (count < 0) throw Err_.new_invalid_arg("< 0", "count", count);
if (s == null) throw Err_.new_null();
int len = Len(s); if (count > len) throw Err_.new_invalid_arg("> len", "count", count, "len", len);
return Mid_lang(s, 0, len + -count);
}
public static String DelEndIf(String s, String find) {
if (s == null) throw Err_.new_null();
if (find == null) throw Err_.new_null();
return Has_at_end(s, find) ? Mid_lang(s, 0, Len(s) - Len(find)) : s;
}
public static String LowerFirst(String s) {
int len = Len(s); if (len == 0) return String_.Empty;
String char0 = Lower(Mid_lang(s, 0, 1));
return len == 1 ? char0 : char0 + Mid(s, 1);
}
public static String UpperFirst(String s) {
int len = Len(s); if (len == 0) return String_.Empty;
String char0 = Upper(Mid_lang(s, 0, 1));
return len == 1 ? char0 : char0 + Mid(s, 1);
}
public static String PadBgn(String s, int totalLen, String pad) {return Pad(s, totalLen, pad, true);}
public static String PadEnd(String s, int totalLen, String pad) {return Pad(s, totalLen, pad, false);}
@gplx.Internal protected static String Pad(String s, int totalLen, String pad, boolean bgn) {
int sLen = Len(s);
int padLen = totalLen - sLen; if (padLen < 0) return s;
String_bldr sb = String_bldr_.new_();
if (!bgn) sb.Add(s);
for (int i = 0; i < padLen; i++)
sb.Add(pad);
if (bgn) sb.Add(s);
return sb.To_str();
}
public static String TrimEnd(String s) {if (s == null) return null;
int len = String_.Len(s);
if (len == 0) return s;
int last = len;
for (int i = len; i > 0; i--) {
char c = s.charAt(i - 1);
last = i;
if (c != ' ' && c != '\t' && c != '\r' && c != '\n') {
break;
}
}
return (last == len) ? s : Mid_lang(s, 0, last);
}
public static String Repeat(String s, int count) {
if (count < 0) throw Err_.new_wo_type("count cannot be negative", "count", count, "s", s);
String_bldr sb = String_bldr_.new_();
for (int i = 0; i < count; i++)
sb.Add(s);
return sb.To_str();
}
public static String Insert(String s, int pos, String toInsert) {
if (pos < 0 || pos >= String_.Len(s)) throw Err_.new_wo_type("String_.Insert failed; pos invalid", "pos", pos, "s", s, "toInsert", toInsert);
return s.substring(0, pos) + toInsert + s.substring(pos);
}
public static String FormatOrEmptyStrIfNull(String fmt, Object arg) {return arg == null ? "" : Format(fmt, arg);}
public static String Concat(char... ary) {return new String(ary);}
public static String Concat(String s1, String s2, String s3) {return s1 + s2 + s3;}
public static String Concat(String... ary) {
String_bldr sb = String_bldr_.new_();
for (String val : ary)
sb.Add(val);
return sb.To_str();
}
public static String Concat_any(Object... ary) {
String_bldr sb = String_bldr_.new_();
for (Object val : ary)
sb.Add_obj(val);
return sb.To_str();
}
public static String Concat_with_obj(String separator, Object... ary) {
String_bldr sb = String_bldr_.new_();
int aryLen = ArrayUtl.Len(ary);
for (int i = 0; i < aryLen; i++) {
if (i != 0) sb.Add(separator);
Object val = ary[i];
sb.Add_obj(Object_.Xto_str_strict_or_empty(val));
}
return sb.To_str();
}
public static String Concat_with_str(String spr, String... ary) {
String_bldr sb = String_bldr_.new_();
int len = ary.length;
for (int i = 0; i < len; i++) {
if (i != 0) sb.Add(spr);
sb.Add_obj(ary[i]);
}
return sb.To_str();
}
public static String Concat_lines_crlf(String... values) {
String_bldr sb = String_bldr_.new_();
for (String val : values)
sb.Add(val).Add(String_.CrLf);
return sb.To_str();
}
public static String Concat_lines_crlf_skipLast(String... values) {
String_bldr sb = String_bldr_.new_();
for (String val : values) {
if (sb.Count() != 0) sb.Add(String_.CrLf);
sb.Add(val);
}
return sb.To_str();
}
public static String Concat_lines_nl(String... values) {
String_bldr sb = String_bldr_.new_();
for (String val : values)
sb.Add(val).Add("\n");
return sb.To_str();
}
public static String Concat_lines_nl_apos_skip_last(String... lines) {
Bry_bfr bfr = Bry_bfr_.Get();
try {
Bry_.New_u8_nl_apos(bfr, lines);
return bfr.To_str_and_clear();
}
finally {bfr.Mkr_rls();}
}
public static String Concat_lines_nl_skip_last(String... ary) {
String_bldr sb = String_bldr_.new_();
int ary_len = ary.length; int ary_end = ary_len - 1;
for (int i = 0; i < ary_len; i++) {
sb.Add(ary[i]);
if (i != ary_end) sb.Add("\n");
}
return sb.To_str();
}
public static String[] Ary(String... ary) {return ary;}
public static String[] Ary_wo_null(String... ary) {
List_adp list = List_adp_.New();
int len = ary.length;
for (int i = 0; i < len; ++i) {
String itm = ary[i];
if (itm == null) continue;
list.Add(itm);
}
return list.ToStrAry();
}
public static String AryXtoStr(String... ary) {
String_bldr sb = String_bldr_.new_();
for (String s : ary)
sb.Add(s).Add(";");
return sb.To_str();
}
public static final String[] Ary_empty = new String[0];
public static String[] Split(String raw, char dlm) {return Split(raw, dlm, false);}
public static String[] Split(String raw, char dlm, boolean addEmptyIfDlmIsLast) {
List_adp list = List_adp_.New(); String_bldr sb = String_bldr_.new_();
int rawLen = String_.Len(raw); char c = '\0';
for (int i = 0; i < rawLen; i++) {
c = String_.CharAt(raw, i);
if (c == dlm) {
if (!addEmptyIfDlmIsLast && sb.Count() == 0 && i == rawLen - 1) {}
else list.Add(sb.To_str_and_clear());
}
else
sb.Add(c);
}
if (sb.Count() > 0)
list.Add(sb.To_str_and_clear());
return list.ToStrAry();
}
public static String[] Split(String s, String separator) {return Split_do(s, separator, false);}
public static String[] SplitLines_crlf(String s) {return Split(s, Op_sys.Wnt.Nl_str());}
public static String[] SplitLines_nl(String s) {return Split(s, Op_sys.Lnx.Nl_str());}
public static String[] SplitLines_any(String s) {return Split_do(s, Op_sys.Lnx.Nl_str(), true);}
public static String[] Split_lang(String s, char c) {return s.split(Character.toString(c));}
static String[] Split_do(String s, String spr, boolean skipChar13) {
if (String_.Eq(s, "") // "".Split('a') return array with one member: ""
|| String_.Eq(spr, "")) // "a".Split('\0') returns array with one member: "a"
return new String[] {s};
List_adp list = List_adp_.New(); String_bldr sb = String_bldr_.new_();
int i = 0, sprPos = 0; boolean sprMatched = false; char spr0 = CharAt(spr, 0);
int textLength = Len(s); int sprLength = Len(spr);
while (true) {
if (sprMatched
|| i == textLength) { // last pass; add whatever's in sb to list
list.Add(sb.To_str_and_clear());
if (sprMatched && i == textLength) list.Add(""); // if s ends with spr and last pass, add emptyString as last
sprMatched = false;
}
if (i == textLength) break;
char c = CharAt(s, i);
if (skipChar13 && c == (char)13) {i++; continue;}
if (c == spr0) { // matches first char of spr
sprPos = 1;
while (true) {
if (sprPos == sprLength) { // made it to end, must be match
sprMatched = true;
break;
}
if (i + sprPos == textLength) break; // ran out of s; handles partial match at end of String; ab+, +-
if (CharAt(s, i + sprPos) != CharAt(spr, sprPos)) break; // no match
sprPos++;
}
if (!sprMatched) // add partial match to sb
sb.Add(Mid_lang(s, i, sprPos));
i += sprPos;
}
else { // no spr match; just add char, increment pos
sb.Add(c);
i++;
}
}
return (String[])list.ToAry(String.class);
}
static String Mid_lang(String s, int bgn, int len) {return s.substring(bgn, bgn + len);}
public static String Extract_after_bwd(String src, String dlm) {
int dlm_pos = String_.FindBwd(src, dlm); if (dlm_pos == String_.Find_none) return String_.Empty;
int src_len = String_.Len(src); if (dlm_pos == src_len - 1) return String_.Empty;
return String_.Mid(src, dlm_pos + 1, src_len);
}
public static String Replace_by_pos(String v, int del_bgn, int del_end, String repl) {
return String_.Mid(v, 0, del_bgn) + repl + String_.Mid(v, del_end, String_.Len(v));
}
public static String read_(Object obj) {// NOTE: same as cast; for consistent readability only
String rv = as_(obj);
if (rv == null && obj != null) throw Err_.new_type_mismatch(String.class, obj); // NOTE: obj != null needed; EX: cast(null) --> null
return rv;
}
public static String[] Ary_parse(String raw, String dlm) {return String_.Split(raw, dlm);}
public static String[] Ary(byte[]... ary) {
if (ary == null) return String_.Ary_empty;
int ary_len = ary.length;
String[] rv = new String[ary_len];
for (int i = 0; i < ary_len; i++) {
byte[] itm = ary[i];
rv[i] = itm == null ? null : String_.new_u8(itm);
}
return rv;
}
public static String [] Ary_filter(String[] src, String[] filter) {
Hash_adp hash = Hash_adp_.New();
int len = filter.length;
for (int i = 0; i < len; i++) {
String itm = filter[i];
hash.AddIfDupeUseNth(itm, itm);
}
List_adp rv = List_adp_.New();
len = src.length;
for (int i = 0; i < len; i++) {
String itm = src[i];
if (hash.Has(itm)) rv.Add(itm);
}
return rv.ToStrAry();
}
public static String[] Ary_flatten(String[][] src_ary) {
int trg_len = 0;
int src_len = ArrayUtl.Len(src_ary);
for (int i = 0; i < src_len; i++) {
String[] itm = src_ary[i];
if (itm != null) trg_len += ArrayUtl.Len(itm);
}
String[] trg_ary = new String[trg_len];
trg_len = 0;
for (int i = 0; i < src_len; i++) {
String[] itm = src_ary[i];
if (itm == null) continue;
int itm_len = ArrayUtl.Len(itm);
for (int j = 0; j < itm_len; j++)
trg_ary[trg_len++] = itm[j];
}
return trg_ary;
}
public static boolean Ary_eq(String[] lhs, String[] rhs) {
int lhs_len = lhs.length, rhs_len = rhs.length;
if (lhs_len != rhs_len) return false;
for (int i = 0; i < lhs_len; ++i)
if (!String_.Eq(lhs[i], rhs[i])) return false;
return true;
}
public static String To_str__as_kv_ary(String... ary) {
int len = ary.length;
Bry_bfr bfr = Bry_bfr_.New();
for (int i = 0; i < len; i+=2) {
bfr.Add_str_u8(ary[i]).Add_byte_eq();
String val = i + 1 < len ? ary[i + 1] : null;
if (val != null) bfr.Add_str_u8(val);
bfr.Add_byte_nl();
}
return bfr.To_str_and_clear();
}
}