a806f99a484f4721fd1b2e2c9408c1facb60694b
[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 com.google.common.base.Verify.verify;
11 import static java.util.Objects.requireNonNull;
12
13 import com.google.common.base.Functions;
14 import com.google.common.base.VerifyException;
15 import com.google.common.collect.ImmutableMap;
16 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
17 import java.util.Arrays;
18 import java.util.Collections;
19 import java.util.List;
20 import org.eclipse.jdt.annotation.NonNull;
21 import org.opendaylight.mdsal.binding.model.api.GeneratedType;
22 import org.opendaylight.mdsal.binding.model.api.JavaTypeName;
23 import org.opendaylight.mdsal.binding.runtime.api.CompositeRuntimeType;
24 import org.opendaylight.mdsal.binding.runtime.api.GeneratedRuntimeType;
25 import org.opendaylight.mdsal.binding.runtime.api.RuntimeType;
26 import org.opendaylight.yangtools.yang.common.QName;
27 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
28 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaTreeEffectiveStatement;
29
30 abstract class AbstractCompositeRuntimeType<S extends EffectiveStatement<?, ?>>
31         extends AbstractRuntimeType<S, GeneratedType> implements CompositeRuntimeType {
32     private static final RuntimeType[] EMPTY = new RuntimeType[0];
33
34     private final ImmutableMap<JavaTypeName, GeneratedRuntimeType> byClass;
35     private final Object bySchemaTree;
36
37     @SuppressFBWarnings(value = "SE_COMPARATOR_SHOULD_BE_SERIALIZABLE",
38         justification = "https://github.com/spotbugs/spotbugs/issues/1985")
39     AbstractCompositeRuntimeType(final GeneratedType bindingType, final S statement, final List<RuntimeType> children) {
40         super(bindingType, statement);
41
42         byClass = children.stream()
43             .filter(GeneratedRuntimeType.class::isInstance)
44             .map(GeneratedRuntimeType.class::cast)
45             .collect(ImmutableMap.toImmutableMap(GeneratedRuntimeType::getIdentifier, Functions.identity()));
46
47         final var tmp = children.stream()
48             .filter(child -> child.statement() instanceof SchemaTreeEffectiveStatement)
49             .toArray(RuntimeType[]::new);
50         bySchemaTree = switch (tmp.length) {
51             case 0 -> EMPTY;
52             case 1 -> tmp[0];
53             default -> {
54                 Arrays.sort(tmp, (o1, o2) -> {
55                     final int cmp = extractQName(o1).compareTo(extractQName(o2));
56                     verify(cmp != 0, "Type %s conflicts with %s on schema tree", o1, o2);
57                     return cmp;
58                 });
59                 yield tmp;
60             }
61         };
62     }
63
64     @Override
65     public final RuntimeType schemaTreeChild(final QName qname) {
66         if (bySchemaTree instanceof RuntimeType tmp) {
67             return qname.equals(tmp.statement().argument()) ? tmp : null;
68         }
69
70         final var tmp = (RuntimeType[]) bySchemaTree;
71         @SuppressFBWarnings(value = "SE_COMPARATOR_SHOULD_BE_SERIALIZABLE",
72             justification = "https://github.com/spotbugs/spotbugs/issues/1985")
73         final int offset = Arrays.binarySearch(tmp, null, (o1, o2) -> {
74             // We make assumptions about how Arrays.binarySearch() is implemented: o2 is expected to be the provided
75             // key -- which is null. This helps CHA by not introducing a fake RuntimeType class and the
76             // corresponding instanceof checks.
77             verify(o2 == null, "Unexpected key %s", o2);
78             return extractQName(o1).compareTo(qname);
79         });
80         return offset < 0 ? null : tmp[offset];
81     }
82
83     @Override
84     public final GeneratedRuntimeType bindingChild(final JavaTypeName typeName) {
85         return byClass.get(requireNonNull(typeName));
86     }
87
88     // Makes an assertion of all types being of specified type
89     @SuppressWarnings("unchecked")
90     final <T extends RuntimeType> @NonNull List<T> schemaTree(final Class<T> expectedType) {
91         if (expectedType.isInstance(bySchemaTree)) {
92             return List.of(expectedType.cast(bySchemaTree));
93         }
94
95         final var tmp = (RuntimeType[]) bySchemaTree;
96         for (var item : tmp) {
97             verify(expectedType.isInstance(item), "Unexpected schema tree child %s", item);
98         }
99         return (List<T>) Collections.unmodifiableList(Arrays.asList(tmp));
100     }
101
102     private static @NonNull QName extractQName(final RuntimeType type) {
103         final var stmt = type.statement();
104         if (stmt instanceof SchemaTreeEffectiveStatement<?> schemaTreeStmt) {
105             return schemaTreeStmt.argument();
106         }
107         throw new VerifyException("Unexpected statement " + stmt + " in " + type);
108     }
109 }