1
2
3
4
5
6 package dev.metaschema.oscal.lib.metapath.function.library;
7
8 import java.net.URI;
9 import java.util.List;
10
11 import dev.metaschema.core.metapath.DynamicContext;
12 import dev.metaschema.core.metapath.MetapathConstants;
13 import dev.metaschema.core.metapath.function.FunctionUtils;
14 import dev.metaschema.core.metapath.function.IArgument;
15 import dev.metaschema.core.metapath.function.IFunction;
16 import dev.metaschema.core.metapath.function.InvalidTypeFunctionException;
17 import dev.metaschema.core.metapath.item.IItem;
18 import dev.metaschema.core.metapath.item.ISequence;
19 import dev.metaschema.core.metapath.item.atomic.IAnyUriItem;
20 import dev.metaschema.core.metapath.item.atomic.IBooleanItem;
21 import dev.metaschema.core.metapath.item.atomic.IStringItem;
22 import dev.metaschema.core.metapath.item.node.IAssemblyNodeItem;
23 import dev.metaschema.core.metapath.item.node.IFieldNodeItem;
24 import dev.metaschema.core.metapath.item.node.IFlagNodeItem;
25 import dev.metaschema.core.metapath.item.node.IModelNodeItem;
26 import dev.metaschema.core.metapath.type.InvalidTypeMetapathException;
27 import dev.metaschema.core.model.IFlagInstance;
28 import dev.metaschema.core.model.IModelDefinition;
29 import dev.metaschema.core.qname.IEnhancedQName;
30 import dev.metaschema.core.util.ObjectUtils;
31 import dev.metaschema.oscal.lib.OscalModelConstants;
32 import dev.metaschema.oscal.lib.model.metadata.IProperty;
33 import edu.umd.cs.findbugs.annotations.NonNull;
34
35 public final class HasOscalNamespace {
36 @NonNull
37 private static final IEnhancedQName NS_FLAG_QNAME = IEnhancedQName.of("ns");
38 @NonNull
39 static final IFunction SIGNATURE_ONE_ARG = IFunction.builder()
40 .name("has-oscal-namespace")
41 .namespace(OscalModelConstants.NS_OSCAL)
42 .argument(IArgument.builder()
43 .name("namespace")
44 .type(IStringItem.type())
45 .oneOrMore()
46 .build())
47 .allowUnboundedArity(true)
48 .returnType(IBooleanItem.type())
49 .focusDependent()
50 .contextIndependent()
51 .deterministic()
52 .returnOne()
53 .functionHandler(HasOscalNamespace::executeOneArg)
54 .build();
55
56 @NonNull
57 static final IFunction SIGNATURE_TWO_ARGS = IFunction.builder()
58 .name("has-oscal-namespace")
59 .namespace(OscalModelConstants.NS_OSCAL)
60 .argument(IArgument.builder()
61 .name("propOrPart")
62 .type(IAssemblyNodeItem.type())
63 .one()
64 .build())
65 .argument(IArgument.builder()
66 .name("namespace")
67 .type(IStringItem.type())
68 .oneOrMore()
69 .build())
70 .allowUnboundedArity(true)
71 .focusIndependent()
72 .contextIndependent()
73 .deterministic()
74 .returnType(IBooleanItem.type())
75 .returnOne()
76 .functionHandler(HasOscalNamespace::executeTwoArg)
77 .build();
78
79 @NonNull
80 static final IFunction SIGNATURE_ONE_ARG_METAPATH = IFunction.builder()
81 .name("has-oscal-namespace")
82 .namespace(MetapathConstants.NS_METAPATH_FUNCTIONS)
83 .argument(IArgument.builder()
84 .name("namespace")
85 .type(IStringItem.type())
86 .oneOrMore()
87 .build())
88 .allowUnboundedArity(true)
89 .returnType(IBooleanItem.type())
90 .focusDependent()
91 .contextIndependent()
92 .deterministic()
93 .returnOne()
94 .functionHandler(HasOscalNamespace::executeOneArg)
95 .build();
96
97 @NonNull
98 static final IFunction SIGNATURE_TWO_ARGS_METAPATH = IFunction.builder()
99 .name("has-oscal-namespace")
100 .namespace(MetapathConstants.NS_METAPATH_FUNCTIONS)
101 .argument(IArgument.builder()
102 .name("propOrPart")
103 .type(IAssemblyNodeItem.type())
104 .one()
105 .build())
106 .argument(IArgument.builder()
107 .name("namespace")
108 .type(IStringItem.type())
109 .oneOrMore()
110 .build())
111 .allowUnboundedArity(true)
112 .focusIndependent()
113 .contextIndependent()
114 .deterministic()
115 .returnType(IBooleanItem.type())
116 .returnOne()
117 .functionHandler(HasOscalNamespace::executeTwoArg)
118 .build();
119
120 private HasOscalNamespace() {
121
122 }
123
124 @SuppressWarnings({ "unused",
125 "PMD.OnlyOneReturn"
126 })
127 @NonNull
128 public static ISequence<?> executeOneArg(
129 @NonNull IFunction function,
130 @NonNull List<ISequence<?>> arguments,
131 @NonNull DynamicContext dynamicContext,
132 IItem focus) {
133 assert arguments.size() == 1;
134 ISequence<? extends IStringItem> namespaceArgs = FunctionUtils.asType(
135 ObjectUtils.notNull(arguments.get(0)));
136
137 if (namespaceArgs.isEmpty()) {
138 return ISequence.empty();
139 }
140
141
142 IModelNodeItem<?, ?> node;
143 if (focus instanceof IAssemblyNodeItem) {
144 node = (IAssemblyNodeItem) focus;
145 } else if (focus instanceof IFieldNodeItem) {
146 node = (IFieldNodeItem) focus;
147 } else {
148 throw new InvalidTypeMetapathException(
149 focus,
150 String.format("Expected an assembly or field node, but got '%s'",
151 focus == null ? "null" : focus.getClass().getName()));
152 }
153 return ISequence.of(hasNamespace(node, namespaceArgs));
154 }
155
156 @SuppressWarnings({ "unused",
157 "PMD.OnlyOneReturn"
158 })
159 @NonNull
160 public static ISequence<?> executeTwoArg(
161 @NonNull IFunction function,
162 @NonNull List<ISequence<?>> arguments,
163 @NonNull DynamicContext dynamicContext,
164 IItem focus) {
165 assert arguments.size() == 2;
166
167 ISequence<? extends IStringItem> namespaceArgs = FunctionUtils.asType(
168 ObjectUtils.notNull(arguments.get(1)));
169 if (namespaceArgs.isEmpty()) {
170 return ISequence.empty();
171 }
172
173 ISequence<? extends IAssemblyNodeItem> nodeSequence = FunctionUtils.asType(
174 ObjectUtils.notNull(arguments.get(0)));
175
176
177 IAssemblyNodeItem node = FunctionUtils.asType(ObjectUtils.requireNonNull(nodeSequence.getFirstItem(true)));
178 return ISequence.of(hasNamespace(node, namespaceArgs));
179 }
180
181 @SuppressWarnings("PMD.LinguisticNaming")
182 @NonNull
183 public static IBooleanItem hasNamespace(
184 @NonNull IModelNodeItem<?, ?> propOrPart,
185 @NonNull ISequence<? extends IStringItem> namespaces) {
186 Object propOrPartObject = propOrPart.getValue();
187 if (propOrPartObject == null) {
188 throw new InvalidTypeFunctionException(InvalidTypeFunctionException.NODE_HAS_NO_TYPED_VALUE, propOrPart);
189 }
190
191 URI nodeNamespace = null;
192
193 IFlagNodeItem ns = propOrPart.getFlagByName(NS_FLAG_QNAME);
194 if (ns == null) {
195
196 IModelDefinition definition = propOrPart.getDefinition();
197 IFlagInstance flag = definition.getFlagInstanceByName(NS_FLAG_QNAME.getIndexPosition());
198 if (flag == null) {
199
200 return IBooleanItem.FALSE;
201 }
202
203 Object defaultValue = flag.getDefinition().getDefaultValue();
204 if (defaultValue != null) {
205 nodeNamespace = IAnyUriItem.valueOf(ObjectUtils.notNull(defaultValue.toString())).asUri();
206 }
207 } else {
208 nodeNamespace = IAnyUriItem.cast(ObjectUtils.notNull(ns.toAtomicItem())).asUri();
209 }
210
211 String nodeNamespaceString = IProperty.normalizeNamespace(nodeNamespace).toString();
212 return IBooleanItem.valueOf(namespaces.stream()
213 .map(node -> nodeNamespaceString.equals(node.asString()))
214 .anyMatch(bool -> bool));
215 }
216 }