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.model.IAssemblyDefinition; 031import gov.nist.secauto.metaschema.core.model.IDefinition; 032import gov.nist.secauto.metaschema.core.model.IModule; 033import gov.nist.secauto.metaschema.core.util.ObjectUtils; 034import gov.nist.secauto.metaschema.schemagen.datatype.IDatatypeManager; 035 036import java.io.Writer; 037import java.util.LinkedList; 038import java.util.List; 039import java.util.function.BiConsumer; 040 041import edu.umd.cs.findbugs.annotations.NonNull; 042import edu.umd.cs.findbugs.annotations.Nullable; 043 044/** 045 * Thsi abstract class provides a common implementation shared by all schema 046 * generators. 047 * 048 * @param <T> 049 * the writer type 050 * @param <D> 051 * the {@link IDatatypeManager} type 052 * @param <S> 053 * the {@link IGenerationState} type 054 */ 055public abstract class AbstractSchemaGenerator< 056 T extends AutoCloseable, 057 D extends IDatatypeManager, 058 S extends AbstractGenerationState< 059 T, D>> 060 implements ISchemaGenerator { 061 062 /** 063 * Create a new writer to use to write the schema. 064 * 065 * @param out 066 * the {@link Writer} to write the schema content to 067 * @return the schema writer 068 * @throws SchemaGenerationException 069 * if an error occurred while creating the writer 070 */ 071 @NonNull 072 protected abstract T newWriter(@NonNull Writer out); 073 074 /** 075 * Create a new schema generation state object. 076 * 077 * @param module 078 * the Metaschema module to generate the schema for 079 * @param schemaWriter 080 * the writer to use to write the schema 081 * @param configuration 082 * the generation configuration 083 * @return the schema generation state used for context and writing 084 * @throws SchemaGenerationException 085 * if an error occurred while creating the generation state object 086 */ 087 @NonNull 088 protected abstract S newGenerationState( 089 @NonNull IModule module, 090 @NonNull T schemaWriter, 091 @NonNull IConfiguration<SchemaGenerationFeature<?>> configuration); 092 093 /** 094 * Called to generate the actual schema content. 095 * 096 * @param generationState 097 * the generation state object 098 */ 099 protected abstract void generateSchema(@NonNull S generationState); 100 101 @Override 102 public void generateFromModule( 103 IModule metaschema, 104 Writer out, 105 IConfiguration<SchemaGenerationFeature<?>> configuration) { 106 // IInlineStrategy inlineStrategy = 107 // IInlineStrategy.newInlineStrategy(configuration); 108 try { 109 // avoid automatically closing streams not owned by the generator 110 @SuppressWarnings("PMD.CloseResource") T schemaWriter = newWriter(out); 111 S generationState = newGenerationState(metaschema, schemaWriter, configuration); 112 generateSchema(generationState); 113 generationState.flushWriter(); 114 } catch (SchemaGenerationException ex) { // NOPMD avoid nesting same exception 115 throw ex; 116 } catch (Exception ex) { // NOPMD need to catch close exception 117 throw new SchemaGenerationException(ex); 118 } 119 } 120 121 /** 122 * Determine the collection of root definitions. 123 * 124 * @param generationState 125 * the schema generation state used for context and writing 126 * @param handler 127 * a callback to execute on each identified root definition 128 * @return the list of identified root definitions 129 */ 130 protected List<IAssemblyDefinition> analyzeDefinitions( 131 @NonNull S generationState, 132 @Nullable BiConsumer<ModuleIndex.DefinitionEntry, IDefinition> handler) { 133 // TODO: use of handler here is confusing and introduces side effects. Consider 134 // refactoring this in 135 // the caller 136 137 List<IAssemblyDefinition> rootAssemblyDefinitions = new LinkedList<>(); 138 for (ModuleIndex.DefinitionEntry entry : generationState.getMetaschemaIndex().getDefinitions()) { 139 140 boolean referenced = entry.isReferenced(); 141 142 IDefinition definition = ObjectUtils.notNull(entry.getDefinition()); 143 if (definition instanceof IAssemblyDefinition && ((IAssemblyDefinition) definition).isRoot()) { 144 // found root definition 145 IAssemblyDefinition assemblyDefinition = (IAssemblyDefinition) definition; 146 rootAssemblyDefinitions.add(assemblyDefinition); 147 if (!referenced) { 148 referenced = true; 149 entry.incrementReferenceCount(); 150 } 151 152 } 153 154 if (!referenced) { 155 // skip unreferenced definitions 156 continue; 157 } 158 159 if (handler != null) { 160 handler.accept(entry, definition); 161 } 162 } 163 return rootAssemblyDefinitions; 164 } 165 166}