Clean up AbstractCompositeRuntimeType
[mdsal.git] / binding / mdsal-binding-generator / src / main / java / org / opendaylight / mdsal / binding / generator / impl / rt / AbstractCompositeRuntimeType.java
1 /*
2  * Copyright (c) 2021 PANTHEON.tech, s.r.o. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.mdsal.binding.generator.impl.rt;
9
10 import static java.util.Objects.requireNonNull;
11 import static java.util.Objects.requireNonNullElse;
12
13 import com.google.common.base.Functions;
14 import com.google.common.base.VerifyException;
15 import com.google.common.collect.ImmutableMap;
16 import java.util.Arrays;
17 import java.util.Collections;
18 import java.util.List;
19 import org.eclipse.jdt.annotation.NonNull;
20 import org.opendaylight.mdsal.binding.model.api.GeneratedType;
21 import org.opendaylight.mdsal.binding.model.api.JavaTypeName;
22 import org.opendaylight.mdsal.binding.runtime.api.CompositeRuntimeType;
23 import org.opendaylight.mdsal.binding.runtime.api.GeneratedRuntimeType;
24 import org.opendaylight.mdsal.binding.runtime.api.RuntimeType;
25 import org.opendaylight.yangtools.yang.common.QName;
26 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
27 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaTreeEffectiveStatement;
28
29 abstract class AbstractCompositeRuntimeType<S extends EffectiveStatement<?, ?>>
30         extends AbstractRuntimeType<S, GeneratedType> implements CompositeRuntimeType {
31     private static final RuntimeType[] EMPTY = new RuntimeType[0];
32
33     private final ImmutableMap<JavaTypeName, GeneratedRuntimeType> byClass;
34     private final Object bySchemaTree;
35
36     AbstractCompositeRuntimeType(final GeneratedType bindingType, final S statement, final List<RuntimeType> children) {
37         super(bindingType, statement);
38
39         byClass = children.stream()
40             .filter(GeneratedRuntimeType.class::isInstance)
41             .map(GeneratedRuntimeType.class::cast)
42             .collect(ImmutableMap.toImmutableMap(GeneratedRuntimeType::getIdentifier, Functions.identity()));
43
44         final var tmp = children.stream()
45             .filter(child -> child.statement() instanceof SchemaTreeEffectiveStatement)
46             .toArray(RuntimeType[]::new);
47         bySchemaTree = switch (tmp.length) {
48             case 0 -> EMPTY;
49             case 1 -> tmp[0];
50             default -> {
51                 Arrays.sort(tmp, (o1, o2) -> {
52                     final int cmp = extractQName(o1).compareTo(extractQName(o2));
53                     if (cmp == 0) {
54                         throw new VerifyException("Type " + o1 + " conflicts with " + o2 + " on schema tree");
55                     }
56                     return cmp;
57                 });
58                 yield tmp;
59             }
60         };
61     }
62
63     @Override
64     public final RuntimeType schemaTreeChild(final QName qname) {
65         if (bySchemaTree instanceof RuntimeType tmp) {
66             return qname.equals(tmp.statement().argument()) ? tmp : null;
67         }
68
69         final var tmp = (RuntimeType[]) bySchemaTree;
70         // Here we are assuming that Arrays.binarySearch() accepts a null object, so as to help CHA by not introducing
71         // a fake RuntimeType class and the corresponding instanceof checks to side-step the statement lookup (which
72         // would need more faking).
73         // We make a slight assumption that o2 is the what we specify as a key, but can recover if it is the other way
74         // around.
75         final int offset = Arrays.binarySearch(tmp, null,
76             (o1, o2) -> extractQName(requireNonNullElse(o1, o2)).compareTo(qname));
77         return offset < 0 ? null : tmp[offset];
78     }
79
80     @Override
81     public final GeneratedRuntimeType bindingChild(final JavaTypeName typeName) {
82         return byClass.get(requireNonNull(typeName));
83     }
84
85     // Makes an assertion of all types being of specified type
86     @SuppressWarnings("unchecked")
87     final <T extends RuntimeType> @NonNull List<T> schemaTree(final Class<T> expectedType) {
88         if (expectedType.isInstance(bySchemaTree)) {
89             return List.of(expectedType.cast(bySchemaTree));
90         }
91
92         final var tmp = (RuntimeType[]) bySchemaTree;
93         for (var item : tmp) {
94             if (!expectedType.isInstance(item)) {
95                 throw new VerifyException("Unexpected schema tree child " + item);
96             }
97         }
98         return (List<T>) Collections.unmodifiableList(Arrays.asList(tmp));
99     }
100
101     private static @NonNull QName extractQName(final RuntimeType type) {
102         final var stmt = type.statement();
103         if (stmt instanceof SchemaTreeEffectiveStatement<?> schemaTreeStmt) {
104             return schemaTreeStmt.argument();
105         }
106         throw new VerifyException("Unexpected statement " + stmt + " in " + type);
107     }
108 }