HierarchicalType.java
package org.opentrafficsim.base;
import java.util.LinkedHashSet;
import java.util.Objects;
import java.util.Set;
import org.djutils.base.Identifiable;
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-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
* BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
* </p>
* @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
* @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
* @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
* @param <T> Self-reference to type.
* @param <I> Infrastructure type belonging to hierarchical type
*/
public abstract class HierarchicalType<T extends HierarchicalType<T, I>, I extends HierarchicallyTyped<T, I>>
implements Identifiable, Type<T>
{
/** */
private static final long serialVersionUID = 20220928L;
/** The id of the type to make it identifiable. */
private final String id;
/** Parent type. */
private final T parent;
/** the children of the hierarchical type. */
private final Set<T> children = new LinkedHashSet<>();
/**
* 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
{
this(id, null);
}
/**
* Constructor that creates a hierarchical type including a link to a parent type.
* @param id String; The id of the type to make it identifiable.
* @param parent T; parent type; can be null, in that case no parent will be identified
* @throws NullPointerException if the id is null
*/
@SuppressWarnings("unchecked")
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;
if (this.parent != null)
{
parent.getChildren().add((T) this);
}
}
/** {@inheritDoc} */
@Override
public final String getId()
{
return this.id;
}
/**
* Return the parent of this type, or null when no parent was identified.
* @return parent or {@code null} if this is a top level type.
*/
public final T getParent()
{
return this.parent;
}
/**
* Return the children of this hierarchical type.
* @return children Set<T> The set of children of the hierarchical type
*/
public Set<T> getChildren()
{
return this.children;
}
/**
* Whether this, or any of the parent types, equals the given type.
* @param type T; type the type to look for
* @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
public int hashCode()
{
return Objects.hash(this.id, this.parent);
}
/** {@inheritDoc} */
@Override
@SuppressWarnings("checkstyle:needbraces")
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;
return Objects.equals(this.id, other.id) && Objects.equals(this.parent, other.parent);
}
}