X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=code-generator%2Fbinding-data-codec%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fyangtools%2Fbinding%2Fdata%2Fcodec%2Fimpl%2FBindingCodecContext.java;h=dfef41afc0aedd37bac71784384be0b3e816db95;hb=ce5c6eb0184495fc7b2723e07bbf245e831f2442;hp=cd223ce091dadde3156e9a9c9c8afb689b7c6e83;hpb=0eb60011b52e4e56c62b47a36eb334f2c3b3ad6a;p=yangtools.git diff --git a/code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/BindingCodecContext.java b/code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/BindingCodecContext.java index cd223ce091..dfef41afc0 100644 --- a/code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/BindingCodecContext.java +++ b/code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/BindingCodecContext.java @@ -13,13 +13,21 @@ import com.google.common.collect.ImmutableMap; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.concurrent.Callable; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; import org.opendaylight.yangtools.binding.data.codec.impl.NodeCodecContext.CodecContextFactory; import org.opendaylight.yangtools.concepts.Codec; @@ -48,20 +56,22 @@ import org.opendaylight.yangtools.yang.model.api.type.BooleanTypeDefinition; 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.UnionTypeDefinition; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; class BindingCodecContext implements CodecContextFactory, Immutable { - + private static final Logger LOG = LoggerFactory.getLogger(BindingCodecContext.class); private static final String GETTER_PREFIX = "get"; + private final SchemaRootCodecContext root; private final BindingRuntimeContext context; - private final Codec> instanceIdentifierCodec; - private final Codec> identityCodec; + private final Codec> instanceIdentifierCodec = + new InstanceIdentifierCodec(); + private final Codec> identityCodec = new IdentityCodec(); public BindingCodecContext(final BindingRuntimeContext context) { - this.context = Preconditions.checkNotNull(context, "Bidning Runtime Context is required."); + this.context = Preconditions.checkNotNull(context, "Binding Runtime Context is required."); this.root = SchemaRootCodecContext.create(this); - this.instanceIdentifierCodec = new InstanceIdentifierCodec(); - this.identityCodec = new IdentityCodec(); } @Override @@ -69,7 +79,7 @@ class BindingCodecContext implements CodecContextFactory, Immutable { return context; } - public Codec> getInstanceIdentifierCodec() { + Codec> getInstanceIdentifierCodec() { return instanceIdentifierCodec; } @@ -99,14 +109,27 @@ class BindingCodecContext implements CodecContextFactory, Immutable { return currentNode; } - public NodeCodecContext getCodecContextNode(final YangInstanceIdentifier dom, - final List builder) { + /** + * Multi-purpose utility function. Traverse the codec tree, looking for + * the appropriate codec for the specified {@link YangInstanceIdentifier}. + * As a side-effect, gather all traversed binding {@link InstanceIdentifier.PathArgument}s + * into the supplied collection. + * + * @param dom {@link YangInstanceIdentifier} which is to be translated + * @param bindingArguments Collection for traversed path arguments + * @return Codec for target node, or @null if the node does not have a + * binding representation (choice, case, leaf). + */ + @Nullable NodeCodecContext getCodecContextNode(final @Nonnull YangInstanceIdentifier dom, + final @Nonnull Collection bindingArguments) { NodeCodecContext currentNode = root; ListNodeCodecContext currentList = null; + for (YangInstanceIdentifier.PathArgument domArg : dom.getPathArguments()) { - Preconditions.checkArgument(currentNode instanceof DataContainerCodecContext); - DataContainerCodecContext previous = (DataContainerCodecContext) currentNode; - NodeCodecContext nextNode = previous.getYangIdentifierChild(domArg); + Preconditions.checkArgument(currentNode instanceof DataContainerCodecContext, "Unexpected child of non-container node %s", currentNode); + final DataContainerCodecContext previous = (DataContainerCodecContext) currentNode; + final NodeCodecContext nextNode = previous.getYangIdentifierChild(domArg); + /* * List representation in YANG Instance Identifier consists of two * arguments: first is list as a whole, second is list as an item so @@ -116,19 +139,13 @@ class BindingCodecContext implements CodecContextFactory, Immutable { * Identifier as Item or IdentifiableItem */ if (currentList != null) { + Preconditions.checkArgument(currentList == nextNode, "List should be referenced two times in YANG Instance Identifier %s", dom); - if (currentList == nextNode) { - - // We entered list, so now we have all information to emit - // list - // path using second list argument. - builder.add(currentList.getBindingPathArgument(domArg)); - currentList = null; - currentNode = nextNode; - } else { - throw new IllegalArgumentException( - "List should be referenced two times in YANG Instance Identifier"); - } + // We entered list, so now we have all information to emit + // list path using second list argument. + bindingArguments.add(currentList.getBindingPathArgument(domArg)); + currentList = null; + currentNode = nextNode; } else if (nextNode instanceof ListNodeCodecContext) { // We enter list, we do not update current Node yet, // since we need to verify @@ -137,24 +154,36 @@ class BindingCodecContext implements CodecContextFactory, Immutable { // We do not add path argument for choice, since // it is not supported by binding instance identifier. currentNode = nextNode; - }else if (nextNode instanceof DataContainerCodecContext) { - builder.add(((DataContainerCodecContext) nextNode).getBindingPathArgument(domArg)); + } else if (nextNode instanceof DataContainerCodecContext) { + bindingArguments.add(((DataContainerCodecContext) nextNode).getBindingPathArgument(domArg)); currentNode = nextNode; } else if (nextNode instanceof LeafNodeCodecContext) { - Preconditions.checkArgument(builder == null,"Instance Identifier for leaf is not representable."); + LOG.debug("Instance identifier referencing a leaf is not representable (%s)", dom); + return null; } } + // Algorithm ended in list as whole representation // we sill need to emit identifier for list + if (currentNode instanceof ChoiceNodeCodecContext) { + LOG.debug("Instance identifier targeting a choice is not representable (%s)", dom); + return null; + } + if (currentNode instanceof CaseNodeCodecContext) { + LOG.debug("Instance identifier targeting a case is not representable (%s)", dom); + return null; + } + if (currentList != null) { - builder.add(currentList.getBindingPathArgument(null)); + bindingArguments.add(currentList.getBindingPathArgument(null)); return currentList; } return currentNode; } @Override - public ImmutableMap getLeafNodes(final Class parentClass, final DataNodeContainer childSchema) { + public ImmutableMap getLeafNodes(final Class parentClass, + final DataNodeContainer childSchema) { HashMap getterToLeafSchema = new HashMap<>(); for (DataSchemaNode leaf : childSchema.getChildNodes()) { final TypeDefinition typeDef; @@ -166,7 +195,7 @@ class BindingCodecContext implements CodecContextFactory, Immutable { continue; } - String getterName = getGetterName(leaf.getQName(),typeDef); + String getterName = getGetterName(leaf.getQName(), typeDef); getterToLeafSchema.put(getterName, leaf); } return getLeafNodesUsingReflection(parentClass, getterToLeafSchema); @@ -175,10 +204,10 @@ class BindingCodecContext implements CodecContextFactory, Immutable { private String getGetterName(final QName qName, TypeDefinition typeDef) { String suffix = BindingMapping.getClassName(qName); - while(typeDef.getBaseType() != null) { + while (typeDef.getBaseType() != null) { typeDef = typeDef.getBaseType(); } - if(typeDef instanceof BooleanTypeDefinition) { + if (typeDef instanceof BooleanTypeDefinition) { return "is" + suffix; } return GETTER_PREFIX + suffix; @@ -190,88 +219,96 @@ class BindingCodecContext implements CodecContextFactory, Immutable { for (Method method : parentClass.getMethods()) { if (method.getParameterTypes().length == 0) { DataSchemaNode schema = getterToLeafSchema.get(method.getName()); - final LeafNodeCodecContext leafNode; + final Class valueType; if (schema instanceof LeafSchemaNode) { - leafNode = leafNodeFrom(method.getReturnType(), schema); - + valueType = method.getReturnType(); + } else if (schema instanceof LeafListSchemaNode) { + Type genericType = ClassLoaderUtils.getFirstGenericParameter(method.getGenericReturnType()); + + if (genericType instanceof Class) { + valueType = (Class) genericType; + } else if (genericType instanceof ParameterizedType) { + valueType = (Class) ((ParameterizedType) genericType).getRawType(); + } else { + throw new IllegalStateException("Unexpected return type " + genericType); + } } else { - // FIXME: extract inner list value - leafNode = null; - } - if (leafNode != null) { - leaves.put(schema.getQName().getLocalName(), leafNode); + continue; // We do not have schema for leaf, so we will ignore it (eg. getClass, getImplementedInterface). } + Codec codec = getCodec(valueType, schema); + final LeafNodeCodecContext leafNode = new LeafNodeCodecContext(schema, codec, method); + leaves.put(schema.getQName().getLocalName(), leafNode); } } return ImmutableMap.copyOf(leaves); } - private LeafNodeCodecContext leafNodeFrom(final Class returnType, final DataSchemaNode schema) { - return new LeafNodeCodecContext(schema, getCodec(returnType,schema)); - } - private Codec getCodec(final Class returnType, final DataSchemaNode schema) { - if(Class.class.equals(returnType)) { + private Codec getCodec(final Class valueType, final DataSchemaNode schema) { + if (Class.class.equals(valueType)) { @SuppressWarnings({ "unchecked", "rawtypes" }) - final Codeccasted = (Codec) identityCodec; + final Codec casted = (Codec) identityCodec; return casted; - } else if(InstanceIdentifier.class.equals(returnType)) { + } else if (InstanceIdentifier.class.equals(valueType)) { @SuppressWarnings({ "unchecked", "rawtypes" }) - final Codeccasted = (Codec) instanceIdentifierCodec; + final Codec casted = (Codec) instanceIdentifierCodec; return casted; - } else if(BindingReflections.isBindingClass(returnType)) { + } else if (BindingReflections.isBindingClass(valueType)) { final TypeDefinition instantiatedType; - if(schema instanceof LeafSchemaNode) { + if (schema instanceof LeafSchemaNode) { instantiatedType = ((LeafSchemaNode) schema).getType(); - } else if(schema instanceof LeafListSchemaNode) { + } else if (schema instanceof LeafListSchemaNode) { instantiatedType = ((LeafListSchemaNode) schema).getType(); } else { instantiatedType = null; } - if(instantiatedType != null) { - return getCodec(returnType,instantiatedType); + if (instantiatedType != null) { + return getCodec(valueType, instantiatedType); } } return ValueTypeCodec.NOOP_CODEC; } - private Codec getCodec(final Class returnType, final TypeDefinition instantiatedType) { + private Codec getCodec(final Class valueType, final TypeDefinition instantiatedType) { @SuppressWarnings("rawtypes") TypeDefinition rootType = instantiatedType; - while(rootType.getBaseType() != null) { + while (rootType.getBaseType() != null) { rootType = rootType.getBaseType(); } - if (rootType instanceof IdentityrefTypeDefinition) { - return ValueTypeCodec.encapsulatedValueCodecFor(returnType,identityCodec); + if (rootType instanceof IdentityrefTypeDefinition) { + return ValueTypeCodec.encapsulatedValueCodecFor(valueType, identityCodec); } else if (rootType instanceof InstanceIdentifierTypeDefinition) { - return ValueTypeCodec.encapsulatedValueCodecFor(returnType,instanceIdentifierCodec); - } else if(rootType instanceof UnionTypeDefinition) { - // FIXME: Return union codec - return ValueTypeCodec.NOOP_CODEC; + return ValueTypeCodec.encapsulatedValueCodecFor(valueType, instanceIdentifierCodec); + } else if (rootType instanceof UnionTypeDefinition) { + Callable loader = UnionTypeCodec.loader(valueType, (UnionTypeDefinition) rootType); + try { + return loader.call(); + } catch (Exception e) { + throw new IllegalStateException("Unable to load codec for " + valueType, e); + } } - return ValueTypeCodec.getCodecFor(returnType, instantiatedType); + return ValueTypeCodec.getCodecFor(valueType, instantiatedType); } - private class InstanceIdentifierCodec implements Codec> { + private class InstanceIdentifierCodec implements Codec> { @Override public YangInstanceIdentifier serialize(final InstanceIdentifier input) { - List domArgs = new LinkedList<>(); + List domArgs = new ArrayList<>(); getCodecContextNode(input, domArgs); return YangInstanceIdentifier.create(domArgs); } @Override public InstanceIdentifier deserialize(final YangInstanceIdentifier input) { - List builder = new LinkedList<>(); - getCodecContextNode(input, builder); - return InstanceIdentifier.create(builder); + final List builder = new ArrayList<>(); + final NodeCodecContext codec = getCodecContextNode(input, builder); + return codec == null ? null : InstanceIdentifier.create(builder); } } - private class IdentityCodec implements Codec> { - + private class IdentityCodec implements Codec> { @Override public Class deserialize(final QName input) { @@ -289,12 +326,13 @@ class BindingCodecContext implements CodecContextFactory, Immutable { private static class ValueContext { Method getter; - Codec codec; + Codec codec; public ValueContext(final Class identifier, final LeafNodeCodecContext leaf) { - final String getterName = GETTER_PREFIX + BindingMapping.getClassName(leaf.getDomPathArgument().getNodeType()); + final String getterName = GETTER_PREFIX + + BindingMapping.getClassName(leaf.getDomPathArgument().getNodeType()); try { - getter =identifier.getMethod(getterName); + getter = identifier.getMethod(getterName); } catch (NoSuchMethodException | SecurityException e) { throw new IllegalStateException(e); } @@ -304,6 +342,9 @@ class BindingCodecContext implements CodecContextFactory, Immutable { public Object getAndSerialize(final Object obj) { try { Object value = getter.invoke(obj); + Preconditions.checkArgument(value != null, + "All keys must be specified for %s. Missing key is %s. Supplied key is %s", + getter.getDeclaringClass(), getter.getName(), obj); return codec.serialize(value); } catch (IllegalAccessException | InvocationTargetException e) { throw new IllegalArgumentException(e); @@ -316,74 +357,86 @@ class BindingCodecContext implements CodecContextFactory, Immutable { } - private class IdentifiableItemCodec implements Codec> { + private static class IdentifiableItemCodec implements Codec> { - private final Class> keyClass; - private final ImmutableMap keyValueContexts; - private final QName name; + private final Map keyValueContexts; + private final ListSchemaNode schema; private final Constructor> constructor; private final Class identifiable; - public IdentifiableItemCodec(final QName name,final Class> keyClass,final Class identifiable,final Map keyValueContexts) { - this.name = name; + public IdentifiableItemCodec(final ListSchemaNode schema, final Class> keyClass, + final Class identifiable, final Map keyValueContexts) { + this.schema = schema; this.identifiable = identifiable; - this.keyClass = keyClass; - this.keyValueContexts = ImmutableMap.copyOf(keyValueContexts); this.constructor = getConstructor(keyClass); + + /* + * We need to re-index to make sure we instantiate nodes in the order in which + * they are defined. + */ + final Map keys = new LinkedHashMap<>(); + for (QName qname : schema.getKeyDefinition()) { + keys.put(qname, keyValueContexts.get(qname)); + } + this.keyValueContexts = ImmutableMap.copyOf(keys); } @Override - public IdentifiableItem deserialize(final NodeIdentifierWithPredicates input) { - ArrayList bindingValues = new ArrayList<>(); - for(Entry yangEntry : input.getKeyValues().entrySet()) { - QName yangName = yangEntry.getKey(); - Object yangValue = yangEntry.getValue(); - bindingValues.add(keyValueContexts.get(yangName).deserialize(yangValue)); + public IdentifiableItem deserialize(final NodeIdentifierWithPredicates input) { + final Collection keys = schema.getKeyDefinition(); + final ArrayList bindingValues = new ArrayList<>(keys.size()); + for (QName key : keys) { + Object yangValue = input.getKeyValues().get(key); + bindingValues.add(keyValueContexts.get(key).deserialize(yangValue)); } + + final Identifier identifier; try { - Identifier identifier = constructor.newInstance(bindingValues.toArray()); - return new IdentifiableItem(identifiable, identifier); - } catch (InstantiationException | IllegalAccessException - | InvocationTargetException e) { - throw new IllegalStateException(e); + identifier = constructor.newInstance(bindingValues.toArray()); + } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { + throw new IllegalStateException(String.format("Failed to instantiate key class %s", constructor.getDeclaringClass()), e); } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + final IdentifiableItem identifiableItem = new IdentifiableItem(identifiable, identifier); + return identifiableItem; } @Override public NodeIdentifierWithPredicates serialize(final IdentifiableItem input) { Object value = input.getKey(); - Map values = new HashMap<>(); - for(Entry valueCtx : keyValueContexts.entrySet()) { + Map values = new LinkedHashMap<>(); + for (Entry valueCtx : keyValueContexts.entrySet()) { values.put(valueCtx.getKey(), valueCtx.getValue().getAndSerialize(value)); } - return new NodeIdentifierWithPredicates(name, values); + return new NodeIdentifierWithPredicates(schema.getQName(), values); } - } + @SuppressWarnings("unchecked") private static Constructor> getConstructor(final Class> clazz) { - for(Constructor constr : clazz.getConstructors()) { + for (@SuppressWarnings("rawtypes") Constructor constr : clazz.getConstructors()) { Class[] parameters = constr.getParameterTypes(); if (!clazz.equals(parameters[0])) { // It is not copy constructor; return constr; } } - throw new IllegalArgumentException("Supplied class " + clazz +"does not have required constructor."); + throw new IllegalArgumentException("Supplied class " + clazz + "does not have required constructor."); } - @Override public Codec> getPathArgumentCodec(final Class listClz, final ListSchemaNode schema) { - Class> identifier =ClassLoaderUtils.findFirstGenericArgument(listClz, Identifiable.class); + Class> identifier = ClassLoaderUtils.findFirstGenericArgument(listClz, + Identifiable.class); Map valueCtx = new HashMap<>(); - for(LeafNodeCodecContext leaf : getLeafNodes(identifier, schema).values()) { + for (LeafNodeCodecContext leaf : getLeafNodes(identifier, schema).values()) { QName name = leaf.getDomPathArgument().getNodeType(); - valueCtx.put(name, new ValueContext(identifier,leaf)); + valueCtx.put(name, new ValueContext(identifier, leaf)); } - return new IdentifiableItemCodec(schema.getQName(), identifier, listClz, valueCtx); + return new IdentifiableItemCodec(schema, identifier, listClz, valueCtx); } }