import static java.util.Objects.requireNonNull;
import com.google.common.annotations.Beta;
-import com.google.common.collect.Collections2;
import java.util.Collection;
import java.util.Map;
-import java.util.Map.Entry;
import java.util.Optional;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
}
+ /**
+ * A boolean flag indicating whether leaf nodes are being treated as expendable.
+ *
+ * @return True if NormalizedNode implementations in this artifact are treating leaf nodes as transient, i.e. do
+ * not retain them.
+ */
+ public static boolean isEnabled() {
+ return EXPENDABLE;
+ }
+
public static Optional<DataContainerChild<?, ?>> findChild(final Map<PathArgument, Object> map,
final PathArgument key) {
final Object value = map.get(key);
@SuppressWarnings({ "rawtypes", "unchecked" })
public static @NonNull Collection<DataContainerChild<?, ?>> getValue(final Map<PathArgument, Object> map) {
- return EXPENDABLE ? Collections2.transform(map.entrySet(), LazyLeafOperations::decodeEntry)
+ return EXPENDABLE ? new LazyValues(map)
// This is an ugly cast, but it is accurate IFF all modifications are done through this class
- : (Collection) map.values();
+ : (Collection)map.values();
}
- private static @NonNull Object encodeExpendableChild(final DataContainerChild<?, ?> key) {
- return key instanceof LeafNode ? ((LeafNode<?>) key).getValue() : requireNonNull(key);
+ static @NonNull LeafNode<?> coerceLeaf(final PathArgument key, final Object value) {
+ verify(key instanceof NodeIdentifier, "Unexpected value %s for child %s", value, key);
+ return ImmutableNodes.leafNode((NodeIdentifier) key, value);
}
private static @Nullable DataContainerChild<?, ?> decodeChild(final PathArgument key, final @NonNull Object value) {
return value instanceof DataContainerChild ? (DataContainerChild<?, ?>) value : coerceLeaf(key, value);
}
+ private static @NonNull Object encodeExpendableChild(final DataContainerChild<?, ?> key) {
+ return key instanceof LeafNode ? ((LeafNode<?>) key).getValue() : requireNonNull(key);
+ }
+
private static @NonNull DataContainerChild<?, ?> verifyCast(final @NonNull Object value) {
verify(value instanceof DataContainerChild, "Unexpected child %s", value);
return (DataContainerChild<?, ?>)value;
}
-
- private static @NonNull DataContainerChild<? extends PathArgument, ?> decodeEntry(
- final @NonNull Entry<PathArgument, Object> entry) {
- final Object value = entry.getValue();
- return value instanceof DataContainerChild ? (DataContainerChild<?, ?>) value
- : coerceLeaf(entry.getKey(), value);
- }
-
- private static @NonNull LeafNode<?> coerceLeaf(final PathArgument key, final Object value) {
- verify(key instanceof NodeIdentifier, "Unexpected value %s for child %s", value, key);
- return ImmutableNodes.leafNode((NodeIdentifier) key, value);
- }
}
--- /dev/null
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.impl.schema.nodes;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.AbstractCollection;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+
+// This is *almost* the same as Guava's TransformedCollection. The main difference is delegation of hashCode()/equals()
+// towards the backing map. This is needed because we do not retain a reference to this object and thus
+// NormalizedNode.getValue() does not compare as equal. When invoked twice and lazy leaves are in effect. Note that
+// Collection.equals() is undefined, but the expectation from users is that we will return the same view object, which
+// equals on identity.
+final class LazyValues extends AbstractCollection<DataContainerChild<?, ?>> {
+ private final Map<PathArgument, Object> map;
+
+ LazyValues(final Map<PathArgument, Object> map) {
+ this.map = requireNonNull(map);
+ }
+
+ @Override
+ public int size() {
+ return map.size();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return map.isEmpty();
+ }
+
+ @Override
+ public Iterator<DataContainerChild<?, ?>> iterator() {
+ return new Iter(map.entrySet().iterator());
+ }
+
+ @Override
+ public int hashCode() {
+ return map.hashCode();
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ return this == obj || obj instanceof LazyValues && map.equals(((LazyValues)obj).map);
+ }
+
+ private static final class Iter implements Iterator<DataContainerChild<?, ?>> {
+ private final Iterator<Entry<PathArgument, Object>> iterator;
+
+ Iter(final Iterator<Entry<PathArgument, Object>> iterator) {
+ this.iterator = requireNonNull(iterator);
+ }
+
+ @Override
+ public boolean hasNext() {
+ return iterator.hasNext();
+ }
+
+ @Override
+ public DataContainerChild<?, ?> next() {
+ final Entry<PathArgument, Object> entry = iterator.next();
+ final Object value = entry.getValue();
+ return value instanceof DataContainerChild ? (DataContainerChild<?, ?>) value
+ : LazyLeafOperations.coerceLeaf(entry.getKey(), value);
+ }
+ }
+}
import org.opendaylight.yangtools.yang.data.api.schema.xpath.XPathDocument;
import org.opendaylight.yangtools.yang.data.api.schema.xpath.XPathSchemaContext;
import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.nodes.LazyLeafOperations;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
.getFunction(null, null, "deref");
final Object derefResult = derefFunction.call(normalizedNodeContext, ImmutableList.of());
assertNotNull(derefResult);
- assertTrue(derefResult instanceof NormalizedNode<?, ?>);
- assertEquals(referencedLeafNode, derefResult);
+ assertTrue(derefResult instanceof LeafNode<?>);
+ assertLeafEquals(referencedLeafNode, (LeafNode<?>) derefResult);
}
@Test
.getFunction(null, null, "deref");
Object derefResult = derefFunction.call(normalizedNodeContext, ImmutableList.of());
assertNotNull(derefResult);
- assertTrue(derefResult instanceof NormalizedNode<?, ?>);
- assertEquals(referencedLeafNode, derefResult);
+ assertTrue(derefResult instanceof LeafNode<?>);
+ assertLeafEquals(referencedLeafNode, (LeafNode<?>) derefResult);
final YangInstanceIdentifier relLeafrefPath = YangInstanceIdentifier.of(MY_INNER_CONTAINER)
.node(REL_LEAFREF_LEAF);
derefResult = derefFunction.call(normalizedNodeContext, ImmutableList.of());
assertNotNull(derefResult);
- assertTrue(derefResult instanceof NormalizedNode<?, ?>);
- assertEquals(referencedLeafNode, derefResult);
+ assertTrue(derefResult instanceof LeafNode<?>);
+ assertLeafEquals(referencedLeafNode, (LeafNode<?>) derefResult);
}
@Test
.withChild(myInnerContainerNode).build();
return myContainerNode;
}
-}
\ No newline at end of file
+
+ private static void assertLeafEquals(final LeafNode<?> expected, final LeafNode<?> actual) {
+ if (LazyLeafOperations.isEnabled()) {
+ assertEquals(expected, actual);
+ } else {
+ assertSame(expected, actual);
+ }
+ }
+}