1   /*
2    * SPDX-FileCopyrightText: none
3    * SPDX-License-Identifier: CC0-1.0
4    */
5   
6   package gov.nist.secauto.metaschema.schemagen;
7   
8   import gov.nist.secauto.metaschema.core.configuration.IConfiguration;
9   import gov.nist.secauto.metaschema.core.metapath.IMetapathExpression;
10  import gov.nist.secauto.metaschema.core.model.IDefinition;
11  import gov.nist.secauto.metaschema.core.model.IModule;
12  import gov.nist.secauto.metaschema.core.model.INamedInstance;
13  import gov.nist.secauto.metaschema.core.model.IValuedDefinition;
14  import gov.nist.secauto.metaschema.core.model.constraint.IAllowedValue;
15  import gov.nist.secauto.metaschema.core.model.constraint.IAllowedValuesConstraint;
16  import gov.nist.secauto.metaschema.core.util.CollectionUtil;
17  import gov.nist.secauto.metaschema.core.util.ObjectUtils;
18  import gov.nist.secauto.metaschema.schemagen.datatype.IDatatypeManager;
19  
20  import java.util.ArrayList;
21  import java.util.LinkedList;
22  import java.util.List;
23  
24  import edu.umd.cs.findbugs.annotations.NonNull;
25  import edu.umd.cs.findbugs.annotations.Nullable;
26  
27  public abstract class AbstractGenerationState<WRITER, DATATYPE_MANAGER extends IDatatypeManager>
28      implements IGenerationState<WRITER> {
29    @NonNull
30    private final IModule module;
31    @NonNull
32    private final WRITER writer;
33    @NonNull
34    private final DATATYPE_MANAGER datatypeManager;
35    @NonNull
36    private final IInlineStrategy inlineStrategy;
37  
38    @NonNull
39    private final ModuleIndex moduleIndex;
40  
41    public AbstractGenerationState(
42        @NonNull IModule module,
43        @NonNull WRITER writer,
44        @NonNull IConfiguration<SchemaGenerationFeature<?>> configuration,
45        @NonNull DATATYPE_MANAGER datatypeManager) {
46      this.module = module;
47      this.writer = writer;
48      this.datatypeManager = datatypeManager;
49      this.inlineStrategy = IInlineStrategy.newInlineStrategy(configuration);
50      this.moduleIndex = ModuleIndex.indexDefinitions(module, this.inlineStrategy);
51    }
52  
53    @Override
54    public IModule getModule() {
55      return module;
56    }
57  
58    @Override
59    public WRITER getWriter() {
60      return writer;
61    }
62  
63    @NonNull
64    protected DATATYPE_MANAGER getDatatypeManager() {
65      return datatypeManager;
66    }
67  
68    @NonNull
69    public ModuleIndex getMetaschemaIndex() {
70      return moduleIndex;
71    }
72  
73    @Override
74    public boolean isInline(@NonNull IDefinition definition) {
75      return inlineStrategy.isInline(definition, getMetaschemaIndex());
76    }
77  
78    /**
79     * Retrieve any allowed values that are context independent, meaning they always
80     * apply regardless of the location of the node in the larger graph.
81     *
82     * @param definition
83     *          the definition to get allowed values for
84     * @return the list of allowed values or an empty list
85     */
86    @NonNull
87    protected static AllowedValueCollection getContextIndependentEnumeratedValues(
88        @NonNull IValuedDefinition definition) {
89      List<IAllowedValue> values = new LinkedList<>();
90      boolean closed = false;
91      for (IAllowedValuesConstraint constraint : definition.getAllowedValuesConstraints()) {
92        assert constraint != null;
93        if (!constraint.isAllowedOther()) {
94          closed = true;
95        }
96  
97        if (!IMetapathExpression.contextNode().getPath().equals(constraint.getTarget().getPath())) {
98          values = CollectionUtil.emptyList();
99          break;
100       }
101 
102       values.addAll(constraint.getAllowedValues().values());
103     }
104     return new AllowedValueCollection(closed, values);
105   }
106 
107   /**
108    * Get the name of the definition (and any parent instances/definition) to
109    * ensure an inline type is unique.
110    *
111    * @param definition
112    *          the definition to generate a type name for
113    * @param childModule
114    *          the module of the left node
115    * @return the unique type name
116    */
117   private CharSequence getTypeContext(
118       @NonNull IDefinition definition,
119       @NonNull IModule childModule) {
120     StringBuilder builder = new StringBuilder();
121     if (definition.isInline()) {
122       INamedInstance inlineInstance = definition.getInlineInstance();
123       IDefinition parentDefinition = inlineInstance.getContainingDefinition();
124 
125       builder
126           .append(getTypeContext(parentDefinition, childModule))
127           .append(IGenerationState.toCamelCase(inlineInstance.getEffectiveName()));
128     } else {
129       builder.append(IGenerationState.toCamelCase(definition.getName()));
130     }
131     return builder;
132   }
133 
134   @Override
135   @NonNull
136   public String getTypeNameForDefinition(@NonNull IDefinition definition, @Nullable String suffix) {
137     StringBuilder builder = new StringBuilder()
138         .append(IGenerationState.toCamelCase(definition.getModelType().name()))
139         .append(IGenerationState.toCamelCase(definition.getContainingModule().getShortName()));
140 
141     if (isInline(definition)) {
142       builder.append(IGenerationState.toCamelCase(definition.getEffectiveName()));
143     } else {
144       // need to append the parent name(s) to disambiguate this type name
145       builder.append(getTypeContext(definition, definition.getContainingModule()));
146     }
147     if (suffix != null && !suffix.isBlank()) {
148       builder.append(suffix);
149     }
150     builder.append("Type");
151 
152     return ObjectUtils.notNull(builder.toString());
153   }
154 
155   public static class AllowedValueCollection {
156     private final boolean closed;
157     @NonNull
158     private final List<IAllowedValue> values;
159 
160     public AllowedValueCollection(boolean closed, @NonNull List<IAllowedValue> values) {
161       this.closed = closed;
162       this.values = CollectionUtil.unmodifiableList(new ArrayList<>(values));
163     }
164 
165     public boolean isClosed() {
166       return closed;
167     }
168 
169     @NonNull
170     public List<IAllowedValue> getValues() {
171       return values;
172     }
173   }
174 }