/** * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. * Portions Copyright 2013-2017 Philip Helger + contributors * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development * and Distribution License("CDDL") (collectively, the "License"). You * may not use this file except in compliance with the License. You can * obtain a copy of the License at * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html * or packager/legal/LICENSE.txt. See the License for the specific * language governing permissions and limitations under the License. * * When distributing the software, include this License Header Notice in each * file and include the License file at packager/legal/LICENSE.txt. * * GPL Classpath Exception: * Oracle designates this particular file as subject to the "Classpath" * exception as provided by Oracle in the GPL Version 2 section of the License * file that accompanied this code. * * Modifications: * If applicable, add the following below the License Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyright [year] [name of copyright owner]" * * Contributor(s): * If you wish your version of this file to be governed by only the CDDL or * only the GPL Version 2, indicate your decision by adding "[Contributor] * elects to include this software in this distribution under the [CDDL or GPL * Version 2] license." If you don't indicate a single choice of license, a * recipient has the option to distribute your version of this file under * either the CDDL, the GPL Version 2 or to extend the choice of license to * its licensees as provided above. However, if you add GPL Version 2 code * and therefore, elected the GPL Version 2 license, then the option applies * only if the new code is made subject to such option by the copyright * holder. */ package com.helger.jcodemodel.util; import java.util.Map; import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.annotation.concurrent.NotThreadSafe; import org.w3c.dom.Node; /** * A small hash code creation class based on the article found in the net. See * <a href= * "http://www.angelikalanger.com/Articles/JavaSpektrum/03.HashCode/03.HashCode.html" * >this article</a> for details.<br> * After calling {@link #append(Object)} for all objects use * {@link #getHashCode()} to retrieve the calculated hash code. Once the hash * code was calculated no modifications are allowed.<br> * <p> * A real world example for a final class derived from {@link Object} or a base * class looks like this: * </p> * * <pre> * @Override * public int hashCode () * { * return new HashCodeGenerator (this).append (member1).append (member2).getHashCode (); * } * </pre> * <p> * For a derived class, the typical code looks like this, assuming the base * class also uses {@link JCHashCodeGenerator}: * </p> * * <pre> * @Override * public int hashCode () * { * return HashCodeGenerator.getDerived (super.hashCode ()).append (member3).append (member4).getHashCode (); * } * </pre> * * @author Philip Helger */ @NotThreadSafe public final class JCHashCodeGenerator { /** Represents an illegal hash code that is never to be returned! */ public static final int ILLEGAL_HASHCODE = 0; /** Use a prime number as the start. */ public static final int INITIAL_HASHCODE = 17; /** * Once the hash code generation has been queried, no further changes may be * done. This flag indicates, whether new items can be added or not. */ private boolean m_bClosed = false; /** The current hash code value. */ private int m_nHC = INITIAL_HASHCODE; /** * This is a sanity constructor that allows for any object to be passed in the * constructor (e.g. <code>this</code>) from which the class is extracted as * the initial value of the hash code. * * @param aSrcObject * The source object from which the class is extracted. May not be * <code>null</code>. */ public JCHashCodeGenerator (@Nonnull final Object aSrcObject) { this (aSrcObject instanceof Class <?> ? (Class <?>) aSrcObject : aSrcObject.getClass ()); } /** * This constructor requires a class name, because in case a class has no * instance variables the hash code may be the same for different instances of * different classes. * * @param aClass * The class this instance is about to create a hash code for. May not * be <code>null</code>. */ public JCHashCodeGenerator (@Nonnull final Class <?> aClass) { if (aClass == null) throw new NullPointerException ("class"); // Use the class name append (aClass.getName ()); // Is it an array class? If so add the component class name. final Class <?> aComponentType = aClass.getComponentType (); if (aComponentType != null) append (aComponentType.getName ()); } private JCHashCodeGenerator (final int nSuperHashCode) { m_nHC = nSuperHashCode; } private void _checkClosed () { if (m_bClosed) throw new IllegalStateException ("Hash code cannot be changed anymore!"); } /** * Atomic type hash code generation. * * @param x * Array to add * @return this */ @Nonnull public JCHashCodeGenerator append (final boolean x) { _checkClosed (); m_nHC = JCHashCodeCalculator.append (m_nHC, x); return this; } /** * Atomic type hash code generation. * * @param x * Array to add * @return this */ @Nonnull public JCHashCodeGenerator append (final byte x) { _checkClosed (); m_nHC = JCHashCodeCalculator.append (m_nHC, x); return this; } /** * Atomic type hash code generation. * * @param x * Array to add * @return this */ @Nonnull public JCHashCodeGenerator append (final char x) { _checkClosed (); m_nHC = JCHashCodeCalculator.append (m_nHC, x); return this; } /** * Atomic type hash code generation. * * @param x * Array to add * @return this */ @Nonnull public JCHashCodeGenerator append (final double x) { _checkClosed (); m_nHC = JCHashCodeCalculator.append (m_nHC, x); return this; } /** * Atomic type hash code generation. * * @param x * Array to add * @return this */ @Nonnull public JCHashCodeGenerator append (final float x) { _checkClosed (); m_nHC = JCHashCodeCalculator.append (m_nHC, x); return this; } /** * Atomic type hash code generation. * * @param x * Array to add * @return this */ @Nonnull public JCHashCodeGenerator append (final int x) { _checkClosed (); m_nHC = JCHashCodeCalculator.append (m_nHC, x); return this; } /** * Atomic type hash code generation. * * @param x * Array to add * @return this */ @Nonnull public JCHashCodeGenerator append (final long x) { _checkClosed (); m_nHC = JCHashCodeCalculator.append (m_nHC, x); return this; } /** * Atomic type hash code generation. * * @param x * Array to add * @return this */ @Nonnull public JCHashCodeGenerator append (final short x) { _checkClosed (); m_nHC = JCHashCodeCalculator.append (m_nHC, x); return this; } /** * Object hash code generation. * * @param x * Array to add * @return this */ @Nonnull public JCHashCodeGenerator append (@Nullable final Object x) { _checkClosed (); m_nHC = JCHashCodeCalculator.append (m_nHC, x); return this; } /** * Object hash code generation. * * @param x * Array to add * @return this */ @Nonnull public JCHashCodeGenerator append (@Nullable final Enum <?> x) { _checkClosed (); m_nHC = JCHashCodeCalculator.append (m_nHC, x); return this; } /** * Array hash code generation. * * @param x * Array to add * @return this */ @Nonnull public JCHashCodeGenerator append (@Nullable final boolean [] x) { _checkClosed (); m_nHC = JCHashCodeCalculator.append (m_nHC, x); return this; } /** * Array hash code generation. * * @param x * Array to add * @return this */ @Nonnull public JCHashCodeGenerator append (@Nullable final byte [] x) { _checkClosed (); m_nHC = JCHashCodeCalculator.append (m_nHC, x); return this; } /** * Array hash code generation. * * @param x * Array to add * @return this */ @Nonnull public JCHashCodeGenerator append (@Nullable final char [] x) { _checkClosed (); m_nHC = JCHashCodeCalculator.append (m_nHC, x); return this; } /** * Array hash code generation. * * @param x * Array to add * @return this */ @Nonnull public JCHashCodeGenerator append (@Nullable final double [] x) { _checkClosed (); m_nHC = JCHashCodeCalculator.append (m_nHC, x); return this; } /** * Array hash code generation. * * @param x * Array to add * @return this */ @Nonnull public JCHashCodeGenerator append (@Nullable final float [] x) { _checkClosed (); m_nHC = JCHashCodeCalculator.append (m_nHC, x); return this; } /** * Array hash code generation. * * @param x * Array to add * @return this */ @Nonnull public JCHashCodeGenerator append (@Nullable final int [] x) { _checkClosed (); m_nHC = JCHashCodeCalculator.append (m_nHC, x); return this; } /** * Array hash code generation. * * @param x * Array to add * @return this */ @Nonnull public JCHashCodeGenerator append (@Nullable final long [] x) { _checkClosed (); m_nHC = JCHashCodeCalculator.append (m_nHC, x); return this; } /** * Array hash code generation. * * @param x * Array to add * @return this */ @Nonnull public JCHashCodeGenerator append (@Nullable final short [] x) { _checkClosed (); m_nHC = JCHashCodeCalculator.append (m_nHC, x); return this; } /** * Array hash code generation. * * @param x * Array to add * @return this */ @Nonnull public JCHashCodeGenerator append (@Nullable final Object [] x) { _checkClosed (); m_nHC = JCHashCodeCalculator.append (m_nHC, x); return this; } /** * Array hash code generation. * * @param x * Array to add * @return this */ @Nonnull public JCHashCodeGenerator append (@Nullable final Enum <?> [] x) { _checkClosed (); m_nHC = JCHashCodeCalculator.append (m_nHC, x); return this; } /** * Type specific hash code generation because parameter class has no * overloaded equals method. * * @param x * object to add * @return this */ @Nonnull public JCHashCodeGenerator append (@Nullable final StringBuffer x) { _checkClosed (); m_nHC = JCHashCodeCalculator.append (m_nHC, x); return this; } /** * Type specific hash code generation because parameter class has no * overloaded equals method. * * @param x * object to add * @return this */ @Nonnull public JCHashCodeGenerator append (@Nullable final StringBuilder x) { _checkClosed (); m_nHC = JCHashCodeCalculator.append (m_nHC, x); return this; } /** * @param x * to be included in the hash code generation. * @return this */ @Nonnull public JCHashCodeGenerator append (@Nullable final Iterable <?> x) { _checkClosed (); m_nHC = JCHashCodeCalculator.append (m_nHC, x); return this; } /** * @param x * to be included in the hash code generation. * @return this */ @Nonnull public JCHashCodeGenerator append (@Nullable final Map <?, ?> x) { _checkClosed (); m_nHC = JCHashCodeCalculator.append (m_nHC, x); return this; } /** * @param x * to be included in the hash code generation. * @return this */ @Nonnull public JCHashCodeGenerator append (@Nullable final Node x) { _checkClosed (); m_nHC = JCHashCodeCalculator.append (m_nHC, x); return this; } /** * Retrieve the final hash code. Once this method has been called, no further * calls to append can be done since the hash value is locked! * * @return The finally completed hash code. The returned value is never * {@link #ILLEGAL_HASHCODE}. If the calculated hash code would be * {@link #ILLEGAL_HASHCODE} it is changed to -1 instead. */ public int getHashCode () { m_bClosed = true; // This is for the very rare case, that the calculated hash code results in // an illegal value. if (m_nHC == ILLEGAL_HASHCODE) m_nHC = -1; return m_nHC; } /** * @return The same as {@link #getHashCode()} but as an {@link Integer} * object. Never <code>null</code>. */ @Nonnull public Integer getHashCodeObj () { return Integer.valueOf (getHashCode ()); } /** * Never compare {@link JCHashCodeGenerator} objects :) */ @Deprecated @Override public boolean equals (final Object o) { return o == this; } /** * Always use {@link #getHashCode()} * * @return {@link #getHashCode()} * @see #getHashCode() */ @Override @Deprecated public int hashCode () { return getHashCode (); } /** * Create a {@link JCHashCodeGenerator} for derived classes where the base * class also uses the {@link JCHashCodeGenerator}. This avoid calculating the * hash code of the class name more than once. * * @param nSuperHashCode * Always pass in <code>super.hashCode ()</code> * @return Never <code>null</code> */ @Nonnull public static JCHashCodeGenerator getDerived (final int nSuperHashCode) { if (nSuperHashCode == ILLEGAL_HASHCODE) throw new IllegalArgumentException ("Passed hash code is invalid!"); return new JCHashCodeGenerator (nSuperHashCode); } /** * Static helper method to create the hashcode of an object with a single * invocation. This method must be used by objects that directly derive from * Object. * * @param aThis * <code>this</code> * @param aMembers * A list of all members. Primitive types must be boxed. * @return The generated hashCode. */ public static int getHashCode (@Nonnull final Object aThis, @Nullable final Object... aMembers) { final JCHashCodeGenerator aHCGen = new JCHashCodeGenerator (aThis); if (aMembers != null) for (final Object aMember : aMembers) aHCGen.append (aMember); return aHCGen.getHashCode (); } /** * Static helper method to create the hashcode of an object with a single * invocation. This method must be used by objects that derive from a class * other than Object. * * @param nSuperHashCode * The result of <code>super.hashCode()</code> * @param aMembers * A list of all members. Primitive types must be boxed. * @return The generated hashCode. */ public static int getHashCode (@Nonnull final int nSuperHashCode, @Nullable final Object... aMembers) { final JCHashCodeGenerator aHCGen = getDerived (nSuperHashCode); if (aMembers != null) for (final Object aMember : aMembers) aHCGen.append (aMember); return aHCGen.getHashCode (); } }