Fix augmentation pointing to a grouping action's input
[mdsal.git] / binding / mdsal-binding-generator-impl / src / main / java / org / opendaylight / mdsal / binding / generator / impl / ModuleContext.java
index 60ffe405039902edfceac6749c69981192f548fb..617189b9ce9c8af3b694ad5add045c5a57050bd5 100644 (file)
@@ -7,6 +7,9 @@
  */
 package org.opendaylight.mdsal.binding.generator.impl;
 
+import static com.google.common.base.Preconditions.checkState;
+import static java.util.Objects.requireNonNull;
+
 import com.google.common.collect.BiMap;
 import com.google.common.collect.HashBiMap;
 import com.google.common.collect.HashMultimap;
@@ -20,34 +23,75 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.mdsal.binding.model.api.GeneratedType;
+import org.opendaylight.mdsal.binding.model.api.JavaTypeName;
 import org.opendaylight.mdsal.binding.model.api.Type;
 import org.opendaylight.mdsal.binding.model.api.type.builder.GeneratedTOBuilder;
 import org.opendaylight.mdsal.binding.model.api.type.builder.GeneratedTypeBuilder;
+import org.opendaylight.mdsal.binding.spec.naming.BindingMapping;
+import org.opendaylight.yangtools.concepts.Mutable;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.DocumentedNode.WithStatus;
+import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
+import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
+import org.opendaylight.yangtools.yang.model.api.OperationDefinition;
 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
