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