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.oscal.lib.profile.resolver.support;
028
029import gov.nist.secauto.metaschema.model.common.metapath.MetapathExpression;
030import gov.nist.secauto.metaschema.model.common.metapath.item.IDocumentNodeItem;
031import gov.nist.secauto.metaschema.model.common.metapath.item.IRequiredValueModelNodeItem;
032import gov.nist.secauto.metaschema.model.common.metapath.item.IRootAssemblyNodeItem;
033import gov.nist.secauto.metaschema.model.common.util.CollectionUtil;
034import gov.nist.secauto.metaschema.model.common.util.ObjectUtils;
035
036import java.util.Collections;
037import java.util.EnumSet;
038import java.util.Set;
039
040import edu.umd.cs.findbugs.annotations.NonNull;
041
042/**
043 * Visits a catalog document and its children as designated.
044 * <p>
045 * This implementation is stateless. The {@code T} parameter can be used to
046 * convey state as needed.
047 *
048 * @param <T>
049 *          the state type
050 * @param <R>
051 *          the result type
052 */
053public abstract class AbstractCatalogEntityVisitor<T, R>
054    extends AbstractCatalogVisitor<T, R> {
055  @NonNull
056  public static final MetapathExpression CHILD_PART_METAPATH
057      = MetapathExpression.compile("part|part//part");
058  @NonNull
059  private static final MetapathExpression BACK_MATTER_RESOURCES_METAPATH
060      = MetapathExpression.compile("back-matter/resource");
061  @NonNull
062  private static final Set<IEntityItem.ItemType> GROUP_CONTAINER_TYPES
063      = ObjectUtils.notNull(EnumSet.of(
064          IEntityItem.ItemType.GROUP,
065          IEntityItem.ItemType.CONTROL,
066          IEntityItem.ItemType.PARAMETER,
067          IEntityItem.ItemType.PART));
068  @NonNull
069  private static final Set<IEntityItem.ItemType> CONTROL_CONTAINER_TYPES
070      = ObjectUtils.notNull(EnumSet.of(
071          IEntityItem.ItemType.CONTROL,
072          IEntityItem.ItemType.PARAMETER,
073          IEntityItem.ItemType.PART));
074  @NonNull
075  private final Set<IEntityItem.ItemType> itemTypesToVisit;
076
077  /**
078   * Create a new visitor that will visit the item types identified by
079   * {@code itemTypesToVisit}.
080   *
081   * @param itemTypesToVisit
082   *          the item type the visitor will visit
083   */
084  public AbstractCatalogEntityVisitor(@NonNull Set<IEntityItem.ItemType> itemTypesToVisit) {
085    this.itemTypesToVisit = CollectionUtil.unmodifiableSet(itemTypesToVisit);
086  }
087
088  public Set<IEntityItem.ItemType> getItemTypesToVisit() {
089    return CollectionUtil.unmodifiableSet(itemTypesToVisit);
090  }
091
092  protected boolean isVisitedItemType(@NonNull IEntityItem.ItemType type) {
093    return itemTypesToVisit.contains(type);
094  }
095
096  @Override
097  public R visitCatalog(IDocumentNodeItem catalogDocument, T state) {
098    R result = super.visitCatalog(catalogDocument, state);
099
100    IRootAssemblyNodeItem root = catalogDocument.getRootAssemblyNodeItem();
101    visitMetadata(root, state);
102    visitBackMatter(root, state);
103    return result;
104  }
105
106  @Override
107  protected R visitGroupContainer(IRequiredValueModelNodeItem catalogOrGroup, R initialResult, T state) {
108    R retval;
109    if (Collections.disjoint(getItemTypesToVisit(), GROUP_CONTAINER_TYPES)) {
110      retval = initialResult;
111    } else {
112      retval = super.visitGroupContainer(catalogOrGroup, initialResult, state);
113    }
114    return retval;
115  }
116
117  @Override
118  protected R visitControlContainer(IRequiredValueModelNodeItem catalogOrGroupOrControl, R initialResult, T state) {
119    R retval;
120    if (Collections.disjoint(getItemTypesToVisit(), CONTROL_CONTAINER_TYPES)) {
121      retval = initialResult;
122    } else {
123      // first descend to all control container children
124      retval = super.visitControlContainer(catalogOrGroupOrControl, initialResult, state);
125
126      // handle parameters
127      if (isVisitedItemType(IEntityItem.ItemType.PARAMETER)) {
128        retval = catalogOrGroupOrControl.getModelItemsByName("param").stream()
129            .map(paramItem -> {
130              return visitParameter(ObjectUtils.requireNonNull(paramItem), catalogOrGroupOrControl, state);
131            })
132            .reduce(retval, (first, second) -> aggregateResults(first, second, state));
133      } // TODO Auto-generated method stub
134    }
135    return retval;
136  }
137
138  protected void visitParts(@NonNull IRequiredValueModelNodeItem groupOrControlItem, T state) {
139    // handle parts
140    if (isVisitedItemType(IEntityItem.ItemType.PART)) {
141      CHILD_PART_METAPATH.evaluate(groupOrControlItem).asStream()
142          .map(item -> (IRequiredValueModelNodeItem) item)
143          .forEachOrdered(partItem -> {
144            visitPart(ObjectUtils.requireNonNull(partItem), groupOrControlItem, state);
145          });
146    }
147  }
148
149  @Override
150  protected R visitGroupInternal(@NonNull IRequiredValueModelNodeItem item, R childResult, T state) {
151    if (isVisitedItemType(IEntityItem.ItemType.PART)) {
152      visitParts(item, state);
153    }
154
155    R retval = childResult;
156    if (isVisitedItemType(IEntityItem.ItemType.GROUP)) {
157      retval = visitGroup(item, retval, state);
158    }
159    return retval;
160  }
161
162  @Override
163  protected R visitControlInternal(IRequiredValueModelNodeItem item, R childResult, T state) {
164    if (isVisitedItemType(IEntityItem.ItemType.PART)) {
165      visitParts(item, state);
166    }
167
168    R retval = childResult;
169    if (isVisitedItemType(IEntityItem.ItemType.CONTROL)) {
170      retval = visitControl(item, retval, state);
171    }
172    return retval;
173  }
174
175  /**
176   * Called when visiting a parameter.
177   * <p>
178   * Can be overridden by classes extending this interface to support processing
179   * of the visited object.
180   *
181   * @param item
182   *          the Metapath item for the parameter
183   * @param catalogOrGroupOrControl
184   *          the parameter's parent Metapath item
185   * @param state
186   *          the calling context information
187   * @return a meaningful result of the given type
188   */
189  protected R visitParameter(
190      @NonNull IRequiredValueModelNodeItem item,
191      @NonNull IRequiredValueModelNodeItem catalogOrGroupOrControl,
192      T state) {
193    // do nothing
194    return newDefaultResult(state);
195  }
196
197  /**
198   * Called when visiting a part.
199   * <p>
200   * Can be overridden by classes extending this interface to support processing
201   * of the visited object.
202   *
203   * @param item
204   *          the Metapath item for the part
205   * @param groupOrControl
206   *          the part's parent Metapath item
207   * @param state
208   *          the calling context information
209   */
210  protected void visitPart( // NOPMD noop default
211      @NonNull IRequiredValueModelNodeItem item,
212      @NonNull IRequiredValueModelNodeItem groupOrControl,
213      T state) {
214    // do nothing
215  }
216
217  /**
218   * Called when visiting the "metadata" section of an OSCAL document.
219   * <p>
220   * Visits each contained role, location, and party.
221   *
222   * @param rootItem
223   *          the root Metaschema node item containing the "metadata" node
224   * @param state
225   *          the calling context information
226   */
227  protected void visitMetadata(@NonNull IRootAssemblyNodeItem rootItem, T state) {
228    rootItem.getModelItemsByName("metadata").forEach(metadataItem -> {
229      if (isVisitedItemType(IEntityItem.ItemType.ROLE)) {
230        metadataItem.getModelItemsByName("role").forEach(roleItem -> {
231          visitRole(ObjectUtils.requireNonNull(roleItem), metadataItem, state);
232        });
233      }
234
235      if (isVisitedItemType(IEntityItem.ItemType.LOCATION)) {
236        metadataItem.getModelItemsByName("location").forEach(locationItem -> {
237          visitLocation(ObjectUtils.requireNonNull(locationItem), metadataItem, state);
238        });
239      }
240
241      if (isVisitedItemType(IEntityItem.ItemType.PARTY)) {
242        metadataItem.getModelItemsByName("party").forEach(partyItem -> {
243          visitParty(ObjectUtils.requireNonNull(partyItem), metadataItem, state);
244        });
245      }
246    });
247  }
248
249  /**
250   * Called when visiting a role in the "metadata" section of an OSCAL document.
251   * <p>
252   * Can be overridden by classes extending this interface to support processing
253   * of the visited object.
254   *
255   * @param item
256   *          the role Metaschema node item which is a child of the "metadata"
257   *          node
258   * @param metadataItem
259   *          the "metadata" Metaschema node item containing the role
260   * @param state
261   *          the calling context information
262   */
263  protected void visitRole( // NOPMD noop default
264      @NonNull IRequiredValueModelNodeItem item,
265      @NonNull IRequiredValueModelNodeItem metadataItem,
266      T state) {
267    // do nothing
268  }
269
270  /**
271   * Called when visiting a location in the "metadata" section of an OSCAL
272   * document.
273   * <p>
274   * Can be overridden by classes extending this interface to support processing
275   * of the visited object.
276   *
277   * @param item
278   *          the location Metaschema node item which is a child of the "metadata"
279   *          node
280   * @param metadataItem
281   *          the "metadata" Metaschema node item containing the location
282   * @param state
283   *          the calling context information
284   */
285  protected void visitLocation( // NOPMD noop default
286      @NonNull IRequiredValueModelNodeItem item,
287      @NonNull IRequiredValueModelNodeItem metadataItem,
288      T state) {
289    // do nothing
290  }
291
292  /**
293   * Called when visiting a party in the "metadata" section of an OSCAL document.
294   * <p>
295   * Can be overridden by classes extending this interface to support processing
296   * of the visited object.
297   *
298   * @param item
299   *          the party Metaschema node item which is a child of the "metadata"
300   *          node
301   * @param metadataItem
302   *          the "metadata" Metaschema node item containing the party
303   * @param state
304   *          the calling context information
305   */
306  protected void visitParty( // NOPMD noop default
307      @NonNull IRequiredValueModelNodeItem item,
308      @NonNull IRequiredValueModelNodeItem metadataItem,
309      T state) {
310    // do nothing
311  }
312
313  /**
314   * Called when visiting the "back-matter" section of an OSCAL document.
315   * <p>
316   * Visits each contained resource.
317   *
318   * @param rootItem
319   *          the root Metaschema node item containing the "back-matter" node
320   * @param state
321   *          the calling context information
322   */
323  protected void visitBackMatter(@NonNull IRootAssemblyNodeItem rootItem, T state) {
324    if (isVisitedItemType(IEntityItem.ItemType.RESOURCE)) {
325      BACK_MATTER_RESOURCES_METAPATH.evaluate(rootItem).asStream()
326          .map(item -> (IRequiredValueModelNodeItem) item)
327          .forEachOrdered(resourceItem -> {
328            visitResource(ObjectUtils.requireNonNull(resourceItem), rootItem, state);
329          });
330    }
331  }
332
333  /**
334   * Called when visiting a resource in the "back-matter" section of an OSCAL
335   * document.
336   * <p>
337   * Can be overridden by classes extending this interface to support processing
338   * of the visited object.
339   *
340   * @param item
341   *          the resource Metaschema node item which is a child of the "metadata"
342   *          node
343   * @param backMatterItem
344   *          the resource Metaschema node item containing the party
345   * @param state
346   *          the calling context information
347   */
348  protected void visitResource( // NOPMD noop default
349      @NonNull IRequiredValueModelNodeItem item,
350      @NonNull IRootAssemblyNodeItem backMatterItem,
351      T state) {
352    // do nothing
353  }
354}