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%2FDataObjectCodecContext.java;h=0ca1d2b8504d3183bb01783312eb1ea4717dcc23;hb=3f754cccb393b989bafb8b194edf9da0ec3e9e8a;hp=fa6e45d00ebbc7923cdd2d6aac915897a574558c;hpb=0eb60011b52e4e56c62b47a36eb334f2c3b3ad6a;p=yangtools.git diff --git a/code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/DataObjectCodecContext.java b/code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/DataObjectCodecContext.java index fa6e45d00e..0ca1d2b850 100644 --- a/code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/DataObjectCodecContext.java +++ b/code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/DataObjectCodecContext.java @@ -7,124 +7,220 @@ */ package org.opendaylight.yangtools.binding.data.codec.impl; +import com.google.common.base.Optional; import com.google.common.base.Preconditions; +import com.google.common.base.Throwables; import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Iterables; - +import com.google.common.collect.ImmutableSortedMap; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodHandles.Lookup; +import java.lang.invoke.MethodType; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.Collection; +import java.util.Comparator; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Map.Entry; - -import org.opendaylight.yangtools.binding.generator.util.ReferencedTypeImpl; +import java.util.SortedMap; +import java.util.TreeMap; import org.opendaylight.yangtools.sal.binding.generator.api.ClassLoadingStrategy; import org.opendaylight.yangtools.sal.binding.model.api.Type; +import org.opendaylight.yangtools.yang.binding.Augmentable; import org.opendaylight.yangtools.yang.binding.Augmentation; +import org.opendaylight.yangtools.yang.binding.AugmentationHolder; import org.opendaylight.yangtools.yang.binding.DataObject; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.binding.util.BindingReflections; import org.opendaylight.yangtools.yang.common.QName; -import org.opendaylight.yangtools.yang.common.QNameModule; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier; +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.PathArgument; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer; import org.opendaylight.yangtools.yang.model.api.AugmentationSchema; -import org.opendaylight.yangtools.yang.model.api.AugmentationTarget; -import org.opendaylight.yangtools.yang.model.api.ChoiceNode; import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; import org.opendaylight.yangtools.yang.model.util.SchemaNodeUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -abstract class DataObjectCodecContext extends DataContainerCodecContext { +abstract class DataObjectCodecContext extends DataContainerCodecContext { + private static final Logger LOG = LoggerFactory.getLogger(DataObjectCodecContext.class); + private static final Lookup LOOKUP = MethodHandles.publicLookup(); + private static final MethodType CONSTRUCTOR_TYPE = MethodType.methodType(void.class, InvocationHandler.class); + private static final MethodType DATAOBJECT_TYPE = MethodType.methodType(DataObject.class, InvocationHandler.class); + private static final Comparator METHOD_BY_ALPHABET = new Comparator() { + @Override + public int compare(final Method o1, final Method o2) { + return o1.getName().compareTo(o2.getName()); + } + }; - protected final ImmutableMap leafChild; - protected final ImmutableMap> choiceCaseChildren; - protected final ImmutableMap augIdentifierToType; + private final ImmutableMap> leafChild; + private final ImmutableMap byYang; + private final ImmutableSortedMap byMethod; + private final ImmutableMap, DataContainerCodecPrototype> byStreamClass; + private final ImmutableMap, DataContainerCodecPrototype> byBindingArgClass; + private final MethodHandle proxyConstructor; - protected DataObjectCodecContext(final Class cls, final QNameModule namespace, final T nodeSchema, - final CodecContextFactory loader) { - super(cls, namespace, nodeSchema, loader); - this.leafChild = loader.getLeafNodes(cls, nodeSchema); - this.choiceCaseChildren = factory.getRuntimeContext().getChoiceCaseChildren(schema); - this.augIdentifierToType = factory.getRuntimeContext().getAvailableAugmentationTypes(nodeSchema); - } + protected DataObjectCodecContext(final DataContainerCodecPrototype prototype) { + super(prototype); - @Override - protected DataContainerCodecContext getIdentifierChild(final InstanceIdentifier.PathArgument arg, - final List builder) { - if (choiceCaseChildren.isEmpty()) { - return super.getIdentifierChild(arg, builder); - } - // Lookup in choiceCase - Class argument = arg.getType(); - ReferencedTypeImpl ref = new ReferencedTypeImpl(argument.getPackage().getName(), argument.getSimpleName()); - Entry cazeId = choiceCaseChildren.get(ref); - if (cazeId == null) { - return super.getIdentifierChild(arg, builder); + this.leafChild = factory().getLeafNodes(getBindingClass(), schema()); + + final Map, Method> clsToMethod = BindingReflections.getChildrenClassToMethod(getBindingClass()); + + final Map byYangBuilder = new HashMap<>(); + final SortedMap byMethodBuilder = new TreeMap<>(METHOD_BY_ALPHABET); + final Map, DataContainerCodecPrototype> byStreamClassBuilder = new HashMap<>(); + final Map, DataContainerCodecPrototype> byBindingArgClassBuilder = new HashMap<>(); + + // Adds leaves to mapping + for (final LeafNodeCodecContext leaf : leafChild.values()) { + byMethodBuilder.put(leaf.getGetter(), leaf); + byYangBuilder.put(leaf.getDomPathArgument(), leaf); } - ClassLoadingStrategy loader = factory.getRuntimeContext().getStrategy(); - try { - Class choice = loader.loadClass(cazeId.getKey()); - Class caze = loader.loadClass(cazeId.getValue()); - ChoiceNodeCodecContext choiceNode = (ChoiceNodeCodecContext) getStreamChild(choice); - choiceNode.addYangPathArgument(arg, builder); - CaseNodeCodecContext cazeNode = (CaseNodeCodecContext) choiceNode.getStreamChild(caze); - cazeNode.addYangPathArgument(arg, builder); - return cazeNode.getIdentifierChild(arg, builder); - } catch (ClassNotFoundException e) { - throw new IllegalArgumentException("Required class not found.", e); + for (final Entry, Method> childDataObj : clsToMethod.entrySet()) { + final DataContainerCodecPrototype childProto = loadChildPrototype(childDataObj.getKey()); + byMethodBuilder.put(childDataObj.getValue(), childProto); + byStreamClassBuilder.put(childProto.getBindingClass(), childProto); + byYangBuilder.put(childProto.getYangArg(), childProto); + if (childProto.isChoice()) { + final ChoiceNodeCodecContext choice = (ChoiceNodeCodecContext) childProto.get(); + for(final Class cazeChild : choice.getCaseChildrenClasses()) { + byBindingArgClassBuilder.put(cazeChild, childProto); + } + } } + this.byMethod = ImmutableSortedMap.copyOfSorted(byMethodBuilder); + if (Augmentable.class.isAssignableFrom(getBindingClass())) { + final ImmutableMap augmentations = factory().getRuntimeContext() + .getAvailableAugmentationTypes(schema()); + for (final Entry augment : augmentations.entrySet()) { + final DataContainerCodecPrototype augProto = getAugmentationPrototype(augment.getValue()); + if (augProto != null) { + byYangBuilder.put(augProto.getYangArg(), augProto); + byStreamClassBuilder.put(augProto.getBindingClass(), augProto); + } + } + } + + this.byYang = ImmutableMap.copyOf(byYangBuilder); + this.byStreamClass = ImmutableMap.copyOf(byStreamClassBuilder); + byBindingArgClassBuilder.putAll(byStreamClass); + this.byBindingArgClass = ImmutableMap.copyOf(byBindingArgClassBuilder); + final Class proxyClass = Proxy.getProxyClass(getBindingClass().getClassLoader(), new Class[] { getBindingClass(), AugmentationHolder.class }); + try { + proxyConstructor = LOOKUP.findConstructor(proxyClass, CONSTRUCTOR_TYPE).asType(DATAOBJECT_TYPE); + } catch (NoSuchMethodException | IllegalAccessException e) { + throw new IllegalStateException("Failed to find contructor for class " + proxyClass); + } } + + @SuppressWarnings("unchecked") @Override - protected NodeCodecContext getYangIdentifierChild(final YangInstanceIdentifier.PathArgument arg) { - if (arg instanceof YangInstanceIdentifier.AugmentationIdentifier) { - return getChildByAugmentationIdentifier((YangInstanceIdentifier.AugmentationIdentifier) arg); - } - - QName childQName = arg.getNodeType(); - DataSchemaNode childSchema = schema.getDataChildByName(childQName); - Preconditions.checkArgument(childSchema != null, "Argument %s is not valid child of %s", arg, schema); - if (childSchema instanceof DataNodeContainer || childSchema instanceof ChoiceNode) { - Class childCls = factory.getRuntimeContext().getClassForSchema(childSchema); - DataContainerCodecContext childNode = getStreamChild(childCls); - return childNode; - } else { - return getLeafChild(childQName.getLocalName()); + public DataContainerCodecContext streamChild(final Class childClass) { + DataContainerCodecPrototype childProto = byStreamClass.get(childClass); + if (childProto != null) { + return (DataContainerCodecContext) childProto.get(); + } + + if (Augmentation.class.isAssignableFrom(childClass)) { + /* + * It is potentially mismatched valid augmentation - we look up equivalent augmentation + * using reflection and walk all stream child and compare augmenations classes + * if they are equivalent. + * + * FIXME: Cache mapping of mismatched augmentation to real one, to speed up lookup. + */ + @SuppressWarnings("rawtypes") + final Class augTarget = BindingReflections.findAugmentationTarget((Class) childClass); + if ((getBindingClass().equals(augTarget))) { + for (final DataContainerCodecPrototype realChild : byStreamClass.values()) { + if (Augmentation.class.isAssignableFrom(realChild.getBindingClass()) + && BindingReflections.isSubstitutionFor(childClass,realChild.getBindingClass())) { + childProto = realChild; + break; + } + } + } } + return (DataContainerCodecContext) childNonNull(childProto, childClass, " Child %s is not valid child.").get(); } - protected NodeCodecContext getChildByAugmentationIdentifier(final YangInstanceIdentifier.AugmentationIdentifier arg) { - final Type augType = augIdentifierToType.get(arg); - try { - Class augClass = factory.getRuntimeContext().getStrategy().loadClass(augType); - return getStreamChild(augClass); - } catch (ClassNotFoundException e) { - throw new IllegalStateException("Unable to load referenced augmentation.", e); + + @SuppressWarnings("unchecked") + @Override + public Optional> possibleStreamChild( + final Class childClass) { + final DataContainerCodecPrototype childProto = byStreamClass.get(childClass); + if(childProto != null) { + return Optional.>of((DataContainerCodecContext) childProto.get()); } + return Optional.absent(); } - protected final LeafNodeCodecContext getLeafChild(final String name) { - final LeafNodeCodecContext value = leafChild.get(name); - Preconditions.checkArgument(value != null, "Leaf %s is not valid for %s", name, bindingClass); - return value; + @Override + public DataContainerCodecContext bindingPathArgumentChild(final InstanceIdentifier.PathArgument arg, + final List builder) { + + final Class argType = arg.getType(); + final DataContainerCodecPrototype ctxProto = byBindingArgClass.get(argType); + final DataContainerCodecContext context = + childNonNull(ctxProto, argType, "Class %s is not valid child of %s", argType, getBindingClass()).get(); + if (context instanceof ChoiceNodeCodecContext) { + final ChoiceNodeCodecContext choice = (ChoiceNodeCodecContext) context; + final DataContainerCodecContext caze = choice.getCazeByChildClass(arg.getType()); + choice.addYangPathArgument(arg, builder); + caze.addYangPathArgument(arg, builder); + return caze.bindingPathArgumentChild(arg, builder); + } + context.addYangPathArgument(arg, builder); + return context; } + @SuppressWarnings("unchecked") @Override - protected DataContainerCodecContext loadChild(final Class childClass) { - if (Augmentation.class.isAssignableFrom(childClass)) { - return loadAugmentation(childClass); + public NodeCodecContext yangPathArgumentChild(YangInstanceIdentifier.PathArgument arg) { + if(arg instanceof NodeIdentifierWithPredicates) { + arg = new NodeIdentifier(arg.getNodeType()); } + final NodeContextSupplier childSupplier = byYang.get(arg); + childNonNull(childSupplier != null, arg, "Argument %s is not valid child of %s", arg, schema()); + return (NodeCodecContext) childSupplier.get(); + } + + protected final LeafNodeCodecContext getLeafChild(final String name) { + final LeafNodeCodecContext value = leafChild.get(name); + return IncorrectNestingException.checkNonNull(value, "Leaf %s is not valid for %s", name, getBindingClass()); + } - DataSchemaNode origDef = factory.getRuntimeContext().getSchemaDefinition(childClass); + private DataContainerCodecPrototype loadChildPrototype(final Class childClass) { + final DataSchemaNode origDef = factory().getRuntimeContext().getSchemaDefinition(childClass); // Direct instantiation or use in same module in which grouping // was defined. - DataSchemaNode sameName = schema.getDataChildByName(origDef.getQName()); + DataSchemaNode sameName; + try { + sameName = schema().getDataChildByName(origDef.getQName()); + } catch (final IllegalArgumentException e) { + sameName = null; + } final DataSchemaNode childSchema; if (sameName != null) { // Exactly same schema node if (origDef.equals(sameName)) { childSchema = sameName; // We check if instantiated node was added via uses - // statement and is an instantiation of same grouping + // statement and is instantiation of same grouping } else if (origDef.equals(SchemaNodeUtils.getRootOriginalIfPossible(sameName))) { childSchema = sameName; } else { @@ -133,30 +229,89 @@ abstract class DataObjectCodecContext extends DataC } } else { // We are looking for instantiation via uses in other module - QName instantiedName = QName.create(namespace, origDef.getQName().getLocalName()); - DataSchemaNode potential = schema.getDataChildByName(instantiedName); + final QName instantiedName = QName.create(namespace(), origDef.getQName().getLocalName()); + final DataSchemaNode potential = schema().getDataChildByName(instantiedName); // We check if it is really instantiated from same - // definition - // as class was derived + // definition as class was derived if (potential != null && origDef.equals(SchemaNodeUtils.getRootOriginalIfPossible(potential))) { childSchema = potential; } else { childSchema = null; } } - Preconditions - .checkArgument(childSchema != null, "Node %s does not have child named %s", schema, childClass); - return DataContainerCodecContext.from(childClass, childSchema, factory); + final DataSchemaNode nonNullChild = + childNonNull(childSchema, childClass, "Node %s does not have child named %s", schema(), childClass); + return DataContainerCodecPrototype.from(childClass, nonNullChild, factory()); + } + + private DataContainerCodecPrototype getAugmentationPrototype(final Type value) { + final ClassLoadingStrategy loader = factory().getRuntimeContext().getStrategy(); + @SuppressWarnings("rawtypes") + final Class augClass; + try { + augClass = loader.loadClass(value); + } catch (final ClassNotFoundException e) { + LOG.warn("Failed to load augmentation prototype for {}", value, e); + return null; + } + + @SuppressWarnings("unchecked") + final Entry augSchema = factory().getRuntimeContext() + .getResolvedAugmentationSchema(schema(), augClass); + return DataContainerCodecPrototype.from(augClass, augSchema.getKey(), augSchema.getValue(), factory()); } @SuppressWarnings("rawtypes") - private AugmentationNode loadAugmentation(final Class childClass) { - Preconditions.checkArgument(schema instanceof AugmentationTarget); + Object getBindingChildValue(final Method method, final NormalizedNodeContainer domData) { + final NodeCodecContext childContext = byMethod.get(method).get(); @SuppressWarnings("unchecked") - Entry augSchema = factory.getRuntimeContext() - .getResolvedAugmentationSchema(schema, childClass); - QNameModule namespace = Iterables.getFirst(augSchema.getKey().getPossibleChildNames(), null).getModule(); - return new AugmentationNode(childClass, namespace, augSchema.getKey(), augSchema.getValue(), factory); + final Optional> domChild = domData.getChild(childContext.getDomPathArgument()); + if (domChild.isPresent()) { + return childContext.deserializeObject(domChild.get()); + } + return null; } -} \ No newline at end of file + protected final D createBindingProxy(final NormalizedNodeContainer node) { + try { + return (D) proxyConstructor.invokeExact((InvocationHandler)new LazyDataObject<>(this, node)); + } catch (final Throwable e) { + throw Throwables.propagate(e); + } + } + + @SuppressWarnings("unchecked") + public Map>, Augmentation> getAllAugmentationsFrom( + final NormalizedNodeContainer> data) { + + @SuppressWarnings("rawtypes") + final Map map = new HashMap<>(); + + for(final DataContainerCodecPrototype value : byStreamClass.values()) { + if(Augmentation.class.isAssignableFrom(value.getBindingClass())) { + final Optional> augData = data.getChild(value.getYangArg()); + if(augData.isPresent()) { + map.put(value.getBindingClass(), value.get().deserializeObject(augData.get())); + } + } + } + return map; + } + + public Collection getHashCodeAndEqualsMethods() { + // FIXME: Sort method in same order as in hashCode for generated class. + return byMethod.keySet(); + } + + @Override + public InstanceIdentifier.PathArgument deserializePathArgument(final YangInstanceIdentifier.PathArgument arg) { + Preconditions.checkArgument(getDomPathArgument().equals(arg)); + return bindingArg(); + } + + @Override + public YangInstanceIdentifier.PathArgument serializePathArgument(final InstanceIdentifier.PathArgument arg) { + Preconditions.checkArgument(bindingArg().equals(arg)); + return getDomPathArgument(); + } +}