001/* 002 * Stallion Core: A Modern Web Framework 003 * 004 * Copyright (C) 2015 - 2016 Stallion Software LLC. 005 * 006 * This program is free software: you can redistribute it and/or modify it under the terms of the 007 * GNU General Public License as published by the Free Software Foundation, either version 2 of 008 * the License, or (at your option) any later version. This program is distributed in the hope that 009 * it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 010 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public 011 * License for more details. You should have received a copy of the GNU General Public License 012 * along with this program. If not, see <http://www.gnu.org/licenses/gpl-2.0.html>. 013 * 014 * 015 * 016 */ 017 018package io.stallion.utils; 019 020 021import com.google.common.collect.Lists; 022import io.stallion.exceptions.AppException; 023import io.stallion.settings.Settings; 024 025import java.nio.charset.Charset; 026import java.time.ZoneId; 027import java.time.ZonedDateTime; 028import java.util.*; 029import java.util.function.Consumer; 030import java.util.function.Function; 031import java.util.function.Predicate; 032import java.util.stream.Collectors; 033 034public class Literals { 035 036 public static final ZoneId UTC = ZoneId.of("UTC"); 037 public static final Charset UTF8 = Charset.forName("UTF-8"); 038 039 public static final char GSEP = Character.forDigit(29, 10); 040 041 /** 042 * Null safe check for equals. 043 * 044 * @param a 045 * @param b 046 * @return 047 */ 048 public static boolean areEqual(Object a, Object b) { 049 if (a == null && b == null) { 050 return true; 051 } else if (a == null && b != null) { 052 return false; 053 } else { 054 return a.equals(b); 055 } 056 } 057 058 public static String or(String obj, String defaultVal) { 059 if (empty(obj)) { 060 return defaultVal; 061 } else { 062 return obj; 063 } 064 } 065 066 067 068 069 public static Long or(Long obj, Long defaultVal) { 070 if (empty(obj)) { 071 return defaultVal; 072 } else { 073 return obj; 074 } 075 } 076 077 public static Integer or(Integer obj, Integer defaultVal) { 078 if (empty(obj)) { 079 return defaultVal; 080 } else { 081 return obj; 082 } 083 } 084 085 086 public static <T> T or(T obj, T defaultVal) { 087 if (obj == null) { 088 return defaultVal; 089 } 090 if (obj instanceof String) { 091 if (empty((String)obj)) { 092 return defaultVal; 093 } 094 } else if (emptyObject(obj)) { 095 return defaultVal; 096 } 097 return obj; 098 } 099 100 public static String firstTruthy(String...things) { 101 for (String thing: things) { 102 if (!empty(thing)) { 103 return thing; 104 } 105 } 106 return ""; 107 } 108 109 /** 110 * Imitation of Python's [2:-7] syntax that never throws errors while slicing 111 * and that accepts negative indexes 112 * 113 * @param s 114 * @param start 115 * @param end 116 * @return 117 */ 118 public static String slice(String s, int start, int end) { 119 if (end < 0) { 120 end = s.length() + end; 121 } 122 if (start < 0) { 123 start = s.length() + start; 124 } 125 if (end < start) { 126 return ""; 127 } 128 if (end > s.length()) { 129 end = s.length(); 130 } 131 return s.substring(start, end); 132 } 133 134 public static <T extends List> T slice(T l, int start, int end) { 135 if (end < 0) { 136 end = l.size() + end; 137 } 138 if (start < 0) { 139 start = l.size() + start; 140 } 141 if (end > l.size()) { 142 end = l.size(); 143 } 144 if (start > l.size() || start > end){ 145 return (T)l.subList(0, 0); 146 } 147 if (end < start) { 148 end = start; 149 } 150 151 return (T)l.subList(start, end); 152 } 153 154 155 156 public static <T extends List> T truncate(T l, int length){ 157 if (l.size() < length) { 158 return l; 159 } 160 return (T)l.subList(0, length); 161 } 162 163 public static String truncate(String s, int length) { 164 if (s == null) { 165 return ""; 166 } 167 if (s.length() < length) { 168 return s; 169 } 170 return s.substring(0, length); 171 } 172 173 /** 174 * A truncat the tries to truncate on a sentence or word boundary. 175 * 176 * @param s 177 * @param length 178 * @return 179 */ 180 public static String truncateSmart(String s, int length) { 181 if (s == null) { 182 return ""; 183 } 184 if (s.length() < length) { 185 return s; 186 } 187 s = s.substring(0, length); 188 int i = s.lastIndexOf('.'); 189 if (i > 20) { 190 s = s.substring(0, i + 1); 191 } else { 192 i = s.lastIndexOf(' '); 193 if (i > 10) { 194 s = s.substring(0, i); 195 } 196 } 197 return s; 198 } 199 200 public static long mils() { 201 return DateUtils.mils(); 202 } 203 204 public static ZonedDateTime utcNow() { 205 return DateUtils.utcNow(); 206 } 207 208 209 public static <T> T[] array(T...elems){ 210 return elems; 211 } 212 213 public static <T> Set<T> set(T...elems){ 214 return new HashSet<T>( list(elems) ); 215 } 216 217 public static <T> List<T> list(T...elems){ 218 ArrayList<T> myList = new ArrayList<>(); 219 for(T ele: elems) { 220 myList.add(ele); 221 } 222 return myList; 223 } 224 225 public static <K, V> Map<K, V> map(Map.Entry<K, V>...entries) { 226 HashMap<K, V> myMap = new HashMap<>(); 227 for (Map.Entry<K, V> entry: entries) { 228 myMap.put(entry.getKey(), entry.getValue()); 229 } 230 return myMap; 231 } 232 233 public static <K, V> Map<K, V> defaultMap(Class<? extends V> cls, Map.Entry<K, V>...entries) { 234 HashMap<K, V> myMap = new DefaultMap<>(cls); 235 for (Map.Entry<K, V> entry: entries) { 236 myMap.put(entry.getKey(), entry.getValue()); 237 } 238 return myMap; 239 } 240 241 public static <K, V> Map<K, V> defaultMap(V prototype, Map.Entry<K, V>...entries) { 242 HashMap<K, V> myMap = new DefaultMap<>(prototype); 243 for (Map.Entry<K, V> entry: entries) { 244 myMap.put(entry.getKey(), entry.getValue()); 245 } 246 return myMap; 247 } 248 249 250 public static <K, V> Map.Entry<K, V> val(K key, V value) { 251 return new AbstractMap.SimpleEntry(key, value); 252 } 253 254 public static <T> List<T> asList(T ...things) { 255 return Arrays.asList(things); 256 } 257 258 public static <T> T[] asArray(List<T> myList, Class<? extends T> cls) { 259 T[] array = (T[]) java.lang.reflect.Array.newInstance(cls, myList.size()); 260 return myList.toArray(array); 261 } 262 263 public static boolean emptyInstance(Object o) { 264 if (o == null) { 265 return true; 266 } 267 if (o instanceof IEmpty) { 268 return true; 269 } 270 return false; 271 } 272 273 public static boolean emptyObject(Object o) { 274 if (o == null) { 275 return true; 276 } 277 if (o instanceof IEmpty) { 278 return true; 279 } 280 if (o instanceof String) { 281 return "".equals(o); 282 } 283 if (o instanceof Long) { 284 return (Long)o == 0L; 285 } 286 if (o instanceof Integer) { 287 return (Integer)o == 0; 288 } 289 if (o instanceof Double) { 290 return (Double)o == 0.0; 291 } 292 if (o instanceof Float) { 293 return (Float)o == 0.0; 294 } 295 if (o instanceof Collection) { 296 return ((List)o).size() == 0; 297 } 298 if (o instanceof Map) { 299 return ((Map)o).size() == 0; 300 } 301 return false; 302 } 303 304 305 public static boolean empty(ZonedDateTime dt) { 306 if (dt == null) { 307 return true; 308 } 309 return false; 310 } 311 312 313 public static boolean empty(Object[] objects) { 314 if (objects == null) { 315 return true; 316 } 317 if (objects.length == 0){ 318 return true; 319 } 320 return false; 321 } 322 323 public static boolean empty(Collection a) { 324 if (a == null) { 325 return true; 326 } 327 if (a.size() == 0) { 328 return true; 329 } 330 return false; 331 } 332 333 public static boolean empty(Map a) { 334 if (a == null) { 335 return true; 336 } 337 if (a.size() == 0) { 338 return true; 339 } 340 return false; 341 } 342 343 public static boolean empty(CharSequence a) { 344 if (a == null) { 345 return true; 346 } 347 if (a.length() == 0) { 348 return true; 349 } 350 return false; 351 } 352 353 public static boolean empty(int a) { 354 if (a == 0) { 355 return true; 356 } 357 return false; 358 } 359 360 public static boolean empty(Integer a) { 361 if (a == null) { 362 return true; 363 } 364 if (a == 0) { 365 return true; 366 } 367 return false; 368 } 369 public static boolean empty(long a) { 370 if (a == 0) { 371 return true; 372 } 373 return false; 374 } 375 public static boolean empty(Long a) { 376 if (a == null) { 377 return true; 378 } 379 if (a == 0) { 380 return true; 381 } 382 383 return false; 384 } 385 386 387 /** 388 * Simple, Python style list comprehensions helper: 389 * 390 * List<Integer> listB = apply(listA, a -> a * a); 391 * 392 * Hat tip: http://stackoverflow.com/questions/26205409/java-8-idiomatic-way-to-apply-a-lambda-to-a-list-returning-another-list 393 * @param coll 394 * @param mapper 395 * @param <T> 396 * @param <R> 397 * @return 398 */ 399 public static <T, R> List<R> apply(Collection<T> coll, Function<? super T, ? extends R> mapper) { 400 return coll.stream().map(mapper).collect(Collectors.toList()); 401 } 402 403 /** 404 * Simple, Python style list comprehensions helper: 405 * 406 * List<Integer> listB = apply(listA, a -> !empty(a)); 407 * 408 * Hat tip: http://stackoverflow.com/questions/26205409/java-8-idiomatic-way-to-apply-a-lambda-to-a-list-returning-another-list 409 * @param coll 410 * @param filterer 411 * @param <T> 412 * @return 413 */ 414 public static <T> List<T> filter(Collection<T> coll, Predicate<? super T> filterer) { 415 return coll.stream().filter(filterer).collect(Collectors.toList()); 416 } 417 418 public static <T> List<T> filterEmpty(Collection<T> things) { 419 return filter(things, e->!emptyObject(e)); 420 } 421 422 /** 423 * safeLoop() returns an iterator that will run a maximum of "max" times before throwing an exception 424 * 425 * Use safeLoop() instead of a while loop: 426 * 427 * Do not do: 428 * 429 * while (true) { 430 * // some logic 431 * if (endCondition) { 432 * break; 433 * } 434 * } 435 * 436 * Instead do: 437 * 438 * for(int x: safeLoop(1000)) { 439 * // some logic 440 * if (endCondition) { 441 * break; 442 * } 443 * } 444 * 445 * That way if your logic goes awry, you won't hard crash the server by eating up all the CPU. 446 * 447 * while() loop bugs are some of the worst bugs you can write, because they take down the entire server, and there is 448 * no easy way to find out which while() loop triggered the problem. 449 * 450 * Thus to avoid the problem altogether, we use safeLoop(); 451 * 452 * @param max 453 * @return 454 */ 455 public static Iterable<Integer> safeLoop(int max) { 456 return new SafeLoop(max); 457 } 458 459 460 private static class SafeLoop implements Iterable { 461 462 private int max = 0; 463 464 public SafeLoop(int maxRunTimes) { 465 max = maxRunTimes; 466 } 467 @Override 468 public Iterator iterator() { 469 return new SafeLoopIterator(max); 470 } 471 472 } 473 474 private static class SafeLoopIterator implements Iterator { 475 private int index = 0; 476 private int max = 0; 477 478 public SafeLoopIterator(Integer maxRunTimes) { 479 this.max = maxRunTimes; 480 } 481 482 public boolean hasNext() { 483 if (index < max) { 484 return true; 485 } 486 throw new AppException("A safeLoop() has run more times than expected. Emergency stop triggered after " + index + " loops"); 487 //implement... 488 } 489 490 public Integer next() { 491 if (index >= max) { 492 throw new AppException("A safeLoop() has run more times than expected. Emergency stop triggered after " + index + " loops"); 493 } 494 return index++; 495 } 496 497 public void remove() { 498 //implement... if supported. 499 } 500 } 501 502 503}