diff options
author | Bjørn Christian Seime <bjorncs@yahoo-inc.com> | 2017-02-02 16:27:54 +0100 |
---|---|---|
committer | Bjørn Christian Seime <bjorncs@yahoo-inc.com> | 2017-02-02 16:27:54 +0100 |
commit | a902829ccab92d6228984d3c50fbdc207e791768 (patch) | |
tree | 627632a5f2d57e88cdd89621e3d614bcada812b6 /config-model/src/main/java/com/yahoo/searchdefinition/DocumentGraphValidator.java | |
parent | bcd32f4fbec62fa4315acfcc9b895c21d8321f0e (diff) |
Detect cyclic document dependencies
Diffstat (limited to 'config-model/src/main/java/com/yahoo/searchdefinition/DocumentGraphValidator.java')
-rw-r--r-- | config-model/src/main/java/com/yahoo/searchdefinition/DocumentGraphValidator.java | 85 |
1 files changed, 85 insertions, 0 deletions
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/DocumentGraphValidator.java b/config-model/src/main/java/com/yahoo/searchdefinition/DocumentGraphValidator.java new file mode 100644 index 00000000000..c941b867120 --- /dev/null +++ b/config-model/src/main/java/com/yahoo/searchdefinition/DocumentGraphValidator.java @@ -0,0 +1,85 @@ +// Copyright 2017 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.searchdefinition; + +import com.yahoo.searchdefinition.document.SDDocumentType; + +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.List; + +import static java.util.stream.Collectors.joining; + +/** + * Validates that there are no cycles between document types (exception: self-reference is allowed). + * Example: if document B inherits A, then A cannot have a document reference to B. + * + * @author bjorncs + */ +public class DocumentGraphValidator { + + public void validateDocumentGraph(List<SDDocumentType> documents) { + for (SDDocumentType document : documents) { + validateRoot(document); + } + } + + private static void validateRoot(SDDocumentType root) { + validateChildren(root, root); + } + + private static void validateChildren(SDDocumentType root, SDDocumentType currentDocument) { + try { + currentDocument.getDocumentReferences().get() + .forEach(entry -> { + DocumentReference documentReference = entry.getValue(); + if (!isSelfReference(documentReference, currentDocument)) { + SDDocumentType referencedDocument = entry.getValue().search().getDocument(); + validateDocument(root, referencedDocument); + } + }); + currentDocument.getInheritedTypes().forEach(inheritedDocument -> { + if (!isRootDocument(inheritedDocument)) { + validateDocument(root, inheritedDocument); + } + }); + } catch (DocumentGraphException e) { + e.addParentDocument(currentDocument); + throw e; + } + } + + private static void validateDocument(SDDocumentType root, SDDocumentType currentDocument) { + if (root.equals(currentDocument)) { + throw new DocumentGraphException(currentDocument); + } + validateChildren(root, currentDocument); + } + + private static boolean isRootDocument(SDDocumentType doc) { + return doc.getName().equals("document"); + } + + private static boolean isSelfReference(DocumentReference documentReference, SDDocumentType document) { + return documentReference.search().getDocument().equals(document); + } + + public static class DocumentGraphException extends IllegalArgumentException { + private final Deque<SDDocumentType> deque = new ArrayDeque<>(); + + public DocumentGraphException(SDDocumentType document) { + deque.addLast(document); + } + + public void addParentDocument(SDDocumentType document) { + deque.addFirst(document); + } + + @Override + public String getMessage() { + return deque.stream() + .map(SDDocumentType::getName) + .collect(joining("->", "Document dependency cycle detected: ", ".")); + } + } + +} |