001/* 002 * Portions of this software was developed by employees of the National Institute 003 * of Standards and Technology (NIST), an agency of the Federal Government and is 004 * being made available as a public service. Pursuant to title 17 United States 005 * Code Section 105, works of NIST employees are not subject to copyright 006 * protection in the United States. This software may be subject to foreign 007 * copyright. Permission in the United States and in foreign countries, to the 008 * extent that NIST may hold copyright, to use, copy, modify, create derivative 009 * works, and distribute this software and its documentation without fee is hereby 010 * granted on a non-exclusive basis, provided that this notice and disclaimer 011 * of warranty appears in all copies. 012 * 013 * THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER 014 * EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY 015 * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF 016 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM 017 * INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE 018 * SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE. IN NO EVENT 019 * SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT, 020 * INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM, 021 * OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY, 022 * CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR 023 * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT 024 * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER. 025 */ 026 027package gov.nist.secauto.metaschema.schemagen; 028 029import gov.nist.secauto.metaschema.core.configuration.IConfiguration; 030import gov.nist.secauto.metaschema.core.metapath.MetapathExpression; 031import gov.nist.secauto.metaschema.core.model.IDefinition; 032import gov.nist.secauto.metaschema.core.model.IModule; 033import gov.nist.secauto.metaschema.core.model.INamedInstance; 034import gov.nist.secauto.metaschema.core.model.IValuedDefinition; 035import gov.nist.secauto.metaschema.core.model.constraint.IAllowedValue; 036import gov.nist.secauto.metaschema.core.model.constraint.IAllowedValuesConstraint; 037import gov.nist.secauto.metaschema.core.util.CollectionUtil; 038import gov.nist.secauto.metaschema.core.util.ObjectUtils; 039import gov.nist.secauto.metaschema.schemagen.datatype.IDatatypeManager; 040 041import java.util.ArrayList; 042import java.util.LinkedList; 043import java.util.List; 044import java.util.Locale; 045 046import edu.umd.cs.findbugs.annotations.NonNull; 047import edu.umd.cs.findbugs.annotations.Nullable; 048 049public abstract class AbstractGenerationState<WRITER, DATATYPE_MANAGER extends IDatatypeManager> 050 implements IGenerationState<WRITER> { 051 @NonNull 052 private final IModule module; 053 @NonNull 054 private final WRITER writer; 055 @NonNull 056 private final DATATYPE_MANAGER datatypeManager; 057 @NonNull 058 private final IInlineStrategy inlineStrategy; 059 060 @NonNull 061 private final ModuleIndex moduleIndex; 062 063 public AbstractGenerationState( 064 @NonNull IModule module, 065 @NonNull WRITER writer, 066 @NonNull IConfiguration<SchemaGenerationFeature<?>> configuration, 067 @NonNull DATATYPE_MANAGER datatypeManager) { 068 this.module = module; 069 this.writer = writer; 070 this.datatypeManager = datatypeManager; 071 this.inlineStrategy = IInlineStrategy.newInlineStrategy(configuration); 072 this.moduleIndex = ModuleIndex.indexDefinitions(module); 073 } 074 075 @Override 076 public IModule getModule() { 077 return module; 078 } 079 080 @Override 081 public WRITER getWriter() { 082 return writer; 083 } 084 085 @NonNull 086 protected DATATYPE_MANAGER getDatatypeManager() { 087 return datatypeManager; 088 } 089 090 @NonNull 091 public ModuleIndex getMetaschemaIndex() { 092 return moduleIndex; 093 } 094 095 @Override 096 public boolean isInline(@NonNull IDefinition definition) { 097 return inlineStrategy.isInline(definition, getMetaschemaIndex()); 098 } 099 100 /** 101 * Retrieve any allowed values that are context independent, meaning they always 102 * apply regardless of the location of the node in the larger graph. 103 * 104 * @param definition 105 * the definition to get allowed values for 106 * @return the list of allowed values or an empty list 107 */ 108 @NonNull 109 protected static AllowedValueCollection getContextIndependentEnumeratedValues( 110 @NonNull IValuedDefinition definition) { 111 List<IAllowedValue> values = new LinkedList<>(); 112 boolean closed = false; 113 for (IAllowedValuesConstraint constraint : definition.getAllowedValuesConstraints()) { 114 if (!constraint.isAllowedOther()) { 115 closed = true; 116 } 117 118 if (!MetapathExpression.CONTEXT_NODE.equals(constraint.getTarget())) { 119 values = CollectionUtil.emptyList(); 120 break; 121 } 122 123 values.addAll(constraint.getAllowedValues().values()); 124 } 125 return new AllowedValueCollection(closed, values); 126 } 127 128 /** 129 * Get the name of the definition (and any parent instances/definition) to 130 * ensure an inline type is unique. 131 * 132 * @param definition 133 * the definition to generate a type name for 134 * @param childModule 135 * the module of the left node 136 * @return the unique type name 137 */ 138 private CharSequence getTypeContext( 139 @NonNull IDefinition definition, 140 @NonNull IModule childModule) { 141 StringBuilder builder = new StringBuilder(); 142 if (definition.isInline()) { 143 INamedInstance inlineInstance = definition.getInlineInstance(); 144 IDefinition parentDefinition = inlineInstance.getContainingDefinition(); 145 146 builder 147 .append(getTypeContext(parentDefinition, childModule)) 148 .append(toCamelCase(inlineInstance.getEffectiveName())); 149 } else { 150 builder.append(toCamelCase(definition.getEffectiveName())); 151 } 152 return builder; 153 } 154 155 @NonNull 156 public String getTypeNameForDefinition(@NonNull IDefinition definition, @Nullable String suffix) { 157 StringBuilder builder = new StringBuilder() 158 .append(toCamelCase(definition.getModelType().name())) 159 .append(toCamelCase(definition.getContainingModule().getShortName())); 160 161 if (isInline(definition)) { 162 builder.append(toCamelCase(definition.getEffectiveName())); 163 } else { 164 // need to append the parent name(s) to disambiguate this type name 165 builder.append(getTypeContext(definition, definition.getContainingModule())); 166 } 167 if (suffix != null && !suffix.isBlank()) { 168 builder.append(toCamelCase(suffix)); 169 } 170 builder.append("Type"); 171 172 return ObjectUtils.notNull(builder.toString()); 173 } 174 175 @NonNull 176 protected static CharSequence toCamelCase(String text) { 177 StringBuilder builder = new StringBuilder(); 178 for (String segment : text.split("\\p{Punct}")) { 179 if (segment.length() > 0) { 180 builder.append(segment.substring(0, 1).toUpperCase(Locale.ROOT)); 181 } 182 if (segment.length() > 1) { 183 builder.append(segment.substring(1).toLowerCase(Locale.ROOT)); 184 } 185 } 186 return builder; 187 } 188 189 public static class AllowedValueCollection { 190 private final boolean closed; 191 @NonNull 192 private final List<IAllowedValue> values; 193 194 public AllowedValueCollection(boolean closed, @NonNull List<IAllowedValue> values) { 195 this.closed = closed; 196 this.values = CollectionUtil.unmodifiableList(new ArrayList<>(values)); 197 } 198 199 public boolean isClosed() { 200 return closed; 201 } 202 203 @NonNull 204 public List<IAllowedValue> getValues() { 205 return values; 206 } 207 } 208}