Revert "MDSAL-361: Deal with restricted types in unions"
[mdsal.git] / binding / mdsal-binding-generator-impl / src / main / java / org / opendaylight / mdsal / binding / yang / types / AbstractTypeProvider.java
index 0567c4937f33c433b36a816feca8128cf1c7f312..74a65c8c39e964c3823a626f327fbd91f15be600 100644 (file)
@@ -7,6 +7,7 @@
  */
 package org.opendaylight.mdsal.binding.yang.types;
 
+import static java.util.Objects.requireNonNull;
 import static org.opendaylight.yangtools.yang.model.util.SchemaContextUtil.findDataSchemaNode;
 import static org.opendaylight.yangtools.yang.model.util.SchemaContextUtil.findDataSchemaNodeForRelativeXPath;
 import static org.opendaylight.yangtools.yang.model.util.SchemaContextUtil.findParentModule;
@@ -31,7 +32,6 @@ import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
 import java.util.TreeMap;
-import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import org.opendaylight.mdsal.binding.generator.spi.TypeProvider;
 import org.opendaylight.mdsal.binding.model.api.AccessModifier;
@@ -53,7 +53,7 @@ import org.opendaylight.mdsal.binding.model.util.TypeConstants;
 import org.opendaylight.mdsal.binding.model.util.Types;
 import org.opendaylight.mdsal.binding.model.util.generated.type.builder.AbstractEnumerationBuilder;
 import org.opendaylight.mdsal.binding.model.util.generated.type.builder.GeneratedPropertyBuilderImpl;
-import org.opendaylight.yangtools.yang.binding.BindingMapping;
+import org.opendaylight.mdsal.binding.spec.naming.BindingMapping;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.Revision;
 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
