BUG-4261; split NormalizedNodeInterners 45/28245/3
authorRobert Varga <rovarga@cisco.com>
Sat, 10 Oct 2015 11:46:55 +0000 (13:46 +0200)
committerGerrit Code Review <gerrit@opendaylight.org>
Mon, 12 Oct 2015 08:53:50 +0000 (08:53 +0000)
As it turns out, having separate classes is easier for users.
LeafsetEntryInterner can be used to check the type once and then rely on
this check. LeafInterner returns a no-op instance for convenience if the
schema is not available.

Change-Id: Iec2117be2f91114943d2896a6bfb273f683ea042
Signed-off-by: Robert Varga <rovarga@cisco.com>
yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/LeafInterner.java [new file with mode: 0644]
yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/LeafsetEntryInterner.java [new file with mode: 0644]
yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/NormalizedNodeInterners.java [deleted file]

diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/LeafInterner.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/LeafInterner.java
new file mode 100644 (file)
index 0000000..7070e77
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * 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 javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import org.opendaylight.yangtools.yang.data.api.AttributesContainer;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Utility class for sharing instances of {@link LeafNode}s which have low cardinality-- e.g. those which hold
+ * boolean or enumeration values. Instances containing attributes are not interned.
+ *
+ * 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.
+ *
+ * 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 abstract class LeafInterner {
+    private static final class Noop extends LeafInterner {
+        @Override
+        public <T extends LeafNode<?>> T intern(final T sample) {
+            return sample;
+        }
+    }
+
+    private static final class Weak extends LeafInterner {
+        private static final Logger LOG = LoggerFactory.getLogger(Weak.class);
+        private static final Interner<Object> INTERNER = Interners.newWeakInterner();
+
+        @Override
+        public <T extends LeafNode<?>> T intern(final T sample) {
+            if (!((AttributesContainer) sample).getAttributes().isEmpty()) {
+                // Non-empty attributes, do not intern
+                return sample;
+            }
+
+            // All checks completed, intern the sample
+            @SuppressWarnings("unchecked")
+            final T ret = (T) INTERNER.intern(sample);
+            LOG.trace("Interned object %s to %s", sample, ret);
+            return ret;
+        }
+    }
+
+    private static final LeafInterner NOOP = new Noop();
+    private static final LeafInterner WEAK = new Weak();
+
+    LeafInterner() {
+
+    }
+
+    /**
+     * Return a {@link LeafInterner} for a particular schema. Interner instances must not be reused for leaves of
+     * different types, otherwise they may produce unexpected results.
+     *
+     * @param schema The leaf node's schema
+     * @return An interner instance
+     */
+    @Nonnull public static LeafInterner forSchema(@Nullable final LeafSchemaNode schema) {
+        if (schema != null) {
+            final TypeDefinition<?> type = schema.getType();
+            if (type instanceof BooleanTypeDefinition || type instanceof EnumTypeDefinition) {
+                return WEAK;
+            }
+        }
+
+        return NOOP;
+    }
+
+    public abstract <T extends LeafNode<?>> T intern(final T sample);
+}
diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/LeafsetEntryInterner.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/LeafsetEntryInterner.java
new file mode 100644 (file)
index 0000000..aca67fd
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * 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 javax.annotation.Nonnull;
+import javax.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.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.
+ *
+ * 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.
+ *
+ * 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<?>> T intern(@Nonnull final T sample) {
+        if (!sample.getAttributes().isEmpty()) {
+            // Non-empty attributes, do not intern
+            return 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 %s to %s", 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.
+     */
+    @Nullable public static LeafsetEntryInterner forSchema(@Nullable final LeafListSchemaNode schema) {
+        if (schema != null) {
+            final TypeDefinition<?> type = schema.getType();
+            if (type instanceof BooleanTypeDefinition || type instanceof EnumTypeDefinition) {
+                return INSTANCE;
+            }
+        }
+        return null;
+    }
+}
diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/NormalizedNodeInterners.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/NormalizedNodeInterners.java
deleted file mode 100644 (file)
index 0252fd7..0000000
+++ /dev/null
@@ -1,81 +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.opendaylight.yangtools.yang.data.api.AttributesContainer;
-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.model.api.LeafListSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
-import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Utility class for sharing instances of {@link NormalizedNode}s which have low cardinality. We currently implement
- * this for LeafNode and LeafSetEntryNode objects, whose value type is either boolean or enumeration. A further
- * restriction is that attributes must not be present.
- *
- * 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.
- *
- * 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 NormalizedNodeInterners {
-    private static final Logger LOG = LoggerFactory.getLogger(NormalizedNodeInterners.class);
-    private static final Interner<Object> LEAF_INTERNER = Interners.newWeakInterner();
-    private static final Interner<Object> LEAFSET_ENTRY_INTERNER = Interners.newWeakInterner();
-
-    private NormalizedNodeInterners() {
-        throw new UnsupportedOperationException();
-    }
-
-    private static <T extends NormalizedNode<?, ?>> T intern(final TypeDefinition<?> type, final Interner<Object> where, final T what) {
-        if (what instanceof AttributesContainer && !((AttributesContainer) what).getAttributes().isEmpty()) {
-            // Non-empty attributes, do not intern
-            return what;
-        }
-
-        // Take advantage of boolean types mapping to a java.lang.Boolean
-        if (!(what.getValue() instanceof Boolean)) {
-            // FIXME: BUG-4268: we really just need the semantic portion (enum or boolean), this recursion just shows
-            //        how broken the TypeDefinition<?>
-            TypeDefinition<?> real = type;
-            while (real.getBaseType() != null) {
-                real = real.getBaseType();
-            }
-
-            if (!(real instanceof EnumTypeDefinition)) {
-                return what;
-            }
-        }
-
-        // All checks completed, intern the sample
-        @SuppressWarnings("unchecked")
-        final T ret = (T) where.intern(what);
-        LOG.trace("Interned object %s to %s", what, ret);
-        return ret;
-    }
-
-    public static <T extends LeafNode<?>> T internLeaf(final LeafSchemaNode node, final T sample) {
-        return intern(node.getType(), LEAF_INTERNER, sample);
-    }
-
-    public static <T extends LeafSetEntryNode<?>> T internLeafEntry(final LeafListSchemaNode node, final T sample) {
-        return intern(node.getType(), LEAFSET_ENTRY_INTERNER, sample);
-    }
-}