2 * Copyright (c) 2018 Pantheon Technologies, s.r.o. All rights reserved.
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
8 package org.opendaylight.mdsal.binding.generator.impl;
10 import static com.google.common.base.Verify.verify;
11 import static com.google.common.base.Verify.verifyNotNull;
12 import static java.util.Objects.requireNonNull;
14 import com.google.common.base.MoreObjects;
15 import com.google.common.collect.BiMap;
16 import com.google.common.collect.ImmutableMap;
17 import com.google.common.collect.ImmutableMultimap;
18 import com.google.common.collect.Multimap;
19 import com.google.common.collect.MultimapBuilder;
20 import java.util.Collection;
21 import java.util.IdentityHashMap;
22 import java.util.List;
24 import java.util.Map.Entry;
25 import java.util.Optional;
27 import java.util.stream.Collectors;
28 import org.eclipse.jdt.annotation.NonNull;
29 import org.opendaylight.mdsal.binding.model.api.GeneratedType;
30 import org.opendaylight.mdsal.binding.model.api.Type;
31 import org.opendaylight.mdsal.binding.runtime.api.BindingRuntimeTypes;
32 import org.opendaylight.yangtools.yang.common.QName;
33 import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
34 import org.opendaylight.yangtools.yang.model.api.DocumentedNode.WithStatus;
35 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
36 import org.opendaylight.yangtools.yang.model.api.stmt.CaseEffectiveStatement;
37 import org.opendaylight.yangtools.yang.model.api.stmt.ChoiceEffectiveStatement;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
41 final class DefaultBindingRuntimeTypes implements BindingRuntimeTypes {
42 private static final Logger LOG = LoggerFactory.getLogger(DefaultBindingRuntimeTypes.class);
44 private final @NonNull EffectiveModelContext schemaContext;
45 private final ImmutableMap<Type, AugmentationSchemaNode> typeToAugmentation;
46 private final ImmutableMap<Type, WithStatus> typeToSchema;
47 private final ImmutableMultimap<Type, Type> choiceToCases;
48 private final ImmutableMap<QName, Type> identities;
49 // Not Immutable as we use two different implementations
50 private final Map<WithStatus, Type> schemaToType;
52 DefaultBindingRuntimeTypes(final EffectiveModelContext schemaContext,
53 final Map<Type, AugmentationSchemaNode> typeToAugmentation,
54 final Map<Type, WithStatus> typeToSchema, final Map<WithStatus, Type> schemaToType,
55 final Map<QName, Type> identities) {
56 this.schemaContext = requireNonNull(schemaContext);
57 this.typeToAugmentation = ImmutableMap.copyOf(typeToAugmentation);
58 this.typeToSchema = ImmutableMap.copyOf(typeToSchema);
59 this.identities = ImmutableMap.copyOf(identities);
61 // Careful to use identity for SchemaNodes, but only if needed
62 // FIXME: 8.0.0: YT should be switching to identity for equals(), so this should become unnecessary
63 Map<WithStatus, Type> copy;
65 copy = ImmutableMap.copyOf(schemaToType);
66 } catch (IllegalArgumentException e) {
67 LOG.debug("Equality-duplicates found in {}", schemaToType.keySet());
68 copy = new IdentityHashMap<>(schemaToType);
71 this.schemaToType = copy;
73 // Two-phase indexing of choice/case nodes. First we load all choices. Note we are using typeToSchema argument,
74 // not field, so as not to instantiate its entrySet.
75 final Set<GeneratedType> choiceTypes = typeToSchema.entrySet().stream()
76 .filter(entry -> entry.getValue() instanceof ChoiceEffectiveStatement)
78 final Type key = entry.getKey();
79 verify(key instanceof GeneratedType, "Unexpected choice type %s", key);
80 return (GeneratedType) key;
82 .collect(Collectors.toUnmodifiableSet());
84 final Multimap<Type, Type> builder = MultimapBuilder.hashKeys(choiceTypes.size()).arrayListValues().build();
85 for (Entry<Type, WithStatus> entry : typeToSchema.entrySet()) {
86 if (entry.getValue() instanceof CaseEffectiveStatement) {
87 final Type type = entry.getKey();
88 verify(type instanceof GeneratedType, "Unexpected case type %s", type);
89 builder.put(verifyNotNull(implementedChoiceType(((GeneratedType) type).getImplements(), choiceTypes),
90 "Cannot determine choice type for %s", type), type);
94 choiceToCases = ImmutableMultimap.copyOf(builder);
97 private static GeneratedType implementedChoiceType(final List<Type> impls, final Set<GeneratedType> choiceTypes) {
98 for (Type impl : impls) {
99 if (impl instanceof GeneratedType && choiceTypes.contains(impl)) {
100 return (GeneratedType) impl;
106 DefaultBindingRuntimeTypes(final EffectiveModelContext schemaContext,
107 final Map<Type, AugmentationSchemaNode> typeToAugmentation,
108 final BiMap<Type, WithStatus> typeToDefiningSchema, final Map<QName, Type> identities) {
109 this(schemaContext, typeToAugmentation, typeToDefiningSchema, typeToDefiningSchema.inverse(), identities);
113 public EffectiveModelContext getEffectiveModelContext() {
114 return schemaContext;
118 public Optional<AugmentationSchemaNode> findAugmentation(final Type type) {
119 return Optional.ofNullable(typeToAugmentation.get(type));
123 public Optional<Type> findIdentity(final QName qname) {
124 return Optional.ofNullable(identities.get(qname));
128 public Optional<WithStatus> findSchema(final Type type) {
129 return Optional.ofNullable(typeToSchema.get(type));
133 public Optional<Type> findType(final WithStatus schema) {
134 return Optional.ofNullable(schemaToType.get(schema));
138 public Optional<Type> findOriginalAugmentationType(final AugmentationSchemaNode augment) {
139 // If the augment statement does not contain any child nodes, we did not generate an augmentation, as it would
140 // be plain littering.
141 // FIXME: MDSAL-695: this check is rather costly (involves filtering), can we just rely on the not being found
142 // in the end? all we are saving is essentially two map lookups after all...
143 if (augment.getChildNodes().isEmpty()) {
144 return Optional.empty();
147 // FIXME: MDSAL-695: We should have enough information from mdsal-binding-generator to receive a (sparse) Map
148 // for current -> original lookup. When combined with schemaToType, this amounts to the
149 // inverse view of what 'typeToSchema' holds
150 AugmentationSchemaNode current = augment;
152 // If this augmentation has been added through 'uses foo { augment bar { ... } }', we need to invert that
153 // walk and arrive at the original declaration site, as that is where we generated 'grouping foo's
154 // augmentation. That site may have a different module, hence the augment namespace may be different.
155 final Optional<AugmentationSchemaNode> original = current.getOriginalDefinition();
156 if (original.isEmpty()) {
157 return findType(current);
159 current = original.orElseThrow();
164 public Multimap<Type, Type> getChoiceToCases() {
165 return choiceToCases;
169 public Collection<Type> findCases(final Type choiceType) {
170 return choiceToCases.get(choiceType);
174 public String toString() {
175 return MoreObjects.toStringHelper(this)
176 .add("typeToAugmentation", typeToAugmentation)
177 .add("typeToSchema", typeToSchema)
178 .add("choiceToCases", choiceToCases)
179 .add("identities", identities)