1   /*
2    * SPDX-FileCopyrightText: none
3    * SPDX-License-Identifier: CC0-1.0
4    */
5   
6   package gov.nist.secauto.metaschema.schemagen.json.impl;
7   
8   import gov.nist.secauto.metaschema.core.model.IDefinition;
9   import gov.nist.secauto.metaschema.core.model.IFlagInstance;
10  import gov.nist.secauto.metaschema.core.model.IModelDefinition;
11  import gov.nist.secauto.metaschema.core.util.ObjectUtils;
12  import gov.nist.secauto.metaschema.schemagen.IGenerationState;
13  import gov.nist.secauto.metaschema.schemagen.json.IDefinitionJsonSchema;
14  import gov.nist.secauto.metaschema.schemagen.json.IJsonGenerationState;
15  
16  import java.util.List;
17  import java.util.Map;
18  import java.util.Objects;
19  import java.util.stream.Collectors;
20  import java.util.stream.Stream;
21  
22  import edu.umd.cs.findbugs.annotations.NonNull;
23  import edu.umd.cs.findbugs.annotations.Nullable;
24  import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
25  
26  public abstract class AbstractModelDefinitionJsonSchema<D extends IModelDefinition>
27      extends AbstractDefinitionJsonSchema<D> {
28    @Nullable
29    private final String jsonKeyFlagName;
30    @Nullable
31    private final String discriminatorProperty;
32    @Nullable
33    private final String discriminatorValue;
34    @NonNull
35    private final List<FlagInstanceJsonProperty> flagProperties;
36    @NonNull
37    private final IKey key;
38  
39    @SuppressFBWarnings(value = "CT_CONSTRUCTOR_THROW", justification = "Use of final fields")
40    protected AbstractModelDefinitionJsonSchema(
41        @NonNull D definition,
42        @Nullable String jsonKeyFlagName,
43        @Nullable String discriminatorProperty,
44        @Nullable String discriminatorValue) {
45      super(definition);
46      this.jsonKeyFlagName = jsonKeyFlagName;
47      this.discriminatorProperty = discriminatorProperty;
48      this.discriminatorValue = discriminatorValue;
49      this.key = IKey.of(definition, jsonKeyFlagName, discriminatorProperty, discriminatorValue);
50  
51      Stream<? extends IFlagInstance> flagStream = definition.getFlagInstances().stream();
52  
53      // determine the flag instances to generate
54      if (jsonKeyFlagName != null) {
55        IFlagInstance jsonKeyFlag = definition.getFlagInstanceByName(
56            definition.getContainingModule().toFlagQName(jsonKeyFlagName));
57        if (jsonKeyFlag == null) {
58          throw new IllegalArgumentException(
59              String.format("The referenced json-key flag-name '%s' does not exist on definition '%s'.",
60                  jsonKeyFlagName,
61                  definition.getName()));
62        }
63        flagStream = flagStream.filter(instance -> !jsonKeyFlag.equals(instance));
64      }
65  
66      this.flagProperties = ObjectUtils.notNull(flagStream
67          .map(instance -> new FlagInstanceJsonProperty(ObjectUtils.requireNonNull(instance)))
68          .collect(Collectors.toUnmodifiableList()));
69    }
70  
71    @Override
72    public IKey getKey() {
73      return key;
74    }
75  
76    protected String getJsonKeyFlagName() {
77      return jsonKeyFlagName;
78    }
79  
80    protected String getDiscriminatorProperty() {
81      return discriminatorProperty;
82    }
83  
84    protected String getDiscriminatorValue() {
85      return discriminatorValue;
86    }
87  
88    @Override
89    protected String generateDefinitionName(IJsonGenerationState state) {
90      IModelDefinition definition = getDefinition();
91      StringBuilder builder = new StringBuilder();
92      if (jsonKeyFlagName != null) {
93        builder
94            .append(IGenerationState.toCamelCase(jsonKeyFlagName))
95            .append("JsonKey");
96      }
97  
98      if (discriminatorProperty != null || discriminatorValue != null) {
99        builder
100           .append(IGenerationState.toCamelCase(ObjectUtils.requireNonNull(discriminatorProperty)))
101           .append(IGenerationState.toCamelCase(ObjectUtils.requireNonNull(discriminatorValue)))
102           .append("Choice");
103     }
104     return state.getTypeNameForDefinition(
105         definition,
106         builder.toString());
107   }
108 
109   protected List<FlagInstanceJsonProperty> getFlagProperties() {
110     return flagProperties;
111   }
112 
113   @Override
114   public void gatherDefinitions(
115       @NonNull Map<IKey, IDefinitionJsonSchema<?>> gatheredDefinitions,
116       @NonNull IJsonGenerationState state) {
117     super.gatherDefinitions(gatheredDefinitions, state);
118 
119     for (FlagInstanceJsonProperty property : getFlagProperties()) {
120       property.gatherDefinitions(gatheredDefinitions, state);
121     }
122   }
123 
124   public static class ComplexKey implements IKey {
125     @NonNull
126     private final IDefinition definition;
127     @Nullable
128     private final String jsonKeyFlagName;
129     @Nullable
130     private final String discriminatorProperty;
131     @Nullable
132     private final String discriminatorValue;
133 
134     public ComplexKey(
135         @NonNull IDefinition definition,
136         @Nullable String jsonKeyFlagName,
137         @Nullable String discriminatorProperty,
138         @Nullable String discriminatorValue) {
139       this.definition = definition;
140       this.jsonKeyFlagName = jsonKeyFlagName;
141       this.discriminatorProperty = discriminatorProperty;
142       this.discriminatorValue = discriminatorValue;
143     }
144 
145     @Override
146     @NonNull
147     public IDefinition getDefinition() {
148       return definition;
149     }
150 
151     @Override
152     @Nullable
153     public String getJsonKeyFlagName() {
154       return jsonKeyFlagName;
155     }
156 
157     @Override
158     public String getDiscriminatorProperty() {
159       return discriminatorProperty;
160     }
161 
162     @Override
163     public String getDiscriminatorValue() {
164       return discriminatorValue;
165     }
166 
167     @Override
168     public int hashCode() {
169       return Objects.hash(definition, jsonKeyFlagName, discriminatorProperty, discriminatorValue);
170     }
171 
172     @SuppressWarnings("PMD.OnlyOneReturn")
173     @Override
174     public boolean equals(Object obj) {
175       if (this == obj) {
176         return true;
177       }
178       if (!(obj instanceof IKey)) {
179         return false;
180       }
181       IKey other = (IKey) obj;
182       return Objects.equals(definition, other.getDefinition())
183           && Objects.equals(jsonKeyFlagName, other.getJsonKeyFlagName())
184           && Objects.equals(discriminatorProperty, other.getDiscriminatorProperty())
185           && Objects.equals(discriminatorValue, other.getDiscriminatorValue());
186     }
187   }
188 }