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.runtime.api;
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.annotations.Beta;
15 import com.google.common.base.MoreObjects;
16 import com.google.common.collect.BiMap;
17 import com.google.common.collect.ImmutableList;
18 import com.google.common.collect.ImmutableMap;
19 import com.google.common.collect.ImmutableMultimap;
20 import com.google.common.collect.Multimap;
21 import com.google.common.collect.MultimapBuilder;
22 import java.lang.invoke.MethodHandles;
23 import java.lang.invoke.VarHandle;
24 import java.util.Collection;
25 import java.util.IdentityHashMap;
26 import java.util.List;
28 import java.util.Map.Entry;
29 import java.util.Optional;
31 import java.util.stream.Collectors;
32 import org.eclipse.jdt.annotation.NonNull;
33 import org.opendaylight.mdsal.binding.model.api.GeneratedType;
34 import org.opendaylight.mdsal.binding.model.api.Type;
35 import org.opendaylight.yangtools.concepts.Immutable;
36 import org.opendaylight.yangtools.yang.common.QName;
37 import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
38 import org.opendaylight.yangtools.yang.model.api.DocumentedNode.WithStatus;
39 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
40 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContextProvider;
41 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
42 import org.opendaylight.yangtools.yang.model.api.stmt.CaseEffectiveStatement;
43 import org.opendaylight.yangtools.yang.model.api.stmt.ChoiceEffectiveStatement;
44 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
49 * The result of BindingGenerator run. Contains mapping between Types and SchemaNodes.
52 public final class BindingRuntimeTypes implements EffectiveModelContextProvider, Immutable {
53 private static final Logger LOG = LoggerFactory.getLogger(BindingRuntimeTypes.class);
54 private static final VarHandle TYPE_TO_IDENTIFIER;
58 TYPE_TO_IDENTIFIER = MethodHandles.lookup().findVarHandle(BindingRuntimeTypes.class, "typeToIdentifier",
60 } catch (NoSuchFieldException | IllegalAccessException e) {
61 throw new ExceptionInInitializerError(e);
65 private final @NonNull EffectiveModelContext schemaContext;
66 private final ImmutableMap<Type, AugmentationSchemaNode> typeToAugmentation;
67 private final ImmutableMap<Type, WithStatus> typeToSchema;
68 private final ImmutableMultimap<Type, Type> choiceToCases;
69 private final ImmutableMap<QName, Type> identities;
70 // Not Immutable as we use two different implementations
71 private final Map<WithStatus, Type> schemaToType;
73 @SuppressWarnings("unused")
74 // Accessed via TYPE_TO_IDENTIFIER
75 private volatile ImmutableMap<Type, Absolute> typeToIdentifier = ImmutableMap.of();
77 public BindingRuntimeTypes(final EffectiveModelContext schemaContext,
78 final Map<Type, AugmentationSchemaNode> typeToAugmentation,
79 final Map<Type, WithStatus> typeToSchema, final Map<WithStatus, Type> schemaToType,
80 final Map<QName, Type> identities) {
81 this.schemaContext = requireNonNull(schemaContext);
82 this.typeToAugmentation = ImmutableMap.copyOf(typeToAugmentation);
83 this.typeToSchema = ImmutableMap.copyOf(typeToSchema);
84 this.identities = ImmutableMap.copyOf(identities);
86 // Careful to use identity for SchemaNodes, but only if needed
87 // FIXME: 8.0.0: YT should be switching to identity for equals(), so this should become unnecessary
88 Map<WithStatus, Type> copy;
90 copy = ImmutableMap.copyOf(schemaToType);
91 } catch (IllegalArgumentException e) {
92 LOG.debug("Equality-duplicates found in {}", schemaToType.keySet());
93 copy = new IdentityHashMap<>(schemaToType);
96 this.schemaToType = copy;
98 // Two-phase indexing of choice/case nodes. First we load all choices. Note we are using typeToSchema argument,
99 // not field, so as not to instantiate its entrySet.
100 final Set<GeneratedType> choiceTypes = typeToSchema.entrySet().stream()
101 .filter(entry -> entry.getValue() instanceof ChoiceEffectiveStatement)
103 final Type key = entry.getKey();
104 verify(key instanceof GeneratedType, "Unexpected choice type %s", key);
105 return (GeneratedType) key;
107 .collect(Collectors.toUnmodifiableSet());
109 final Multimap<Type, Type> builder = MultimapBuilder.hashKeys(choiceTypes.size()).arrayListValues().build();
110 for (Entry<Type, WithStatus> entry : typeToSchema.entrySet()) {
111 if (entry.getValue() instanceof CaseEffectiveStatement) {
112 final Type type = entry.getKey();
113 verify(type instanceof GeneratedType, "Unexpected case type %s", type);
114 builder.put(verifyNotNull(implementedChoiceType(((GeneratedType) type).getImplements(), choiceTypes),
115 "Cannot determine choice type for %s", type), type);
119 choiceToCases = ImmutableMultimap.copyOf(builder);
122 private static GeneratedType implementedChoiceType(final List<Type> impls, final Set<GeneratedType> choiceTypes) {
123 for (Type impl : impls) {
124 if (impl instanceof GeneratedType && choiceTypes.contains(impl)) {
125 return (GeneratedType) impl;
131 public BindingRuntimeTypes(final EffectiveModelContext schemaContext,
132 final Map<Type, AugmentationSchemaNode> typeToAugmentation,
133 final BiMap<Type, WithStatus> typeToDefiningSchema, final Map<QName, Type> identities) {
134 this(schemaContext, typeToAugmentation, typeToDefiningSchema, typeToDefiningSchema.inverse(), identities);
138 public EffectiveModelContext getEffectiveModelContext() {
139 return schemaContext;
142 public Optional<AugmentationSchemaNode> findAugmentation(final Type type) {
143 return Optional.ofNullable(typeToAugmentation.get(type));
146 public Optional<Type> findIdentity(final QName qname) {
147 return Optional.ofNullable(identities.get(qname));
150 public Optional<WithStatus> findSchema(final Type type) {
151 return Optional.ofNullable(typeToSchema.get(type));
154 public Optional<Absolute> findSchemaNodeIdentifier(final Type type) {
155 final ImmutableMap<Type, Absolute> local = (ImmutableMap<Type, Absolute>) TYPE_TO_IDENTIFIER.getAcquire(this);
156 final Absolute existing = local.get(type);
157 return existing != null ? Optional.of(existing) : loadSchemaNodeIdentifier(local, type);
160 public Optional<Type> findType(final WithStatus schema) {
161 return Optional.ofNullable(schemaToType.get(schema));
164 public Multimap<Type, Type> getChoiceToCases() {
165 return choiceToCases;
168 public Collection<Type> findCases(final Type choiceType) {
169 return choiceToCases.get(choiceType);
173 public String toString() {
174 return MoreObjects.toStringHelper(this)
175 .add("typeToAugmentation", typeToAugmentation)
176 .add("typeToSchema", typeToSchema)
177 .add("choiceToCases", choiceToCases)
178 .add("identities", identities)
182 private Optional<Absolute> loadSchemaNodeIdentifier(final ImmutableMap<Type, Absolute> local, final Type type) {
183 final WithStatus schema = typeToSchema.get(type);
184 if (!(schema instanceof SchemaNode)) {
185 return Optional.empty();
188 // TODO: do not rely on getPath() here
189 final Absolute created = Absolute.of(ImmutableList.copyOf(((SchemaNode) schema).getPath().getPathFromRoot()))
192 ImmutableMap<Type, Absolute> prev = local;
194 // Compute next cache
195 final ImmutableMap<Type, Absolute> next =
196 ImmutableMap.<Type, Absolute>builderWithExpectedSize(prev.size() + 1)
198 .put(type, created).build();
200 final Object witness = TYPE_TO_IDENTIFIER.compareAndExchangeRelease(this, prev, next);
201 if (witness == prev) {
202 // Cache populated successfully, we are all done now
203 return Optional.of(created);
206 // Remember cache for next computation
207 prev = (ImmutableMap<Type, Absolute>) witness;
208 final Absolute raced = prev.get(type);
210 // We have raced on this item, use it from cache
211 return Optional.of(raced);
214 // We have raced on a different item, loop around and repeat