001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.juneau.commons.lang;
018
019import static java.util.Arrays.*;
020
021import java.lang.annotation.*;
022import java.util.*;
023
024import org.apache.juneau.commons.utils.*;
025
026/**
027 * Utility class for generating integer hash codes.
028 *
029 * <p>
030 * General usage:
031 * <p class='bjava'>
032 *    <jk>int</jk> <jv>hashCode</jv> = HashCode.<jsm>create</jsm>().add(<js>"foobar"</js>).add(<jv>myobject</jv>).add(123).get();
033 * </p>
034 *
035 */
036public class HashCode {
037
038   /**
039    * Create a new HashCode object.
040    *
041    * @return A new HashCode object.
042    */
043   public static final HashCode create() {
044      return new HashCode();
045   }
046
047   /**
048    * Calculates a hash code over the specified objects.
049    *
050    * <p>
051    * Uses the same algorithm as {@link java.util.Objects#hash(Object...)} (31 * result + element hash).
052    *
053    * <p>
054    * Special handling is provided for:
055    * <ul>
056    *    <li><b>Annotations:</b> Uses {@link AnnotationUtils#hash(Annotation)} to ensure consistent hashing
057    *       according to the {@link java.lang.annotation.Annotation#hashCode()} contract.
058    *    <li><b>Arrays:</b> Uses content-based hashing via {@link java.util.Arrays#hashCode(Object[])}
059    *       instead of identity-based hashing.
060    *    <li><b>Null values:</b> Treated as 0 in the hash calculation.
061    * </ul>
062    *
063    * @param objects The objects to calculate a hashcode over.
064    * @return A numerical hashcode value.
065    * @see #add(Object)
066    * @see AnnotationUtils#hash(Annotation)
067    * @see java.util.Objects#hash(Object...)
068    */
069   public static final int of(Object...objects) {
070      HashCode x = create();
071      for (var oo : objects)
072         x.add(oo);
073      return x.get();
074   }
075
076   private int hashCode = 1;
077
078   /**
079    * Hashes the hashcode into this object.
080    *
081    * <p>
082    * The formula is simply <c>hashCode = 31*hashCode + i;</c>
083    *
084    * @param i The hashcode to hash into this object's hashcode.
085    * @return This object.
086    */
087   public HashCode add(int i) {
088      hashCode = 31 * hashCode + i;
089      return this;
090   }
091
092   /**
093    * Hashes the hashcode of the specified object into this object.
094    *
095    * <p>
096    * The formula is <c>hashCode = 31*hashCode + elementHash;</c>
097    *
098    * <p>
099    * Special handling is provided for:
100    * <ul>
101    *    <li><b>Null values:</b> Adds 0 to the hash code.
102    *    <li><b>Annotations:</b> Uses {@link AnnotationUtils#hash(Annotation)} to ensure consistent hashing
103    *       according to the {@link java.lang.annotation.Annotation#hashCode()} contract.
104    *    <li><b>Arrays:</b> Uses content-based hashing via {@link java.util.Arrays#hashCode(Object[])}
105    *       instead of identity-based hashing. Supports all primitive array types and object arrays.
106    *    <li><b>Other objects:</b> Uses the object's {@link Object#hashCode()} method.
107    * </ul>
108    *
109    * @param o The object whose hashcode will be hashed with this object.
110    * @return This object.
111    * @see AnnotationUtils#hash(Annotation)
112    * @see java.util.Arrays#hashCode(Object[])
113    */
114   public HashCode add(Object o) {
115      o = unswap(o);
116      if (o == null) {
117         add(0);
118      } else if (o instanceof Annotation a) {
119         add(AnnotationUtils.hash(a));
120      } else if (o.getClass().isArray()) {
121         // Use content-based hashcode for arrays
122         if (o instanceof Object[] o2)
123            add(deepHashCode(o2));
124         else if (o instanceof int[] o2)
125            add(Arrays.hashCode(o2));
126         else if (o instanceof long[] o2)
127            add(Arrays.hashCode(o2));
128         else if (o instanceof short[] o2)
129            add(Arrays.hashCode(o2));
130         else if (o instanceof byte[] o2)
131            add(Arrays.hashCode(o2));
132         else if (o instanceof char[] o2)
133            add(Arrays.hashCode(o2));
134         else if (o instanceof boolean[] o2)
135            add(Arrays.hashCode(o2));
136         else if (o instanceof float[] o2)
137            add(java.util.Arrays.hashCode(o2));
138         else if (o instanceof double[] o2)
139            add(java.util.Arrays.hashCode(o2));
140      } else {
141         add(o.hashCode());
142      }
143      return this;
144   }
145
146   /**
147    * Return the calculated hashcode value.
148    *
149    * @return The calculated hashcode.
150    */
151   public int get() {
152      return hashCode;
153   }
154
155   /**
156    * Converts the object to a normalized form before grabbing it's hashcode.
157    *
158    * <p>
159    * Subclasses can override this method to provide specialized handling (e.g. converting numbers to strings so that
160    * <c>123</c> and <js>"123"</js> end up creating the same hashcode.)
161    *
162    * <p>
163    * Default implementation does nothing.
164    *
165    * @param o The object to normalize before getting it's hashcode.
166    * @return The normalized object.
167    */
168   protected Object unswap(Object o) {
169      return o;
170   }
171}