All Downloads are FREE. Search and download functionalities are using the official Maven repository.

norswap.utils.visitors.Visitor Maven / Gradle / Ivy

The newest version!
package norswap.utils.visitors;

import java.util.HashMap;
import java.util.function.Consumer;

import static norswap.utils.Util.cast;

/**
 * An instance of this class can be used to specify an operation that has different behaviours
 * (specializations) for different subclasses of {@code T}.
 *
 * 

Each specialization is represented by an instance of {@link Consumer}. They are registered by * calling {@link #register(Class, Consumer)}. Specializations for a class are only operational for * values that have that specific class — inheritance does not enter into account when dispatching * the operation. * *

The operation is invoked by calling {@link #accept}. * *

If a specialization for the class of the value does not exist, a fallback specialization can * be called. The fallback specialization is registered by calling {@link * #registerFallback(Consumer)}. If not supplied, an {@link IllegalArgumentException} is thrown. * *

If you need to return a value from the visitor, use {@link ValuedVisitor} instead. * *

In practice it's easy to let the specializations access a context object and to wrap the * visitor in a function that sets arguments and retrieves the result from that object. Typically * that context object might be the {@code Consumer} instance itself, or an instance of the class * where all consumers are defined as lambda or methods. * *

This is implemented on top of a class-to-specialization hashmap. */ public final class Visitor implements Consumer { // --------------------------------------------------------------------------------------------- /** Map from classes to specializations.*/ private final HashMap, Consumer> dispatch = new HashMap<>(); private Consumer fallbackSpecialization = null; // --------------------------------------------------------------------------------------------- /** * Run the operation by calling the appropriate overload for {@code value}, or the fallback. */ @Override public void accept (T value) { Consumer action = dispatch.get(value.getClass()); if (action == null) { if (fallbackSpecialization == null) throw new IllegalArgumentException("no fallback specified for " + this); fallbackSpecialization.accept(value); } else action.accept(value); } // --------------------------------------------------------------------------------------------- /** * Register a specialization for the given class. */ public Visitor register (Class klass, Consumer specialization) { // The cast is a lie, but its statically safe because of erasure, and safe at runtime, // by construction. dispatch.put(klass, cast(specialization)); return this; } // --------------------------------------------------------------------------------------------- /** * Register the fallback specialization. */ public Visitor registerFallback (Consumer fallback) { this.fallbackSpecialization = fallback; return this; } // --------------------------------------------------------------------------------------------- }