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}