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&lt;Integer&gt; listB = apply(listA, a -&gt; 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&lt;Integer&gt; listB = apply(listA, a -&gt; !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}