Promote InterningLeafSetNodeBuilder 08/109408/1
authorRobert Varga <robert.varga@pantheon.tech>
Fri, 22 Dec 2023 23:34:06 +0000 (00:34 +0100)
committerRobert Varga <robert.varga@pantheon.tech>
Fri, 22 Dec 2023 23:35:21 +0000 (00:35 +0100)
This is a useful utility, promote it to data.spi.node, integrating
LeafsetEntryInterner.

JIRA: YANGTOOLS-980
Change-Id: Idd49b9e85fc96d9fb9d80541ce720efb92466225
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
data/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/ImmutableNormalizedNodeStreamWriter.java
data/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/InterningLeafSetNodeBuilder.java [deleted file]
data/yang-data-spi/src/main/java/org/opendaylight/yangtools/yang/data/spi/node/InterningLeafSetNodeBuilder.java [new file with mode: 0644]
data/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/LeafsetEntryInterner.java [deleted file]

index c2c3b98e429ccc803dc110a0b4354e96733f91c5..800bff0d9756e6051cb5e1b9826712949f911b61 100644 (file)
@@ -11,11 +11,14 @@ import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkState;
 import static java.util.Objects.requireNonNull;
 
+import com.google.common.collect.Interner;
+import com.google.common.collect.Interners;
 import java.io.IOException;
 import java.util.ArrayDeque;
 import java.util.Deque;
 import javax.xml.transform.dom.DOMSource;
 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.NodeIdentifierWithPredicates;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
@@ -23,6 +26,7 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgum
 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.SystemLeafSetNode.Builder;
 import org.opendaylight.yangtools.yang.data.api.schema.builder.DataContainerNodeBuilder;
 import org.opendaylight.yangtools.yang.data.api.schema.builder.NormalizedNodeBuilder;
 import org.opendaylight.yangtools.yang.data.api.schema.builder.NormalizedNodeContainerBuilder;
@@ -33,9 +37,14 @@ import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableUn
 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableUserLeafSetNodeBuilder;
 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableUserMapNodeBuilder;
 import org.opendaylight.yangtools.yang.data.spi.node.InterningLeafNodeBuilder;
