001/*
002 * Portions of this software was developed by employees of the National Institute
003 * of Standards and Technology (NIST), an agency of the Federal Government and is
004 * being made available as a public service. Pursuant to title 17 United States
005 * Code Section 105, works of NIST employees are not subject to copyright
006 * protection in the United States. This software may be subject to foreign
007 * copyright. Permission in the United States and in foreign countries, to the
008 * extent that NIST may hold copyright, to use, copy, modify, create derivative
009 * works, and distribute this software and its documentation without fee is hereby
010 * granted on a non-exclusive basis, provided that this notice and disclaimer
011 * of warranty appears in all copies.
012 *
013 * THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER
014 * EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY
015 * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF
016 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM
017 * INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE
018 * SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE.  IN NO EVENT
019 * SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT,
020 * INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM,
021 * OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY,
022 * CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR
023 * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT
024 * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER.
025 */
026
027package gov.nist.secauto.metaschema.core.metapath.item.node;
028
029import gov.nist.secauto.metaschema.core.metapath.format.IPathFormatter;
030import gov.nist.secauto.metaschema.core.metapath.format.IPathSegment;
031import gov.nist.secauto.metaschema.core.metapath.item.IItem;
032import gov.nist.secauto.metaschema.core.util.ObjectUtils;
033
034import java.net.URI;
035import java.util.Collection;
036import java.util.List;
037import java.util.stream.Stream;
038
039import edu.umd.cs.findbugs.annotations.NonNull;
040import edu.umd.cs.findbugs.annotations.Nullable;
041
042/**
043 * Represents a queryable Metapath model node.
044 */
045public interface INodeItem extends IItem, IPathSegment, INodeItemVisitable {
046
047  /**
048   * Generate a path for this node in the directed node graph, using the provided
049   * path formatter.
050   */
051  @Override
052  String format(IPathFormatter formatter);
053
054  /**
055   * Gets the value of the provided node item.
056   * <p>
057   * If the provided node item is a document, this method get the first child node
058   * item's value, since a document doesn't have a value.
059   *
060   * @param <CLASS>
061   *          the type of the bound object to return
062   * @param item
063   *          the node item to get the value of
064   * @return a bound object
065   * @throws NullPointerException
066   *           if the node item has no associated value
067   */
068  @SuppressWarnings("unchecked")
069  @NonNull
070  static <CLASS> CLASS toValue(@NonNull INodeItem item) {
071    INodeItem valuedItem;
072    if (item instanceof IDocumentNodeItem) {
073      // get first child item, since the document has no value
074      valuedItem = item.modelItems().findFirst().get();
075    } else {
076      valuedItem = item;
077    }
078    return ObjectUtils.requireNonNull((CLASS) valuedItem.getValue());
079  }
080
081  /**
082   * Retrieve the parent node item if it exists.
083   *
084   * @return the parent node item, or {@code null} if this node item has no known
085   *         parent
086   */
087  INodeItem getParentNodeItem();
088
089  /**
090   * Retrieve the parent content node item if it exists. A content node is a
091   * non-document node.
092   *
093   * @return the parent content node item, or {@code null} if this node item has
094   *         no known parent content node item
095   */
096  IModelNodeItem<?, ?> getParentContentNodeItem();
097
098  /**
099   * Get the type of node item this is.
100   *
101   * @return the node item's type
102   */
103  @NonNull
104  NodeItemType getNodeItemType();
105
106  /**
107   * Retrieve the base URI of this node.
108   * <p>
109   * The base URI of a node will be in order of preference:
110   * <ol>
111   * <li>the base URI defined on the node</li>
112   * <li>the base URI defined on the nearest ancestor node</li>
113   * <li>the base URI defined on the document node</li>
114   * <li>{@code null} if the document node is unknown</li>
115   * </ol>
116   *
117   * @return the base URI or {@code null} if it is unknown
118   */
119  URI getBaseUri();
120
121  /**
122   * Get the path for this node item as a Metapath.
123   *
124   * @return the Metapath
125   */
126  @NonNull
127  default String getMetapath() {
128    return toPath(IPathFormatter.METAPATH_PATH_FORMATER);
129  }
130
131  @Override
132  default Stream<? extends INodeItem> getPathStream() {
133    INodeItem parent = getParentNodeItem();
134    return ObjectUtils.notNull(
135        parent == null ? Stream.of(this) : Stream.concat(getParentNodeItem().getPathStream(), Stream.of(this)));
136  }
137
138  /**
139   * Get a stream of all ancestors of this node item. The stream is ordered from
140   * closest to farthest ancestor.
141   *
142   * @return a stream of ancestor node items
143   */
144  @NonNull
145  default Stream<? extends INodeItem> ancestor() {
146    return ancestorsOf(this);
147  }
148
149  /**
150   * Get a stream of this and all ancestors of this node item. The stream is
151   * ordered from self, then closest to farthest ancestor.
152   *
153   * @return a stream of this node followed by all ancestor node items
154   */
155  @NonNull
156  default Stream<? extends INodeItem> ancestorOrSelf() {
157    return ObjectUtils.notNull(Stream.concat(Stream.of(this), ancestor()));
158  }
159
160  /**
161   * Get a stream of the ancestors of the provided {@code item}. The stream is
162   * ordered from the closest to farthest ancestor.
163   *
164   * @param item
165   *          the target item to get ancestors for
166   *
167   * @return a stream of all ancestor node items
168   */
169  @NonNull
170  static Stream<? extends INodeItem> ancestorsOf(@NonNull INodeItem item) {
171    INodeItem parent = item.getParentNodeItem();
172    return ObjectUtils.notNull(parent == null ? Stream.empty() : Stream.concat(Stream.of(parent), ancestorsOf(parent)));
173  }
174
175  /**
176   * Get a stream of all descendant model items of this node item. The stream is
177   * ordered from closest to farthest descendants in a depth-first order.
178   *
179   * @return a stream of descendant node items
180   */
181  @NonNull
182  default Stream<? extends INodeItem> descendant() {
183    return decendantsOf(this);
184  }
185
186  /**
187   * Get a stream of all descendant model items of the provided {@code item}. The
188   * stream is ordered from closest to farthest descendants in a depth-first
189   * order.
190   *
191   * @param item
192   *          the target item to get descendants for
193   *
194   * @return a stream of descendant node items
195   */
196  @NonNull
197  static Stream<? extends INodeItem> decendantsOf(@NonNull INodeItem item) {
198    Stream<? extends INodeItem> children = item.modelItems();
199
200    return ObjectUtils.notNull(children.flatMap(child -> {
201      assert child != null;
202      return Stream.concat(Stream.of(child), decendantsOf(child));
203    }));
204  }
205
206  /**
207   * Get a stream of this node, followed by all descendant model items of this
208   * node item. The stream is ordered from closest to farthest descendants in a
209   * depth-first order.
210   *
211   * @return a stream of this node and descendant node items
212   */
213  @NonNull
214  default Stream<? extends INodeItem> descendantOrSelf() {
215    return ObjectUtils.notNull(Stream.concat(Stream.of(this), descendant()));
216  }
217
218  /**
219   * Get the flags and value data associated this node. The resulting collection
220   * is expected to be ordered, with the results in document order.
221   * <p>
222   * The resulting collection may be modified, but such modification is not thread
223   * safe
224   *
225   * @return a collection of flags
226   */
227  @NonNull
228  Collection<? extends IFlagNodeItem> getFlags();
229
230  /**
231   * Lookup a flag and value data on this node by it's effective name.
232   *
233   * @param name
234   *          the effective name of the flag
235   * @return the flag with the matching effective name or {@code null} if no match
236   *         was found
237   */
238  @Nullable
239  IFlagNodeItem getFlagByName(@NonNull String name);
240
241  /**
242   * Get the flags and value data associated with this node as a stream.
243   *
244   * @return the stream of flags or an empty stream if none exist
245   */
246  @SuppressWarnings("null")
247  @NonNull
248  default Stream<? extends IFlagNodeItem> flags() {
249    return getFlags().stream();
250  }
251
252  /**
253   * Get the model items (i.e., fields, assemblies) and value data associated this
254   * node. A given model instance can be multi-valued, so the value of each
255   * instance will be a list. The resulting collection is expected to be ordered,
256   * with the results in document order.
257   * <p>
258   * The resulting collection may be modified, but such modification is not thread
259   * safe
260   *
261   * @return a collection of list(s), with each list containing the items for a
262   *         given model instance
263   */
264  @NonNull
265  Collection<? extends List<? extends IModelNodeItem<?, ?>>> getModelItems();
266
267  /**
268   * Get the collection of model items associated with the instance having the
269   * provided {@code name}.
270   * <p>
271   * The resulting collection may be modified, but such modification is not thread
272   * safe
273   *
274   * @param name
275   *          the instance name to get model items for
276   * @return the sequence of items associated with the named model instance, or an
277   *         empty list if an instance with that name is not present
278   */
279  @NonNull
280  List<? extends IModelNodeItem<?, ?>> getModelItemsByName(@NonNull String name);
281
282  /**
283   * Get the model items (i.e., fields, assemblies) and value data associated this
284   * node as a stream.
285   *
286   * @return the stream of model items or an empty stream if none exist
287   */
288  @SuppressWarnings("null")
289  @NonNull
290  default Stream<? extends IModelNodeItem<?, ?>> modelItems() {
291    return getModelItems().stream().flatMap(list -> list.stream());
292  }
293}