1
2
3
4
5
6 package dev.metaschema.core.testsupport.builder;
7
8 import static org.mockito.ArgumentMatchers.eq;
9 import static org.mockito.Mockito.doReturn;
10
11 import java.net.URI;
12 import java.util.ArrayList;
13 import java.util.LinkedHashMap;
14 import java.util.List;
15 import java.util.Map;
16 import java.util.stream.Collectors;
17
18 import dev.metaschema.core.model.IAssemblyDefinition;
19 import dev.metaschema.core.model.IFieldDefinition;
20 import dev.metaschema.core.model.IFlagDefinition;
21 import dev.metaschema.core.model.IModule;
22 import dev.metaschema.core.model.ISource;
23 import dev.metaschema.core.qname.IEnhancedQName;
24 import dev.metaschema.core.testsupport.mocking.AbstractMockitoFactory;
25 import dev.metaschema.core.util.CollectionUtil;
26 import dev.metaschema.core.util.ObjectUtils;
27 import edu.umd.cs.findbugs.annotations.NonNull;
28 import edu.umd.cs.findbugs.annotations.Nullable;
29
30
31
32
33 final class ModuleBuilder
34 extends AbstractMockitoFactory
35 implements IModuleBuilder {
36
37 private String namespace;
38 private String shortName;
39 private String version;
40 private ISource source;
41 private final List<IFlagBuilder> flagBuilders = new ArrayList<>();
42 private final List<IFieldBuilder> fieldBuilders = new ArrayList<>();
43 private final List<IAssemblyBuilder> assemblyBuilders = new ArrayList<>();
44
45 ModuleBuilder() {
46
47 }
48
49 @Override
50 @NonNull
51 public IModuleBuilder reset() {
52 this.namespace = null;
53 this.shortName = null;
54 this.version = null;
55 this.source = null;
56 this.flagBuilders.clear();
57 this.fieldBuilders.clear();
58 this.assemblyBuilders.clear();
59 return this;
60 }
61
62 @Override
63 @NonNull
64 public IModuleBuilder namespace(@NonNull String namespace) {
65 this.namespace = namespace;
66 return this;
67 }
68
69 @Override
70 @NonNull
71 public IModuleBuilder shortName(@NonNull String shortName) {
72 this.shortName = shortName;
73 return this;
74 }
75
76 @Override
77 @NonNull
78 public IModuleBuilder version(@NonNull String version) {
79 this.version = version;
80 return this;
81 }
82
83 @Override
84 @NonNull
85 public IModuleBuilder source(@NonNull ISource source) {
86 this.source = source;
87 return this;
88 }
89
90 @Override
91 @NonNull
92 public IModuleBuilder flag(@Nullable IFlagBuilder flag) {
93 if (flag != null) {
94 this.flagBuilders.add(flag);
95 }
96 return this;
97 }
98
99 @Override
100 @NonNull
101 public IModuleBuilder field(@Nullable IFieldBuilder field) {
102 if (field != null) {
103 this.fieldBuilders.add(field);
104 }
105 return this;
106 }
107
108 @Override
109 @NonNull
110 public IModuleBuilder assembly(@Nullable IAssemblyBuilder assembly) {
111 if (assembly != null) {
112 this.assemblyBuilders.add(assembly);
113 }
114 return this;
115 }
116
117
118
119
120 private void validate() {
121 ObjectUtils.requireNonNull(namespace, "namespace");
122 ObjectUtils.requireNonNull(shortName, "shortName");
123 ObjectUtils.requireNonNull(version, "version");
124 ObjectUtils.requireNonNull(source, "source");
125 }
126
127 @Override
128 @NonNull
129 public IModule toModule() {
130 validate();
131
132 IModule module = mock(IModule.class);
133
134
135 URI namespaceUri = URI.create(ObjectUtils.notNull(namespace));
136 doReturn(namespaceUri).when(module).getXmlNamespace();
137 doReturn(namespaceUri).when(module).getJsonBaseUri();
138 doReturn(shortName).when(module).getShortName();
139 doReturn(version).when(module).getVersion();
140 doReturn(source).when(module).getSource();
141
142
143 URI sourceUri = source.getSource();
144 doReturn(sourceUri).when(module).getLocation();
145 String locationHint = sourceUri != null ? sourceUri.toString() : shortName;
146 doReturn(locationHint).when(module).getLocationHint();
147
148
149 IEnhancedQName qname = IEnhancedQName.of(namespace, shortName);
150 doReturn(qname).when(module).getQName();
151
152
153 doReturn(CollectionUtil.emptyList()).when(module).getImportedModules();
154 doReturn(null).when(module).getImportedModuleByShortName(org.mockito.ArgumentMatchers.anyString());
155
156
157 doReturn(null).when(module).getName();
158 doReturn(null).when(module).getRemarks();
159
160
161 buildDefinitions(module);
162
163 return module;
164 }
165
166
167
168
169
170
171
172 private void buildDefinitions(@NonNull IModule module) {
173 String moduleNamespace = ObjectUtils.notNull(namespace);
174 ISource moduleSource = ObjectUtils.notNull(source);
175
176
177 List<IFlagDefinition> flagDefs = new ArrayList<>();
178 for (IFlagBuilder builder : flagBuilders) {
179 IFlagDefinition def = builder
180 .namespace(moduleNamespace)
181 .source(moduleSource)
182 .toDefinition(module);
183 flagDefs.add(def);
184 }
185 doReturn(CollectionUtil.unmodifiableList(flagDefs)).when(module).getFlagDefinitions();
186
187 for (IFlagDefinition def : flagDefs) {
188 IEnhancedQName qname = def.getDefinitionQName();
189 doReturn(def).when(module).getFlagDefinitionByName(eq(qname));
190 }
191
192
193 Map<String, IFieldDefinition> fieldDefsByName = new LinkedHashMap<>();
194 for (IFieldBuilder builder : fieldBuilders) {
195 IFieldDefinition def = builder
196 .namespace(moduleNamespace)
197 .source(moduleSource)
198 .toDefinition(module);
199 fieldDefsByName.put(def.getName(), def);
200 }
201 List<IFieldDefinition> fieldDefs = new ArrayList<>(fieldDefsByName.values());
202 doReturn(CollectionUtil.unmodifiableList(fieldDefs)).when(module).getFieldDefinitions();
203
204
205 for (IFieldDefinition def : fieldDefs) {
206 Integer index = def.getDefinitionQName().getIndexPosition();
207 doReturn(def).when(module).getFieldDefinitionByName(eq(index));
208 }
209
210
211 boolean hasReferences = assemblyBuilders.stream()
212 .filter(AssemblyBuilder.class::isInstance)
213 .map(AssemblyBuilder.class::cast)
214 .anyMatch(AssemblyBuilder::hasModelReferences);
215
216
217 Map<String, IAssemblyDefinition> assemblyDefsByName = new LinkedHashMap<>();
218 Map<AssemblyBuilder, IAssemblyDefinition> builderToDefMap = new LinkedHashMap<>();
219
220 if (hasReferences) {
221
222 for (IAssemblyBuilder builder : assemblyBuilders) {
223 if (!(builder instanceof AssemblyBuilder)) {
224 throw new IllegalStateException(
225 "Two-phase construction requires AssemblyBuilder instances, got: " + builder.getClass().getName());
226 }
227 AssemblyBuilder ab = (AssemblyBuilder) builder;
228 ab.namespace(moduleNamespace).source(moduleSource);
229 IAssemblyDefinition def = ab.toDefinitionShell(module);
230 assemblyDefsByName.put(def.getName(), def);
231 builderToDefMap.put(ab, def);
232 }
233
234
235 for (Map.Entry<AssemblyBuilder, IAssemblyDefinition> entry : builderToDefMap.entrySet()) {
236 AssemblyBuilder ab = entry.getKey();
237 IAssemblyDefinition def = entry.getValue();
238 ab.resolveModelInstances(def, assemblyDefsByName, fieldDefsByName);
239 }
240 } else {
241
242 for (IAssemblyBuilder builder : assemblyBuilders) {
243 IAssemblyDefinition def = builder
244 .namespace(moduleNamespace)
245 .source(moduleSource)
246 .toDefinition(module);
247 assemblyDefsByName.put(def.getName(), def);
248 }
249 }
250
251 List<IAssemblyDefinition> assemblyDefs = new ArrayList<>(assemblyDefsByName.values());
252 doReturn(CollectionUtil.unmodifiableList(assemblyDefs)).when(module).getAssemblyDefinitions();
253
254
255 for (IAssemblyDefinition def : assemblyDefs) {
256 Integer index = def.getDefinitionQName().getIndexPosition();
257 doReturn(def).when(module).getAssemblyDefinitionByName(eq(index));
258 }
259
260
261 doReturn(CollectionUtil.unmodifiableList(flagDefs)).when(module).getExportedFlagDefinitions();
262 doReturn(CollectionUtil.unmodifiableList(fieldDefs)).when(module).getExportedFieldDefinitions();
263 doReturn(CollectionUtil.unmodifiableList(assemblyDefs)).when(module).getExportedAssemblyDefinitions();
264
265
266 List<IAssemblyDefinition> rootDefs = assemblyDefs.stream()
267 .filter(def -> def.getRootQName() != null)
268 .collect(Collectors.toList());
269 doReturn(CollectionUtil.unmodifiableList(rootDefs)).when(module).getRootAssemblyDefinitions();
270 doReturn(CollectionUtil.unmodifiableList(rootDefs)).when(module).getExportedRootAssemblyDefinitions();
271
272
273 for (IAssemblyDefinition rootDef : rootDefs) {
274 IEnhancedQName rootQName = rootDef.getRootQName();
275 if (rootQName != null) {
276 Integer rootIndex = rootQName.getIndexPosition();
277 doReturn(rootDef).when(module).getExportedRootAssemblyDefinitionByName(eq(rootIndex));
278 }
279 }
280
281
282 List<Object> assemblyAndFieldDefs = new ArrayList<>();
283 assemblyAndFieldDefs.addAll(assemblyDefs);
284 assemblyAndFieldDefs.addAll(fieldDefs);
285 doReturn(CollectionUtil.unmodifiableList(assemblyAndFieldDefs)).when(module).getAssemblyAndFieldDefinitions();
286
287
288 for (IFlagDefinition def : flagDefs) {
289 IEnhancedQName qname = def.getDefinitionQName();
290 doReturn(def).when(module).getScopedFlagDefinitionByName(eq(qname));
291 }
292 for (IFieldDefinition def : fieldDefs) {
293 Integer index = def.getDefinitionQName().getIndexPosition();
294 doReturn(def).when(module).getScopedFieldDefinitionByName(eq(index));
295 }
296 for (IAssemblyDefinition def : assemblyDefs) {
297 Integer index = def.getDefinitionQName().getIndexPosition();
298 doReturn(def).when(module).getScopedAssemblyDefinitionByName(eq(index));
299 }
300 }
301 }