aboutsummaryrefslogtreecommitdiffstats
path: root/config-model/src/main/java/com/yahoo/searchdefinition/DocumentGraphValidator.java
diff options
context:
space:
mode:
authorBjørn Christian Seime <bjorncs@yahoo-inc.com>2017-02-02 16:27:54 +0100
committerBjørn Christian Seime <bjorncs@yahoo-inc.com>2017-02-02 16:27:54 +0100
commita902829ccab92d6228984d3c50fbdc207e791768 (patch)
tree627632a5f2d57e88cdd89621e3d614bcada812b6 /config-model/src/main/java/com/yahoo/searchdefinition/DocumentGraphValidator.java
parentbcd32f4fbec62fa4315acfcc9b895c21d8321f0e (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.java85
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: ", "."));
+ }
+ }
+
+}