1   /*
2    * SPDX-FileCopyrightText: none
3    * SPDX-License-Identifier: CC0-1.0
4    */
5   
6   package dev.metaschema.core.model.constraint;
7   
8   import dev.metaschema.core.metapath.IMetapathExpression;
9   import dev.metaschema.core.model.constraint.impl.DefaultReportConstraint;
10  import dev.metaschema.core.util.ObjectUtils;
11  import edu.umd.cs.findbugs.annotations.NonNull;
12  
13  /**
14   * Represents a rule reporting a condition when a Metaschema assembly, field, or
15   * flag data instance matches a Metapath-based test.
16   * <p>
17   * Unlike {@link IExpectConstraint} which generates a finding when the test is
18   * FALSE, a report constraint generates a finding when the test is TRUE. This is
19   * useful for:
20   * <ul>
21   * <li>Reporting deprecated usage patterns</li>
22   * <li>Flagging known issues or limitations</li>
23   * <li>Providing informational messages about content</li>
24   * </ul>
25   * <p>
26   * A custom message can be used to indicate what a matching condition signifies.
27   * The default severity level is {@link Level#INFORMATIONAL}.
28   *
29   * @since 2.0.0
30   */
31  public interface IReportConstraint extends IConfigurableMessageConstraint {
32    /**
33     * The default severity level for report constraints.
34     */
35    @NonNull
36    Level DEFAULT_LEVEL = Level.INFORMATIONAL;
37  
38    @Override
39    default Type getType() {
40      return Type.REPORT;
41    }
42  
43    /**
44     * Get the test to use to identify reportable conditions in selected nodes.
45     * <p>
46     * A finding is generated when this test evaluates to {@code true}.
47     *
48     * @return the test metapath expression to use
49     */
50    @NonNull
51    IMetapathExpression getTest();
52  
53    @Override
54    default <T, R> R accept(IConstraintVisitor<T, R> visitor, T state) {
55      return visitor.visitReportConstraint(this, state);
56    }
57  
58    /**
59     * Create a new constraint builder.
60     *
61     * @return the builder
62     */
63    @NonNull
64    static Builder builder() {
65      return new Builder();
66    }
67  
68    /**
69     * Provides a builder pattern for constructing a new {@link IReportConstraint}.
70     */
71    final class Builder
72        extends AbstractConfigurableMessageConstraintBuilder<Builder, IReportConstraint> {
73      private IMetapathExpression test;
74      private boolean levelSet;
75  
76      private Builder() {
77        // disable construction
78      }
79  
80      /**
81       * Use the provided test to identify reportable conditions in selected nodes.
82       * <p>
83       * A finding is generated when this test evaluates to {@code true}.
84       *
85       * @param test
86       *          the test metapath expression to use
87       * @return this builder
88       */
89      @NonNull
90      public Builder test(@NonNull IMetapathExpression test) {
91        this.test = test;
92        return this;
93      }
94  
95      @Override
96      protected Builder getThis() {
97        return this;
98      }
99  
100     /**
101      * {@inheritDoc}
102      * <p>
103      * For report constraints, the default level is {@link Level#INFORMATIONAL} if
104      * no level is explicitly set.
105      */
106     @Override
107     @NonNull
108     public Builder level(@NonNull Level level) {
109       this.levelSet = true;
110       return super.level(level);
111     }
112 
113     @Override
114     @NonNull
115     protected Level getLevel() {
116       return levelSet ? super.getLevel() : DEFAULT_LEVEL;
117     }
118 
119     @Override
120     protected void validate() {
121       super.validate();
122 
123       ObjectUtils.requireNonNull(getTest());
124     }
125 
126     private IMetapathExpression getTest() {
127       return test;
128     }
129 
130     @Override
131     protected IReportConstraint newInstance() {
132       return new DefaultReportConstraint(
133           getId(),
134           getFormalName(),
135           getDescription(),
136           ObjectUtils.notNull(getSource()),
137           getLevel(),
138           getTarget(),
139           getProperties(),
140           ObjectUtils.requireNonNull(getTest()),
141           getMessage(),
142           getRemarks());
143     }
144   }
145 }