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}