Add BitsTypeObject
[mdsal.git] / binding / mdsal-binding-generator / src / main / java / org / opendaylight / mdsal / binding / generator / impl / reactor / AbstractTypeObjectGenerator.java
index e8a7cf1f83d857024dfee404f2dca3d29048c098..048cf5a1d84ec7e8b2b9d96e1aaf5f028fde4bc6 100644 (file)
@@ -7,9 +7,11 @@
  */
 package org.opendaylight.mdsal.binding.generator.impl.reactor;
 
+import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Verify.verify;
 import static com.google.common.base.Verify.verifyNotNull;
 
+import com.google.common.base.VerifyException;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Maps;
 import java.util.ArrayList;
@@ -22,7 +24,6 @@ import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.jdt.annotation.Nullable;
 import org.opendaylight.mdsal.binding.generator.BindingGeneratorUtil;
 import org.opendaylight.mdsal.binding.generator.impl.reactor.TypeReference.ResolvedLeafref;
-import org.opendaylight.mdsal.binding.model.api.AccessModifier;
 import org.opendaylight.mdsal.binding.model.api.ConcreteType;
 import org.opendaylight.mdsal.binding.model.api.Enumeration;
 import org.opendaylight.mdsal.binding.model.api.GeneratedProperty;
@@ -31,6 +32,7 @@ import org.opendaylight.mdsal.binding.model.api.GeneratedType;
 import org.opendaylight.mdsal.binding.model.api.JavaTypeName;
 import org.opendaylight.mdsal.binding.model.api.Restrictions;
 import org.opendaylight.mdsal.binding.model.api.Type;
+import org.opendaylight.mdsal.binding.model.api.YangSourceDefinition;
 import org.opendaylight.mdsal.binding.model.api.type.builder.GeneratedPropertyBuilder;
 import org.opendaylight.mdsal.binding.model.api.type.builder.GeneratedTOBuilder;
 import org.opendaylight.mdsal.binding.model.api.type.builder.GeneratedTypeBuilderBase;
@@ -41,6 +43,7 @@ import org.opendaylight.mdsal.binding.model.ri.TypeConstants;
 import org.opendaylight.mdsal.binding.model.ri.Types;
 import org.opendaylight.mdsal.binding.model.ri.generated.type.builder.AbstractEnumerationBuilder;
 import org.opendaylight.mdsal.binding.model.ri.generated.type.builder.GeneratedPropertyBuilderImpl;
+import org.opendaylight.mdsal.binding.runtime.api.RuntimeType;
 import org.opendaylight.mdsal.binding.spec.naming.BindingMapping;
 import org.opendaylight.yangtools.concepts.Immutable;
 import org.opendaylight.yangtools.yang.binding.RegexPatterns;
@@ -221,7 +224,8 @@ import org.slf4j.LoggerFactory;
  * type indirection in YANG constructs is therefore explicitly excluded from the generated Java code, but the Binding
  * Specification still takes them into account when determining types as outlined above.
  */
