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