001// *************************************************************************************************************************** 002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * 003// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * 004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * 005// * with the License. You may obtain a copy of the License at * 006// * * 007// * http://www.apache.org/licenses/LICENSE-2.0 * 008// * * 009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * 010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * 011// * specific language governing permissions and limitations under the License. * 012// *************************************************************************************************************************** 013package org.apache.juneau.internal; 014 015import java.lang.reflect.*; 016import java.util.*; 017import java.util.concurrent.*; 018import java.util.function.*; 019 020import org.apache.juneau.*; 021import org.apache.juneau.collections.*; 022import org.apache.juneau.common.internal.*; 023import org.apache.juneau.reflect.*; 024 025/** 026 * Various generic object utility methods. 027 * 028 * <h5 class='section'>See Also:</h5><ul> 029 * </ul> 030 */ 031public class ObjectUtils { 032 033 /** 034 * If the specified object is an instance of the specified class, casts it to that type. 035 * 036 * @param <T> The class to cast to. 037 * @param o The object to cast. 038 * @param c The class to cast to. 039 * @return The cast object, or <jk>null</jk> if the object wasn't an instance of the specified class. 040 */ 041 public static <T> T castOrNull(Object o, Class<T> c) { 042 if (c.isInstance(o)) 043 return c.cast(o); 044 return null; 045 } 046 047 /** 048 * Compares two objects for equality. 049 * 050 * <p> 051 * Nulls are always considered less-than unless both are null. 052 * 053 * @param o1 Object 1. 054 * @param o2 Object 2. 055 * @return 056 * <c>-1</c>, <c>0</c>, or <c>1</c> if <c>o1</c> is less-than, equal, or greater-than <c>o2</c>. 057 * <br><c>0</c> if objects are not of the same type or do not implement the {@link Comparable} interface. 058 */ 059 @SuppressWarnings({ "rawtypes", "unchecked" }) 060 public static int compare(Object o1, Object o2) { 061 if (o1 == null) { 062 if (o2 == null) 063 return 0; 064 return -1; 065 } else if (o2 == null) { 066 return 1; 067 } 068 069 if (o1.getClass() == o2.getClass() && o1 instanceof Comparable) 070 return ((Comparable)o1).compareTo(o2); 071 072 return 0; 073 } 074 075 /** 076 * Compare two integers numerically. 077 * 078 * @param i1 Integer #1 079 * @param i2 Integer #2 080 * @return 081 * The value <c>0</c> if Integer #1 is equal to Integer #2; a value less than <c>0</c> if 082 * Integer #1 numerically less than Integer #2; and a value greater than <c>0</c> if Integer #1 is 083 * numerically greater than Integer #2 (signed comparison). 084 */ 085 public static final int compare(int i1, int i2) { 086 return (i1<i2 ? -1 : (i1==i2 ? 0 : 1)); 087 } 088 089 /** 090 * Tests two objects for equality, gracefully handling nulls. 091 * 092 * @param <T> Object 1 type. 093 * @param <U> Object 2 type. 094 * @param o1 Object 1. 095 * @param o2 Object 2. 096 * @param test The test to use for equality. 097 * @return <jk>true</jk> if both objects are equal based on the test. 098 */ 099 public static <T,U> boolean eq(T o1, U o2, BiPredicate<T,U> test) { 100 if (o1 == null) 101 return o2 == null; 102 if (o2 == null) 103 return false; 104 if (o1 == o2) 105 return true; 106 return test.test(o1, o2); 107 } 108 109 /** 110 * Tests two objects for equality, gracefully handling nulls and arrays. 111 * 112 * @param <T> The value types. 113 * @param o1 Object 1. 114 * @param o2 Object 2. 115 * @return <jk>true</jk> if both objects are equal based on the {@link Object#equals(Object)} method. 116 */ 117 public static <T> boolean eq(T o1, T o2) { 118 if (isArray(o1) && isArray(o2)) { 119 int l1 = Array.getLength(o1), l2 = Array.getLength(o2); 120 if (l1 != l2) 121 return false; 122 for (int i = 0; i < l1; i++) 123 if (! eq(Array.get(o1, i), Array.get(o2, i))) 124 return false; 125 return true; 126 } 127 return Objects.equals(o1, o2); 128 } 129 130 /** 131 * Tests two arrays for equality, gracefully handling nulls. 132 * 133 * @param <T> The value types. 134 * @param o1 Array 1. 135 * @param o2 Array 2. 136 * @return <jk>true</jk> if both arrays are equal based on the {@link Object#equals(Object)} method on each element. 137 */ 138 public static <T> boolean eq(T[] o1, T[] o2) { 139 if (o1 == null || o2 == null) { 140 if (o1 != null || o2 != null) 141 return false; 142 return true; 143 } 144 if (o1.length != o2.length) 145 return false; 146 for (int i = 0; i < o1.length; i++) 147 if (! eq(o1[i], o2[i])) 148 return false; 149 return true; 150 151 } 152 153 /** 154 * Tests two objects for inequality, gracefully handling nulls. 155 * 156 * @param <T> Object 1 type. 157 * @param <U> Object 2 type. 158 * @param o1 Object 1. 159 * @param o2 Object 2. 160 * @param test The test to use for equality. 161 * @return <jk>false</jk> if both objects are equal based on the test. 162 */ 163 public static <T,U> boolean ne(T o1, U o2, BiPredicate<T,U> test) { 164 if (o1 == null) 165 return o2 != null; 166 if (o2 == null) 167 return true; 168 if (o1 == o2) 169 return false; 170 return ! test.test(o1, o2); 171 } 172 173 /** 174 * Tests two objects for equality, gracefully handling nulls and arrays. 175 * 176 * @param o1 Object 1. 177 * @param o2 Object 2. 178 * @return <jk>false</jk> if both objects are equal based on the {@link Object#equals(Object)} method. 179 */ 180 public static boolean ne(Object o1, Object o2) { 181 return ! eq(o1, o2); 182 } 183 184 private static boolean isArray(Object o) { 185 return o != null && o.getClass().isArray(); 186 } 187 188 /** 189 * If the specified object is a {@link Supplier} or {@link Value}, returns the inner value, otherwise the same value. 190 * 191 * @param o The object to unwrap. 192 * @return The unwrapped object. 193 */ 194 public static Object unwrap(Object o) { 195 while (o instanceof Supplier) 196 o = ((Supplier<?>)o).get(); 197 while (o instanceof Value) 198 o = ((Value<?>)o).get(); 199 return o; 200 } 201 202 /** 203 * Returns <jk>true</jk> if the specified object is empty. 204 * 205 * <p> 206 * Return <jk>true</jk> if the value is any of the following: 207 * <ul> 208 * <li><jk>null</jk> 209 * <li>An empty Collection 210 * <li>An empty Map 211 * <li>An empty array 212 * <li>An empty CharSequence 213 * <li>An empty String when serialized to a string using {@link Object#toString()}. 214 * </ul> 215 * 216 * @param o The object to test. 217 * @return <jk>true</jk> if the specified object is empty. 218 */ 219 @SuppressWarnings("rawtypes") 220 public static boolean isEmpty(Object o) { 221 if (o == null) 222 return true; 223 if (o instanceof Collection) 224 return ((Collection)o).isEmpty(); 225 if (o instanceof Map) 226 return ((Map)o).isEmpty(); 227 if (o.getClass().isArray()) 228 return (Array.getLength(o) == 0); 229 return o.toString().isEmpty(); 230 } 231 232 /** 233 * Returns the first non-null value in the specified array 234 * 235 * @param <T> The value types. 236 * @param t The values to check. 237 * @return The first non-null value, or <jk>null</jk> if the array is null or empty or contains only <jk>null</jk> values. 238 */ 239 @SafeVarargs 240 public static <T> T firstNonNull(T... t) { 241 if (t != null) 242 for (T tt : t) 243 if (tt != null) 244 return tt; 245 return null; 246 } 247 248 /** 249 * Casts an object to a specific type if it's an instance of that type. 250 * 251 * @param <T> The type to cast to. 252 * @param c The type to cast to. 253 * @param o The object to cast to. 254 * @return The cast object, or <jk>null</jk> if the object wasn't the specified type. 255 */ 256 public static <T> T cast(Class<T> c, Object o) { 257 return o != null && c.isInstance(o) ? c.cast(o) : null; 258 } 259 260 /** 261 * Converts the specified object into an identifiable string of the form "Class[identityHashCode]" 262 * @param o The object to convert to a string. 263 * @return An identity string. 264 */ 265 public static String identity(Object o) { 266 if (o instanceof Optional) 267 o = ((Optional<?>)o).orElse(null); 268 if (o == null) 269 return null; 270 return ClassInfo.of(o).getShortName() + "@" + System.identityHashCode(o); 271 } 272 273 private static final ConcurrentHashMap<Class<?>,Map<String,MethodInfo>> PROPERTIES_METHODS = new ConcurrentHashMap<>(); 274 275 /** 276 * Searches for all <c>properties()</c> methods on the specified object and creates a combine map of them. 277 * 278 * @param o The object to return a property map of. 279 * @return A new property map. 280 */ 281 public static JsonMap toPropertyMap(Object o) { 282 if (o == null) 283 return null; 284 Map<String,MethodInfo> methods = PROPERTIES_METHODS.get(o.getClass()); 285 if (methods == null) { 286 ClassInfo ci = ClassInfo.of(o); 287 Map<String,MethodInfo> methods2 = new LinkedHashMap<>(); 288 do { 289 String cname = ci.getShortName(); 290 MethodInfo mi = ci.getDeclaredMethod(x -> x.hasName("properties")); 291 if (mi != null) 292 methods2.put(cname, mi.accessible()); 293 ci = ci.getSuperclass(); 294 } while (ci != null); 295 methods = methods2; 296 PROPERTIES_METHODS.put(o.getClass(), methods); 297 } 298 JsonMap m = JsonMap.create().append("id", identity(o)); 299 methods.forEach((k,v) -> m.put(k, v.invoke(o))); 300 return m; 301 } 302 303 /** 304 * Returns <jk>true</jk> if the specified object is not <jk>null</jk>. 305 * 306 * @param <T> The value type. 307 * @param value The value being checked. 308 * @return <jk>true</jk> if the specified object is not <jk>null</jk>. 309 */ 310 public static <T> boolean isNotNull(T value) { 311 return value != null; 312 } 313 314 /** 315 * Returns <jk>true</jk> if the specified boolean is not <jk>null</jk> and is <jk>true</jk>. 316 * 317 * @param value The value being checked. 318 * @return <jk>true</jk> if the specified boolean is not <jk>null</jk> and is <jk>true</jk>. 319 */ 320 public static boolean isTrue(Boolean value) { 321 return value != null && value; 322 } 323 324 /** 325 * Returns <jk>true</jk> if the specified number is not <jk>null</jk> and not <c>-1</c>. 326 * 327 * @param <T> The value types. 328 * @param value The value being checked. 329 * @return <jk>true</jk> if the specified number is not <jk>null</jk> and not <c>-1</c>. 330 */ 331 public static <T extends Number> boolean isNotMinusOne(T value) { 332 return value != null && value.intValue() != -1; 333 } 334 335 /** 336 * Returns <jk>true</jk> if the specified object is not <jk>null</jk> and not empty. 337 * 338 * Works on any of the following data types: String, CharSequence, Collection, Map, array. 339 * All other types are stringified and then checked as a String. 340 * 341 * @param value The value being checked. 342 * @return <jk>true</jk> if the specified object is not <jk>null</jk> and not empty. 343 */ 344 public static boolean isNotEmpty(Object value) { 345 if (value == null) return false; 346 if (value instanceof CharSequence) return CharSequence.class.cast(value).length() > 0; 347 if (value instanceof Collection) return ! Collection.class.cast(value).isEmpty(); 348 if (value instanceof Map) return ! Map.class.cast(value).isEmpty(); 349 if (value.getClass().isArray()) return Array.getLength(value) > 0; 350 return StringUtils.isNotEmpty(StringUtils.stringify(value)); 351 } 352}