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.model.IAssemblyDefinition;
030import gov.nist.secauto.metaschema.core.model.IAssemblyInstance;
031import gov.nist.secauto.metaschema.core.model.IChoiceInstance;
032import gov.nist.secauto.metaschema.core.model.IFieldDefinition;
033import gov.nist.secauto.metaschema.core.model.IFieldInstance;
034import gov.nist.secauto.metaschema.core.model.IModelContainer;
035import gov.nist.secauto.metaschema.core.model.IModule;
036import gov.nist.secauto.metaschema.core.model.INamedModelInstance;
037import gov.nist.secauto.metaschema.core.util.ObjectUtils;
038
039import java.net.URI;
040import java.util.stream.Stream;
041
042import edu.umd.cs.findbugs.annotations.NonNull;
043import edu.umd.cs.findbugs.annotations.Nullable;
044
045public abstract class AbstractNodeItemFactory implements INodeItemFactory, INodeItemGenerator {
046  @Override
047  public IDocumentNodeItem newDocumentNodeItem(
048      IAssemblyDefinition definition,
049      URI documentUri,
050      Object value) {
051    return new DocumentNodeItemImpl(
052        definition,
053        value,
054        documentUri,
055        this);
056  }
057
058  @Override
059  public IModuleNodeItem newModuleNodeItem(
060      IModule module) {
061    return new ModuleNodeItemImpl(
062        module,
063        this);
064  }
065
066  @Override
067  public IFieldNodeItem newFieldNodeItem(
068      IFieldDefinition definition,
069      IModuleNodeItem module) {
070    return new FieldGlobalDefinitionNodeItemImpl(
071        definition,
072        module,
073        this);
074  }
075
076  @Override
077  public IFieldNodeItem newFieldNodeItem(
078      IFieldDefinition definition,
079      URI baseUri) {
080    return new FieldOrphanedDefinitionNodeItemImpl(
081        definition,
082        baseUri,
083        this);
084  }
085
086  @Override
087  public IFieldNodeItem newFieldNodeItem(
088      IFieldInstance instance,
089      IAssemblyNodeItem parent) {
090    return new FieldInstanceNoValueNodeItemImpl(instance, parent, this);
091  }
092
093  @Override
094  public IFieldNodeItem newFieldNodeItem(
095      IFieldInstance instance,
096      IAssemblyNodeItem parent,
097      int position,
098      Object value) {
099    return new FieldInstanceNodeItemImpl(instance, parent, position, value, this);
100  }
101
102  @Override
103  public IAssemblyNodeItem newAssemblyNodeItem(
104      IAssemblyDefinition definition,
105      IModuleNodeItem module) {
106    return new AssemblyGlobalDefinitionNodeItemImpl(
107        definition,
108        module,
109        this);
110  }
111
112  @Override
113  public IAssemblyNodeItem newAssemblyNodeItem(
114      IAssemblyDefinition definition,
115      URI baseUri) {
116    return new AssemblyOrphanedDefinitionNodeItemImpl(
117        definition,
118        baseUri,
119        this);
120  }
121
122  @Override
123  public IAssemblyNodeItem newAssemblyNodeItem(
124      IAssemblyDefinition definition,
125      URI baseUri,
126      Object value) {
127    return new AssemblyOrphanedDefinitionDataNodeItemImpl(
128        definition,
129        baseUri,
130        value,
131        this);
132  }
133
134  @Override
135  public IAssemblyNodeItem newAssemblyNodeItem(
136      IAssemblyInstance instance,
137      IAssemblyNodeItem parent) {
138    IAssemblyNodeItem retval = null;
139    if (!instance.getDefinition().isInline()) {
140      // if not inline, need to check for a cycle
141      IAssemblyNodeItem cycle = getCycledInstance(instance.getEffectiveName(), instance.getDefinition(), parent);
142      if (cycle != null) {
143        // generate a cycle wrapper of the original node item
144        retval = new CycledAssemblyInstanceNodeItemImpl(instance, parent, cycle);
145      }
146    }
147
148    if (retval == null) {
149      retval = new AssemblyInstanceNoValueNodeItemImpl(instance, parent, this);
150    }
151    return retval;
152  }
153
154  @Override
155  public IAssemblyNodeItem newAssemblyNodeItem(
156      IAssemblyInstance instance,
157      IAssemblyNodeItem parent,
158      int position,
159      Object value) {
160    return new AssemblyInstanceNodeItemImpl(instance, parent, position, value, this);
161  }
162
163  @Nullable
164  private IAssemblyNodeItem getCycledInstance(
165      @NonNull String effectiveName,
166      @NonNull IAssemblyDefinition definition,
167      @NonNull IAssemblyNodeItem parent) {
168    IAssemblyNodeItem retval = null;
169
170    IAssemblyDefinition parentDefinition = parent.getDefinition();
171    if (parent.getName().equals(effectiveName) && parentDefinition.equals(definition)) {
172      retval = parent;
173    } else {
174      IAssemblyNodeItem ancestor = parent.getParentContentNodeItem();
175      if (ancestor != null) {
176        retval = getCycledInstance(effectiveName, definition, ancestor);
177      }
178    }
179    return retval;
180  }
181
182  /**
183   * Create a new {@link IModelNodeItem} based on the provided {@code instance}
184   * that is a child of the provided {@code parent}. This new item will have the
185   * provided {@code value}.
186   *
187   * @param instance
188   *          the model instance to create the node for
189   * @param parent
190   *          the item to use as the parent item for the created node item
191   * @param position
192   *          the data item's position in the sequence of data items for the
193   *          instance
194   * @param value
195   *          the data item's value
196   * @return the created node item
197   */
198  @NonNull
199  protected IModelNodeItem<?, ?> newModelItem(
200      @NonNull INamedModelInstance instance,
201      @NonNull IAssemblyNodeItem parent,
202      int position,
203      @NonNull Object value) {
204    @NonNull IModelNodeItem<?, ?> item;
205    if (instance instanceof IAssemblyInstance) {
206      item = newAssemblyNodeItem((IAssemblyInstance) instance, parent, position, value);
207    } else if (instance instanceof IFieldInstance) {
208      item = newFieldNodeItem((IFieldInstance) instance, parent, position, value);
209    } else {
210      throw new UnsupportedOperationException("unsupported instance type: " + instance.getClass().getName());
211    }
212    return item;
213  }
214
215  /**
216   * Create a new {@link IModelNodeItem} based on the provided {@code instance}
217   * that is a child of the provided {@code parent}. This new item will have no
218   * associated value.
219   *
220   * @param instance
221   *          the model instance to create the node for
222   * @param parent
223   *          the item to use as the parent item for the created node item
224   * @return the created node item
225   */
226  @NonNull
227  protected IModelNodeItem<?, ?> newModelItem(
228      @NonNull INamedModelInstance instance,
229      @NonNull IAssemblyNodeItem parent) {
230    @NonNull IModelNodeItem<?, ?> item;
231    if (instance instanceof IAssemblyInstance) {
232      item = newAssemblyNodeItem((IAssemblyInstance) instance, parent);
233    } else if (instance instanceof IFieldInstance) {
234      item = newFieldNodeItem((IFieldInstance) instance, parent);
235    } else {
236      throw new UnsupportedOperationException("unsupported instance type: " + instance.getClass().getName());
237    }
238    return item;
239  }
240
241  /**
242   * Get the descendant model instances of the provided {@code container}.
243   *
244   * @param container
245   *          the container to get descendant instances for
246   * @return the stream of descendant instances
247   */
248  @NonNull
249  protected Stream<INamedModelInstance> getNamedModelInstances(@NonNull IModelContainer container) {
250    return ObjectUtils.notNull(container.getModelInstances().stream()
251        .flatMap(instance -> {
252          Stream<INamedModelInstance> retval;
253          if (instance instanceof IAssemblyInstance || instance instanceof IFieldInstance) {
254            retval = Stream.of((INamedModelInstance) instance);
255          } else if (instance instanceof IChoiceInstance) {
256            // descend into the choice
257            retval = getNamedModelInstances((IChoiceInstance) instance);
258          } else {
259            throw new UnsupportedOperationException("unsupported instance type: " + instance.getClass().getName());
260          }
261          return retval;
262        }));
263  }
264}