diff options
Diffstat (limited to 'config-model/src/main/java/com/yahoo/schema/parser/ConvertParsedTypes.java')
-rw-r--r-- | config-model/src/main/java/com/yahoo/schema/parser/ConvertParsedTypes.java | 337 |
1 files changed, 337 insertions, 0 deletions
diff --git a/config-model/src/main/java/com/yahoo/schema/parser/ConvertParsedTypes.java b/config-model/src/main/java/com/yahoo/schema/parser/ConvertParsedTypes.java new file mode 100644 index 00000000000..9f1203ffc9f --- /dev/null +++ b/config-model/src/main/java/com/yahoo/schema/parser/ConvertParsedTypes.java @@ -0,0 +1,337 @@ +// 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.document.DataType; +import com.yahoo.document.DocumentType; +import com.yahoo.document.DocumentTypeManager; +import com.yahoo.document.PositionDataType; +import com.yahoo.document.StructDataType; +import com.yahoo.document.WeightedSetDataType; +import com.yahoo.document.annotation.AnnotationReferenceDataType; +import com.yahoo.documentmodel.NewDocumentReferenceDataType; +import com.yahoo.documentmodel.OwnedStructDataType; +import com.yahoo.schema.document.annotation.SDAnnotationType; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Helper class for converting ParsedType instances to DataType + * + * @author arnej27959 + **/ +public class ConvertParsedTypes { + + private final List<ParsedSchema> orderedInput; + private final DocumentTypeManager docMan; + + ConvertParsedTypes(List<ParsedSchema> input) { + this.orderedInput = input; + this.docMan = new DocumentTypeManager(); + } + + public ConvertParsedTypes(List<ParsedSchema> input, DocumentTypeManager docMan) { + this.orderedInput = input; + this.docMan = docMan; + } + + public void convert(boolean andRegister) { + startDataTypes(); + fillDataTypes(); + if (andRegister) { + registerDataTypes(); + } + } + + private Map<String, DocumentType> documentsFromSchemas = new HashMap<>(); + private Map<String, StructDataType> structsFromSchemas = new HashMap<>(); + private Map<String, SDAnnotationType> annotationsFromSchemas = new HashMap<>(); + + private void startDataTypes() { + for (var schema : orderedInput) { + String name = schema.getDocument().name(); + documentsFromSchemas.put(name, new DocumentType(name)); + } + for (var schema : orderedInput) { + var doc = schema.getDocument(); + for (var struct : doc.getStructs()) { + String structId = doc.name() + "->" + struct.name(); + var dt = new OwnedStructDataType(struct.name(), doc.name()); + structsFromSchemas.put(structId, dt); + } + for (var annotation : doc.getAnnotations()) { + String annId = doc.name() + "->" + annotation.name(); + var at = new SDAnnotationType(annotation.name()); + annotationsFromSchemas.put(annId, at); + for (String inherit : annotation.getInherited()) { + at.inherit(inherit); + } + var withStruct = annotation.getStruct(); + if (withStruct.isPresent()) { + ParsedStruct struct = withStruct.get(); + String structId = doc.name() + "->" + struct.name(); + var old = structsFromSchemas.put(structId, new OwnedStructDataType(struct.name(), doc.name())); + assert(old == null); + } + } + } + } + + void fillAnnotationStruct(ParsedAnnotation annotation) { + var withStruct = annotation.getStruct(); + if (withStruct.isPresent()) { + var doc = annotation.getOwnerDoc(); + var toFill = findStructFromParsed(withStruct.get()); + for (ParsedField field : withStruct.get().getFields()) { + var t = resolveFromContext(field.getType(), doc); + var f = field.hasIdOverride() + ? new com.yahoo.document.Field(field.name(), field.idOverride(), t) + : new com.yahoo.document.Field(field.name(), t); + toFill.addField(f); + } + for (var parent : annotation.getResolvedInherits()) { + parent.getStruct().ifPresent + (ps -> toFill.inherit(findStructFromParsed(ps))); + } + var at = findAnnotationFromParsed(annotation); + at.setDataType(toFill); + } + } + + private void fillDataTypes() { + for (var schema : orderedInput) { + var doc = schema.getDocument(); + for (var annotation : doc.getAnnotations()) { + var at = findAnnotationFromParsed(annotation); + for (var parent : annotation.getResolvedInherits()) { + at.inherit(findAnnotationFromParsed(parent)); + } + fillAnnotationStruct(annotation); + } + for (var struct : doc.getStructs()) { + var toFill = findStructFromParsed(struct); + // evil ugliness + for (ParsedField field : struct.getFields()) { + if (! field.hasIdOverride()) { + var t = resolveFromContext(field.getType(), doc); + var f = new com.yahoo.document.Field(field.name(), t); + toFill.addField(f); + } + } + for (ParsedField field : struct.getFields()) { + if (field.hasIdOverride()) { + var t = resolveFromContext(field.getType(), doc); + var f = new com.yahoo.document.Field(field.name(), field.idOverride(), t); + toFill.addField(f); + } + } + for (var inherit : struct.getResolvedInherits()) { + var parent = findStructFromParsed(inherit); + // ensure a nice, compatible exception message + for (var field : toFill.getFields()) { + if (parent.hasField(field)) { + for (var base : parent.getInheritedTypes()) { + if (base.hasField(field)) { + parent = base; + } + } + throw new IllegalArgumentException + ("In document " + doc.name() + ": struct " + struct.name() + + " cannot inherit from " + parent.getName() + " and redeclare field " + field.getName()); + } + } + toFill.inherit(parent); + } + } + var docToFill = documentsFromSchemas.get(doc.name()); + Map<String, Collection<String>> fieldSets = new HashMap<>(); + List<String> inDocFields = new ArrayList<>(); + for (var docField : doc.getFields()) { + String name = docField.name(); + var t = resolveFromContext(docField.getType(), doc); + var f = new com.yahoo.document.Field(docField.name(), t); + docToFill.addField(f); + if (docField.hasIdOverride()) { + f.setId(docField.idOverride(), docToFill); + } + inDocFields.add(name); + } + fieldSets.put("[document]", inDocFields); + for (var extraField : schema.getFields()) { + String name = extraField.name(); + if (docToFill.hasField(name)) continue; + var t = resolveFromContext(extraField.getType(), doc); + var f = new com.yahoo.document.Field(name, t); + docToFill.addField(f); + } + for (var fieldset : schema.getFieldSets()) { + fieldSets.put(fieldset.name(), fieldset.getFieldNames()); + } + docToFill.addFieldSets(fieldSets); + for (String inherit : doc.getInherited()) { + docToFill.inherit(findDocFromSchemas(inherit)); + } + } + } + + private StructDataType findStructFromParsed(ParsedStruct resolved) { + String structId = resolved.getOwnerName() + "->" + resolved.name(); + var struct = structsFromSchemas.get(structId); + assert(struct != null); + return struct; + } + + private StructDataType findStructFromSchemas(String name, ParsedDocument context) { + var resolved = context.findParsedStruct(name); + if (resolved == null) { + throw new IllegalArgumentException("no struct named " + name + " in context " + context); + } + return findStructFromParsed(resolved); + } + + private SDAnnotationType findAnnotationFromSchemas(String name, ParsedDocument context) { + var resolved = context.findParsedAnnotation(name); + String annotationId = resolved.getOwnerName() + "->" + resolved.name(); + var annotation = annotationsFromSchemas.get(annotationId); + if (annotation == null) { + throw new IllegalArgumentException("no annotation named " + name + " in context " + context); + } + return annotation; + } + + private SDAnnotationType findAnnotationFromParsed(ParsedAnnotation resolved) { + String annotationId = resolved.getOwnerName() + "->" + resolved.name(); + var annotation = annotationsFromSchemas.get(annotationId); + if (annotation == null) { + throw new IllegalArgumentException("no annotation " + resolved.name() + " in " + resolved.getOwnerName()); + } + return annotation; + } + + private DataType createArray(ParsedType pType, ParsedDocument context) { + DataType nested = resolveFromContext(pType.nestedType(), context); + return DataType.getArray(nested); + } + + private DataType createWset(ParsedType pType, ParsedDocument context) { + DataType nested = resolveFromContext(pType.nestedType(), context); + boolean cine = pType.getCreateIfNonExistent(); + boolean riz = pType.getRemoveIfZero(); + return new WeightedSetDataType(nested, cine, riz); + } + + private DataType createMap(ParsedType pType, ParsedDocument context) { + DataType kt = resolveFromContext(pType.mapKeyType(), context); + DataType vt = resolveFromContext(pType.mapValueType(), context); + return DataType.getMap(kt, vt); + } + + private DocumentType findDocFromSchemas(String name) { + var dt = documentsFromSchemas.get(name); + if (dt == null) { + throw new IllegalArgumentException("missing document type for: " + name); + } + return dt; + } + + private DataType createAnnRef(ParsedType pType, ParsedDocument context) { + SDAnnotationType annotation = findAnnotationFromSchemas(pType.getNameOfReferencedAnnotation(), context); + return new AnnotationReferenceDataType(annotation); + } + + private DataType createDocRef(ParsedType pType) { + var ref = pType.getReferencedDocumentType(); + assert(ref.getVariant() == ParsedType.Variant.DOCUMENT); + return new NewDocumentReferenceDataType(findDocFromSchemas(ref.name())); + } + + private DataType getBuiltinType(String name) { + switch (name) { + case "bool": return DataType.BOOL; + case "byte": return DataType.BYTE; + case "int": return DataType.INT; + case "long": return DataType.LONG; + case "string": return DataType.STRING; + case "float": return DataType.FLOAT; + case "double": return DataType.DOUBLE; + case "uri": return DataType.URI; + case "predicate": return DataType.PREDICATE; + case "raw": return DataType.RAW; + case "tag": return DataType.TAG; + case "float16": return DataType.FLOAT16; + default: + throw new IllegalArgumentException("Unknown builtin type: "+name); + } + } + + private DataType resolveFromContext(ParsedType pType, ParsedDocument context) { + String name = pType.name(); + switch (pType.getVariant()) { + case NONE: return DataType.NONE; + case BUILTIN: return getBuiltinType(name); + case POSITION: return PositionDataType.INSTANCE; + case ARRAY: return createArray(pType, context); + case WSET: return createWset(pType, context); + case MAP: return createMap(pType, context); + case TENSOR: return DataType.getTensor(pType.getTensorType()); + case DOC_REFERENCE: return createDocRef(pType); + case ANN_REFERENCE: return createAnnRef(pType, context); + case DOCUMENT: return findDocFromSchemas(name); + case STRUCT: return findStructFromSchemas(name, context); + case UNKNOWN: + // fallthrough + } + // unknown is probably struct + var found = context.findParsedStruct(name); + if (found != null) { + pType.setVariant(ParsedType.Variant.STRUCT); + return findStructFromSchemas(name, context); + } + if (documentsFromSchemas.containsKey(name)) { + pType.setVariant(ParsedType.Variant.DOCUMENT); + return findDocFromSchemas(name); + } + throw new IllegalArgumentException("unknown type named '" + name + "' in context "+context); + } + + @SuppressWarnings("deprecation") + private void registerDataTypes() { + for (DataType t : structsFromSchemas.values()) { + docMan.register(t); + } + for (DocumentType t : documentsFromSchemas.values()) { + docMan.registerDocumentType(t); + } + for (SDAnnotationType t : annotationsFromSchemas.values()) { + docMan.getAnnotationTypeRegistry().register(t); + } + } + + public class TypeResolver { + private final ParsedDocument context; + public DataType resolveType(ParsedType parsed) { + return resolveFromContext(parsed, context); + } + public DataType resolveStruct(ParsedStruct parsed) { + String structId = context.name() + "->" + parsed.name(); + var r = structsFromSchemas.get(structId); + if (r == null) { + throw new IllegalArgumentException("no datatype found for struct: " + structId); + } + return r; + } + public SDAnnotationType resolveAnnotation(String name) { + return findAnnotationFromSchemas(name, context); + } + TypeResolver(ParsedDocument context) { + this.context = context; + } + } + + public TypeResolver makeContext(ParsedDocument doc) { + return new TypeResolver(doc); + } +} |