d40cc29bbef20fad228b99ef7b927f61d80659b3
[cdmlib.git] / cdmlib-commons / src / main / java / eu / etaxonomy / cdm / common / CdmUtils.java
1 /**
2 * Copyright (C) 2007 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
5 *
6 * The contents of this file are subject to the Mozilla Public License Version 1.1
7 * See LICENSE.TXT at the top of this package for the full license terms.
8 */
9 package eu.etaxonomy.cdm.common;
10
11 import java.io.BufferedReader;
12 import java.io.IOException;
13 import java.io.InputStream;
14 import java.io.InputStreamReader;
15 import java.lang.annotation.Annotation;
16 import java.lang.reflect.Field;
17 import java.lang.reflect.Modifier;
18 import java.net.HttpURLConnection;
19 import java.net.MalformedURLException;
20 import java.net.URL;
21 import java.util.ArrayList;
22 import java.util.HashMap;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.regex.Matcher;
26 import java.util.regex.Pattern;
27
28 import org.apache.commons.lang3.StringUtils;
29 import org.apache.log4j.Logger;
30
31 /**
32 * @author a.mueller
33 * @author a.kohlbecker
34 */
35 public class CdmUtils {
36
37 private static final Logger logger = Logger.getLogger(CdmUtils.class);
38
39 static private boolean urlIsJarOrBundle(URL url){
40 return url.getProtocol().startsWith("jar") || url.getProtocol().startsWith("bundleresource");
41 }
42
43 /**
44 * Returns the an InputStream for a read-only source
45 * @param resourceFileName the resources path within the classpath(!)
46 * @return
47 * @throws IOException
48 */
49 public static InputStream getReadableResourceStream(String resourceFileName)
50 throws IOException{
51 InputStream urlStream = CdmUtils.class.getResourceAsStream("/"+ resourceFileName);
52 return urlStream;
53 }
54
55 /**
56 * Returns an InputStream for a read-only source
57 * @param resourceFileName the resources path within the classpath(!)
58 * @return
59 * @throws IOException
60 */
61 public static InputStreamReader getUtf8ResourceReader(String resourceFileName)
62 throws IOException{
63 InputStream urlStream = CdmUtils.class.getResourceAsStream("/"+ resourceFileName);
64 InputStreamReader inputStreamReader = new InputStreamReader(urlStream, "UTF8");
65 return inputStreamReader;
66 }
67
68 /**
69 * Returns the file name for the file in which 'clazz' is to be found (helps finding according libraries)
70 * @param clazz
71 * @return
72 */
73 static public String findLibrary(Class<?> clazz){
74 String result = null;
75 if (clazz != null){
76 String fullPackageName = clazz.getCanonicalName();
77 fullPackageName = fullPackageName.replace(".", "/");
78 URL url = CdmUtils.class.getResource("/" + fullPackageName + ".class" );
79 if (url != null){
80 result = url.getFile();
81 }else{
82 result = "";
83 }
84 logger.debug("LibraryURL for " + clazz.getCanonicalName() + " : " + result);
85 }
86 return result;
87 }
88
89 static public String readInputLine(String inputQuestion){
90
91 try {
92 System.out.print(inputQuestion);
93 BufferedReader in = new BufferedReader( new InputStreamReader( System.in ));
94 String input;
95 input = in.readLine();
96 return input;
97 } catch (IOException e) {
98 logger.warn("IOExeption");
99 return null;
100 }
101 }
102
103 /**
104 * Returns the trimmed value string if value is not <code>null</code>.
105 * Returns the empty string if value is <code>null</code>.
106 * @param value
107 * @return
108 */
109 static public String NzTrim(String value){
110 return (value == null ? "" : value);
111 }
112
113 /**
114 * Returns value if value is not <code>null</code>. Returns empty string if value is <code>null</code>.
115 * @param value
116 * @return
117 */
118 static public String Nz(String value){
119 return (value == null ? "" : value);
120 }
121
122 /**
123 * Returns value if value is not <code>null</code>. Returns defaultValue if value is <code>null</code>.
124 * @param value
125 * @return
126 */
127 static public String Nz(String value, String defaultValue){
128 return (value == null ? defaultValue : value);
129 }
130
131 /**
132 * Returns value if value is not <code>null</code>. Returns 0 if value is <code>null</code>.
133 * @param value
134 * @return
135 */
136 static public Integer Nz(Integer value){
137 return (value == null ? 0 : value);
138 }
139
140 /**
141 * Returns value if value is not <code>null</code>. Returns 0 if value is <code>null</code>.
142 * @param value
143 * @return
144 */
145 static public Long Nz(Long value){
146 return (value == null ? 0 : value);
147 }
148
149 /**
150 * Returns str if str is not the empty String (''). Returns null if str is empty.
151 * @param str
152 * @return
153 */
154 static public String Ne(String str){
155 return ("".equals(str)? null : str);
156 }
157
158 /**
159 * Returns str if str.trim() is not empty. Returns null otherwise.
160 * @param str
161 * @return
162 */
163 static public String Nb(String str){
164 return (str == null || str.trim().equals("")? null : str);
165 }
166
167 /**
168 * Concatenates an array of strings using the defined separator.<BR>
169 * <code>Null</code> values and empty strings are handled as if they
170 * do not exist. So <BR><BR>
171 *
172 * concat(":", "a", "", null, "b") results in "a:b"<BR><BR>
173 *
174 * If all strings are <code>null</code> then <code>null</code> is returned.
175 *
176 * @see #concat(CharSequence, String, String)
177 * @param strings the strings to concatenate
178 * @param seperator the separator for concatenation
179 * @return String the concatenation result
180 */
181 static public String concat(CharSequence separator, String... strings){
182 StringBuilder result = new StringBuilder();
183 boolean allNull = true;
184 for (String string : strings){
185 if (string != null){
186 if (result.length() > 0 && string.length() > 0){
187 result.append(separator);
188 }
189 result.append(string);
190 allNull = false;
191 }
192 }
193 //if all strings are null result should be null, not ""
194 if (allNull){
195 return null;
196 }else {
197 return result.toString();
198 }
199 }
200
201
202 /**
203 * Concatenates two strings, using the defined separator.<BR>
204 * <code>Null</code> values are interpreted as empty strings.<BR>
205 * Empty strings are not included in concatenation so concat(":", "a", "")
206 * results in "a", not "a:".<BR>
207 *
208 * If both strings are <code>null</code> then <code>null</code> is returned.
209 *
210 * @see #concat(CharSequence, String[])
211 * @param sepearator the separator
212 * @param string1 first string to concatenate
213 * @param string2 second string to concatenate
214 * @return String the concatenated string
215 */
216 static public String concat(CharSequence separator, String string1, String string2){
217 String[] strings = {string1, string2};
218 return concat(separator, strings);
219 }
220
221 /**
222 * Returns <code>preferred</code> if not blank, else returns <code>alternative</code>.
223 * If reverse is <code>true</code> computation is
224 * the other way round (<code>alternative</code> if not blank, otherwise <code>preferred</code>).
225 * @param preferred first string
226 * @param alternative second string
227 * @param reverse reverse flag
228 * @param nzTrim if <code>true</code> the result is trimmed and <code>null</code> values are replaced by empty string.
229 * @return the preferred string
230 */
231 static public String getPreferredNonEmptyString(String preferred, String alternative, boolean reverse, boolean nzTrim){
232 String result;
233 if (! reverse){
234 result = StringUtils.isBlank(preferred) ? alternative : preferred;
235 }else{
236 result = StringUtils.isBlank(alternative) ? preferred : alternative;
237 }
238 if (nzTrim){
239 result = Nz(result).trim();
240 }
241 return result;
242 }
243
244 /** Returns a version of the input where all contiguous
245 * whitespace characters are replaced with a single
246 * space. Line terminators are treated like whitespace.
247 *
248 * @param inputStr
249 * @return
250 */
251 public static CharSequence removeDuplicateWhitespace(CharSequence inputStr) {
252
253 String patternStr = "\\s+";
254 String replaceStr = " ";
255 Pattern pattern = Pattern.compile(patternStr);
256 Matcher matcher = pattern.matcher(inputStr);
257 return matcher.replaceAll(replaceStr);
258 }
259
260 /**
261 * Builds a list of strings by splitting an input string
262 * with delimiters whitespace, comma, or semicolon
263 * @param value
264 * @return
265 */
266 public static List<String> buildList(String value) {
267
268 List<String> resultList = new ArrayList<String>();
269 for (String tag : value.split("[\\s,;]+")) {
270 resultList.add(tag);
271 }
272 return resultList;
273 }
274
275 static public boolean urlExists(String strUrl, boolean withWarning){
276 try {
277 HttpURLConnection.setFollowRedirects(false);
278 // note : you may also need
279 // HttpURLConnection.setInstanceFollowRedirects(false)
280 HttpURLConnection con =
281 (HttpURLConnection) new URL(strUrl).openConnection();
282 con.setRequestMethod("HEAD");
283 return (con.getResponseCode() == HttpURLConnection.HTTP_OK);
284 } catch (MalformedURLException e) {
285 if (withWarning) {
286 logger.warn(e);
287 }
288 } catch (IOException e) {
289 //
290 }
291 return false;
292 }
293
294 static public boolean isNumeric(String string){
295 if (string == null){
296 return false;
297 }
298 try {
299 Double.valueOf(string);
300 return true;
301 } catch (NumberFormatException e) {
302 return false;
303 }
304 }
305
306 /**
307 * Returns <code>true</code> if the passed string starts with an upper case letter.
308 * <code>false</code> otherwise. The later includes <code>null</code> and empty strings.
309 * @param string
310 * @return
311 */
312 static public boolean isCapital(String string){
313 if (isBlank(string)){
314 return false;
315 }else{
316 Character firstChar = string.charAt(0);
317 if (firstChar.equals(Character.toUpperCase(firstChar))){
318 return true;
319 }else{
320 return false;
321 }
322 }
323 }
324
325 /**
326 * Returns true if string is null, "" or string.trim() is ""
327 * @see isNotEmpty(String string)
328 * @param string
329 * @return
330 */
331 static public boolean isBlank(String string){
332 if (string == null){
333 return true;
334 }
335 if ("".equals(string.trim())){
336 return true;
337 }
338 return false;
339 }
340
341 /**
342 * Returns <code>false</code> if string is null, "" or string.trim() is ""
343 * @see isNotEmpty(String string)
344 * @param string
345 * @return
346 */
347 static public boolean isNotBlank(String string){
348 return ! isBlank(string);
349 }
350
351 /**
352 * @see #isBlank(String)
353 * @deprecated use {@link #isBlank(String)} instead
354 * @param string
355 * @return
356 */
357 @Deprecated
358 static public boolean isEmpty(String string){
359 return isBlank(string);
360 }
361
362 static public boolean areBlank(String ... strings){
363 for (String string : strings){
364 if (! isBlank(string)){
365 return false;
366 }
367 }
368 return true;
369 }
370
371 /**
372 * Tests if two objects are equal or both null. Otherwise returns false
373 * @param obj1
374 * @param obj2
375 * @return
376 */
377 public static boolean nullSafeEqual(Object obj1, Object obj2) {
378 if (obj1 == null){
379 return obj2 == null;
380 }
381 return (obj1.equals(obj2));
382 }
383
384 /**
385 * Compares 2 instances of {@link Comparable} with defined values for <code>null</code>
386 */
387 public static <T extends Comparable<T>> int nullSafeCompareTo(T c1, T c2) {
388 if (c1 == null){
389 return c2 == null ? 0 : -1;
390 }else if (c2 == null){
391 return 1;
392 }else{
393 return (c1.compareTo(c2));
394 }
395 }
396
397 /**
398 * Computes all fields recursively
399 * @param clazz
400 * @return
401 */
402 public static Map<String, Field> getAllFields(Class clazz, Class highestClass, boolean includeStatic, boolean includeTransient, boolean makeAccessible, boolean includeHighestClass) {
403 Map<String, Field> result = new HashMap<>();
404 if ( highestClass.isAssignableFrom(clazz) && (clazz != highestClass || includeHighestClass)){
405 //exclude static
406 for (Field field: clazz.getDeclaredFields()){
407 if (includeStatic || ! Modifier.isStatic(field.getModifiers())){
408 if (includeTransient || ! isTransient(field)){
409 field.setAccessible(makeAccessible);
410 result.put(field.getName(), field);
411 }
412 }
413 }
414
415 //include superclass fields
416 Class<?> superclass = clazz.getSuperclass();
417 if (superclass != null){
418 result.putAll(getAllFields(superclass, highestClass, includeStatic, includeTransient, makeAccessible, includeHighestClass));
419 }
420 }
421 return result;
422 }
423
424 /**
425 * Returns true, if field has an annotation of type javax.persistence.Annotation
426 * @param field
427 * @return
428 */
429 protected static boolean isTransient(Field field) {
430 for (Annotation annotation : field.getAnnotations()){
431 //if (Transient.class.isAssignableFrom(annotation.annotationType())){
432 if (annotation.annotationType().getSimpleName().equals("Transient")){
433 return true;
434 }
435 }
436 return false;
437 }
438
439 /**
440 * Trims the string and if the string ends with 1 or more dots removes it.
441 */
442 public static String removeTrailingDots(String string){
443 while (string != null && string.trim().endsWith(".")){
444 return string.substring(0, string.length() -1);
445 }
446 return string;
447 }
448
449 /**
450 * Adds a trailing dot to the given String
451 * if string is not blank and does not end with dot already.
452 * Otherwise str is returned.
453 */
454 public static String addTrailingDotIfNotExists(String str){
455 if (StringUtils.isNotBlank(str) && !str.endsWith(".")){
456 str += ".";
457 }
458 return str;
459 }
460
461 /**
462 * Returns surrounding brackets "(",")". Trim the string if necessary.
463 * @param text
464 * @return
465 */
466 public static String removeBrackets(String text) {
467 if (text == null){
468 return null;
469 }
470 text = text.trim();
471 if (text.matches("^\\(.*\\)$")){
472 text = text.substring(1, text.length() -1);
473 }
474 return text;
475 }
476
477 /**
478 * Compares 2 strings. If they are not empty and equal returns <code>true</code>
479 * otherwise false.
480 *
481 * @param str1
482 * @param str2
483 * @return compare result as boolean
484 */
485 public static boolean nonEmptyEquals(String str1, String str2) {
486 return (isNotBlank(str1) && str1.equals(str2));
487 }
488
489 /**
490 * Compares if str1 and str2 is equal when ignoring whitespaces.
491 * Returns <code>true</code> if both or <code>null</code> or
492 * whitespace ignore equal.
493 * @param str1
494 * @param str2
495 * @return
496 */
497 public static boolean equalsIgnoreWS(String str1, String str2) {
498 if (str1 == null){
499 return str2 == null;
500 }else if (str2 == null){
501 return false;
502 }else{
503 return str1.replaceAll("\\s", "").equals(str2.replaceAll("\\s", ""));
504 }
505 }
506
507 /**
508 * Checks if all strings given provide are {@link #isBlank(String) blank}.
509 * Returns <code>true</code> if strs is null or empty
510 * @param strs
511 * @return
512 */
513 public static boolean isBlank(String ... strs) {
514 if (strs == null){
515 return true;
516 }
517 for (String str : strs) {
518 if (isNotBlank(str)){
519 return false;
520 }
521 }
522 return true;
523 }
524
525 /**
526 * Transforms a search string which allows wildcard "*" into a
527 * java regular expression such that all other characters are handled as normal text.
528 * @param regEx
529 * @return
530 */
531 public static String quoteRegExWithWildcard(String regEx){
532 return Pattern.quote(regEx).replace("*", "\\E.*\\Q").replace("\\Q\\E", "");
533 }
534
535 public static int diffIndex(String str1, String str2) {
536 if (str1 == null || str2 == null){
537 return 0;
538 }
539 for (int i = 0; i<str1.length() && i<str2.length() ;i++) {
540 if (str1.charAt(i)!= str2.charAt(i)){
541 return i;
542 }
543 }
544 if(str1.length()!=str2.length()){
545 return Math.max(str1.length(), str2.length());
546 }
547 return -1;
548 }
549
550 public static String userFriendlyCamelCase(String camelCase){
551 return String.join(" ", StringUtils.splitByCharacterTypeCamelCase(camelCase));
552 }
553
554 public static String userFriendlyClassName(Class<?> clazz){
555 return userFriendlyCamelCase(clazz.getSimpleName());
556 }
557
558 }