1   /*
2    * SPDX-FileCopyrightText: none
3    * SPDX-License-Identifier: CC0-1.0
4    */
5   
6   package dev.metaschema.core.metapath;
7   
8   import dev.metaschema.core.metapath.item.IItem;
9   import edu.umd.cs.findbugs.annotations.NonNull;
10  
11  /**
12   * Represents the focus context for Metapath evaluation, containing the context
13   * item, position, and size as defined in the
14   * <a href="https://www.w3.org/TR/xpath-31/#eval_context">XPath 3.1 evaluation
15   * context</a>.
16   * <p>
17   * The focus context is established when evaluating predicates and provides the
18   * information needed by the {@code fn:position()} and {@code fn:last()}
19   * functions.
20   */
21  public final class FocusContext {
22    @NonNull
23    private final IItem contextItem;
24    private final int position;
25    private final int size;
26  
27    private FocusContext(@NonNull IItem contextItem, int position, int size) {
28      this.contextItem = contextItem;
29      this.position = position;
30      this.size = size;
31    }
32  
33    /**
34     * Create a new focus context for the given item at the specified position
35     * within a sequence.
36     *
37     * @param item
38     *          the context item
39     * @param position
40     *          the 1-based position of the item within the sequence
41     * @param size
42     *          the total number of items in the sequence
43     * @return a new focus context
44     * @throws IllegalArgumentException
45     *           if position is less than 1, size is less than 1, or position is
46     *           greater than size
47     */
48    @NonNull
49    public static FocusContext of(@NonNull IItem item, int position, int size) {
50      if (position < 1) {
51        throw new IllegalArgumentException("Position must be >= 1, got: " + position);
52      }
53      if (size < 1) {
54        throw new IllegalArgumentException("Size must be >= 1, got: " + size);
55      }
56      if (position > size) {
57        throw new IllegalArgumentException(
58            String.format("Position (%d) cannot be greater than size (%d)", position, size));
59      }
60      return new FocusContext(item, position, size);
61    }
62  
63    /**
64     * Get the context item.
65     *
66     * @return the context item
67     */
68    @NonNull
69    public IItem getContextItem() {
70      return contextItem;
71    }
72  
73    /**
74     * Get the context position.
75     * <p>
76     * This is the 1-based position of the context item within the sequence
77     * currently being processed, as returned by {@code fn:position()}.
78     *
79     * @return the context position (1-based)
80     */
81    public int getPosition() {
82      return position;
83    }
84  
85    /**
86     * Get the context size.
87     * <p>
88     * This is the total number of items in the sequence currently being processed,
89     * as returned by {@code fn:last()}.
90     *
91     * @return the context size
92     */
93    public int getSize() {
94      return size;
95    }
96  }