Context.java

package com.timtrense.template;

import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NonNull;
import lombok.ToString;

import java.time.Instant;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;
import java.util.concurrent.Callable;

/**
 * The context with which a {@link Template} can be resolved to real text.
 * The {@link Context} holds the placeholders values and the {@link Locale} and
 * {@link TimeZone} of the user for that the template will be resolved (both default to system settings).
 *
 * @author Tim Trense
 * @since 1.0
 */
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class Context extends HashMap<@NonNull String, Object> {

    @Getter
    @NonNull
    private ZoneId zoneId = ZoneOffset.UTC;
    @Getter
    @NonNull
    private Locale locale = Locale.getDefault();

    /**
     * Instantiates a Context with initially no placeholder values
     */
    public Context() {
    }

    /**
     * Instantiates a Context from the given placeholder values
     *
     * @param values {@link Map} of placeholder keys (names) to values that will be copied
     */
    public Context(Map<@NonNull String, Object> values) {
        super(values);
    }

    /**
     * Instantiates a Context from the given placeholder values
     *
     * @param values   {@link Map} of placeholder keys (names) to values that will be copied
     * @param locale   the users {@link Locale}
     * @param zoneId the users {@link ZoneId time zone}
     */
    public Context(Map<@NonNull String, Object> values, @NonNull Locale locale, @NonNull ZoneId zoneId) {
        super(values);
        this.locale = locale;
        this.zoneId = zoneId;
    }

    /**
     * Instantiates a Context from the given locale
     *
     * @param locale   the users {@link Locale}
     * @param zoneId the users {@link ZoneId time zone}
     */
    public Context(@NonNull Locale locale, @NonNull ZoneId zoneId) {
        this.locale = locale;
        this.zoneId = zoneId;
    }

    /**
     * Instantiates a Context from the given locale
     *
     * @param zoneId the users {@link ZoneId time zone}
     */
    public Context(@NonNull ZoneId zoneId) {
        this.zoneId = zoneId;
    }


    /**
     * Instantiates a Context from the given locale
     *
     * @param locale the users {@link Locale}
     */
    public Context(@NonNull Locale locale) {
        this.locale = locale;
    }

    /**
     * @return the current {@link ZoneOffset} of the contained {@link #zoneId}
     */
    public ZoneOffset getZoneOffsetNow() {
        return getZoneOffsetAt(Instant.now());
    }

    /**
     * @param instant the time at which to resolve the offset
     * @return the {@link ZoneOffset} of the contained {@link #zoneId} at specified time
     */
    public ZoneOffset getZoneOffsetAt(@NonNull Instant instant) {
        return zoneId.getRules().getOffset(instant);
    }

    /**
     * Contract to put in lazily evaluated lambda expressions
     *
     * @param key   as stated by {@link Map#put(Object, Object)}
     * @param value as stated by {@link Map#put(Object, Object)}
     * @return as stated by {@link Map#put(Object, Object)}
     * @see Map#put(Object, Object)
     */
    public Object put(@NonNull String key, Callable<?> value) {
        return super.put(key, value);
    }
}