+import org.opendaylight.yangtools.yang.data.spi.node.InterningLeafSetNodeBuilder;
 import org.opendaylight.yangtools.yang.data.util.LeafInterner;
 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.type.BooleanTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
 
 /**
  * Implementation of {@link NormalizedNodeStreamWriter}, which constructs immutable instances of
@@ -53,6 +62,8 @@ import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
  * This class is not final for purposes of customization, normal users should not need to subclass it.
  */
 public class ImmutableNormalizedNodeStreamWriter implements NormalizedNodeStreamWriter {
+    private static final Interner<LeafSetEntryNode<?>> ENTRY_INTERNER = Interners.newWeakInterner();
+
     private final Deque<NormalizedNode.Builder> builders = new ArrayDeque<>();
 
     private DataSchemaNode nextSchema;
@@ -112,8 +123,20 @@ public class ImmutableNormalizedNodeStreamWriter implements NormalizedNodeStream
     @Override
     public void startLeafSet(final NodeIdentifier name, final int childSizeHint) {
         checkDataNodeContainer();
-        enter(name, UNKNOWN_SIZE == childSizeHint ? InterningLeafSetNodeBuilder.create(nextSchema)
-                : InterningLeafSetNodeBuilder.create(nextSchema, childSizeHint));
+        final var builder = UNKNOWN_SIZE == childSizeHint ? Builders.leafSetBuilder()
+            : Builders.leafSetBuilder(childSizeHint);
+        enter(name, leafSetNodeBuilder(builder, nextSchema));
+    }
+
+    private static <T> Builder<T> leafSetNodeBuilder(final Builder<T> delegate, final @Nullable DataSchemaNode schema) {
+        if (schema instanceof LeafListSchemaNode leafListSchema) {
+            final var type = leafListSchema.getType();
+            if (type instanceof BooleanTypeDefinition || type instanceof EnumTypeDefinition
+                    || type instanceof IdentityrefTypeDefinition) {
+                return new InterningLeafSetNodeBuilder<>(delegate, (Interner) ENTRY_INTERNER);
+            }
+        }
+        return delegate;
     }
 
     @Override
diff --git a/data/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/InterningLeafSetNodeBuilder.java b/data/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/InterningLeafSetNodeBuilder.java
deleted file mode 100644 (file)
index b8ff249..0000000
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (c) 2015 Cisco Systems, Inc. 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;
-
-import static java.util.Objects.requireNonNull;
-
-import java.util.Collection;
-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.LeafSetEntryNode;
-import org.opendaylight.yangtools.yang.data.api.schema.SystemLeafSetNode;
-import org.opendaylight.yangtools.yang.data.api.schema.builder.ListNodeBuilder;
-import org.opendaylight.yangtools.yang.data.api.schema.builder.NormalizedNodeContainerBuilder;
-import org.opendaylight.yangtools.yang.data.util.LeafsetEntryInterner;
-import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
-
-final class InterningLeafSetNodeBuilder<T> implements SystemLeafSetNode.Builder<T> {
-    private final SystemLeafSetNode.Builder<T> delegate;
-    private final LeafsetEntryInterner interner;
-
-    private InterningLeafSetNodeBuilder(final SystemLeafSetNode.Builder<T> delegate,
-            final LeafsetEntryInterner interner) {
-        this.delegate = requireNonNull(delegate);
-        this.interner = requireNonNull(interner);
-    }
-
-    private static @Nullable LeafsetEntryInterner getInterner(final @Nullable DataSchemaNode schema) {
-        return schema instanceof LeafListSchemaNode leafListSchema ? LeafsetEntryInterner.forSchema(leafListSchema)
-                : null;
-    }
-
-    static <T> ListNodeBuilder<T, SystemLeafSetNode<T>> create(final @Nullable DataSchemaNode schema) {
-        final var delegate = Builders.<T>leafSetBuilder();
-        final var interner = getInterner(schema);
-        return interner == null ? delegate : new InterningLeafSetNodeBuilder<>(delegate, interner);
-    }
-
-    static <T> ListNodeBuilder<T, SystemLeafSetNode<T>> create(final @Nullable DataSchemaNode schema,
-            final int sizeHint) {
-        final var delegate = Builders.<T>leafSetBuilder(sizeHint);
-        final var interner = getInterner(schema);
-        return interner == null ? delegate : new InterningLeafSetNodeBuilder<>(delegate, interner);
-    }
-
-    @Override
-    public ListNodeBuilder<T, SystemLeafSetNode<T>> withNodeIdentifier(final NodeIdentifier nodeIdentifier) {
-        return delegate.withNodeIdentifier(nodeIdentifier);
-    }
-
-    @Override
-    public ListNodeBuilder<T, SystemLeafSetNode<T>> withValue(final Collection<LeafSetEntryNode<T>> value) {
-        // FIXME: pass through interner
-        return delegate.withValue(value);
-    }
-
-    @Override
-    public ListNodeBuilder<T, SystemLeafSetNode<T>> withChild(final LeafSetEntryNode<T> child) {
-        return delegate.withChild(interner.intern(child));
-    }
-
-    @Override
-    public ListNodeBuilder<T, SystemLeafSetNode<T>> withoutChild(final PathArgument key) {
-        return delegate.withoutChild(key);
-    }
-
-    @Override
-    public ListNodeBuilder<T, SystemLeafSetNode<T>> withChildValue(final T child) {
-        // TODO Auto-generated method stub
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public NormalizedNodeContainerBuilder<NodeIdentifier, PathArgument, LeafSetEntryNode<T>, SystemLeafSetNode<T>>
-            addChild(final LeafSetEntryNode<T> child) {
-        return withChild(child);
-    }
-
-    @Override
-    public NormalizedNodeContainerBuilder<NodeIdentifier, PathArgument, LeafSetEntryNode<T>, SystemLeafSetNode<T>>
-            removeChild(final PathArgument key) {
-        return withoutChild(key);
-    }
-
-    @Override
-    public SystemLeafSetNode<T> build() {
-        return delegate.build();
-    }
-}
diff --git a/data/yang-data-spi/src/main/java/org/opendaylight/yangtools/yang/data/spi/node/InterningLeafSetNodeBuilder.java b/data/yang-data-spi/src/main/java/org/opendaylight/yangtools/yang/data/spi/node/InterningLeafSetNodeBuilder.java
new file mode 100644 (file)
index 0000000..4f238ee
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. 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.spi.node;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.Interner;
+import java.util.Collection;
+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.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.SystemLeafSetNode;
+import org.opendaylight.yangtools.yang.data.api.schema.SystemLeafSetNode.Builder;
+
+/**
+ * Utility class for sharing instances of {@link LeafSetEntryNode}s which have low cardinality -- e.g. those which hold
+ * boolean or enumeration values. Instances containing attributes are not interned.
+ *
+ * <p>
+ * Such objects have cardinality which is capped at the product of QNAMES * TYPE_CARDINALITY, where QNAMES is the total
+ * number of different QNames where the type is used and TYPE_CARDINALITY is the number of possible values for the type.
+ * Boolean has cardinality of 2, enumerations have cardinality equal to the number of enum statements.
+ *
+ * <p>
+ * The theory here is that we tend to have a large number (100K+) of entries in a few places, which could end up hogging
+ * the heap retained via the DataTree with duplicate objects (same QName, same value, different object). Using this
+ * utility, such objects will end up reusing the same object, preventing this overhead.
+ */
+public final class InterningLeafSetNodeBuilder<T> implements Builder<T> {
+    private final Interner<LeafSetEntryNode<T>> interner;
+    private final Builder<T> delegate;
+
+    public InterningLeafSetNodeBuilder(final Builder<T> delegate, final Interner<LeafSetEntryNode<T>> interner) {
+        this.delegate = requireNonNull(delegate);
+        this.interner = requireNonNull(interner);
+    }
+
+    @Override
+    public Builder<T> withNodeIdentifier(final NodeIdentifier nodeIdentifier) {
+        delegate.withNodeIdentifier(nodeIdentifier);
+        return this;
+    }
+
+    @Override
+    public Builder<T> withValue(final Collection<LeafSetEntryNode<T>> value) {
+        // FIXME: pass through interner
+        delegate.withValue(value);
+        return this;
+    }
+
+    @Override
+    public Builder<T> withChild(final LeafSetEntryNode<T> child) {
+        delegate.withChild(interner.intern(child));
+        return this;
+    }
+
+    @Override
+    public Builder<T> withoutChild(final PathArgument key) {
+        delegate.withoutChild(key);
+        return this;
+    }
+
+    @Override
+    public Builder<T> withChildValue(final T child) {
+        // FIXME: implement this
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Builder<T> addChild(final LeafSetEntryNode<T> child) {
+        return withChild(child);
+    }
+
+    @Override
+    public Builder<T> removeChild(final PathArgument key) {
+        return withoutChild(key);
+    }
+
+    @Override
+    public SystemLeafSetNode<T> build() {
+        return delegate.build();
+    }
+}
diff --git a/data/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/LeafsetEntryInterner.java b/data/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/LeafsetEntryInterner.java
deleted file mode 100644 (file)
index b43662a..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (c) 2015 Cisco Systems, Inc. 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.util;
-
-import com.google.common.annotations.Beta;
-import com.google.common.collect.Interner;
-import com.google.common.collect.Interners;
-import org.eclipse.jdt.annotation.NonNull;
-import org.eclipse.jdt.annotation.Nullable;
-import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
-import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
-import org.opendaylight.yangtools.yang.model.api.type.BooleanTypeDefinition;
-import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
-import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Utility class for sharing instances of {@link LeafSetEntryNode}s which have low cardinality -- e.g. those which hold
- * boolean or enumeration values. Instances containing attributes are not interned.
- *
- * <p>
- * Such objects have cardinality which is capped at the product of QNAMES * TYPE_CARDINALITY, where QNAMES is the total
- * number of different QNames where the type is used and TYPE_CARDINALITY is the number of possible values for the type.
- * Boolean has cardinality of 2, enumerations have cardinality equal to the number of enum statements.
- *
- * <p>
- * The theory here is that we tend to have a large number (100K+) of entries in a few places, which could end up hogging
- * the heap retained via the DataTree with duplicate objects (same QName, same value, different object). Using this
- * utility, such objects will end up reusing the same object, preventing this overhead.
- */
-@Beta
-public final class LeafsetEntryInterner {
-    private static final Logger LOG = LoggerFactory.getLogger(LeafsetEntryInterner.class);
-    private static final LeafsetEntryInterner INSTANCE = new LeafsetEntryInterner();
-    private static final Interner<Object> INTERNER = Interners.newWeakInterner();
-
-    private LeafsetEntryInterner() {
-
-    }
-
-    @SuppressWarnings("static-method")
-    public <T extends LeafSetEntryNode<?>> @NonNull T intern(final @NonNull T sample) {
-        /*
-         * We do not perform type checks here as they are implied by #forSchema(LeafListSchemaNode). Any misuse can
-         * result in inappropriate candidates being interned, but the alternative would be quite a bit slower.
-         */
-        @SuppressWarnings("unchecked")
-        final T ret = (T) INTERNER.intern(sample);
-        LOG.trace("Interned object {} to {}", sample, ret);
-        return ret;
-    }
-
-    /**
-     * Return a {@link LeafsetEntryInterner} for a particular schema. Interner instances must be used only for leafset
-     * entries for that particular schema, otherwise they may produce unexpected results.
-     *
-     * @param schema Schema of the parent leaf set
-     * @return An interner instance, or null if the leafset's type should not be interned.
-     */
-    public static @Nullable LeafsetEntryInterner forSchema(final @Nullable LeafListSchemaNode schema) {
-        if (schema != null) {
-            final TypeDefinition<?> type = schema.getType();
-            if (type instanceof BooleanTypeDefinition || type instanceof EnumTypeDefinition
-                    || type instanceof IdentityrefTypeDefinition) {
-                return INSTANCE;
-            }
-        }
-        return null;
-    }
-}