import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder;
import org.opendaylight.yangtools.yang.data.impl.schema.nodes.AbstractImmutableDataContainerNode;
import org.opendaylight.yangtools.yang.data.impl.schema.nodes.CloneableMap;
+import org.opendaylight.yangtools.yang.data.impl.schema.nodes.LazyLeafOperations;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
}
}
- private Map<PathArgument, DataContainerChild<? extends PathArgument, ?>> value;
+ private Map<PathArgument, Object> value;
private I nodeIdentifier;
/*
}
protected final DataContainerChild<? extends PathArgument, ?> getChild(final PathArgument child) {
- return value.get(child);
+ return LazyLeafOperations.getChild(value, child);
}
- protected final Map<PathArgument, DataContainerChild<? extends PathArgument, ?>> buildValue() {
+ protected final Map<PathArgument, Object> buildValue() {
if (value instanceof ModifiableMapPhase) {
- return ((ModifiableMapPhase<PathArgument, DataContainerChild<? extends PathArgument, ?>>)value)
- .toUnmodifiableMap();
+ return ((ModifiableMapPhase<PathArgument, Object>)value).toUnmodifiableMap();
}
dirty = true;
private void checkDirty() {
if (dirty) {
if (value instanceof UnmodifiableMapPhase) {
- value = ((UnmodifiableMapPhase<PathArgument, DataContainerChild<? extends PathArgument, ?>>) value)
- .toModifiableMap();
+ value = ((UnmodifiableMapPhase<PathArgument, Object>) value).toModifiableMap();
} else if (value instanceof CloneableMap) {
- value = ((CloneableMap<PathArgument, DataContainerChild<? extends PathArgument, ?>>) value)
- .createMutableClone();
+ value = ((CloneableMap<PathArgument, Object>) value).createMutableClone();
} else {
value = newHashMap(value);
}
@Override
public DataContainerNodeBuilder<I, R> withChild(final DataContainerChild<?, ?> child) {
checkDirty();
- this.value.put(child.getIdentifier(), child);
+ LazyLeafOperations.putChild(value, child);
return this;
}
extends AbstractImmutableDataContainerNode<AugmentationIdentifier> implements AugmentationNode {
ImmutableAugmentationNode(final AugmentationIdentifier nodeIdentifier,
- final Map<PathArgument, DataContainerChild<? extends PathArgument, ?>> children) {
+ final Map<PathArgument, Object> children) {
super(children, nodeIdentifier);
}
}
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
-import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
import org.opendaylight.yangtools.yang.data.impl.schema.nodes.AbstractImmutableDataContainerNode;
private static final class ImmutableChoiceNode extends AbstractImmutableDataContainerNode<NodeIdentifier>
implements ChoiceNode {
- ImmutableChoiceNode(final NodeIdentifier nodeIdentifier,
- final Map<PathArgument, DataContainerChild<? extends PathArgument, ?>> children) {
+ ImmutableChoiceNode(final NodeIdentifier nodeIdentifier, final Map<PathArgument, Object> children) {
super(children, nodeIdentifier);
}
}
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
-import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
import org.opendaylight.yangtools.yang.data.impl.schema.nodes.AbstractImmutableDataContainerNode;
protected static final class ImmutableContainerNode extends AbstractImmutableDataContainerNode<NodeIdentifier>
implements ContainerNode {
- ImmutableContainerNode(final NodeIdentifier nodeIdentifier,
- final Map<PathArgument, DataContainerChild<? extends PathArgument, ?>> children) {
+ ImmutableContainerNode(final NodeIdentifier nodeIdentifier, final Map<PathArgument, Object> children) {
super(children, nodeIdentifier);
}
}
}
}
-
@Override
public DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> withValue(
final Collection<DataContainerChild<? extends PathArgument, ?>> withValue) {
extends AbstractImmutableDataContainerNode<NodeIdentifierWithPredicates> implements MapEntryNode {
ImmutableMapEntryNode(final NodeIdentifierWithPredicates nodeIdentifier,
- final Map<PathArgument, DataContainerChild<? extends PathArgument, ?>> children) {
+ final Map<PathArgument, Object> children) {
super(children, nodeIdentifier);
}
}
import org.eclipse.jdt.annotation.NonNull;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
-import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
import org.opendaylight.yangtools.yang.data.impl.schema.nodes.AbstractImmutableDataContainerNode;
protected static final class ImmutableUnkeyedListEntryNode
extends AbstractImmutableDataContainerNode<NodeIdentifier> implements UnkeyedListEntryNode {
- ImmutableUnkeyedListEntryNode(final NodeIdentifier nodeIdentifier,
- final Map<PathArgument, DataContainerChild<? extends PathArgument, ?>> children) {
+ ImmutableUnkeyedListEntryNode(final NodeIdentifier nodeIdentifier, final Map<PathArgument, Object> children) {
super(children, nodeIdentifier);
}
}
import org.opendaylight.yangtools.odlext.model.api.YangModeledAnyXmlSchemaNode;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
-import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
import org.opendaylight.yangtools.yang.data.api.schema.YangModeledAnyXmlNode;
import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
import org.opendaylight.yangtools.yang.data.impl.schema.nodes.AbstractImmutableDataContainerNode;
private final @NonNull ContainerSchemaNode contentSchema;
- ImmutableYangModeledAnyXmlNode(final NodeIdentifier nodeIdentifier,
- final Map<PathArgument, DataContainerChild<? extends PathArgument, ?>> value,
+ ImmutableYangModeledAnyXmlNode(final NodeIdentifier nodeIdentifier, final Map<PathArgument, Object> value,
final ContainerSchemaNode contentSchema) {
super(value, nodeIdentifier);
this.contentSchema = requireNonNull(contentSchema, "Schema of yang modeled anyXml content cannot be null.");
public abstract class AbstractImmutableDataContainerNode<K extends PathArgument>
extends AbstractImmutableNormalizedNode<K, Collection<DataContainerChild<? extends PathArgument, ?>>>
implements DataContainerNode<K> {
- private final Map<PathArgument, DataContainerChild<? extends PathArgument, ?>> children;
+ private final Map<PathArgument, Object> children;
- public AbstractImmutableDataContainerNode(
- final Map<PathArgument, DataContainerChild<? extends PathArgument, ?>> children, final K nodeIdentifier) {
+ protected AbstractImmutableDataContainerNode(final Map<PathArgument, Object> children, final K nodeIdentifier) {
super(nodeIdentifier);
this.children = ImmutableOffsetMap.unorderedCopyOf(children);
@Override
public final Optional<DataContainerChild<? extends PathArgument, ?>> getChild(final PathArgument child) {
- return Optional.ofNullable(children.get(child));
+ return LazyLeafOperations.findChild(children, child);
}
@Override
public final Collection<DataContainerChild<? extends PathArgument, ?>> getValue() {
- return children.values();
+ return LazyLeafOperations.getValue(children);
}
@Override
* DO NOT USE THIS METHOD.
*
* <p>
- * This is an implementation-internal API and no outside users should use it. If you do,
- * you are asking for trouble, as the returned object is not guaranteed to conform to
- * java.util.Map interface.
+ * This is an implementation-internal API and no outside users should use it. If you do, you are asking for trouble,
+ * as the returned object is not guaranteed to conform to java.util.Map interface, nor is its contents well-defined.
*
* @return An unmodifiable view if this node's children.
*/
- public final Map<PathArgument, DataContainerChild<? extends PathArgument, ?>> getChildren() {
+ public final Map<PathArgument, Object> getChildren() {
return children;
}
protected boolean valueEquals(final AbstractImmutableNormalizedNode<?, ?> other) {
return other instanceof AbstractImmutableDataContainerNode<?> && children.equals(
((AbstractImmutableDataContainerNode<?>) other).children);
-
}
}
--- /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 com.google.common.base.Verify.verify;
+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;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Support utilities for dealing with Maps which would normally hold {@link DataContainerChild} values, but are modified
+ * to eliminate {@link LeafNode} instances.
+ *
+ * <p>
+ * This class holds implementation logic which controls lifecycle of {@link LeafNode}s by providing a central policy
+ * point for how the implementation treats these nodes. There are two modes of operation:
+ * <ul>
+ * <li>eager, which means leaf nodes are retained by their parent<li>
+ * <li>lazy, which means leaf nodes are created whenever they are queried and no attempt is made to retain them</li>
+ * </ul>
+ *
+ * <p>
+ * Selection of the mode in effect is available through {@value #EXPENDABLE_PROP_NAME} system property.
+ */
+@Beta
+public final class LazyLeafOperations {
+ private static final Logger LOG = LoggerFactory.getLogger(LazyLeafOperations.class);
+ private static final String EXPENDABLE_PROP_NAME =
+ "org.opendaylight.yangtools.yang.data.impl.schema.nodes.lazy-leaves";
+
+ /**
+ * Global enabled run-time constant. If set to true, this class will treat {@link LeafNode} and
+ * {@link LeafSetEntryNode} as an expendable object. This constant is controlled by {@value #EXPENDABLE_PROP_NAME}
+ * system property.
+ */
+ private static final boolean EXPENDABLE;
+
+ static {
+ EXPENDABLE = Boolean.parseBoolean(System.getProperty(EXPENDABLE_PROP_NAME, "true"));
+ LOG.info("Leaf nodes are treated as {} nodes", EXPENDABLE ? "transient" : "regular");
+ }
+
+ private LazyLeafOperations() {
+
+ }
+
+ public static Optional<DataContainerChild<?, ?>> findChild(final Map<PathArgument, Object> map,
+ final PathArgument key) {
+ final Object value = map.get(key);
+ return value == null ? Optional.empty() : Optional.of(decodeChild(key, value));
+ }
+
+ public static @Nullable DataContainerChild<?, ?> getChild(final Map<PathArgument, Object> map,
+ final PathArgument key) {
+ final Object value = map.get(key);
+ return value == null ? null : decodeChild(key, value);
+ }
+
+ public static void putChild(final Map<PathArgument, Object> map, final DataContainerChild<?, ?> child) {
+ final DataContainerChild<?, ?> node = requireNonNull(child);
+ map.put(child.getIdentifier(), EXPENDABLE ? encodeExpendableChild(node) : node);
+ }
+
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ public static @NonNull Collection<DataContainerChild<?, ?>> getValue(final Map<PathArgument, Object> map) {
+ return EXPENDABLE ? Collections2.transform(map.entrySet(), LazyLeafOperations::decodeEntry)
+ // This is an ugly cast, but it is accurate IFF all modifications are done through this class
+ : (Collection) map.values();
+ }
+
+ private static @NonNull Object encodeExpendableChild(final DataContainerChild<?, ?> key) {
+ return key instanceof LeafNode ? ((LeafNode<?>) key).getValue() : requireNonNull(key);
+ }
+
+ private static @Nullable DataContainerChild<?, ?> decodeChild(final PathArgument key, final @NonNull Object value) {
+ return EXPENDABLE ? decodeExpendableChild(key, value) : verifyCast(value);
+ }
+
+ private static @NonNull DataContainerChild<?, ?> decodeExpendableChild(final PathArgument key,
+ @NonNull final Object value) {
+ return value instanceof DataContainerChild ? (DataContainerChild<?, ?>) value : coerceLeaf(key, value);
+ }
+
+ 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);
+ }
+}
package org.opendaylight.yangtools.yang.data.jaxen;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
final Object derefResult = derefFunction.call(normalizedNodeContext, ImmutableList.of());
assertNotNull(derefResult);
assertTrue(derefResult instanceof NormalizedNode<?, ?>);
- assertSame(referencedLeafNode, derefResult);
+ assertEquals(referencedLeafNode, derefResult);
}
@Test
Object derefResult = derefFunction.call(normalizedNodeContext, ImmutableList.of());
assertNotNull(derefResult);
assertTrue(derefResult instanceof NormalizedNode<?, ?>);
- assertSame(referencedLeafNode, derefResult);
+ assertEquals(referencedLeafNode, 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<?, ?>);
- assertSame(referencedLeafNode, derefResult);
+ assertEquals(referencedLeafNode, derefResult);
}
@Test