1 /**
2 * Copyright 2014 Internet2
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 /**
17 *
18 */
19 package edu.internet2.middleware.grouper.util;
20
21 import java.io.IOException;
22 import java.io.PrintWriter;
23 import java.io.StringWriter;
24 import java.io.UnsupportedEncodingException;
25 import java.io.Writer;
26 import java.lang.reflect.Array;
27 import java.lang.reflect.Field;
28 import java.lang.reflect.InvocationTargetException;
29 import java.lang.reflect.Method;
30 import java.math.BigDecimal;
31 import java.net.URLDecoder;
32 import java.net.URLEncoder;
33 import java.security.MessageDigest;
34 import java.security.NoSuchAlgorithmException;
35 import java.sql.SQLException;
36 import java.sql.Timestamp;
37 import java.text.DecimalFormat;
38 import java.text.ParseException;
39 import java.text.SimpleDateFormat;
40 import java.util.ArrayList;
41 import java.util.Calendar;
42 import java.util.Collection;
43 import java.util.Collections;
44 import java.util.Date;
45 import java.util.HashMap;
46 import java.util.HashSet;
47 import java.util.Iterator;
48 import java.util.LinkedHashMap;
49 import java.util.LinkedHashSet;
50 import java.util.List;
51 import java.util.Map;
52 import java.util.Set;
53 import java.util.regex.Matcher;
54 import java.util.regex.Pattern;
55
56 import javassist.util.proxy.ProxyObject;
57
58 import org.apache.commons.codec.binary.Base64;
59 import org.apache.commons.collections.keyvalue.MultiKey;
60 import org.apache.commons.lang.StringUtils;
61 import org.apache.commons.lang.exception.Nestable;
62 import org.apache.commons.logging.Log;
63
64 import edu.internet2.middleware.grouper.GrouperSession;
65 import edu.internet2.middleware.grouper.Stem;
66 import edu.internet2.middleware.grouper.StemFinder;
67 import edu.internet2.middleware.subject.Source;
68 import edu.internet2.middleware.subject.Subject;
69
70
71 /**
72 * utility methods for grouper that are safe to use in expression language. Generally these are static methods.
73 *
74 * @author mchyzer
75 *
76 */
77 public class GrouperUtilElSafe {
78
79 /**
80 * null safe to upper method
81 * @param input
82 * @return upper
83 */
84 public static String toUpperCase(String input) {
85 return input == null ? null : input.toUpperCase();
86 }
87
88 /**
89 * null safe to lower method
90 * @param input
91 * @return lower
92 */
93 public static String toLowerCase(String input) {
94 return input == null ? null : input.toLowerCase();
95 }
96
97 /**
98 * take email addresses from a textarea and turn them into semi separated
99 * @param emailAddresses can be whitespace, comma, or semi separated
100 * @return the email addresses semi separated
101 */
102 public static String normalizeEmailAddresses(String emailAddresses) {
103 if (emailAddresses == null) {
104 return null;
105 }
106 emailAddresses = StringUtils.replace(emailAddresses, ",", " ");
107 emailAddresses = StringUtils.replace(emailAddresses, ";", " ");
108 emailAddresses = StringUtils.replace(emailAddresses, "\n", " ");
109 emailAddresses = StringUtils.replace(emailAddresses, "\t", " ");
110 emailAddresses = StringUtils.replace(emailAddresses, "\r", " ");
111 emailAddresses = GrouperUtilElSafe.join(GrouperUtilElSafe.splitTrim(emailAddresses, " "), ";");
112 return emailAddresses;
113 }
114
115 /**
116 * pattern as simple validation for email. need text, @ sign, then text, dot, and text.
117 * granted this could be better, but this is a first step
118 */
119 private static Pattern emailPattern = Pattern.compile("^[^@]+@[^.]+\\..+$");
120
121 /**
122 *
123 * @param email
124 * @return true if valid, false if not
125 */
126 public static boolean validEmail(String email) {
127 Matcher matcher = emailPattern.matcher(email);
128 return matcher.matches();
129 }
130
131 /**
132 * see if a subject has an attribute
133 * @param subject
134 * @param attributeName
135 * @return true if the subject has an attribute
136 */
137 public static boolean subjectHasAttribute(Subject subject, String attributeName) {
138 if (subject == null) {
139 return false;
140 }
141 String attributeValue = subject.getAttributeValue(attributeName);
142 return !isBlank(attributeValue);
143 }
144
145 /**
146 * shorten a set if it is too long
147 * @param <T>
148 * @param theSet
149 * @param maxSize
150 * @return the new set
151 */
152 public static <T> Set<T> setShorten(Set<T> theSet, int maxSize) {
153
154 Set<T> newList = new LinkedHashSet<T>();
155
156 if (length(theSet) < maxSize) {
157 newList.addAll(theSet);
158 return newList;
159 }
160
161 //truncate the list
162 int i=0;
163
164 //TODO test this logic
165 for (T t : theSet) {
166
167 if (i>=maxSize) {
168 break;
169 }
170
171 newList.add(t);
172 i++;
173 }
174 return newList;
175 }
176
177 /**
178 *
179 * @param number e.g. 12345678
180 * @return the string, e.g. 12,345,678
181 */
182 public static String formatNumberWithCommas(Long number) {
183 if (number == null) {
184 return "null";
185 }
186 DecimalFormat df = new DecimalFormat();
187 return df.format(number);
188 }
189
190 /**
191 * compare null safe
192 * @param first
193 * @param second
194 * @return 0 for equal, 1 for greater, -1 for less
195 */
196 public static int compare(Comparable first, Comparable second) {
197 if (first == second) {
198 return 0;
199 }
200 if (first == null) {
201 return -1;
202 }
203 if (second == null) {
204 return 1;
205 }
206 return first.compareTo(second);
207 }
208
209 /**
210 * turn some strings into a map
211 * @param strings
212 * @return the map (never null)
213 */
214 public static Map<String, String> toMap(String... strings) {
215 Map<String, String> map = new LinkedHashMap<String, String>();
216 if (strings != null) {
217 if (strings.length % 2 != 0) {
218 throw new RuntimeException("Must pass in an even number of strings: " + strings.length);
219 }
220 for (int i=0;i<strings.length;i+=2) {
221 map.put(strings[i], strings[i+1]);
222 }
223 }
224 return map;
225 }
226
227 /**
228 * turn some strings into a map
229 * @param stringObjects is an array of String,Object,String,Object etc where the
230 * Strings are the key, and the Object is the value
231 * @return the map (never null)
232 */
233 public static Map<String, Object> toStringObjectMap(Object... stringObjects) {
234 Map<String, Object> map = new LinkedHashMap<String, Object>();
235 if (stringObjects != null) {
236 if (stringObjects.length % 2 != 0) {
237 throw new RuntimeException("Must pass in an even number of strings: " + stringObjects.length);
238 }
239 for (int i=0;i<stringObjects.length;i+=2) {
240 String key = (String)stringObjects[i];
241 map.put(key, stringObjects[i+1]);
242 }
243 }
244 return map;
245 }
246
247 /**
248 * convert millis to friendly string
249 * @param duration
250 * @return the friendly string
251 */
252 public static String convertMillisToFriendlyString(Integer duration) {
253 if (duration == null) {
254 return convertMillisToFriendlyString((Long)null);
255 }
256 return convertMillisToFriendlyString(new Long(duration.intValue()));
257 }
258
259 /**
260 * convert millis to friendly string
261 * @param duration
262 * @return the friendly string
263 */
264 public static String convertMillisToFriendlyString(Long duration) {
265
266 if (duration == null) {
267 return "";
268 }
269
270 if (duration < 1000) {
271 return duration + "ms";
272 }
273
274 long ms = duration % 1000;
275 duration = duration / 1000;
276 long s = duration % 60;
277 duration = duration / 60;
278
279 if (duration == 0) {
280 return s + "s, " + ms + "ms";
281 }
282
283 long m = duration % 60;
284 duration = duration / 60;
285
286 if (duration == 0) {
287 return m + "m, " + s + "s, " + ms + "ms";
288 }
289
290 long h = duration % 24;
291 duration = duration / 24;
292
293 if (duration == 0) {
294 return h + "h, " + m + "m, " + s + "s, " + ms + "ms";
295 }
296
297 long d = duration;
298
299 return d + "d, " + h + "h, " + m + "m, " + s + "s, " + ms + "ms";
300 }
301
302 /**
303 * return the arg after the argBefore, or null if not there, or exception
304 * if argBefore is not found
305 * @param args
306 * @param argBefore
307 * @return the arg
308 */
309 public static String argAfter(String[] args, String argBefore) {
310 if (length(args) <= 1) {
311 return null;
312 }
313 int argBeforeIndex = -1;
314 for (int i=0;i<args.length;i++) {
315 if (equals(args[i], argBefore)) {
316 argBeforeIndex = i;
317 break;
318 }
319 }
320 if (argBeforeIndex == -1) {
321 throw new RuntimeException("Cant find arg before");
322 }
323 if (argBeforeIndex < args.length - 1) {
324 return args[argBeforeIndex + 1];
325 }
326 return null;
327 }
328
329 /**
330 * append and maybe put a separator in there
331 * @param result
332 * @param separatorIfResultNotEmpty
333 * @param stringToAppend
334 */
335 public static void append(StringBuilder result,
336 String separatorIfResultNotEmpty, String stringToAppend) {
337 if (result.length() != 0) {
338 result.append(separatorIfResultNotEmpty);
339 }
340 result.append(stringToAppend);
341 }
342
343 /**
344 *
345 */
346 public static final String LOG_ERROR = "Error trying to make parent dirs for logger or logging first statement, check to make " +
347 "sure you have proper file permissions, and that your servlet container is giving " +
348 "your app rights to access the log directory (e.g. for tomcat set TOMCAT5_SECURITY=no), g" +
349 "oogle it for more info";
350
351 /**
352 * The number of bytes in a kilobyte.
353 */
354 public static final long ONE_KB = 1024;
355
356 /**
357 * The number of bytes in a megabyte.
358 */
359 public static final long ONE_MB = ONE_KB * ONE_KB;
360
361 /**
362 * The number of bytes in a gigabyte.
363 */
364 public static final long ONE_GB = ONE_KB * ONE_MB;
365
366 /**
367 * Returns a human-readable version of the file size (original is in
368 * bytes).
369 *
370 * @param size The number of bytes.
371 * @return A human-readable display value (includes units).
372 * @todo need for I18N?
373 */
374 public static String byteCountToDisplaySize(long size) {
375 String displaySize;
376
377 if (size / ONE_GB > 0) {
378 displaySize = String.valueOf(size / ONE_GB) + " GB";
379 } else if (size / ONE_MB > 0) {
380 displaySize = String.valueOf(size / ONE_MB) + " MB";
381 } else if (size / ONE_KB > 0) {
382 displaySize = String.valueOf(size / ONE_KB) + " KB";
383 } else {
384 displaySize = String.valueOf(size) + " bytes";
385 }
386
387 return displaySize;
388 }
389 /**
390 * return the suffix after a char. If the char doesnt exist, just return the string
391 * @param input string
392 * @param theChar char
393 * @return new string
394 */
395 public static String suffixAfterChar(String input, char theChar) {
396 if (input == null) {
397 return null;
398 }
399 //get the real type off the end
400 int lastIndex = input.lastIndexOf(theChar);
401 if (lastIndex > -1) {
402 input = input.substring(lastIndex + 1, input.length());
403 }
404 return input;
405 }
406
407 /**
408 * empty map
409 */
410 private static final Map EMPTY_MAP = Collections.unmodifiableMap(new HashMap());
411
412 /**
413 * sleep, if interrupted, throw runtime
414 * @param millis
415 */
416 public static void sleep(long millis) {
417 try {
418 Thread.sleep(millis);
419 } catch (InterruptedException ie) {
420 throw new RuntimeException(ie);
421 }
422 }
423
424 /**
425 *
426 * @param seconds
427 */
428 public static void sleepWithStdoutCountdown(int seconds) {
429 for (int i=seconds;i>0;i--) {
430 System.out.println("Sleeping: " + i);
431 sleep(1000);
432 }
433 }
434
435 /**
436 * encrypt a message to SHA
437 * @param plaintext
438 * @return the hash
439 */
440 public synchronized static String encryptSha(String plaintext) {
441 MessageDigest md = null;
442 try {
443 md = MessageDigest.getInstance("SHA"); //step 2
444 } catch (NoSuchAlgorithmException e) {
445 throw new RuntimeException(e);
446 }
447 try {
448 md.update(plaintext.getBytes("UTF-8")); //step 3
449 } catch (UnsupportedEncodingException e) {
450 throw new RuntimeException(e);
451 }
452 byte raw[] = md.digest(); //step 4
453 byte[] encoded = Base64.encodeBase64(raw); //step 5
454 String hash = new String(encoded);
455 //String hash = (new BASE64Encoder()).encode(raw); //step 5
456 return hash; //step 6
457 }
458
459 /**
460 * get a unique string identifier based on the current time,
461 * this is not globally unique, just unique for as long as this
462 * server is running...
463 *
464 * @return String
465 */
466 public static String uniqueId() {
467 //this needs to be threadsafe since we are using a static field
468 synchronized (GrouperUtilElSafe.class) {
469 lastId = incrementStringInt(lastId);
470 }
471
472 return String.valueOf(lastId);
473 }
474
475 /**
476 * make sure a array is non null. If null, then return an empty array.
477 * @param <T>
478 * @param array
479 * @param theClass to make array from
480 * @return the list or empty list if null
481 */
482 @SuppressWarnings({ "unchecked", "cast" })
483 public static <T> T[] nonNull(T[] array, Class<?> theClass) {
484 if (int.class.equals(theClass)) {
485 return (T[])(Object)new int[0];
486 }
487 if (float.class.equals(theClass)) {
488 return (T[])(Object)new float[0];
489 }
490 if (double.class.equals(theClass)) {
491 return (T[])(Object)new double[0];
492 }
493 if (short.class.equals(theClass)) {
494 return (T[])(Object)new short[0];
495 }
496 if (long.class.equals(theClass)) {
497 return (T[])(Object)new long[0];
498 }
499 if (byte.class.equals(theClass)) {
500 return (T[])(Object)new byte[0];
501 }
502 if (boolean.class.equals(theClass)) {
503 return (T[])(Object)new boolean[0];
504 }
505 if (char.class.equals(theClass)) {
506 return (T[])(Object)new char[0];
507 }
508 return array == null ? ((T[])Array.newInstance(theClass, 0)) : array;
509 }
510
511 /**
512 * strip the suffix off
513 * @param string
514 * @param suffix
515 * @return the string without the suffix
516 */
517 public static String stripSuffix(String string, String suffix) {
518 if (string == null || suffix == null) {
519 return string;
520 }
521 if (string.endsWith(suffix)) {
522 return string.substring(0, string.length() - suffix.length());
523 }
524 return string;
525 }
526
527 /**
528 * get the prefix or suffix of a string based on a separator
529 *
530 * @param startString
531 * is the string to start with
532 * @param separator
533 * is the separator to split on
534 * @param isPrefix
535 * if thre prefix or suffix should be returned
536 *
537 * @return the prefix or suffix, if the separator isnt there, return the
538 * original string
539 */
540 public static String prefixOrSuffix(String startString, String separator,
541 boolean isPrefix) {
542 String prefixOrSuffix = null;
543
544 //no nulls
545 if (startString == null) {
546 return startString;
547 }
548
549 //where is the separator
550 int separatorIndex = startString.indexOf(separator);
551
552 //if none exists, dont proceed
553 if (separatorIndex == -1) {
554 return startString;
555 }
556
557 //maybe the separator isnt on character
558 int separatorLength = separator.length();
559
560 if (isPrefix) {
561 prefixOrSuffix = startString.substring(0, separatorIndex);
562 } else {
563 prefixOrSuffix = startString.substring(separatorIndex + separatorLength,
564 startString.length());
565 }
566
567 return prefixOrSuffix;
568 }
569
570 /**
571 * <pre>
572 * this method will indent xml or json.
573 * this is for logging or documentations purposes only and should
574 * not be used for a production use (since it is not 100% tested
575 * or compliant with all constructs like xml CDATA
576 *
577 * For xml, assumes elements either have text or sub elements, not both.
578 * No cdata, nothing fancy.
579 *
580 * If the input is <a><b><c>hey</c><d><e>there</e></d></b></a>
581 * It would output:
582 * <a>
583 * <b>
584 * <c>hey</c>
585 * <d>
586 * <e>there</e>
587 * </d>
588 * </b>
589 * </a>
590 *
591 * For , if the input is: {"a":{"b\"b":{"c\\":"d"},"e":"f","g":["h":"i"]}}
592 * It would output:
593 * {
594 * "a":{
595 * "b\"b":{
596 * "c\\":"d"
597 * },
598 * "e":"f",
599 * "g":[
600 * "h":"i"
601 * ]
602 * }
603 * }
604 *
605 *
606 * <pre>
607 * @param string
608 * @param failIfTypeNotFound
609 * @return the indented string, 2 spaces per line
610 */
611 public static String indent(String string, boolean failIfTypeNotFound) {
612 if (string == null) {
613 return null;
614 }
615 string = trim(string);
616 if (string.startsWith("<")) {
617 //this is xml
618 return new XmlIndenter(string).result();
619 }
620 if (string.startsWith("{")) {
621 return new JsonIndenter(string).result();
622 }
623 if (!failIfTypeNotFound) {
624 //just return if cant indent
625 return string;
626 }
627 throw new RuntimeException("Cant find type of string: " + string);
628 }
629
630 /**
631 * get the extension from name. if name is a:b:c, name is c
632 * @param name
633 * @return the name
634 */
635 public static String extensionFromName(String name) {
636 if (isBlank(name)) {
637 return name;
638 }
639 int lastColonIndex = name.lastIndexOf(':');
640 if (lastColonIndex == -1) {
641 return name;
642 }
643 String extension = name.substring(lastColonIndex+1);
644 return extension;
645 }
646
647 /**
648 * get the parent stem name from name. if already a root stem
649 * then just return null. e.g. if the name is a:b:c then
650 * the return value is a:b
651 * @param name
652 * @return the parent stem name or null if none
653 */
654 public static String parentStemNameFromName(String name) {
655 return parentStemNameFromName(name, true);
656 }
657 /**
658 * get the parent stem name from name. if already a root stem
659 * then just return null. e.g. if the name is a:b:c then
660 * the return value is a:b
661 * @param name
662 * @param nullForRoot null for root, otherwise colon
663 * @return the parent stem name or null if none
664 */
665 public static String parentStemNameFromName(String name, boolean nullForRoot) {
666
667 //null safe
668 if (GrouperUtilElSafe.isBlank(name)) {
669 return name;
670 }
671
672 int lastColonIndex = name.lastIndexOf(':');
673 if (lastColonIndex == -1) {
674
675 if (nullForRoot) {
676 return null;
677 }
678 return ":";
679 }
680 String parentStemName = name.substring(0,lastColonIndex);
681 return parentStemName;
682
683 }
684
685 /**
686 * return the string or the other if the first is blank
687 * @param string
688 * @param defaultStringIfBlank
689 * @return the string or the default one
690 */
691 public static String defaultIfBlank(String string, String defaultStringIfBlank) {
692 return isBlank(string) ? defaultStringIfBlank : string;
693 }
694
695 /**
696 * genericized method to see if first is null, if so then return second, else first.
697 * @param <T>
698 * @param theValue first input
699 * @param defaultIfTheValueIsNull second input
700 * @return the first if not null, second if no
701 */
702 public static <T> T defaultIfNull(T theValue, T defaultIfTheValueIsNull) {
703 return theValue != null ? theValue : defaultIfTheValueIsNull;
704 }
705
706 /**
707 * add each element of listToAdd if it is not already in list
708 * @param <T>
709 * @param list to add to
710 * @param listToAdd each element will be added to list, or null if none
711 */
712 public static <T> void addIfNotThere(Collection<T> list, Collection<T> listToAdd) {
713 //maybe nothing to do
714 if (listToAdd == null) {
715 return;
716 }
717 for (T t : listToAdd) {
718 if (!list.contains(t)) {
719 list.add(t);
720 }
721 }
722 }
723
724
725 /**
726 * print out various types of objects
727 *
728 * @param object
729 * @param maxChars is where it should stop when figuring out object. note, result might be longer than max...
730 * need to abbreviate when back
731 * @param result is where to append to
732 */
733 private static void toStringForLogHelper(Object object, int maxChars, StringBuilder result) {
734
735 try {
736 if (object == null) {
737 result.append("null");
738 } else if (object.getClass().isArray()) {
739 // handle arrays
740 int length = Array.getLength(object);
741 if (length == 0) {
742 result.append("Empty array");
743 } else {
744 result.append("Array size: ").append(length).append(": ");
745 for (int i = 0; i < length; i++) {
746 result.append("[").append(i).append("]: ").append(
747 Array.get(object, i)).append("\n");
748 if (maxChars != -1 && result.length() > maxChars) {
749 return;
750 }
751 }
752 }
753 } else if (object instanceof Collection) {
754 //give size and type if collection
755 Collection<Object> collection = (Collection<Object>) object;
756 int collectionSize = collection.size();
757 if (collectionSize == 0) {
758 result.append("Empty ").append(object.getClass().getSimpleName());
759 } else {
760 result.append(object.getClass().getSimpleName()).append(" size: ").append(collectionSize).append(": ");
761 int i=0;
762 for (Object collectionObject : collection) {
763 result.append("[").append(i).append("]: ").append(
764 collectionObject).append("\n");
765 if (maxChars != -1 && result.length() > maxChars) {
766 return;
767 }
768 i++;
769 }
770 }
771 } else {
772 result.append(object.toString());
773 }
774 } catch (Exception e) {
775 result.append("<<exception>> ").append(object.getClass()).append(":\n")
776 .append(getFullStackTrace(e)).append("\n");
777 }
778 }
779
780 /**
781 * convert a set to a string (comma separate)
782 * @param collection
783 * @return the String
784 */
785 public static String collectionToString(Collection collection) {
786 if (collection == null) {
787 return "null";
788 }
789 if (collection.size() == 0) {
790 return "empty";
791 }
792 StringBuilder result = new StringBuilder();
793 boolean first = true;
794 for (Object object : collection) {
795 if (!first) {
796 result.append(", ");
797 }
798 first = false;
799 result.append(object);
800 }
801 return result.toString();
802
803 }
804 /**
805 * convert a set to a string (comma separate)
806 * @param set
807 * @return the String
808 */
809 public static String setToString(Set set) {
810 return collectionToString(set);
811 }
812
813 /**
814 * convert a set to a string (comma separate)
815 * @param map
816 * @return the String
817 * @deprecated use mapToString(map)
818 */
819 @Deprecated
820 public static String MapToString(Map map) {
821 return mapToString(map);
822 }
823
824 /**
825 * convert a set to a string (comma separate)
826 * @param map
827 * @return the String
828 */
829 public static String mapToString(Map map) {
830 if (map == null) {
831 return "null";
832 }
833 if (map.size() == 0) {
834 return "empty";
835 }
836 StringBuilder result = new StringBuilder();
837 boolean first = true;
838 for (Object object : map.keySet()) {
839 if (!first) {
840 result.append(", ");
841 }
842 first = false;
843 result.append(object).append(": ").append(map.get(object));
844 }
845 return result.toString();
846 }
847
848 /**
849 * print out various types of objects
850 *
851 * @param object
852 * @return the string value
853 */
854 public static String toStringForLog(Object object) {
855 StringBuilder result = new StringBuilder();
856 toStringForLogHelper(object, -1, result);
857 return result.toString();
858 }
859
860 /**
861 * print out various types of objects
862 *
863 * @param object
864 * @param maxChars is the max chars that should be returned (abbreviate if longer), or -1 for any amount
865 * @return the string value
866 */
867 public static String toStringForLog(Object object, int maxChars) {
868 StringBuilder result = new StringBuilder();
869 toStringForLogHelper(object, -1, result);
870 String resultString = result.toString();
871 if (maxChars != -1) {
872 return abbreviate(resultString, maxChars);
873 }
874 return resultString;
875 }
876
877 /**
878 * If batching this is the number of batches
879 * @param count is size of set
880 * @param batchSize
881 * @return the number of batches
882 */
883 public static int batchNumberOfBatches(int count, int batchSize) {
884 //not sure why this would be 0...
885 if (batchSize == 0) {
886 return 0;
887 }
888 int batches = 1 + ((count - 1) / batchSize);
889 return batches;
890
891 }
892
893 /**
894 * If batching this is the number of batches
895 * @param collection
896 * @param batchSize
897 * @return the number of batches
898 */
899 public static int batchNumberOfBatches(Collection<?> collection, int batchSize) {
900 int arrraySize = length(collection);
901 return batchNumberOfBatches(arrraySize, batchSize);
902
903 }
904
905 /**
906 * retrieve a batch by 0 index. Will return an array of size batchSize or
907 * the remainder. the array will be full of elements. Note, this requires an
908 * ordered input (so use linkedhashset not hashset if doing sets)
909 * @param <T> template type
910 * @param collection
911 * @param batchSize
912 * @param batchIndex
913 * @return the list
914 * This never returns null, only empty list
915 */
916 @SuppressWarnings("unchecked")
917 public static <T> List<T> batchList(Collection<T> collection, int batchSize,
918 int batchIndex) {
919
920 int numberOfBatches = batchNumberOfBatches(collection, batchSize);
921 int arraySize = length(collection);
922
923 // short circuit
924 if (arraySize == 0) {
925 return new ArrayList<T>();
926 }
927
928 List<T> theBatchObjects = new ArrayList<T>();
929
930 // lets get the type of the first element if possible
931 // Object first = get(arrayOrCollection, 0);
932 //
933 // Class theType = first == null ? Object.class : first.getClass();
934
935 // if last batch
936 if (batchIndex == numberOfBatches - 1) {
937
938 // needs to work to 1-n
939 //int thisBatchSize = 1 + ((arraySize - 1) % batchSize);
940
941 int collectionIndex = 0;
942 for (T t : collection) {
943 if (collectionIndex++ < batchIndex * batchSize) {
944 continue;
945 }
946 //just copy the rest
947 //if (collectionIndex >= (batchIndex * batchSize) + arraySize) {
948 // break;
949 //}
950 //we are in the copy mode
951 theBatchObjects.add(t);
952 }
953
954 } else {
955 // if non-last batch
956 //int newIndex = 0;
957 int collectionIndex = 0;
958 for (T t : collection) {
959 if (collectionIndex < batchIndex * batchSize) {
960 collectionIndex++;
961 continue;
962 }
963 //done with batch
964 if (collectionIndex >= (batchIndex + 1) * batchSize) {
965 break;
966 }
967 theBatchObjects.add(t);
968 collectionIndex++;
969 }
970 }
971 return theBatchObjects;
972 }
973 /**
974 * split a string based on a separator into an array, and trim each entry (see
975 * the Commons Util trim() for more details)
976 *
977 * @param input
978 * is the delimited input to split and trim
979 * @param separator
980 * is what to split on
981 *
982 * @return the array of items after split and trimmed, or null if input is null. will be trimmed to empty
983 */
984 public static String[] splitTrim(String input, String separator) {
985 return splitTrim(input, separator, true);
986 }
987
988 /**
989 * split a string based on a separator into an array, and trim each entry (see
990 * the Commons Util trim() for more details)
991 *
992 * @param input
993 * is the delimited input to split and trim
994 * @param separator
995 * is what to split on
996 *
997 * @return the list of items after split and trimmed, or null if input is null. will be trimmed to empty
998 */
999 public static List<String> splitTrimToList(String input, String separator) {
1000 if (isBlank(input)) {
1001 return null;
1002 }
1003 String[] array = splitTrim(input, separator);
1004 return toList(array);
1005 }
1006
1007 /**
1008 * split a string based on a separator into an array, and trim each entry (see
1009 * the Commons Util trim() for more details)
1010 *
1011 * @param input
1012 * is the delimited input to split and trim
1013 * @param separator
1014 * is what to split on
1015 *
1016 * @return the set of items after split and trimmed, or null if input is null. will be trimmed to empty
1017 */
1018 public static Set<String> splitTrimToSet(String input, String separator) {
1019 if (isBlank(input)) {
1020 return null;
1021 }
1022 String[] array = splitTrim(input, separator);
1023 return toSet(array);
1024 }
1025
1026 /**
1027 * split a string based on a separator into an array, and trim each entry (see
1028 * the Commons Util trim() for more details)
1029 *
1030 * @param input
1031 * is the delimited input to split and trim
1032 * @param separator
1033 * is what to split on
1034 * @param treatAdjacentSeparatorsAsOne
1035 * @return the array of items after split and trimmed, or null if input is null. will be trimmed to empty
1036 */
1037 public static String[] splitTrim(String input, String separator, boolean treatAdjacentSeparatorsAsOne) {
1038 if (isBlank(input)) {
1039 return null;
1040 }
1041
1042 //first split
1043 String[] items = treatAdjacentSeparatorsAsOne ? splitByWholeSeparator(input, separator) :
1044 split(input, separator);
1045
1046 //then trim
1047 for (int i = 0; (items != null) && (i < items.length); i++) {
1048 items[i] = trim(items[i]);
1049 }
1050
1051 //return the array
1052 return items;
1053 }
1054
1055 /**
1056 * escape url chars (e.g. a # is %23)
1057 * @param string input
1058 * @return the encoded string
1059 */
1060 public static String escapeUrlEncode(String string) {
1061 String result = null;
1062 try {
1063 result = URLEncoder.encode(string, "UTF-8");
1064 } catch (UnsupportedEncodingException ex) {
1065 throw new RuntimeException("UTF-8 not supported", ex);
1066 }
1067 return result;
1068 }
1069
1070 /**
1071 * unescape url chars (e.g. a space is %20)
1072 * @param string input
1073 * @return the encoded string
1074 */
1075 public static String escapeUrlDecode(String string) {
1076 String result = null;
1077 try {
1078 result = URLDecoder.decode(string, "UTF-8");
1079 } catch (UnsupportedEncodingException ex) {
1080 throw new RuntimeException("UTF-8 not supported", ex);
1081 }
1082 return result;
1083 }
1084
1085 /**
1086 * make sure a list is non null. If null, then return an empty list
1087 * @param <T>
1088 * @param list
1089 * @return the list or empty list if null
1090 */
1091 public static <T> List<T> nonNull(List<T> list) {
1092 return list == null ? new ArrayList<T>() : list;
1093 }
1094
1095 /**
1096 * make sure a collection is non null. If null, then return an empty list
1097 * @param <T>
1098 * @param list
1099 * @return the list or empty list if null
1100 */
1101 public static <T> Collection<T> nonNull(Collection<T> list) {
1102 return list == null ? new ArrayList<T>() : list;
1103 }
1104
1105 /**
1106 * make sure a list is non null. If null, then return an empty set
1107 * @param <T>
1108 * @param set
1109 * @return the set or empty set if null
1110 */
1111 public static <T> Set<T> nonNull(Set<T> set) {
1112 return set == null ? new HashSet<T>() : set;
1113 }
1114
1115 /**
1116 * make sure it is non null, if null, then give new map
1117 *
1118 * @param <K> key of map
1119 * @param <V> value of map
1120 * @param map is map
1121 * @return set non null
1122 */
1123 public static <K,V> Map<K,V> nonNull(Map<K,V> map) {
1124 return map == null ? new HashMap<K,V>() : map;
1125 }
1126
1127 /**
1128 * return a list of objects from varargs. Though if there is one
1129 * object, and it is a list, return it.
1130 *
1131 * @param <T>
1132 * template type of the objects
1133 * @param objects
1134 * @return the list or null if objects is null
1135 */
1136 public static <T> List<T> toList(T... objects) {
1137 if (objects == null) {
1138 return null;
1139 }
1140 if (objects.length == 1 && objects[0] instanceof List) {
1141 return (List<T>)objects[0];
1142 }
1143
1144 List<T> result = new ArrayList<T>();
1145 for (T object : objects) {
1146 result.add(object);
1147 }
1148 return result;
1149 }
1150
1151 /**
1152 * return a list of objects from varargs. Though if there is one
1153 * object, and it is a list, return it.
1154 *
1155 * @param <T>
1156 * template type of the objects
1157 * @param objects
1158 * @return the list or null if objects is null
1159 */
1160 public static List<Object> toListObject(Object... objects) {
1161 if (objects == null) {
1162 return null;
1163 }
1164 List<Object> result = new ArrayList<Object>();
1165 for (Object object : objects) {
1166 result.add(object);
1167 }
1168 return result;
1169 }
1170
1171
1172 /**
1173 * return a set of objects from varargs.
1174 *
1175 * @param <T> template type of the objects
1176 * @param objects
1177 * @return the set
1178 */
1179 public static <T> Set<T> toSet(T... objects) {
1180 if (objects == null) {
1181 return null;
1182 }
1183 Set<T> result = new LinkedHashSet<T>();
1184 for (T object : objects) {
1185 result.add(object);
1186 }
1187 return result;
1188 }
1189
1190 /**
1191 * return a set of string
1192 *
1193 * @param <T> template type of the objects
1194 * @param object
1195 * @return the set
1196 */
1197 public static <T> Set<T> toSetObject(T object) {
1198 if (object == null) {
1199 return null;
1200 }
1201 Set<T> result = new LinkedHashSet<T>();
1202 result.add(object);
1203 return result;
1204 }
1205
1206 /**
1207 * string format of dates
1208 */
1209 public static final String DATE_FORMAT = "yyyyMMdd";
1210
1211 /**
1212 * string format of dates for file names
1213 */
1214 public static final String TIMESTAMP_FILE_FORMAT = "yyyy_MM_dd__HH_mm_ss_SSS";
1215
1216 /**
1217 * timestamp format, make sure to synchronize
1218 */
1219 final static SimpleDateFormat timestampFileFormat = new SimpleDateFormat(TIMESTAMP_FILE_FORMAT);
1220
1221 /**
1222 * string format of dates
1223 */
1224 public static final String DATE_FORMAT2 = "yyyy/MM/dd";
1225
1226 /**
1227 * format including minutes and seconds: yyyy/MM/dd HH:mm:ss
1228 */
1229 public static final String DATE_MINUTES_SECONDS_FORMAT = "yyyy/MM/dd HH:mm:ss";
1230
1231 /**
1232 * format including minutes and seconds: yyyyMMdd HH:mm:ss
1233 */
1234 public static final String DATE_MINUTES_SECONDS_NO_SLASH_FORMAT = "yyyyMMdd HH:mm:ss";
1235
1236 /**
1237 * format on screen of config for milestone: yyyy/MM/dd HH:mm:ss.SSS
1238 */
1239 public static final String TIMESTAMP_FORMAT = "yyyy/MM/dd HH:mm:ss.SSS";
1240
1241 /**
1242 * format on screen of config for milestone: yyyyMMdd HH:mm:ss.SSS
1243 */
1244 public static final String TIMESTAMP_NO_SLASH_FORMAT = "yyyyMMdd HH:mm:ss.SSS";
1245
1246 /**
1247 * date format, make sure to synchronize
1248 */
1249 final static SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT);
1250
1251 /**
1252 * date format, make sure to synchronize
1253 */
1254 final static SimpleDateFormat dateFormat2 = new SimpleDateFormat(DATE_FORMAT2);
1255
1256 /**
1257 * synchronize code that uses this standard formatter for dates with minutes and seconds
1258 */
1259 final static SimpleDateFormat dateMinutesSecondsFormat = new SimpleDateFormat(
1260 DATE_MINUTES_SECONDS_FORMAT);
1261
1262 /**
1263 * synchronize code that uses this standard formatter for dates with minutes and seconds
1264 */
1265 final static SimpleDateFormat dateMinutesSecondsNoSlashFormat = new SimpleDateFormat(
1266 DATE_MINUTES_SECONDS_NO_SLASH_FORMAT);
1267
1268 /**
1269 * <pre> format: yyyy/MM/dd HH:mm:ss.SSS synchronize code that uses this standard formatter for timestamps </pre>
1270 */
1271 final static SimpleDateFormat timestampFormat = new SimpleDateFormat(TIMESTAMP_FORMAT);
1272
1273 /**
1274 * synchronize code that uses this standard formatter for timestamps
1275 */
1276 final static SimpleDateFormat timestampNoSlashFormat = new SimpleDateFormat(
1277 TIMESTAMP_NO_SLASH_FORMAT);
1278
1279 /**
1280 * If false, throw an assertException, and give a reason
1281 *
1282 * @param isTrue
1283 * @param reason
1284 */
1285 public static void assertion(boolean isTrue, String reason) {
1286 if (!isTrue) {
1287 throw new RuntimeException(reason);
1288 }
1289
1290 }
1291
1292 /**
1293 * Field lastId.
1294 */
1295 private static char[] lastId = convertLongToStringSmall(new Date().getTime())
1296 .toCharArray();
1297
1298 /**
1299 * null safe iterator getter if the type if collection
1300 *
1301 * @param collection
1302 * @return the iterator
1303 */
1304 public static Iterator iterator(Object collection) {
1305 if (collection == null) {
1306 return null;
1307 }
1308 // array list doesnt need an iterator
1309 if (collection instanceof Collection
1310 && !(collection instanceof ArrayList)) {
1311 return ((Collection) collection).iterator();
1312 }
1313 return null;
1314 }
1315
1316 /**
1317 * Null safe array length or map
1318 *
1319 * @param arrayOrCollection
1320 * @return the length of the array (0 for null)
1321 */
1322 public static int length(Object arrayOrCollection) {
1323 if (arrayOrCollection == null) {
1324 return 0;
1325 }
1326 if (arrayOrCollection.getClass().isArray()) {
1327 return Array.getLength(arrayOrCollection);
1328 }
1329 if (arrayOrCollection instanceof Collection) {
1330 return ((Collection) arrayOrCollection).size();
1331 }
1332 if (arrayOrCollection instanceof Map) {
1333 return ((Map) arrayOrCollection).size();
1334 }
1335 // simple non array non collection object
1336 return 1;
1337 }
1338
1339 /**
1340 * If array, get the element based on index, if Collection, get it based on
1341 * iterator.
1342 *
1343 * @param arrayOrCollection
1344 * @param iterator
1345 * @param index
1346 * @return the object
1347 */
1348 public static Object next(Object arrayOrCollection, Iterator iterator,
1349 int index) {
1350 if (arrayOrCollection.getClass().isArray()) {
1351 return Array.get(arrayOrCollection, index);
1352 }
1353 if (arrayOrCollection instanceof ArrayList) {
1354 return ((ArrayList) arrayOrCollection).get(index);
1355 }
1356 if (arrayOrCollection instanceof Collection) {
1357 return iterator.next();
1358 }
1359 // simple object
1360 if (0 == index) {
1361 return arrayOrCollection;
1362 }
1363 throw new RuntimeException("Invalid class type: "
1364 + arrayOrCollection.getClass().getName());
1365 }
1366
1367 /**
1368 * Remove the iterator or index
1369 *
1370 * @param arrayOrCollection
1371 * @param index
1372 * @return the object list or new array
1373 */
1374 public static Object remove(Object arrayOrCollection,
1375 int index) {
1376 return remove(arrayOrCollection, null, index);
1377 }
1378
1379 /**
1380 * Remove the iterator or index
1381 *
1382 * @param arrayOrCollection
1383 * @param iterator
1384 * @param index
1385 * @return the object list or new array
1386 */
1387 public static Object remove(Object arrayOrCollection, Iterator iterator,
1388 int index) {
1389
1390 //if theres an iterator, just use that
1391 if (iterator != null) {
1392 iterator.remove();
1393 return arrayOrCollection;
1394 }
1395 if (arrayOrCollection.getClass().isArray()) {
1396 int newLength = Array.getLength(arrayOrCollection) - 1;
1397 Object newArray = Array.newInstance(arrayOrCollection.getClass().getComponentType(), newLength);
1398 if (newLength == 0) {
1399 return newArray;
1400 }
1401 if (index > 0) {
1402 System.arraycopy(arrayOrCollection, 0, newArray, 0, index);
1403 }
1404 if (index < newLength) {
1405 System.arraycopy(arrayOrCollection, index+1, newArray, index, newLength - index);
1406 }
1407 return newArray;
1408 }
1409 if (arrayOrCollection instanceof List) {
1410 ((List)arrayOrCollection).remove(index);
1411 return arrayOrCollection;
1412 } else if (arrayOrCollection instanceof Collection) {
1413 //this should work unless there are duplicates or something weird
1414 ((Collection)arrayOrCollection).remove(get(arrayOrCollection, index));
1415 return arrayOrCollection;
1416 }
1417 throw new RuntimeException("Invalid class type: "
1418 + arrayOrCollection.getClass().getName());
1419 }
1420
1421 /**
1422 * print the simple names of a list of classes
1423 * @param object
1424 * @return the simple names
1425 */
1426 public static String classesString(Object object) {
1427 StringBuilder result = new StringBuilder();
1428 if (object.getClass().isArray()) {
1429 int length = Array.getLength(object);
1430 for (int i=0;i<length;i++) {
1431 result.append(((Class)Array.get(object, i)).getSimpleName());
1432 if (i < length-1) {
1433 result.append(", ");
1434 }
1435 }
1436 return result.toString();
1437 }
1438
1439 throw new RuntimeException("Not implemented: " + className(object));
1440 }
1441
1442 /**
1443 * null safe classname method, max out at 20
1444 *
1445 * @param object
1446 * @return the classname
1447 */
1448 public static String classNameCollection(Object object) {
1449 if (object == null) {
1450 return null;
1451 }
1452 StringBuffer result = new StringBuffer();
1453
1454 Iterator iterator = iterator(object);
1455 int length = length(object);
1456 for (int i = 0; i < length && i < 20; i++) {
1457 result.append(className(next(object, iterator, i)));
1458 if (i != length - 1) {
1459 result.append(", ");
1460 }
1461 }
1462 return result.toString();
1463 }
1464
1465 /**
1466 * null safe classname method, gets the unenhanced name
1467 *
1468 * @param object
1469 * @return the classname
1470 */
1471 public static String className(Object object) {
1472 return object == null ? null : unenhanceClass(object.getClass())
1473 .getName();
1474 }
1475
1476 /** pass this in the invokeOn to signify no params */
1477 private static final Object NO_PARAMS = new Object();
1478
1479 /**
1480 * Convert a list to an array with the type of the first element e.g. if it
1481 * is a list of Person objects, then the array is Person[]
1482 *
1483 * @param objectOrArrayOrCollection
1484 * is a list
1485 * @return the array of objects with type of the first element in the list
1486 */
1487 public static Object toArray(Object objectOrArrayOrCollection) {
1488 // do this before length since if array with null in it, we want ti get
1489 // it back
1490 if (objectOrArrayOrCollection != null
1491 && objectOrArrayOrCollection.getClass().isArray()) {
1492 return objectOrArrayOrCollection;
1493 }
1494 int length = length(objectOrArrayOrCollection);
1495 if (length == 0) {
1496 return null;
1497 }
1498
1499 if (objectOrArrayOrCollection instanceof Collection) {
1500 Collection<Object> collection = (Collection<Object>) objectOrArrayOrCollection;
1501 Object first = collection.iterator().next();
1502 Class<Object> theClass = first == null ? (Class)Object.class : (Class)first
1503 .getClass();
1504 return toArray(collection, theClass);
1505 }
1506 // make an array of the type of object passed in, size one
1507 Object array = Array.newInstance(objectOrArrayOrCollection.getClass(),
1508 1);
1509 Array.set(array, 0, objectOrArrayOrCollection);
1510 return array;
1511 }
1512
1513 /**
1514 * convert a list into an array of type of theClass
1515 * @param <T> is the type of the array
1516 * @param collection list to convert
1517 * @param theClass type of array to return
1518 * @return array of type theClass[] filled with the objects from list
1519 */
1520 @SuppressWarnings("unchecked")
1521 public static <T> T[] toArray(Collection collection, Class<T> theClass) {
1522 if (collection == null || collection.size() == 0) {
1523 return null;
1524 }
1525
1526 return (T[])collection.toArray((Object[]) Array.newInstance(theClass,
1527 collection.size()));
1528
1529 }
1530
1531 /**
1532 * replace a string or strings from a string, and put the output in a string
1533 * buffer. This does not recurse
1534 *
1535 * @param text
1536 * string to look in
1537 * @param searchFor
1538 * string array to search for
1539 * @param replaceWith
1540 * string array to replace with
1541 * @return the string
1542 */
1543 public static String replace(String text, Object searchFor,
1544 Object replaceWith) {
1545 return replace(null, null, text, searchFor, replaceWith, false, 0,
1546 false);
1547 }
1548
1549 /**
1550 * replace a string or strings from a string, and put the output in a string
1551 * buffer
1552 *
1553 * @param text
1554 * string to look in
1555 * @param searchFor
1556 * string array to search for
1557 * @param replaceWith
1558 * string array to replace with
1559 * @param recurse
1560 * if true then do multiple replaces (on the replacements)
1561 * @return the string
1562 */
1563 public static String replace(String text, Object searchFor,
1564 Object replaceWith, boolean recurse) {
1565 return replace(null, null, text, searchFor, replaceWith, recurse,
1566 recurse ? length(searchFor) : 0, false);
1567 }
1568
1569 /**
1570 * replace a string or strings from a string, and put the output in a string
1571 * buffer
1572 *
1573 * @param text
1574 * string to look in
1575 * @param searchFor
1576 * string array to search for
1577 * @param replaceWith
1578 * string array to replace with
1579 * @param recurse
1580 * if true then do multiple replaces (on the replacements)
1581 * @param removeIfFound
1582 * true if removing from searchFor and replaceWith if found
1583 * @return the string
1584 */
1585 public static String replace(String text, Object searchFor,
1586 Object replaceWith, boolean recurse, boolean removeIfFound) {
1587 return replace(null, null, text, searchFor, replaceWith, recurse,
1588 recurse ? length(searchFor) : 0, removeIfFound);
1589 }
1590
1591 /**
1592 * <p>
1593 * Replaces all occurrences of a String within another String.
1594 * </p>
1595 *
1596 * <p>
1597 * A <code>null</code> reference passed to this method is a no-op.
1598 * </p>
1599 *
1600 * <pre>
1601 * replace(null, *, *) = null
1602 * replace("", *, *) = ""
1603 * replace("any", null, *) = "any"
1604 * replace("any", *, null) = "any"
1605 * replace("any", "", *) = "any"
1606 * replace("aba", "a", null) = "aba"
1607 * replace("aba", "a", "") = "b"
1608 * replace("aba", "a", "z") = "zbz"
1609 * </pre>
1610 *
1611 * @see #replace(String text, String repl, String with, int max)
1612 * @param text
1613 * text to search and replace in, may be null
1614 * @param repl
1615 * the String to search for, may be null
1616 * @param with
1617 * the String to replace with, may be null
1618 * @return the text with any replacements processed, <code>null</code> if
1619 * null String input
1620 */
1621 public static String replace(String text, String repl, String with) {
1622 return replace(text, repl, with, -1);
1623 }
1624
1625 /**
1626 * <p>
1627 * Replaces a String with another String inside a larger String, for the
1628 * first <code>max</code> values of the search String.
1629 * </p>
1630 *
1631 * <p>
1632 * A <code>null</code> reference passed to this method is a no-op.
1633 * </p>
1634 *
1635 * <pre>
1636 * replace(null, *, *, *) = null
1637 * replace("", *, *, *) = ""
1638 * replace("any", null, *, *) = "any"
1639 * replace("any", *, null, *) = "any"
1640 * replace("any", "", *, *) = "any"
1641 * replace("any", *, *, 0) = "any"
1642 * replace("abaa", "a", null, -1) = "abaa"
1643 * replace("abaa", "a", "", -1) = "b"
1644 * replace("abaa", "a", "z", 0) = "abaa"
1645 * replace("abaa", "a", "z", 1) = "zbaa"
1646 * replace("abaa", "a", "z", 2) = "zbza"
1647 * replace("abaa", "a", "z", -1) = "zbzz"
1648 * </pre>
1649 *
1650 * @param text
1651 * text to search and replace in, may be null
1652 * @param repl
1653 * the String to search for, may be null
1654 * @param with
1655 * the String to replace with, may be null
1656 * @param max
1657 * maximum number of values to replace, or <code>-1</code> if
1658 * no maximum
1659 * @return the text with any replacements processed, <code>null</code> if
1660 * null String input
1661 */
1662 public static String replace(String text, String repl, String with, int max) {
1663 if (text == null || isEmpty(repl) || with == null || max == 0) {
1664 return text;
1665 }
1666
1667 StringBuffer buf = new StringBuffer(text.length());
1668 int start = 0, end = 0;
1669 while ((end = text.indexOf(repl, start)) != -1) {
1670 buf.append(text.substring(start, end)).append(with);
1671 start = end + repl.length();
1672
1673 if (--max == 0) {
1674 break;
1675 }
1676 }
1677 buf.append(text.substring(start));
1678 return buf.toString();
1679 }
1680
1681 /**
1682 * <p>
1683 * Checks if a String is empty ("") or null.
1684 * </p>
1685 *
1686 * <pre>
1687 * isEmpty(null) = true
1688 * isEmpty("") = true
1689 * isEmpty(" ") = false
1690 * isEmpty("bob") = false
1691 * isEmpty(" bob ") = false
1692 * </pre>
1693 *
1694 * <p>
1695 * NOTE: This method changed in Lang version 2.0. It no longer trims the
1696 * String. That functionality is available in isBlank().
1697 * </p>
1698 *
1699 * @param str
1700 * the String to check, may be null
1701 * @return <code>true</code> if the String is empty or null
1702 */
1703 public static boolean isEmpty(String str) {
1704 return str == null || str.length() == 0;
1705 }
1706
1707 /**
1708 * replace a string or strings from a string, and put the output in a string
1709 * buffer. This does not recurse
1710 *
1711 * @param outBuffer
1712 * stringbuffer to write to
1713 * @param text
1714 * string to look in
1715 * @param searchFor
1716 * string array to search for
1717 * @param replaceWith
1718 * string array to replace with
1719 */
1720 public static void replace(StringBuffer outBuffer, String text,
1721 Object searchFor, Object replaceWith) {
1722 replace(outBuffer, null, text, searchFor, replaceWith, false, 0, false);
1723 }
1724
1725 /**
1726 * replace a string or strings from a string, and put the output in a string
1727 * buffer
1728 *
1729 * @param outBuffer
1730 * stringbuffer to write to
1731 * @param text
1732 * string to look in
1733 * @param searchFor
1734 * string array to search for
1735 * @param replaceWith
1736 * string array to replace with
1737 * @param recurse
1738 * if true then do multiple replaces (on the replacements)
1739 */
1740 public static void replace(StringBuffer outBuffer, String text,
1741 Object searchFor, Object replaceWith, boolean recurse) {
1742 replace(outBuffer, null, text, searchFor, replaceWith, recurse,
1743 recurse ? length(searchFor) : 0, false);
1744 }
1745
1746 /**
1747 * replace a string with other strings, and either write to outWriter, or
1748 * StringBuffer, and if StringBuffer potentially return a string. If
1749 * outBuffer and outWriter are null, then return the String
1750 *
1751 * @param outBuffer
1752 * stringbuffer to write to, or null to not
1753 * @param outWriter
1754 * Writer to write to, or null to not.
1755 * @param text
1756 * string to look in
1757 * @param searchFor
1758 * string array to search for, or string, or list
1759 * @param replaceWith
1760 * string array to replace with, or string, or list
1761 * @param recurse
1762 * if true then do multiple replaces (on the replacements)
1763 * @param timeToLive
1764 * if recursing, prevent endless loops
1765 * @param removeIfFound
1766 * true if removing from searchFor and replaceWith if found
1767 * @return the String if outBuffer and outWriter are null
1768 * @throws IndexOutOfBoundsException
1769 * if the lengths of the arrays are not the same (null is ok,
1770 * and/or size 0)
1771 * @throws IllegalArgumentException
1772 * if the search is recursive and there is an endless loop due
1773 * to outputs of one being inputs to another
1774 */
1775 private static String replace(StringBuffer outBuffer, Writer outWriter,
1776 String text, Object searchFor, Object replaceWith, boolean recurse,
1777 int timeToLive, boolean removeIfFound) {
1778
1779 // if recursing, we need to get the string, then print to buffer (since
1780 // we need multiple passes)
1781 if (!recurse) {
1782 return replaceHelper(outBuffer, outWriter, text, searchFor,
1783 replaceWith, recurse, timeToLive, removeIfFound);
1784 }
1785 // get the string
1786 String result = replaceHelper(null, null, text, searchFor, replaceWith,
1787 recurse, timeToLive, removeIfFound);
1788 if (outBuffer != null) {
1789 outBuffer.append(result);
1790 return null;
1791 }
1792
1793 if (outWriter != null) {
1794 try {
1795 outWriter.write(result);
1796 } catch (IOException ioe) {
1797 throw new RuntimeException(ioe);
1798 }
1799 return null;
1800 }
1801
1802 return result;
1803
1804 }
1805
1806 /**
1807 * replace a string or strings from a string, and put the output in a string
1808 * buffer. This does not recurse
1809 *
1810 * @param outWriter
1811 * writer to write to
1812 * @param text
1813 * string to look in
1814 * @param searchFor
1815 * string array to search for
1816 * @param replaceWith
1817 * string array to replace with
1818 */
1819 public static void replace(Writer outWriter, String text, Object searchFor,
1820 Object replaceWith) {
1821 replace(null, outWriter, text, searchFor, replaceWith, false, 0, false);
1822 }
1823
1824 /**
1825 * replace a string or strings from a string, and put the output in a string
1826 * buffer
1827 *
1828 * @param outWriter
1829 * writer to write to
1830 * @param text
1831 * string to look in
1832 * @param searchFor
1833 * string array to search for
1834 * @param replaceWith
1835 * string array to replace with
1836 * @param recurse
1837 * if true then do multiple replaces (on the replacements)
1838 */
1839 public static void replace(Writer outWriter, String text, Object searchFor,
1840 Object replaceWith, boolean recurse) {
1841 replace(null, outWriter, text, searchFor, replaceWith, recurse,
1842 recurse ? length(searchFor) : 0, false);
1843 }
1844
1845 /**
1846 * replace a string with other strings, and either write to outWriter, or
1847 * StringBuffer, and if StringBuffer potentially return a string. If
1848 * outBuffer and outWriter are null, then return the String
1849 *
1850 * @param outBuffer
1851 * stringbuffer to write to, or null to not
1852 * @param outWriter
1853 * Writer to write to, or null to not.
1854 * @param text
1855 * string to look in
1856 * @param searchFor
1857 * string array to search for, or string, or list
1858 * @param replaceWith
1859 * string array to replace with, or string, or list
1860 * @param recurse
1861 * if true then do multiple replaces (on the replacements)
1862 * @param timeToLive
1863 * if recursing, prevent endless loops
1864 * @param removeIfFound
1865 * true if removing from searchFor and replaceWith if found
1866 * @return the String if outBuffer and outWriter are null
1867 * @throws IllegalArgumentException
1868 * if the search is recursive and there is an endless loop due
1869 * to outputs of one being inputs to another
1870 * @throws IndexOutOfBoundsException
1871 * if the lengths of the arrays are not the same (null is ok,
1872 * and/or size 0)
1873 */
1874 private static String replaceHelper(StringBuffer outBuffer,
1875 Writer outWriter, String text, Object searchFor,
1876 Object replaceWith, boolean recurse, int timeToLive,
1877 boolean removeIfFound) {
1878
1879 try {
1880 // if recursing, this shouldnt be less than 0
1881 if (timeToLive < 0) {
1882 throw new IllegalArgumentException("TimeToLive under 0: "
1883 + timeToLive + ", " + text);
1884 }
1885
1886 int searchForLength = length(searchFor);
1887 boolean done = false;
1888 // no need to do anything
1889 if (isEmpty(text)) {
1890 return text;
1891 }
1892 // need to write the input to output, later
1893 if (searchForLength == 0) {
1894 done = true;
1895 }
1896
1897 boolean[] noMoreMatchesForReplIndex = null;
1898 int inputIndex = -1;
1899 int replaceIndex = -1;
1900 long resultPacked = -1;
1901
1902 if (!done) {
1903 // make sure lengths are ok, these need to be equal
1904 if (searchForLength != length(replaceWith)) {
1905 throw new IndexOutOfBoundsException("Lengths dont match: "
1906 + searchForLength + ", " + length(replaceWith));
1907 }
1908
1909 // keep track of which still have matches
1910 noMoreMatchesForReplIndex = new boolean[searchForLength];
1911
1912 // index of replace array that will replace the search string
1913 // found
1914
1915
1916 resultPacked = findNextIndexHelper(searchForLength, searchFor,
1917 replaceWith,
1918 noMoreMatchesForReplIndex, text, 0);
1919
1920 inputIndex = unpackInt(resultPacked, true);
1921 replaceIndex = unpackInt(resultPacked, false);
1922 }
1923
1924 // get a good guess on the size of the result buffer so it doesnt
1925 // have to double if it
1926 // goes over a bit
1927 boolean writeToWriter = outWriter != null;
1928
1929 // no search strings found, we are done
1930 if (done || inputIndex == -1) {
1931 if (writeToWriter) {
1932 outWriter.write(text, 0, text.length());
1933 return null;
1934 }
1935 if (outBuffer != null) {
1936 appendSubstring(outBuffer, text, 0, text.length());
1937 return null;
1938 }
1939 return text;
1940 }
1941
1942 // no buffer if writing to writer
1943 StringBuffer bufferToWriteTo = outBuffer != null ? outBuffer
1944 : (writeToWriter ? null : new StringBuffer(text.length()
1945 + replaceStringsBufferIncrease(text, searchFor,
1946 replaceWith)));
1947
1948 String searchString = null;
1949 String replaceString = null;
1950
1951 int start = 0;
1952
1953 while (inputIndex != -1) {
1954
1955 searchString = (String) get(searchFor, replaceIndex);
1956 replaceString = (String) get(replaceWith, replaceIndex);
1957 if (writeToWriter) {
1958 outWriter.write(text, start, inputIndex - start);
1959 outWriter.write(replaceString);
1960 } else {
1961 appendSubstring(bufferToWriteTo, text, start, inputIndex)
1962 .append(replaceString);
1963 }
1964
1965 if (removeIfFound) {
1966 // better be an iterator based find replace
1967 searchFor = remove(searchFor, replaceIndex);
1968 replaceWith = remove(replaceWith, replaceIndex);
1969 noMoreMatchesForReplIndex = (boolean[])remove(noMoreMatchesForReplIndex, replaceIndex);
1970 // we now have a lesser size if we removed one
1971 searchForLength--;
1972 }
1973
1974 start = inputIndex + searchString.length();
1975
1976 resultPacked = findNextIndexHelper(searchForLength, searchFor,
1977 replaceWith,
1978 noMoreMatchesForReplIndex, text, start);
1979 inputIndex = unpackInt(resultPacked, true);
1980 replaceIndex = unpackInt(resultPacked, false);
1981 }
1982 if (writeToWriter) {
1983 outWriter.write(text, start, text.length() - start);
1984
1985 } else {
1986 appendSubstring(bufferToWriteTo, text, start, text.length());
1987 }
1988
1989 // no need to convert to string if incoming buffer or writer
1990 if (writeToWriter || outBuffer != null) {
1991 if (recurse) {
1992 throw new IllegalArgumentException(
1993 "Cannot recurse and write to existing buffer or writer!");
1994 }
1995 return null;
1996 }
1997 String resultString = bufferToWriteTo.toString();
1998
1999 if (recurse) {
2000 return replaceHelper(outBuffer, outWriter, resultString,
2001 searchFor, replaceWith, recurse, timeToLive - 1, false);
2002 }
2003 // this might be null for writer
2004 return resultString;
2005 } catch (IOException ioe) {
2006 throw new RuntimeException(ioe);
2007 }
2008 }
2009
2010 /**
2011 * give a best guess on buffer increase for String[] replace get a good
2012 * guess on the size of the result buffer so it doesnt have to double if it
2013 * goes over a bit
2014 *
2015 * @param text
2016 * @param repl
2017 * @param with
2018 * @return the increase, with 20% cap
2019 */
2020 static int replaceStringsBufferIncrease(String text, Object repl,
2021 Object with) {
2022 // count the greaters
2023 int increase = 0;
2024 Iterator iteratorReplace = iterator(repl);
2025 Iterator iteratorWith = iterator(with);
2026 int replLength = length(repl);
2027 String currentRepl = null;
2028 String currentWith = null;
2029 for (int i = 0; i < replLength; i++) {
2030 currentRepl = (String) next(repl, iteratorReplace, i);
2031 currentWith = (String) next(with, iteratorWith, i);
2032 if (currentRepl == null || currentWith == null) {
2033 throw new NullPointerException("Replace string is null: "
2034 + text + ", " + currentRepl + ", " + currentWith);
2035 }
2036 int greater = currentWith.length() - currentRepl.length();
2037 increase += greater > 0 ? 3 * greater : 0; // assume 3 matches
2038 }
2039 // have upper-bound at 20% increase, then let Java take over
2040 increase = Math.min(increase, text.length() / 5);
2041 return increase;
2042 }
2043
2044 /**
2045 * Helper method to find the next match in an array of strings replace
2046 *
2047 * @param searchForLength
2048 * @param searchFor
2049 * @param replaceWith
2050 * @param noMoreMatchesForReplIndex
2051 * @param input
2052 * @param start
2053 * is where to start looking
2054 * @return result packed into a long, inputIndex first, then replaceIndex
2055 */
2056 private static long findNextIndexHelper(int searchForLength,
2057 Object searchFor, Object replaceWith, boolean[] noMoreMatchesForReplIndex,
2058 String input, int start) {
2059
2060 int inputIndex = -1;
2061 int replaceIndex = -1;
2062
2063 Iterator iteratorSearchFor = iterator(searchFor);
2064 Iterator iteratorReplaceWith = iterator(replaceWith);
2065
2066 String currentSearchFor = null;
2067 String currentReplaceWith = null;
2068 int tempIndex = -1;
2069 for (int i = 0; i < searchForLength; i++) {
2070 currentSearchFor = (String) next(searchFor, iteratorSearchFor, i);
2071 currentReplaceWith = (String) next(replaceWith,
2072 iteratorReplaceWith, i);
2073 if (noMoreMatchesForReplIndex[i] || isEmpty(currentSearchFor)
2074 || currentReplaceWith == null) {
2075 continue;
2076 }
2077 tempIndex = input.indexOf(currentSearchFor, start);
2078
2079 // see if we need to keep searching for this
2080 noMoreMatchesForReplIndex[i] = tempIndex == -1;
2081
2082 if (tempIndex != -1 && (inputIndex == -1 || tempIndex < inputIndex)) {
2083 inputIndex = tempIndex;
2084 replaceIndex = i;
2085 }
2086
2087 }
2088 // dont create an array, no more objects
2089 long resultPacked = packInts(inputIndex, replaceIndex);
2090 return resultPacked;
2091 }
2092
2093 /**
2094 * pack two ints into a long. Note: the first is held in the left bits, the
2095 * second is held in the right bits
2096 *
2097 * @param first
2098 * is first int
2099 * @param second
2100 * is second int
2101 * @return the long which has two ints in there
2102 */
2103 public static long packInts(int first, int second) {
2104 long result = first;
2105 result <<= 32;
2106 result |= second;
2107 return result;
2108 }
2109
2110 /**
2111 * take a long
2112 *
2113 * @param theLong
2114 * to unpack
2115 * @param isFirst
2116 * true for first, false for second
2117 * @return one of the packed ints, first or second
2118 */
2119 public static int unpackInt(long theLong, boolean isFirst) {
2120
2121 int result = 0;
2122 // put this in the position of the second one
2123 if (isFirst) {
2124 theLong >>= 32;
2125 }
2126 // only look at right part
2127 result = (int) (theLong & 0xffffffff);
2128 return result;
2129 }
2130
2131 /**
2132 * append a substring to a stringbuffer. removes dependency on substring
2133 * which creates objects
2134 *
2135 * @param buf
2136 * stringbuffer
2137 * @param string
2138 * source string
2139 * @param start
2140 * start index of source string
2141 * @param end
2142 * end index of source string
2143 * @return the string buffer for chaining
2144 */
2145 private static StringBuffer appendSubstring(StringBuffer buf,
2146 String string, int start, int end) {
2147 for (int i = start; i < end; i++) {
2148 buf.append(string.charAt(i));
2149 }
2150 return buf;
2151 }
2152
2153 /**
2154 * convert a collection of sources to a string
2155 * @param sources
2156 * @return the string
2157 */
2158 public static String toString(Collection<Source> sources) {
2159 if (length(sources) == 0) {
2160 return null;
2161 }
2162 StringBuilder result = new StringBuilder();
2163 for (Source source : sources) {
2164 result.append(source.getId()).append(", ");
2165 }
2166 result.delete(result.length()-2, result.length());
2167 return result.toString();
2168 }
2169
2170 /**
2171 * fail safe toString for Exception blocks, and include the stack
2172 * if there is a problem with toString()
2173 * @param object
2174 * @return the toStringSafe string
2175 */
2176 public static String toStringSafe(Object object) {
2177 if (object == null) {
2178 return null;
2179 }
2180
2181 try {
2182 //give size and type if collection
2183 if (object instanceof Collection) {
2184 Collection<Object> collection = (Collection<Object>) object;
2185 int collectionSize = collection.size();
2186 if (collectionSize == 0) {
2187 return "Empty " + object.getClass().getSimpleName();
2188 }
2189 Object first = collection.iterator().next();
2190 return object.getClass().getSimpleName() + " of size "
2191 + collectionSize + " with first type: " +
2192 (first == null ? null : first.getClass());
2193 }
2194
2195 return object.toString();
2196 } catch (Exception e) {
2197 return "<<exception>> " + object.getClass() + ":\n" + getFullStackTrace(e) + "\n";
2198 }
2199 }
2200
2201 /**
2202 * get the boolean value for an object, cant be null or blank
2203 *
2204 * @param object
2205 * @return the boolean
2206 */
2207 public static boolean booleanValue(Object object) {
2208 // first handle blanks
2209 if (nullOrBlank(object)) {
2210 throw new RuntimeException(
2211 "Expecting something which can be converted to boolean, but is null or blank: '"
2212 + object + "'");
2213 }
2214 // its not blank, just convert
2215 if (object instanceof Boolean) {
2216 return (Boolean) object;
2217 }
2218 if (object instanceof String) {
2219 String string = (String) object;
2220 if (equalsIgnoreCase(string, "true")
2221 || equalsIgnoreCase(string, "t")
2222 || equalsIgnoreCase(string, "yes")
2223 || equalsIgnoreCase(string, "y")) {
2224 return true;
2225 }
2226 if (equalsIgnoreCase(string, "false")
2227 || equalsIgnoreCase(string, "f")
2228 || equalsIgnoreCase(string, "no")
2229 || equalsIgnoreCase(string, "n")) {
2230 return false;
2231 }
2232 throw new RuntimeException(
2233 "Invalid string to boolean conversion: '" + string
2234 + "' expecting true|false or t|f or yes|no or y|n case insensitive");
2235
2236 }
2237 throw new RuntimeException("Cant convert object to boolean: "
2238 + object.getClass());
2239
2240 }
2241
2242 /**
2243 * get the boolean value for an object
2244 *
2245 * @param object
2246 * @param defaultBoolean
2247 * if object is null or empty
2248 * @return the boolean
2249 */
2250 public static boolean booleanValue(Object object, boolean defaultBoolean) {
2251 if (nullOrBlank(object)) {
2252 return defaultBoolean;
2253 }
2254 return booleanValue(object);
2255 }
2256
2257 /**
2258 * get the Boolean value for an object
2259 *
2260 * @param object
2261 * @return the Boolean or null if null or empty
2262 */
2263 public static Boolean booleanObjectValue(Object object) {
2264 if (nullOrBlank(object)) {
2265 return null;
2266 }
2267 return booleanValue(object);
2268 }
2269
2270 /**
2271 * is an object null or blank
2272 *
2273 * @param object
2274 * @return true if null or blank
2275 */
2276 public static boolean nullOrBlank(Object object) {
2277 // first handle blanks and nulls
2278 if (object == null) {
2279 return true;
2280 }
2281 if (object instanceof String && isBlank(((String) object))) {
2282 return true;
2283 }
2284 return false;
2285
2286 }
2287
2288 /**
2289 * convert a string date into a long date (e.g. for xml export)
2290 * @param date
2291 * @return the long or null if the date was null or blank
2292 */
2293 public static Long dateLongValue(String date) {
2294 if (isBlank(date)) {
2295 return null;
2296 }
2297 Date dateObject = dateValue(date);
2298 return dateObject.getTime();
2299 }
2300
2301 /**
2302 * web service format string
2303 */
2304 private static final String TIMESTAMP_XML_FORMAT = "yyyy/MM/dd HH:mm:ss.SSS";
2305
2306 /**
2307 * date object to a string:
2308 * @param date
2309 * @return the long or null if the date was null or blank
2310 */
2311 public static String dateStringValue(Date date) {
2312 if (date == null) {
2313 return null;
2314 }
2315 SimpleDateFormat simpleDateFormat = new SimpleDateFormat(TIMESTAMP_XML_FORMAT);
2316 return simpleDateFormat.format(date);
2317 }
2318
2319 /**
2320 * date object to a string:
2321 * @param theDate
2322 * @return the long or null if the date was null or blank
2323 */
2324 public static String dateStringValue(Long theDate) {
2325 if (theDate == null) {
2326 return null;
2327 }
2328 return dateStringValue(new Date(theDate));
2329 }
2330
2331 /**
2332 * <pre>
2333 * Convert an object to a java.util.Date. allows, dates, null, blank,
2334 * yyyymmdd or yyyymmdd hh24:mm:ss
2335 * or yyyy/MM/dd HH:mm:ss.SSS
2336 * </pre>
2337 * @param inputObject
2338 * is the String or Date to convert
2339 *
2340 * @return the Date
2341 */
2342 public static Date dateValue(Object inputObject) {
2343 if (inputObject == null) {
2344 return null;
2345 }
2346
2347 if (inputObject instanceof java.util.Date) {
2348 return (Date)inputObject;
2349 }
2350
2351 if (inputObject instanceof String) {
2352 String input = (String)inputObject;
2353 //trim and handle null and empty
2354 if (isBlank(input)) {
2355 return null;
2356 }
2357
2358 try {
2359 if (input.length() == 8) {
2360
2361 return dateFormat().parse(input);
2362 }
2363 if (!contains(input, '.')) {
2364 if (contains(input, '/')) {
2365 return dateMinutesSecondsFormat.parse(input);
2366 }
2367 //else no slash
2368 return dateMinutesSecondsNoSlashFormat.parse(input);
2369 }
2370 if (contains(input, '/')) {
2371 //see if the period is 6 back
2372 int lastDotIndex = input.lastIndexOf('.');
2373 if (lastDotIndex == input.length() - 7) {
2374 String nonNanoInput = input.substring(0,input.length()-3);
2375 Date date = timestampFormat.parse(nonNanoInput);
2376 //get the last 3
2377 String lastThree = input.substring(input.length()-3,input.length());
2378 int lastThreeInt = Integer.parseInt(lastThree);
2379 Timestamp timestamp = new Timestamp(date.getTime());
2380 timestamp.setNanos(timestamp.getNanos() + (lastThreeInt * 1000));
2381 return timestamp;
2382 }
2383 return timestampFormat.parse(input);
2384 }
2385 //else no slash
2386 return timestampNoSlashFormat.parse(input);
2387 } catch (ParseException pe) {
2388 throw new RuntimeException(errorStart + toStringForLog(input));
2389 }
2390 }
2391
2392 throw new RuntimeException("Cannot convert Object to date : " + toStringForLog(inputObject));
2393 }
2394
2395 /**
2396 * match regex pattern yyyy-mm-dd or yyyy/mm/dd
2397 */
2398 private static Pattern datePattern_yyyy_mm_dd = Pattern.compile("^(\\d{4})[^\\d]+(\\d{1,2})[^\\d]+(\\d{1,2})$");
2399
2400 /**
2401 * match regex pattern dd-mon-yyyy or dd/mon/yyyy
2402 */
2403 private static Pattern datePattern_dd_mon_yyyy = Pattern.compile("^(\\d{1,2})[^\\d]+([a-zA-Z]{3,15})[^\\d]+(\\d{4})$");
2404
2405 /**
2406 * match regex pattern yyyy-mm-dd hh:mm:ss or yyyy/mm/dd hh:mm:ss
2407 */
2408 private static Pattern datePattern_yyyy_mm_dd_hhmmss = Pattern.compile("^(\\d{4})[^\\d]+(\\d{1,2})[^\\d]+(\\d{1,2})[^\\d]+(\\d{1,2})[^\\d]+(\\d{1,2})[^\\d]+(\\d{1,2})$");
2409
2410 /**
2411 * match regex pattern dd-mon-yyyy hh:mm:ss or dd/mon/yyyy hh:mm:ss
2412 */
2413 private static Pattern datePattern_dd_mon_yyyy_hhmmss = Pattern.compile("^(\\d{1,2})[^\\d]+([a-zA-Z]{3,15})[^\\d]+(\\d{4})[^\\d]+(\\d{1,2})[^\\d]+(\\d{1,2})[^\\d]+(\\d{1,2})$");
2414
2415 /**
2416 * match regex pattern yyyy-mm-dd hh:mm:ss.SSS or yyyy/mm/dd hh:mm:ss.SSS
2417 */
2418 private static Pattern datePattern_yyyy_mm_dd_hhmmss_SSS = Pattern.compile("^(\\d{4})[^\\d]+(\\d{1,2})[^\\d]+(\\d{1,2})[^\\d]+(\\d{1,2})[^\\d]+(\\d{1,2})[^\\d]+(\\d{1,2})[^\\d]+(\\d{1,3})$");
2419
2420 /**
2421 * match regex pattern dd-mon-yyyy hh:mm:ss.SSS or dd/mon/yyyy hh:mm:ss.SSS
2422 */
2423 private static Pattern datePattern_dd_mon_yyyy_hhmmss_SSS = Pattern.compile("^(\\d{1,2})[^\\d]+([a-zA-Z]{3,15})[^\\d]+(\\d{4})[^\\d]+(\\d{1,2})[^\\d]+(\\d{1,2})[^\\d]+(\\d{1,2})[^\\d]+(\\d{1,3})$");
2424
2425 /**
2426 * take as input:
2427 * yyyy/mm/dd
2428 * yyyy-mm-dd
2429 * dd-mon-yyyy
2430 * yyyy/mm/dd hh:mm:ss
2431 * dd-mon-yyyy hh:mm:ss
2432 * yyyy/mm/dd hh:mm:ss.SSS
2433 * dd-mon-yyyy hh:mm:ss.SSS
2434 * @param input
2435 * @return the date
2436 */
2437 public static Date stringToDate2(String input) {
2438
2439 if (isBlank(input)) {
2440 return null;
2441 }
2442 input = input.trim();
2443 Matcher matcher = null;
2444
2445 int month = 0;
2446 int day = 0;
2447 int year = 0;
2448 int hour = 0;
2449 int minute = 0;
2450 int second = 0;
2451 int milli = 0;
2452
2453 boolean foundMatch = false;
2454
2455 //yyyy/mm/dd
2456 if (!foundMatch) {
2457 matcher = datePattern_yyyy_mm_dd.matcher(input);
2458 if (matcher.matches()) {
2459 year = intValue(matcher.group(1));
2460 month = intValue(matcher.group(2));
2461 day = intValue(matcher.group(3));
2462 foundMatch = true;
2463 }
2464 }
2465
2466 //dd-mon-yyyy
2467 if (!foundMatch) {
2468 matcher = datePattern_dd_mon_yyyy.matcher(input);
2469 if (matcher.matches()) {
2470 day = intValue(matcher.group(1));
2471 month = monthInt(matcher.group(2));
2472 year = intValue(matcher.group(3));
2473 foundMatch = true;
2474 }
2475 }
2476
2477 //yyyy/mm/dd hh:mm:ss
2478 if (!foundMatch) {
2479 matcher = datePattern_yyyy_mm_dd_hhmmss.matcher(input);
2480 if (matcher.matches()) {
2481 year = intValue(matcher.group(1));
2482 month = intValue(matcher.group(2));
2483 day = intValue(matcher.group(3));
2484 hour = intValue(matcher.group(4));
2485 minute = intValue(matcher.group(5));
2486 second = intValue(matcher.group(6));
2487 foundMatch = true;
2488 }
2489 }
2490
2491 //dd-mon-yyyy hh:mm:ss
2492 if (!foundMatch) {
2493 matcher = datePattern_dd_mon_yyyy_hhmmss.matcher(input);
2494 if (matcher.matches()) {
2495 day = intValue(matcher.group(1));
2496 month = monthInt(matcher.group(2));
2497 year = intValue(matcher.group(3));
2498 hour = intValue(matcher.group(4));
2499 minute = intValue(matcher.group(5));
2500 second = intValue(matcher.group(6));
2501 foundMatch = true;
2502 }
2503 }
2504
2505 //yyyy/mm/dd hh:mm:ss.SSS
2506 if (!foundMatch) {
2507 matcher = datePattern_yyyy_mm_dd_hhmmss_SSS.matcher(input);
2508 if (matcher.matches()) {
2509 year = intValue(matcher.group(1));
2510 month = intValue(matcher.group(2));
2511 day = intValue(matcher.group(3));
2512 hour = intValue(matcher.group(4));
2513 minute = intValue(matcher.group(5));
2514 second = intValue(matcher.group(6));
2515 milli = intValue(matcher.group(7));
2516 foundMatch = true;
2517 }
2518 }
2519
2520 //dd-mon-yyyy hh:mm:ss.SSS
2521 if (!foundMatch) {
2522 matcher = datePattern_dd_mon_yyyy_hhmmss_SSS.matcher(input);
2523 if (matcher.matches()) {
2524 day = intValue(matcher.group(1));
2525 month = monthInt(matcher.group(2));
2526 year = intValue(matcher.group(3));
2527 hour = intValue(matcher.group(4));
2528 minute = intValue(matcher.group(5));
2529 second = intValue(matcher.group(6));
2530 milli = intValue(matcher.group(7));
2531 foundMatch = true;
2532 }
2533 }
2534
2535 Calendar calendar = Calendar.getInstance();
2536 calendar.set(Calendar.YEAR, year);
2537 calendar.set(Calendar.MONTH, month-1);
2538 calendar.set(Calendar.DAY_OF_MONTH, day);
2539 calendar.set(Calendar.HOUR_OF_DAY, hour);
2540 calendar.set(Calendar.MINUTE, minute);
2541 calendar.set(Calendar.SECOND, second);
2542 calendar.set(Calendar.MILLISECOND, milli);
2543 return calendar.getTime();
2544 }
2545
2546 /**
2547 * convert a month string to an int (1 indexed).
2548 * e.g. if input is feb or Feb or february or February return 2
2549 * @param mon
2550 * @return the month
2551 */
2552 public static int monthInt(String mon) {
2553
2554 if (!isBlank(mon)) {
2555 mon = mon.toLowerCase();
2556
2557 if (equals(mon, "jan") || equals(mon, "january")) {
2558 return 1;
2559 }
2560
2561 if (equals(mon, "feb") || equals(mon, "february")) {
2562 return 2;
2563 }
2564
2565 if (equals(mon, "mar") || equals(mon, "march")) {
2566 return 3;
2567 }
2568
2569 if (equals(mon, "apr") || equals(mon, "april")) {
2570 return 4;
2571 }
2572
2573 if (equals(mon, "may")) {
2574 return 5;
2575 }
2576
2577 if (equals(mon, "jun") || equals(mon, "june")) {
2578 return 6;
2579 }
2580
2581 if (equals(mon, "jul") || equals(mon, "july")) {
2582 return 7;
2583 }
2584
2585 if (equals(mon, "aug") || equals(mon, "august")) {
2586 return 8;
2587 }
2588
2589 if (equals(mon, "sep") || equals(mon, "september")) {
2590 return 9;
2591 }
2592
2593 if (equals(mon, "oct") || equals(mon, "october")) {
2594 return 10;
2595 }
2596
2597 if (equals(mon, "nov") || equals(mon, "november")) {
2598 return 11;
2599 }
2600
2601 if (equals(mon, "dec") || equals(mon, "december")) {
2602 return 12;
2603 }
2604
2605 }
2606
2607 throw new RuntimeException("Invalid month: " + mon);
2608 }
2609
2610 /**
2611 * See if the input is null or if string, if it is empty or blank (whitespace)
2612 * @param input
2613 * @return true if blank
2614 */
2615 public static boolean isBlank(Object input) {
2616 if (null == input) {
2617 return true;
2618 }
2619 return (input instanceof String && isBlank((String)input));
2620 }
2621
2622 /**
2623 * see if a class is a scalar (not bean, not array or list, etc)
2624 * @param type
2625 * @return true if scalar
2626 */
2627 public static boolean isScalar(Class<?> type) {
2628
2629 if (type.isArray()) {
2630 return false;
2631 }
2632
2633 //definitely all primitives
2634 if (type.isPrimitive()) {
2635 return true;
2636 }
2637 //Integer, Float, etc
2638 if (Number.class.isAssignableFrom(type)) {
2639 return true;
2640 }
2641 //Date, Timestamp
2642 if (Date.class.isAssignableFrom(type)) {
2643 return true;
2644 }
2645 if (Character.class.equals(type)) {
2646 return true;
2647 }
2648 //handles strings and string builders
2649 if (CharSequence.class.equals(type) || CharSequence.class.isAssignableFrom(type)) {
2650 return true;
2651 }
2652 if (Class.class == type || Boolean.class == type || type.isEnum()) {
2653 return true;
2654 }
2655 //appears not to be a scalar
2656 return false;
2657 }
2658
2659
2660 /**
2661 * <pre>
2662 * Convert a string or object to a timestamp (could be string, date, timestamp, etc)
2663 * yyyymmdd
2664 * or
2665 * yyyy/MM/dd
2666 * or
2667 * yyyy/MM/dd HH:mm:ss
2668 * or
2669 * yyyy/MM/dd HH:mm:ss.SSS
2670 * or
2671 * yyyy/MM/dd HH:mm:ss.SSSSSS
2672 *
2673 * </pre>
2674 *
2675 * @param input
2676 * @return the timestamp
2677 * @throws RuntimeException if invalid format
2678 */
2679 public static Timestamp toTimestamp(Object input) {
2680
2681 if (null == input) {
2682 return null;
2683 } else if (input instanceof java.sql.Timestamp) {
2684 return (Timestamp) input;
2685 } else if (input instanceof String) {
2686 return stringToTimestamp((String) input);
2687 } else if (input instanceof Date) {
2688 return new Timestamp(((Date)input).getTime());
2689 } else if (input instanceof java.sql.Date) {
2690 return new Timestamp(((java.sql.Date)input).getTime());
2691 } else {
2692 throw new RuntimeException("Cannot convert Object to timestamp : " + input);
2693 }
2694
2695 }
2696
2697 /**
2698 * convert an object to a string
2699 *
2700 * @param input
2701 * is the object to convert
2702 *
2703 * @return the String conversion of the object
2704 */
2705 public static String stringValue(Object input) {
2706 //this isnt needed
2707 if (input == null) {
2708 return (String) input;
2709 }
2710
2711 if (input instanceof Timestamp) {
2712 //convert to yyyy/MM/dd HH:mm:ss.SSS
2713 return timestampToString((Timestamp) input);
2714 }
2715
2716 if (input instanceof Date) {
2717 //convert to yyyymmdd
2718 return stringValue((Date) input);
2719 }
2720
2721 if (input instanceof Number) {
2722 DecimalFormat decimalFormat = new DecimalFormat(
2723 "###################.###############");
2724 return decimalFormat.format(((Number) input).doubleValue());
2725
2726 }
2727
2728 return input.toString();
2729 }
2730
2731 /**
2732 * Convert a timestamp into a string: yyyy/MM/dd HH:mm:ss.SSS
2733 * @param timestamp
2734 * @return the string representation
2735 */
2736 public synchronized static String timestampToString(Date timestamp) {
2737 if (timestamp == null) {
2738 return null;
2739 }
2740 return timestampFormat.format(timestamp);
2741 }
2742
2743 /**
2744 * Convert a timestamp into a string: yyyy/MM/dd HH:mm:ss.SSS
2745 * @param timestamp
2746 * @return the string representation
2747 */
2748 public synchronized static String timestampToFileString(Date timestamp) {
2749 if (timestamp == null) {
2750 return null;
2751 }
2752 return timestampFileFormat.format(timestamp);
2753 }
2754
2755 /**
2756 * get the timestamp format for this thread
2757 * if you call this make sure to synchronize on FastDateUtils.class
2758 * @return the timestamp format
2759 */
2760 synchronized static SimpleDateFormat dateFormat() {
2761 return dateFormat;
2762 }
2763
2764 /**
2765 * get the timestamp format for this thread
2766 * if you call this make sure to synchronize on FastDateUtils.class
2767 * @return the timestamp format
2768 */
2769 synchronized static SimpleDateFormat dateFormat2() {
2770 return dateFormat2;
2771 }
2772
2773 /**
2774 * convert a date to the standard string yyyymmdd
2775 * @param date
2776 * @return the string value
2777 */
2778 public static String stringValue(java.util.Date date) {
2779 synchronized (GrouperUtilElSafe.class) {
2780 if (date == null) {
2781 return null;
2782 }
2783
2784 String theString = dateFormat().format(date);
2785
2786 return theString;
2787 }
2788 }
2789
2790 /**
2791 * <pre>convert a string to timestamp based on the following formats:
2792 * yyyyMMdd
2793 * yyyy/MM/dd
2794 * yyyy/MM/dd HH:mm:ss
2795 * yyyy/MM/dd HH:mm:ss.SSS
2796 * yyyy/MM/dd HH:mm:ss.SSSSSS
2797 * </pre>
2798 * @param input
2799 * @return the timestamp object
2800 */
2801 public static Timestamp stringToTimestamp(String input) {
2802 Date date = stringToTimestampHelper(input);
2803 if (date == null) {
2804 return null;
2805 }
2806 //maybe already a timestamp
2807 if (date instanceof Timestamp) {
2808 return (Timestamp)date;
2809 }
2810 return new Timestamp(date.getTime());
2811 }
2812
2813 /**
2814 * return a date based on input, null safe. Allow any of the three
2815 * formats:
2816 * yyyyMMdd
2817 * yyyy/MM/dd
2818 * yyyy/MM/dd HH:mm:ss
2819 * yyyy/MM/dd HH:mm:ss.SSS
2820 * yyyy/MM/dd HH:mm:ss.SSSSSS
2821 *
2822 * @param input
2823 * @return the millis, -1 for null
2824 */
2825 synchronized static Date stringToTimestampHelper(String input) {
2826 //trim and handle null and empty
2827 if (isBlank(input)) {
2828 return null;
2829 }
2830 input = input.trim();
2831 try {
2832 //convert mainframe
2833 if (equals("99999999", input)
2834 || equals("999999", input)) {
2835 input = "20991231";
2836 }
2837 if (input.length() == 8) {
2838
2839 return dateFormat().parse(input);
2840 }
2841 if (input.length() == 10) {
2842
2843 return dateFormat2().parse(input);
2844 }
2845 if (!contains(input, '.')) {
2846 if (contains(input, '/')) {
2847 return dateMinutesSecondsFormat.parse(input);
2848 }
2849 //else no slash
2850 return dateMinutesSecondsNoSlashFormat.parse(input);
2851 }
2852 if (contains(input, '/')) {
2853 //see if the period is 6 back
2854 int lastDotIndex = input.lastIndexOf('.');
2855 if (lastDotIndex == input.length() - 7) {
2856 String nonNanoInput = input.substring(0,input.length()-3);
2857 Date date = timestampFormat.parse(nonNanoInput);
2858 //get the last 3
2859 String lastThree = input.substring(input.length()-3,input.length());
2860 int lastThreeInt = Integer.parseInt(lastThree);
2861 Timestamp timestamp = new Timestamp(date.getTime());
2862 timestamp.setNanos(timestamp.getNanos() + (lastThreeInt * 1000));
2863 return timestamp;
2864 }
2865 return timestampFormat.parse(input);
2866 }
2867 //else no slash
2868 return timestampNoSlashFormat.parse(input);
2869 } catch (ParseException pe) {
2870 throw new RuntimeException(errorStart + input);
2871 }
2872 }
2873
2874 /**
2875 * start of error parsing messages
2876 */
2877 private static final String errorStart = "Invalid timestamp, please use any of the formats: "
2878 + DATE_FORMAT + ", " + TIMESTAMP_FORMAT
2879 + ", " + DATE_MINUTES_SECONDS_FORMAT + ": ";
2880
2881 /**
2882 * Convert an object to a byte, allow nulls
2883 * @param input
2884 * @return the boolean object value
2885 */
2886 public static BigDecimal bigDecimalObjectValue(Object input) {
2887 if (input instanceof BigDecimal) {
2888 return (BigDecimal)input;
2889 }
2890 if (isBlank(input)) {
2891 return null;
2892 }
2893 return BigDecimal.valueOf(doubleValue(input));
2894 }
2895
2896 /**
2897 * Convert an object to a byte, allow nulls
2898 * @param input
2899 * @return the boolean object value
2900 */
2901 public static Byte byteObjectValue(Object input) {
2902 if (input instanceof Byte) {
2903 return (Byte)input;
2904 }
2905 if (isBlank(input)) {
2906 return null;
2907 }
2908 return Byte.valueOf(byteValue(input));
2909 }
2910
2911 /**
2912 * convert an object to a byte
2913 * @param input
2914 * @return the byte
2915 */
2916 public static byte byteValue(Object input) {
2917 if (input instanceof String) {
2918 String string = (String)input;
2919 return Byte.parseByte(string);
2920 }
2921 if (input instanceof Number) {
2922 return ((Number)input).byteValue();
2923 }
2924 throw new RuntimeException("Cannot convert to byte: " + className(input));
2925 }
2926
2927 /**
2928 * get the Double value of an object
2929 *
2930 * @param input
2931 * is a number or String
2932 * @param allowNullBlank used to default to false, if true, return null if nul inputted
2933 *
2934 * @return the Double equivalent
2935 */
2936 public static Double doubleObjectValue(Object input, boolean allowNullBlank) {
2937
2938 if (input instanceof Double) {
2939 return (Double) input;
2940 }
2941
2942 if (allowNullBlank && isBlank(input)) {
2943 return null;
2944 }
2945
2946 return Double.valueOf(doubleValue(input));
2947 }
2948
2949 /**
2950 * get the double value of an object
2951 *
2952 * @param input
2953 * is a number or String
2954 *
2955 * @return the double equivalent
2956 */
2957 public static double doubleValue(Object input) {
2958 if (input instanceof String) {
2959 String string = (String)input;
2960 return Double.parseDouble(string);
2961 }
2962 if (input instanceof Number) {
2963 return ((Number)input).doubleValue();
2964 }
2965 throw new RuntimeException("Cannot convert to double: " + className(input));
2966 }
2967
2968 /**
2969 * get the double value of an object, do not throw an
2970 * exception if there is an
2971 * error
2972 *
2973 * @param input
2974 * is a number or String
2975 *
2976 * @return the double equivalent
2977 */
2978 public static double doubleValueNoError(Object input) {
2979 if (input == null || (input instanceof String
2980 && isBlank((String)input))) {
2981 return NOT_FOUND;
2982 }
2983
2984 try {
2985 return doubleValue(input);
2986 } catch (Exception e) {
2987 //no need to log here
2988 }
2989
2990 return NOT_FOUND;
2991 }
2992
2993 /**
2994 * get the Float value of an object
2995 *
2996 * @param input
2997 * is a number or String
2998 * @param allowNullBlank true if allow null or blank
2999 *
3000 * @return the Float equivalent
3001 */
3002 public static Float floatObjectValue(Object input, boolean allowNullBlank) {
3003
3004 if (input instanceof Float) {
3005 return (Float) input;
3006 }
3007
3008 if (allowNullBlank && isBlank(input)) {
3009 return null;
3010 }
3011 return Float.valueOf(floatValue(input));
3012 }
3013
3014 /**
3015 * get the float value of an object
3016 *
3017 * @param input
3018 * is a number or String
3019 *
3020 * @return the float equivalent
3021 */
3022 public static float floatValue(Object input) {
3023 if (input instanceof String) {
3024 String string = (String)input;
3025 return Float.parseFloat(string);
3026 }
3027 if (input instanceof Number) {
3028 return ((Number)input).floatValue();
3029 }
3030 throw new RuntimeException("Cannot convert to float: " + className(input));
3031 }
3032
3033 /**
3034 * get the float value of an object, do not throw an exception if there is an
3035 * error
3036 *
3037 * @param input
3038 * is a number or String
3039 *
3040 * @return the float equivalent
3041 */
3042 public static float floatValueNoError(Object input) {
3043 if (input == null || (input instanceof String
3044 && isBlank((String)input))) {
3045 return NOT_FOUND;
3046 }
3047 try {
3048 return floatValue(input);
3049 } catch (Exception e) {
3050 LOG.error(e);
3051 }
3052
3053 return NOT_FOUND;
3054 }
3055
3056 /**
3057 * get the Integer value of an object
3058 *
3059 * @param input
3060 * is a number or String
3061 * @param allowNullBlank true if convert null or blank to null
3062 *
3063 * @return the Integer equivalent
3064 */
3065 public static Integer intObjectValue(Object input, boolean allowNullBlank) {
3066
3067 if (input instanceof Integer) {
3068 return (Integer) input;
3069 }
3070
3071 if (allowNullBlank && isBlank(input)) {
3072 return null;
3073 }
3074
3075 return Integer.valueOf(intValue(input));
3076 }
3077
3078 /**
3079 * convert an object to a int
3080 * @param input
3081 * @return the number
3082 */
3083 public static int intValue(Object input) {
3084 if (input instanceof String) {
3085 String string = (String)input;
3086 return Integer.parseInt(string);
3087 }
3088 if (input instanceof Number) {
3089 return ((Number)input).intValue();
3090 }
3091 throw new RuntimeException("Cannot convert to int: " + className(input));
3092 }
3093
3094 /**
3095 * convert an object to a int
3096 * @param input
3097 * @param valueIfNull is if the input is null or empty, return this value
3098 * @return the number
3099 */
3100 public static int intValue(Object input, int valueIfNull) {
3101 if (input == null || "".equals(input)) {
3102 return valueIfNull;
3103 }
3104 return intObjectValue(input, false);
3105 }
3106
3107 /**
3108 * get the int value of an object, do not throw an exception if there is an
3109 * error
3110 *
3111 * @param input
3112 * is a number or String
3113 *
3114 * @return the int equivalent
3115 */
3116 public static int intValueNoError(Object input) {
3117 if (input == null || (input instanceof String
3118 && isBlank((String)input))) {
3119 return NOT_FOUND;
3120 }
3121 try {
3122 return intValue(input);
3123 } catch (Exception e) {
3124 //no need to log here
3125 }
3126
3127 return NOT_FOUND;
3128 }
3129
3130 /** special number when a number is not found */
3131 public static final int NOT_FOUND = -999999999;
3132
3133 /**
3134 * logger
3135 */
3136 private static final Log LOG = GrouperUtil.getLog(GrouperUtilElSafe.class);
3137
3138 /**
3139 * The name says it all.
3140 */
3141 public static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
3142
3143 /**
3144 * get the Long value of an object
3145 *
3146 * @param input
3147 * is a number or String
3148 * @param allowNullBlank true if null or blank converts to null
3149 *
3150 * @return the Long equivalent
3151 */
3152 public static Long longObjectValue(Object input, boolean allowNullBlank) {
3153
3154 if (input instanceof Long) {
3155 return (Long) input;
3156 }
3157
3158 if (allowNullBlank && isBlank(input)) {
3159 return null;
3160 }
3161
3162 return Long.valueOf(longValue(input));
3163 }
3164
3165 /**
3166 * convert an object to a long
3167 * @param input
3168 * @return the number
3169 */
3170 public static long longValue(Object input) {
3171 if (input instanceof String) {
3172 String string = (String)input;
3173 return Long.parseLong(string);
3174 }
3175 if (input instanceof Number) {
3176 return ((Number)input).longValue();
3177 }
3178 throw new RuntimeException("Cannot convert to long: " + className(input));
3179 }
3180
3181 /**
3182 * convert an object to a long
3183 * @param input
3184 * @param valueIfNull is if the input is null or empty, return this value
3185 * @return the number
3186 */
3187 public static long longValue(Object input, long valueIfNull) {
3188 if (input == null || "".equals(input)) {
3189 return valueIfNull;
3190 }
3191 return longObjectValue(input, false);
3192 }
3193
3194 /**
3195 * get the long value of an object, do not throw an exception if there is an
3196 * error
3197 *
3198 * @param input
3199 * is a number or String
3200 *
3201 * @return the long equivalent
3202 */
3203 public static long longValueNoError(Object input) {
3204 if (input == null || (input instanceof String
3205 && isBlank((String)input))) {
3206 return NOT_FOUND;
3207 }
3208 try {
3209 return longValue(input);
3210 } catch (Exception e) {
3211 //no need to log here
3212 }
3213
3214 return NOT_FOUND;
3215 }
3216
3217 /**
3218 * get the Short value of an object. converts null or blank to null
3219 *
3220 * @param input
3221 * is a number or String
3222 *
3223 * @return the Long equivalent
3224 */
3225 public static Short shortObjectValue(Object input) {
3226
3227 if (input instanceof Short) {
3228 return (Short) input;
3229 }
3230
3231 if (isBlank(input)) {
3232 return null;
3233 }
3234
3235 return Short.valueOf(shortValue(input));
3236 }
3237
3238 /**
3239 * convert an object to a short
3240 * @param input
3241 * @return the number
3242 */
3243 public static short shortValue(Object input) {
3244 if (input instanceof String) {
3245 String string = (String)input;
3246 return Short.parseShort(string);
3247 }
3248 if (input instanceof Number) {
3249 return ((Number)input).shortValue();
3250 }
3251 throw new RuntimeException("Cannot convert to short: " + className(input));
3252 }
3253
3254 /**
3255 * get the Character wrapper value for the input
3256 * @param input allow null, return null
3257 * @return the Character object wrapper
3258 */
3259 public static Character charObjectValue(Object input) {
3260 if (input instanceof Character) {
3261 return (Character) input;
3262 }
3263 if (isBlank(input)) {
3264 return null;
3265 }
3266 return new Character(charValue(input));
3267 }
3268
3269 /**
3270 * convert an object to a char
3271 * @param input
3272 * @return the number
3273 */
3274 public static char charValue(Object input) {
3275 if (input instanceof Character) {
3276 return ((Character) input).charValue();
3277 }
3278 //if string length 1, thats ok
3279 if (input instanceof String) {
3280 String inputString = (String) input;
3281 if (inputString.length() == 1) {
3282 return inputString.charAt(0);
3283 }
3284 }
3285 throw new RuntimeException("Cannot convert to char: "
3286 + (input == null ? null : (input.getClass() + ", " + input)));
3287 }
3288
3289 /**
3290 * replace all whitespace with space
3291 * @param input
3292 * @return the string
3293 */
3294 public static String replaceWhitespaceWithSpace(String input) {
3295 if (input == null) {
3296 return input;
3297 }
3298 return input.replaceAll("\\s+", " ");
3299 }
3300
3301 /**
3302 * this method takes a long (less than 62) and converts it to a 1 character
3303 * string (a-z, A-Z, 0-9)
3304 *
3305 * @param theLong
3306 * is the long (less than 62) to convert to a 1 character string
3307 *
3308 * @return a one character string
3309 */
3310 public static String convertLongToChar(long theLong) {
3311 if ((theLong < 0) || (theLong >= 62)) {
3312 throw new RuntimeException("convertLongToChar() "
3313 + " invalid input (not >=0 && <62: " + theLong);
3314 } else if (theLong < 26) {
3315 return "" + (char) ('a' + theLong);
3316 } else if (theLong < 52) {
3317 return "" + (char) ('A' + (theLong - 26));
3318 } else {
3319 return "" + (char) ('0' + (theLong - 52));
3320 }
3321 }
3322
3323 /**
3324 * this method takes a long (less than 36) and converts it to a 1 character
3325 * string (A-Z, 0-9)
3326 *
3327 * @param theLong
3328 * is the long (less than 36) to convert to a 1 character string
3329 *
3330 * @return a one character string
3331 */
3332 public static String convertLongToCharSmall(long theLong) {
3333 if ((theLong < 0) || (theLong >= 36)) {
3334 throw new RuntimeException("convertLongToCharSmall() "
3335 + " invalid input (not >=0 && <36: " + theLong);
3336 } else if (theLong < 26) {
3337 return "" + (char) ('A' + theLong);
3338 } else {
3339 return "" + (char) ('0' + (theLong - 26));
3340 }
3341 }
3342
3343 /**
3344 * convert a long to a string by converting it to base 62 (26 lower, 26 upper,
3345 * 10 digits)
3346 *
3347 * @param theLong
3348 * is the long to convert
3349 *
3350 * @return the String conversion of this
3351 */
3352 public static String convertLongToString(long theLong) {
3353 long quotient = theLong / 62;
3354 long remainder = theLong % 62;
3355
3356 if (quotient == 0) {
3357 return convertLongToChar(remainder);
3358 }
3359 StringBuffer result = new StringBuffer();
3360 result.append(convertLongToString(quotient));
3361 result.append(convertLongToChar(remainder));
3362
3363 return result.toString();
3364 }
3365
3366 /**
3367 * convert a long to a string by converting it to base 36 (26 upper, 10
3368 * digits)
3369 *
3370 * @param theLong
3371 * is the long to convert
3372 *
3373 * @return the String conversion of this
3374 */
3375 public static String convertLongToStringSmall(long theLong) {
3376 long quotient = theLong / 36;
3377 long remainder = theLong % 36;
3378
3379 if (quotient == 0) {
3380 return convertLongToCharSmall(remainder);
3381 }
3382 StringBuffer result = new StringBuffer();
3383 result.append(convertLongToStringSmall(quotient));
3384 result.append(convertLongToCharSmall(remainder));
3385
3386 return result.toString();
3387 }
3388
3389 /**
3390 * increment a character (A-Z then 0-9)
3391 *
3392 * @param theChar
3393 *
3394 * @return the value
3395 */
3396 public static char incrementChar(char theChar) {
3397 if (theChar == 'Z') {
3398 return '0';
3399 }
3400
3401 if (theChar == '9') {
3402 return 'A';
3403 }
3404
3405 return ++theChar;
3406 }
3407
3408 /**
3409 * Increment a string with A-Z and 0-9 (no lower case so case insensitive apps
3410 * like windows IE will still work)
3411 *
3412 * @param string
3413 *
3414 * @return the value
3415 */
3416 public static char[] incrementStringInt(char[] string) {
3417 if (string == null) {
3418 return string;
3419 }
3420
3421 //loop through the string backwards
3422 int i = 0;
3423
3424 for (i = string.length - 1; i >= 0; i--) {
3425 char inc = string[i];
3426 inc = incrementChar(inc);
3427 string[i] = inc;
3428
3429 if (inc != 'A') {
3430 break;
3431 }
3432 }
3433
3434 //if we are at 0, then it means we hit AAAAAAA (or more)
3435 if (i < 0) {
3436 return ("A" + new String(string)).toCharArray();
3437 }
3438
3439 return string;
3440 }
3441
3442 /**
3443 * is ascii char
3444 * @param input
3445 * @return true if ascii
3446 */
3447 public static boolean isAscii(char input) {
3448 return input < 128;
3449 }
3450
3451 /**
3452 * find the length of ascii chars (non ascii are counted as two)
3453 * @param input
3454 * @return the length of ascii chars
3455 */
3456 public static int lengthAscii(String input) {
3457 if (input == null) {
3458 return 0;
3459 }
3460 //see what real length is
3461 int utfLength = input.length();
3462 //count how many non asciis
3463 int extras = 0;
3464 for (int i=0;i<utfLength;i++) {
3465 //keep count of non ascii chars
3466 if (!isAscii(input.charAt(i))) {
3467 extras++;
3468 }
3469 }
3470 return utfLength + extras;
3471 }
3472
3473 /**
3474 * string length
3475 * @param string
3476 * @return string length
3477 */
3478 public static int stringLength(String string) {
3479 return string == null ? 0 : string.length();
3480 }
3481
3482 /**
3483 * find the length of ascii chars (non ascii are counted as two)
3484 * @param input is the string to operate on
3485 * @param requiredLength length we need the string to be
3486 * @return the length of ascii chars
3487 */
3488 public static String truncateAscii(String input, int requiredLength) {
3489 if (input == null) {
3490 return input;
3491 }
3492 //see what real length is
3493 int utfLength = input.length();
3494
3495 //see if not worth checking
3496 if (utfLength * 2 < requiredLength) {
3497 return input;
3498 }
3499
3500 //count how many non asciis
3501 int asciiLength = 0;
3502 for (int i=0;i<utfLength;i++) {
3503
3504 asciiLength++;
3505
3506 //keep count of non ascii chars
3507 if (!isAscii(input.charAt(i))) {
3508 asciiLength++;
3509 }
3510
3511 //see if we are over
3512 if (asciiLength > requiredLength) {
3513 //do not include the current char
3514 return input.substring(0,i);
3515 }
3516 }
3517 //must have fit
3518 return input;
3519 }
3520
3521 /**
3522 * convert a subject to string safely
3523 * @param subject
3524 * @return the string value of subject (might be null)
3525 */
3526 public static String subjectToString(Subject subject) {
3527 if (subject == null) {
3528 return null;
3529 }
3530 try {
3531 return "Subject id: " + subject.getId() + ", sourceId: " + subject.getSource().getId();
3532 } catch (RuntimeException e) {
3533 //might be subject not found if lazy subject
3534 return subject.toString();
3535 }
3536 }
3537
3538 /**
3539 * null safe string compare
3540 * @param first
3541 * @param second
3542 * @return true if equal
3543 */
3544 public static boolean equals(String first, String second) {
3545 if (first == second) {
3546 return true;
3547 }
3548 if (first == null || second == null) {
3549 return false;
3550 }
3551 return first.equals(second);
3552 }
3553
3554 /**
3555 * <p>Checks if a String is whitespace, empty ("") or null.</p>
3556 *
3557 * <pre>
3558 * isBlank(null) = true
3559 * isBlank("") = true
3560 * isBlank(" ") = true
3561 * isBlank("bob") = false
3562 * isBlank(" bob ") = false
3563 * </pre>
3564 *
3565 * @param str the String to check, may be null
3566 * @return <code>true</code> if the String is null, empty or whitespace
3567 * @since 2.0
3568 */
3569 public static boolean isBlank(String str) {
3570 int strLen;
3571 if (str == null || (strLen = str.length()) == 0) {
3572 return true;
3573 }
3574 for (int i = 0; i < strLen; i++) {
3575 if ((Character.isWhitespace(str.charAt(i)) == false)) {
3576 return false;
3577 }
3578 }
3579 return true;
3580 }
3581
3582 /**
3583 *
3584 * @param str
3585 * @return true if not blank
3586 */
3587 public static boolean isNotBlank(String str) {
3588 return !isBlank(str);
3589 }
3590
3591 /**
3592 * trim whitespace from string
3593 * @param str
3594 * @return trimmed string
3595 */
3596 public static String trim(String str) {
3597 return str == null ? null : str.trim();
3598 }
3599
3600 /**
3601 * equalsignorecase
3602 * @param str1
3603 * @param str2
3604 * @return true if the strings are equal ignore case
3605 */
3606 public static boolean equalsIgnoreCase(String str1, String str2) {
3607 return str1 == null ? str2 == null : str1.equalsIgnoreCase(str2);
3608 }
3609
3610 /**
3611 * trim to empty, convert null to empty
3612 * @param str
3613 * @return trimmed
3614 */
3615 public static String trimToEmpty(String str) {
3616 return str == null ? "" : str.trim();
3617 }
3618
3619 /**
3620 * <p>Abbreviates a String using ellipses. This will turn
3621 * "Now is the time for all good men" into "Now is the time for..."</p>
3622 *
3623 * <p>Specifically:
3624 * <ul>
3625 * <li>If <code>str</code> is less than <code>maxWidth</code> characters
3626 * long, return it.</li>
3627 * <li>Else abbreviate it to <code>(substring(str, 0, max-3) + "...")</code>.</li>
3628 * <li>If <code>maxWidth</code> is less than <code>4</code>, throw an
3629 * <code>IllegalArgumentException</code>.</li>
3630 * <li>In no case will it return a String of length greater than
3631 * <code>maxWidth</code>.</li>
3632 * </ul>
3633 * </p>
3634 *
3635 * <pre>
3636 * StringUtils.abbreviate(null, *) = null
3637 * StringUtils.abbreviate("", 4) = ""
3638 * StringUtils.abbreviate("abcdefg", 6) = "abc..."
3639 * StringUtils.abbreviate("abcdefg", 7) = "abcdefg"
3640 * StringUtils.abbreviate("abcdefg", 8) = "abcdefg"
3641 * StringUtils.abbreviate("abcdefg", 4) = "a..."
3642 * StringUtils.abbreviate("abcdefg", 3) = IllegalArgumentException
3643 * </pre>
3644 *
3645 * @param str the String to check, may be null
3646 * @param maxWidth maximum length of result String, must be at least 4
3647 * @return abbreviated String, <code>null</code> if null String input
3648 * @throws IllegalArgumentException if the width is too small
3649 * @since 2.0
3650 */
3651 public static String abbreviate(String str, int maxWidth) {
3652 return abbreviate(str, 0, maxWidth);
3653 }
3654
3655 /**
3656 * <p>Abbreviates a String using ellipses. This will turn
3657 * "Now is the time for all good men" into "...is the time for..."</p>
3658 *
3659 * <p>Works like <code>abbreviate(String, int)</code>, but allows you to specify
3660 * a "left edge" offset. Note that this left edge is not necessarily going to
3661 * be the leftmost character in the result, or the first character following the
3662 * ellipses, but it will appear somewhere in the result.
3663 *
3664 * <p>In no case will it return a String of length greater than
3665 * <code>maxWidth</code>.</p>
3666 *
3667 * <pre>
3668 * StringUtils.abbreviate(null, *, *) = null
3669 * StringUtils.abbreviate("", 0, 4) = ""
3670 * StringUtils.abbreviate("abcdefghijklmno", -1, 10) = "abcdefg..."
3671 * StringUtils.abbreviate("abcdefghijklmno", 0, 10) = "abcdefg..."
3672 * StringUtils.abbreviate("abcdefghijklmno", 1, 10) = "abcdefg..."
3673 * StringUtils.abbreviate("abcdefghijklmno", 4, 10) = "abcdefg..."
3674 * StringUtils.abbreviate("abcdefghijklmno", 5, 10) = "...fghi..."
3675 * StringUtils.abbreviate("abcdefghijklmno", 6, 10) = "...ghij..."
3676 * StringUtils.abbreviate("abcdefghijklmno", 8, 10) = "...ijklmno"
3677 * StringUtils.abbreviate("abcdefghijklmno", 10, 10) = "...ijklmno"
3678 * StringUtils.abbreviate("abcdefghijklmno", 12, 10) = "...ijklmno"
3679 * StringUtils.abbreviate("abcdefghij", 0, 3) = IllegalArgumentException
3680 * StringUtils.abbreviate("abcdefghij", 5, 6) = IllegalArgumentException
3681 * </pre>
3682 *
3683 * @param str the String to check, may be null
3684 * @param offset left edge of source String
3685 * @param maxWidth maximum length of result String, must be at least 4
3686 * @return abbreviated String, <code>null</code> if null String input
3687 * @throws IllegalArgumentException if the width is too small
3688 * @since 2.0
3689 */
3690 public static String abbreviate(String str, int offset, int maxWidth) {
3691 if (str == null) {
3692 return null;
3693 }
3694 if (maxWidth < 4) {
3695 throw new IllegalArgumentException("Minimum abbreviation width is 4");
3696 }
3697 if (str.length() <= maxWidth) {
3698 return str;
3699 }
3700 if (offset > str.length()) {
3701 offset = str.length();
3702 }
3703 if ((str.length() - offset) < (maxWidth - 3)) {
3704 offset = str.length() - (maxWidth - 3);
3705 }
3706 if (offset <= 4) {
3707 return str.substring(0, maxWidth - 3) + "...";
3708 }
3709 if (maxWidth < 7) {
3710 throw new IllegalArgumentException("Minimum abbreviation width with offset is 7");
3711 }
3712 if ((offset + (maxWidth - 3)) < str.length()) {
3713 return "..." + abbreviate(str.substring(offset), maxWidth - 3);
3714 }
3715 return "..." + str.substring(str.length() - (maxWidth - 3));
3716 }
3717
3718 // Splitting
3719 //-----------------------------------------------------------------------
3720 /**
3721 * <p>Splits the provided text into an array, using whitespace as the
3722 * separator.
3723 * Whitespace is defined by {@link Character#isWhitespace(char)}.</p>
3724 *
3725 * <p>The separator is not included in the returned String array.
3726 * Adjacent separators are treated as one separator.
3727 * For more control over the split use the StrTokenizer class.</p>
3728 *
3729 * <p>A <code>null</code> input String returns <code>null</code>.</p>
3730 *
3731 * <pre>
3732 * StringUtils.split(null) = null
3733 * StringUtils.split("") = []
3734 * StringUtils.split("abc def") = ["abc", "def"]
3735 * StringUtils.split("abc def") = ["abc", "def"]
3736 * StringUtils.split(" abc ") = ["abc"]
3737 * </pre>
3738 *
3739 * @param str the String to parse, may be null
3740 * @return an array of parsed Strings, <code>null</code> if null String input
3741 */
3742 public static String[] split(String str) {
3743 return split(str, null, -1);
3744 }
3745
3746 /**
3747 * <p>Splits the provided text into an array, separator specified.
3748 * This is an alternative to using StringTokenizer.</p>
3749 *
3750 * <p>The separator is not included in the returned String array.
3751 * Adjacent separators are treated as one separator.
3752 * For more control over the split use the StrTokenizer class.</p>
3753 *
3754 * <p>A <code>null</code> input String returns <code>null</code>.</p>
3755 *
3756 * <pre>
3757 * StringUtils.split(null, *) = null
3758 * StringUtils.split("", *) = []
3759 * StringUtils.split("a.b.c", '.') = ["a", "b", "c"]
3760 * StringUtils.split("a..b.c", '.') = ["a", "b", "c"]
3761 * StringUtils.split("a:b:c", '.') = ["a:b:c"]
3762 * StringUtils.split("a\tb\nc", null) = ["a", "b", "c"]
3763 * StringUtils.split("a b c", ' ') = ["a", "b", "c"]
3764 * </pre>
3765 *
3766 * @param str the String to parse, may be null
3767 * @param separatorChar the character used as the delimiter,
3768 * <code>null</code> splits on whitespace
3769 * @return an array of parsed Strings, <code>null</code> if null String input
3770 * @since 2.0
3771 */
3772 public static String[] split(String str, char separatorChar) {
3773 return splitWorker(str, separatorChar, false);
3774 }
3775
3776 /**
3777 * <p>Splits the provided text into an array, separators specified.
3778 * This is an alternative to using StringTokenizer.</p>
3779 *
3780 * <p>The separator is not included in the returned String array.
3781 * Adjacent separators are treated as one separator.
3782 * For more control over the split use the StrTokenizer class.</p>
3783 *
3784 * <p>A <code>null</code> input String returns <code>null</code>.
3785 * A <code>null</code> separatorChars splits on whitespace.</p>
3786 *
3787 * <pre>
3788 * StringUtils.split(null, *) = null
3789 * StringUtils.split("", *) = []
3790 * StringUtils.split("abc def", null) = ["abc", "def"]
3791 * StringUtils.split("abc def", " ") = ["abc", "def"]
3792 * StringUtils.split("abc def", " ") = ["abc", "def"]
3793 * StringUtils.split("ab:cd:ef", ":") = ["ab", "cd", "ef"]
3794 * </pre>
3795 *
3796 * @param str the String to parse, may be null
3797 * @param separatorChars the characters used as the delimiters,
3798 * <code>null</code> splits on whitespace
3799 * @return an array of parsed Strings, <code>null</code> if null String input
3800 */
3801 public static String[] split(String str, String separatorChars) {
3802 return splitWorker(str, separatorChars, -1, false);
3803 }
3804
3805 /**
3806 * <p>Splits the provided text into an array with a maximum length,
3807 * separators specified.</p>
3808 *
3809 * <p>The separator is not included in the returned String array.
3810 * Adjacent separators are treated as one separator.</p>
3811 *
3812 * <p>A <code>null</code> input String returns <code>null</code>.
3813 * A <code>null</code> separatorChars splits on whitespace.</p>
3814 *
3815 * <p>If more than <code>max</code> delimited substrings are found, the last
3816 * returned string includes all characters after the first <code>max - 1</code>
3817 * returned strings (including separator characters).</p>
3818 *
3819 * <pre>
3820 * StringUtils.split(null, *, *) = null
3821 * StringUtils.split("", *, *) = []
3822 * StringUtils.split("ab de fg", null, 0) = ["ab", "cd", "ef"]
3823 * StringUtils.split("ab de fg", null, 0) = ["ab", "cd", "ef"]
3824 * StringUtils.split("ab:cd:ef", ":", 0) = ["ab", "cd", "ef"]
3825 * StringUtils.split("ab:cd:ef", ":", 2) = ["ab", "cd:ef"]
3826 * </pre>
3827 *
3828 * @param str the String to parse, may be null
3829 * @param separatorChars the characters used as the delimiters,
3830 * <code>null</code> splits on whitespace
3831 * @param max the maximum number of elements to include in the
3832 * array. A zero or negative value implies no limit
3833 * @return an array of parsed Strings, <code>null</code> if null String input
3834 */
3835 public static String[] split(String str, String separatorChars, int max) {
3836 return splitWorker(str, separatorChars, max, false);
3837 }
3838
3839 /**
3840 * <p>Splits the provided text into an array, separator string specified.</p>
3841 *
3842 * <p>The separator(s) will not be included in the returned String array.
3843 * Adjacent separators are treated as one separator.</p>
3844 *
3845 * <p>A <code>null</code> input String returns <code>null</code>.
3846 * A <code>null</code> separator splits on whitespace.</p>
3847 *
3848 * <pre>
3849 * StringUtils.split(null, *) = null
3850 * StringUtils.split("", *) = []
3851 * StringUtils.split("ab de fg", null) = ["ab", "de", "fg"]
3852 * StringUtils.split("ab de fg", null) = ["ab", "de", "fg"]
3853 * StringUtils.split("ab:cd:ef", ":") = ["ab", "cd", "ef"]
3854 * StringUtils.split("abstemiouslyaeiouyabstemiously", "aeiouy") = ["bst", "m", "sl", "bst", "m", "sl"]
3855 * StringUtils.split("abstemiouslyaeiouyabstemiously", "aeiouy") = ["abstemiously", "abstemiously"]
3856 * </pre>
3857 *
3858 * @param str the String to parse, may be null
3859 * @param separator String containing the String to be used as a delimiter,
3860 * <code>null</code> splits on whitespace
3861 * @return an array of parsed Strings, <code>null</code> if null String was input
3862 */
3863 public static String[] splitByWholeSeparator(String str, String separator) {
3864 return splitByWholeSeparator(str, separator, -1);
3865 }
3866
3867 /**
3868 * <p>Splits the provided text into an array, separator string specified.
3869 * Returns a maximum of <code>max</code> substrings.</p>
3870 *
3871 * <p>The separator(s) will not be included in the returned String array.
3872 * Adjacent separators are treated as one separator.</p>
3873 *
3874 * <p>A <code>null</code> input String returns <code>null</code>.
3875 * A <code>null</code> separator splits on whitespace.</p>
3876 *
3877 * <pre>
3878 * StringUtils.splitByWholeSeparator(null, *, *) = null
3879 * StringUtils.splitByWholeSeparator("", *, *) = []
3880 * StringUtils.splitByWholeSeparator("ab de fg", null, 0) = ["ab", "de", "fg"]
3881 * StringUtils.splitByWholeSeparator("ab de fg", null, 0) = ["ab", "de", "fg"]
3882 * StringUtils.splitByWholeSeparator("ab:cd:ef", ":", 2) = ["ab", "cd"]
3883 * StringUtils.splitByWholeSeparator("abstemiouslyaeiouyabstemiously", "aeiouy", 2) = ["bst", "m"]
3884 * StringUtils.splitByWholeSeparator("abstemiouslyaeiouyabstemiously", "aeiouy", 2) = ["abstemiously", "abstemiously"]
3885 * </pre>
3886 *
3887 * @param str the String to parse, may be null
3888 * @param separator String containing the String to be used as a delimiter,
3889 * <code>null</code> splits on whitespace
3890 * @param max the maximum number of elements to include in the returned
3891 * array. A zero or negative value implies no limit.
3892 * @return an array of parsed Strings, <code>null</code> if null String was input
3893 */
3894 public static String[] splitByWholeSeparator(String str, String separator, int max) {
3895 if (str == null) {
3896 return null;
3897 }
3898
3899 int len = str.length();
3900
3901 if (len == 0) {
3902 return EMPTY_STRING_ARRAY;
3903 }
3904
3905 if ((separator == null) || ("".equals(separator))) {
3906 // Split on whitespace.
3907 return split(str, null, max);
3908 }
3909
3910 int separatorLength = separator.length();
3911
3912 ArrayList substrings = new ArrayList();
3913 int numberOfSubstrings = 0;
3914 int beg = 0;
3915 int end = 0;
3916 while (end < len) {
3917 end = str.indexOf(separator, beg);
3918
3919 if (end > -1) {
3920 if (end > beg) {
3921 numberOfSubstrings += 1;
3922
3923 if (numberOfSubstrings == max) {
3924 end = len;
3925 substrings.add(str.substring(beg));
3926 } else {
3927 // The following is OK, because String.substring( beg, end ) excludes
3928 // the character at the position 'end'.
3929 substrings.add(str.substring(beg, end));
3930
3931 // Set the starting point for the next search.
3932 // The following is equivalent to beg = end + (separatorLength - 1) + 1,
3933 // which is the right calculation:
3934 beg = end + separatorLength;
3935 }
3936 } else {
3937 // We found a consecutive occurrence of the separator, so skip it.
3938 beg = end + separatorLength;
3939 }
3940 } else {
3941 // String.substring( beg ) goes from 'beg' to the end of the String.
3942 substrings.add(str.substring(beg));
3943 end = len;
3944 }
3945 }
3946
3947 return (String[]) substrings.toArray(new String[substrings.size()]);
3948 }
3949
3950 //-----------------------------------------------------------------------
3951 /**
3952 * <p>Splits the provided text into an array, using whitespace as the
3953 * separator, preserving all tokens, including empty tokens created by
3954 * adjacent separators. This is an alternative to using StringTokenizer.
3955 * Whitespace is defined by {@link Character#isWhitespace(char)}.</p>
3956 *
3957 * <p>The separator is not included in the returned String array.
3958 * Adjacent separators are treated as separators for empty tokens.
3959 * For more control over the split use the StrTokenizer class.</p>
3960 *
3961 * <p>A <code>null</code> input String returns <code>null</code>.</p>
3962 *
3963 * <pre>
3964 * StringUtils.splitPreserveAllTokens(null) = null
3965 * StringUtils.splitPreserveAllTokens("") = []
3966 * StringUtils.splitPreserveAllTokens("abc def") = ["abc", "def"]
3967 * StringUtils.splitPreserveAllTokens("abc def") = ["abc", "", "def"]
3968 * StringUtils.splitPreserveAllTokens(" abc ") = ["", "abc", ""]
3969 * </pre>
3970 *
3971 * @param str the String to parse, may be <code>null</code>
3972 * @return an array of parsed Strings, <code>null</code> if null String input
3973 * @since 2.1
3974 */
3975 public static String[] splitPreserveAllTokens(String str) {
3976 return splitWorker(str, null, -1, true);
3977 }
3978
3979 /**
3980 * <p>Splits the provided text into an array, separator specified,
3981 * preserving all tokens, including empty tokens created by adjacent
3982 * separators. This is an alternative to using StringTokenizer.</p>
3983 *
3984 * <p>The separator is not included in the returned String array.
3985 * Adjacent separators are treated as separators for empty tokens.
3986 * For more control over the split use the StrTokenizer class.</p>
3987 *
3988 * <p>A <code>null</code> input String returns <code>null</code>.</p>
3989 *
3990 * <pre>
3991 * StringUtils.splitPreserveAllTokens(null, *) = null
3992 * StringUtils.splitPreserveAllTokens("", *) = []
3993 * StringUtils.splitPreserveAllTokens("a.b.c", '.') = ["a", "b", "c"]
3994 * StringUtils.splitPreserveAllTokens("a..b.c", '.') = ["a", "b", "c"]
3995 * StringUtils.splitPreserveAllTokens("a:b:c", '.') = ["a:b:c"]
3996 * StringUtils.splitPreserveAllTokens("a\tb\nc", null) = ["a", "b", "c"]
3997 * StringUtils.splitPreserveAllTokens("a b c", ' ') = ["a", "b", "c"]
3998 * StringUtils.splitPreserveAllTokens("a b c ", ' ') = ["a", "b", "c", ""]
3999 * StringUtils.splitPreserveAllTokens("a b c ", ' ') = ["a", "b", "c", "", ""]
4000 * StringUtils.splitPreserveAllTokens(" a b c", ' ') = ["", a", "b", "c"]
4001 * StringUtils.splitPreserveAllTokens(" a b c", ' ') = ["", "", a", "b", "c"]
4002 * StringUtils.splitPreserveAllTokens(" a b c ", ' ') = ["", a", "b", "c", ""]
4003 * </pre>
4004 *
4005 * @param str the String to parse, may be <code>null</code>
4006 * @param separatorChar the character used as the delimiter,
4007 * <code>null</code> splits on whitespace
4008 * @return an array of parsed Strings, <code>null</code> if null String input
4009 * @since 2.1
4010 */
4011 public static String[] splitPreserveAllTokens(String str, char separatorChar) {
4012 return splitWorker(str, separatorChar, true);
4013 }
4014
4015 /**
4016 * Performs the logic for the <code>split</code> and
4017 * <code>splitPreserveAllTokens</code> methods that do not return a
4018 * maximum array length.
4019 *
4020 * @param str the String to parse, may be <code>null</code>
4021 * @param separatorChar the separate character
4022 * @param preserveAllTokens if <code>true</code>, adjacent separators are
4023 * treated as empty token separators; if <code>false</code>, adjacent
4024 * separators are treated as one separator.
4025 * @return an array of parsed Strings, <code>null</code> if null String input
4026 */
4027 private static String[] splitWorker(String str, char separatorChar,
4028 boolean preserveAllTokens) {
4029 // Performance tuned for 2.0 (JDK1.4)
4030
4031 if (str == null) {
4032 return null;
4033 }
4034 int len = str.length();
4035 if (len == 0) {
4036 return EMPTY_STRING_ARRAY;
4037 }
4038 List list = new ArrayList();
4039 int i = 0, start = 0;
4040 boolean match = false;
4041 boolean lastMatch = false;
4042 while (i < len) {
4043 if (str.charAt(i) == separatorChar) {
4044 if (match || preserveAllTokens) {
4045 list.add(str.substring(start, i));
4046 match = false;
4047 lastMatch = true;
4048 }
4049 start = ++i;
4050 continue;
4051 }
4052 lastMatch = false;
4053 match = true;
4054 i++;
4055 }
4056 if (match || (preserveAllTokens && lastMatch)) {
4057 list.add(str.substring(start, i));
4058 }
4059 return (String[]) list.toArray(new String[list.size()]);
4060 }
4061
4062 /**
4063 * <p>Splits the provided text into an array, separators specified,
4064 * preserving all tokens, including empty tokens created by adjacent
4065 * separators. This is an alternative to using StringTokenizer.</p>
4066 *
4067 * <p>The separator is not included in the returned String array.
4068 * Adjacent separators are treated as separators for empty tokens.
4069 * For more control over the split use the StrTokenizer class.</p>
4070 *
4071 * <p>A <code>null</code> input String returns <code>null</code>.
4072 * A <code>null</code> separatorChars splits on whitespace.</p>
4073 *
4074 * <pre>
4075 * StringUtils.splitPreserveAllTokens(null, *) = null
4076 * StringUtils.splitPreserveAllTokens("", *) = []
4077 * StringUtils.splitPreserveAllTokens("abc def", null) = ["abc", "def"]
4078 * StringUtils.splitPreserveAllTokens("abc def", " ") = ["abc", "def"]
4079 * StringUtils.splitPreserveAllTokens("abc def", " ") = ["abc", "", def"]
4080 * StringUtils.splitPreserveAllTokens("ab:cd:ef", ":") = ["ab", "cd", "ef"]
4081 * StringUtils.splitPreserveAllTokens("ab:cd:ef:", ":") = ["ab", "cd", "ef", ""]
4082 * StringUtils.splitPreserveAllTokens("ab:cd:ef::", ":") = ["ab", "cd", "ef", "", ""]
4083 * StringUtils.splitPreserveAllTokens("ab::cd:ef", ":") = ["ab", "", cd", "ef"]
4084 * StringUtils.splitPreserveAllTokens(":cd:ef", ":") = ["", cd", "ef"]
4085 * StringUtils.splitPreserveAllTokens("::cd:ef", ":") = ["", "", cd", "ef"]
4086 * StringUtils.splitPreserveAllTokens(":cd:ef:", ":") = ["", cd", "ef", ""]
4087 * </pre>
4088 *
4089 * @param str the String to parse, may be <code>null</code>
4090 * @param separatorChars the characters used as the delimiters,
4091 * <code>null</code> splits on whitespace
4092 * @return an array of parsed Strings, <code>null</code> if null String input
4093 * @since 2.1
4094 */
4095 public static String[] splitPreserveAllTokens(String str, String separatorChars) {
4096 return splitWorker(str, separatorChars, -1, true);
4097 }
4098
4099 /**
4100 * <p>Splits the provided text into an array with a maximum length,
4101 * separators specified, preserving all tokens, including empty tokens
4102 * created by adjacent separators.</p>
4103 *
4104 * <p>The separator is not included in the returned String array.
4105 * Adjacent separators are treated as separators for empty tokens.
4106 * Adjacent separators are treated as one separator.</p>
4107 *
4108 * <p>A <code>null</code> input String returns <code>null</code>.
4109 * A <code>null</code> separatorChars splits on whitespace.</p>
4110 *
4111 * <p>If more than <code>max</code> delimited substrings are found, the last
4112 * returned string includes all characters after the first <code>max - 1</code>
4113 * returned strings (including separator characters).</p>
4114 *
4115 * <pre>
4116 * StringUtils.splitPreserveAllTokens(null, *, *) = null
4117 * StringUtils.splitPreserveAllTokens("", *, *) = []
4118 * StringUtils.splitPreserveAllTokens("ab de fg", null, 0) = ["ab", "cd", "ef"]
4119 * StringUtils.splitPreserveAllTokens("ab de fg", null, 0) = ["ab", "cd", "ef"]
4120 * StringUtils.splitPreserveAllTokens("ab:cd:ef", ":", 0) = ["ab", "cd", "ef"]
4121 * StringUtils.splitPreserveAllTokens("ab:cd:ef", ":", 2) = ["ab", "cd:ef"]
4122 * StringUtils.splitPreserveAllTokens("ab de fg", null, 2) = ["ab", " de fg"]
4123 * StringUtils.splitPreserveAllTokens("ab de fg", null, 3) = ["ab", "", " de fg"]
4124 * StringUtils.splitPreserveAllTokens("ab de fg", null, 4) = ["ab", "", "", "de fg"]
4125 * </pre>
4126 *
4127 * @param str the String to parse, may be <code>null</code>
4128 * @param separatorChars the characters used as the delimiters,
4129 * <code>null</code> splits on whitespace
4130 * @param max the maximum number of elements to include in the
4131 * array. A zero or negative value implies no limit
4132 * @return an array of parsed Strings, <code>null</code> if null String input
4133 * @since 2.1
4134 */
4135 public static String[] splitPreserveAllTokens(String str, String separatorChars, int max) {
4136 return splitWorker(str, separatorChars, max, true);
4137 }
4138
4139 /**
4140 * Performs the logic for the <code>split</code> and
4141 * <code>splitPreserveAllTokens</code> methods that return a maximum array
4142 * length.
4143 *
4144 * @param str the String to parse, may be <code>null</code>
4145 * @param separatorChars the separate character
4146 * @param max the maximum number of elements to include in the
4147 * array. A zero or negative value implies no limit.
4148 * @param preserveAllTokens if <code>true</code>, adjacent separators are
4149 * treated as empty token separators; if <code>false</code>, adjacent
4150 * separators are treated as one separator.
4151 * @return an array of parsed Strings, <code>null</code> if null String input
4152 */
4153 private static String[] splitWorker(String str, String separatorChars, int max,
4154 boolean preserveAllTokens) {
4155 // Performance tuned for 2.0 (JDK1.4)
4156 // Direct code is quicker than StringTokenizer.
4157 // Also, StringTokenizer uses isSpace() not isWhitespace()
4158
4159 if (str == null) {
4160 return null;
4161 }
4162 int len = str.length();
4163 if (len == 0) {
4164 return EMPTY_STRING_ARRAY;
4165 }
4166 List list = new ArrayList();
4167 int sizePlus1 = 1;
4168 int i = 0, start = 0;
4169 boolean match = false;
4170 boolean lastMatch = false;
4171 if (separatorChars == null) {
4172 // Null separator means use whitespace
4173 while (i < len) {
4174 if (Character.isWhitespace(str.charAt(i))) {
4175 if (match || preserveAllTokens) {
4176 lastMatch = true;
4177 if (sizePlus1++ == max) {
4178 i = len;
4179 lastMatch = false;
4180 }
4181 list.add(str.substring(start, i));
4182 match = false;
4183 }
4184 start = ++i;
4185 continue;
4186 }
4187 lastMatch = false;
4188 match = true;
4189 i++;
4190 }
4191 } else if (separatorChars.length() == 1) {
4192 // Optimise 1 character case
4193 char sep = separatorChars.charAt(0);
4194 while (i < len) {
4195 if (str.charAt(i) == sep) {
4196 if (match || preserveAllTokens) {
4197 lastMatch = true;
4198 if (sizePlus1++ == max) {
4199 i = len;
4200 lastMatch = false;
4201 }
4202 list.add(str.substring(start, i));
4203 match = false;
4204 }
4205 start = ++i;
4206 continue;
4207 }
4208 lastMatch = false;
4209 match = true;
4210 i++;
4211 }
4212 } else {
4213 // standard case
4214 while (i < len) {
4215 if (separatorChars.indexOf(str.charAt(i)) >= 0) {
4216 if (match || preserveAllTokens) {
4217 lastMatch = true;
4218 if (sizePlus1++ == max) {
4219 i = len;
4220 lastMatch = false;
4221 }
4222 list.add(str.substring(start, i));
4223 match = false;
4224 }
4225 start = ++i;
4226 continue;
4227 }
4228 lastMatch = false;
4229 match = true;
4230 i++;
4231 }
4232 }
4233 if (match || (preserveAllTokens && lastMatch)) {
4234 list.add(str.substring(start, i));
4235 }
4236 return (String[]) list.toArray(new String[list.size()]);
4237 }
4238
4239 /**
4240 * <p>Joins the elements of the provided array into a single String
4241 * containing the provided list of elements.</p>
4242 *
4243 * <p>No separator is added to the joined String.
4244 * Null objects or empty strings within the array are represented by
4245 * empty strings.</p>
4246 *
4247 * <pre>
4248 * StringUtils.join(null) = null
4249 * StringUtils.join([]) = ""
4250 * StringUtils.join([null]) = ""
4251 * StringUtils.join(["a", "b", "c"]) = "abc"
4252 * StringUtils.join([null, "", "a"]) = "a"
4253 * </pre>
4254 *
4255 * @param array the array of values to join together, may be null
4256 * @return the joined String, <code>null</code> if null array input
4257 * @since 2.0
4258 */
4259 public static String join(Object[] array) {
4260 return join(array, null);
4261 }
4262
4263 /**
4264 * <p>Joins the elements of the provided array into a single String
4265 * containing the provided list of elements.</p>
4266 *
4267 * <p>No delimiter is added before or after the list.
4268 * Null objects or empty strings within the array are represented by
4269 * empty strings.</p>
4270 *
4271 * <pre>
4272 * StringUtils.join(null, *) = null
4273 * StringUtils.join([], *) = ""
4274 * StringUtils.join([null], *) = ""
4275 * StringUtils.join(["a", "b", "c"], ';') = "a;b;c"
4276 * StringUtils.join(["a", "b", "c"], null) = "abc"
4277 * StringUtils.join([null, "", "a"], ';') = ";;a"
4278 * </pre>
4279 *
4280 * @param array the array of values to join together, may be null
4281 * @param separator the separator character to use
4282 * @return the joined String, <code>null</code> if null array input
4283 * @since 2.0
4284 */
4285 public static String join(Object[] array, char separator) {
4286 if (array == null) {
4287 return null;
4288 }
4289 int arraySize = array.length;
4290 int bufSize = (arraySize == 0 ? 0 : ((array[0] == null ? 16 : array[0].toString()
4291 .length()) + 1)
4292 * arraySize);
4293 StringBuffer buf = new StringBuffer(bufSize);
4294
4295 for (int i = 0; i < arraySize; i++) {
4296 if (i > 0) {
4297 buf.append(separator);
4298 }
4299 if (array[i] != null) {
4300 buf.append(array[i]);
4301 }
4302 }
4303 return buf.toString();
4304 }
4305
4306 /**
4307 * <p>Joins the elements of the provided array into a single String
4308 * containing the provided list of elements.</p>
4309 *
4310 * <p>No delimiter is added before or after the list.
4311 * A <code>null</code> separator is the same as an empty String ("").
4312 * Null objects or empty strings within the array are represented by
4313 * empty strings.</p>
4314 *
4315 * <pre>
4316 * StringUtils.join(null, *) = null
4317 * StringUtils.join([], *) = ""
4318 * StringUtils.join([null], *) = ""
4319 * StringUtils.join(["a", "b", "c"], "--") = "a--b--c"
4320 * StringUtils.join(["a", "b", "c"], null) = "abc"
4321 * StringUtils.join(["a", "b", "c"], "") = "abc"
4322 * StringUtils.join([null, "", "a"], ',') = ",,a"
4323 * </pre>
4324 *
4325 * @param array the array of values to join together, may be null
4326 * @param separator the separator character to use, null treated as ""
4327 * @return the joined String, <code>null</code> if null array input
4328 */
4329 public static String join(Object[] array, String separator) {
4330 if (array == null) {
4331 return null;
4332 }
4333 if (separator == null) {
4334 separator = "";
4335 }
4336 int arraySize = array.length;
4337
4338 // ArraySize == 0: Len = 0
4339 // ArraySize > 0: Len = NofStrings *(len(firstString) + len(separator))
4340 // (Assuming that all Strings are roughly equally long)
4341 int bufSize = ((arraySize == 0) ? 0 : arraySize
4342 * ((array[0] == null ? 16 : array[0].toString().length()) + separator.length()));
4343
4344 StringBuffer buf = new StringBuffer(bufSize);
4345
4346 for (int i = 0; i < arraySize; i++) {
4347 if (i > 0) {
4348 buf.append(separator);
4349 }
4350 if (array[i] != null) {
4351 buf.append(array[i]);
4352 }
4353 }
4354 return buf.toString();
4355 }
4356
4357 /**
4358 * <p>Joins the elements of the provided <code>Iterator</code> into
4359 * a single String containing the provided elements.</p>
4360 *
4361 * <p>No delimiter is added before or after the list. Null objects or empty
4362 * strings within the iteration are represented by empty strings.</p>
4363 *
4364 * <p>See the examples here: {@link #join(Object[],char)}. </p>
4365 *
4366 * @param iterator the <code>Iterator</code> of values to join together, may be null
4367 * @param separator the separator character to use
4368 * @return the joined String, <code>null</code> if null iterator input
4369 * @since 2.0
4370 */
4371 public static String join(Iterator iterator, char separator) {
4372 if (iterator == null) {
4373 return null;
4374 }
4375 StringBuffer buf = new StringBuffer(256); // Java default is 16, probably too small
4376 while (iterator.hasNext()) {
4377 Object obj = iterator.next();
4378 if (obj != null) {
4379 buf.append(obj);
4380 }
4381 if (iterator.hasNext()) {
4382 buf.append(separator);
4383 }
4384 }
4385 return buf.toString();
4386 }
4387
4388 /**
4389 * <p>Joins the elements of the provided <code>Iterator</code> into
4390 * a single String containing the provided elements.</p>
4391 *
4392 * <p>No delimiter is added before or after the list.
4393 * A <code>null</code> separator is the same as an empty String ("").</p>
4394 *
4395 * <p>See the examples here: {@link #join(Object[],String)}. </p>
4396 *
4397 * @param iterator the <code>Iterator</code> of values to join together, may be null
4398 * @param separator the separator character to use, null treated as ""
4399 * @return the joined String, <code>null</code> if null iterator input
4400 */
4401 public static String join(Iterator iterator, String separator) {
4402 if (iterator == null) {
4403 return null;
4404 }
4405 StringBuffer buf = new StringBuffer(256); // Java default is 16, probably too small
4406 while (iterator.hasNext()) {
4407 Object obj = iterator.next();
4408 if (obj != null) {
4409 buf.append(obj);
4410 }
4411 if ((separator != null) && iterator.hasNext()) {
4412 buf.append(separator);
4413 }
4414 }
4415 return buf.toString();
4416 }
4417
4418 /**
4419 * <p>Returns either the passed in String,
4420 * or if the String is <code>null</code>, an empty String ("").</p>
4421 *
4422 * <pre>
4423 * StringUtils.defaultString(null) = ""
4424 * StringUtils.defaultString("") = ""
4425 * StringUtils.defaultString("bat") = "bat"
4426 * </pre>
4427 *
4428 * @see String#valueOf(Object)
4429 * @param str the String to check, may be null
4430 * @return the passed in String, or the empty String if it
4431 * was <code>null</code>
4432 */
4433 public static String defaultString(String str) {
4434 return str == null ? "" : str;
4435 }
4436
4437 /**
4438 * <p>Returns either the passed in String, or if the String is
4439 * <code>null</code>, the value of <code>defaultStr</code>.</p>
4440 *
4441 * <pre>
4442 * StringUtils.defaultString(null, "NULL") = "NULL"
4443 * StringUtils.defaultString("", "NULL") = ""
4444 * StringUtils.defaultString("bat", "NULL") = "bat"
4445 * </pre>
4446 *
4447 * @see String#valueOf(Object)
4448 * @param str the String to check, may be null
4449 * @param defaultStr the default String to return
4450 * if the input is <code>null</code>, may be null
4451 * @return the passed in String, or the default if it was <code>null</code>
4452 */
4453 public static String defaultString(String str, String defaultStr) {
4454 return str == null ? defaultStr : str;
4455 }
4456
4457 /**
4458 * <p>Returns either the passed in String, or if the String is
4459 * empty or <code>null</code>, the value of <code>defaultStr</code>.</p>
4460 *
4461 * <pre>
4462 * StringUtils.defaultIfEmpty(null, "NULL") = "NULL"
4463 * StringUtils.defaultIfEmpty("", "NULL") = "NULL"
4464 * StringUtils.defaultIfEmpty("bat", "NULL") = "bat"
4465 * </pre>
4466 *
4467 * @param str the String to check, may be null
4468 * @param defaultStr the default String to return
4469 * if the input is empty ("") or <code>null</code>, may be null
4470 * @return the passed in String, or the default
4471 */
4472 public static String defaultIfEmpty(String str, String defaultStr) {
4473 return isEmpty(str) ? defaultStr : str;
4474 }
4475
4476 /**
4477 * <p>Capitalizes a String changing the first letter to title case as
4478 * per {@link Character#toTitleCase(char)}. No other letters are changed.</p>
4479 *
4480 * A <code>null</code> input String returns <code>null</code>.</p>
4481 *
4482 * <pre>
4483 * StringUtils.capitalize(null) = null
4484 * StringUtils.capitalize("") = ""
4485 * StringUtils.capitalize("cat") = "Cat"
4486 * StringUtils.capitalize("cAt") = "CAt"
4487 * </pre>
4488 *
4489 * @param str the String to capitalize, may be null
4490 * @return the capitalized String, <code>null</code> if null String input
4491 * @since 2.0
4492 */
4493 public static String capitalize(String str) {
4494 int strLen;
4495 if (str == null || (strLen = str.length()) == 0) {
4496 return str;
4497 }
4498 return new StringBuffer(strLen).append(Character.toTitleCase(str.charAt(0))).append(
4499 str.substring(1)).toString();
4500 }
4501
4502 /**
4503 * <p>Converts all the whitespace separated words in a String into capitalized words,
4504 * that is each word is made up of a titlecase character and then a series of
4505 * lowercase characters. </p>
4506 *
4507 * <p>Whitespace is defined by {@link Character#isWhitespace(char)}.
4508 * A <code>null</code> input String returns <code>null</code>.
4509 * Capitalization uses the Unicode title case, normally equivalent to
4510 * upper case.</p>
4511 *
4512 * <pre>
4513 * WordUtils.capitalizeFully(null) = null
4514 * WordUtils.capitalizeFully("") = ""
4515 * WordUtils.capitalizeFully("i am FINE") = "I Am Fine"
4516 * </pre>
4517 *
4518 * @param str the String to capitalize, may be null
4519 * @return capitalized String, <code>null</code> if null String input
4520 */
4521 public static String capitalizeFully(String str) {
4522 return capitalizeFully(str, null);
4523 }
4524
4525 /**
4526 * <p>Converts all the delimiter separated words in a String into capitalized words,
4527 * that is each word is made up of a titlecase character and then a series of
4528 * lowercase characters. </p>
4529 *
4530 * <p>The delimiters represent a set of characters understood to separate words.
4531 * The first string character and the first non-delimiter character after a
4532 * delimiter will be capitalized. </p>
4533 *
4534 * <p>A <code>null</code> input String returns <code>null</code>.
4535 * Capitalization uses the Unicode title case, normally equivalent to
4536 * upper case.</p>
4537 *
4538 * <pre>
4539 * WordUtils.capitalizeFully(null, *) = null
4540 * WordUtils.capitalizeFully("", *) = ""
4541 * WordUtils.capitalizeFully(*, null) = *
4542 * WordUtils.capitalizeFully(*, new char[0]) = *
4543 * WordUtils.capitalizeFully("i aM.fine", {'.'}) = "I am.Fine"
4544 * </pre>
4545 *
4546 * @param str the String to capitalize, may be null
4547 * @param delimiters set of characters to determine capitalization, null means whitespace
4548 * @return capitalized String, <code>null</code> if null String input
4549 * @since 2.1
4550 */
4551 public static String capitalizeFully(String str, char... delimiters) {
4552 int delimLen = delimiters == null ? -1 : delimiters.length;
4553 if (StringUtils.isEmpty(str) || delimLen == 0) {
4554 return str;
4555 }
4556 str = str.toLowerCase();
4557 return capitalize(str, delimiters);
4558 }
4559
4560 /**
4561 * <p>Capitalizes all the delimiter separated words in a String.
4562 * Only the first letter of each word is changed. To convert the
4563 * rest of each word to lowercase at the same time,
4564 * use {@link #capitalizeFully(String, char[])}.</p>
4565 *
4566 * <p>The delimiters represent a set of characters understood to separate words.
4567 * The first string character and the first non-delimiter character after a
4568 * delimiter will be capitalized. </p>
4569 *
4570 * <p>A <code>null</code> input String returns <code>null</code>.
4571 * Capitalization uses the Unicode title case, normally equivalent to
4572 * upper case.</p>
4573 *
4574 * <pre>
4575 * WordUtils.capitalize(null, *) = null
4576 * WordUtils.capitalize("", *) = ""
4577 * WordUtils.capitalize(*, new char[0]) = *
4578 * WordUtils.capitalize("i am fine", null) = "I Am Fine"
4579 * WordUtils.capitalize("i aM.fine", {'.'}) = "I aM.Fine"
4580 * </pre>
4581 *
4582 * @param str the String to capitalize, may be null
4583 * @param delimiters set of characters to determine capitalization, null means whitespace
4584 * @return capitalized String, <code>null</code> if null String input
4585 * @see #capitalizeFully(String)
4586 * @since 2.1
4587 */
4588 public static String capitalize(String str, char... delimiters) {
4589 int delimLen = delimiters == null ? -1 : delimiters.length;
4590 if (StringUtils.isEmpty(str) || delimLen == 0) {
4591 return str;
4592 }
4593 char[] buffer = str.toCharArray();
4594 boolean capitalizeNext = true;
4595 for (int i = 0; i < buffer.length; i++) {
4596 char ch = buffer[i];
4597 if (isDelimiter(ch, delimiters)) {
4598 capitalizeNext = true;
4599 } else if (capitalizeNext) {
4600 buffer[i] = Character.toTitleCase(ch);
4601 capitalizeNext = false;
4602 }
4603 }
4604 return new String(buffer);
4605 }
4606
4607 /**
4608 * Is the character a delimiter.
4609 *
4610 * @param ch the character to check
4611 * @param delimiters the delimiters
4612 * @return true if it is a delimiter
4613 */
4614 private static boolean isDelimiter(char ch, char[] delimiters) {
4615 if (delimiters == null) {
4616 return Character.isWhitespace(ch);
4617 }
4618 for (char delimiter : delimiters) {
4619 if (ch == delimiter) {
4620 return true;
4621 }
4622 }
4623 return false;
4624 }
4625
4626 /**
4627 * <p>Checks if String contains a search character, handling <code>null</code>.
4628 * This method uses {@link String#indexOf(int)}.</p>
4629 *
4630 * <p>A <code>null</code> or empty ("") String will return <code>false</code>.</p>
4631 *
4632 * <pre>
4633 * StringUtils.contains(null, *) = false
4634 * StringUtils.contains("", *) = false
4635 * StringUtils.contains("abc", 'a') = true
4636 * StringUtils.contains("abc", 'z') = false
4637 * </pre>
4638 *
4639 * @param str the String to check, may be null
4640 * @param searchChar the character to find
4641 * @return true if the String contains the search character,
4642 * false if not or <code>null</code> string input
4643 * @since 2.0
4644 */
4645 public static boolean contains(String str, char searchChar) {
4646 if (isEmpty(str)) {
4647 return false;
4648 }
4649 return str.indexOf(searchChar) >= 0;
4650 }
4651
4652 /**
4653 * <p>Checks if String contains a search String, handling <code>null</code>.
4654 * This method uses {@link String#indexOf(int)}.</p>
4655 *
4656 * <p>A <code>null</code> String will return <code>false</code>.</p>
4657 *
4658 * <pre>
4659 * StringUtils.contains(null, *) = false
4660 * StringUtils.contains(*, null) = false
4661 * StringUtils.contains("", "") = true
4662 * StringUtils.contains("abc", "") = true
4663 * StringUtils.contains("abc", "a") = true
4664 * StringUtils.contains("abc", "z") = false
4665 * </pre>
4666 *
4667 * @param str the String to check, may be null
4668 * @param searchStr the String to find, may be null
4669 * @return true if the String contains the search String,
4670 * false if not or <code>null</code> string input
4671 * @since 2.0
4672 */
4673 public static boolean contains(String str, String searchStr) {
4674 if (str == null || searchStr == null) {
4675 return false;
4676 }
4677 return str.indexOf(searchStr) >= 0;
4678 }
4679
4680 /**
4681 * An empty immutable <code>String</code> array.
4682 */
4683 public static final String[] EMPTY_STRING_ARRAY = new String[0];
4684
4685 /**
4686 * <p>Compares two objects for equality, where either one or both
4687 * objects may be <code>null</code>.</p>
4688 *
4689 * <pre>
4690 * ObjectUtils.equals(null, null) = true
4691 * ObjectUtils.equals(null, "") = false
4692 * ObjectUtils.equals("", null) = false
4693 * ObjectUtils.equals("", "") = true
4694 * ObjectUtils.equals(Boolean.TRUE, null) = false
4695 * ObjectUtils.equals(Boolean.TRUE, "true") = false
4696 * ObjectUtils.equals(Boolean.TRUE, Boolean.TRUE) = true
4697 * ObjectUtils.equals(Boolean.TRUE, Boolean.FALSE) = false
4698 * </pre>
4699 *
4700 * @param object1 the first object, may be <code>null</code>
4701 * @param object2 the second object, may be <code>null</code>
4702 * @return <code>true</code> if the values of both objects are the same
4703 */
4704 public static boolean equals(Object object1, Object object2) {
4705 if (object1 == object2) {
4706 return true;
4707 }
4708 if ((object1 == null) || (object2 == null)) {
4709 return false;
4710 }
4711 return object1.equals(object2);
4712 }
4713
4714 /**
4715 * An empty immutable <code>Object</code> array.
4716 */
4717 public static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
4718
4719 /**
4720 * strip the last slash (/ or \) from a string if it exists
4721 *
4722 * @param input
4723 *
4724 * @return input - the last / or \
4725 */
4726 public static String stripLastSlashIfExists(String input) {
4727 if ((input == null) || (input.length() == 0)) {
4728 return null;
4729 }
4730
4731 char lastChar = input.charAt(input.length() - 1);
4732
4733 if ((lastChar == '\\') || (lastChar == '/')) {
4734 return input.substring(0, input.length() - 1);
4735 }
4736
4737 return input;
4738 }
4739
4740 /**
4741 * <p>Strips any of a set of characters from the start of a String.</p>
4742 *
4743 * <p>A <code>null</code> input String returns <code>null</code>.
4744 * An empty string ("") input returns the empty string.</p>
4745 *
4746 * <p>If the stripChars String is <code>null</code>, whitespace is
4747 * stripped as defined by {@link Character#isWhitespace(char)}.</p>
4748 *
4749 * <pre>
4750 * StringUtils.stripStart(null, *) = null
4751 * StringUtils.stripStart("", *) = ""
4752 * StringUtils.stripStart("abc", "") = "abc"
4753 * StringUtils.stripStart("abc", null) = "abc"
4754 * StringUtils.stripStart(" abc", null) = "abc"
4755 * StringUtils.stripStart("abc ", null) = "abc "
4756 * StringUtils.stripStart(" abc ", null) = "abc "
4757 * StringUtils.stripStart("yxabc ", "xyz") = "abc "
4758 * </pre>
4759 *
4760 * @param str the String to remove characters from, may be null
4761 * @param stripChars the characters to remove, null treated as whitespace
4762 * @return the stripped String, <code>null</code> if null String input
4763 */
4764 public static String stripStart(String str, String stripChars) {
4765 int strLen;
4766 if (str == null || (strLen = str.length()) == 0) {
4767 return str;
4768 }
4769 int start = 0;
4770 if (stripChars == null) {
4771 while ((start != strLen) && Character.isWhitespace(str.charAt(start))) {
4772 start++;
4773 }
4774 } else if (stripChars.length() == 0) {
4775 return str;
4776 } else {
4777 while ((start != strLen) && (stripChars.indexOf(str.charAt(start)) != -1)) {
4778 start++;
4779 }
4780 }
4781 return str.substring(start);
4782 }
4783
4784 /**
4785 * <p>Strips any of a set of characters from the end of a String.</p>
4786 *
4787 * <p>A <code>null</code> input String returns <code>null</code>.
4788 * An empty string ("") input returns the empty string.</p>
4789 *
4790 * <p>If the stripChars String is <code>null</code>, whitespace is
4791 * stripped as defined by {@link Character#isWhitespace(char)}.</p>
4792 *
4793 * <pre>
4794 * StringUtils.stripEnd(null, *) = null
4795 * StringUtils.stripEnd("", *) = ""
4796 * StringUtils.stripEnd("abc", "") = "abc"
4797 * StringUtils.stripEnd("abc", null) = "abc"
4798 * StringUtils.stripEnd(" abc", null) = " abc"
4799 * StringUtils.stripEnd("abc ", null) = "abc"
4800 * StringUtils.stripEnd(" abc ", null) = " abc"
4801 * StringUtils.stripEnd(" abcyx", "xyz") = " abc"
4802 * </pre>
4803 *
4804 * @param str the String to remove characters from, may be null
4805 * @param stripChars the characters to remove, null treated as whitespace
4806 * @return the stripped String, <code>null</code> if null String input
4807 */
4808 public static String stripEnd(String str, String stripChars) {
4809 int end;
4810 if (str == null || (end = str.length()) == 0) {
4811 return str;
4812 }
4813
4814 if (stripChars == null) {
4815 while ((end != 0) && Character.isWhitespace(str.charAt(end - 1))) {
4816 end--;
4817 }
4818 } else if (stripChars.length() == 0) {
4819 return str;
4820 } else {
4821 while ((end != 0) && (stripChars.indexOf(str.charAt(end - 1)) != -1)) {
4822 end--;
4823 }
4824 }
4825 return str.substring(0, end);
4826 }
4827
4828 /**
4829 * The empty String <code>""</code>.
4830 * @since 2.0
4831 */
4832 public static final String EMPTY = "";
4833
4834 /**
4835 * Represents a failed index search.
4836 * @since 2.1
4837 */
4838 public static final int INDEX_NOT_FOUND = -1;
4839
4840 /**
4841 * <p>The maximum size to which the padding constant(s) can expand.</p>
4842 */
4843 private static final int PAD_LIMIT = 8192;
4844
4845 /**
4846 * <p>An array of <code>String</code>s used for padding.</p>
4847 *
4848 * <p>Used for efficient space padding. The length of each String expands as needed.</p>
4849 */
4850 private static final String[] PADDING = new String[Character.MAX_VALUE];
4851
4852 static {
4853 // space padding is most common, start with 64 chars
4854 PADDING[32] = " ";
4855 }
4856
4857 /**
4858 * <p>Repeat a String <code>repeat</code> times to form a
4859 * new String.</p>
4860 *
4861 * <pre>
4862 * StringUtils.repeat(null, 2) = null
4863 * StringUtils.repeat("", 0) = ""
4864 * StringUtils.repeat("", 2) = ""
4865 * StringUtils.repeat("a", 3) = "aaa"
4866 * StringUtils.repeat("ab", 2) = "abab"
4867 * StringUtils.repeat("a", -2) = ""
4868 * </pre>
4869 *
4870 * @param str the String to repeat, may be null
4871 * @param repeat number of times to repeat str, negative treated as zero
4872 * @return a new String consisting of the original String repeated,
4873 * <code>null</code> if null String input
4874 */
4875 public static String repeat(String str, int repeat) {
4876 // Performance tuned for 2.0 (JDK1.4)
4877
4878 if (str == null) {
4879 return null;
4880 }
4881 if (repeat <= 0) {
4882 return EMPTY;
4883 }
4884 int inputLength = str.length();
4885 if (repeat == 1 || inputLength == 0) {
4886 return str;
4887 }
4888 if (inputLength == 1 && repeat <= PAD_LIMIT) {
4889 return padding(repeat, str.charAt(0));
4890 }
4891
4892 int outputLength = inputLength * repeat;
4893 switch (inputLength) {
4894 case 1:
4895 char ch = str.charAt(0);
4896 char[] output1 = new char[outputLength];
4897 for (int i = repeat - 1; i >= 0; i--) {
4898 output1[i] = ch;
4899 }
4900 return new String(output1);
4901 case 2:
4902 char ch0 = str.charAt(0);
4903 char ch1 = str.charAt(1);
4904 char[] output2 = new char[outputLength];
4905 for (int i = repeat * 2 - 2; i >= 0; i--, i--) {
4906 output2[i] = ch0;
4907 output2[i + 1] = ch1;
4908 }
4909 return new String(output2);
4910 default:
4911 StringBuffer buf = new StringBuffer(outputLength);
4912 for (int i = 0; i < repeat; i++) {
4913 buf.append(str);
4914 }
4915 return buf.toString();
4916 }
4917 }
4918
4919 /**
4920 * <p>Returns padding using the specified delimiter repeated
4921 * to a given length.</p>
4922 *
4923 * <pre>
4924 * StringUtils.padding(0, 'e') = ""
4925 * StringUtils.padding(3, 'e') = "eee"
4926 * StringUtils.padding(-2, 'e') = IndexOutOfBoundsException
4927 * </pre>
4928 *
4929 * @param repeat number of times to repeat delim
4930 * @param padChar character to repeat
4931 * @return String with repeated character
4932 * @throws IndexOutOfBoundsException if <code>repeat < 0</code>
4933 */
4934 private static String padding(int repeat, char padChar) {
4935 // be careful of synchronization in this method
4936 // we are assuming that get and set from an array index is atomic
4937 String pad = PADDING[padChar];
4938 if (pad == null) {
4939 pad = String.valueOf(padChar);
4940 }
4941 while (pad.length() < repeat) {
4942 pad = pad.concat(pad);
4943 }
4944 PADDING[padChar] = pad;
4945 return pad.substring(0, repeat);
4946 }
4947
4948 /**
4949 * <p>Right pad a String with spaces (' ').</p>
4950 *
4951 * <p>The String is padded to the size of <code>size</code>.</p>
4952 *
4953 * <pre>
4954 * StringUtils.rightPad(null, *) = null
4955 * StringUtils.rightPad("", 3) = " "
4956 * StringUtils.rightPad("bat", 3) = "bat"
4957 * StringUtils.rightPad("bat", 5) = "bat "
4958 * StringUtils.rightPad("bat", 1) = "bat"
4959 * StringUtils.rightPad("bat", -1) = "bat"
4960 * </pre>
4961 *
4962 * @param str the String to pad out, may be null
4963 * @param size the size to pad to
4964 * @return right padded String or original String if no padding is necessary,
4965 * <code>null</code> if null String input
4966 */
4967 public static String rightPad(String str, int size) {
4968 return rightPad(str, size, ' ');
4969 }
4970
4971 /**
4972 * <p>Right pad a String with a specified character.</p>
4973 *
4974 * <p>The String is padded to the size of <code>size</code>.</p>
4975 *
4976 * <pre>
4977 * StringUtils.rightPad(null, *, *) = null
4978 * StringUtils.rightPad("", 3, 'z') = "zzz"
4979 * StringUtils.rightPad("bat", 3, 'z') = "bat"
4980 * StringUtils.rightPad("bat", 5, 'z') = "batzz"
4981 * StringUtils.rightPad("bat", 1, 'z') = "bat"
4982 * StringUtils.rightPad("bat", -1, 'z') = "bat"
4983 * </pre>
4984 *
4985 * @param str the String to pad out, may be null
4986 * @param size the size to pad to
4987 * @param padChar the character to pad with
4988 * @return right padded String or original String if no padding is necessary,
4989 * <code>null</code> if null String input
4990 * @since 2.0
4991 */
4992 public static String rightPad(String str, int size, char padChar) {
4993 if (str == null) {
4994 return null;
4995 }
4996 int pads = size - str.length();
4997 if (pads <= 0) {
4998 return str; // returns original String when possible
4999 }
5000 if (pads > PAD_LIMIT) {
5001 return rightPad(str, size, String.valueOf(padChar));
5002 }
5003 return str.concat(padding(pads, padChar));
5004 }
5005
5006 /**
5007 * <p>Right pad a String with a specified String.</p>
5008 *
5009 * <p>The String is padded to the size of <code>size</code>.</p>
5010 *
5011 * <pre>
5012 * StringUtils.rightPad(null, *, *) = null
5013 * StringUtils.rightPad("", 3, "z") = "zzz"
5014 * StringUtils.rightPad("bat", 3, "yz") = "bat"
5015 * StringUtils.rightPad("bat", 5, "yz") = "batyz"
5016 * StringUtils.rightPad("bat", 8, "yz") = "batyzyzy"
5017 * StringUtils.rightPad("bat", 1, "yz") = "bat"
5018 * StringUtils.rightPad("bat", -1, "yz") = "bat"
5019 * StringUtils.rightPad("bat", 5, null) = "bat "
5020 * StringUtils.rightPad("bat", 5, "") = "bat "
5021 * </pre>
5022 *
5023 * @param str the String to pad out, may be null
5024 * @param size the size to pad to
5025 * @param padStr the String to pad with, null or empty treated as single space
5026 * @return right padded String or original String if no padding is necessary,
5027 * <code>null</code> if null String input
5028 */
5029 public static String rightPad(String str, int size, String padStr) {
5030 if (str == null) {
5031 return null;
5032 }
5033 if (isEmpty(padStr)) {
5034 padStr = " ";
5035 }
5036 int padLen = padStr.length();
5037 int strLen = str.length();
5038 int pads = size - strLen;
5039 if (pads <= 0) {
5040 return str; // returns original String when possible
5041 }
5042 if (padLen == 1 && pads <= PAD_LIMIT) {
5043 return rightPad(str, size, padStr.charAt(0));
5044 }
5045
5046 if (pads == padLen) {
5047 return str.concat(padStr);
5048 } else if (pads < padLen) {
5049 return str.concat(padStr.substring(0, pads));
5050 } else {
5051 char[] padding = new char[pads];
5052 char[] padChars = padStr.toCharArray();
5053 for (int i = 0; i < pads; i++) {
5054 padding[i] = padChars[i % padLen];
5055 }
5056 return str.concat(new String(padding));
5057 }
5058 }
5059
5060 /**
5061 * <p>Left pad a String with spaces (' ').</p>
5062 *
5063 * <p>The String is padded to the size of <code>size<code>.</p>
5064 *
5065 * <pre>
5066 * StringUtils.leftPad(null, *) = null
5067 * StringUtils.leftPad("", 3) = " "
5068 * StringUtils.leftPad("bat", 3) = "bat"
5069 * StringUtils.leftPad("bat", 5) = " bat"
5070 * StringUtils.leftPad("bat", 1) = "bat"
5071 * StringUtils.leftPad("bat", -1) = "bat"
5072 * </pre>
5073 *
5074 * @param str the String to pad out, may be null
5075 * @param size the size to pad to
5076 * @return left padded String or original String if no padding is necessary,
5077 * <code>null</code> if null String input
5078 */
5079 public static String leftPad(String str, int size) {
5080 return leftPad(str, size, ' ');
5081 }
5082
5083 /**
5084 * <p>Left pad a String with a specified character.</p>
5085 *
5086 * <p>Pad to a size of <code>size</code>.</p>
5087 *
5088 * <pre>
5089 * StringUtils.leftPad(null, *, *) = null
5090 * StringUtils.leftPad("", 3, 'z') = "zzz"
5091 * StringUtils.leftPad("bat", 3, 'z') = "bat"
5092 * StringUtils.leftPad("bat", 5, 'z') = "zzbat"
5093 * StringUtils.leftPad("bat", 1, 'z') = "bat"
5094 * StringUtils.leftPad("bat", -1, 'z') = "bat"
5095 * </pre>
5096 *
5097 * @param str the String to pad out, may be null
5098 * @param size the size to pad to
5099 * @param padChar the character to pad with
5100 * @return left padded String or original String if no padding is necessary,
5101 * <code>null</code> if null String input
5102 * @since 2.0
5103 */
5104 public static String leftPad(String str, int size, char padChar) {
5105 if (str == null) {
5106 return null;
5107 }
5108 int pads = size - str.length();
5109 if (pads <= 0) {
5110 return str; // returns original String when possible
5111 }
5112 if (pads > PAD_LIMIT) {
5113 return leftPad(str, size, String.valueOf(padChar));
5114 }
5115 return padding(pads, padChar).concat(str);
5116 }
5117
5118 /**
5119 * <p>Left pad a String with a specified String.</p>
5120 *
5121 * <p>Pad to a size of <code>size</code>.</p>
5122 *
5123 * <pre>
5124 * StringUtils.leftPad(null, *, *) = null
5125 * StringUtils.leftPad("", 3, "z") = "zzz"
5126 * StringUtils.leftPad("bat", 3, "yz") = "bat"
5127 * StringUtils.leftPad("bat", 5, "yz") = "yzbat"
5128 * StringUtils.leftPad("bat", 8, "yz") = "yzyzybat"
5129 * StringUtils.leftPad("bat", 1, "yz") = "bat"
5130 * StringUtils.leftPad("bat", -1, "yz") = "bat"
5131 * StringUtils.leftPad("bat", 5, null) = " bat"
5132 * StringUtils.leftPad("bat", 5, "") = " bat"
5133 * </pre>
5134 *
5135 * @param str the String to pad out, may be null
5136 * @param size the size to pad to
5137 * @param padStr the String to pad with, null or empty treated as single space
5138 * @return left padded String or original String if no padding is necessary,
5139 * <code>null</code> if null String input
5140 */
5141 public static String leftPad(String str, int size, String padStr) {
5142 if (str == null) {
5143 return null;
5144 }
5145 if (isEmpty(padStr)) {
5146 padStr = " ";
5147 }
5148 int padLen = padStr.length();
5149 int strLen = str.length();
5150 int pads = size - strLen;
5151 if (pads <= 0) {
5152 return str; // returns original String when possible
5153 }
5154 if (padLen == 1 && pads <= PAD_LIMIT) {
5155 return leftPad(str, size, padStr.charAt(0));
5156 }
5157
5158 if (pads == padLen) {
5159 return padStr.concat(str);
5160 } else if (pads < padLen) {
5161 return padStr.substring(0, pads).concat(str);
5162 } else {
5163 char[] padding = new char[pads];
5164 char[] padChars = padStr.toCharArray();
5165 for (int i = 0; i < pads; i++) {
5166 padding[i] = padChars[i % padLen];
5167 }
5168 return new String(padding).concat(str);
5169 }
5170 }
5171
5172 /**
5173 * <p>Gets the substring before the first occurrence of a separator.
5174 * The separator is not returned.</p>
5175 *
5176 * <p>A <code>null</code> string input will return <code>null</code>.
5177 * An empty ("") string input will return the empty string.
5178 * A <code>null</code> separator will return the input string.</p>
5179 *
5180 * <pre>
5181 * StringUtils.substringBefore(null, *) = null
5182 * StringUtils.substringBefore("", *) = ""
5183 * StringUtils.substringBefore("abc", "a") = ""
5184 * StringUtils.substringBefore("abcba", "b") = "a"
5185 * StringUtils.substringBefore("abc", "c") = "ab"
5186 * StringUtils.substringBefore("abc", "d") = "abc"
5187 * StringUtils.substringBefore("abc", "") = ""
5188 * StringUtils.substringBefore("abc", null) = "abc"
5189 * </pre>
5190 *
5191 * @param str the String to get a substring from, may be null
5192 * @param separator the String to search for, may be null
5193 * @return the substring before the first occurrence of the separator,
5194 * <code>null</code> if null String input
5195 * @since 2.0
5196 */
5197 public static String substringBefore(String str, String separator) {
5198 if (isEmpty(str) || separator == null) {
5199 return str;
5200 }
5201 if (separator.length() == 0) {
5202 return EMPTY;
5203 }
5204 int pos = str.indexOf(separator);
5205 if (pos == -1) {
5206 return str;
5207 }
5208 return str.substring(0, pos);
5209 }
5210
5211 /**
5212 * <p>Gets the substring after the first occurrence of a separator.
5213 * The separator is not returned.</p>
5214 *
5215 * <p>A <code>null</code> string input will return <code>null</code>.
5216 * An empty ("") string input will return the empty string.
5217 * A <code>null</code> separator will return the empty string if the
5218 * input string is not <code>null</code>.</p>
5219 *
5220 * <pre>
5221 * StringUtils.substringAfter(null, *) = null
5222 * StringUtils.substringAfter("", *) = ""
5223 * StringUtils.substringAfter(*, null) = ""
5224 * StringUtils.substringAfter("abc", "a") = "bc"
5225 * StringUtils.substringAfter("abcba", "b") = "cba"
5226 * StringUtils.substringAfter("abc", "c") = ""
5227 * StringUtils.substringAfter("abc", "d") = ""
5228 * StringUtils.substringAfter("abc", "") = "abc"
5229 * </pre>
5230 *
5231 * @param str the String to get a substring from, may be null
5232 * @param separator the String to search for, may be null
5233 * @return the substring after the first occurrence of the separator,
5234 * <code>null</code> if null String input
5235 * @since 2.0
5236 */
5237 public static String substringAfter(String str, String separator) {
5238 if (isEmpty(str)) {
5239 return str;
5240 }
5241 if (separator == null) {
5242 return EMPTY;
5243 }
5244 int pos = str.indexOf(separator);
5245 if (pos == -1) {
5246 return EMPTY;
5247 }
5248 return str.substring(pos + separator.length());
5249 }
5250
5251 /**
5252 * <p>Gets the substring before the last occurrence of a separator.
5253 * The separator is not returned.</p>
5254 *
5255 * <p>A <code>null</code> string input will return <code>null</code>.
5256 * An empty ("") string input will return the empty string.
5257 * An empty or <code>null</code> separator will return the input string.</p>
5258 *
5259 * <pre>
5260 * StringUtils.substringBeforeLast(null, *) = null
5261 * StringUtils.substringBeforeLast("", *) = ""
5262 * StringUtils.substringBeforeLast("abcba", "b") = "abc"
5263 * StringUtils.substringBeforeLast("abc", "c") = "ab"
5264 * StringUtils.substringBeforeLast("a", "a") = ""
5265 * StringUtils.substringBeforeLast("a", "z") = "a"
5266 * StringUtils.substringBeforeLast("a", null) = "a"
5267 * StringUtils.substringBeforeLast("a", "") = "a"
5268 * </pre>
5269 *
5270 * @param str the String to get a substring from, may be null
5271 * @param separator the String to search for, may be null
5272 * @return the substring before the last occurrence of the separator,
5273 * <code>null</code> if null String input
5274 * @since 2.0
5275 */
5276 public static String substringBeforeLast(String str, String separator) {
5277 if (isEmpty(str) || isEmpty(separator)) {
5278 return str;
5279 }
5280 int pos = str.lastIndexOf(separator);
5281 if (pos == -1) {
5282 return str;
5283 }
5284 return str.substring(0, pos);
5285 }
5286
5287 /**
5288 * <p>Gets the substring after the last occurrence of a separator.
5289 * The separator is not returned.</p>
5290 *
5291 * <p>A <code>null</code> string input will return <code>null</code>.
5292 * An empty ("") string input will return the empty string.
5293 * An empty or <code>null</code> separator will return the empty string if
5294 * the input string is not <code>null</code>.</p>
5295 *
5296 * <pre>
5297 * StringUtils.substringAfterLast(null, *) = null
5298 * StringUtils.substringAfterLast("", *) = ""
5299 * StringUtils.substringAfterLast(*, "") = ""
5300 * StringUtils.substringAfterLast(*, null) = ""
5301 * StringUtils.substringAfterLast("abc", "a") = "bc"
5302 * StringUtils.substringAfterLast("abcba", "b") = "a"
5303 * StringUtils.substringAfterLast("abc", "c") = ""
5304 * StringUtils.substringAfterLast("a", "a") = ""
5305 * StringUtils.substringAfterLast("a", "z") = ""
5306 * </pre>
5307 *
5308 * @param str the String to get a substring from, may be null
5309 * @param separator the String to search for, may be null
5310 * @return the substring after the last occurrence of the separator,
5311 * <code>null</code> if null String input
5312 * @since 2.0
5313 */
5314 public static String substringAfterLast(String str, String separator) {
5315 if (isEmpty(str)) {
5316 return str;
5317 }
5318 if (isEmpty(separator)) {
5319 return EMPTY;
5320 }
5321 int pos = str.lastIndexOf(separator);
5322 if (pos == -1 || pos == (str.length() - separator.length())) {
5323 return EMPTY;
5324 }
5325 return str.substring(pos + separator.length());
5326 }
5327
5328 /**
5329 * Returns the first existing parent stem of a given name.
5330 * So if the following stems exist:
5331 * i2
5332 * i2:test
5333 *
5334 * And you run getFirstParentStemOfName("i2:test:mystem:mygroup"),
5335 * you will get back the stem for i2:test.
5336 *
5337 * If you run getFirstParentStemOfName("test1:test2"),
5338 * you will get back the root stem.
5339 *
5340 * @param name
5341 * @return Stem
5342 */
5343 public static Stem getFirstParentStemOfName(String name) {
5344 String parent = GrouperUtilElSafe.parentStemNameFromName(name);
5345
5346 if (parent == null || parent.equals(name)) {
5347 return StemFinder.findRootStem(GrouperSession.staticGrouperSession()
5348 .internal_getRootSession());
5349 }
5350
5351 Stem stem = StemFinder.findByName(GrouperSession.staticGrouperSession()
5352 .internal_getRootSession(), parent, false);
5353 if (stem != null) {
5354 return stem;
5355 }
5356
5357 return getFirstParentStemOfName(parent);
5358 }
5359
5360 /**
5361 * Return the zero element of the list, if it exists, null if the list is empty.
5362 * If there is more than one element in the list, an exception is thrown.
5363 * @param <T>
5364 * @param list is the container of objects to get the first of.
5365 * @return the first object, null, or exception.
5366 */
5367 public static <T> T listPopOne(List<T> list) {
5368 int size = length(list);
5369 if (size == 1) {
5370 return list.get(0);
5371 } else if (size == 0) {
5372 return null;
5373 }
5374 throw new RuntimeException("More than one object of type " + className(list.get(0))
5375 + " was returned when only one was expected. (size:" + size +")" );
5376 }
5377
5378 /**
5379 * Return the zero element of the set, if it exists, null if the list is empty.
5380 * If there is more than one element in the list, an exception is thrown.
5381 * @param <T>
5382 * @param set is the container of objects to get the first of.
5383 * @return the first object, null, or exception.
5384 */
5385 public static <T> T setPopOne(Set<T> set) {
5386 int size = length(set);
5387 if (size == 1) {
5388 return set.iterator().next();
5389 } else if (size == 0) {
5390 return null;
5391 }
5392 throw new RuntimeException("More than one object of type " + className(set.iterator().next())
5393 + " was returned when only one was expected. (size:" + size +")" );
5394 }
5395
5396 /**
5397 * Return the zero element of the list, if it exists, null if the list is empty.
5398 * If there is more than one element in the list, an exception is thrown.
5399 * @param <T>
5400 * @param collection is the container of objects to get the first of.
5401 * @param exceptionIfMoreThanOne will throw exception if there is more than one item in list
5402 * @return the first object, null, or exception.
5403 */
5404 public static <T> T collectionPopOne(Collection<T> collection, boolean exceptionIfMoreThanOne) {
5405 int size = length(collection);
5406 if (size > 1 && exceptionIfMoreThanOne) {
5407 throw new RuntimeException("More than one object of type " + className(get(collection, 0))
5408 + " was returned when only one was expected. (size:" + size +")" );
5409 }
5410 if (size == 0) {
5411 return null;
5412 }
5413 return collection.iterator().next();
5414 }
5415
5416 /**
5417 * Convert an XML string to HTML to display on the screen
5418 *
5419 * @param input
5420 * is the XML to convert
5421 * @param isEscape true to escape chars, false to unescape
5422 *
5423 * @return the HTML converted string
5424 */
5425 public static String xmlEscape(String input) {
5426 return xmlEscape(input, true);
5427 }
5428
5429 /**
5430 * Convert an XML string to HTML to display on the screen
5431 *
5432 * @param input
5433 * is the XML to convert
5434 * @param isEscape true to escape chars, false to unescape
5435 *
5436 * @return the HTML converted string
5437 */
5438 public static String xmlEscape(String input, boolean isEscape) {
5439 if (isEscape) {
5440 return replace(input, XML_SEARCH_NO_SINGLE, XML_REPLACE_NO_SINGLE);
5441 }
5442 return replace(input, XML_REPLACE_NO_SINGLE, XML_SEARCH_NO_SINGLE);
5443 }
5444
5445 /**
5446 * if theString is not blank, apppend it to the result, and if appending,
5447 * @param result to append to
5448 * add a prefix and suffix (if not null)
5449 * @param theStringOrArrayOrList is a string, array, list, or set of strings
5450 * @return true if something appended, false if not
5451 */
5452 public static boolean appendIfNotBlank(StringBuilder result,
5453 Object theStringOrArrayOrList) {
5454 return appendIfNotBlank(result, null, theStringOrArrayOrList, null);
5455 }
5456
5457 /**
5458 * <pre>
5459 * append a string to another string if both not blank, with separator. trim to empty everything
5460 * </pre>
5461 * @param string
5462 * @param separator
5463 * @param suffix
5464 * @return the resulting string or blank if nothing
5465 */
5466 public static String appendIfNotBlankString(String string, String separator, String suffix) {
5467
5468 string = StringUtils.trimToEmpty(string);
5469 suffix = StringUtils.trimToEmpty(suffix);
5470
5471 boolean stringIsBlank = StringUtils.isBlank(string);
5472 boolean suffixIsBlank = StringUtils.isBlank(suffix);
5473
5474 if (stringIsBlank && suffixIsBlank) {
5475 return "";
5476 }
5477
5478 if (stringIsBlank) {
5479 return suffix;
5480 }
5481
5482 if (suffixIsBlank) {
5483 return string;
5484 }
5485
5486 return string + separator + suffix;
5487
5488 }
5489
5490 /**
5491 * <pre>
5492 * append a prefix to another string if both not blank. trim to empty everything except separator
5493 * i.e. appendPrefixIfStringNotBlank("[]", " - ", "a") returns [] - a
5494 * i.e. appendPrefixIfStringNotBlank("", " - ", "a") returns a
5495 * i.e. appendPrefixIfStringNotBlank("[]", " - ", "") returns ""
5496 * i.e. appendPrefixIfStringNotBlank("", " - ", "") returns ""
5497 * </pre>
5498 * @param prefix
5499 * @param separator
5500 * @param string
5501 * @return the resulting string or blank if nothing
5502 */
5503 public static String appendPrefixIfStringNotBlank(String prefix, String separator, String string) {
5504
5505 string = StringUtils.trimToEmpty(string);
5506 prefix = StringUtils.trimToEmpty(prefix);
5507
5508 boolean stringIsBlank = StringUtils.isBlank(string);
5509 boolean prefixIsBlank = StringUtils.isBlank(prefix);
5510
5511 if (stringIsBlank) {
5512 return "";
5513 }
5514
5515 if (prefixIsBlank) {
5516 return string;
5517 }
5518
5519 return prefix + separator + string;
5520
5521 }
5522
5523 /**
5524 * if theString is not Blank, apppend it to the result, and if appending,
5525 * add a prefix (if not null)
5526 * @param result to append to
5527 * @param prefix
5528 * @param theStringOrArrayOrList is a string, array, list, or set of strings
5529 * @return true if something appended, false if not
5530 */
5531 public static boolean appendIfNotBlank(StringBuilder result,
5532 String prefix, Object theStringOrArrayOrList) {
5533 return appendIfNotBlank(result, prefix, theStringOrArrayOrList, null);
5534 }
5535
5536 /**
5537 * if theString is not Blank, apppend it to the result, and if appending,
5538 * add a prefix and suffix (if not null)
5539 * @param result to append to, assumed to be not null
5540 * @param prefix
5541 * @param theStringOrArrayOrList is a string, array, list, or set of strings
5542 * @param suffix
5543 * @return true if anything appended, false if not
5544 */
5545 public static boolean appendIfNotBlank(StringBuilder result,
5546 String prefix, Object theStringOrArrayOrList, String suffix) {
5547 return appendIfNotBlank(result, prefix, null, theStringOrArrayOrList, suffix);
5548 }
5549
5550 /**
5551 * if theString is not Blank, apppend it to the result, and if appending,
5552 * add a prefix and suffix (if not null)
5553 * @param result to append to, assumed to be not null
5554 * @param prefix prepend this prefix always (when a result not empty). Will be after the other prefix
5555 * @param prefixIfNotBlank prepend this prefix if the result is not empty
5556 * @param theStringOrArrayOrList is a string, array, list, or set of strings
5557 * @param suffix
5558 * @return true if anything appended, false if not
5559 */
5560 public static boolean appendIfNotBlank(StringBuilder result,
5561 String prefix, String prefixIfNotBlank, Object theStringOrArrayOrList, String suffix) {
5562 int length = length(theStringOrArrayOrList);
5563 Iterator iterator = iterator(theStringOrArrayOrList);
5564 boolean appendedAnything = false;
5565
5566 //these could be appending spaces, so only check to see if they are empty
5567 boolean hasPrefix = !StringUtils.isEmpty(prefix);
5568 boolean hasPrefixIfNotBlank = !StringUtils.isEmpty(prefixIfNotBlank);
5569 boolean hasSuffix = !StringUtils.isEmpty(suffix);
5570 for (int i=0;i<length;i++) {
5571 String current = (String) next(theStringOrArrayOrList, iterator, i);
5572
5573 //only append if not empty
5574 if (!StringUtils.isBlank(current)) {
5575
5576 //keeping track if anything changed
5577 appendedAnything = true;
5578 if (hasPrefix) {
5579 result.append(prefix);
5580 }
5581 if (hasPrefixIfNotBlank && result.length() > 0) {
5582 result.append(prefixIfNotBlank);
5583 }
5584 result.append(current);
5585 if (hasSuffix) {
5586 result.append(suffix);
5587 }
5588 }
5589 }
5590 return appendedAnything;
5591 }
5592
5593 /**
5594 * if theString is not empty, apppend it to the result, and if appending,
5595 * @param result to append to
5596 * add a prefix and suffix (if not null)
5597 * @param theStringOrArrayOrList is a string, array, list, or set of strings
5598 * @return true if something appended, false if not
5599 */
5600 public static boolean appendIfNotEmpty(StringBuilder result,
5601 Object theStringOrArrayOrList) {
5602 return appendIfNotEmpty(result, null, theStringOrArrayOrList, null);
5603 }
5604
5605 /**
5606 * if theString is not empty, apppend it to the result, and if appending,
5607 * add a prefix (if not null)
5608 * @param result to append to
5609 * @param prefix
5610 * @param theStringOrArrayOrList is a string, array, list, or set of strings
5611 * @return true if something appended, false if not
5612 */
5613 public static boolean appendIfNotEmpty(StringBuilder result,
5614 String prefix, Object theStringOrArrayOrList) {
5615 return appendIfNotEmpty(result, prefix, theStringOrArrayOrList, null);
5616 }
5617
5618 /**
5619 * if theString is not empty, apppend it to the result, and if appending,
5620 * add a prefix and suffix (if not null)
5621 * @param result to append to, assumed to be not null
5622 * @param prefix
5623 * @param theStringOrArrayOrList is a string, array, list, or set of strings
5624 * @param suffix
5625 * @return true if anything appended, false if not
5626 */
5627 public static boolean appendIfNotEmpty(StringBuilder result,
5628 String prefix, Object theStringOrArrayOrList, String suffix) {
5629 return appendIfNotEmpty(result, prefix, null, theStringOrArrayOrList, suffix);
5630 }
5631
5632 /**
5633 * if theString is not empty, apppend it to the result, and if appending,
5634 * add a prefix and suffix (if not null)
5635 * @param result to append to, assumed to be not null
5636 * @param prefix prepend this prefix always (when a result not empty). Will be after the other prefix
5637 * @param prefixIfNotEmpty prepend this prefix if the result is not empty
5638 * @param theStringOrArrayOrList is a string, array, list, or set of strings
5639 * @param suffix
5640 * @return true if anything appended, false if not
5641 */
5642 public static boolean appendIfNotEmpty(StringBuilder result,
5643 String prefix, String prefixIfNotEmpty, Object theStringOrArrayOrList, String suffix) {
5644 int length = length(theStringOrArrayOrList);
5645 Iterator iterator = iterator(theStringOrArrayOrList);
5646 boolean appendedAnything = false;
5647 boolean hasPrefix = !StringUtils.isEmpty(prefix);
5648 boolean hasPrefixIfNotEmpty = !StringUtils.isEmpty(prefixIfNotEmpty);
5649 boolean hasSuffix = !StringUtils.isEmpty(suffix);
5650 for (int i=0;i<length;i++) {
5651 String current = (String) next(theStringOrArrayOrList, iterator, i);
5652
5653 //only append if not empty
5654 if (!StringUtils.isEmpty(current)) {
5655
5656 //keeping track if anything changed
5657 appendedAnything = true;
5658 if (hasPrefix) {
5659 result.append(prefix);
5660 }
5661 if (hasPrefixIfNotEmpty && result.length() > 0) {
5662 result.append(prefixIfNotEmpty);
5663 }
5664 result.append(current);
5665 if (hasSuffix) {
5666 result.append(suffix);
5667 }
5668 }
5669 }
5670 return appendedAnything;
5671 }
5672
5673 /**
5674 * <p>Find the index of the given object in the array.</p>
5675 *
5676 * <p>This method returns <code>-1</code> if <code>null</code> array input.</p>
5677 *
5678 * @param array the array to search through for the object, may be <code>null</code>
5679 * @param objectToFind the object to find, may be <code>null</code>
5680 * @return the index of the object within the array,
5681 * <code>-1</code> if not found or <code>null</code> array input
5682 */
5683 public static int indexOf(Object[] array, Object objectToFind) {
5684 return indexOf(array, objectToFind, 0);
5685 }
5686
5687 /**
5688 * <p>Checks if the object is in the given array.</p>
5689 *
5690 * <p>The method returns <code>false</code> if a <code>null</code> array is passed in.</p>
5691 *
5692 * @param array the array to search through
5693 * @param objectToFind the object to find
5694 * @return <code>true</code> if the array contains the object
5695 */
5696 public static boolean contains(Object[] array, Object objectToFind) {
5697 return indexOf(array, objectToFind) != -1;
5698 }
5699
5700 /**
5701 * <p>Find the index of the given object in the array starting at the given index.</p>
5702 *
5703 * <p>This method returns <code>-1</code> if <code>null</code> array input.</p>
5704 *
5705 * <p>A negative startIndex is treated as zero. A startIndex larger than the array
5706 * length will return <code>-1</code>.</p>
5707 *
5708 * @param array the array to search through for the object, may be <code>null</code>
5709 * @param objectToFind the object to find, may be <code>null</code>
5710 * @param startIndex the index to start searching at
5711 * @return the index of the object within the array starting at the index,
5712 * <code>-1</code> if not found or <code>null</code> array input
5713 */
5714 public static int indexOf(Object[] array, Object objectToFind, int startIndex) {
5715 if (array == null) {
5716 return -1;
5717 }
5718 if (startIndex < 0) {
5719 startIndex = 0;
5720 }
5721 if (objectToFind == null) {
5722 for (int i = startIndex; i < array.length; i++) {
5723 if (array[i] == null) {
5724 return i;
5725 }
5726 }
5727 } else {
5728 for (int i = startIndex; i < array.length; i++) {
5729 if (objectToFind.equals(array[i])) {
5730 return i;
5731 }
5732 }
5733 }
5734 return -1;
5735 }
5736
5737 /**
5738 * Return the zero element of the array, if it exists, null if the array is empty.
5739 * If there is more than one element in the list, an exception is thrown.
5740 * @param <T>
5741 * @param array is the container of objects to get the first of.
5742 * @return the first object, null, or exception.
5743 */
5744 public static <T> T arrayPopOne(T[] array) {
5745 int size = length(array);
5746 if (size == 1) {
5747 return array[0];
5748 } else if (size == 0) {
5749 return null;
5750 }
5751 throw new RuntimeException("More than one object of type " + className(array[0])
5752 + " was returned when only one was expected. (size:" + size +")" );
5753 }
5754
5755 /**
5756 * Note, this is
5757 * web service format string
5758 */
5759 private static final String WS_DATE_FORMAT = "yyyy/MM/dd HH:mm:ss.SSS";
5760
5761 /**
5762 * Note, this is
5763 * web service format string
5764 */
5765 private static final String WS_DATE_FORMAT2 = "yyyy/MM/dd_HH:mm:ss.SSS";
5766
5767 /**
5768 * convert a date to a string using the standard web service pattern
5769 * yyyy/MM/dd HH:mm:ss.SSS Note that HH is 0-23
5770 *
5771 * @param date
5772 * @return the string, or null if the date is null
5773 */
5774 public static String dateToString(Date date) {
5775 if (date == null) {
5776 return null;
5777 }
5778 SimpleDateFormat simpleDateFormat = new SimpleDateFormat(WS_DATE_FORMAT);
5779 return simpleDateFormat.format(date);
5780 }
5781
5782 /**
5783 * convert a string to a date using the standard web service pattern Note
5784 * that HH is 0-23
5785 *
5786 * @param dateString
5787 * @return the string, or null if the date was null
5788 */
5789 public static Date stringToDate(String dateString) {
5790 if (isBlank(dateString)) {
5791 return null;
5792 }
5793 SimpleDateFormat simpleDateFormat = new SimpleDateFormat(WS_DATE_FORMAT);
5794 try {
5795 return simpleDateFormat.parse(dateString);
5796 } catch (ParseException e) {
5797 SimpleDateFormat simpleDateFormat2 = new SimpleDateFormat(WS_DATE_FORMAT2);
5798 try {
5799 return simpleDateFormat2.parse(dateString);
5800 } catch (ParseException e2) {
5801 throw new RuntimeException("Cannot convert '" + dateString
5802 + "' to a date based on format: " + WS_DATE_FORMAT, e);
5803 }
5804 }
5805 }
5806
5807 /**
5808 * @param values
5809 * @return the max long in the list of args
5810 */
5811 public static Long getMaxLongValue(Long... values) {
5812 if (values == null || values.length == 0) {
5813 return null;
5814 }
5815
5816 Long maxValue = null;
5817 for (int i = 0; i < values.length; i++) {
5818 if (values[i] != null) {
5819 if (maxValue == null || maxValue.compareTo(values[i]) < 0) {
5820 maxValue = new Long(values[i]);
5821 }
5822 }
5823 }
5824
5825 return maxValue;
5826 }
5827
5828 /**
5829 * @param values
5830 * @return the min long in the list of args
5831 */
5832 public static Long getMinLongValue(Long... values) {
5833 if (values == null || values.length == 0) {
5834 return null;
5835 }
5836
5837 Long minValue = null;
5838 for (int i = 0; i < values.length; i++) {
5839 if (values[i] != null) {
5840 if (minValue == null || minValue.compareTo(values[i]) > 0) {
5841 minValue = new Long(values[i]);
5842 }
5843 }
5844 }
5845
5846 return minValue;
5847 }
5848
5849 /**
5850 * see if an ip address is on a network
5851 *
5852 * @param ipString
5853 * is the ip address to check
5854 * @param networkIpString
5855 * is the ip address of the network
5856 * @param mask
5857 * is the length of the mask (0-32)
5858 * @return boolean
5859 */
5860 public static boolean ipOnNetwork(String ipString, String networkIpString, int mask) {
5861
5862 //this allows all
5863 if (mask == 0) {
5864 return true;
5865 }
5866 int ip = ipInt(ipString);
5867 int networkIp = ipInt(networkIpString);
5868
5869 ip = ipReadyForAnd(ip, mask);
5870 networkIp = ipReadyForAnd(networkIp, mask);
5871
5872 return ip == networkIp;
5873 }
5874
5875 /**
5876 * see if an ip address is on a network
5877 *
5878 * @param ipString
5879 * is the ip address to check
5880 * @param networkIpStrings
5881 * are the ip addresses of the networks, e.g. 1.2.3.4/12, 2.3.4.5/24
5882 * @return boolean
5883 */
5884 public static boolean ipOnNetworks(String ipString, String networkIpStrings) {
5885
5886 String[] networkIpStringsArray = splitTrim(networkIpStrings, ",");
5887
5888 //check each one
5889 for (String networkIpString : networkIpStringsArray) {
5890
5891 if (!contains(networkIpString, "/")) {
5892 throw new RuntimeException("String must contain slash and CIDR network bits, e.g. 1.2.3.4/14");
5893 }
5894 //get network part:
5895 String network = prefixOrSuffix(networkIpString, "/", true);
5896 network = trim(network);
5897
5898 String mask = prefixOrSuffix(networkIpString, "/", false);
5899 mask = trim(mask);
5900 int maskInt = -1;
5901
5902 maskInt = Integer.parseInt(mask);
5903
5904 //if on the network, then all good
5905 if (ipOnNetwork(ipString, network, maskInt)) {
5906 return true;
5907 }
5908
5909
5910 }
5911 return false;
5912 }
5913
5914 /**
5915 * get the ip address after putting 1's where the subnet mask is not
5916 * @param ip int
5917 * @param maskLength int
5918 * @return int
5919 */
5920 public static int ipReadyForAnd(int ip, int maskLength) {
5921 int mask = -1 + (int) Math.pow(2, 32 - maskLength);
5922
5923 return ip | mask;
5924 }
5925
5926 /**
5927 * get the ip addres integer from a string ip address
5928 * @param ip String
5929 * @return int
5930 */
5931 public static int ipInt(String ip) {
5932 int block1;
5933 int block2;
5934 int block3;
5935 int block4;
5936
5937 try {
5938 int periodIndex = ip.indexOf('.');
5939 String blockString = ip.substring(0, periodIndex);
5940 block1 = Integer.parseInt(blockString);
5941
5942 //split it up for 2^24 since it does the math wrong if you dont
5943 int mathPow = (int) Math.pow(2, 24);
5944 block1 *= mathPow;
5945
5946 int oldPeriodIndex = periodIndex;
5947
5948 periodIndex = ip.indexOf('.', periodIndex + 1);
5949 blockString = ip.substring(oldPeriodIndex + 1, periodIndex);
5950 block2 = Integer.parseInt(blockString);
5951 block2 *= Math.pow(2, 16);
5952 oldPeriodIndex = periodIndex;
5953
5954 periodIndex = ip.indexOf('.', periodIndex + 1);
5955 blockString = ip.substring(oldPeriodIndex + 1, periodIndex);
5956 block3 = Integer.parseInt(blockString);
5957 block3 *= Math.pow(2, 8);
5958
5959 blockString = ip.substring(periodIndex + 1, ip.length());
5960 block4 = Integer.parseInt(blockString);
5961 } catch (NumberFormatException nfe) {
5962 throw new RuntimeException("Could not parse the ipaddress: " + ip);
5963 }
5964
5965 return block1 + block2 + block3 + block4;
5966 }
5967
5968 /** array for converting HTML to string */
5969 private static final String[] XML_REPLACE_NO_SINGLE = new String[]{"&","<",">","""};
5970
5971 /** array for converting HTML to string */
5972 private static final String[] XML_SEARCH_NO_SINGLE = new String[]{"&","<",">","\""};
5973
5974 /**
5975 * <p>A way to get the entire nested stack-trace of an throwable.</p>
5976 *
5977 * @param throwable the <code>Throwable</code> to be examined
5978 * @return the nested stack trace, with the root cause first
5979 * @since 2.0
5980 */
5981 public static String getFullStackTrace(Throwable throwable) {
5982 StringWriter sw = new StringWriter();
5983 PrintWriter pw = new PrintWriter(sw, true);
5984 Throwable[] ts = getThrowables(throwable);
5985 for (int i = 0; i < ts.length; i++) {
5986 ts[i].printStackTrace(pw);
5987 if (isNestedThrowable(ts[i])) {
5988 break;
5989 }
5990 }
5991 return sw.getBuffer().toString();
5992 }
5993
5994 /**
5995 * Get a specific index of an array or collection (note for collections and
5996 * iterating, it is more efficient to get an iterator and iterate
5997 *
5998 * @param arrayOrCollection
5999 * @param index
6000 * @return the object at that index
6001 */
6002 public static Object get(Object arrayOrCollection, int index) {
6003
6004 if (arrayOrCollection == null) {
6005 if (index == 0) {
6006 return null;
6007 }
6008 throw new RuntimeException("Trying to access index " + index
6009 + " of null");
6010 }
6011
6012 // no need to iterator on list (e.g. FastProxyList has no iterator
6013 if (arrayOrCollection instanceof List) {
6014 return ((List) arrayOrCollection).get(index);
6015 }
6016 if (arrayOrCollection instanceof Collection) {
6017 Iterator iterator = iterator(arrayOrCollection);
6018 for (int i = 0; i < index; i++) {
6019 next(arrayOrCollection, iterator, i);
6020 }
6021 return next(arrayOrCollection, iterator, index);
6022 }
6023
6024 if (arrayOrCollection.getClass().isArray()) {
6025 return Array.get(arrayOrCollection, index);
6026 }
6027
6028 if (index == 0) {
6029 return arrayOrCollection;
6030 }
6031
6032 throw new RuntimeException("Trying to access index " + index
6033 + " of and object: " + arrayOrCollection);
6034 }
6035
6036 /**
6037 * if a class is enhanced, get the unenhanced version
6038 *
6039 * @param theClass
6040 * @return the unenhanced version
6041 */
6042 public static Class unenhanceClass(Class theClass) {
6043 try {
6044 //this was cglib
6045 // while (Enhancer.isEnhanced(theClass)) {
6046 // theClass = theClass.getSuperclass();
6047 // }
6048
6049 while (ProxyObject.class.isAssignableFrom(theClass)) {
6050 theClass = theClass.getSuperclass();
6051 }
6052
6053 return theClass;
6054 } catch (Exception e) {
6055 throw new RuntimeException("Problem unenhancing " + theClass, e);
6056 }
6057 }
6058
6059 /**
6060 * <p>Returns the list of <code>Throwable</code> objects in the
6061 * exception chain.</p>
6062 *
6063 * <p>A throwable without cause will return an array containing
6064 * one element - the input throwable.
6065 * A throwable with one cause will return an array containing
6066 * two elements. - the input throwable and the cause throwable.
6067 * A <code>null</code> throwable will return an array size zero.</p>
6068 *
6069 * @param throwable the throwable to inspect, may be null
6070 * @return the array of throwables, never null
6071 */
6072 public static Throwable[] getThrowables(Throwable throwable) {
6073 List list = new ArrayList();
6074 while (throwable != null) {
6075 list.add(throwable);
6076 throwable = getCause(throwable);
6077 }
6078 return (Throwable[]) list.toArray(new Throwable[list.size()]);
6079 }
6080
6081 /**
6082 * <p>Checks if the Throwable class has a <code>getCause</code> method.</p>
6083 *
6084 * <p>This is true for JDK 1.4 and above.</p>
6085 *
6086 * @return true if Throwable is nestable
6087 * @since 2.0
6088 */
6089 public static boolean isThrowableNested() {
6090 return THROWABLE_CAUSE_METHOD != null;
6091 }
6092
6093 /**
6094 * <p>Checks whether this <code>Throwable</code> class can store a cause.</p>
6095 *
6096 * <p>This method does <b>not</b> check whether it actually does store a cause.<p>
6097 *
6098 * @param throwable the <code>Throwable</code> to examine, may be null
6099 * @return boolean <code>true</code> if nested otherwise <code>false</code>
6100 * @since 2.0
6101 */
6102 public static boolean isNestedThrowable(Throwable throwable) {
6103 if (throwable == null) {
6104 return false;
6105 }
6106
6107 if (throwable instanceof Nestable) {
6108 return true;
6109 } else if (throwable instanceof SQLException) {
6110 return true;
6111 } else if (throwable instanceof InvocationTargetException) {
6112 return true;
6113 } else if (isThrowableNested()) {
6114 return true;
6115 }
6116
6117 Class cls = throwable.getClass();
6118 for (int i = 0, isize = CAUSE_METHOD_NAMES.length; i < isize; i++) {
6119 try {
6120 Method method = cls.getMethod(CAUSE_METHOD_NAMES[i], (Class[])null);
6121 if (method != null && Throwable.class.isAssignableFrom(method.getReturnType())) {
6122 return true;
6123 }
6124 } catch (NoSuchMethodException ignored) {
6125 } catch (SecurityException ignored) {
6126 }
6127 }
6128
6129 try {
6130 Field field = cls.getField("detail");
6131 if (field != null) {
6132 return true;
6133 }
6134 } catch (NoSuchFieldException ignored) {
6135 } catch (SecurityException ignored) {
6136 }
6137
6138 return false;
6139 }
6140
6141 /**
6142 * <p>The Method object for JDK1.4 getCause.</p>
6143 */
6144 private static final Method THROWABLE_CAUSE_METHOD;
6145
6146 static {
6147 Method getCauseMethod;
6148 try {
6149 getCauseMethod = Throwable.class.getMethod("getCause", (Class[])null);
6150 } catch (Exception e) {
6151 getCauseMethod = null;
6152 }
6153 THROWABLE_CAUSE_METHOD = getCauseMethod;
6154 }
6155
6156 /**
6157 * <p>Introspects the <code>Throwable</code> to obtain the cause.</p>
6158 *
6159 * <p>The method searches for methods with specific names that return a
6160 * <code>Throwable</code> object. This will pick up most wrapping exceptions,
6161 * including those from JDK 1.4, and
6162 * {@link org.apache.commons.lang.exception.NestableException NestableException}.</p>
6163 *
6164 * <p>The default list searched for are:</p>
6165 * <ul>
6166 * <li><code>getCause()</code></li>
6167 * <li><code>getNextException()</code></li>
6168 * <li><code>getTargetException()</code></li>
6169 * <li><code>getException()</code></li>
6170 * <li><code>getSourceException()</code></li>
6171 * <li><code>getRootCause()</code></li>
6172 * <li><code>getCausedByException()</code></li>
6173 * <li><code>getNested()</code></li>
6174 * </ul>
6175 *
6176 * <p>In the absence of any such method, the object is inspected for a
6177 * <code>detail</code> field assignable to a <code>Throwable</code>.</p>
6178 *
6179 * <p>If none of the above is found, returns <code>null</code>.</p>
6180 *
6181 * @param throwable the throwable to introspect for a cause, may be null
6182 * @return the cause of the <code>Throwable</code>,
6183 * <code>null</code> if none found or null throwable input
6184 * @since 1.0
6185 */
6186 public static Throwable getCause(Throwable throwable) {
6187 return getCause(throwable, CAUSE_METHOD_NAMES);
6188 }
6189
6190 /**
6191 * <p>The names of methods commonly used to access a wrapped exception.</p>
6192 */
6193 private static String[] CAUSE_METHOD_NAMES = {
6194 "getCause",
6195 "getNextException",
6196 "getTargetException",
6197 "getException",
6198 "getSourceException",
6199 "getRootCause",
6200 "getCausedByException",
6201 "getNested",
6202 "getLinkedException",
6203 "getNestedException",
6204 "getLinkedCause",
6205 "getThrowable",
6206 };
6207
6208 /**
6209 * <p>Introspects the <code>Throwable</code> to obtain the cause.</p>
6210 *
6211 * <ol>
6212 * <li>Try known exception types.</li>
6213 * <li>Try the supplied array of method names.</li>
6214 * <li>Try the field 'detail'.</li>
6215 * </ol>
6216 *
6217 * <p>A <code>null</code> set of method names means use the default set.
6218 * A <code>null</code> in the set of method names will be ignored.</p>
6219 *
6220 * @param throwable the throwable to introspect for a cause, may be null
6221 * @param methodNames the method names, null treated as default set
6222 * @return the cause of the <code>Throwable</code>,
6223 * <code>null</code> if none found or null throwable input
6224 * @since 1.0
6225 */
6226 public static Throwable getCause(Throwable throwable, String[] methodNames) {
6227 if (throwable == null) {
6228 return null;
6229 }
6230 Throwable cause = getCauseUsingWellKnownTypes(throwable);
6231 if (cause == null) {
6232 if (methodNames == null) {
6233 methodNames = CAUSE_METHOD_NAMES;
6234 }
6235 for (int i = 0; i < methodNames.length; i++) {
6236 String methodName = methodNames[i];
6237 if (methodName != null) {
6238 cause = getCauseUsingMethodName(throwable, methodName);
6239 if (cause != null) {
6240 break;
6241 }
6242 }
6243 }
6244
6245 if (cause == null) {
6246 cause = getCauseUsingFieldName(throwable, "detail");
6247 }
6248 }
6249 return cause;
6250 }
6251
6252 /**
6253 * <p>Finds a <code>Throwable</code> by field name.</p>
6254 *
6255 * @param throwable the exception to examine
6256 * @param fieldName the name of the attribute to examine
6257 * @return the wrapped exception, or <code>null</code> if not found
6258 */
6259 private static Throwable getCauseUsingFieldName(Throwable throwable, String fieldName) {
6260 Field field = null;
6261 try {
6262 field = throwable.getClass().getField(fieldName);
6263 } catch (NoSuchFieldException ignored) {
6264 } catch (SecurityException ignored) {
6265 }
6266
6267 if (field != null && Throwable.class.isAssignableFrom(field.getType())) {
6268 try {
6269 return (Throwable) field.get(throwable);
6270 } catch (IllegalAccessException ignored) {
6271 } catch (IllegalArgumentException ignored) {
6272 }
6273 }
6274 return null;
6275 }
6276
6277 /**
6278 * <p>Finds a <code>Throwable</code> by method name.</p>
6279 *
6280 * @param throwable the exception to examine
6281 * @param methodName the name of the method to find and invoke
6282 * @return the wrapped exception, or <code>null</code> if not found
6283 */
6284 private static Throwable getCauseUsingMethodName(Throwable throwable, String methodName) {
6285 Method method = null;
6286 try {
6287 method = throwable.getClass().getMethod(methodName, (Class[])null);
6288 } catch (NoSuchMethodException ignored) {
6289 } catch (SecurityException ignored) {
6290 }
6291
6292 if (method != null && Throwable.class.isAssignableFrom(method.getReturnType())) {
6293 try {
6294 return (Throwable) method.invoke(throwable, EMPTY_OBJECT_ARRAY);
6295 } catch (IllegalAccessException ignored) {
6296 } catch (IllegalArgumentException ignored) {
6297 } catch (InvocationTargetException ignored) {
6298 }
6299 }
6300 return null;
6301 }
6302
6303 /**
6304 * <p>Finds a <code>Throwable</code> for known types.</p>
6305 *
6306 * <p>Uses <code>instanceof</code> checks to examine the exception,
6307 * looking for well known types which could contain chained or
6308 * wrapped exceptions.</p>
6309 *
6310 * @param throwable the exception to examine
6311 * @return the wrapped exception, or <code>null</code> if not found
6312 */
6313 private static Throwable getCauseUsingWellKnownTypes(Throwable throwable) {
6314 if (throwable instanceof Nestable) {
6315 return ((Nestable) throwable).getCause();
6316 } else if (throwable instanceof SQLException) {
6317 return ((SQLException) throwable).getNextException();
6318 } else if (throwable instanceof InvocationTargetException) {
6319 return ((InvocationTargetException) throwable).getTargetException();
6320 } else {
6321 return null;
6322 }
6323 }
6324
6325 /**
6326 * concat two strings
6327 * @param a
6328 * @param b
6329 * @return the concatted string
6330 */
6331 public static String concat(String a, String b) {
6332 return a+b;
6333 }
6334
6335 /**
6336 * concat strings
6337 * @param a
6338 * @param b
6339 * @param c
6340 * @return the concatted string
6341 */
6342 public static String concat(String a, String b, String c) {
6343 return a+b+c;
6344 }
6345
6346 /**
6347 * concat strings
6348 * @param a
6349 * @param b
6350 * @param c
6351 * @param d
6352 * @return the concatted string
6353 */
6354 public static String concat(String a, String b, String c, String d) {
6355 return a+b+c+d;
6356 }
6357
6358
6359 /**
6360 * concat strings
6361 * @param a
6362 * @param b
6363 * @param c
6364 * @param d
6365 * @return the concatted string
6366 */
6367 public static String concat(String a, String b, String c, String d, String e) {
6368 return a+b+c+d+e;
6369 }
6370
6371 /**
6372 * Hostname of server
6373 * @return hostname of server
6374 */
6375 public static String hostname() {
6376 return GrouperUtil.hostname();
6377 }
6378
6379 /**
6380 * see if a regex matches a char, cache it
6381 */
6382 private static Map<MultiKey, Boolean> characterMatchesRegexCache = new HashMap<MultiKey, Boolean>();
6383
6384 /**
6385 *
6386 * @param character
6387 * @param regex
6388 * @return if regex
6389 */
6390 public static boolean characterMatchesRegex(Character character, String regex) {
6391
6392 MultiKey key = new MultiKey(regex, character);
6393
6394 Boolean result = characterMatchesRegexCache.get(key);
6395
6396 if (result == null) {
6397
6398 Pattern pattern = Pattern.compile(regex);
6399
6400 if (pattern.matcher(character.toString()).matches()) {
6401
6402 result = true;
6403
6404 } else {
6405
6406 result = false;
6407
6408 }
6409
6410 characterMatchesRegexCache.put(key, result);
6411
6412 }
6413
6414 return result;
6415 }
6416
6417
6418 /**
6419 * split by curly colons. only keep specific attribute values. if a regexAllowedChars is specified, then whitelist chars by regex.
6420 * character to replace (e.g. underscore), default is underscore. if there isnt a value, dont put it in the list
6421 * @param input
6422 * @param commaSeparatedAttributes
6423 * @param regexAllowedChars
6424 * @param replaceChar
6425 * @return the list of attributes
6426 *
6427 */
6428 public static List<String> splitTrimCurlyColons(String input, String commaSeparatedAttributes, String regexAllowedChars, String replaceChar) {
6429
6430 // {jobCategory=Staff}:{campus=UM_ANN-ARBOR}:{deptId=316033}:{deptGroup=UM_HOSPITAL}:{deptDescription=MEDICAL SHORT STAY UNIT}:{deptGroupDescription=Univ Hospitals & Health Center}
6431 // :{deptVPArea=EXEC_VP_MED_AFF}:{jobcode=278040}:{jobFamily=31}:{emplStatus=A}:{regTemp=R}:{supervisorId=44272654}:{tenureStatus=NA}:{jobIndicator=P}
6432
6433 Set<String> attributeNames = GrouperUtil.splitTrimToSet(commaSeparatedAttributes, ",");
6434
6435 List<String> results = new ArrayList<String>();
6436
6437 if (!StringUtils.isBlank(input)) {
6438
6439 Set<String> entries = GrouperUtil.nonNull(GrouperUtil.splitTrimToSet(input, ":"));
6440
6441 for (String entry : entries) {
6442
6443 if (GrouperUtil.isBlank(entry)) {
6444 continue;
6445 }
6446
6447 entry = GrouperUtil.stripStart(entry, "{");
6448 entry = GrouperUtil.stripEnd(entry, "}");
6449
6450 String name = GrouperUtil.prefixOrSuffix(entry, "=", true);
6451
6452 if (!attributeNames.contains(name)) {
6453 continue;
6454 }
6455
6456 String value = GrouperUtil.prefixOrSuffix(entry, "=", false);
6457
6458 if (!StringUtils.isBlank(regexAllowedChars)) {
6459
6460 StringBuilder valueBuilder = new StringBuilder();
6461
6462 for (char curChar : value.toCharArray()) {
6463
6464 if (!StringUtils.isBlank(regexAllowedChars)) {
6465 if (!characterMatchesRegex(curChar, regexAllowedChars)) {
6466 curChar = replaceChar.charAt(0);
6467 }
6468 }
6469
6470 valueBuilder.append(curChar);
6471 }
6472
6473 value = valueBuilder.toString();
6474 }
6475
6476 results.add(value);
6477
6478 }
6479
6480 }
6481
6482 return results;
6483 }
6484 /**
6485 * split by commas. only keep specific attribute values. if a regexAllowedChars is specified, then whitelist chars by regex.
6486 * character to replace (e.g. underscore), default is underscore. if there isnt a value, dont put it in the list
6487 * @param input
6488 * @param commaSeparatedAttributes
6489 * @param regexAllowedChars
6490 * @param replaceChar
6491 * @return the list of attributes
6492 *
6493 */
6494 public static List<String> splitTrimCommas(String input, String commaSeparatedAttributes, String regexAllowedChars, String replaceChar) {
6495
6496 // uid=sw4p,ou=Account,dc=andrew,dc=cmu,dc=edu
6497
6498 Set<String> attributeNames = GrouperUtil.splitTrimToSet(commaSeparatedAttributes, ",");
6499
6500 List<String> results = new ArrayList<String>();
6501
6502 if (!StringUtils.isBlank(input)) {
6503
6504 Set<String> entries = GrouperUtil.nonNull(GrouperUtil.splitTrimToSet(input, ","));
6505
6506 for (String entry : entries) {
6507
6508 if (GrouperUtil.isBlank(entry)) {
6509 continue;
6510 }
6511
6512 String name = GrouperUtil.prefixOrSuffix(entry, "=", true);
6513
6514 if (!attributeNames.contains(name)) {
6515 continue;
6516 }
6517
6518 String value = GrouperUtil.prefixOrSuffix(entry, "=", false);
6519
6520 if (!StringUtils.isBlank(regexAllowedChars)) {
6521
6522 StringBuilder valueBuilder = new StringBuilder();
6523
6524 for (char curChar : value.toCharArray()) {
6525
6526 if (!StringUtils.isBlank(regexAllowedChars)) {
6527 if (!characterMatchesRegex(curChar, regexAllowedChars)) {
6528 curChar = replaceChar.charAt(0);
6529 }
6530 }
6531
6532 valueBuilder.append(curChar);
6533 }
6534
6535 value = valueBuilder.toString();
6536 }
6537
6538 results.add(value);
6539
6540 }
6541
6542 }
6543
6544 return results;
6545 }
6546 }