BUG-2402: add FIXMEs and javadoc
[yangtools.git] / yang / yang-data-impl / src / main / java / org / opendaylight / yangtools / yang / data / impl / schema / builder / impl / AbstractImmutableDataContainerNodeBuilder.java
1 /*
2  * Copyright (c) 2013 Cisco Systems, Inc. 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.data.impl.schema.builder.impl;
9
10 import java.util.HashMap;
11 import java.util.List;
12 import java.util.Map;
13 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
14 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
15 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
16 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
17 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
18 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder;
19 import org.opendaylight.yangtools.yang.data.impl.schema.nodes.AbstractImmutableDataContainerNode;
20
21 abstract class AbstractImmutableDataContainerNodeBuilder<I extends YangInstanceIdentifier.PathArgument, R extends DataContainerNode<I>> implements DataContainerNodeBuilder<I, R> {
22     private static final int DEFAULT_CAPACITY = 4;
23     private Map<YangInstanceIdentifier.PathArgument, DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?>> value;
24     private I nodeIdentifier;
25
26     /*
27      * Tracks whether the builder is dirty, e.g. whether the value map has been used
28      * to construct a child. If it has, we detect this condition before any further
29      * modification and create a new value map with same contents. This way we do not
30      * force a map copy if the builder is not reused.
31      */
32     private boolean dirty;
33
34     protected AbstractImmutableDataContainerNodeBuilder() {
35         this.value = new HashMap<>(DEFAULT_CAPACITY);
36         this.dirty = false;
37     }
38
39     protected AbstractImmutableDataContainerNodeBuilder(final int sizeHint) {
40         this.value = new HashMap<>(DEFAULT_CAPACITY);
41         this.dirty = false;
42     }
43
44     protected AbstractImmutableDataContainerNodeBuilder(final AbstractImmutableDataContainerNode<I> node) {
45         this.nodeIdentifier = node.getIdentifier();
46
47         /*
48          * FIXME: BUG-2402: this call is not what we actually want. We are the
49          *        only user of getChildren(), and we really want this to be a
50          *        zero-copy operation if we happen to not modify the children.
51          *        If we do, we want to perform an efficient copy-on-write before
52          *        we make the change.
53          *
54          *        With this interface we end up creating a lot of short-lived
55          *        objects in case we modify the map -- see checkDirty().
56          */
57         this.value = node.getChildren();
58         this.dirty = true;
59     }
60
61     protected final I getNodeIdentifier() {
62         return nodeIdentifier;
63     }
64
65     protected final DataContainerChild<? extends PathArgument, ?> getChild(final PathArgument child) {
66         return value.get(child);
67     }
68
69     protected final Map<PathArgument, DataContainerChild<? extends PathArgument, ?>> buildValue() {
70         dirty = true;
71         return value;
72     }
73
74     private void checkDirty() {
75         if (dirty) {
76             /*
77              * FIXME: BUG-2402: This is the second part of the above. Note
78              *        that value here is usually a read-only view. Invocation
79              *        of this constructor will force instantiation of a wrapper
80              *        Map.Entry object, just to make sure this read path does
81              *        not modify the map.
82              */
83             value = new HashMap<>(value);
84             dirty = false;
85         }
86     }
87
88     @Override
89     public DataContainerNodeBuilder<I, R> withValue(final List<DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?>> value) {
90         // TODO Replace or putAll ?
91         for (final DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?> dataContainerChild : value) {
92             withChild(dataContainerChild);
93         }
94         return this;
95     }
96
97     @Override
98     public DataContainerNodeBuilder<I, R> withChild(final DataContainerChild<?, ?> child) {
99         checkDirty();
100         this.value.put(child.getIdentifier(), child);
101         return this;
102     }
103
104     @Override
105     public DataContainerNodeBuilder<I, R> withoutChild(final PathArgument key) {
106         checkDirty();
107         this.value.remove(key);
108         return this;
109     }
110
111     @Override
112     public DataContainerNodeBuilder<I, R> withNodeIdentifier(final I nodeIdentifier) {
113         this.nodeIdentifier = nodeIdentifier;
114         return this;
115     }
116
117     @Override
118     public DataContainerNodeBuilder<I, R> addChild(
119             final DataContainerChild<? extends PathArgument, ?> child) {
120         return withChild(child);
121     }
122
123     @Override
124     public NormalizedNodeContainerBuilder<I, PathArgument, DataContainerChild<? extends PathArgument, ?>, R> removeChild(final PathArgument key) {
125         return withoutChild(key);
126     }
127 }