1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 package gov.nist.secauto.metaschema.core.metapath.function.library;
28
29 import gov.nist.secauto.metaschema.core.metapath.DynamicContext;
30 import gov.nist.secauto.metaschema.core.metapath.DynamicMetapathException;
31 import gov.nist.secauto.metaschema.core.metapath.ISequence;
32 import gov.nist.secauto.metaschema.core.metapath.MetapathConstants;
33 import gov.nist.secauto.metaschema.core.metapath.MetapathException;
34 import gov.nist.secauto.metaschema.core.metapath.MetapathExpression;
35 import gov.nist.secauto.metaschema.core.metapath.function.FunctionUtils;
36 import gov.nist.secauto.metaschema.core.metapath.function.IArgument;
37 import gov.nist.secauto.metaschema.core.metapath.function.IFunction;
38 import gov.nist.secauto.metaschema.core.metapath.item.IItem;
39 import gov.nist.secauto.metaschema.core.metapath.item.atomic.IStringItem;
40 import gov.nist.secauto.metaschema.core.metapath.item.node.INodeItem;
41 import gov.nist.secauto.metaschema.core.util.ObjectUtils;
42
43 import java.util.List;
44 import java.util.stream.Stream;
45
46 import edu.umd.cs.findbugs.annotations.NonNull;
47
48 public final class MpRecurseDepth {
49
50
51 @NonNull
52 static final IFunction SIGNATURE_ONE_ARG = IFunction.builder()
53 .name("recurse-depth")
54 .namespace(MetapathConstants.NS_METAPATH)
55 .deterministic()
56 .contextDependent()
57 .focusDependent()
58 .argument(IArgument.newBuilder()
59 .name("recursePath")
60 .type(IStringItem.class)
61 .one()
62 .build())
63 .returnType(INodeItem.class)
64 .returnZeroOrMore()
65 .functionHandler(MpRecurseDepth::executeOneArg)
66 .build();
67
68 @NonNull
69 static final IFunction SIGNATURE_TWO_ARG = IFunction.builder()
70 .name("recurse-depth")
71 .namespace(MetapathConstants.NS_METAPATH)
72 .deterministic()
73 .contextDependent()
74 .focusIndependent()
75 .argument(IArgument.newBuilder()
76 .name("context")
77 .type(INodeItem.class)
78 .zeroOrMore()
79 .build())
80 .argument(IArgument.newBuilder()
81 .name("recursePath")
82 .type(IStringItem.class)
83 .one()
84 .build())
85 .returnType(INodeItem.class)
86 .returnZeroOrMore()
87 .functionHandler(MpRecurseDepth::executeTwoArg)
88 .build();
89
90 private MpRecurseDepth() {
91
92 }
93
94 @SuppressWarnings("unused")
95 @NonNull
96 private static ISequence<INodeItem> executeOneArg(
97 @NonNull IFunction function,
98 @NonNull List<ISequence<?>> arguments,
99 @NonNull DynamicContext dynamicContext,
100 IItem focus) {
101
102 ISequence<INodeItem> initalContext = ISequence.of(FunctionUtils.requireType(INodeItem.class, focus));
103
104 ISequence<? extends IStringItem> arg = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(0)));
105 IStringItem recursionPath = FunctionUtils.requireFirstItem(arg, true);
106
107 return recurseDepth(initalContext, recursionPath, dynamicContext);
108 }
109
110 @SuppressWarnings("unused")
111 @NonNull
112 private static ISequence<INodeItem> executeTwoArg(
113 @NonNull IFunction function,
114 @NonNull List<ISequence<?>> arguments,
115 @NonNull DynamicContext dynamicContext,
116 IItem focus) {
117
118 ISequence<INodeItem> initalContext = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(0)));
119
120 ISequence<? extends IStringItem> arg = FunctionUtils.asType(ObjectUtils.requireNonNull(arguments.get(1)));
121 IStringItem recursionPath = FunctionUtils.requireFirstItem(arg, true);
122
123 return recurseDepth(initalContext, recursionPath, dynamicContext);
124 }
125
126 @NonNull
127 private static ISequence<INodeItem> recurseDepth(
128 @NonNull ISequence<INodeItem> initialContext,
129 @NonNull IStringItem recursionPath,
130 @NonNull DynamicContext dynamicContext) {
131
132 MetapathExpression recursionMetapath;
133 try {
134 recursionMetapath = MetapathExpression.compile(recursionPath.asString());
135 } catch (MetapathException ex) {
136 throw new DynamicMetapathException(DynamicMetapathException.INVALID_PATH_GRAMMAR, ex.getMessage(), ex);
137 }
138
139 return recurseDepth(initialContext, recursionMetapath, dynamicContext);
140 }
141
142 @NonNull
143 public static ISequence<INodeItem> recurseDepth(
144 @NonNull ISequence<INodeItem> initialContext,
145 @NonNull MetapathExpression recursionMetapath,
146 @NonNull DynamicContext dynamicContext) {
147
148 return ISequence.of(ObjectUtils.notNull(initialContext.asStream()
149 .flatMap(item -> {
150 @NonNull ISequence<INodeItem> metapathResult
151 = recursionMetapath.evaluate(item, dynamicContext);
152 ISequence<INodeItem> result = recurseDepth(metapathResult, recursionMetapath, dynamicContext);
153 return ObjectUtils.notNull(Stream.concat(result.asStream(), Stream.of(item)));
154 })));
155 }
156 }