2 * Copyright (c) 2020 PANTHEON.tech, s.r.o. and others. 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.dom.codec.impl;
10 import static com.google.common.base.Verify.verify;
11 import static java.util.Objects.requireNonNull;
13 import com.google.common.annotations.VisibleForTesting;
14 import java.lang.invoke.MethodHandles;
15 import java.lang.invoke.VarHandle;
16 import java.util.AbstractList;
17 import java.util.Collection;
18 import java.util.Comparator;
19 import java.util.List;
20 import java.util.RandomAccess;
21 import java.util.function.UnaryOperator;
22 import org.eclipse.jdt.annotation.NonNull;
23 import org.opendaylight.yangtools.concepts.Immutable;
24 import org.opendaylight.yangtools.yang.binding.DataObject;
25 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
26 import org.slf4j.Logger;
27 import org.slf4j.LoggerFactory;
30 * Lazily-populated List implementation backed by NormalizedNodes. This implementation defers creating Binding objects
31 * until they are actually needed, caching them in a pre-allocated array.
34 * The cost of this deferred instantiation is two-fold:
36 * <li>each access issues a {@link VarHandle#getAcquire(Object...)} load and a class equality check</li>
37 * <li>initial load additionally incurs a {@link VarHandle#compareAndExchangeRelease(Object...)} store</li>
40 * @param <E> the type of elements in this list
42 final class LazyBindingList<E extends DataObject> extends AbstractList<E> implements Immutable, RandomAccess {
43 // Object array access variable handle
44 static final VarHandle OBJ_AA = MethodHandles.arrayElementVarHandle(Object[].class);
46 private static final Logger LOG = LoggerFactory.getLogger(LazyBindingList.class);
47 private static final String LAZY_CUTOFF_PROPERTY =
48 "org.opendaylight.mdsal.binding.dom.codec.impl.LazyBindingList.max-eager-elements";
49 private static final int DEFAULT_LAZY_CUTOFF = 16;
52 static final int LAZY_CUTOFF;
55 final int value = Integer.getInteger(LAZY_CUTOFF_PROPERTY, DEFAULT_LAZY_CUTOFF);
57 LOG.info("Lazy population of lists disabled");
58 LAZY_CUTOFF = Integer.MAX_VALUE;
60 LOG.info("Using lazy population for lists larger than {} element(s)", value);
65 private final ListNodeCodecContext<E> codec;
66 private final Object[] objects;
68 private LazyBindingList(final ListNodeCodecContext<E> codec,
69 final Collection<? extends NormalizedNodeContainer<?, ?, ?>> entries) {
70 this.codec = requireNonNull(codec);
71 objects = entries.toArray();
74 static <E extends DataObject> @NonNull List<E> create(final ListNodeCodecContext<E> codec, final int size,
75 final Collection<? extends NormalizedNodeContainer<?, ?, ?>> entries) {
77 // Do not bother with lazy instantiation in case of a singleton
78 return List.of(codec.createBindingProxy(entries.iterator().next()));
80 return size > LAZY_CUTOFF ? new LazyBindingList<>(codec, entries) : eagerList(codec, size, entries);
83 private static <E extends DataObject> @NonNull List<E> eagerList(final ListNodeCodecContext<E> codec,
84 final int size, final Collection<? extends NormalizedNodeContainer<?, ?, ?>> entries) {
85 @SuppressWarnings("unchecked")
86 final E[] objs = (E[]) new DataObject[size];
88 for (NormalizedNodeContainer<?, ?, ?> node : entries) {
89 objs[offset++] = codec.createBindingProxy(node);
91 verify(offset == objs.length);
97 return objects.length;
101 public E get(final int index) {
102 final Object obj = OBJ_AA.getAcquire(objects, index);
103 // Check whether the object has been converted. The object is always non-null, but it can either be in DOM form
104 // (either a MapEntryNode or UnkeyedListEntryNode) or in Binding form. We know the exact class for the latter,
105 // as we are creating it via codec -- hence we can perform a direct comparison.
107 // We could do a Class.isInstance() check here, but since the implementation is not marked as final (yet) we
108 // would be at the mercy of CHA being able to prove this invariant.
109 return obj.getClass() == codec.generatedClass() ? (E) obj : load(index, (NormalizedNodeContainer<?, ?, ?>) obj);
112 private @NonNull E load(final int index, final NormalizedNodeContainer<?, ?, ?> node) {
113 final E ret = codec.createBindingProxy(node);
114 final Object witness;
115 return (witness = OBJ_AA.compareAndExchangeRelease(objects, index, node, ret)) == node ? ret : (E) witness;
119 @SuppressWarnings("checkstyle:parameterName")
120 public boolean remove(final Object o) {
125 @SuppressWarnings("checkstyle:parameterName")
126 public boolean addAll(final Collection<? extends E> c) {
131 @SuppressWarnings("checkstyle:parameterName")
132 public boolean addAll(final int index, final Collection<? extends E> c) {
137 @SuppressWarnings("checkstyle:parameterName")
138 public boolean removeAll(final Collection<?> c) {
143 @SuppressWarnings("checkstyle:parameterName")
144 public boolean retainAll(final Collection<?> c) {
149 @SuppressWarnings("checkstyle:parameterName")
150 public void sort(final Comparator<? super E> c) {
155 public void replaceAll(final UnaryOperator<E> operator) {
160 protected void removeRange(final int fromIndex, final int toIndex) {
164 private static UnsupportedOperationException uoe() {
165 return new UnsupportedOperationException("Modification not supported");