001/* 002 * PlotSquared, a land and world management plugin for Minecraft. 003 * Copyright (C) IntellectualSites <https://intellectualsites.com> 004 * Copyright (C) IntellectualSites team and contributors 005 * 006 * This program is free software: you can redistribute it and/or modify 007 * it under the terms of the GNU General Public License as published by 008 * the Free Software Foundation, either version 3 of the License, or 009 * (at your option) any later version. 010 * 011 * This program is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 014 * GNU General Public License for more details. 015 * 016 * You should have received a copy of the GNU General Public License 017 * along with this program. If not, see <https://www.gnu.org/licenses/>. 018 */ 019package com.plotsquared.core.util; 020 021import com.plotsquared.core.PlotSquared; 022import com.plotsquared.core.configuration.caption.Caption; 023import org.checkerframework.checker.nullness.qual.NonNull; 024 025import java.lang.reflect.Array; 026import java.util.ArrayList; 027import java.util.Arrays; 028import java.util.Collection; 029import java.util.Comparator; 030import java.util.Iterator; 031import java.util.List; 032import java.util.Map; 033import java.util.Map.Entry; 034import java.util.Set; 035import java.util.regex.Pattern; 036 037public class StringMan { 038 039 // Stolen from https://stackoverflow.com/a/366532/12620913 | Debug: https://regex101.com/r/DudJLb/1 040 private static final Pattern STRING_SPLIT_PATTERN = Pattern.compile("[^\\s\"]+|\"([^\"]*)\""); 041 042 public static String replaceFromMap(String string, Map<String, String> replacements) { 043 StringBuilder sb = new StringBuilder(string); 044 int size = string.length(); 045 for (Entry<String, String> entry : replacements.entrySet()) { 046 if (size == 0) { 047 break; 048 } 049 String key = entry.getKey(); 050 String value = entry.getValue(); 051 int start = sb.indexOf(key, 0); 052 while (start > -1) { 053 int end = start + key.length(); 054 int nextSearchStart = start + value.length(); 055 sb.replace(start, end, value); 056 size -= end - start; 057 start = sb.indexOf(key, nextSearchStart); 058 } 059 } 060 return sb.toString(); 061 } 062 063 public static int intersection(Set<String> options, String[] toCheck) { 064 int count = 0; 065 for (String check : toCheck) { 066 if (options.contains(check)) { 067 count++; 068 } 069 } 070 return count; 071 } 072 073 public static String getString(Object obj) { 074 if (obj == null) { 075 return "null"; 076 } 077 if (obj instanceof String) { 078 return (String) obj; 079 } 080 if (obj instanceof Caption) { 081 return ((Caption) obj).getComponent(PlotSquared.platform()); 082 } 083 if (obj.getClass().isArray()) { 084 StringBuilder result = new StringBuilder(); 085 String prefix = ""; 086 087 for (int i = 0; i < Array.getLength(obj); i++) { 088 result.append(prefix).append(getString(Array.get(obj, i))); 089 prefix = ","; 090 } 091 return "( " + result + " )"; 092 } else if (obj instanceof Collection<?>) { 093 StringBuilder result = new StringBuilder(); 094 String prefix = ""; 095 for (Object element : (Collection<?>) obj) { 096 result.append(prefix).append(getString(element)); 097 prefix = ","; 098 } 099 return "[ " + result + " ]"; 100 } else { 101 return obj.toString(); 102 } 103 } 104 105 public static String replaceFirst(char c, String s) { 106 if (s == null) { 107 return ""; 108 } 109 if (s.isEmpty()) { 110 return s; 111 } 112 char[] chars = s.toCharArray(); 113 char[] newChars = new char[chars.length]; 114 int used = 0; 115 boolean found = false; 116 for (char cc : chars) { 117 if (!found && (c == cc)) { 118 found = true; 119 } else { 120 newChars[used++] = cc; 121 } 122 } 123 if (found) { 124 chars = new char[newChars.length - 1]; 125 System.arraycopy(newChars, 0, chars, 0, chars.length); 126 return String.valueOf(chars); 127 } 128 return s; 129 } 130 131 public static String replaceAll(String string, Object... pairs) { 132 StringBuilder sb = new StringBuilder(string); 133 for (int i = 0; i < pairs.length; i += 2) { 134 String key = pairs[i] + ""; 135 String value = pairs[i + 1] + ""; 136 int start = sb.indexOf(key, 0); 137 while (start > -1) { 138 int end = start + key.length(); 139 int nextSearchStart = start + value.length(); 140 sb.replace(start, end, value); 141 start = sb.indexOf(key, nextSearchStart); 142 } 143 } 144 return sb.toString(); 145 } 146 147 public static boolean isAlphanumeric(String str) { 148 for (int i = 0; i < str.length(); i++) { 149 char c = str.charAt(i); 150 if ((c < 0x30) || ((c >= 0x3a) && (c <= 0x40)) || ((c > 0x5a) && (c <= 0x60)) || (c 151 > 0x7a)) { 152 return false; 153 } 154 } 155 return true; 156 } 157 158 public static boolean isAlphanumericUnd(String str) { 159 for (int i = 0; i < str.length(); i++) { 160 char c = str.charAt(i); 161 if (c < 0x30 || (c >= 0x3a) && (c <= 0x40) || (c > 0x5a) && (c <= 0x60) || (c > 0x7a)) { 162 return false; 163 } 164 } 165 return true; 166 } 167 168 public static boolean isAlpha(String str) { 169 for (int i = 0; i < str.length(); i++) { 170 char c = str.charAt(i); 171 if ((c <= 0x40) || ((c > 0x5a) && (c <= 0x60)) || (c > 0x7a)) { 172 return false; 173 } 174 } 175 return true; 176 } 177 178 public static String join(Collection<?> collection, String delimiter) { 179 return join(collection.toArray(), delimiter); 180 } 181 182 public static String joinOrdered(Collection<?> collection, String delimiter) { 183 Object[] array = collection.toArray(); 184 Arrays.sort(array, Comparator.comparingInt(Object::hashCode)); 185 return join(array, delimiter); 186 } 187 188 public static String join(Collection<?> collection, char delimiter) { 189 return join(collection.toArray(), delimiter + ""); 190 } 191 192 public static boolean isAsciiPrintable(char c) { 193 return (c >= ' ') && (c < ''); 194 } 195 196 public static boolean isAsciiPrintable(String s) { 197 for (char c : s.toCharArray()) { 198 if (!isAsciiPrintable(c)) { 199 return false; 200 } 201 } 202 return true; 203 } 204 205 public static int getLevenshteinDistance(String s, String t) { 206 int n = s.length(); 207 int m = t.length(); 208 if (n == 0) { 209 return m; 210 } else if (m == 0) { 211 return n; 212 } 213 if (n > m) { 214 String tmp = s; 215 s = t; 216 t = tmp; 217 n = m; 218 m = t.length(); 219 } 220 int[] p = new int[n + 1]; 221 int[] d = new int[n + 1]; 222 int i; 223 for (i = 0; i <= n; i++) { 224 p[i] = i; 225 } 226 for (int j = 1; j <= m; j++) { 227 char t_j = t.charAt(j - 1); 228 d[0] = j; 229 230 for (i = 1; i <= n; i++) { 231 int cost = s.charAt(i - 1) == t_j ? 0 : 1; 232 d[i] = Math.min(Math.min(d[i - 1] + 1, p[i] + 1), p[i - 1] + cost); 233 } 234 int[] _d = p; 235 p = d; 236 d = _d; 237 } 238 return p[n]; 239 } 240 241 public static String join(Object[] array, String delimiter) { 242 StringBuilder result = new StringBuilder(); 243 for (int i = 0, j = array.length; i < j; i++) { 244 if (i > 0) { 245 result.append(delimiter); 246 } 247 result.append(array[i]); 248 } 249 return result.toString(); 250 } 251 252 public static String join(int[] array, String delimiter) { 253 Integer[] wrapped = new Integer[array.length]; 254 for (int i = 0; i < array.length; i++) { 255 wrapped[i] = array[i]; 256 } 257 return join(wrapped, delimiter); 258 } 259 260 public static boolean isEqualToAny(String a, String... args) { 261 for (String arg : args) { 262 if (StringMan.isEqual(a, arg)) { 263 return true; 264 } 265 } 266 return false; 267 } 268 269 public static boolean isEqualIgnoreCaseToAny(@NonNull String a, String... args) { 270 for (String arg : args) { 271 if (a.equalsIgnoreCase(arg)) { 272 return true; 273 } 274 } 275 return false; 276 } 277 278 public static boolean isEqual(String a, String b) { 279 if ((a == null && b != null) || (a != null && b == null)) { 280 return false; 281 } else if (a == null /* implies that b is null */) { 282 return false; 283 } 284 return a.equals(b); 285 } 286 287 public static boolean isEqualIgnoreCase(String a, String b) { 288 return a.equals(b) || ((a != null) && (b != null) && (a.length() == b.length()) && a 289 .equalsIgnoreCase(b)); 290 } 291 292 public static String repeat(String s, int n) { 293 StringBuilder sb = new StringBuilder(); 294 sb.append(String.valueOf(s).repeat(Math.max(0, n))); 295 return sb.toString(); 296 } 297 298 public static boolean contains(String name, char c) { 299 for (char current : name.toCharArray()) { 300 if (c == current) { 301 return true; 302 } 303 } 304 return false; 305 } 306 307 public <T> Collection<T> match(Collection<T> col, String startsWith) { 308 if (col == null) { 309 return null; 310 } 311 startsWith = startsWith.toLowerCase(); 312 Iterator<?> iterator = col.iterator(); 313 while (iterator.hasNext()) { 314 Object item = iterator.next(); 315 if (item == null || !item.toString().toLowerCase().startsWith(startsWith)) { 316 iterator.remove(); 317 } 318 } 319 return col; 320 } 321 322 /** 323 * @param message an input string 324 * @return a list of strings 325 * @since 6.4.0 326 * 327 * <table border="1"> 328 * <caption>Converts multiple quoted and single strings into a list of strings</caption> 329 * <thead> 330 * <tr> 331 * <th>Input</th> 332 * <th>Output</th> 333 * </tr> 334 * </thead> 335 * <tbody> 336 * <tr> 337 * <td>title "sub title"</td> 338 * <td>["title", "sub title"]</td> 339 * </tr> 340 * <tr> 341 * <td>"a title" subtitle</td> 342 * <td>["a title", "subtitle"]</td> 343 * </tr> 344 * <tr> 345 * <td>"title" "subtitle"</td> 346 * <td>["title", "subtitle"]</td> 347 * </tr> 348 * <tr> 349 * <td>"PlotSquared is going well" the authors "and many contributors"</td> 350 * <td>["PlotSquared is going well", "the", "authors", "and many contributors"]</td> 351 * </tr> 352 * </tbody> 353 * </table> 354 */ 355 public static @NonNull List<String> splitMessage(@NonNull String message) { 356 var matcher = StringMan.STRING_SPLIT_PATTERN.matcher(message); 357 List<String> splitMessages = new ArrayList<>(); 358 while (matcher.find()) { 359 splitMessages.add(matcher.group(matcher.groupCount() - 1).replaceAll("\"", "")); 360 } 361 return splitMessages; 362 } 363 364}