aboutsummaryrefslogtreecommitdiffstats
path: root/config-model/src/main/java/com/yahoo/schema/DocumentGraphValidator.java
diff options
context:
space:
mode:
Diffstat (limited to 'config-model/src/main/java/com/yahoo/schema/DocumentGraphValidator.java')
-rw-r--r--config-model/src/main/java/com/yahoo/schema/DocumentGraphValidator.java79
1 files changed, 79 insertions, 0 deletions
diff --git a/config-model/src/main/java/com/yahoo/schema/DocumentGraphValidator.java b/config-model/src/main/java/com/yahoo/schema/DocumentGraphValidator.java
new file mode 100644
index 00000000000..648cdf18c5b
--- /dev/null
+++ b/config-model/src/main/java/com/yahoo/schema/DocumentGraphValidator.java
@@ -0,0 +1,79 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.schema;
+
+import com.yahoo.schema.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 -> {
+ SDDocumentType referencedDocument = entry.getValue().targetSearch().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");
+ }
+
+ 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: ", "."));
+ }
+ }
+
+}