Switch to Objects.requireNonNull
[mdsal.git] / binding2 / mdsal-binding2-dom-codec / src / main / java / org / opendaylight / mdsal / binding / javav2 / dom / codec / modification / LazyTreeNodeModification.java
1 /*
2  * Copyright (c) 2017 Pantheon Technologies 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.mdsal.binding.javav2.dom.codec.modification;
9
10 import static java.util.Objects.requireNonNull;
11
12 import com.google.common.annotations.Beta;
13 import java.util.ArrayList;
14 import java.util.Collection;
15 import java.util.Iterator;
16 import java.util.List;
17 import java.util.Optional;
18 import javax.annotation.Nonnull;
19 import javax.annotation.Nullable;
20 import org.opendaylight.mdsal.binding.javav2.api.TreeNodeModification;
21 import org.opendaylight.mdsal.binding.javav2.dom.codec.api.BindingTreeNodeCodec;
22 import org.opendaylight.mdsal.binding.javav2.spec.base.IdentifiableItem;
23 import org.opendaylight.mdsal.binding.javav2.spec.base.Item;
24 import org.opendaylight.mdsal.binding.javav2.spec.base.TreeArgument;
25 import org.opendaylight.mdsal.binding.javav2.spec.base.TreeNode;
26 import org.opendaylight.mdsal.binding.javav2.spec.structural.Augmentation;
27 import org.opendaylight.mdsal.binding.javav2.spec.structural.TreeChildNode;
28 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
29 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
30 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
31 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory;
33
34 /**
35  * Lazily translated {@link TreeNodeModification} based on {@link DataTreeCandidateNode}.
36  *
37  * <p>
38  * {@link LazyTreeNodeModification} represents Data tree change event, but whole tree is not translated or
39  * resolved eagerly, but only child nodes which are directly accessed by user of tree node modification.
40  *
41  * @param <T>
42  *            Type of Binding Tree Node
43  */
44 @Beta
45 final class LazyTreeNodeModification<T extends TreeNode> implements TreeNodeModification<T> {
46
47     private static final Logger LOG = LoggerFactory.getLogger(LazyTreeNodeModification.class);
48
49     private final BindingTreeNodeCodec<T> codec;
50     private final DataTreeCandidateNode domData;
51     private final TreeArgument<?> identifier;
52     private Collection<TreeNodeModification<? extends TreeNode>> childNodesCache;
53
54     private LazyTreeNodeModification(final BindingTreeNodeCodec<T> codec, final DataTreeCandidateNode domData) {
55         this.codec = requireNonNull(codec);
56         this.domData = requireNonNull(domData);
57         this.identifier = codec.deserializePathArgument(domData.getIdentifier());
58     }
59
60     static <T extends TreeNode> TreeNodeModification<T> create(final BindingTreeNodeCodec<T> codec,
61             final DataTreeCandidateNode domData) {
62         return new LazyTreeNodeModification<>(codec, domData);
63     }
64
65     private static Collection<TreeNodeModification<? extends TreeNode>> from(final BindingTreeNodeCodec<?> parentCodec,
66             final Collection<DataTreeCandidateNode> domChildNodes) {
67         final List<TreeNodeModification<? extends TreeNode>> result = new ArrayList<>(domChildNodes.size());
68         populateList(result, parentCodec, domChildNodes);
69         return result;
70     }
71
72     private static void populateList(final List<TreeNodeModification<? extends TreeNode>> result,
73             final BindingTreeNodeCodec<?> parentCodec, final Collection<DataTreeCandidateNode> domChildNodes) {
74         for (final DataTreeCandidateNode domChildNode : domChildNodes) {
75             final BindingStructuralType type = BindingStructuralType.from(domChildNode);
76             if (type != BindingStructuralType.NOT_ADDRESSABLE) {
77                 /*
78                  * Even if type is UNKNOWN, from perspective of BindingStructuralType we try to load codec for
79                  * it. We will use that type to further specify debug log.
80                  */
81                 try {
82                     final BindingTreeNodeCodec<?> childCodec =
83                             parentCodec.yangPathArgumentChild(domChildNode.getIdentifier());
84                     populateList(result, type, childCodec, domChildNode);
85                 } catch (final IllegalArgumentException e) {
86                     if (type == BindingStructuralType.UNKNOWN) {
87                         LOG.debug("Unable to deserialize unknown DOM node {}", domChildNode, e);
88                     } else {
89                         LOG.debug("Binding representation for DOM node {} was not found", domChildNode, e);
90                     }
91                 }
92             }
93         }
94     }
95
96     private static void populateList(final List<TreeNodeModification<? extends TreeNode>> result,
97             final BindingStructuralType type, final BindingTreeNodeCodec<?> childCodec,
98             final DataTreeCandidateNode domChildNode) {
99         switch (type) {
100             case INVISIBLE_LIST:
101                 // We use parent codec intentionally.
102                 populateListWithSingleCodec(result, childCodec, domChildNode.getChildNodes());
103                 break;
104             case INVISIBLE_CONTAINER:
105                 populateList(result, childCodec, domChildNode.getChildNodes());
106                 break;
107             case UNKNOWN:
108             case VISIBLE_CONTAINER:
109                 result.add(create(childCodec, domChildNode));
110                 break;
111             default:
112         }
113     }
114
115     private static void populateListWithSingleCodec(final List<TreeNodeModification<? extends TreeNode>> result,
116             final BindingTreeNodeCodec<?> codec, final Collection<DataTreeCandidateNode> childNodes) {
117         for (final DataTreeCandidateNode child : childNodes) {
118             result.add(create(codec, child));
119         }
120     }
121
122     @Nullable
123     @Override
124     public T getDataBefore() {
125         return deserialize(domData.getDataBefore());
126     }
127
128     @Nullable
129     @Override
130     public T getDataAfter() {
131         return deserialize(domData.getDataAfter());
132     }
133
134     @Nonnull
135     @Override
136     public Class<T> getDataType() {
137         return codec.getBindingClass();
138     }
139
140     @Nonnull
141     @Override
142     public TreeArgument<?> getIdentifier() {
143         return identifier;
144     }
145
146     @Nonnull
147     @Override
148     public TreeNodeModification.ModificationType getModificationType() {
149         switch (domData.getModificationType()) {
150             case APPEARED:
151             case WRITE:
152                 return TreeNodeModification.ModificationType.WRITE;
153             case SUBTREE_MODIFIED:
154                 return TreeNodeModification.ModificationType.SUBTREE_MODIFIED;
155             case DISAPPEARED:
156             case DELETE:
157                 return TreeNodeModification.ModificationType.DELETE;
158
159             default:
160                 // TODO: Should we lie about modification type instead of exception?
161                 throw new IllegalStateException("Unsupported DOM Modification type " + domData.getModificationType());
162         }
163     }
164
165     @Nonnull
166     @Override
167     public Collection<TreeNodeModification<? extends TreeNode>> getModifiedChildren() {
168         if (childNodesCache == null) {
169             childNodesCache = from(codec, domData.getChildNodes());
170         }
171         return childNodesCache;
172     }
173
174     @SuppressWarnings("unchecked")
175     @Override
176     public <C extends TreeChildNode<? super T, ?>> Collection<TreeNodeModification<C>>
177             getModifiedChildren(@Nonnull final Class<C> childType) {
178         final List<TreeNodeModification<C>> children = new ArrayList<>();
179         for (final TreeNodeModification<? extends TreeNode> potential : getModifiedChildren()) {
180             if (childType.isAssignableFrom(potential.getDataType())) {
181                 children.add((TreeNodeModification<C>) potential);
182             }
183         }
184         return children;
185     }
186
187     @SuppressWarnings("rawtypes")
188     @Nullable
189     @Override
190     public TreeNodeModification<? extends TreeNode> getModifiedChild(final TreeArgument childArgument) {
191         final List<YangInstanceIdentifier.PathArgument> domArgumentList = new ArrayList<>();
192         final BindingTreeNodeCodec<?> childCodec = codec.bindingPathArgumentChild(childArgument, domArgumentList);
193         final Iterator<YangInstanceIdentifier.PathArgument> toEnter = domArgumentList.iterator();
194         DataTreeCandidateNode current = domData;
195         while (toEnter.hasNext() && current != null) {
196             current = current.getModifiedChild(toEnter.next());
197         }
198         if (current != null) {
199             return create(childCodec, current);
200         }
201         return null;
202     }
203
204     @SuppressWarnings({ "unchecked", "rawtypes" })
205     @Override
206     public <C extends IdentifiableItem<T, K> & TreeChildNode<? super T, ?>, K extends IdentifiableItem<T, K>>
207             TreeNodeModification<C>
208             getModifiedChildListItem(@Nonnull final Class<C> listItem, @Nonnull final K listKey) {
209         return (TreeNodeModification) getModifiedChild(new IdentifiableItem(listItem, listKey));
210     }
211
212     @SuppressWarnings({ "unchecked", "rawtypes" })
213     @Nullable
214     @Override
215     public <C extends TreeChildNode<? super T, ?>> TreeNodeModification<C>
216             getModifiedChildContainer(@Nonnull final Class<C> child) {
217         return (TreeNodeModification<C>) getModifiedChild(new Item(child));
218     }
219
220     @SuppressWarnings({ "unchecked", "rawtypes" })
221     @Nullable
222     @Override
223     public <C extends Augmentation<T> & TreeNode> TreeNodeModification<C>
224             getModifiedAugmentation(@Nonnull final Class<C> augmentation) {
225         return (TreeNodeModification<C>) getModifiedChild(new Item(augmentation));
226     }
227
228     private T deserialize(final Optional<NormalizedNode<?, ?>> dataAfter) {
229         if (dataAfter.isPresent()) {
230             return codec.deserialize(dataAfter.get());
231         }
232         return null;
233     }
234 }
235