001/*
002 * SPDX-FileCopyrightText: none
003 * SPDX-License-Identifier: CC0-1.0
004 */
005
006package dev.metaschema.core.metapath;
007
008import dev.metaschema.core.metapath.item.IItem;
009import edu.umd.cs.findbugs.annotations.NonNull;
010
011/**
012 * Represents the focus context for Metapath evaluation, containing the context
013 * item, position, and size as defined in the
014 * <a href="https://www.w3.org/TR/xpath-31/#eval_context">XPath 3.1 evaluation
015 * context</a>.
016 * <p>
017 * The focus context is established when evaluating predicates and provides the
018 * information needed by the {@code fn:position()} and {@code fn:last()}
019 * functions.
020 */
021public final class FocusContext {
022  @NonNull
023  private final IItem contextItem;
024  private final int position;
025  private final int size;
026
027  private FocusContext(@NonNull IItem contextItem, int position, int size) {
028    this.contextItem = contextItem;
029    this.position = position;
030    this.size = size;
031  }
032
033  /**
034   * Create a new focus context for the given item at the specified position
035   * within a sequence.
036   *
037   * @param item
038   *          the context item
039   * @param position
040   *          the 1-based position of the item within the sequence
041   * @param size
042   *          the total number of items in the sequence
043   * @return a new focus context
044   * @throws IllegalArgumentException
045   *           if position is less than 1, size is less than 1, or position is
046   *           greater than size
047   */
048  @NonNull
049  public static FocusContext of(@NonNull IItem item, int position, int size) {
050    if (position < 1) {
051      throw new IllegalArgumentException("Position must be >= 1, got: " + position);
052    }
053    if (size < 1) {
054      throw new IllegalArgumentException("Size must be >= 1, got: " + size);
055    }
056    if (position > size) {
057      throw new IllegalArgumentException(
058          String.format("Position (%d) cannot be greater than size (%d)", position, size));
059    }
060    return new FocusContext(item, position, size);
061  }
062
063  /**
064   * Get the context item.
065   *
066   * @return the context item
067   */
068  @NonNull
069  public IItem getContextItem() {
070    return contextItem;
071  }
072
073  /**
074   * Get the context position.
075   * <p>
076   * This is the 1-based position of the context item within the sequence
077   * currently being processed, as returned by {@code fn:position()}.
078   *
079   * @return the context position (1-based)
080   */
081  public int getPosition() {
082    return position;
083  }
084
085  /**
086   * Get the context size.
087   * <p>
088   * This is the total number of items in the sequence currently being processed,
089   * as returned by {@code fn:last()}.
090   *
091   * @return the context size
092   */
093  public int getSize() {
094    return size;
095  }
096}