diff options
Diffstat (limited to 'config-model/src/main/java/com/yahoo/schema/parser/ConvertSchemaCollection.java')
-rw-r--r-- | config-model/src/main/java/com/yahoo/schema/parser/ConvertSchemaCollection.java | 212 |
1 files changed, 212 insertions, 0 deletions
diff --git a/config-model/src/main/java/com/yahoo/schema/parser/ConvertSchemaCollection.java b/config-model/src/main/java/com/yahoo/schema/parser/ConvertSchemaCollection.java new file mode 100644 index 00000000000..5509d11885c --- /dev/null +++ b/config-model/src/main/java/com/yahoo/schema/parser/ConvertSchemaCollection.java @@ -0,0 +1,212 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.schema.parser; + +import com.yahoo.config.application.api.ApplicationPackage; +import com.yahoo.config.application.api.DeployLogger; +import com.yahoo.config.application.api.FileRegistry; +import com.yahoo.config.model.api.ModelContext; +import com.yahoo.config.model.application.provider.BaseDeployLogger; +import com.yahoo.config.model.application.provider.MockFileRegistry; +import com.yahoo.config.model.deploy.TestProperties; +import com.yahoo.config.model.test.MockApplicationPackage; +import com.yahoo.document.DocumentTypeManager; +import com.yahoo.schema.RankProfileRegistry; +import com.yahoo.schema.Schema; + +import java.util.ArrayList; +import java.util.List; + +/** + * Class converting a collection of schemas from the intermediate format. + * + * @author arnej27959 + **/ +public class ConvertSchemaCollection { + + private final IntermediateCollection input; + private final List<ParsedSchema> orderedInput = new ArrayList<>(); + private final DocumentTypeManager docMan; + private final ApplicationPackage applicationPackage; + private final FileRegistry fileRegistry; + private final DeployLogger deployLogger; + private final ModelContext.Properties properties; + private final RankProfileRegistry rankProfileRegistry; + private final boolean documentsOnly; + + // for unit test + ConvertSchemaCollection(IntermediateCollection input, + DocumentTypeManager documentTypeManager) + { + this(input, documentTypeManager, + MockApplicationPackage.createEmpty(), + new MockFileRegistry(), + new BaseDeployLogger(), + new TestProperties(), + new RankProfileRegistry(), + true); + } + + public ConvertSchemaCollection(IntermediateCollection input, + DocumentTypeManager documentTypeManager, + ApplicationPackage applicationPackage, + FileRegistry fileRegistry, + DeployLogger deployLogger, + ModelContext.Properties properties, + RankProfileRegistry rankProfileRegistry, + boolean documentsOnly) + { + this.input = input; + this.docMan = documentTypeManager; + this.applicationPackage = applicationPackage; + this.fileRegistry = fileRegistry; + this.deployLogger = deployLogger; + this.properties = properties; + this.rankProfileRegistry = rankProfileRegistry; + this.documentsOnly = documentsOnly; + + input.resolveInternalConnections(); + order(); + pushTypesToDocuments(); + } + + void order() { + var map = input.getParsedSchemas(); + for (var schema : map.values()) { + findOrdering(schema); + } + } + + void findOrdering(ParsedSchema schema) { + if (orderedInput.contains(schema)) return; + for (var parent : schema.getAllResolvedInherits()) { + findOrdering(parent); + } + orderedInput.add(schema); + } + + void pushTypesToDocuments() { + for (var schema : orderedInput) { + for (var struct : schema.getStructs()) { + schema.getDocument().addStruct(struct); + } + for (var annotation : schema.getAnnotations()) { + schema.getDocument().addAnnotation(annotation); + } + } + } + + private ConvertParsedTypes typeConverter; + + public void convertTypes() { + typeConverter = new ConvertParsedTypes(orderedInput, docMan); + typeConverter.convert(true); + } + + public List<Schema> convertToSchemas() { + resolveStructInheritance(); + resolveAnnotationInheritance(); + addMissingAnnotationStructs(); + var converter = new ConvertParsedSchemas(orderedInput, + docMan, + applicationPackage, + fileRegistry, + deployLogger, + properties, + rankProfileRegistry, + documentsOnly); + return converter.convertToSchemas(); + } + + private void resolveStructInheritance() { + List<ParsedStruct> all = new ArrayList<>(); + for (var schema : orderedInput) { + var doc = schema.getDocument(); + for (var struct : doc.getStructs()) { + for (String inherit : struct.getInherited()) { + var parent = doc.findParsedStruct(inherit); + if (parent == null) { + throw new IllegalArgumentException("Can not find parent for "+struct+" in "+doc); + } + struct.resolveInherit(inherit, parent); + } + all.add(struct); + } + } + List<String> seen = new ArrayList<>(); + for (ParsedStruct struct : all) { + inheritanceCycleCheck(struct, seen); + } + } + + private void resolveAnnotationInheritance() { + List<ParsedAnnotation> all = new ArrayList(); + for (var schema : orderedInput) { + var doc = schema.getDocument(); + for (var annotation : doc.getAnnotations()) { + for (String inherit : annotation.getInherited()) { + var parent = doc.findParsedAnnotation(inherit); + if (parent == null) { + throw new IllegalArgumentException("Can not find parent for "+annotation+" in "+doc); + } + annotation.resolveInherit(inherit, parent); + } + all.add(annotation); + } + } + List<String> seen = new ArrayList<>(); + for (ParsedAnnotation annotation : all) { + inheritanceCycleCheck(annotation, seen); + } + } + + private void fixupAnnotationStruct(ParsedAnnotation parsed) { + for (var parent : parsed.getResolvedInherits()) { + fixupAnnotationStruct(parent); + parent.getStruct().ifPresent(ps -> { + var myStruct = parsed.ensureStruct(); + if (! myStruct.getInherited().contains(ps.name())) { + myStruct.inherit(ps.name()); + myStruct.resolveInherit(ps.name(), ps); + } + }); + } + } + + private void addMissingAnnotationStructs() { + for (var schema : orderedInput) { + var doc = schema.getDocument(); + for (var annotation : doc.getAnnotations()) { + fixupAnnotationStruct(annotation); + } + } + } + + private void inheritanceCycleCheck(ParsedStruct struct, List<String> seen) { + String name = struct.name(); + if (seen.contains(name)) { + seen.add(name); + throw new IllegalArgumentException("Inheritance/reference cycle for structs: " + + String.join(" -> ", seen)); + } + seen.add(name); + for (ParsedStruct parent : struct.getResolvedInherits()) { + inheritanceCycleCheck(parent, seen); + } + seen.remove(name); + } + + private void inheritanceCycleCheck(ParsedAnnotation annotation, List<String> seen) { + String name = annotation.name(); + if (seen.contains(name)) { + seen.add(name); + throw new IllegalArgumentException("Inheritance/reference cycle for annotations: " + + String.join(" -> ", seen)); + } + seen.add(name); + for (ParsedAnnotation parent : annotation.getResolvedInherits()) { + inheritanceCycleCheck(parent, seen); + } + seen.remove(name); + } + +} |