-abstract class AbstractTypeObjectGenerator<T extends EffectiveStatement<?, ?>> extends AbstractDependentGenerator<T> {
+abstract class AbstractTypeObjectGenerator<S extends EffectiveStatement<?, ?>, R extends RuntimeType>
+        extends AbstractDependentGenerator<S, R> {
     private static final class UnionDependencies implements Immutable {
         private final Map<EffectiveStatement<?, ?>, TypeReference> identityTypes = new HashMap<>();
         private final Map<EffectiveStatement<?, ?>, TypeReference> leafTypes = new HashMap<>();
@@ -233,8 +237,7 @@ abstract class AbstractTypeObjectGenerator<T extends EffectiveStatement<?, ?>> e
 
         private void resolveUnionDependencies(final GeneratorContext context, final TypeEffectiveStatement<?> union) {
             for (EffectiveStatement<?, ?> stmt : union.effectiveSubstatements()) {
-                if (stmt instanceof TypeEffectiveStatement) {
-                    final TypeEffectiveStatement<?> type = (TypeEffectiveStatement<?>) stmt;
+                if (stmt instanceof TypeEffectiveStatement<?> type) {
                     final QName typeName = type.argument();
                     if (TypeDefinitions.IDENTITYREF.equals(typeName)) {
                         if (!identityTypes.containsKey(stmt)) {
@@ -280,6 +283,11 @@ abstract class AbstractTypeObjectGenerator<T extends EffectiveStatement<?, ?>> e
 
     private final TypeEffectiveStatement<?> type;
 
+    // FIXME: these fields should be better-controlled with explicit sequencing guards. It it currently stands, we are
+    //        expending two (or more) additional fields to express downstream linking. If we had the concept of
+    //        resolution step (an enum), we could just get by with a simple queue of Step/Callback pairs, which would
+    //        trigger as needed. See how we manage baseGen/inferred fields.
+
     /**
      * The generator corresponding to our YANG base type. It produces the superclass of our encapsulated type. If it is
      * {@code null}, this generator is the root of the hierarchy.
@@ -288,9 +296,15 @@ abstract class AbstractTypeObjectGenerator<T extends EffectiveStatement<?, ?>> e
     private TypeReference refType;
     private List<GeneratedType> auxiliaryGeneratedTypes = List.of();
     private UnionDependencies unionDependencies;
-    private List<AbstractTypeObjectGenerator<?>> inferred = List.of();
+    private List<AbstractTypeObjectGenerator<?, ?>> inferred = List.of();
 
-    AbstractTypeObjectGenerator(final T statement, final AbstractCompositeGenerator<?> parent) {
+    /**
+     * The type of single-element return type of the getter method associated with this generator. This is retained for
+     * run-time type purposes. It may be uninitialized, in which case this object must have a generated type.
+     */
+    private Type methodReturnTypeElement;
+
+    AbstractTypeObjectGenerator(final S statement, final AbstractCompositeGenerator<?, ?> parent) {
         super(statement, parent);
         type = statement().findFirstEffectiveSubstatement(TypeEffectiveStatement.class).orElseThrow();
     }
@@ -311,16 +325,16 @@ abstract class AbstractTypeObjectGenerator<T extends EffectiveStatement<?, ?>> e
             return;
         }
 
-        final AbstractExplicitGenerator<?> prev = previous();
+        final AbstractExplicitGenerator<S, R> prev = previous();
         if (prev != null) {
             verify(prev instanceof AbstractTypeObjectGenerator, "Unexpected previous %s", prev);
-            ((AbstractTypeObjectGenerator<?>) prev).linkInferred(this);
+            ((AbstractTypeObjectGenerator<S, R>) prev).linkInferred(this);
         } else {
             linkBaseGen(context.resolveTypedef(typeName));
         }
     }
 
-    private void linkInferred(final AbstractTypeObjectGenerator<?> downstream) {
+    private void linkInferred(final AbstractTypeObjectGenerator<?, ?> downstream) {
         if (inferred == null) {
             downstream.linkBaseGen(verifyNotNull(baseGen, "Mismatch on linking between %s and %s", this, downstream));
             return;
@@ -334,13 +348,13 @@ abstract class AbstractTypeObjectGenerator<T extends EffectiveStatement<?, ?>> e
 
     private void linkBaseGen(final TypedefGenerator upstreamBaseGen) {
         verify(baseGen == null, "Attempted to replace base %s with %s in %s", baseGen, upstreamBaseGen, this);
-        final List<AbstractTypeObjectGenerator<?>> downstreams = verifyNotNull(inferred,
+        final List<AbstractTypeObjectGenerator<?, ?>> downstreams = verifyNotNull(inferred,
             "Duplicated linking of %s", this);
         baseGen = verifyNotNull(upstreamBaseGen);
         baseGen.addDerivedGenerator(this);
         inferred = null;
 
-        for (AbstractTypeObjectGenerator<?> downstream : downstreams) {
+        for (AbstractTypeObjectGenerator<?, ?> downstream : downstreams) {
             downstream.linkBaseGen(upstreamBaseGen);
         }
     }
@@ -358,8 +372,11 @@ abstract class AbstractTypeObjectGenerator<T extends EffectiveStatement<?, ?>> e
                 .map(context::resolveIdentity)
                 .collect(Collectors.toUnmodifiableList()));
         } else if (TypeDefinitions.LEAFREF.equals(arg)) {
-            refType = TypeReference.leafRef(context.resolveLeafref(
-                type.findFirstEffectiveSubstatementArgument(PathEffectiveStatement.class).orElseThrow()));
+            final AbstractTypeObjectGenerator<?, ?> targetGenerator = context.resolveLeafref(
+                type.findFirstEffectiveSubstatementArgument(PathEffectiveStatement.class).orElseThrow());
+            checkArgument(targetGenerator != this, "Effective model contains self-referencing leaf %s",
+                statement().argument());
+            refType = TypeReference.leafRef(targetGenerator);
         } else if (TypeDefinitions.UNION.equals(arg)) {
             unionDependencies = new UnionDependencies(type, context);
             LOG.trace("Resolved union {} to dependencies {}", type, unionDependencies);
@@ -439,7 +456,28 @@ abstract class AbstractTypeObjectGenerator<T extends EffectiveStatement<?, ?>> e
         return methodReturnElementType(builderFactory);
     }
 
+    @Override
+    final Type runtimeJavaType() {
+        if (methodReturnTypeElement != null) {
+            return methodReturnTypeElement;
+        }
+        final var genType = generatedType();
+        if (genType.isPresent()) {
+            return genType.orElseThrow();
+        }
+        final var prev = verifyNotNull(previous(), "No previous generator for %s", this);
+        return prev.runtimeJavaType();
+    }
+
     final @NonNull Type methodReturnElementType(final @NonNull TypeBuilderFactory builderFactory) {
+        var local = methodReturnTypeElement;
+        if (local == null) {
+            methodReturnTypeElement = local = createMethodReturnElementType(builderFactory);
+        }
+        return local;
+    }
+
+    private @NonNull Type createMethodReturnElementType(final @NonNull TypeBuilderFactory builderFactory) {
         final GeneratedType generatedType = tryGeneratedType(builderFactory);
         if (generatedType != null) {
             // We have generated a type here, so return it. This covers 'bits', 'enumeration' and 'union'.
@@ -451,7 +489,7 @@ abstract class AbstractTypeObjectGenerator<T extends EffectiveStatement<?, ?>> e
             return refType.methodReturnType(builderFactory);
         }
 
-        final AbstractExplicitGenerator<?> prev = previous();
+        final AbstractExplicitGenerator<?, ?> prev = previous();
         if (prev != null) {
             // We have been added through augment/uses, defer to the original definition
             return prev.methodReturnType(builderFactory);
@@ -476,13 +514,12 @@ abstract class AbstractTypeObjectGenerator<T extends EffectiveStatement<?, ?>> e
             return baseType;
         }
 
-        if (!(baseType instanceof GeneratedTransferObject)) {
+        if (!(baseType instanceof GeneratedTransferObject gto)) {
             // This is a simple Java type, just wrap it with new restrictions
             return Types.restrictedType(baseType, restrictions);
         }
 
         // Base type is a GTO, we need to re-adjust it with new restrictions
-        final GeneratedTransferObject gto = (GeneratedTransferObject) baseType;
         final GeneratedTOBuilder builder = builderFactory.newGeneratedTOBuilder(gto.getIdentifier());
         final GeneratedTransferObject parent = gto.getSuperType();
         if (parent != null) {
@@ -509,8 +546,8 @@ abstract class AbstractTypeObjectGenerator<T extends EffectiveStatement<?, ?>> e
             return;
         }
 
-        final AbstractTypeObjectGenerator<?> prev =
-            (AbstractTypeObjectGenerator<?>) verifyNotNull(previous(), "Missing previous link in %s", this);
+        final AbstractTypeObjectGenerator<?, ?> prev =
+            (AbstractTypeObjectGenerator<?, ?>) verifyNotNull(previous(), "Missing previous link in %s", this);
         if (prev.refType instanceof ResolvedLeafref) {
             // We should be already inheriting the correct type
             return;
@@ -552,9 +589,10 @@ abstract class AbstractTypeObjectGenerator<T extends EffectiveStatement<?, ?>> e
         final boolean isTypedef = this instanceof TypedefGenerator;
         final QName arg = type.argument();
         if (TypeDefinitions.BITS.equals(arg)) {
-            return createBits(builderFactory, typeName(), currentModule(), extractTypeDefinition(), isTypedef);
+            return createBits(builderFactory, statement(), typeName(), currentModule(),
+                (BitsTypeDefinition) extractTypeDefinition(), isTypedef);
         } else if (TypeDefinitions.ENUMERATION.equals(arg)) {
-            return createEnumeration(builderFactory, typeName(), currentModule(),
+            return createEnumeration(builderFactory, statement(), typeName(), currentModule(),
                 (EnumTypeDefinition) extractTypeDefinition());
         } else if (TypeDefinitions.UNION.equals(arg)) {
             final List<GeneratedType> tmp = new ArrayList<>(1);
@@ -563,42 +601,46 @@ abstract class AbstractTypeObjectGenerator<T extends EffectiveStatement<?, ?>> e
             auxiliaryGeneratedTypes = List.copyOf(tmp);
             return ret;
         } else {
-            return createSimple(builderFactory, typeName(), currentModule(),
+            return createSimple(builderFactory, statement(), typeName(), currentModule(),
                 verifyNotNull(SIMPLE_TYPES.get(arg), "Unhandled type %s", arg), extractTypeDefinition());
         }
     }
 
     private static @NonNull GeneratedTransferObject createBits(final TypeBuilderFactory builderFactory,
-            final JavaTypeName typeName, final ModuleGenerator module, final TypeDefinition<?> typedef,
-            final boolean isTypedef) {
+            final EffectiveStatement<?, ?> definingStatement, final JavaTypeName typeName, final ModuleGenerator module,
+            final BitsTypeDefinition typedef, final boolean isTypedef) {
         final GeneratedTOBuilder builder = builderFactory.newGeneratedTOBuilder(typeName);
         builder.setTypedef(isTypedef);
-        builder.addImplementsType(BindingTypes.TYPE_OBJECT);
+        builder.addImplementsType(BindingTypes.BITS_TYPE_OBJECT);
         builder.setBaseType(typedef);
+        YangSourceDefinition.of(module.statement(), definingStatement).ifPresent(builder::setYangSourceDefinition);
 
-        for (Bit bit : ((BitsTypeDefinition) typedef).getBits()) {
+        for (Bit bit : typedef.getBits()) {
             final String name = bit.getName();
             GeneratedPropertyBuilder genPropertyBuilder = builder.addProperty(BindingMapping.getPropertyName(name));
             genPropertyBuilder.setReadOnly(true);
-            genPropertyBuilder.setReturnType(BaseYangTypes.BOOLEAN_TYPE);
+            genPropertyBuilder.setReturnType(Types.primitiveBooleanType());
 
             builder.addEqualsIdentity(genPropertyBuilder);
             builder.addHashIdentity(genPropertyBuilder);
             builder.addToStringProperty(genPropertyBuilder);
         }
+        builder.addConstant(Types.immutableSetTypeFor(Types.STRING), TypeConstants.VALID_NAMES_NAME, typedef);
 
         // builder.setSchemaPath(typedef.getPath());
         builder.setModuleName(module.statement().argument().getLocalName());
-        addCodegenInformation(typedef, builder);
+        builderFactory.addCodegenInformation(typedef, builder);
         annotateDeprecatedIfNecessary(typedef, builder);
         makeSerializable(builder);
         return builder.build();
     }
 
     private static @NonNull Enumeration createEnumeration(final TypeBuilderFactory builderFactory,
-            final JavaTypeName typeName, final ModuleGenerator module, final EnumTypeDefinition typedef) {
+            final EffectiveStatement<?, ?> definingStatement, final JavaTypeName typeName,
+            final ModuleGenerator module, final EnumTypeDefinition typedef) {
         // TODO units for typedef enum
         final AbstractEnumerationBuilder builder = builderFactory.newEnumerationBuilder(typeName);
+        YangSourceDefinition.of(module.statement(), definingStatement).ifPresent(builder::setYangSourceDefinition);
 
         typedef.getDescription().map(BindingGeneratorUtil::encodeAngleBrackets)
             .ifPresent(builder::setDescription);
@@ -610,12 +652,13 @@ abstract class AbstractTypeObjectGenerator<T extends EffectiveStatement<?, ?>> e
     }
 
     private static @NonNull GeneratedType createSimple(final TypeBuilderFactory builderFactory,
-            final JavaTypeName typeName, final ModuleGenerator module, final Type javaType,
-            final TypeDefinition<?> typedef) {
+            final EffectiveStatement<?, ?> definingStatement, final JavaTypeName typeName, final ModuleGenerator module,
+            final Type javaType, final TypeDefinition<?> typedef) {
         final String moduleName = module.statement().argument().getLocalName();
         final GeneratedTOBuilder builder = builderFactory.newGeneratedTOBuilder(typeName);
         builder.setTypedef(true);
         builder.addImplementsType(BindingTypes.scalarTypeObject(javaType));
+        YangSourceDefinition.of(module.statement(), definingStatement).ifPresent(builder::setYangSourceDefinition);
 
         final GeneratedPropertyBuilder genPropBuilder = builder.addProperty(TypeConstants.VALUE_PROP);
         genPropBuilder.setReturnType(javaType);
@@ -627,7 +670,7 @@ abstract class AbstractTypeObjectGenerator<T extends EffectiveStatement<?, ?>> e
 
 //        builder.setSchemaPath(typedef.getPath());
         builder.setModuleName(moduleName);
-        addCodegenInformation(typedef, builder);
+        builderFactory.addCodegenInformation(typedef, builder);
 
         annotateDeprecatedIfNecessary(typedef, builder);
 
@@ -647,12 +690,13 @@ abstract class AbstractTypeObjectGenerator<T extends EffectiveStatement<?, ?>> e
             final UnionDependencies dependencies, final JavaTypeName typeName, final ModuleGenerator module,
             final TypeEffectiveStatement<?> type, final boolean isTypedef, final TypeDefinition<?> typedef) {
         final GeneratedUnionBuilder builder = builderFactory.newGeneratedUnionBuilder(typeName);
+        YangSourceDefinition.of(module.statement(), definingStatement).ifPresent(builder::setYangSourceDefinition);
         builder.addImplementsType(BindingTypes.TYPE_OBJECT);
         builder.setIsUnion(true);
 
 //        builder.setSchemaPath(typedef.getPath());
         builder.setModuleName(module.statement().argument().getLocalName());
-        addCodegenInformation(definingStatement, builder);
+        builderFactory.addCodegenInformation(definingStatement, builder);
 
         annotateDeprecatedIfNecessary(definingStatement, builder);
 
@@ -666,8 +710,7 @@ abstract class AbstractTypeObjectGenerator<T extends EffectiveStatement<?, ?>> e
         final List<String> typeProperties = new ArrayList<>();
 
         for (EffectiveStatement<?, ?> stmt : type.effectiveSubstatements()) {
-            if (stmt instanceof TypeEffectiveStatement) {
-                final TypeEffectiveStatement<?> subType = (TypeEffectiveStatement<?>) stmt;
+            if (stmt instanceof TypeEffectiveStatement<?> subType) {
                 final QName subName = subType.argument();
                 final String localName = subName.getLocalName();
 
@@ -683,18 +726,22 @@ abstract class AbstractTypeObjectGenerator<T extends EffectiveStatement<?, ?>> e
                     propSource = subUnionName.simpleName();
                     generatedType = subUnion;
                 } else if (TypeDefinitions.ENUMERATION.equals(subName)) {
-                    final Enumeration subEnumeration = createEnumeration(builderFactory,
+                    final Enumeration subEnumeration = createEnumeration(builderFactory, definingStatement,
                         typeName.createEnclosed(BindingMapping.getClassName(localName), "$"), module,
                         (EnumTypeDefinition) subType.getTypeDefinition());
                     builder.addEnumeration(subEnumeration);
                     generatedType = subEnumeration;
                 } else if (TypeDefinitions.BITS.equals(subName)) {
-                    final GeneratedTransferObject subBits = createBits(builderFactory,
+                    final GeneratedTransferObject subBits = createBits(builderFactory, definingStatement,
                         typeName.createEnclosed(BindingMapping.getClassName(localName), "$"), module,
-                        subType.getTypeDefinition(), isTypedef);
+                        (BitsTypeDefinition) subType.getTypeDefinition(), isTypedef);
                     builder.addEnclosingTransferObject(subBits);
                     generatedType = subBits;
                 } else if (TypeDefinitions.IDENTITYREF.equals(subName)) {
+                    propSource = stmt.findFirstEffectiveSubstatement(BaseEffectiveStatement.class)
+                        .orElseThrow(() -> new VerifyException(String.format("Invalid identityref "
+                            + "definition %s in %s, missing BASE statement", stmt, definingStatement)))
+                        .argument().getLocalName();
                     generatedType = verifyNotNull(dependencies.identityTypes.get(stmt),
                         "Cannot resolve identityref %s in %s", stmt, definingStatement)
                         .methodReturnType(builderFactory);
@@ -706,7 +753,7 @@ abstract class AbstractTypeObjectGenerator<T extends EffectiveStatement<?, ?>> e
                     Type baseType = SIMPLE_TYPES.get(subName);
                     if (baseType == null) {
                         // This has to be a reference to a typedef, let's lookup it up and pick up its type
-                        final AbstractTypeObjectGenerator<?> baseGen = verifyNotNull(
+                        final AbstractTypeObjectGenerator<?, ?> baseGen = verifyNotNull(
                             dependencies.baseTypes.get(subName), "Cannot resolve base type %s in %s", subName,
                             definingStatement);
                         baseType = baseGen.methodReturnType(builderFactory);
@@ -781,29 +828,7 @@ abstract class AbstractTypeObjectGenerator<T extends EffectiveStatement<?, ?>> e
         addUnits(builder, typedef);
 
         makeSerializable(builder);
-        final GeneratedTransferObject ret = builder.build();
-
-        // Define a corresponding union builder. Typedefs are always anchored at a Java package root,
-        // so we are placing the builder alongside the union.
-        final GeneratedTOBuilder unionBuilder = builderFactory.newGeneratedTOBuilder(unionBuilderName(typeName));
-        unionBuilder.setIsUnionBuilder(true);
-        unionBuilder.addMethod("getDefaultInstance")
-            .setAccessModifier(AccessModifier.PUBLIC)
-            .setStatic(true)
-            .setReturnType(ret)
-            .addParameter(Types.STRING, "defaultValue");
-        auxiliaryGeneratedTypes.add(unionBuilder.build());
-
-        return ret;
-    }
-
-    // FIXME: this can be a source of conflicts as we are not guarding against nesting
-    private static @NonNull JavaTypeName unionBuilderName(final JavaTypeName unionName) {
-        final StringBuilder sb = new StringBuilder();
-        for (String part : unionName.localNameComponents()) {
-            sb.append(part);
-        }
-        return JavaTypeName.create(unionName.packageName(), sb.append("Builder").toString());
+        return builder.build();
     }
 
     // FIXME: we should not rely on TypeDefinition
@@ -834,9 +859,9 @@ abstract class AbstractTypeObjectGenerator<T extends EffectiveStatement<?, ?>> e
      * @throws IllegalArgumentException if <code>typedef</code> equals null
      */
     static Map<String, String> resolveRegExpressions(final TypeDefinition<?> typedef) {
-        return typedef instanceof StringTypeDefinition
+        return typedef instanceof StringTypeDefinition stringTypedef
             // TODO: run diff against base ?
-            ? resolveRegExpressions(((StringTypeDefinition) typedef).getPatternConstraints())
+            ? resolveRegExpressions(stringTypedef.getPatternConstraints())
                 : ImmutableMap.of();
     }
 
@@ -887,12 +912,8 @@ abstract class AbstractTypeObjectGenerator<T extends EffectiveStatement<?, ?>> e
     }
 
     private static String applyModifier(final ModifierKind modifier, final String pattern) {
-        switch (modifier) {
-            case INVERT_MATCH:
-                return RegexPatterns.negatePatternString(pattern);
-            default:
-                LOG.warn("Ignoring unhandled modifier {}", modifier);
-                return pattern;
-        }
+        return switch (modifier) {
+            case INVERT_MATCH -> RegexPatterns.negatePatternString(pattern);
+        };
     }
 }