70622a9da979e995959e50f5ffbf95d84a463bc4
[mdsal.git] / binding / mdsal-binding-runtime-api / src / main / java / org / opendaylight / mdsal / binding / runtime / api / BindingRuntimeTypes.java
1 /*
2  * Copyright (c) 2018 Pantheon Technologies, s.r.o.  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.runtime.api;
9
10 import static java.util.Objects.requireNonNull;
11
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;
23 import java.util.Map;
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;
37
38 /**
39  * The result of BindingGenerator run. Contains mapping between Types and SchemaNodes.
40  */
41 @Beta
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;
45
46     static {
47         try {
48             TYPE_TO_IDENTIFIER = MethodHandles.lookup().findVarHandle(BindingRuntimeTypes.class, "typeToIdentifier",
49                 ImmutableMap.class);
50         } catch (NoSuchFieldException | IllegalAccessException e) {
51             throw new ExceptionInInitializerError(e);
52         }
53     }
54
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;
62
63     @SuppressWarnings("unused")
64     // Accessed via TYPE_TO_IDENTIFIER
65     private volatile ImmutableMap<Type, Absolute> typeToIdentifier = ImmutableMap.of();
66
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);
76
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;
80         try {
81             copy = ImmutableMap.copyOf(schemaToType);
82         } catch (IllegalArgumentException e) {
83             LOG.debug("Equality-duplicates found in {}", schemaToType.keySet());
84             copy = new IdentityHashMap<>(schemaToType);
85         }
86
87         this.schemaToType = copy;
88     }
89
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,
95             identities);
96     }
97
98     @Override
99     public EffectiveModelContext getEffectiveModelContext() {
100         return schemaContext;
101     }
102
103     public Optional<AugmentationSchemaNode> findAugmentation(final Type type) {
104         return Optional.ofNullable(typeToAugmentation.get(type));
105     }
106
107     public Optional<Type> findIdentity(final QName qname) {
108         return Optional.ofNullable(identities.get(qname));
109     }
110
111     public Optional<WithStatus> findSchema(final Type type) {
112         return Optional.ofNullable(typeToSchema.get(type));
113     }
114
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);
119     }
120
121     public Optional<Type> findType(final WithStatus schema) {
122         return Optional.ofNullable(schemaToType.get(schema));
123     }
124
125     public Multimap<Type, Type> getChoiceToCases() {
126         return choiceToCases;
127     }
128
129     public Collection<Type> findCases(final Type choiceType) {
130         return choiceToCases.get(choiceType);
131     }
132
133     @Override
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)
140                 .toString();
141     }
142
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();
147         }
148
149         // TODO: do not rely on getPath() here
150         final Absolute created = Absolute.of(ImmutableList.copyOf(((SchemaNode) schema).getPath().getPathFromRoot()))
151                 .intern();
152
153         ImmutableMap<Type, Absolute> prev = local;
154         while (true) {
155             // Compute next cache
156             final ImmutableMap<Type, Absolute> next =
157                     ImmutableMap.<Type, Absolute>builderWithExpectedSize(prev.size() + 1)
158                         .putAll(prev)
159                         .put(type, created).build();
160
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);
165             }
166
167             // Remember cache for next computation
168             prev = (ImmutableMap<Type, Absolute>) witness;
169             final Absolute raced = prev.get(type);
170             if (raced != null) {
171                 // We have raced on this item, use it from cache
172                 return Optional.of(raced);
173             }
174
175             // We have raced on a different item, loop around and repeat
176         }
177     }
178 }