-public final class ModuleContext {
-    private GeneratedTypeBuilder moduleNode;
-    private final List<GeneratedTOBuilder> genTOs = new ArrayList<>();
-    private final Map<SchemaPath, Type> typedefs = new HashMap<>();
+/**
+ * Utility class for building up Binding mapping information. This class is NOT thread-safe.
+ */
+public final class ModuleContext implements Mutable {
+    private static final Logger LOG = LoggerFactory.getLogger(ModuleContext.class);
+
+    private final BiMap<Type, AugmentationSchemaNode> typeToAugmentation = HashBiMap.create();
     private final Map<SchemaPath, GeneratedTypeBuilder> childNodes = new HashMap<>();
     private final Map<SchemaPath, GeneratedTypeBuilder> groupings = new HashMap<>();
+    private final BiMap<Type, CaseSchemaNode> caseTypeToSchema = HashBiMap.create();
     private final Map<SchemaPath, GeneratedTypeBuilder> cases = new HashMap<>();
-    private final Map<QName,GeneratedTOBuilder> identities = new HashMap<>();
-    private final Set<GeneratedTypeBuilder> topLevelNodes = new HashSet<>();
+    private final Map<QName, GeneratedTypeBuilder> identities = new HashMap<>();
     private final List<GeneratedTypeBuilder> augmentations = new ArrayList<>();
-    private final BiMap<Type, AugmentationSchemaNode> typeToAugmentation = HashBiMap.create();
-    private final Map<Type, WithStatus> typeToSchema = new HashMap<>();
     private final Multimap<Type, Type> choiceToCases = HashMultimap.create();
-    private final BiMap<Type, CaseSchemaNode> caseTypeToSchema = HashBiMap.create();
-
+    private final Set<GeneratedTypeBuilder> topLevelNodes = new HashSet<>();
+    private final Map<SchemaPath, JavaTypeName> aliases = new HashMap<>();
+    private final Map<Type, WithStatus> typeToSchema = new HashMap<>();
+    private final List<GeneratedTOBuilder> genTOs = new ArrayList<>();
     private final Map<SchemaPath, Type> innerTypes = new HashMap<>();
+    private final Map<SchemaPath, Type> typedefs = new HashMap<>();
+    private final Module module;
+
+    // Conflict mapping
+    private final Map<JavaTypeName, SchemaNode> nameMapping = new HashMap<>();
+
+    private GeneratedTypeBuilder moduleNode;
+    private String modulePackageName;
+
+    ModuleContext(final Module module) {
+        this.module = requireNonNull(module);
+    }
+
+    Module module() {
+        return module;
+    }
+
+    String modulePackageName() {
+        String ret = modulePackageName;
+        if (ret == null) {
+            modulePackageName = ret = BindingMapping.getRootPackageName(module.getQNameModule());
+        }
+        return ret;
+    }
 
     List<Type> getGeneratedTypes() {
         List<Type> result = new ArrayList<>();
@@ -73,7 +117,7 @@ public final class ModuleContext {
         for (GeneratedTypeBuilder b : cases.values()) {
             result.add(b.build());
         }
-        for (GeneratedTOBuilder b : identities.values()) {
+        for (GeneratedTypeBuilder b : identities.values()) {
             result.add(b.build());
         }
         for (GeneratedTypeBuilder b : topLevelNodes) {
@@ -93,53 +137,84 @@ public final class ModuleContext {
         return moduleNode;
     }
 
-    public GeneratedTypeBuilder getChildNode(final SchemaPath p) {
-        return childNodes.get(p);
+    public GeneratedTypeBuilder getChildNode(final SchemaPath path) {
+        return childNodes.get(path);
     }
 
-    public GeneratedTypeBuilder getGrouping(final SchemaPath p) {
-        return groupings.get(p);
+    public GeneratedTypeBuilder getGrouping(final SchemaPath path) {
+        return groupings.get(path);
     }
 
-    public GeneratedTypeBuilder getCase(final SchemaPath p) {
-        return cases.get(p);
+    public GeneratedTypeBuilder getCase(final SchemaPath path) {
+        return cases.get(path);
     }
 
-    public void addModuleNode(final GeneratedTypeBuilder moduleNode) {
-        this.moduleNode = moduleNode;
+    public void addModuleNode(final GeneratedTypeBuilder newModuleNode) {
+        this.moduleNode = newModuleNode;
     }
 
-    public void addGeneratedTOBuilder(final GeneratedTOBuilder b) {
-        genTOs.add(b);
+    public void addGeneratedTOBuilder(final GeneratedTOBuilder builder) {
+        genTOs.add(builder);
     }
 
-    public void addChildNodeType(final SchemaNode p, final GeneratedTypeBuilder b) {
-        childNodes.put(p.getPath(), b);
-        typeToSchema.put(b,p);
+    @NonNull GeneratedType addAliasType(final ModuleContext sourceContext, final ContainerSchemaNode source,
+            final ContainerSchemaNode alias) {
+        final GeneratedTypeBuilder builder = sourceContext.getChildNode(source.getPath());
+        checkState(builder != null, "Could not find builder for %s", source);
+
+        final JavaTypeName id = builder.getIdentifier();
+        final SchemaPath path = alias.getPath();
+        final JavaTypeName prev = aliases.putIfAbsent(path, id);
+        checkState(prev == null, "Type aliasing conflict on %s: %s vs %s", path, prev, id);
+
+        return builder.build();
     }
 
-    public void addGroupingType(final SchemaPath p, final GeneratedTypeBuilder b) {
-        groupings.put(p, b);
+    @Nullable JavaTypeName getAlias(final SchemaPath path) {
+        return aliases.get(path);
     }
 
-    public void addTypedefType(final SchemaPath p, final Type t) {
-        typedefs.put(p, t);
+    public void addChildNodeType(final SchemaNode def, final GeneratedTypeBuilder builder) {
+        checkNamingConflict(def, builder.getIdentifier());
+        childNodes.put(def.getPath(), builder);
+        typeToSchema.put(builder, def);
     }
 
-    public void addCaseType(final SchemaPath p, final GeneratedTypeBuilder b) {
-        cases.put(p, b);
+    public void addGroupingType(final GroupingDefinition def, final GeneratedTypeBuilder builder) {
+        checkNamingConflict(def, builder.getIdentifier());
+        groupings.put(def.getPath(), builder);
     }
 
-    public void addIdentityType(final QName name,final GeneratedTOBuilder b) {
-        identities.put(name,b);
+    public void addTypedefType(final TypeDefinition<?> def, final Type type) {
+        final JavaTypeName name = type.getIdentifier();
+        final SchemaNode existingDef = nameMapping.putIfAbsent(name, def);
+        if (existingDef != null) {
+            if (!(existingDef instanceof TypeDefinition)) {
+                throw resolveNamingConfict(existingDef, def, name);
+            }
+
+            // This seems to be fine
+            LOG.debug("GeneratedType conflict between {} and {} on {}", def, existingDef, name);
+        }
+
+        typedefs.put(def.getPath(), type);
     }
 
-    public void addTopLevelNodeType(final GeneratedTypeBuilder b) {
-        topLevelNodes.add(b);
+    public void addCaseType(final SchemaPath path, final GeneratedTypeBuilder builder) {
+        cases.put(path, builder);
     }
 
-    public void addAugmentType(final GeneratedTypeBuilder b) {
-        augmentations.add(b);
+    public void addIdentityType(final IdentitySchemaNode def, final GeneratedTypeBuilder builder) {
+        checkNamingConflict(def, builder.getIdentifier());
+        identities.put(def.getQName(), builder);
+    }
+
+    public void addTopLevelNodeType(final GeneratedTypeBuilder builder) {
+        topLevelNodes.add(builder);
+    }
+
+    public void addAugmentType(final GeneratedTypeBuilder builder) {
+        augmentations.add(builder);
     }
 
     public Map<SchemaPath, Type> getTypedefs() {
@@ -158,7 +233,7 @@ public final class ModuleContext {
         return Collections.unmodifiableMap(cases);
     }
 
-    public Map<QName,GeneratedTOBuilder> getIdentities() {
+    public Map<QName, GeneratedTypeBuilder> getIdentities() {
         return Collections.unmodifiableMap(identities);
     }
 
@@ -190,10 +265,8 @@ public final class ModuleContext {
     }
 
     /**
-     *
-     * Returns mapping of type to its schema.
-     *
-     * Valid values are only instances of {@link DataSchemaNode} or {@link AugmentationSchemaNode}
+     * Returns mapping of type to its schema. Valid values are only instances of {@link DataSchemaNode}
+     * or {@link AugmentationSchemaNode}.
      *
      * @return Mapping from type to corresponding schema
      */
@@ -207,9 +280,6 @@ public final class ModuleContext {
 
     /**
      * Adds mapping between schema path and an inner type.
-     *
-     * @param path
-     * @param type
      */
     void addInnerTypedefType(final SchemaPath path, final Type type) {
         innerTypes.put(path, type);
@@ -219,4 +289,52 @@ public final class ModuleContext {
         return innerTypes.get(path);
     }
 
+    private void checkNamingConflict(final SchemaNode def, final JavaTypeName name) {
+        final SchemaNode existingDef = nameMapping.putIfAbsent(name, def);
+        if (existingDef != null) {
+            if (def.equals(existingDef)) {
+                if (LOG.isDebugEnabled()) {
+                    // TODO: this should not really be happening
+                    LOG.debug("Duplicate definition on {} as {}", name, def, new Throwable());
+                }
+            } else {
+                throw resolveNamingConfict(existingDef, def, name);
+            }
+        }
+    }
+
+    private static IllegalStateException resolveNamingConfict(final SchemaNode existing, final SchemaNode incoming,
+            final JavaTypeName name) {
+        if (existing instanceof IdentitySchemaNode) {
+            if (incoming instanceof GroupingDefinition || incoming instanceof TypeDefinition
+                    || incoming instanceof DataSchemaNode || incoming instanceof NotificationDefinition
+                    || incoming instanceof OperationDefinition) {
+                return new RenameMappingException(name, existing);
+            }
+        } else if (existing instanceof GroupingDefinition) {
+            if (incoming instanceof IdentitySchemaNode) {
+                return new RenameMappingException(name, incoming);
+            }
+            if (incoming instanceof TypeDefinition || incoming instanceof DataSchemaNode
+                    || incoming instanceof NotificationDefinition || incoming instanceof OperationDefinition) {
+                return new RenameMappingException(name, existing);
+            }
+        } else if (existing instanceof TypeDefinition) {
+            if (incoming instanceof GroupingDefinition || incoming instanceof IdentitySchemaNode) {
+                return new RenameMappingException(name, incoming);
+            }
+            if (incoming instanceof DataSchemaNode || incoming instanceof NotificationDefinition
+                    || incoming instanceof OperationDefinition) {
+                return new RenameMappingException(name, existing);
+            }
+        } else {
+            if (incoming instanceof GroupingDefinition || incoming instanceof IdentitySchemaNode
+                    || incoming instanceof TypeDefinition) {
+                return new RenameMappingException(name, incoming);
+            }
+        }
+
+        return new IllegalStateException(String.format("Unhandled GeneratedType conflict between %s and %s on %s",
+            incoming, existing, name));
+    }
 }