1 /*
2 * Portions of this software was developed by employees of the National Institute
3 * of Standards and Technology (NIST), an agency of the Federal Government and is
4 * being made available as a public service. Pursuant to title 17 United States
5 * Code Section 105, works of NIST employees are not subject to copyright
6 * protection in the United States. This software may be subject to foreign
7 * copyright. Permission in the United States and in foreign countries, to the
8 * extent that NIST may hold copyright, to use, copy, modify, create derivative
9 * works, and distribute this software and its documentation without fee is hereby
10 * granted on a non-exclusive basis, provided that this notice and disclaimer
11 * of warranty appears in all copies.
12 *
13 * THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER
14 * EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY
15 * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF
16 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM
17 * INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE
18 * SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE. IN NO EVENT
19 * SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT,
20 * INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM,
21 * OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY,
22 * CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR
23 * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT
24 * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER.
25 */
26
27 package gov.nist.secauto.metaschema.schemagen;
28
29 import gov.nist.secauto.metaschema.core.configuration.IConfiguration;
30 import gov.nist.secauto.metaschema.core.model.IAssemblyDefinition;
31 import gov.nist.secauto.metaschema.core.model.IDefinition;
32 import gov.nist.secauto.metaschema.core.model.IModule;
33 import gov.nist.secauto.metaschema.core.util.ObjectUtils;
34 import gov.nist.secauto.metaschema.schemagen.datatype.IDatatypeManager;
35
36 import java.io.Writer;
37 import java.util.LinkedList;
38 import java.util.List;
39 import java.util.function.BiConsumer;
40
41 import edu.umd.cs.findbugs.annotations.NonNull;
42 import edu.umd.cs.findbugs.annotations.Nullable;
43
44 /**
45 * Thsi abstract class provides a common implementation shared by all schema
46 * generators.
47 *
48 * @param <T>
49 * the writer type
50 * @param <D>
51 * the {@link IDatatypeManager} type
52 * @param <S>
53 * the {@link IGenerationState} type
54 */
55 public abstract class AbstractSchemaGenerator<
56 T extends AutoCloseable,
57 D extends IDatatypeManager,
58 S extends AbstractGenerationState<
59 T, D>>
60 implements ISchemaGenerator {
61
62 /**
63 * Create a new writer to use to write the schema.
64 *
65 * @param out
66 * the {@link Writer} to write the schema content to
67 * @return the schema writer
68 * @throws SchemaGenerationException
69 * if an error occurred while creating the writer
70 */
71 @NonNull
72 protected abstract T newWriter(@NonNull Writer out);
73
74 /**
75 * Create a new schema generation state object.
76 *
77 * @param module
78 * the Metaschema module to generate the schema for
79 * @param schemaWriter
80 * the writer to use to write the schema
81 * @param configuration
82 * the generation configuration
83 * @return the schema generation state used for context and writing
84 * @throws SchemaGenerationException
85 * if an error occurred while creating the generation state object
86 */
87 @NonNull
88 protected abstract S newGenerationState(
89 @NonNull IModule module,
90 @NonNull T schemaWriter,
91 @NonNull IConfiguration<SchemaGenerationFeature<?>> configuration);
92
93 /**
94 * Called to generate the actual schema content.
95 *
96 * @param generationState
97 * the generation state object
98 */
99 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 }