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>
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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;
+ }
+}
+++ /dev/null
-/*
- * 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);
- }
-}