AbstractPathExpression.java

/*
 * SPDX-FileCopyrightText: none
 * SPDX-License-Identifier: CC0-1.0
 */

package gov.nist.secauto.metaschema.core.metapath.cst.path;

import gov.nist.secauto.metaschema.core.metapath.DynamicContext;
import gov.nist.secauto.metaschema.core.metapath.ISequence;
import gov.nist.secauto.metaschema.core.metapath.cst.AbstractExpression;
import gov.nist.secauto.metaschema.core.metapath.cst.IExpression;
import gov.nist.secauto.metaschema.core.metapath.cst.IPathExpression;
import gov.nist.secauto.metaschema.core.metapath.item.IItem;
import gov.nist.secauto.metaschema.core.metapath.item.ItemUtils;
import gov.nist.secauto.metaschema.core.metapath.item.node.ICycledAssemblyNodeItem;
import gov.nist.secauto.metaschema.core.metapath.item.node.INodeItem;
import gov.nist.secauto.metaschema.core.util.ObjectUtils;

import java.util.stream.Stream;

import edu.umd.cs.findbugs.annotations.NonNull;

public abstract class AbstractPathExpression<RESULT_TYPE extends IItem>
    extends AbstractExpression
    implements IPathExpression<RESULT_TYPE> {
  @Override
  public abstract Class<RESULT_TYPE> getBaseResultType();

  @Override
  public Class<? extends RESULT_TYPE> getStaticResultType() {
    return getBaseResultType();
  }

  /**
   * Evaluate the {@code nodeContext} and its ancestors against the provided
   * {@code expression}, keeping any matching nodes.
   *
   * @param expression
   *          the expression to evaluate
   * @param dynamicContext
   *          the evaluation context
   * @param outerFocus
   *          the current context node
   * @return the matching nodes
   */
  @NonNull
  protected Stream<? extends INodeItem> searchExpression(
      @NonNull IExpression expression,
      @NonNull DynamicContext dynamicContext,
      @NonNull ISequence<?> outerFocus) {
    // ensure the sequence is backed by a list
    outerFocus.getValue();

    // check the current focus
    @SuppressWarnings("unchecked")
    Stream<? extends INodeItem> nodeMatches
        = (Stream<? extends INodeItem>) expression.accept(dynamicContext, outerFocus).stream();

    Stream<? extends INodeItem> childMatches = outerFocus.stream()
        .map(ItemUtils::checkItemIsNodeItemForStep)
        .flatMap(focusedNode -> {

          Stream<? extends INodeItem> matches;
          if (focusedNode instanceof ICycledAssemblyNodeItem) {
            // prevent stack overflow
            matches = Stream.empty();
          } else {
            assert focusedNode != null; // may be null?
            // create a stream of flags and model elements to check
            Stream<? extends INodeItem> flags = focusedNode.flags();
            Stream<? extends INodeItem> modelItems = focusedNode.modelItems();

            matches = searchExpression(
                expression,
                dynamicContext,
                ISequence.of(ObjectUtils.notNull(Stream.concat(flags, modelItems))));
          }
          return matches;
        });

    return ObjectUtils.notNull(Stream.concat(nodeMatches, childMatches).distinct());
  }

  /**
   * Evaluate the {@code nodeContext} and its ancestors against the provided
   * {@code expression}, keeping any matching nodes.
   *
   * @param expression
   *          the expression to evaluate
   * @param dynamicContext
   *          the evaluation context
   * @param focus
   *          the current context node
   * @return the matching nodes
   */
  @NonNull
  protected Stream<? extends INodeItem> search(
      @NonNull IExpression expression,
      @NonNull DynamicContext dynamicContext,
      @NonNull ISequence<?> focus) {
    // Stream<? extends INodeItem> retval;
    // if (expr instanceof Flag) {
    // // check instances as a flag
    // retval = searchFlags((Flag) expr, context);
    // } else if (expr instanceof ModelInstance) {
    // // check instances as a ModelInstance
    // retval = searchModelInstances((ModelInstance) expr, context);
    // } else {
    // recurse tree
    // searchExpression(expr, context);
    // retval = searchExpression(expr, dynamicContext, context);
    // }
    // return retval;
    return searchExpression(expression, dynamicContext, focus);

  }

  // /**
  // * Recursively searches the node graph for {@link IRequiredValueModelNodeItem}
  // instances that
  // match
  // * the provided {@link ModelInstance} expression. The resulting nodes are
  // returned in document
  // * order.
  // *
  // * @param modelInstance
  // * the search expression
  // * @param context
  // * the current node context
  // * @return a stream of matching model node items
  // */
  // @NonNull
  // protected Stream<? extends IModelNodeItem> searchModelInstances(@NonNull
  // ModelInstance
  // modelInstance,
  // @NonNull IFocus context) {
  //
  // // check if the current node context matches the expression
  // Stream<? extends IModelNodeItem> nodeMatches =
  // matchModelInstance(modelInstance, context);
  //
  // // next iterate over the child model instances, if the context item is an
  // assembly
  // @SuppressWarnings("null")
  // Stream<? extends IModelNodeItem> childMatches
  // = context.modelItems().flatMap(modelItem -> {
  // // apply the search criteria to these node items
  // return searchModelInstances(modelInstance, modelItem);
  // });
  //
  // // combine the results
  // @SuppressWarnings("null")
  // @NonNull
  // Stream<? extends IModelNodeItem> retval = Stream.concat(nodeMatches,
  // childMatches);
  // return retval;
  // }

  // /**
  // * Recursively searches the node graph for {@link IRequiredValueFlagNodeItem}
  // instances that match
  // the provided
  // * {@link Flag} expression. The resulting nodes are returned in document
  // order.
  // *
  // * @param expr
  // * the search expression
  // * @param context
  // * the current node context
  // * @return a stream of matching flag node items
  // */
  // @NonNull
  // private Stream<? extends IRequiredValueFlagNodeItem> searchFlags(Flag expr,
  // IFocus context)
  // {
  //
  // // check if any flags on the the current node context matches the expression
  // Stream<? extends IRequiredValueFlagNodeItem> retval =
  // context.getMatchingChildFlags(expr);
  //
  // // next iterate over the child model instances, if the context item is an
  // assembly
  // INodeItem contextItem = context.getContextNodeItem();
  //
  // if (contextItem instanceof IRequiredValueAssemblyNodeItem) {
  // IRequiredValueAssemblyNodeItem assemblyContextItem =
  // (IRequiredValueAssemblyNodeItem)
  // contextItem;
  //
  // Stream<? extends IRequiredValueFlagNodeItem> childFlagInstances =
  // assemblyContextItem.modelItems().flatMap(modelItem -> {
  // // apply the search criteria to these node items
  // return searchFlags(expr, modelItem);
  // });
  // retval = Stream.concat(retval, childFlagInstances);
  // }
  // return retval;
  // return Stream.empty();
  // }
}