Binding codec v2 - fix augmentation #3
[mdsal.git] / binding2 / mdsal-binding2-runtime / src / main / java / org / opendaylight / mdsal / binding / javav2 / runtime / context / BindingRuntimeContext.java
old mode 100644 (file)
new mode 100755 (executable)
index 3786531..f82fc42
@@ -19,17 +19,19 @@ import com.google.common.collect.HashBiMap;
 import com.google.common.collect.HashMultimap;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Multimap;
+import java.lang.reflect.Method;
 import java.util.AbstractMap.SimpleEntry;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
 import javax.annotation.Nullable;
 import org.opendaylight.mdsal.binding.javav2.generator.api.ClassLoadingStrategy;
+import org.opendaylight.mdsal.binding.javav2.generator.context.ModuleContext;
 import org.opendaylight.mdsal.binding.javav2.generator.impl.BindingGeneratorImpl;
-import org.opendaylight.mdsal.binding.javav2.generator.impl.ModuleContext;
 import org.opendaylight.mdsal.binding.javav2.generator.util.JavaIdentifier;
 import org.opendaylight.mdsal.binding.javav2.generator.util.JavaIdentifierNormalizer;
 import org.opendaylight.mdsal.binding.javav2.generator.util.ReferencedTypeImpl;
@@ -44,16 +46,20 @@ import org.opendaylight.mdsal.binding.javav2.spec.structural.Augmentation;
 import org.opendaylight.yangtools.concepts.Immutable;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
-import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
-import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
+import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.OperationDefinition;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementSource;
 import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
 import org.opendaylight.yangtools.yang.model.util.EffectiveAugmentationSchema;
 import org.opendaylight.yangtools.yang.model.util.SchemaNodeUtils;
