HierarchicalType.java

package org.opentrafficsim.base;

import org.djutils.exceptions.Throw;

/**
 * Super class for types with hierarchical structure. Upper level types without parent can be created in sub classes using a
 * protected constructor without parent.
 * <p>
 * Copyright (c) 2013-2019 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
 * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
 * <p>
 * @version $Revision$, $LastChangedDate$, by $Author$, initial version 30 jun. 2017 <br>
 * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
 * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
 * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
 * @param <T> Self-reference to type.
 */
public abstract class HierarchicalType<T extends HierarchicalType<T>> extends Type<T> implements Identifiable
{

    /** The id of the type to make it identifiable. */
    private final String id;

    /** Parent type. */
    private final T parent;

    /** Cached hash code. */
    private int hashCode;

    /**
     * Constructor for creating the top level types in subclasses.
     * @param id String; The id of the type to make it identifiable.
     * @throws NullPointerException if the id is null
     */
    protected HierarchicalType(final String id) throws NullPointerException
    {
        Throw.whenNull(id, "id cannot be null for hierarchical types");
        this.id = id;
        this.parent = null;
    }

    /**
     * Constructor.
     * @param id String; The id of the type to make it identifiable.
     * @param parent T; parent type
     * @throws NullPointerException if the id is null
     */
    public HierarchicalType(final String id, final T parent) throws NullPointerException
    {
        Throw.whenNull(id, "id cannot be null for hierarchical types");
        this.id = id;
        this.parent = parent;
    }

    /** {@inheritDoc} */
    @Override
    public final String getId()
    {
        return this.id;
    }

    /**
     * @return parent or {@code null} if this is a top level type.
     */
    public final T getParent()
    {
        return this.parent;
    }

    /**
     * Whether this, or any of the parent types, equals the given type.
     * @param type T; type
     * @return whether this, or any of the parent types, equals the given type
     */
    public final boolean isOfType(final T type)
    {
        if (this.equals(type))
        {
            return true;
        }
        if (this.parent != null)
        {
            return this.parent.isOfType(type);
        }
        return false;
    }

    /**
     * Returns the common ancestor of both types.
     * @param type T; other type.
     * @return common ancestor of both types, {@code null} if none
     */
    public final T commonAncestor(final T type)
    {
        T otherType = type;
        while (otherType != null && !isOfType(otherType))
        {
            otherType = otherType.getParent();
        }
        return otherType;
    }

    /** {@inheritDoc} */
    @Override
    @SuppressWarnings("checkstyle:designforextension")
    public int hashCode()
    {
        if (this.hashCode == 0)
        {
            final int prime = 31;
            int result = 1;
            result = prime * result + ((this.id == null) ? 0 : this.id.hashCode());
            this.hashCode = prime * result + ((this.parent == null) ? 0 : this.parent.hashCode());
        }
        return this.hashCode;
    }

    /** {@inheritDoc} */
    @Override
    @SuppressWarnings("checkstyle:designforextension")
    public boolean equals(final Object obj)
    {
        if (this == obj)
        {
            return true;
        }
        if (obj == null)
        {
            return false;
        }
        if (getClass() != obj.getClass())
        {
            return false;
        }
        HierarchicalType<?> other = (HierarchicalType<?>) obj;
        if (this.id == null)
        {
            if (other.id != null)
            {
                return false;
            }
        }
        else if (!this.id.equals(other.id))
        {
            return false;
        }
        if (this.parent == null)
        {
            if (other.parent != null)
            {
                return false;
            }
        }
        else if (!this.parent.equals(other.parent))
        {
            return false;
        }
        return true;
    }

}