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.IMetapathExpression; 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 assert constraint != null; 093 if (!constraint.isAllowedOther()) { 094 closed = true; 095 } 096 097 // FIXME: Should this compare the actual compiled expression? 098 if (!IMetapathExpression.contextNode().getPath().equals(constraint.getTarget().getPath())) { 099 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}