*/
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;
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;
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;
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;
* 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<>();
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)) {
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.
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();
}
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;
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);
}
}
.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);
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'.
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);
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) {
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;
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);
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);
}
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);
// builder.setSchemaPath(typedef.getPath());
builder.setModuleName(moduleName);
- addCodegenInformation(typedef, builder);
+ builderFactory.addCodegenInformation(typedef, builder);
annotateDeprecatedIfNecessary(typedef, builder);
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);
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();
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);
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);
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
* @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();
}
}
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);
+ };
}
}