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.model.constraint;
028
029import gov.nist.secauto.metaschema.core.datatype.markup.MarkupLine;
030import gov.nist.secauto.metaschema.core.datatype.markup.MarkupMultiline;
031import gov.nist.secauto.metaschema.core.metapath.DynamicContext;
032import gov.nist.secauto.metaschema.core.metapath.ISequence;
033import gov.nist.secauto.metaschema.core.metapath.MetapathExpression;
034import gov.nist.secauto.metaschema.core.metapath.item.node.IDefinitionNodeItem;
035
036import java.net.URI;
037import java.util.HashMap;
038import java.util.Map;
039import java.util.Set;
040
041import javax.xml.namespace.QName;
042
043import edu.umd.cs.findbugs.annotations.NonNull;
044import edu.umd.cs.findbugs.annotations.Nullable;
045
046/**
047 * Represents a rule constraining the model of a Metaschema assembly, field or
048 * flag. Provides a common interface for all constraint definitions.
049 */
050public interface IConstraint {
051  /**
052   * The degree to which a constraint violation is significant.
053   * <p>
054   * These values are ordered from least significant to most significant.
055   */
056  enum Level {
057    /**
058     * A violation of the constraint represents a point of interest.
059     */
060    INFORMATIONAL,
061    /**
062     * A violation of the constraint represents a potential issue with the content.
063     */
064    WARNING,
065    /**
066     * A violation of the constraint represents a fault in the content. This may
067     * include issues around compatibility, integrity, consistency, etc.
068     */
069    ERROR,
070    /**
071     * A violation of the constraint represents a serious fault in the content that
072     * will prevent typical use of the content.
073     */
074    CRITICAL;
075  }
076
077  /**
078   * The default level to use if no level is provided.
079   */
080  @NonNull
081  Level DEFAULT_LEVEL = Level.ERROR;
082  /**
083   * The default target Metapath expression to use if no target is provided.
084   */
085  @NonNull
086  MetapathExpression DEFAULT_TARGET = MetapathExpression.CONTEXT_NODE;
087  /**
088   * The default target Metapath expression to use if no target is provided.
089   */
090  @NonNull
091  String DEFAULT_TARGET_METAPATH = ".";
092
093  /**
094   * Retrieve the unique identifier for the constraint.
095   *
096   * @return the identifier or {@code null} if no identifier is defined
097   */
098  @Nullable
099  String getId();
100
101  @Nullable
102  MarkupLine getDescription();
103
104  @Nullable
105  String getFormalName();
106
107  /**
108   * Get information about the source of the constraint.
109   *
110   * @return the source information
111   */
112  @NonNull
113  ISource getSource();
114
115  /**
116   * The significance of a violation of this constraint.
117   *
118   * @return the level
119   */
120  @NonNull
121  Level getLevel();
122
123  @NonNull
124  Map<QName, Set<String>> getProperties();
125
126  /**
127   * Retrieve the Metapath expression to use to query the targets of the
128   * constraint.
129   *
130   * @return a Metapath expression
131   */
132  @NonNull
133  MetapathExpression getTarget();
134
135  /**
136   * Based on the provided {@code contextNodeItem}, find all nodes matching the
137   * target expression.
138   *
139   * @param contextNodeItem
140   *          the node item to evaluate the target expression against
141   * @return the matching nodes as a sequence
142   * @see #getTarget()
143   */
144  @NonNull
145  default ISequence<? extends IDefinitionNodeItem<?, ?>> matchTargets(
146      @NonNull IDefinitionNodeItem<?, ?> contextNodeItem) {
147    return getTarget().evaluate(contextNodeItem);
148  }
149
150  /**
151   * Based on the provided {@code contextNodeItem}, find all nodes matching the
152   * target expression.
153   *
154   * @param item
155   *          the node item to evaluate the target expression against
156   * @param dynamicContext
157   *          the Metapath evaluation context to use
158   * @return the matching nodes as a sequence
159   * @see #getTarget()
160   */
161  @NonNull
162  default ISequence<? extends IDefinitionNodeItem<?, ?>> matchTargets(@NonNull IDefinitionNodeItem<?, ?> item,
163      @NonNull DynamicContext dynamicContext) {
164    return item.hasValue() ? getTarget().evaluate(item, dynamicContext) : ISequence.empty();
165  }
166
167  /**
168   * Retrieve the remarks associated with the constraint.
169   *
170   * @return the remarks or {@code null} if no remarks are defined
171   */
172  MarkupMultiline getRemarks();
173
174  <T, R> R accept(@NonNull IConstraintVisitor<T, R> visitor, T state);
175
176  interface ISource {
177    enum SourceType {
178      /**
179       * A constraint embedded in a model.
180       */
181      MODEL,
182      /**
183       * A constraint defined externally from a model.
184       */
185      EXTERNAL;
186    }
187
188    @NonNull
189    SourceType getSourceType();
190
191    @Nullable
192    URI getSource();
193  }
194
195  final class InternalModelSource implements ISource {
196    @NonNull
197    private static final ISource SINGLETON = new InternalModelSource();
198
199    @NonNull
200    public static ISource instance() {
201      return SINGLETON;
202    }
203
204    private InternalModelSource() {
205      // reduce visibility
206    }
207
208    @Override
209    public SourceType getSourceType() {
210      return SourceType.MODEL;
211    }
212
213    @Override
214    public URI getSource() {
215      // always null
216      return null;
217    }
218  }
219
220  final class ExternalModelSource implements IConstraint.ISource {
221    @NonNull
222    private static final Map<URI, ExternalModelSource> sources = new HashMap<>(); // NOPMD - intentional
223    @NonNull
224    private final URI modelUri;
225
226    @NonNull
227    public static ISource instance(@NonNull URI location) {
228      ISource retval;
229      synchronized (sources) {
230        retval = sources.get(location);
231        if (retval == null) {
232          retval = new ExternalModelSource(location);
233        }
234      }
235      return retval;
236    }
237
238    private ExternalModelSource(@NonNull URI modelSource) {
239      this.modelUri = modelSource;
240    }
241
242    @Override
243    public SourceType getSourceType() {
244      return SourceType.MODEL;
245    }
246
247    @NonNull
248    @Override
249    public URI getSource() {
250      return modelUri;
251    }
252  }
253
254  final class ExternalSource implements IConstraint.ISource {
255    @NonNull
256    private static final Map<URI, ExternalSource> sources = new HashMap<>(); // NOPMD - intentional
257
258    @NonNull
259    private final URI modelUri;
260
261    @NonNull
262    public static ISource instance(@NonNull URI location) {
263      ISource retval;
264      synchronized (sources) {
265        retval = sources.get(location);
266        if (retval == null) {
267          retval = new ExternalModelSource(location);
268        }
269      }
270      return retval;
271    }
272
273    private ExternalSource(@NonNull URI modelSource) {
274      this.modelUri = modelSource;
275    }
276
277    @Override
278    public SourceType getSourceType() {
279      return SourceType.EXTERNAL;
280    }
281
282    @NonNull
283    @Override
284    public URI getSource() {
285      return modelUri;
286    }
287  }
288}