1   /*
2    * SPDX-FileCopyrightText: none
3    * SPDX-License-Identifier: CC0-1.0
4    */
5   
6   package dev.metaschema.core.metapath.cst.items;
7   
8   import java.util.List;
9   
10  import dev.metaschema.core.metapath.DynamicContext;
11  import dev.metaschema.core.metapath.IExpression;
12  import dev.metaschema.core.metapath.cst.IExpressionVisitor;
13  import dev.metaschema.core.metapath.item.ICollectionValue;
14  import dev.metaschema.core.metapath.item.ISequence;
15  import dev.metaschema.core.metapath.item.function.IArrayItem;
16  import dev.metaschema.core.metapath.item.function.IKeySpecifier;
17  import dev.metaschema.core.metapath.item.function.IMapItem;
18  import dev.metaschema.core.util.ObjectUtils;
19  import edu.umd.cs.findbugs.annotations.NonNull;
20  
21  /**
22   * An implementation of
23   * <a href="https://www.w3.org/TR/xpath-31/#id-postfix-lookup">Postfix Lookup
24   * Operators</a> supporting access to items in Metapath maps and arrays.
25   * <p>
26   * Provides support for various types of key- and index-based lookups related to
27   * {@link IMapItem} and {@link IArrayItem} objects.
28   */
29  public class PostfixLookup
30      extends AbstractLookup {
31  
32    @NonNull
33    private final IExpression base;
34  
35    /**
36     * Construct a new postfix lookup expression that uses the provided key
37     * specifier.
38     *
39     * @param text
40     *          the parsed text of the expression
41     * @param base
42     *          the base expression used to get the target of the lookup
43     * @param keySpecifier
44     *          the key specifier used to determine matching entries
45     */
46    public PostfixLookup(@NonNull String text, @NonNull IExpression base, @NonNull IKeySpecifier keySpecifier) {
47      super(text, keySpecifier);
48      this.base = base;
49    }
50  
51    /**
52     * Get the base sub-expression.
53     *
54     * @return the sub-expression
55     */
56    @NonNull
57    public IExpression getBase() {
58      return base;
59    }
60  
61    @SuppressWarnings("null")
62    @Override
63    public List<? extends IExpression> getChildren() {
64      return List.of(getBase());
65    }
66  
67    @Override
68    protected ISequence<?> evaluate(DynamicContext dynamicContext, ISequence<?> focus) {
69      ISequence<?> base = getBase().accept(dynamicContext, focus);
70  
71      IKeySpecifier specifier = getKeySpecifier();
72  
73      return ISequence.of(ObjectUtils.notNull(base.stream()
74          .flatMap(item -> {
75            assert item != null;
76            return specifier.lookup(item, dynamicContext, focus);
77          })
78          .flatMap(ICollectionValue::normalizeAsItems)));
79    }
80  
81    @Override
82    public <RESULT, CONTEXT> RESULT accept(@NonNull IExpressionVisitor<RESULT, CONTEXT> visitor, CONTEXT context) {
83      return visitor.visitPostfixLookup(this, context);
84    }
85  }