980e7455ce572596c8ca3e44a3371275ab50bfa8
[yangtools.git] / yang / yang-parser-rfc7950 / src / main / java / org / opendaylight / yangtools / yang / parser / rfc7950 / stmt / AbstractEffectiveStatement.java
1 /*
2  * Copyright (c) 2020 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.yangtools.yang.parser.rfc7950.stmt;
9
10 import static java.util.Objects.requireNonNull;
11
12 import com.google.common.annotations.Beta;
13 import com.google.common.collect.ImmutableList;
14 import com.google.common.collect.ImmutableMap;
15 import java.util.Collection;
16 import java.util.LinkedHashMap;
17 import java.util.Map;
18 import java.util.Optional;
19 import org.eclipse.jdt.annotation.NonNull;
20 import org.opendaylight.yangtools.yang.common.QName;
21 import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
22 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
23 import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
24 import org.opendaylight.yangtools.yang.model.api.stmt.CaseEffectiveStatement;
25 import org.opendaylight.yangtools.yang.model.api.stmt.ChoiceEffectiveStatement;
26 import org.opendaylight.yangtools.yang.model.api.stmt.DataTreeEffectiveStatement;
27 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaTreeEffectiveStatement;
28 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
29 import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference;
30
31 /**
32  * Baseline stateless implementation of an EffectiveStatement. This class adds a few default implementations and
33  * namespace dispatch, but does not actually force any state on its subclasses. This approach is different from
34  * {@link EffectiveStatementBase} in that it adds requirements for an implementation, but it leaves it up to the final
35  * class to provide object layout.
36  *
37  * <p>
38  * This finds immense value in catering the common case, for example effective statements which can, but typically
39  * do not, contain substatements.
40  *
41  * @param <A> Argument type ({@link Void} if statement does not have argument.)
42  * @param <D> Class representing declared version of this statement.
43  */
44 @Beta
45 public abstract class AbstractEffectiveStatement<A, D extends DeclaredStatement<A>>
46         extends AbstractModelStatement<A> implements EffectiveStatement<A, D> {
47     @Override
48     public final <K, V, N extends IdentifierNamespace<K, V>> Optional<? extends V> get(final Class<N> namespace,
49             final K identifier) {
50         return Optional.ofNullable(getAll(namespace).get(requireNonNull(identifier)));
51     }
52
53     @Override
54     public final <K, V, N extends IdentifierNamespace<K, V>> Map<K, V> getAll(final Class<N> namespace) {
55         final Optional<? extends Map<K, V>> ret = getNamespaceContents(requireNonNull(namespace));
56         return ret.isPresent() ? ret.get() : ImmutableMap.of();
57     }
58
59     @Override
60     public Collection<? extends EffectiveStatement<?, ?>> effectiveSubstatements() {
61         return ImmutableList.of();
62     }
63
64     /**
65      * Return the statement-specific contents of specified namespace, if available.
66      *
67      * @param namespace Requested namespace
68      * @return Namespace contents, if available.
69      */
70     protected <K, V, N extends IdentifierNamespace<K, V>> Optional<? extends Map<K, V>> getNamespaceContents(
71             final @NonNull Class<N> namespace) {
72         return Optional.empty();
73     }
74
75     /**
76      * Utility method for recovering singleton lists squashed by {@link #maskList(ImmutableList)}.
77      *
78      * @param masked list to unmask
79      * @return Unmasked list
80      * @throws NullPointerException if masked is null
81      * @throws ClassCastException if masked object does not match EffectiveStatement
82      */
83     @SuppressWarnings({ "rawtypes", "unchecked" })
84     protected static final @NonNull ImmutableList<? extends EffectiveStatement<?, ?>> unmaskList(
85             final @NonNull Object masked) {
86         return (ImmutableList) unmaskList(masked, EffectiveStatement.class);
87     }
88
89     // TODO: below methods need to find a better place, this is just a temporary hideout as their public class is on
90     //       its way out
91     static @NonNull Map<QName, SchemaTreeEffectiveStatement<?>> createSchemaTreeNamespace(
92             final StatementSourceReference ref, final Collection<? extends EffectiveStatement<?, ?>> substatements) {
93         final Map<QName, SchemaTreeEffectiveStatement<?>> schemaChildren = new LinkedHashMap<>();
94         substatements.stream().filter(SchemaTreeEffectiveStatement.class::isInstance)
95             .forEach(child -> putChild(schemaChildren, (SchemaTreeEffectiveStatement) child, ref, "schema"));
96         return schemaChildren;
97     }
98
99     static @NonNull ImmutableMap<QName, DataTreeEffectiveStatement<?>> createDataTreeNamespace(
100             final StatementSourceReference ref,
101             final Collection<SchemaTreeEffectiveStatement<?>> schemaTreeStatements,
102             // Note: this dance is needed to not retain ImmutableMap$Values
103             final ImmutableMap<QName, SchemaTreeEffectiveStatement<?>> schemaTreeNamespace) {
104         final Map<QName, DataTreeEffectiveStatement<?>> dataChildren = new LinkedHashMap<>();
105         boolean sameAsSchema = true;
106
107         for (SchemaTreeEffectiveStatement<?> child : schemaTreeStatements) {
108             if (child instanceof DataTreeEffectiveStatement) {
109                 putChild(dataChildren, (DataTreeEffectiveStatement<?>) child, ref, "data");
110             } else {
111                 sameAsSchema = false;
112                 putChoiceDataChildren(dataChildren, ref, child);
113             }
114         }
115
116         // This is a mighty hack to lower memory usage: if we consumed all schema tree children as data nodes,
117         // the two maps are equal and hence we can share the instance.
118         return sameAsSchema ? (ImmutableMap) schemaTreeNamespace : ImmutableMap.copyOf(dataChildren);
119     }
120
121     private static <T extends SchemaTreeEffectiveStatement<?>> void putChild(final Map<QName, T> map,
122             final T child, final StatementSourceReference ref, final String tree) {
123         final QName id = child.getIdentifier();
124         final T prev = map.putIfAbsent(id, child);
125         SourceException.throwIf(prev != null, ref,
126                 "Cannot add %s tree child with name %s, a conflicting child already exists", tree, id);
127     }
128
129     private static void putChoiceDataChildren(final Map<QName, DataTreeEffectiveStatement<?>> map,
130             final StatementSourceReference ref, final SchemaTreeEffectiveStatement<?> child) {
131         // For choice statements go through all their cases and fetch their data children
132         if (child instanceof ChoiceEffectiveStatement) {
133             child.streamEffectiveSubstatements(CaseEffectiveStatement.class).forEach(
134                 caseStmt -> caseStmt.streamEffectiveSubstatements(SchemaTreeEffectiveStatement.class).forEach(stmt -> {
135                     if (stmt instanceof DataTreeEffectiveStatement) {
136                         putChild(map, (DataTreeEffectiveStatement<?>) stmt, ref, "data");
137                     } else {
138                         putChoiceDataChildren(map, ref, stmt);
139                     }
140                 }));
141         }
142     }
143 }