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