1
2 package dev.metaschema.core.metapath.item.node;
3
4 import java.net.URI;
5 import java.util.Collection;
6 import java.util.List;
7 import java.util.stream.Stream;
8
9 import dev.metaschema.core.metapath.StaticContext;
10 import dev.metaschema.core.metapath.format.IPathFormatter;
11 import dev.metaschema.core.metapath.format.IPathSegment;
12 import dev.metaschema.core.metapath.item.IItem;
13 import dev.metaschema.core.metapath.item.IItemVisitor;
14 import dev.metaschema.core.metapath.type.IItemType;
15 import dev.metaschema.core.model.IResourceLocation;
16 import dev.metaschema.core.qname.IEnhancedQName;
17 import dev.metaschema.core.util.ObjectUtils;
18 import edu.umd.cs.findbugs.annotations.NonNull;
19 import edu.umd.cs.findbugs.annotations.Nullable;
20
21 /**
22 * Represents a Metapath model node.
23 */
24 public interface INodeItem extends IItem, IPathSegment, INodeItemVisitable {
25 /**
26 * The type of node.
27 */
28 enum NodeType {
29 /** A Metaschema module node. */
30 MODULE,
31 /** A document node representing the root of a data instance. */
32 DOCUMENT,
33 /** An assembly node containing fields and other assemblies. */
34 ASSEMBLY,
35 /** A field node containing flags and a value. */
36 FIELD,
37 /** A flag node containing a simple value. */
38 FLAG;
39 }
40
41 /**
42 * Get the node type for the node item.
43 *
44 * @return the node type
45 */
46 @NonNull
47 NodeType getNodeType();
48
49 /**
50 * Get the static type information of the node item.
51 *
52 * @return the item type
53 */
54 @NonNull
55 static IItemType type() {
56 return IItemType.node();
57 }
58
59 /**
60 * Retrieve the relative position of this node relative to sibling nodes.
61 * <p>
62 * A singleton item in a sequence will have a position value of {@code 1}.
63 * <p>
64 * The value {@code 1} is used as the starting value to align with the XPath
65 * specification.
66 *
67 * @return a positive integer value designating this instance's position within
68 * a collection
69 */
70 default int getPosition() {
71 // only model node items have positions other than 1
72 return 1;
73 }
74
75 /**
76 * Generate a path for this node in the directed node graph, using the provided
77 * path formatter.
78 */
79 @Override
80 String format(IPathFormatter formatter);
81
82 /**
83 * Gets the value of the provided node item.
84 * <p>
85 * If the provided node item is a document, this method get the first child node
86 * item's value, since a document doesn't have a value.
87 *
88 * @param <CLASS>
89 * the type of the bound object to return
90 * @param item
91 * the node item to get the value of
92 * @return a bound object
93 * @throws NullPointerException
94 * if the node item has no associated value
95 */
96 @SuppressWarnings("unchecked")
97 @NonNull
98 static <CLASS> CLASS toValue(@NonNull INodeItem item) {
99 INodeItem valuedItem;
100 if (item instanceof IDocumentNodeItem) {
101 // get first child item, since the document has no value
102 valuedItem = item.modelItems().findFirst().get();
103 } else {
104 valuedItem = item;
105 }
106 return ObjectUtils.requireNonNull((CLASS) valuedItem.getValue());
107 }
108
109 /**
110 * Retrieve the parent node item if it exists.
111 *
112 * @return the parent node item, or {@code null} if this node item has no known
113 * parent
114 */
115 INodeItem getParentNodeItem();
116
117 /**
118 * Retrieve the parent content node item if it exists. A content node is a
119 * non-document node.
120 *
121 * @return the parent content node item, or {@code null} if this node item has
122 * no known parent content node item
123 */
124 IModelNodeItem<?, ?> getParentContentNodeItem();
125
126 /**
127 * Get the kind of node item this is.
128 *
129 * @return the node item's kind
130 */
131 @NonNull
132 NodeItemKind getNodeItemKind();
133
134 /**
135 * Retrieve the base URI of this node.
136 * <p>
137 * The base URI of a node will be in order of preference:
138 * <ol>
139 * <li>the base URI defined on the node
140 * <li>the base URI defined on the nearest ancestor node
141 * <li>the base URI defined on the document node
142 * <li>{@code null} if the document node is unknown
143 * </ol>
144 *
145 * @return the base URI or {@code null} if it is unknown
146 */
147 URI getBaseUri();
148
149 /**
150 * Get the path for this node item as a Metapath.
151 *
152 * @return the Metapath
153 */
154 @NonNull
155 default String getMetapath() {
156 return toPath(IPathFormatter.METAPATH_PATH_FORMATER);
157 }
158
159 @Override
160 default Stream<? extends INodeItem> getPathStream() {
161 INodeItem parent = getParentNodeItem();
162 return ObjectUtils.notNull(
163 parent == null ? Stream.of(this) : Stream.concat(getParentNodeItem().getPathStream(), Stream.of(this)));
164 }
165
166 /**
167 * Get a stream of all ancestors of this node item. The stream is ordered from
168 * closest to farthest ancestor.
169 *
170 * @return a stream of ancestor node items
171 */
172 @NonNull
173 default Stream<? extends INodeItem> ancestor() {
174 return ancestorsOf(this);
175 }
176
177 /**
178 * Get a stream of this and all ancestors of this node item. The stream is
179 * ordered from self, then closest to farthest ancestor.
180 *
181 * @return a stream of this node followed by all ancestor node items
182 */
183 @NonNull
184 default Stream<? extends INodeItem> ancestorOrSelf() {
185 return ObjectUtils.notNull(Stream.concat(ancestor(), Stream.of(this)));
186 }
187
188 /**
189 * Get a stream of the ancestors of the provided {@code item}. The stream is
190 * ordered from the farthest ancestor to the closest.
191 *
192 * @param item
193 * the target item to get ancestors for
194 *
195 * @return a stream of all ancestor node items
196 */
197 @NonNull
198 static Stream<? extends INodeItem> ancestorsOf(@NonNull INodeItem item) {
199 INodeItem parent = item.getParentNodeItem();
200 return ObjectUtils.notNull(parent == null ? Stream.empty() : Stream.concat(ancestorsOf(parent), Stream.of(parent)));
201 }
202
203 /**
204 * Get a stream of all descendant model items of this node item. The stream is
205 * ordered from closest to farthest descendants in a depth-first order.
206 *
207 * @return a stream of descendant node items
208 */
209 @NonNull
210 default Stream<? extends IModelNodeItem<?, ?>> descendant() {
211 return decendantsOf(this);
212 }
213
214 /**
215 * Get a stream of all descendant model items of the provided {@code item}. The
216 * stream is ordered from closest to farthest descendants in a depth-first
217 * order.
218 *
219 * @param item
220 * the target item to get descendants for
221 *
222 * @return a stream of descendant node items
223 */
224 @NonNull
225 static Stream<? extends IModelNodeItem<?, ?>> decendantsOf(@NonNull INodeItem item) {
226 Stream<? extends IModelNodeItem<?, ?>> children = item.modelItems();
227
228 return ObjectUtils.notNull(children.flatMap(child -> {
229 assert child != null;
230 return Stream.concat(Stream.of(child), decendantsOf(child));
231 }));
232 }
233
234 /**
235 * Get a stream of this node, followed by all descendant model items of this
236 * node item. The stream is ordered from closest to farthest descendants in a
237 * depth-first order.
238 *
239 * @return a stream of this node and descendant node items
240 */
241 @NonNull
242 default Stream<? extends INodeItem> descendantOrSelf() {
243 return ObjectUtils.notNull(Stream.concat(Stream.of(this), descendant()));
244 }
245
246 /**
247 * Get the children of this node's parent that occur after this node in a
248 * depth-first order.
249 *
250 * @return a stream of nodes
251 */
252 @NonNull
253 default Stream<? extends IModelNodeItem<?, ?>> followingSibling() {
254 return ObjectUtils.notNull(Stream.empty());
255 }
256
257 /**
258 * Get the children of this node's parent, and their descendants, that occur
259 * before this node in a depth-first order.
260 *
261 * @return a stream of nodes
262 */
263 @NonNull
264 default Stream<? extends IModelNodeItem<?, ?>> precedingSibling() {
265 return ObjectUtils.notNull(Stream.empty());
266 }
267
268 /**
269 * Get the children of this node's parent, and their descendants, that occur
270 * before this node in a depth-first order.
271 *
272 * @return a stream of nodes
273 */
274 default Stream<? extends IModelNodeItem<?, ?>> following() {
275 return ObjectUtils.notNull(Stream.empty());
276 }
277
278 /**
279 * Get the children of this node's parent, and their descendants, that occur
280 * after this node in a depth-first order.
281 *
282 * @return a stream of nodes
283 */
284 default Stream<? extends IModelNodeItem<?, ?>> preceding() {
285 return ObjectUtils.notNull(Stream.empty());
286 }
287
288 /**
289 * Get the flags and value data associated this node. The resulting collection
290 * is expected to be ordered, with the results in document order.
291 * <p>
292 * The resulting collection may be modified, but such modification is not thread
293 * safe
294 *
295 * @return a collection of flags
296 */
297 @NonNull
298 Collection<? extends IFlagNodeItem> getFlags();
299
300 /**
301 * Lookup a flag and value data on this node by it's effective qualified name.
302 *
303 * @param name
304 * the effective qualified name of the flag
305 * @return the flag with the matching effective name or {@code null} if no match
306 * was found
307 */
308 @Nullable
309 IFlagNodeItem getFlagByName(@NonNull IEnhancedQName name);
310
311 /**
312 * Get the flags and value data associated with this node as a stream.
313 *
314 * @return the stream of flags or an empty stream if none exist
315 */
316 @SuppressWarnings("null")
317 @NonNull
318 default Stream<? extends IFlagNodeItem> flags() {
319 return getFlags().stream();
320 }
321
322 /**
323 * Get the model items (i.e., fields, assemblies) and value data associated this
324 * node. A given model instance can be multi-valued, so the value of each
325 * instance will be a list. The resulting collection is expected to be ordered,
326 * with the results in document order.
327 * <p>
328 * The resulting collection may be modified, but such modification is not thread
329 * safe
330 *
331 * @return a collection of list(s), with each list containing the items for a
332 * given model instance
333 */
334 @NonNull
335 Collection<? extends List<? extends IModelNodeItem<?, ?>>> getModelItems();
336
337 /**
338 * Get the collection of model items associated with the instance having the
339 * provided {@code name}.
340 * <p>
341 * The resulting collection may be modified, but such modification is not thread
342 * safe
343 *
344 * @param name
345 * the instance name to get model items for
346 * @return the sequence of items associated with the named model instance, or an
347 * empty list if an instance with that name is not present
348 */
349 @NonNull
350 List<? extends IModelNodeItem<?, ?>> getModelItemsByName(@NonNull IEnhancedQName name);
351
352 /**
353 * Get the model items (i.e., fields, assemblies) and value data associated this
354 * node as a stream.
355 *
356 * @return the stream of model items or an empty stream if none exist
357 */
358 @SuppressWarnings("null")
359 @NonNull
360 default Stream<? extends IModelNodeItem<?, ?>> modelItems() {
361 return getModelItems().stream().flatMap(Collection::stream);
362 }
363
364 /**
365 * Get the resource location information for the node, if known.
366 *
367 * @return the resource location information, or {@code null} if not known
368 */
369 @Nullable
370 IResourceLocation getLocation();
371
372 /**
373 * Get the string value of the node.
374 *
375 * @return the string value of the node or an empty string if it has no value.
376 */
377 @NonNull
378 String stringValue();
379
380 /**
381 * Get the static context to use to query this node item.
382 *
383 * @return the static context
384 */
385 @NonNull
386 StaticContext getStaticContext();
387
388 @Override
389 default void accept(IItemVisitor visitor) {
390 visitor.visit(this);
391 }
392 }