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 java.util.List;
030
031import edu.umd.cs.findbugs.annotations.NonNull;
032
033/**
034 * Used by implementations of this class to visit a sequence of node items in a
035 * directed graph, using depth-first ordering.
036 *
037 * @param <RESULT>
038 *          the type of result produced by visitation
039 * @param <CONTEXT>
040 *          the type of data to pass to each visited node
041 */
042public abstract class AbstractNodeItemVisitor<RESULT, CONTEXT> implements INodeItemVisitor<RESULT, CONTEXT> {
043  /**
044   * Visit the provided {@code item}.
045   *
046   * @param item
047   *          the item to visit
048   * @param context
049   *          provides contextual information for use by the visitor
050   * @return the result produced by visiting the item
051   */
052  public final RESULT visit(@NonNull INodeItemVisitable item, CONTEXT context) {
053    return item.accept(this, context);
054  }
055
056  /**
057   * Visit any child flags associated with the provided {@code item}.
058   *
059   * @param item
060   *          the item to visit
061   * @param context
062   *          provides contextual information for use by the visitor
063   * @return the result produced by visiting the item's flags
064   */
065  protected RESULT visitFlags(@NonNull INodeItem item, CONTEXT context) {
066    RESULT result = defaultResult();
067    for (IFlagNodeItem flag : item.getFlags()) {
068      assert flag != null;
069      if (!shouldVisitNextChild(item, flag, result, context)) {
070        break;
071      }
072
073      RESULT childResult = flag.accept(this, context);
074      result = aggregateResult(result, childResult, context);
075    }
076    return result;
077  }
078
079  /**
080   * Visit any child model items associated with the provided {@code item}.
081   *
082   * @param item
083   *          the item to visit
084   * @param context
085   *          provides contextual information for use by the visitor
086   * @return the result produced by visiting the item's child model items
087   */
088  protected RESULT visitModelChildren(@NonNull INodeItem item, CONTEXT context) {
089    RESULT result = defaultResult();
090
091    for (List<? extends IModelNodeItem<?, ?>> childItems : item.getModelItems()) {
092      for (IModelNodeItem<?, ?> childItem : childItems) {
093        assert childItem != null;
094        if (!shouldVisitNextChild(item, childItem, result, context)) {
095          break;
096        }
097
098        RESULT childResult = childItem.accept(this, context);
099        result = aggregateResult(result, childResult, context);
100      }
101    }
102    return result;
103  }
104
105  /**
106   * Determine if the child should be visited next, or skipped.
107   *
108   * @param parent
109   *          the parent of the child to visit next
110   * @param child
111   *          the next child to visit
112   * @param result
113   *          the current visitation result
114   * @param context
115   *          provides contextual information for use by the visitor
116   * @return {@code true} if the child should be visited, or {@code false} if the
117   *         child should be skipped
118   */
119  protected boolean shouldVisitNextChild(
120      @NonNull INodeItem parent,
121      @NonNull INodeItem child,
122      RESULT result,
123      CONTEXT context) {
124    // this is the default behavior, which can be overridden
125    return true;
126  }
127
128  /**
129   * Determine if the child should be visited next, or skipped.
130   *
131   * @param parent
132   *          the parent of the child to visit next
133   * @param child
134   *          the next child to visit
135   * @param result
136   *          the current visitation result
137   * @param context
138   *          provides contextual information for use by the visitor
139   * @return {@code true} if the child should be visited, or {@code false} if the
140   *         child should be skipped
141   */
142  protected boolean shouldVisitNextChild(
143      @NonNull INodeItem parent,
144      @NonNull IModelNodeItem<?, ?> child,
145      RESULT result,
146      CONTEXT context) {
147    // this is the default behavior, which can be overridden
148    return true;
149  }
150
151  /**
152   * The initial, default visitation result, which will be used as the basis for
153   * aggregating results produced when visiting.
154   *
155   * @return the default result
156   * @see #aggregateResult(Object, Object, Object)
157   */
158  protected abstract RESULT defaultResult();
159
160  /**
161   * Combine two results into a single, aggregate result.
162   *
163   * @param first
164   *          the original result
165   * @param second
166   *          the new result to combine with the original result
167   * @param context
168   *          provides contextual information for use by the visitor
169   * @return the combined result
170   */
171  protected RESULT aggregateResult(RESULT first, RESULT second, CONTEXT context) {
172    // this is the default behavior, which can be overridden
173    return second;
174  }
175
176  @Override
177  public RESULT visitDocument(IDocumentNodeItem item, CONTEXT context) {
178    // this is the default behavior, which can be overridden
179    return visitModelChildren(item, context);
180    // return visitAssembly(item.getRootAssemblyNodeItem(), context);
181  }
182
183  @Override
184  public RESULT visitFlag(IFlagNodeItem item, CONTEXT context) {
185    // this is the default behavior, which can be overridden
186    return defaultResult();
187  }
188
189  @Override
190  public RESULT visitField(IFieldNodeItem item, CONTEXT context) {
191    // this is the default behavior, which can be overridden
192    return visitFlags(item, context);
193  }
194
195  @Override
196  public RESULT visitAssembly(IAssemblyNodeItem item, CONTEXT context) {
197    // this is the default behavior, which can be overridden
198    return aggregateResult(visitFlags(item, context), visitModelChildren(item, context), context);
199  }
200
201  @Override
202  public RESULT visitMetaschema(IModuleNodeItem item, CONTEXT context) {
203    // this is the default behavior, which can be overridden
204    return aggregateResult(visitFlags(item, context), visitModelChildren(item, context), context);
205  }
206}