/*
* Copyright (c) 2017 Pantheon Technologies, 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.util.codec;
import com.google.common.base.Preconditions;
import com.google.common.base.Verify;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.concurrent.ThreadSafe;
import org.opendaylight.yangtools.yang.common.QNameModule;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
import org.opendaylight.yangtools.yang.model.api.TypedSchemaNode;
import org.opendaylight.yangtools.yang.model.api.type.BinaryTypeDefinition;
import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition;
import org.opendaylight.yangtools.yang.model.api.type.BooleanTypeDefinition;
import org.opendaylight.yangtools.yang.model.api.type.DecimalTypeDefinition;
import org.opendaylight.yangtools.yang.model.api.type.EmptyTypeDefinition;
import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
import org.opendaylight.yangtools.yang.model.api.type.IntegerTypeDefinition;
import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition;
import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
import org.opendaylight.yangtools.yang.model.api.type.UnknownTypeDefinition;
import org.opendaylight.yangtools.yang.model.api.type.UnsignedIntegerTypeDefinition;
import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A type-to-codec factory base class with logic to efficiently lookup and cache codec instances,
* also dealing with union type composition.
*
* @author Robert Varga
*
* @param <T> Codec type
*/
@ThreadSafe
public abstract class AbstractCodecFactory<T extends TypeAwareCodec<?, ?, ?>> {
private static final Logger LOG = LoggerFactory.getLogger(AbstractCodecFactory.class);
private final CodecCache<T> cache;
private final SchemaContext schemaContext;
protected AbstractCodecFactory(final SchemaContext schemaContext, final CodecCache<T> cache) {
this.schemaContext = Preconditions.checkNotNull(schemaContext);
this.cache = Preconditions.checkNotNull(cache);
}
public final SchemaContext getSchemaContext() {
return schemaContext;
}
public final T codecFor(final TypedSchemaNode schema) {
/*
* There are many trade-offs to be made here. We need the common case being as fast as possible while reusing
* codecs as much as possible.
*
* This gives us essentially four classes of codecs:
* - simple codecs, which are based on the type definition only
* - complex codecs, which depend on both type definition and the leaf
* - null codec, which does not depend on anything
* - instance identifier codec, which is based on namespace mapping
*
* We assume prevalence is in above order and that caching is effective. We therefore
*/
final TypeDefinition<?> type = schema.getType();
T ret = cache.lookupSimple(type);
if (ret != null) {
LOG.trace("Type {} hit simple {}", type, ret);
return ret;
}
ret = cache.lookupComplex(schema);
if (ret != null) {
LOG.trace("Type {} hit complex {}", type, ret);
return ret;
}
// Dealing with simple types first...
ret = getSimpleCodecFor(type);
if (ret != null) {
LOG.trace("Type {} miss simple {}", type, ret);
return ret;
}
// ... and complex types afterwards
ret = createComplexCodecFor(schema, type);
LOG.trace("Type {} miss complex {}", type, ret);
return cache.getComplex(schema, ret);
}
protected abstract T binaryCodec(BinaryTypeDefinition type);
protected abstract T booleanCodec(BooleanTypeDefinition type);
protected abstract T bitsCodec(BitsTypeDefinition type);
protected abstract T emptyCodec(EmptyTypeDefinition type);
protected abstract T enumCodec(EnumTypeDefinition type);
protected abstract T identityRefCodec(IdentityrefTypeDefinition type, QNameModule module);
protected abstract T instanceIdentifierCodec(InstanceIdentifierTypeDefinition type);
protected abstract T intCodec(IntegerTypeDefinition type);
protected abstract T decimalCodec(DecimalTypeDefinition type);
protected abstract T stringCodec(StringTypeDefinition type);
protected abstract T uintCodec(UnsignedIntegerTypeDefinition type);
protected abstract T unionCodec(UnionTypeDefinition type, List<T> codecs);
protected abstract T unknownCodec(UnknownTypeDefinition type);
private T getSimpleCodecFor(final TypeDefinition<?> type) {
// These types are expected to be fully-shared
if (type instanceof EmptyTypeDefinition) {
return emptyCodec((EmptyTypeDefinition) type);
} else if (type instanceof UnknownTypeDefinition) {
return unknownCodec((UnknownTypeDefinition) type);
}
// Now deal with simple types. Note we consider union composed of purely simple types a simple type itself.
// The checks here are optimized for common types.
final T ret;
if (type instanceof StringTypeDefinition) {
ret = stringCodec((StringTypeDefinition) type);
} else if (type instanceof IntegerTypeDefinition) {
ret = intCodec((IntegerTypeDefinition) type);
} else if (type instanceof UnsignedIntegerTypeDefinition) {
ret = uintCodec((UnsignedIntegerTypeDefinition) type);
} else if (type instanceof BooleanTypeDefinition) {
ret = booleanCodec((BooleanTypeDefinition) type);
} else if (type instanceof DecimalTypeDefinition) {
ret = decimalCodec((DecimalTypeDefinition) type);
} else if (type instanceof EnumTypeDefinition) {
ret = enumCodec((EnumTypeDefinition) type);
} else if (type instanceof BitsTypeDefinition) {
ret = bitsCodec((BitsTypeDefinition) type);
} else if (type instanceof UnionTypeDefinition) {
final UnionTypeDefinition union = (UnionTypeDefinition) type;
if (!isSimpleUnion(union)) {
return null;
}
ret = createSimpleUnion(union);
} else if (type instanceof BinaryTypeDefinition) {
ret = binaryCodec((BinaryTypeDefinition) type);
} else if (type instanceof InstanceIdentifierTypeDefinition) {
return instanceIdentifierCodec((InstanceIdentifierTypeDefinition) type);
} else {
return null;
}
return cache.getSimple(type, Verify.verifyNotNull(ret));
}
private static boolean isSimpleUnion(final UnionTypeDefinition union) {
for (TypeDefinition<?> t : union.getTypes()) {
if (t instanceof IdentityrefTypeDefinition || t instanceof LeafrefTypeDefinition
|| (t instanceof UnionTypeDefinition && !isSimpleUnion((UnionTypeDefinition) t))) {
LOG.debug("Type {} has non-simple subtype", t);
return false;
}
}
LOG.debug("Type {} is simple", union);
return true;
}
private T createComplexCodecFor(final TypedSchemaNode schema, final TypeDefinition<?> type) {
if (type instanceof UnionTypeDefinition) {
return createComplexUnion(schema, (UnionTypeDefinition) type);
} else if (type instanceof LeafrefTypeDefinition) {
final TypeDefinition<?> target = SchemaContextUtil.getBaseTypeForLeafRef((LeafrefTypeDefinition) type,
schemaContext, schema);
Verify.verifyNotNull(target, "Unable to find base type for leafref node %s type %s.", schema.getPath(),
target);
final T ret = getSimpleCodecFor(target);
return ret != null ? ret : createComplexCodecFor(schema, target);
} else if (type instanceof IdentityrefTypeDefinition) {
return identityRefCodec((IdentityrefTypeDefinition) type, schema.getQName().getModule());
} else {
throw new IllegalArgumentException("Unsupported type " + type);
}
}
private T createSimpleUnion(final UnionTypeDefinition union) {
final List<TypeDefinition<?>> types = union.getTypes();
final List<T> codecs = new ArrayList<>(types.size());
for (TypeDefinition<?> type : types) {
T codec = cache.lookupSimple(type);
if (codec == null) {
codec = Verify.verifyNotNull(getSimpleCodecFor(type), "Type %s did not resolve to a simple codec",
type);
}
codecs.add(codec);
}
return unionCodec(union, codecs);
}
private T createComplexUnion(final TypedSchemaNode schema, final UnionTypeDefinition union) {
final List<TypeDefinition<?>> types = union.getTypes();
final List<T> codecs = new ArrayList<>(types.size());
for (TypeDefinition<?> type : types) {
T codec = cache.lookupSimple(type);
if (codec == null) {
codec = getSimpleCodecFor(type);
if (codec == null) {
codec = createComplexCodecFor(schema, type);
}
}
codecs.add(Verify.verifyNotNull(codec, "Schema %s subtype %s has no codec", schema, type));
}
return unionCodec(union, codecs);
}
}