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.MetapathExpression;
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        if (!constraint.isAllowedOther()) {
93          closed = true;
94        }
95  
96        if (!MetapathExpression.CONTEXT_NODE.getPath().equals(constraint.getTarget())) {
97          values = CollectionUtil.emptyList();
98          break;
99        }
100 
101       values.addAll(constraint.getAllowedValues().values());
102     }
103     return new AllowedValueCollection(closed, values);
104   }
105 
106   /**
107    * Get the name of the definition (and any parent instances/definition) to
108    * ensure an inline type is unique.
109    *
110    * @param definition
111    *          the definition to generate a type name for
112    * @param childModule
113    *          the module of the left node
114    * @return the unique type name
115    */
116   private CharSequence getTypeContext(
117       @NonNull IDefinition definition,
118       @NonNull IModule childModule) {
119     StringBuilder builder = new StringBuilder();
120     if (definition.isInline()) {
121       INamedInstance inlineInstance = definition.getInlineInstance();
122       IDefinition parentDefinition = inlineInstance.getContainingDefinition();
123 
124       builder
125           .append(getTypeContext(parentDefinition, childModule))
126           .append(IGenerationState.toCamelCase(inlineInstance.getEffectiveName()));
127     } else {
128       builder.append(IGenerationState.toCamelCase(definition.getName()));
129     }
130     return builder;
131   }
132 
133   @Override
134   @NonNull
135   public String getTypeNameForDefinition(@NonNull IDefinition definition, @Nullable String suffix) {
136     StringBuilder builder = new StringBuilder()
137         .append(IGenerationState.toCamelCase(definition.getModelType().name()))
138         .append(IGenerationState.toCamelCase(definition.getContainingModule().getShortName()));
139 
140     if (isInline(definition)) {
141       builder.append(IGenerationState.toCamelCase(definition.getEffectiveName()));
142     } else {
143       // need to append the parent name(s) to disambiguate this type name
144       builder.append(getTypeContext(definition, definition.getContainingModule()));
145     }
146     if (suffix != null && !suffix.isBlank()) {
147       builder.append(suffix);
148     }
149     builder.append("Type");
150 
151     return ObjectUtils.notNull(builder.toString());
152   }
153 
154   public static class AllowedValueCollection {
155     private final boolean closed;
156     @NonNull
157     private final List<IAllowedValue> values;
158 
159     public AllowedValueCollection(boolean closed, @NonNull List<IAllowedValue> values) {
160       this.closed = closed;
161       this.values = CollectionUtil.unmodifiableList(new ArrayList<>(values));
162     }
163 
164     public boolean isClosed() {
165       return closed;
166     }
167 
168     @NonNull
169     public List<IAllowedValue> getValues() {
170       return values;
171     }
172   }
173 }