@@ -93,7 +93,6 @@ import org.slf4j.LoggerFactory;
 public abstract class AbstractTypeProvider implements TypeProvider {
     private static final Logger LOG = LoggerFactory.getLogger(AbstractTypeProvider.class);
     private static final Pattern GROUPS_PATTERN = Pattern.compile("\\[(.*?)\\]");
-    private static final Pattern NUMBERS_PATTERN = Pattern.compile("[0-9]+\\z");
 
     // Backwards compatibility: Union types used to be instantiated in YANG namespace, which is no longer
     // the case, as unions are emitted to their correct schema path.
@@ -115,19 +114,22 @@ public abstract class AbstractTypeProvider implements TypeProvider {
      */
     private final Map<SchemaPath, Type> referencedTypes = new HashMap<>();
     private final Map<Module, Set<Type>> additionalTypes = new HashMap<>();
+    private final Map<SchemaNode, JavaTypeName> renames;
 
     /**
      * Creates new instance of class <code>TypeProviderImpl</code>.
      *
      * @param schemaContext
      *            contains the schema data red from YANG files
+     * @param renames
      * @throws IllegalArgumentException
      *             if <code>schemaContext</code> equal null.
      */
-    AbstractTypeProvider(final SchemaContext schemaContext) {
+    AbstractTypeProvider(final SchemaContext schemaContext, final Map<SchemaNode, JavaTypeName> renames) {
         Preconditions.checkArgument(schemaContext != null, "Schema Context cannot be null!");
 
         this.schemaContext = schemaContext;
+        this.renames = requireNonNull(renames);
         resolveTypeDefsFromContext();
     }
 
@@ -156,21 +158,6 @@ public abstract class AbstractTypeProvider implements TypeProvider {
         return additionalTypes;
     }
 
-    /**
-     *
-     * Converts basic YANG type <code>type</code> to JAVA <code>Type</code>.
-     *
-     * @param type
-     *            string with YANG name of type
-     * @return JAVA <code>Type</code> for YANG type <code>type</code>
-     * @see TypeProvider#javaTypeForYangType(String)
-     */
-    @Override
-    @Deprecated
-    public Type javaTypeForYangType(final String type) {
-        return BaseYangTypes.BASE_YANG_TYPES_PROVIDER.javaTypeForYangType(type);
-    }
-
     @Override
     public Type javaTypeForSchemaDefinitionType(final TypeDefinition<?> typeDefinition, final SchemaNode parentNode) {
         return javaTypeForSchemaDefinitionType(typeDefinition, parentNode, null);
@@ -218,7 +205,7 @@ public abstract class AbstractTypeProvider implements TypeProvider {
             }
 
             // FIXME: it looks as though we could be using the same codepath as above...
-            ret = BaseYangTypes.BASE_YANG_TYPES_PROVIDER.javaTypeForYangType(typeDefinition.getQName().getLocalName());
+            ret = BaseYangTypes.javaTypeForYangType(typeDefinition.getQName().getLocalName());
             if (ret == null) {
                 LOG.debug("Failed to resolve Java type for {}", typeDefinition);
             }
@@ -396,8 +383,7 @@ public abstract class AbstractTypeProvider implements TypeProvider {
         final String basePackageName = BindingMapping.getRootPackageName(module.getQNameModule());
         final JavaTypeName identifier = JavaTypeName.create(BindingGeneratorUtil.packageNameForGeneratedType(basePackageName,
             identity.getPath()), BindingMapping.getClassName(identity.getQName()));
-        final Type paramType = Types.wildcardTypeFor(identifier);
-        return Types.parameterizedTypeFor(Types.typeForClass(Class.class), paramType);
+        return Types.classType(Types.wildcardTypeFor(identifier));
     }
 
     /**
@@ -552,15 +538,17 @@ public abstract class AbstractTypeProvider implements TypeProvider {
                 Preconditions.checkArgument(dataNode != null, "Failed to find leafref target: %s in module %s (%s)",
                         strXPath, this.getParentModule(parentNode).getName(), parentNode.getQName().getModule());
 
+                // FIXME: this block seems to be some weird magic hack. Analyze and refactor it.
                 if (leafContainsEnumDefinition(dataNode)) {
                     returnType = referencedTypes.get(dataNode.getPath());
                 } else if (leafListContainsEnumDefinition(dataNode)) {
                     returnType = Types.listTypeFor(referencedTypes.get(dataNode.getPath()));
-                } else {
+                }
+                if (returnType == null) {
                     returnType = resolveTypeFromDataSchemaNode(dataNode);
                 }
             } else {
-                returnType = Types.typeForClass(Object.class);
+                returnType = Types.objectType();
             }
         }
         Preconditions.checkArgument(returnType != null, "Failed to find leafref target: %s in module %s (%s)",
@@ -801,8 +789,7 @@ public abstract class AbstractTypeProvider implements TypeProvider {
      *            string with the name of the module for to which the
      *            <code>typedef</code> belongs
      * @param typedef
-     *            type definition of the node for which should be creted JAVA
-     *            <code>Type</code> (usually generated TO)
+     *            type definition of the node for which should be created JAVA <code>Type</code> (usually generated TO)
      * @return JAVA <code>Type</code> representation of <code>typedef</code> or
      *         <code>null</code> value if <code>basePackageName</code> or
      *         <code>modulName</code> or <code>typedef</code> or Q name of
@@ -810,23 +797,23 @@ public abstract class AbstractTypeProvider implements TypeProvider {
      */
     private Type typedefToGeneratedType(final String basePackageName, final Module module,
             final TypeDefinition<?> typedef) {
-        final TypeDefinition<?> innerTypedef = typedef.getBaseType();
+        final TypeDefinition<?> baseTypedef = typedef.getBaseType();
 
         // See generatedTypeForExtendedDefinitionType() above for rationale behind this special case.
-        if (innerTypedef instanceof LeafrefTypeDefinition || innerTypedef instanceof IdentityrefTypeDefinition) {
+        if (baseTypedef instanceof LeafrefTypeDefinition || baseTypedef instanceof IdentityrefTypeDefinition) {
             return null;
         }
 
         final String typedefName = typedef.getQName().getLocalName();
 
         final Type returnType;
-        if (innerTypedef.getBaseType() != null) {
-            returnType = provideGeneratedTOFromExtendedType(typedef, innerTypedef, basePackageName,
+        if (baseTypedef.getBaseType() != null) {
+            returnType = provideGeneratedTOFromExtendedType(typedef, baseTypedef, basePackageName,
                 module.getName());
-        } else if (innerTypedef instanceof UnionTypeDefinition) {
+        } else if (baseTypedef instanceof UnionTypeDefinition) {
             final GeneratedTOBuilder genTOBuilder = provideGeneratedTOBuilderForUnionTypeDef(
                 JavaTypeName.create(basePackageName, BindingMapping.getClassName(typedef.getQName())),
-                (UnionTypeDefinition) innerTypedef, typedef);
+                (UnionTypeDefinition) baseTypedef, typedef);
             genTOBuilder.setTypedef(true);
             genTOBuilder.setIsUnion(true);
             addUnitsToGenTO(genTOBuilder, typedef.getUnits().orElse(null));
@@ -850,21 +837,21 @@ public abstract class AbstractTypeProvider implements TypeProvider {
             } else {
                 types.add(unionBuilder.build());
             }
-        } else if (innerTypedef instanceof EnumTypeDefinition) {
+        } else if (baseTypedef instanceof EnumTypeDefinition) {
             // enums are automatically Serializable
-            final EnumTypeDefinition enumTypeDef = (EnumTypeDefinition) innerTypedef;
+            final EnumTypeDefinition enumTypeDef = (EnumTypeDefinition) baseTypedef;
             // TODO units for typedef enum
             returnType = provideTypeForEnum(enumTypeDef, typedefName, typedef);
-        } else if (innerTypedef instanceof BitsTypeDefinition) {
+        } else if (baseTypedef instanceof BitsTypeDefinition) {
             final GeneratedTOBuilder genTOBuilder = provideGeneratedTOBuilderForBitsTypeDefinition(
                 JavaTypeName.create(basePackageName, BindingMapping.getClassName(typedef.getQName())),
-                (BitsTypeDefinition) innerTypedef, module.getName());
+                (BitsTypeDefinition) baseTypedef, module.getName());
             genTOBuilder.setTypedef(true);
             addUnitsToGenTO(genTOBuilder, typedef.getUnits().orElse(null));
             makeSerializable(genTOBuilder);
             returnType = genTOBuilder.build();
         } else {
-            final Type javaType = javaTypeForSchemaDefinitionType(innerTypedef, typedef);
+            final Type javaType = javaTypeForSchemaDefinitionType(baseTypedef, typedef);
             returnType = wrapJavaTypeIntoTO(basePackageName, typedef, javaType, module.getName());
         }
         if (returnType != null) {
@@ -898,11 +885,10 @@ public abstract class AbstractTypeProvider implements TypeProvider {
     private GeneratedTransferObject wrapJavaTypeIntoTO(final String basePackageName, final TypeDefinition<?> typedef,
             final Type javaType, final String moduleName) {
         Preconditions.checkNotNull(javaType, "javaType cannot be null");
-        final String propertyName = "value";
 
         final GeneratedTOBuilder genTOBuilder = typedefToTransferObject(basePackageName, typedef, moduleName);
         genTOBuilder.setRestrictions(BindingGeneratorUtil.getRestrictions(typedef));
-        final GeneratedPropertyBuilder genPropBuilder = genTOBuilder.addProperty(propertyName);
+        final GeneratedPropertyBuilder genPropBuilder = genTOBuilder.addProperty("value");
         genPropBuilder.setReturnType(javaType);
         genTOBuilder.addEqualsIdentity(genPropBuilder);
         genTOBuilder.addHashIdentity(genPropBuilder);
@@ -935,8 +921,6 @@ public abstract class AbstractTypeProvider implements TypeProvider {
 
         final GeneratedTOBuilder resultTOBuilder = builders.remove(0);
         builders.forEach(resultTOBuilder::addEnclosingTransferObject);
-
-        resultTOBuilder.addProperty("value").setReturnType(Types.CHAR_ARRAY);
         return resultTOBuilder;
     }
 
@@ -967,12 +951,11 @@ public abstract class AbstractTypeProvider implements TypeProvider {
         final Module module = findParentModule(schemaContext, parentNode);
 
         final GeneratedTOBuilder unionGenTOBuilder = newGeneratedTOBuilder(typeName);
+        unionGenTOBuilder.setIsUnion(true);
         unionGenTOBuilder.setSchemaPath(typedef.getPath());
         unionGenTOBuilder.setModuleName(module.getName());
         addCodegenInformation(unionGenTOBuilder, typedef);
-
         generatedTOBuilders.add(unionGenTOBuilder);
-        unionGenTOBuilder.setIsUnion(true);
 
         // Pattern string is the key, XSD regex is the value. The reason for this choice is that the pattern carries
         // also negation information and hence guarantees uniqueness.
@@ -1004,16 +987,12 @@ public abstract class AbstractTypeProvider implements TypeProvider {
     }
 
     /**
-     * Wraps code which handle case when union subtype is also of the type
-     * <code>UnionType</code>.
+     * Wraps code which handles the case when union subtype is also of the type <code>UnionType</code>.
      *
-     * In this case the new generated TO is created for union subtype (recursive
-     * call of method
-     * {@link #provideGeneratedTOBuildersForUnionTypeDef(String, UnionTypeDefinition,
-     * String, SchemaNode)}
-     * provideGeneratedTOBuilderForUnionTypeDef} and in parent TO builder
-     * <code>parentUnionGenTOBuilder</code> is created property which type is
-     * equal to new generated TO.
+     * In this case the new generated TO is created for union subtype (recursive call of method
+     * {@link #provideGeneratedTOBuildersForUnionTypeDef(String, UnionTypeDefinition, String, SchemaNode)}
+     * provideGeneratedTOBuilderForUnionTypeDef} and in parent TO builder <code>parentUnionGenTOBuilder</code> is
+     * created property which type is equal to new generated TO.
      *
      * @param parentUnionGenTOBuilder
      *            generated TO builder to which is the property with the child
@@ -1036,7 +1015,7 @@ public abstract class AbstractTypeProvider implements TypeProvider {
         final GeneratedPropertyBuilder propertyBuilder;
         propertyBuilder = parentUnionGenTOBuilder.addProperty(BindingMapping.getPropertyName(
             newTOBuilderName.simpleName()));
-        propertyBuilder.setReturnType(subUnionGenTOBUilders.get(0));
+        propertyBuilder.setReturnType(subUnionGenTOBUilders.get(0).build());
         parentUnionGenTOBuilder.addEqualsIdentity(propertyBuilder);
         parentUnionGenTOBuilder.addToStringProperty(propertyBuilder);
 
@@ -1074,7 +1053,7 @@ public abstract class AbstractTypeProvider implements TypeProvider {
         final TypeDefinition<?> baseType = baseTypeDefForExtendedType(unionSubtype);
         if (unionTypeName.equals(baseType.getQName().getLocalName())) {
             final Type javaType = BaseYangTypes.BASE_YANG_TYPES_PROVIDER.javaTypeForSchemaDefinitionType(baseType,
-                parentNode);
+                parentNode, BindingGeneratorUtil.getRestrictions(unionSubtype));
             if (javaType != null) {
                 updateUnionTypeAsProperty(parentUnionGenTOBuilder, javaType, unionTypeName);
             }
@@ -1178,9 +1157,14 @@ public abstract class AbstractTypeProvider implements TypeProvider {
      */
     private GeneratedTOBuilder typedefToTransferObject(final String basePackageName,
             final TypeDefinition<?> typedef, final String moduleName) {
-        final GeneratedTOBuilder newType = newGeneratedTOBuilder(JavaTypeName.create(
-            BindingGeneratorUtil.packageNameForGeneratedType(basePackageName, typedef.getPath()),
-            BindingMapping.getClassName(typedef.getQName().getLocalName())));
+        JavaTypeName name = renames.get(typedef);
+        if (name == null) {
+            name = JavaTypeName.create(
+                BindingGeneratorUtil.packageNameForGeneratedType(basePackageName, typedef.getPath()),
+                BindingMapping.getClassName(typedef.getQName().getLocalName()));
+        }
+
+        final GeneratedTOBuilder newType = newGeneratedTOBuilder(name);
         newType.setSchemaPath(typedef.getPath());
         newType.setModuleName(moduleName);
         addCodegenInformation(newType, typedef);
@@ -1327,7 +1311,7 @@ public abstract class AbstractTypeProvider implements TypeProvider {
      *            transfer object which needs to be serializable
      */
     private static void makeSerializable(final GeneratedTOBuilder gto) {
-        gto.addImplementsType(Types.typeForClass(Serializable.class));
+        gto.addImplementsType(Types.serializableType());
         final GeneratedPropertyBuilder prop = new GeneratedPropertyBuilderImpl("serialVersionUID");
         prop.setValue(Long.toString(BindingGeneratorUtil.computeDefaultSUID(gto)));
         gto.setSUID(prop);
@@ -1354,11 +1338,8 @@ public abstract class AbstractTypeProvider implements TypeProvider {
         final Map<Integer, List<TypeDefinition<?>>> typeDefinitionsDepths = new TreeMap<>();
         for (TypeDefinition<?> unsortedTypeDefinition : unsortedTypeDefinitions) {
             final Integer depth = getTypeDefinitionDepth(unsortedTypeDefinition);
-            List<TypeDefinition<?>> typeDefinitionsConcreteDepth = typeDefinitionsDepths.get(depth);
-            if (typeDefinitionsConcreteDepth == null) {
-                typeDefinitionsConcreteDepth = new ArrayList<>();
-                typeDefinitionsDepths.put(depth, typeDefinitionsConcreteDepth);
-            }
+            List<TypeDefinition<?>> typeDefinitionsConcreteDepth =
+                typeDefinitionsDepths.computeIfAbsent(depth, k -> new ArrayList<>());
             typeDefinitionsConcreteDepth.add(unsortedTypeDefinition);
         }
 
@@ -1408,21 +1389,22 @@ public abstract class AbstractTypeProvider implements TypeProvider {
     }
 
     /**
-     * Returns string which contains the same value as <code>name</code> but
-     * integer suffix is incremented by one. If <code>name</code> contains no
-     * number suffix then number 1 is added.
+     * Returns string which contains the same value as <code>name</code> but integer suffix is incremented by one. If
+     * <code>name</code> contains no number suffix, a new suffix initialized at 1 is added. A suffix is actually
+     * composed of a '$' marker, which is safe, as no YANG identifier can contain '$', and a unsigned decimal integer.
      *
      * @param name string with name of augmented node
      * @return string with the number suffix incremented by one (or 1 is added)
      */
     private static String provideAvailableNameForGenTOBuilder(final String name) {
-        final Matcher mtch = NUMBERS_PATTERN.matcher(name);
-        if (mtch.find()) {
-            final int newSuffix = Integer.parseInt(name.substring(mtch.start())) + 1;
-            return name.substring(0, mtch.start()) + newSuffix;
+        final int dollar = name.indexOf('$');
+        if (dollar == -1) {
+            return name + "$1";
         }
 
-        return name + 1;
+        final int newSuffix = Integer.parseUnsignedInt(name.substring(dollar + 1)) + 1;
+        Preconditions.checkState(newSuffix > 0, "Suffix counter overflow");
+        return name.substring(0, dollar + 1) + newSuffix;
     }
 
     public static void addUnitsToGenTO(final GeneratedTOBuilder to, final String units) {
@@ -1582,11 +1564,11 @@ public abstract class AbstractTypeProvider implements TypeProvider {
         return sb.toString();
     }
 
-    private static final Comparator<Bit> BIT_NAME_COMPARATOR = (o1, o2) -> o1.getName().compareTo(o2.getName());
+    private static final Comparator<Bit> BIT_NAME_COMPARATOR = Comparator.comparing(Bit::getName);
 
     private static String bitsToDef(final BitsTypeDefinition type, final String className, final String defaultValue, final boolean isExt) {
         final List<Bit> bits = new ArrayList<>(type.getBits());
-        Collections.sort(bits, BIT_NAME_COMPARATOR);
+        bits.sort(BIT_NAME_COMPARATOR);
         final StringBuilder sb = new StringBuilder();
         if (!isExt) {
             sb.append("new ");
@@ -1661,7 +1643,7 @@ public abstract class AbstractTypeProvider implements TypeProvider {
                 }
                 if (module == null) {
                     final List<Module> modulesList = new ArrayList<>(modules);
-                    Collections.sort(modulesList, (o1, o2) -> Revision.compare(o1.getRevision(), o2.getRevision()));
+                    modulesList.sort((o1, o2) -> Revision.compare(o1.getRevision(), o2.getRevision()));
                     module = modulesList.get(0);
                 }
             } else {