001/*
002 * SPDX-FileCopyrightText: none
003 * SPDX-License-Identifier: CC0-1.0
004 */
005
006package gov.nist.secauto.metaschema.schemagen;
007
008import gov.nist.secauto.metaschema.core.configuration.IConfiguration;
009import gov.nist.secauto.metaschema.core.metapath.MetapathExpression;
010import gov.nist.secauto.metaschema.core.model.IDefinition;
011import gov.nist.secauto.metaschema.core.model.IModule;
012import gov.nist.secauto.metaschema.core.model.INamedInstance;
013import gov.nist.secauto.metaschema.core.model.IValuedDefinition;
014import gov.nist.secauto.metaschema.core.model.constraint.IAllowedValue;
015import gov.nist.secauto.metaschema.core.model.constraint.IAllowedValuesConstraint;
016import gov.nist.secauto.metaschema.core.util.CollectionUtil;
017import gov.nist.secauto.metaschema.core.util.ObjectUtils;
018import gov.nist.secauto.metaschema.schemagen.datatype.IDatatypeManager;
019
020import java.util.ArrayList;
021import java.util.LinkedList;
022import java.util.List;
023
024import edu.umd.cs.findbugs.annotations.NonNull;
025import edu.umd.cs.findbugs.annotations.Nullable;
026
027public abstract class AbstractGenerationState<WRITER, DATATYPE_MANAGER extends IDatatypeManager>
028    implements IGenerationState<WRITER> {
029  @NonNull
030  private final IModule module;
031  @NonNull
032  private final WRITER writer;
033  @NonNull
034  private final DATATYPE_MANAGER datatypeManager;
035  @NonNull
036  private final IInlineStrategy inlineStrategy;
037
038  @NonNull
039  private final ModuleIndex moduleIndex;
040
041  public AbstractGenerationState(
042      @NonNull IModule module,
043      @NonNull WRITER writer,
044      @NonNull IConfiguration<SchemaGenerationFeature<?>> configuration,
045      @NonNull DATATYPE_MANAGER datatypeManager) {
046    this.module = module;
047    this.writer = writer;
048    this.datatypeManager = datatypeManager;
049    this.inlineStrategy = IInlineStrategy.newInlineStrategy(configuration);
050    this.moduleIndex = ModuleIndex.indexDefinitions(module, this.inlineStrategy);
051  }
052
053  @Override
054  public IModule getModule() {
055    return module;
056  }
057
058  @Override
059  public WRITER getWriter() {
060    return writer;
061  }
062
063  @NonNull
064  protected DATATYPE_MANAGER getDatatypeManager() {
065    return datatypeManager;
066  }
067
068  @NonNull
069  public ModuleIndex getMetaschemaIndex() {
070    return moduleIndex;
071  }
072
073  @Override
074  public boolean isInline(@NonNull IDefinition definition) {
075    return inlineStrategy.isInline(definition, getMetaschemaIndex());
076  }
077
078  /**
079   * Retrieve any allowed values that are context independent, meaning they always
080   * apply regardless of the location of the node in the larger graph.
081   *
082   * @param definition
083   *          the definition to get allowed values for
084   * @return the list of allowed values or an empty list
085   */
086  @NonNull
087  protected static AllowedValueCollection getContextIndependentEnumeratedValues(
088      @NonNull IValuedDefinition definition) {
089    List<IAllowedValue> values = new LinkedList<>();
090    boolean closed = false;
091    for (IAllowedValuesConstraint constraint : definition.getAllowedValuesConstraints()) {
092      if (!constraint.isAllowedOther()) {
093        closed = true;
094      }
095
096      if (!MetapathExpression.CONTEXT_NODE.getPath().equals(constraint.getTarget())) {
097        values = CollectionUtil.emptyList();
098        break;
099      }
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}