Bug 1027: Improved instance identifier codec for augmentations 20/7220/1
authorTony Tkacik <ttkacik@cisco.com>
Mon, 19 May 2014 14:36:53 +0000 (16:36 +0200)
committerTony Tkacik <ttkacik@cisco.com>
Mon, 19 May 2014 14:39:56 +0000 (16:39 +0200)
InstanceIdentifierCodecImpl
Updated instance identifier codec to also create context
for child nodes of augmentation when augmentation is
for first time encountered in new context.

Improved structure of instance identifier codec to be bit
more readable and which allows for reuse.

LazyGeneratedCodecRegistry
Updated AugmentableDispatchCodec to try serialization
of InstanceIdentifier to augmentation when new
location is discovered and report back if serialization
failed.

Added documentation to adaptForPathImpl, renamed parameters
to be more readable.

Change-Id: Ie55b163a4ab617b82dd89ed85a504e104f147fe8
Signed-off-by: Tony Tkacik <ttkacik@cisco.com>
code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/InstanceIdentifierCodecImpl.java
code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/LazyGeneratedCodecRegistry.java
yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/util/BindingReflections.java

index 9fac9571c23f2124b36a087da7fd312a7517c075..fda3f068ac8faed5c341ae19083d36ef805f006d 100644 (file)
@@ -11,6 +11,7 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -46,6 +47,8 @@ public class InstanceIdentifierCodecImpl implements InstanceIdentifierCodec {
 
     private final CodecRegistry codecRegistry;
 
+    private final Map<Class<?>,Set<List<QName>>> augmentationAdapted = new WeakHashMap<>();
+
     private final Map<Class<?>, Map<List<QName>, Class<?>>> classToPreviousAugment = Collections
             .synchronizedMap(new WeakHashMap<Class<?>, Map<List<QName>, Class<?>>>());
 
@@ -70,6 +73,7 @@ public class InstanceIdentifierCodecImpl implements InstanceIdentifierCodec {
             }
             Map<List<QName>, Class<?>> injectAugment = classToPreviousAugment.get(baType);
             if (injectAugment != null) {
+                @SuppressWarnings("unchecked")
                 Class<? extends DataObject> augment = (Class<? extends DataObject>) injectAugment.get(scannedPath);
                 if (augment != null) {
                     baArgs.add(new Item(augment));
@@ -77,7 +81,7 @@ public class InstanceIdentifierCodecImpl implements InstanceIdentifierCodec {
             }
             baArgs.add(baArg);
         }
-        InstanceIdentifier ret = InstanceIdentifier.create(baArgs);
+        InstanceIdentifier<?> ret = InstanceIdentifier.create(baArgs);
         LOG.debug("DOM Instance Identifier {} deserialized to {}", input, ret);
         return ret;
     }
@@ -85,23 +89,28 @@ public class InstanceIdentifierCodecImpl implements InstanceIdentifierCodec {
     @Override
     public InstanceIdentifier<? extends Object> deserialize(
             final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier input,
-            InstanceIdentifier<?> bindingIdentifier) {
+            final InstanceIdentifier<?> bindingIdentifier) {
         return deserialize(input);
     }
 
-    private org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument _deserializePathArgument(
+    private InstanceIdentifier.PathArgument deserializeNodeIdentifier(
             final NodeIdentifier argument, final List<QName> processedPath) {
+        @SuppressWarnings("rawtypes")
         final Class cls = codecRegistry.getClassForPath(processedPath);
+        @SuppressWarnings("unchecked")
         Item<DataObject> item = new Item<>(cls);
         return item;
     }
 
-    private org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument _deserializePathArgument(
+    private InstanceIdentifier.PathArgument deserializeNodeIdentifierWithPrecicates(
             final NodeIdentifierWithPredicates argument, final List<QName> processedPath) {
+        @SuppressWarnings("rawtypes")
         final Class type = codecRegistry.getClassForPath(processedPath);
+        @SuppressWarnings({ "unchecked", "rawtypes" })
         final IdentifierCodec codec = codecRegistry
                 .<Identifiable<? extends Object>> getIdentifierCodecForIdentifiable(type);
         CompositeNode _compositeNode = this.toCompositeNode(argument);
+        @SuppressWarnings("unchecked")
         ValueWithQName<CompositeNode> deserialize = codec.deserialize(_compositeNode);
         Object value = null;
         if (deserialize != null) {
@@ -122,27 +131,21 @@ public class InstanceIdentifierCodecImpl implements InstanceIdentifierCodec {
     @Override
     public org.opendaylight.yangtools.yang.data.api.InstanceIdentifier serialize(final InstanceIdentifier<?> input) {
         Class<?> previousAugmentation = null;
-        List<InstanceIdentifier.PathArgument> pathArgs = input.getPath();
+        Iterable<InstanceIdentifier.PathArgument> pathArgs = input.getPathArguments();
         QName previousQName = null;
-        List<PathArgument> components = new ArrayList<>(pathArgs.size());
-        List<QName> qnamePath = new ArrayList<>(pathArgs.size());
+        List<PathArgument> components = new ArrayList<>();
+        List<QName> qnamePath = new ArrayList<>();
         for (InstanceIdentifier.PathArgument baArg : pathArgs) {
-
             if (!Augmentation.class.isAssignableFrom(baArg.getType())) {
-                org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument biArg = serializePathArgument(
-                        baArg, previousQName);
-                previousQName = biArg.getNodeType();
+                PathArgument biArg = serializePathArgumentAndUpdateMapping(qnamePath, baArg, previousQName,previousAugmentation);
                 components.add(biArg);
                 qnamePath.add(biArg.getNodeType());
-                ImmutableList<QName> immutableList = ImmutableList.copyOf(qnamePath);
-                codecRegistry.putPathToClass(immutableList, baArg.getType());
-                if (previousAugmentation != null) {
-                    updateAugmentationInjection(baArg.getType(), immutableList, previousAugmentation);
-                }
+                previousQName = biArg.getNodeType();
                 previousAugmentation = null;
             } else {
                 previousQName = codecRegistry.getQNameForAugmentation(baArg.getType());
                 previousAugmentation = baArg.getType();
+                ensureAugmentation(qnamePath,previousQName,baArg.getType());
             }
         }
         org.opendaylight.yangtools.yang.data.api.InstanceIdentifier ret = new org.opendaylight.yangtools.yang.data.api.InstanceIdentifier(
@@ -151,15 +154,34 @@ public class InstanceIdentifierCodecImpl implements InstanceIdentifierCodec {
         return ret;
     }
 
+    private synchronized void ensureAugmentation(final List<QName> augPath, final QName augQName, final Class<? extends DataObject> type) {
+        Set<List<QName>> augPotential = augmentationAdapted.get(type);
+        if(augPotential == null) {
+            augPotential = new HashSet<>();
+            augmentationAdapted.put(type, augPotential);
+        }
+        ImmutableList<QName> augTargetPath = ImmutableList.copyOf(augPath);
+        if(augPotential.contains(augPath)) {
+            return;
+        }
+
+        for(Class<? extends DataObject> child : BindingReflections.getChildrenClasses(type)) {
+            Item<? extends DataObject> baArg = new Item<>(child);
+            PathArgument biArg = serializePathArgumentAndUpdateMapping(augPath, baArg, augQName,type);
+        }
+        augPotential.add(augTargetPath);
+    }
+
+
     public Class<? extends Object> updateAugmentationInjection(final Class<? extends DataObject> class1,
-            final ImmutableList<QName> list, final Class<?> augmentation) {
+            final List<QName> list, final Class<?> augmentation) {
         if (classToPreviousAugment.get(class1) == null) {
             classToPreviousAugment.put(class1, new ConcurrentHashMap<List<QName>, Class<?>>());
         }
         return classToPreviousAugment.get(class1).put(list, augmentation);
     }
 
-    private PathArgument _serializePathArgument(final Item<?> argument, final QName previousQname) {
+    private PathArgument serializeItem(final Item<?> argument, final QName previousQname) {
         Class<?> type = argument.getType();
         QName qname = BindingReflections.findQName(type);
         if (previousQname == null || (BindingReflections.isAugmentationChild(argument.getType()))) {
@@ -168,15 +190,19 @@ public class InstanceIdentifierCodecImpl implements InstanceIdentifierCodec {
         return new NodeIdentifier(QName.create(previousQname, qname.getLocalName()));
     }
 
-    private PathArgument _serializePathArgument(final IdentifiableItem argument, final QName previousQname) {
+    private PathArgument serializeIdentifiableItem(final IdentifiableItem<?,?> argument, final QName previousQname) {
         Map<QName, Object> predicates = new HashMap<>();
+        @SuppressWarnings("rawtypes")
         Class type = argument.getType();
+        @SuppressWarnings("unchecked")
         IdentifierCodec<? extends Object> keyCodec = codecRegistry.getIdentifierCodecForIdentifiable(type);
         QName qname = BindingReflections.findQName(type);
         if (previousQname != null && !(BindingReflections.isAugmentationChild(argument.getType()))) {
             qname = QName.create(previousQname, qname.getLocalName());
         }
+        @SuppressWarnings({ "rawtypes", "unchecked" })
         ValueWithQName combinedInput = new ValueWithQName(previousQname, argument.getKey());
+        @SuppressWarnings("unchecked")
         CompositeNode compositeOutput = keyCodec.serialize(combinedInput);
         for (Node<?> outputValue : compositeOutput.getValue()) {
             predicates.put(outputValue.getNodeType(), outputValue.getValue());
@@ -190,26 +216,40 @@ public class InstanceIdentifierCodecImpl implements InstanceIdentifierCodec {
     private org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument deserializePathArgument(
             final PathArgument argument, final List<QName> processedPath) {
         if (argument instanceof NodeIdentifier) {
-            return _deserializePathArgument((NodeIdentifier) argument, processedPath);
+            return deserializeNodeIdentifier((NodeIdentifier) argument, processedPath);
         } else if (argument instanceof NodeIdentifierWithPredicates) {
-            return _deserializePathArgument((NodeIdentifierWithPredicates) argument, processedPath);
+            return deserializeNodeIdentifierWithPrecicates((NodeIdentifierWithPredicates) argument, processedPath);
         } else {
             throw new IllegalArgumentException("Unhandled parameter types: "
                     + Arrays.<Object> asList(argument, processedPath).toString());
         }
     }
 
+    private PathArgument serializePathArgumentAndUpdateMapping(final List<QName> parentPath, final InstanceIdentifier.PathArgument baArg, final QName previousQName, final Class<?> previousAugmentation) {
+        org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument biArg = serializePathArgument(baArg, previousQName);
+        List<QName> qnamePath = new ArrayList<>(parentPath);
+        qnamePath.add(biArg.getNodeType());
+        ImmutableList<QName> currentPath = ImmutableList.copyOf(qnamePath);
+        codecRegistry.putPathToClass(currentPath, baArg.getType());
+        if (previousAugmentation != null) {
+            updateAugmentationInjection(baArg.getType(), currentPath, previousAugmentation);
+        }
+        return biArg;
+    }
+
     private PathArgument serializePathArgument(
-            final org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument argument,
+            final InstanceIdentifier.PathArgument argument,
             final QName previousQname) {
         if (argument instanceof IdentifiableItem) {
-            return _serializePathArgument((IdentifiableItem) argument, previousQname);
+            return serializeIdentifiableItem((IdentifiableItem<?,?>) argument, previousQname);
         } else if (argument instanceof Item) {
-            return _serializePathArgument((Item<?>) argument, previousQname);
+            return serializeItem((Item<?>) argument, previousQname);
         } else {
             throw new IllegalArgumentException("Unhandled parameter types: "
                     + Arrays.<Object> asList(argument, previousQname).toString());
         }
     }
 
+
+
 }
index a2c1e4ce88f590e87cafe2bfc74448672bfc0a73..e62f28e6469f2ad1f9a2af9e319b7ca92650ec4a 100644 (file)
@@ -112,7 +112,7 @@ class LazyGeneratedCodecRegistry implements //
     @SuppressWarnings("rawtypes")
     private static final ConcurrentMap<Type, ChoiceCaseCodecImpl> typeToCaseCodecs = new ConcurrentHashMap<>();
 
-    private static final Map<SchemaPath, GeneratedTypeBuilder> pathToType = new ConcurrentHashMap<>();
+    private static final Map<SchemaPath, Type> pathToType = new ConcurrentHashMap<>();
     private static final Map<List<QName>, Type> pathToInstantiatedType = new ConcurrentHashMap<>();
     private static final Map<Type, QName> typeToQname = new ConcurrentHashMap<>();
     private static final BiMap<Type, AugmentationSchema> typeToAugment = HashBiMap
@@ -151,32 +151,32 @@ class LazyGeneratedCodecRegistry implements //
 
     @SuppressWarnings("unchecked")
     @Override
-    public <T extends Augmentation<?>> AugmentationCodecWrapper<T> getCodecForAugmentation(final Class<T> object) {
+    public <T extends Augmentation<?>> AugmentationCodecWrapper<T> getCodecForAugmentation(final Class<T> augClass) {
         AugmentationCodecWrapper<T> codec = null;
         @SuppressWarnings("rawtypes")
-        AugmentationCodecWrapper potentialCodec = augmentationCodecs.get(object);
+        AugmentationCodecWrapper potentialCodec = augmentationCodecs.get(augClass);
         if (potentialCodec != null) {
             codec = potentialCodec;
         } else {
-            lock.waitForSchema(object);
+            lock.waitForSchema(augClass);
             Class<? extends BindingCodec<Map<QName, Object>, Object>> augmentRawCodec = generator
-                    .augmentationTransformerFor(object);
+                    .augmentationTransformerFor(augClass);
 
             BindingCodec<Map<QName, Object>, Object> rawCodec = newInstanceOf(augmentRawCodec);
-            codec = new AugmentationCodecWrapper<T>(rawCodec, null, object);
-            augmentationCodecs.put(object, codec);
+            codec = new AugmentationCodecWrapper<T>(rawCodec, augClass);
+            augmentationCodecs.put(augClass, codec);
         }
 
         final Class<? extends Augmentable<?>> objectSupertype;
         try {
-            objectSupertype = BindingReflections.findAugmentationTarget(object);
+            objectSupertype = BindingReflections.findAugmentationTarget(augClass);
         } catch (Exception e) {
-            LOG.warn("Failed to find target for augmentation {}, ignoring it", object, e);
+            LOG.warn("Failed to find target for augmentation {}, ignoring it", augClass, e);
             return codec;
         }
 
         if (objectSupertype == null) {
-            LOG.warn("Augmentation target for {} not found, ignoring it", object);
+            LOG.warn("Augmentation target for {} not found, ignoring it", augClass);
             return codec;
         }
 
@@ -207,7 +207,8 @@ class LazyGeneratedCodecRegistry implements //
 
         @SuppressWarnings("rawtypes")
         final WeakReference<Class> weakRef = typeToClass.get(type);
-        Preconditions.checkState(weakRef != null, "Could not find loaded class for path: %s and type: %s", path, type.getFullyQualifiedName());
+        Preconditions.checkState(weakRef != null, "Could not find loaded class for path: %s and type: %s", path,
+                type.getFullyQualifiedName());
         return weakRef.get();
     }
 
@@ -273,7 +274,8 @@ class LazyGeneratedCodecRegistry implements //
 
     private DataSchemaNode getSchemaNode(final List<QName> path) {
         QName firstNode = path.get(0);
-        DataNodeContainer previous = currentSchema.findModuleByNamespaceAndRevision(firstNode.getNamespace(), firstNode.getRevision());
+        DataNodeContainer previous = currentSchema.findModuleByNamespaceAndRevision(firstNode.getNamespace(),
+                firstNode.getRevision());
         Preconditions.checkArgument(previous != null, "Failed to find module %s for path %s", firstNode, path);
 
         Iterator<QName> iterator = path.iterator();
@@ -327,7 +329,8 @@ class LazyGeneratedCodecRegistry implements //
             LOG.error("Failed to instantiate codec {}", cls.getSimpleName(), e);
             throw new IllegalStateException(String.format("Failed to instantiate codec %s", cls), e);
         } catch (IllegalAccessException e) {
-            LOG.debug("Run-time consistency issue: constructor for {} is not available. This indicates either a code generation bug or a misconfiguration of JVM.",
+            LOG.debug(
+                    "Run-time consistency issue: constructor for {} is not available. This indicates either a code generation bug or a misconfiguration of JVM.",
                     cls.getSimpleName(), e);
             throw new IllegalStateException(String.format("Cannot access contructor of %s", cls), e);
         }
@@ -428,10 +431,10 @@ class LazyGeneratedCodecRegistry implements //
                     identity.getKey());
         }
 
-        synchronized(augmentableToAugmentations) {
+        synchronized (augmentableToAugmentations) {
             augmentableToAugmentations.putAll(context.getAugmentableToAugmentations());
         }
-        synchronized(choiceToCases)  {
+        synchronized (choiceToCases) {
             choiceToCases.putAll(context.getChoiceToCases());
         }
         captureCases(context.getCases(), schemaContext);
@@ -493,7 +496,7 @@ class LazyGeneratedCodecRegistry implements //
             }
             SchemaPath path = caseNode.getPath();
 
-            GeneratedTypeBuilder type;
+            Type type;
             if (path != null && (type = pathToType.get(path)) != null) {
                 ReferencedTypeImpl typeref = new ReferencedTypeImpl(type.getPackageName(), type.getName());
                 @SuppressWarnings("rawtypes")
@@ -530,7 +533,7 @@ class LazyGeneratedCodecRegistry implements //
         }
     }
 
-    public AugmentableDispatchCodec getAugmentableCodec(final Class<?> dataClass) {
+    public synchronized AugmentableDispatchCodec getAugmentableCodec(final Class<?> dataClass) {
         AugmentableDispatchCodec ret = augmentableCodecs.get(dataClass);
         if (ret != null) {
             return ret;
@@ -675,43 +678,49 @@ class LazyGeneratedCodecRegistry implements //
             return implementation;
         }
 
-        protected final void adaptForPath(final InstanceIdentifier<?> path) {
+        protected final synchronized void adaptForPath(final InstanceIdentifier<?> path) {
             if (adaptedForPaths.contains(path)) {
                 return;
             }
             /**
-             * We search in schema context if the use of this location aware codec (augmentable codec, case codec)
-             * makes sense on provided location (path)
+             * We search in schema context if the use of this location aware
+             * codec (augmentable codec, case codec) makes sense on provided
+             * location (path)
              *
              */
-            Optional<DataNodeContainer> contextNode = BindingSchemaContextUtils.findDataNodeContainer(currentSchema, path);
+            Optional<DataNodeContainer> contextNode = BindingSchemaContextUtils.findDataNodeContainer(currentSchema,
+                    path);
             /**
-             * If context node is present, this codec makes sense on provided location.
+             * If context node is present, this codec makes sense on provided
+             * location.
              *
              */
             if (contextNode.isPresent()) {
                 synchronized (this) {
                     /**
                      *
-                     * We adapt (turn on / off) possible implementations of child codecs (augmentations, cases)
-                     * based on this location.
+                     * We adapt (turn on / off) possible implementations of
+                     * child codecs (augmentations, cases) based on this
+                     * location.
                      *
                      *
                      */
+
                     adaptForPathImpl(path, contextNode.get());
-                    try  {
+                    try {
                         /**
-                         * We trigger serialization of instance identifier, to make sure instance identifier
-                         * codec is aware of combination of this path / augmentation / case
+                         * We trigger serialization of instance identifier, to
+                         * make sure instance identifier codec is aware of
+                         * combination of this path / augmentation / case
                          */
                         instanceIdentifierCodec.serialize(path);
                     } catch (Exception e) {
-                        LOG.warn("Exception during preparation of instance identifier codec for  path {}.",path,e);
+                        LOG.warn("Exception during preparation of instance identifier codec for  path {}.", path, e);
                     }
                     adaptedForPaths.add(path);
                 }
             } else {
-                LOG.debug("Context node (parent node) not found for {}",path);
+                LOG.debug("Context node (parent node) not found for {}", path);
             }
         }
 
@@ -835,7 +844,8 @@ class LazyGeneratedCodecRegistry implements //
         }
     }
 
-    private static class PublicChoiceCodecImpl<T> implements ChoiceCodec<T>, Delegator<BindingCodec<Map<QName, Object>, Object>> {
+    private static class PublicChoiceCodecImpl<T> implements ChoiceCodec<T>,
+            Delegator<BindingCodec<Map<QName, Object>, Object>> {
 
         private final BindingCodec<Map<QName, Object>, Object> delegate;
 
@@ -880,7 +890,8 @@ class LazyGeneratedCodecRegistry implements //
     private class DispatchChoiceCodecImpl extends LocationAwareDispatchCodec<ChoiceCaseCodecImpl<?>> {
 
         @Override
-        public Object deserialize(final Object input, @SuppressWarnings("rawtypes") final InstanceIdentifier bindingIdentifier) {
+        public Object deserialize(final Object input,
+                @SuppressWarnings("rawtypes") final InstanceIdentifier bindingIdentifier) {
             // TODO Auto-generated method stub
             return null;
         }
@@ -1036,7 +1047,7 @@ class LazyGeneratedCodecRegistry implements //
     }
 
     @SuppressWarnings({ "rawtypes", "unchecked" })
-    private class AugmentableDispatchCodec extends LocationAwareDispatchCodec<AugmentationCodecWrapper> {
+    class AugmentableDispatchCodec extends LocationAwareDispatchCodec<AugmentationCodecWrapper> {
 
         private final Class augmentableType;
 
@@ -1124,7 +1135,7 @@ class LazyGeneratedCodecRegistry implements //
         protected void tryToLoadImplementations() {
             Type type = referencedType(augmentableType);
             Collection<Type> potentialAugmentations;
-            synchronized(augmentableToAugmentations) {
+            synchronized (augmentableToAugmentations) {
                 potentialAugmentations = new ArrayList(augmentableToAugmentations.get(type));
             }
             for (Type potential : potentialAugmentations) {
@@ -1137,37 +1148,74 @@ class LazyGeneratedCodecRegistry implements //
         }
 
         @Override
-        protected void adaptForPathImpl(final InstanceIdentifier<?> path, final DataNodeContainer ctxNode) {
+        protected void adaptForPathImpl(final InstanceIdentifier<?> augTarget, final DataNodeContainer ctxNode) {
             if (ctxNode instanceof AugmentationTarget) {
                 Set<AugmentationSchema> availableAugmentations = ((AugmentationTarget) ctxNode)
                         .getAvailableAugmentations();
                 if (!availableAugmentations.isEmpty()) {
-                    updateAugmentationMapping(path,availableAugmentations);
+                    updateAugmentationMapping(augTarget, availableAugmentations);
                 }
             }
         }
 
-        private void updateAugmentationMapping(final InstanceIdentifier<?> path, final Set<AugmentationSchema> availableAugmentations) {
+        /**
+         *
+         * Adapts augmentation codec for specific provider location (target)
+         *
+         * Since augmentation are not forward-referencing and may be discovered
+         * during runtime, we need to adapt {@link AugmentableDispatchCodec},
+         * {@link AugmentationCodecWrapper} and {@link InstanceIdentifierCodec}
+         * for this newly discovered location where augmentation may be used.
+         *
+         * Adaptation consists of:
+         * <ol>
+         * <li> scan of available (valid) augmentations for
+         * current location
+         * <li>lookup for Java classes derived from this augmentations
+         * <li>generation of missing codecs
+         * <li>updating Augmentation codecs to work with new location
+         * <li>updating Instance Identifier to work with new location
+         *
+         */
+        private void updateAugmentationMapping(final InstanceIdentifier<?> augTarget,
+                final Set<AugmentationSchema> availableAugmentations) {
             for (AugmentationSchema aug : availableAugmentations) {
 
                 Type potentialType = getTypeForAugmentation(aug);
                 if (potentialType != null) {
                     Optional<AugmentationCodecWrapper> potentialImpl = tryToLoadImplementation(potentialType);
                     if (potentialImpl.isPresent()) {
-                        potentialImpl.get().addApplicableFor(path,aug);
+                        potentialImpl.get().addApplicableFor(augTarget, aug);
+                        Class augType = potentialImpl.get().getDataType();
+                        InstanceIdentifier augPath = augTarget.augmentation(augType);
+                        try {
+
+                            org.opendaylight.yangtools.yang.data.api.InstanceIdentifier domPath = getInstanceIdentifierCodec().serialize(augPath);
+                            if(domPath == null) {
+                                LOG.error("Unable to serialize instance identifier for {}",augPath);
+                            }
+                        } catch (Exception e) {
+                            LOG.error("Unable to serialize instance identifiers for {}",augPath,e);
+                        }
+
                     }
                 } else {
-                    LOG.warn("Could not find generated type for augmentation {} with children {}", aug, aug.getChildNodes());
+                    // Omits warning for empty augmentations since they are not represented in data
+                    if(!aug.getChildNodes().isEmpty()) {
+                        LOG.warn("Could not find generated type for augmentation {} with children {}", aug,
+                            aug.getChildNodes());
+                    }
                 }
             }
-            availableAugmentations.toString();
         }
 
+
+
         private Type getTypeForAugmentation(final AugmentationSchema aug) {
             Optional<AugmentationSchema> currentAug = Optional.of(aug);
-            while(currentAug.isPresent()) {
+            while (currentAug.isPresent()) {
                 Type potentialType = typeToAugment.inverse().get(currentAug.get());
-                if(potentialType != null) {
+                if (potentialType != null) {
                     return potentialType;
                 }
                 currentAug = currentAug.get().getOriginalDefinition();
@@ -1212,20 +1260,20 @@ class LazyGeneratedCodecRegistry implements //
 
         private final BindingCodec delegate;
         private final QName augmentationQName;
-        private final Multimap<InstanceIdentifier<?>,QName> validAugmentationTargets;
+        private final Multimap<InstanceIdentifier<?>, QName> validAugmentationTargets;
         private final Class<?> augmentationType;
 
-        public AugmentationCodecWrapper(final BindingCodec<Map<QName, Object>, Object> rawCodec,
-                final InstanceIdentifier<?> targetId, final Class<?> dataType) {
+        public AugmentationCodecWrapper(final BindingCodec<Map<QName, Object>, Object> rawCodec, final Class<?> dataType) {
             this.delegate = rawCodec;
             this.augmentationType = dataType;
             this.augmentationQName = BindingReflections.findQName(rawCodec.getClass());
-            this.validAugmentationTargets = Multimaps.synchronizedSetMultimap(HashMultimap.<InstanceIdentifier<?>,QName>create());
+            this.validAugmentationTargets = Multimaps.synchronizedSetMultimap(HashMultimap
+                    .<InstanceIdentifier<?>, QName> create());
         }
 
         public void addApplicableFor(final InstanceIdentifier<?> path, final AugmentationSchema aug) {
-            for(DataSchemaNode child : aug.getChildNodes()) {
-                validAugmentationTargets.put(path,child.getQName());
+            for (DataSchemaNode child : aug.getChildNodes()) {
+                validAugmentationTargets.put(path, child.getQName());
             }
         }
 
@@ -1321,7 +1369,7 @@ class LazyGeneratedCodecRegistry implements //
         }
 
         @Override
-        public Object deserialize(final Object input,final InstanceIdentifier bindingIdentifier) {
+        public Object deserialize(final Object input, final InstanceIdentifier bindingIdentifier) {
             Type type = qnamesToIdentityMap.get(input);
             if (type == null) {
                 return null;
index b0671397f3b179afcd5b14f80d60d2e766b72543..c1b300c15ec87a33a726b56aae9ebcc152fa5e3b 100644 (file)
@@ -14,6 +14,8 @@ import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Type;
+import java.util.LinkedList;
+import java.util.List;
 import java.util.ServiceLoader;
 import java.util.concurrent.Callable;
 import java.util.concurrent.Future;
@@ -99,9 +101,10 @@ public class BindingReflections {
      * Returns a QName associated to supplied type
      *
      * @param dataType
-     * @return QName associated to supplied dataType. If dataType is Augmentation
-     *    method does not return canonical QName, but QName with correct namespace
-     *    revision, but virtual local name, since augmentations do not have name.
+     * @return QName associated to supplied dataType. If dataType is
+     *         Augmentation method does not return canonical QName, but QName
+     *         with correct namespace revision, but virtual local name, since
+     *         augmentations do not have name.
      */
     public static final QName findQName(final Class<?> dataType) {
         return classToQName.getUnchecked(dataType).orNull();
@@ -118,9 +121,10 @@ public class BindingReflections {
                     return Optional.of((QName) obj);
                 }
             } catch (NoSuchFieldException e) {
-                if(Augmentation.class.isAssignableFrom(key)) {
+                if (Augmentation.class.isAssignableFrom(key)) {
                     YangModuleInfo moduleInfo = getModuleInfo(key);
-                    return Optional.of(QName.create(moduleInfo.getNamespace(), moduleInfo.getRevision(), moduleInfo.getName()));
+                    return Optional.of(QName.create(moduleInfo.getNamespace(), moduleInfo.getRevision(),
+                            moduleInfo.getName()));
                 }
             } catch (SecurityException | IllegalArgumentException | IllegalAccessException e) {
                 // NOOP
@@ -191,7 +195,8 @@ public class BindingReflections {
         checkArgument(name.startsWith(BindingMapping.PACKAGE_PREFIX), "Package name not starting with %s, is: %s",
                 BindingMapping.PACKAGE_PREFIX, name);
         Matcher match = ROOT_PACKAGE_PATTERN.matcher(name);
-        checkArgument(match.find(),"Package name '%s' does not match required pattern '%s'",name,ROOT_PACKAGE_PATTERN_STRING);
+        checkArgument(match.find(), "Package name '%s' does not match required pattern '%s'", name,
+                ROOT_PACKAGE_PATTERN_STRING);
         return match.group(0);
     }
 
@@ -201,7 +206,8 @@ public class BindingReflections {
         final String potentialClassName = getModuleInfoClassName(packageName);
         return ClassLoaderUtils.withClassLoader(cls.getClassLoader(), new Callable<YangModuleInfo>() {
             @Override
-            public YangModuleInfo call() throws ClassNotFoundException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
+            public YangModuleInfo call() throws ClassNotFoundException, IllegalAccessException,
+                    IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
                 Class<?> moduleInfoClass = Thread.currentThread().getContextClassLoader().loadClass(potentialClassName);
                 return (YangModuleInfo) moduleInfoClass.getMethod("getInstance").invoke(null);
             }
@@ -241,28 +247,86 @@ public class BindingReflections {
     }
 
     public static ImmutableSet<YangModuleInfo> loadModuleInfos(final ClassLoader loader) {
-        Builder<YangModuleInfo> moduleInfoSet = ImmutableSet.<YangModuleInfo>builder();
-        ServiceLoader<YangModelBindingProvider> serviceLoader = ServiceLoader.load(YangModelBindingProvider.class, loader);
-        for(YangModelBindingProvider bindingProvider : serviceLoader) {
+        Builder<YangModuleInfo> moduleInfoSet = ImmutableSet.<YangModuleInfo> builder();
+        ServiceLoader<YangModelBindingProvider> serviceLoader = ServiceLoader.load(YangModelBindingProvider.class,
+                loader);
+        for (YangModelBindingProvider bindingProvider : serviceLoader) {
             YangModuleInfo moduleInfo = bindingProvider.getModuleInfo();
-            checkState(moduleInfo != null, "Module Info for %s is not available.",bindingProvider.getClass());
-            collectYangModuleInfo(bindingProvider.getModuleInfo(),moduleInfoSet);
+            checkState(moduleInfo != null, "Module Info for %s is not available.", bindingProvider.getClass());
+            collectYangModuleInfo(bindingProvider.getModuleInfo(), moduleInfoSet);
         }
-        return  moduleInfoSet.build();
+        return moduleInfoSet.build();
     }
 
-    private static void collectYangModuleInfo(final YangModuleInfo moduleInfo, final Builder<YangModuleInfo> moduleInfoSet) {
+    private static void collectYangModuleInfo(final YangModuleInfo moduleInfo,
+            final Builder<YangModuleInfo> moduleInfoSet) {
         moduleInfoSet.add(moduleInfo);
-        for(YangModuleInfo dependency : moduleInfo.getImportedModules()) {
+        for (YangModuleInfo dependency : moduleInfo.getImportedModules()) {
             collectYangModuleInfo(dependency, moduleInfoSet);
         }
     }
 
     public static boolean isRpcType(final Class<? extends DataObject> targetType) {
-        return  DataContainer.class.isAssignableFrom(targetType) //
+        return DataContainer.class.isAssignableFrom(targetType) //
                 && !ChildOf.class.isAssignableFrom(targetType) //
                 && !Notification.class.isAssignableFrom(targetType) //
                 && (targetType.getName().endsWith("Input") || targetType.getName().endsWith("Output"));
     }
 
+    /**
+     *
+     * Scans supplied class and returns an iterable of all data children classes.
+     *
+     * @param type YANG Modeled Entity derived from DataContainer
+     * @return Iterable of all data children, which have YANG modeled entity
+     */
+    @SuppressWarnings("unchecked")
+    public static Iterable<Class<? extends DataObject>> getChildrenClasses(final Class<? extends DataContainer> type) {
+        checkArgument(type != null, "Target type must not be null");
+        checkArgument(DataContainer.class.isAssignableFrom(type), "Supplied type must be derived from DataContainer");
+        List<Class<? extends DataObject>> ret = new LinkedList<>();
+        for (Method method : type.getMethods()) {
+            Optional<Class<? extends DataContainer>> entity = getYangModeledReturnType(method);
+            if (entity.isPresent()) {
+                ret.add((Class<? extends DataObject>) entity.get());
+            }
+        }
+        return ret;
+    }
+
+    @SuppressWarnings("unchecked")
+    private static Optional<Class<? extends DataContainer>> getYangModeledReturnType(final Method method) {
+        if (method.getName().equals("getClass") || !method.getName().startsWith("get")
+                || method.getParameterTypes().length > 0) {
+            return Optional.absent();
+        }
+
+        @SuppressWarnings("rawtypes")
+        Class returnType = method.getReturnType();
+        if (DataContainer.class.isAssignableFrom(returnType)) {
+            return Optional.<Class<? extends DataContainer>> of(returnType);
+        } else if (List.class.isAssignableFrom(returnType)) {
+            try {
+                return ClassLoaderUtils.withClassLoader(method.getDeclaringClass().getClassLoader(),
+                        new Callable<Optional<Class<? extends DataContainer>>>() {
+                            @SuppressWarnings("rawtypes")
+                            @Override
+                            public Optional<Class<? extends DataContainer>> call() {
+                                Type listResult = ClassLoaderUtils.getFirstGenericParameter(method
+                                        .getGenericReturnType());
+                                if (listResult instanceof Class
+                                        && DataContainer.class.isAssignableFrom((Class) listResult)) {
+                                    return Optional.<Class<? extends DataContainer>> of((Class) listResult);
+                                }
+                                return Optional.absent();
+                            }
+
+                        });
+            } catch (Exception e) {
+                LOG.debug("Unable to find YANG modeled return type for {}", method, e);
+            }
+        }
+        return Optional.absent();
+    }
+
 }