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 java.util.Objects.requireNonNull;
12 import com.google.common.annotations.Beta;
13 import com.google.common.base.MoreObjects;
14 import com.google.common.collect.BiMap;
15 import com.google.common.collect.ImmutableList;
16 import com.google.common.collect.ImmutableMap;
17 import com.google.common.collect.ImmutableMultimap;
18 import com.google.common.collect.Multimap;
19 import java.lang.invoke.MethodHandles;
20 import java.lang.invoke.VarHandle;
21 import java.util.Collection;
22 import java.util.IdentityHashMap;
24 import java.util.Optional;
25 import org.eclipse.jdt.annotation.NonNull;
26 import org.opendaylight.mdsal.binding.model.api.Type;
27 import org.opendaylight.yangtools.concepts.Immutable;
28 import org.opendaylight.yangtools.yang.common.QName;
29 import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
30 import org.opendaylight.yangtools.yang.model.api.DocumentedNode.WithStatus;
31 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
32 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContextProvider;
33 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
34 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
39 * The result of BindingGenerator run. Contains mapping between Types and SchemaNodes.
42 public final class BindingRuntimeTypes implements EffectiveModelContextProvider, Immutable {
43 private static final Logger LOG = LoggerFactory.getLogger(BindingRuntimeTypes.class);
44 private static final VarHandle TYPE_TO_IDENTIFIER;
48 TYPE_TO_IDENTIFIER = MethodHandles.lookup().findVarHandle(BindingRuntimeTypes.class, "typeToIdentifier",
50 } catch (NoSuchFieldException | IllegalAccessException e) {
51 throw new ExceptionInInitializerError(e);
55 private final @NonNull EffectiveModelContext schemaContext;
56 private final ImmutableMap<Type, AugmentationSchemaNode> typeToAugmentation;
57 private final ImmutableMap<Type, WithStatus> typeToSchema;
58 private final ImmutableMultimap<Type, Type> choiceToCases;
59 private final ImmutableMap<QName, Type> identities;
60 // Not Immutable as we use two different implementations
61 private final Map<WithStatus, Type> schemaToType;
63 @SuppressWarnings("unused")
64 // Accessed via TYPE_TO_IDENTIFIER
65 private volatile ImmutableMap<Type, Absolute> typeToIdentifier = ImmutableMap.of();
67 public BindingRuntimeTypes(final EffectiveModelContext schemaContext,
68 final Map<Type, AugmentationSchemaNode> typeToAugmentation,
69 final Map<Type, WithStatus> typeToSchema, final Map<WithStatus, Type> schemaToType,
70 final Multimap<Type, Type> choiceToCases, final Map<QName, Type> identities) {
71 this.schemaContext = requireNonNull(schemaContext);
72 this.typeToAugmentation = ImmutableMap.copyOf(typeToAugmentation);
73 this.typeToSchema = ImmutableMap.copyOf(typeToSchema);
74 this.choiceToCases = ImmutableMultimap.copyOf(choiceToCases);
75 this.identities = ImmutableMap.copyOf(identities);
77 // Careful to use identity for SchemaNodes, but only if needed
78 // FIXME: 8.0.0: YT should be switching to identity for equals(), so this should become unnecessary
79 Map<WithStatus, Type> copy;
81 copy = ImmutableMap.copyOf(schemaToType);
82 } catch (IllegalArgumentException e) {
83 LOG.debug("Equality-duplicates found in {}", schemaToType.keySet());
84 copy = new IdentityHashMap<>(schemaToType);
87 this.schemaToType = copy;
90 public BindingRuntimeTypes(final EffectiveModelContext schemaContext,
91 final Map<Type, AugmentationSchemaNode> typeToAugmentation,
92 final BiMap<Type, WithStatus> typeToDefiningSchema,
93 final Multimap<Type, Type> choiceToCases, final Map<QName, Type> identities) {
94 this(schemaContext, typeToAugmentation, typeToDefiningSchema, typeToDefiningSchema.inverse(), choiceToCases,
99 public EffectiveModelContext getEffectiveModelContext() {
100 return schemaContext;
103 public Optional<AugmentationSchemaNode> findAugmentation(final Type type) {
104 return Optional.ofNullable(typeToAugmentation.get(type));
107 public Optional<Type> findIdentity(final QName qname) {
108 return Optional.ofNullable(identities.get(qname));
111 public Optional<WithStatus> findSchema(final Type type) {
112 return Optional.ofNullable(typeToSchema.get(type));
115 public Optional<Absolute> findSchemaNodeIdentifier(final Type type) {
116 final ImmutableMap<Type, Absolute> local = (ImmutableMap<Type, Absolute>) TYPE_TO_IDENTIFIER.getAcquire(this);
117 final Absolute existing = local.get(type);
118 return existing != null ? Optional.of(existing) : loadSchemaNodeIdentifier(local, type);
121 public Optional<Type> findType(final WithStatus schema) {
122 return Optional.ofNullable(schemaToType.get(schema));
125 public Multimap<Type, Type> getChoiceToCases() {
126 return choiceToCases;
129 public Collection<Type> findCases(final Type choiceType) {
130 return choiceToCases.get(choiceType);
134 public String toString() {
135 return MoreObjects.toStringHelper(this)
136 .add("typeToAugmentation", typeToAugmentation)
137 .add("typeToSchema", typeToSchema)
138 .add("choiceToCases", choiceToCases)
139 .add("identities", identities)
143 private Optional<Absolute> loadSchemaNodeIdentifier(final ImmutableMap<Type, Absolute> local, final Type type) {
144 final WithStatus schema = typeToSchema.get(type);
145 if (!(schema instanceof SchemaNode)) {
146 return Optional.empty();
149 // TODO: do not rely on getPath() here
150 final Absolute created = Absolute.of(ImmutableList.copyOf(((SchemaNode) schema).getPath().getPathFromRoot()))
153 ImmutableMap<Type, Absolute> prev = local;
155 // Compute next cache
156 final ImmutableMap<Type, Absolute> next =
157 ImmutableMap.<Type, Absolute>builderWithExpectedSize(prev.size() + 1)
159 .put(type, created).build();
161 final Object witness = TYPE_TO_IDENTIFIER.compareAndExchangeRelease(this, prev, next);
162 if (witness == prev) {
163 // Cache populated successfully, we are all done now
164 return Optional.of(created);
167 // Remember cache for next computation
168 prev = (ImmutableMap<Type, Absolute>) witness;
169 final Absolute raced = prev.get(type);
171 // We have raced on this item, use it from cache
172 return Optional.of(raced);
175 // We have raced on a different item, loop around and repeat