@@ -83,8 +89,7 @@ public class BindingRuntimeContext implements Immutable {
     private static final char DOT = '.';
     private final ClassLoadingStrategy strategy;
     private final SchemaContext schemaContext;
-
-    private final Map<Type, AugmentationSchema> augmentationToSchema = new HashMap<>();
+    private final Multimap<Type, AugmentationSchemaNode> augmentationToSchema = HashMultimap.create();
     private final BiMap<Type, Object> typeToDefiningSchema = HashBiMap.create();
     private final Multimap<Type, Type> choiceToCases = HashMultimap.create();
     private final Map<QName, Type> identities = new HashMap<>();
@@ -111,7 +116,7 @@ public class BindingRuntimeContext implements Immutable {
         final Map<Module, ModuleContext> modules = generator.getModuleContexts(this.schemaContext);
 
         for (final ModuleContext ctx : modules.values()) {
-            this.augmentationToSchema.putAll(ctx.getTypeToAugmentation());
+            this.augmentationToSchema.putAll(ctx.getTypeToAugmentations());
             this.typeToDefiningSchema.putAll(ctx.getTypeToSchema());
 
             ctx.getTypedefs();
@@ -166,7 +171,7 @@ public class BindingRuntimeContext implements Immutable {
      * augmentations, which may be present in runtime for them, thus returned
      * schema is unsuitable for use for validation of data.
      * <p>
-     * For retrieving {@link AugmentationSchema}, which will contains full model
+     * For retrieving {@link AugmentationSchemaNode}, which will contains full model
      * for child nodes, you should use method
      * {@link #getResolvedAugmentationSchema(DataNodeContainer, Class)} which
      * will return augmentation schema derived from supplied augmentation target
@@ -179,8 +184,11 @@ public class BindingRuntimeContext implements Immutable {
      * @throws IllegalArgumentException
      *             - if supplied class is not an augmentation
      */
-    public @Nullable AugmentationSchema getAugmentationDefinition(final Class<?> augClass) throws IllegalArgumentException {
-        Preconditions.checkArgument(Augmentation.class.isAssignableFrom(augClass), "Class %s does not represent augmentation", augClass);
+
+    public @Nullable Collection<AugmentationSchemaNode> getAugmentationDefinition(final Class<?> augClass)
+            throws IllegalArgumentException {
+        Preconditions.checkArgument(Augmentation.class.isAssignableFrom(augClass),
+            "Class %s does not represent augmentation", augClass);
         return this.augmentationToSchema.get(referencedType(augClass));
     }
 
@@ -205,19 +213,19 @@ public class BindingRuntimeContext implements Immutable {
     }
 
     /**
-     * Returns defining {@link AugmentationSchema} of target for supplied class.
+     * Returns defining {@link AugmentationSchemaNode} of target for supplied class.
      *
      * @param target
      *            - {@link DataNodeContainer}
      * @param aug
      *            - supplied class
-     * @return entry of {@link AugmentationSchema} according to its identifier
+     * @return entry of {@link AugmentationSchemaNode} according to its identifier
      *         {@link AugmentationIdentifier}
      */
-    public Entry<AugmentationIdentifier, AugmentationSchema> getResolvedAugmentationSchema(final DataNodeContainer target,
-            final Class<? extends Augmentation<?>> aug) {
-        final AugmentationSchema origSchema = getAugmentationDefinition(aug);
-        Preconditions.checkArgument(origSchema != null, "Augmentation %s is not known in current schema context",aug);
+    public Entry<AugmentationIdentifier, AugmentationSchemaNode> getResolvedAugmentationSchema(
+            final DataNodeContainer target, final Class<? extends Augmentation<?>> aug) {
+        final Collection<AugmentationSchemaNode> origSchemas = getAugmentationDefinition(aug);
+        Preconditions.checkArgument(origSchemas != null, "Augmentation %s is not known in current schema context",aug);
         /*
          * FIXME: Validate augmentation schema lookup
          *
@@ -232,24 +240,27 @@ public class BindingRuntimeContext implements Immutable {
          */
         final Set<QName> childNames = new HashSet<>();
         final Set<DataSchemaNode> realChilds = new HashSet<>();
-        for (final DataSchemaNode child : origSchema.getChildNodes()) {
-            final DataSchemaNode dataChildQNname = target.getDataChildByName(child.getQName());
-            final String childLocalName = child.getQName().getLocalName();
-            if (dataChildQNname == null) {
-                for (final DataSchemaNode dataSchemaNode : target.getChildNodes()) {
-                    if (childLocalName.equals(dataSchemaNode.getQName().getLocalName())) {
-                        realChilds.add(dataSchemaNode);
-                        childNames.add(dataSchemaNode.getQName());
+        for (final AugmentationSchemaNode origSchema : origSchemas) {
+            for (final DataSchemaNode child : origSchema.getChildNodes()) {
+                final DataSchemaNode dataChildQNname = target.getDataChildByName(child.getQName());
+                final String childLocalName = child.getQName().getLocalName();
+                if (dataChildQNname == null) {
+                    for (final DataSchemaNode dataSchemaNode : target.getChildNodes()) {
+                        if (childLocalName.equals(dataSchemaNode.getQName().getLocalName())) {
+                            realChilds.add(dataSchemaNode);
+                            childNames.add(dataSchemaNode.getQName());
+                        }
                     }
+                } else {
+                    realChilds.add(dataChildQNname);
+                    childNames.add(child.getQName());
                 }
-            } else {
-                realChilds.add(dataChildQNname);
-                childNames.add(child.getQName());
             }
         }
 
         final AugmentationIdentifier identifier = new AugmentationIdentifier(childNames);
-        final AugmentationSchema proxy = new EffectiveAugmentationSchema(origSchema, realChilds);
+        final AugmentationSchemaNode proxy = new EffectiveAugmentationSchema(origSchemas.stream().findFirst().get(),
+            realChilds);
         return new SimpleEntry<>(identifier, proxy);
     }
 
@@ -265,9 +276,9 @@ public class BindingRuntimeContext implements Immutable {
      * @throws IllegalArgumentException
      *             - if supplied class does not represent case
      */
-    public Optional<ChoiceCaseNode> getCaseSchemaDefinition(final ChoiceSchemaNode schema, final Class<?> childClass) throws IllegalArgumentException {
+    public Optional<CaseSchemaNode> getCaseSchemaDefinition(final ChoiceSchemaNode schema, final Class<?> childClass) throws IllegalArgumentException {
         final DataSchemaNode origSchema = getSchemaDefinition(childClass);
-        Preconditions.checkArgument(origSchema instanceof ChoiceCaseNode, "Supplied schema %s is not case.", origSchema);
+        Preconditions.checkArgument(origSchema instanceof CaseSchemaNode, "Supplied schema %s is not case.", origSchema);
 
         /*
          * FIXME: Make sure that if there are multiple augmentations of same
@@ -276,29 +287,30 @@ public class BindingRuntimeContext implements Immutable {
          * unaware that he is using incorrect case which was generated for
          * choice inside grouping.
          */
-        final Optional<ChoiceCaseNode> found = BindingSchemaContextUtils.findInstantiatedCase(schema,
-                (ChoiceCaseNode) origSchema);
+        final Optional<CaseSchemaNode> found = BindingSchemaContextUtils.findInstantiatedCase(schema,
+                (CaseSchemaNode) origSchema);
         return found;
     }
 
     private static Type referencedType(final Class<?> type) {
-        return new ReferencedTypeImpl(type.getPackage().getName(), type.getSimpleName());
+        return new ReferencedTypeImpl(type.getPackage().getName(), type.getSimpleName(), true, null);
     }
 
     static Type referencedType(final String type) {
         final int packageClassSeparator = type.lastIndexOf(DOT);
-        return new ReferencedTypeImpl(type.substring(0, packageClassSeparator), type.substring(packageClassSeparator + 1));
+        return new ReferencedTypeImpl(type.substring(0, packageClassSeparator),
+            type.substring(packageClassSeparator + 1), true, null);
     }
 
     /**
-     * Returns schema ({@link DataSchemaNode}, {@link AugmentationSchema} or {@link TypeDefinition})
+     * Returns schema ({@link DataSchemaNode}, {@link AugmentationSchemaNode} or {@link TypeDefinition})
      * from which supplied class was generated. Returned schema may be augmented with
      * additional information, which was not available at compile type
      * (e.g. third party augmentations).
      *
      * @param type Binding Class for which schema should be retrieved.
      * @return Instance of generated type (definition of Java API), along with
-     *     {@link DataSchemaNode}, {@link AugmentationSchema} or {@link TypeDefinition}
+     *     {@link DataSchemaNode}, {@link AugmentationSchemaNode} or {@link TypeDefinition}
      *     which was used to generate supplied class.
      */
     public Entry<GeneratedType, Object> getTypeWithSchema(final Class<?> type) {
@@ -406,21 +418,34 @@ public class BindingRuntimeContext implements Immutable {
         }
     }
 
+    private boolean isLocalAugment(final AugmentationTarget container, final AugmentationSchemaNode augment) {
+        Preconditions.checkState(container instanceof SchemaNode);
+        final QName root = ((SchemaNode) container).getPath().getPathFromRoot().iterator().next();
+        // findFirst makes no sense but just pick up one child to judge whether the target node is
+        // in the same module.
+        final java.util.Optional<DataSchemaNode> child = augment.getChildNodes().stream().findFirst();
+        if (child.isPresent()) {
+            return root.getModule().equals(child.get().getQName().getModule());
+        }
+        return false;
+    }
+
     public ImmutableMap<AugmentationIdentifier,Type> getAvailableAugmentationTypes(final DataNodeContainer container) {
         final Map<AugmentationIdentifier,Type> identifierToType = new HashMap<>();
         if (container instanceof AugmentationTarget) {
-            final Set<AugmentationSchema> augments = ((AugmentationTarget) container).getAvailableAugmentations();
-            for (final AugmentationSchema augment : augments) {
-                // Augmentation must have child nodes if is to be used with Binding classes
-                AugmentationSchema augOrig = augment;
-                while (augOrig.getOriginalDefinition().isPresent()) {
-                    augOrig = augOrig.getOriginalDefinition().get();
-                }
+            for (final AugmentationSchemaNode augment : ((AugmentationTarget) container).getAvailableAugmentations()) {
+                if (!isLocalAugment((AugmentationTarget) container, augment)) {
+                    // Augmentation must have child nodes if is to be used with Binding classes
+                    AugmentationSchemaNode augOrig = augment;
+                    while (augOrig.getOriginalDefinition().isPresent()) {
+                        augOrig = augOrig.getOriginalDefinition().get();
+                    }
 
-                if (!augment.getChildNodes().isEmpty()) {
-                    final Type augType = this.typeToDefiningSchema.inverse().get(augOrig);
-                    if (augType != null) {
-                        identifierToType.put(getAugmentationIdentifier(augment),augType);
+                    if (!augment.getChildNodes().isEmpty()) {
+                        final Type augType = this.typeToDefiningSchema.inverse().get(augOrig);
+                        if (augType != null) {
+                            identifierToType.put(getAugmentationIdentifier(augment), augType);
+                        }
                     }
                 }
             }
@@ -429,7 +454,7 @@ public class BindingRuntimeContext implements Immutable {
         return ImmutableMap.copyOf(identifierToType);
     }
 
-    private static AugmentationIdentifier getAugmentationIdentifier(final AugmentationSchema augment) {
+    private static AugmentationIdentifier getAugmentationIdentifier(final AugmentationSchemaNode augment) {
         final Set<QName> childNames = new HashSet<>();
         for (final DataSchemaNode child : augment.getChildNodes()) {
             childNames.add(child.getQName());
@@ -441,7 +466,7 @@ public class BindingRuntimeContext implements Immutable {
         if (type instanceof ReferencedTypeImpl) {
             return type;
         }
-        return new ReferencedTypeImpl(type.getPackageName(), type.getName());
+        return new ReferencedTypeImpl(type.getPackageName(), type.getName(), true, null);
     }
 
     private static Set<Type> collectAllContainerTypes(final GeneratedType type, final Set<Type> collection) {
@@ -474,4 +499,22 @@ public class BindingRuntimeContext implements Immutable {
     public Class<?> getIdentityClass(final QName input) {
         return this.identityClasses.getUnchecked(input);
     }
+
+    public Method findOperationMethod(final Class<?> key, final OperationDefinition operationDef)
+            throws NoSuchMethodException {
+        final String methodName =
+                JavaIdentifierNormalizer.normalizeSpecificIdentifier(operationDef.getQName().getLocalName(),
+                JavaIdentifier.METHOD);
+        if (operationDef.getInput() != null && isExplicitStatement(operationDef.getInput())) {
+            final Class<?> inputClz = this.getClassForSchema(operationDef.getInput());
+            return key.getMethod(methodName, inputClz);
+        }
+        return key.getMethod(methodName);
+    }
+
+    @SuppressWarnings("rawtypes")
+    private static boolean isExplicitStatement(final ContainerSchemaNode node) {
+        return node instanceof EffectiveStatement
+                && ((EffectiveStatement) node).getDeclared().getStatementSource() == StatementSource.DECLARATION;
+    }
 }