/* * Copyright (c) 2018 Pantheon Technologies, s.r.o. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ package org.opendaylight.mdsal.binding.generator.impl; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Verify.verifyNotNull; import static java.util.Objects.requireNonNull; import static org.opendaylight.mdsal.binding.model.util.BindingGeneratorUtil.computeDefaultSUID; import static org.opendaylight.mdsal.binding.model.util.BindingGeneratorUtil.packageNameForAugmentedGeneratedType; import static org.opendaylight.mdsal.binding.model.util.BindingGeneratorUtil.packageNameForGeneratedType; import static org.opendaylight.mdsal.binding.model.util.BindingTypes.BASE_IDENTITY; import static org.opendaylight.mdsal.binding.model.util.BindingTypes.DATA_OBJECT; import static org.opendaylight.mdsal.binding.model.util.BindingTypes.DATA_ROOT; import static org.opendaylight.mdsal.binding.model.util.BindingTypes.NOTIFICATION; import static org.opendaylight.mdsal.binding.model.util.BindingTypes.NOTIFICATION_LISTENER; import static org.opendaylight.mdsal.binding.model.util.BindingTypes.QNAME; import static org.opendaylight.mdsal.binding.model.util.BindingTypes.ROUTING_CONTEXT; import static org.opendaylight.mdsal.binding.model.util.BindingTypes.RPC_INPUT; import static org.opendaylight.mdsal.binding.model.util.BindingTypes.RPC_OUTPUT; import static org.opendaylight.mdsal.binding.model.util.BindingTypes.RPC_SERVICE; import static org.opendaylight.mdsal.binding.model.util.BindingTypes.action; import static org.opendaylight.mdsal.binding.model.util.BindingTypes.augmentable; import static org.opendaylight.mdsal.binding.model.util.BindingTypes.augmentation; import static org.opendaylight.mdsal.binding.model.util.BindingTypes.childOf; import static org.opendaylight.mdsal.binding.model.util.BindingTypes.choiceIn; import static org.opendaylight.mdsal.binding.model.util.BindingTypes.identifiable; import static org.opendaylight.mdsal.binding.model.util.BindingTypes.identifier; import static org.opendaylight.mdsal.binding.model.util.BindingTypes.instanceNotification; import static org.opendaylight.mdsal.binding.model.util.BindingTypes.keyedListAction; import static org.opendaylight.mdsal.binding.model.util.BindingTypes.keyedListNotification; import static org.opendaylight.mdsal.binding.model.util.BindingTypes.opaqueObject; import static org.opendaylight.mdsal.binding.model.util.BindingTypes.rpcResult; import static org.opendaylight.mdsal.binding.model.util.Types.BOOLEAN; import static org.opendaylight.mdsal.binding.model.util.Types.STRING; import static org.opendaylight.mdsal.binding.model.util.Types.classType; import static org.opendaylight.mdsal.binding.model.util.Types.listTypeFor; import static org.opendaylight.mdsal.binding.model.util.Types.listenableFutureTypeFor; import static org.opendaylight.mdsal.binding.model.util.Types.primitiveVoidType; import static org.opendaylight.mdsal.binding.model.util.Types.wildcardTypeFor; import static org.opendaylight.yangtools.yang.model.util.SchemaContextUtil.findDataSchemaNode; import static org.opendaylight.yangtools.yang.model.util.SchemaContextUtil.findNodeInSchemaContext; import static org.opendaylight.yangtools.yang.model.util.SchemaContextUtil.findParentModule; import com.google.common.base.Splitter; import com.google.common.collect.Iterables; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import java.util.AbstractMap.SimpleImmutableEntry; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import org.eclipse.jdt.annotation.Nullable; import org.opendaylight.mdsal.binding.model.api.AccessModifier; import org.opendaylight.mdsal.binding.model.api.Constant; import org.opendaylight.mdsal.binding.model.api.GeneratedTransferObject; import org.opendaylight.mdsal.binding.model.api.GeneratedType; import org.opendaylight.mdsal.binding.model.api.JavaTypeName; import org.opendaylight.mdsal.binding.model.api.ParameterizedType; import org.opendaylight.mdsal.binding.model.api.Restrictions; import org.opendaylight.mdsal.binding.model.api.Type; import org.opendaylight.mdsal.binding.model.api.type.builder.AnnotableTypeBuilder; import org.opendaylight.mdsal.binding.model.api.type.builder.AnnotationTypeBuilder; import org.opendaylight.mdsal.binding.model.api.type.builder.EnumBuilder; 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.GeneratedTypeBuilder; import org.opendaylight.mdsal.binding.model.api.type.builder.GeneratedTypeBuilderBase; import org.opendaylight.mdsal.binding.model.api.type.builder.MethodSignatureBuilder; import org.opendaylight.mdsal.binding.model.api.type.builder.TypeMemberBuilder; import org.opendaylight.mdsal.binding.model.util.BindingGeneratorUtil; import org.opendaylight.mdsal.binding.model.util.ReferencedTypeImpl; import org.opendaylight.mdsal.binding.model.util.TypeConstants; import org.opendaylight.mdsal.binding.model.util.generated.type.builder.GeneratedPropertyBuilderImpl; import org.opendaylight.mdsal.binding.spec.naming.BindingMapping; import org.opendaylight.mdsal.binding.yang.types.AbstractTypeProvider; import org.opendaylight.mdsal.binding.yang.types.BaseYangTypes; import org.opendaylight.mdsal.binding.yang.types.GroupingDefinitionDependencySort; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.common.QNameModule; import org.opendaylight.yangtools.yang.model.api.ActionDefinition; import org.opendaylight.yangtools.yang.model.api.ActionNodeContainer; import org.opendaylight.yangtools.yang.model.api.AnyDataSchemaNode; import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode; import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode; 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.DerivableSchemaNode; import org.opendaylight.yangtools.yang.model.api.DocumentedNode; 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.LeafListSchemaNode; import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode; import org.opendaylight.yangtools.yang.model.api.ListSchemaNode; import org.opendaylight.yangtools.yang.model.api.Module; import org.opendaylight.yangtools.yang.model.api.ModuleImport; import org.opendaylight.yangtools.yang.model.api.NotificationDefinition; import org.opendaylight.yangtools.yang.model.api.NotificationNodeContainer; import org.opendaylight.yangtools.yang.model.api.RpcDefinition; import org.opendaylight.yangtools.yang.model.api.SchemaContext; 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.opendaylight.yangtools.yang.model.api.UnknownSchemaNode; import org.opendaylight.yangtools.yang.model.api.UsesNode; import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition; import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition; import org.opendaylight.yangtools.yang.model.api.type.PatternConstraint; import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition; import org.opendaylight.yangtools.yang.model.util.ModuleDependencySort; import org.opendaylight.yangtools.yang.model.util.SchemaNodeUtils; import org.opendaylight.yangtools.yang.model.util.type.CompatUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; abstract class AbstractTypeGenerator { private static final Logger LOG = LoggerFactory.getLogger(AbstractTypeGenerator.class); private static final Splitter COLON_SPLITTER = Splitter.on(':'); private static final JavaTypeName DEPRECATED_ANNOTATION = JavaTypeName.create(Deprecated.class); private static final JavaTypeName OVERRIDE_ANNOTATION = JavaTypeName.create(Override.class); private static final Type LIST_STRING_TYPE = listTypeFor(BaseYangTypes.STRING_TYPE); /** * Comparator based on augment target path. */ private static final Comparator AUGMENT_COMP = (o1, o2) -> { final Iterator thisIt = o1.getTargetPath().getPathFromRoot().iterator(); final Iterator otherIt = o2.getTargetPath().getPathFromRoot().iterator(); while (thisIt.hasNext()) { if (!otherIt.hasNext()) { return 1; } final int comp = thisIt.next().compareTo(otherIt.next()); if (comp != 0) { return comp; } } return otherIt.hasNext() ? -1 : 0; }; /** * Constant with the concrete name of identifier. */ private static final String AUGMENT_IDENTIFIER_NAME = "augment-identifier"; /** * Constant with the concrete name of namespace. */ private static final String YANG_EXT_NAMESPACE = "urn:opendaylight:yang:extension:yang-ext"; private final Map genCtx = new HashMap<>(); /** * Outer key represents the package name. Outer value represents map of all builders in the same package. Inner key * represents the schema node name (in JAVA class/interface name format). Inner value represents instance of builder * for schema node specified in key part. */ private final Map> genTypeBuilders = new HashMap<>(); /** * Provide methods for converting YANG types to JAVA types. */ private final AbstractTypeProvider typeProvider; /** * Holds reference to schema context to resolve data of augmented element when creating augmentation builder. */ private final SchemaContext schemaContext; /** * Holds renamed elements. */ private final Map renames; AbstractTypeGenerator(final SchemaContext context, final AbstractTypeProvider typeProvider, final Map renames) { this.schemaContext = requireNonNull(context); this.typeProvider = requireNonNull(typeProvider); this.renames = requireNonNull(renames); final List contextModules = ModuleDependencySort.sort(schemaContext.getModules()); final List contexts = new ArrayList<>(contextModules.size()); for (final Module contextModule : contextModules) { contexts.add(moduleToGenTypes(contextModule)); } contexts.forEach(this::allAugmentsToGenTypes); } final Collection moduleContexts() { return genCtx.values(); } final ModuleContext moduleContext(final QNameModule module) { return requireNonNull(genCtx.get(module), () -> "Module context not found for module " + module); } final AbstractTypeProvider typeProvider() { return typeProvider; } abstract void addCodegenInformation(GeneratedTypeBuilderBase genType, Module module); abstract void addCodegenInformation(GeneratedTypeBuilderBase genType, Module module, SchemaNode node); abstract void addCodegenInformation(GeneratedTypeBuilder interfaceBuilder, Module module, String description, Set nodes); abstract void addComment(TypeMemberBuilder genType, DocumentedNode node); private ModuleContext moduleToGenTypes(final Module module) { final ModuleContext context = new ModuleContext(module); genCtx.put(module.getQNameModule(), context); allTypeDefinitionsToGenTypes(context); groupingsToGenTypes(context, module.getGroupings()); rpcMethodsToGenType(context); allIdentitiesToGenTypes(context); notificationsToGenType(context); if (!module.getChildNodes().isEmpty()) { final GeneratedTypeBuilder moduleType = moduleToDataType(context); context.addModuleNode(moduleType); resolveDataSchemaNodes(context, moduleType, moduleType, module.getChildNodes(), false); } return context; } /** * Converts all extended type definitions of module to the list of * Type objects. * * @param module * module from which is obtained set of type definitions * @throws IllegalArgumentException *
    *
  • if module is null
  • *
  • if name of module is null
  • *
* @throws IllegalStateException * if set of type definitions from module is null */ private void allTypeDefinitionsToGenTypes(final ModuleContext context) { final Module module = context.module(); checkArgument(module.getName() != null, "Module name cannot be NULL."); for (final TypeDefinition typedef : SchemaNodeUtils.getAllTypeDefinitions(module)) { if (typedef != null) { final Type type = typeProvider.generatedTypeForExtendedDefinitionType(typedef, typedef); if (type != null) { context.addTypedefType(typedef, type); context.addTypeToSchema(type,typedef); } } } } private GeneratedTypeBuilder processDataSchemaNode(final ModuleContext context, final Type baseInterface, final DataSchemaNode node, final boolean inGrouping) { if (node.isAugmenting() || node.isAddedByUses()) { return null; } final GeneratedTypeBuilder genType = addDefaultInterfaceDefinition(context, node, baseInterface); defaultImplementedInterace(genType); annotateDeprecatedIfNecessary(node, genType); final Module module = context.module(); genType.setModuleName(module.getName()); addCodegenInformation(genType, module, node); genType.setSchemaPath(node.getPath()); if (node instanceof DataNodeContainer) { context.addChildNodeType(node, genType); groupingsToGenTypes(context, ((DataNodeContainer) node).getGroupings()); processUsesAugments((DataNodeContainer) node, context, inGrouping); } return genType; } private void containerToGenType(final ModuleContext context, final GeneratedTypeBuilder parent, final Type baseInterface, final ContainerSchemaNode node, final boolean inGrouping) { final GeneratedTypeBuilder genType = processDataSchemaNode(context, baseInterface, node, inGrouping); if (genType != null) { constructGetter(parent, genType, node); resolveDataSchemaNodes(context, genType, genType, node.getChildNodes(), inGrouping); actionsToGenType(context, genType, node, null, inGrouping); notificationsToGenType(context, genType, node, null, inGrouping); } } private void listToGenType(final ModuleContext context, final GeneratedTypeBuilder parent, final Type baseInterface, final ListSchemaNode node, final boolean inGrouping) { final GeneratedTypeBuilder genType = processDataSchemaNode(context, baseInterface, node, inGrouping); if (genType != null) { final List listKeys = listKeys(node); final GeneratedTOBuilder keyTypeBuilder; if (!listKeys.isEmpty()) { keyTypeBuilder = typeProvider.newGeneratedTOBuilder(JavaTypeName.create( packageNameForGeneratedType(context.modulePackageName(), node.getPath()), BindingMapping.getClassName(node.getQName().getLocalName() + "Key"))) .addImplementsType(identifier(genType)); genType.addImplementsType(identifiable(keyTypeBuilder)); } else { keyTypeBuilder = null; } final ParameterizedType listType = listTypeFor(genType); constructGetter(parent, listType, node); constructNonnull(parent, listType, node); actionsToGenType(context, genType, node, keyTypeBuilder, inGrouping); notificationsToGenType(context, genType, node, keyTypeBuilder, inGrouping); for (final DataSchemaNode schemaNode : node.getChildNodes()) { if (!schemaNode.isAugmenting()) { addSchemaNodeToListBuilders(context, schemaNode, genType, keyTypeBuilder, listKeys, inGrouping); } } // serialVersionUID if (keyTypeBuilder != null) { final GeneratedPropertyBuilder prop = new GeneratedPropertyBuilderImpl("serialVersionUID"); prop.setValue(Long.toString(computeDefaultSUID(keyTypeBuilder))); keyTypeBuilder.setSUID(prop); } typeBuildersToGenTypes(context, genType, keyTypeBuilder); } } private void processUsesAugments(final DataNodeContainer node, final ModuleContext context, final boolean inGrouping) { for (final UsesNode usesNode : node.getUses()) { for (final AugmentationSchemaNode augment : usesNode.getAugmentations()) { usesAugmentationToGenTypes(context, augment, usesNode, node, inGrouping); processUsesAugments(augment, context, inGrouping); } } } /** * Converts all augmentation of the module to the list * Type objects. * * @param module * module from which is obtained list of all augmentation objects * to iterate over them * @throws IllegalArgumentException *
    *
  • if the module is null
  • *
  • if the name of module is null
  • *
* @throws IllegalStateException * if set of augmentations from module is null */ private void allAugmentsToGenTypes(final ModuleContext context) { final Module module = context.module(); checkArgument(module != null, "Module reference cannot be NULL."); checkArgument(module.getName() != null, "Module name cannot be NULL."); checkState(module.getAugmentations() != null, "Augmentations Set cannot be NULL."); for (final AugmentationSchemaNode augment : resolveAugmentations(module)) { augmentationToGenTypes(context, augment); } } /** * Returns list of AugmentationSchema objects. The objects are * sorted according to the length of their target path from the shortest to * the longest. * * @param module * module from which is obtained list of all augmentation objects * @return list of sorted AugmentationSchema objects obtained * from module * @throws IllegalArgumentException * if module is null * @throws IllegalStateException * if set of module augmentations is null */ private static List resolveAugmentations(final Module module) { checkArgument(module != null, "Module reference cannot be NULL."); checkState(module.getAugmentations() != null, "Augmentations Set cannot be NULL."); final Set augmentations = module.getAugmentations(); final List sortedAugmentations = new ArrayList<>(augmentations); sortedAugmentations.sort(AUGMENT_COMP); return sortedAugmentations; } /** * Create GeneratedTypeBuilder object from module argument. * * @param module * Module object from which builder will be created * @return GeneratedTypeBuilder which is internal * representation of the module * @throws IllegalArgumentException * if module is null */ private GeneratedTypeBuilder moduleToDataType(final ModuleContext context) { final GeneratedTypeBuilder moduleDataTypeBuilder = moduleTypeBuilder(context, "Data"); final Module module = context.module(); addImplementedInterfaceFromUses(module, moduleDataTypeBuilder); moduleDataTypeBuilder.addImplementsType(DATA_ROOT); addCodegenInformation(moduleDataTypeBuilder, module); return moduleDataTypeBuilder; } private void actionsToGenType(final ModuleContext context, final Type parent, final T parentSchema, final Type keyType, final boolean inGrouping) { for (final ActionDefinition action : parentSchema.getActions()) { if (action.isAugmenting()) { continue; } final GeneratedType input; final GeneratedType output; if (action.isAddedByUses()) { final ActionDefinition orig = findOrigAction(parentSchema, action).get(); // Original definition may live in a different module, make sure we account for that final ModuleContext origContext = moduleContext( orig.getPath().getPathFromRoot().iterator().next().getModule()); input = origContext.getChildNode(orig.getInput().getPath()).build(); output = origContext.getChildNode(orig.getOutput().getPath()).build(); } else { input = actionContainer(context, RPC_INPUT, action.getInput(), inGrouping); output = actionContainer(context, RPC_OUTPUT, action.getOutput(), inGrouping); } if (!(parentSchema instanceof GroupingDefinition)) { // Parent is a non-grouping, hence we need to establish an Action instance, which can be completely // identified by an InstanceIdentifier. We do not generate Actions for groupings as they are inexact, // and do not capture an actual instantiation. final QName qname = action.getQName(); final GeneratedTypeBuilder builder = typeProvider.newGeneratedTypeBuilder(JavaTypeName.create( packageNameForGeneratedType(context.modulePackageName(), action.getPath()), BindingMapping.getClassName(qname))); qnameConstant(builder, JavaTypeName.create(context.modulePackageName(), BindingMapping.MODULE_INFO_CLASS_NAME), qname.getLocalName()); annotateDeprecatedIfNecessary(action, builder); builder.addImplementsType(keyType != null ? keyedListAction(parent, keyType, input, output) : action(parent, input, output)); addCodegenInformation(builder, context.module(), action); context.addChildNodeType(action, builder); } } } private Optional findOrigAction(final DataNodeContainer parent, final ActionDefinition action) { final QName qname = action.getQName(); for (UsesNode uses : parent.getUses()) { final GroupingDefinition grp = findUsedGrouping(uses); // Target grouping may reside in a different module, hence we need to rebind the QName to match grouping's // namespace final Optional found = grp.findAction(qname.withModule(grp.getQName().getModule())); if (found.isPresent()) { final ActionDefinition result = found.get(); return result.isAddedByUses() ? findOrigAction(grp, result) : found; } } return Optional.empty(); } private GeneratedType actionContainer(final ModuleContext context, final Type baseInterface, final ContainerSchemaNode schema, final boolean inGrouping) { final GeneratedTypeBuilder genType = processDataSchemaNode(context, baseInterface, schema, inGrouping); resolveDataSchemaNodes(context, genType, genType, schema.getChildNodes(), inGrouping); return genType.build(); } /** * Converts all RPCs input and output substatements of the module * to the list of Type objects. In addition are to containers * and lists which belong to input or output also part of returning list. * * @param module * module from which is obtained set of all rpc objects to * iterate over them * @throws IllegalArgumentException *
    *
  • if the module is null
  • *
  • if the name of module is null
  • *
* @throws IllegalStateException * if set of rpcs from module is null */ private void rpcMethodsToGenType(final ModuleContext context) { final Module module = context.module(); checkArgument(module.getName() != null, "Module name cannot be NULL."); final Set rpcDefinitions = module.getRpcs(); checkState(rpcDefinitions != null, "Set of rpcs from module " + module.getName() + " cannot be NULL."); if (rpcDefinitions.isEmpty()) { return; } final GeneratedTypeBuilder interfaceBuilder = moduleTypeBuilder(context, "Service"); interfaceBuilder.addImplementsType(RPC_SERVICE); addCodegenInformation(interfaceBuilder, module, "RPCs", rpcDefinitions); for (final RpcDefinition rpc : rpcDefinitions) { if (rpc != null) { final String rpcName = BindingMapping.getClassName(rpc.getQName()); final String rpcMethodName = BindingMapping.getRpcMethodName(rpc.getQName()); final MethodSignatureBuilder method = interfaceBuilder.addMethod(rpcMethodName); // Do not refer to annotation class, as it may not be available at runtime method.addAnnotation("edu.umd.cs.findbugs.annotations", "CheckReturnValue"); addComment(method, rpc); method.addParameter( createRpcContainer(context, rpcName, rpc, verifyNotNull(rpc.getInput()), RPC_INPUT), "input"); method.setReturnType(listenableFutureTypeFor( rpcResult(createRpcContainer(context, rpcName, rpc, verifyNotNull(rpc.getOutput()), RPC_OUTPUT)))); } } context.addTopLevelNodeType(interfaceBuilder); } private Type createRpcContainer(final ModuleContext context, final String rpcName, final RpcDefinition rpc, final ContainerSchemaNode schema, final Type type) { processUsesAugments(schema, context, false); final GeneratedTypeBuilder outType = addRawInterfaceDefinition( JavaTypeName.create(context.modulePackageName(), rpcName + BindingMapping.getClassName(schema.getQName())), schema); addImplementedInterfaceFromUses(schema, outType); outType.addImplementsType(type); outType.addImplementsType(augmentable(outType)); defaultImplementedInterace(outType); annotateDeprecatedIfNecessary(rpc, outType); resolveDataSchemaNodes(context, outType, outType, schema.getChildNodes(), false); context.addChildNodeType(schema, outType); return outType.build(); } /** * Converts all notifications of the module to the list of * Type objects. In addition are to this list added containers * and lists which are part of this notification. * * @param module * module from which is obtained set of all notification objects * to iterate over them * @throws IllegalArgumentException *
    *
  • if the module equals null
  • *
  • if the name of module equals null
  • *
* @throws IllegalStateException * if set of notifications from module is null */ private void notificationsToGenType(final ModuleContext context) { final Module module = context.module(); checkArgument(module.getName() != null, "Module name cannot be NULL."); final Set notifications = module.getNotifications(); if (notifications.isEmpty()) { return; } final GeneratedTypeBuilder listenerInterface = moduleTypeBuilder(context, "Listener"); listenerInterface.addImplementsType(NOTIFICATION_LISTENER); for (final NotificationDefinition notification : notifications) { if (notification != null) { processUsesAugments(notification, context, false); final GeneratedTypeBuilder notificationInterface = addDefaultInterfaceDefinition( context.modulePackageName(), notification, DATA_OBJECT, context); defaultImplementedInterace(notificationInterface); annotateDeprecatedIfNecessary(notification, notificationInterface); notificationInterface.addImplementsType(NOTIFICATION); context.addChildNodeType(notification, notificationInterface); // Notification object resolveDataSchemaNodes(context, notificationInterface, notificationInterface, notification.getChildNodes(), false); addComment(listenerInterface.addMethod("on" + notificationInterface.getName()) .setAccessModifier(AccessModifier.PUBLIC).addParameter(notificationInterface, "notification") .setReturnType(primitiveVoidType()), notification); } } addCodegenInformation(listenerInterface, module, "notifications", notifications); context.addTopLevelNodeType(listenerInterface); } private void notificationsToGenType( final ModuleContext context, final Type parent, final T parentSchema, final Type keyType, final boolean inGrouping) { final Set notifications = parentSchema.getNotifications(); if (notifications.isEmpty()) { return; } for (NotificationDefinition notif : notifications) { if (notif.isAugmenting()) { continue; } if (parentSchema instanceof GroupingDefinition) { // Notifications cannot be really established, as they lack instantiation context, which would be // completely described by an InstanceIdentifier -- hence we cannot create a binding class continue; } processUsesAugments(notif, context, false); final GeneratedTypeBuilder notifInterface = addDefaultInterfaceDefinition( packageNameForGeneratedType(context.modulePackageName(), notif.getPath()), notif, DATA_OBJECT, context); defaultImplementedInterace(notifInterface); annotateDeprecatedIfNecessary(notif, notifInterface); notifInterface.addImplementsType(keyType != null ? keyedListNotification(notifInterface, parent, keyType) : instanceNotification(notifInterface, parent)); context.addChildNodeType(notif, notifInterface); // Notification object resolveDataSchemaNodes(context, notifInterface, notifInterface, notif.getChildNodes(), false); } } /** * Converts all identities of the module to the list of * Type objects. * * @param module * module from which is obtained set of all identity objects to * iterate over them * @param schemaContext * schema context only used as input parameter for method * {@link BindingGeneratorImpl#identityToGenType} * */ private void allIdentitiesToGenTypes(final ModuleContext context) { final Set schemaIdentities = context.module().getIdentities(); if (schemaIdentities != null && !schemaIdentities.isEmpty()) { for (final IdentitySchemaNode identity : schemaIdentities) { identityToGenType(context, identity); } } } /** * Converts the identity object to GeneratedType. Firstly it is * created transport object builder. If identity contains base identity then * reference to base identity is added to superior identity as its extend. * If identity doesn't contain base identity then only reference to abstract * class {@link org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode * BaseIdentity} is added * * @param module * current module * @param basePackageName * string contains the module package name * @param identity * IdentitySchemaNode which contains data about identity */ private void identityToGenType(final ModuleContext context,final IdentitySchemaNode identity) { if (identity == null) { return; } JavaTypeName name = renames.get(identity); if (name == null) { name = JavaTypeName.create(packageNameForGeneratedType(context.modulePackageName(), identity.getPath()), BindingMapping.getClassName(identity.getQName())); } final GeneratedTypeBuilder newType = typeProvider.newGeneratedTypeBuilder(name); final Set baseIdentities = identity.getBaseIdentities(); if (!baseIdentities.isEmpty()) { for (IdentitySchemaNode baseIdentity : baseIdentities) { JavaTypeName base = renames.get(baseIdentity); if (base == null) { final QName qname = baseIdentity.getQName(); base = JavaTypeName.create(BindingMapping.getRootPackageName(qname.getModule()), BindingMapping.getClassName(qname)); } final GeneratedTransferObject gto = typeProvider.newGeneratedTOBuilder(base).build(); newType.addImplementsType(gto); } } else { newType.addImplementsType(BASE_IDENTITY); } final Module module = context.module(); addCodegenInformation(newType, module, identity); newType.setModuleName(module.getName()); newType.setSchemaPath(identity.getPath()); qnameConstant(newType, JavaTypeName.create(context.modulePackageName(), BindingMapping.MODULE_INFO_CLASS_NAME), identity.getQName().getLocalName()); context.addIdentityType(identity, newType); } private static Constant qnameConstant(final GeneratedTypeBuilderBase toBuilder, final JavaTypeName yangModuleInfo, final String localName) { return toBuilder.addConstant(QNAME, BindingMapping.QNAME_STATIC_FIELD_NAME, new SimpleImmutableEntry<>(yangModuleInfo, localName)); } /** * Converts all groupings of the module to the list of * Type objects. Firstly are groupings sorted according mutual * dependencies. At least dependent (independent) groupings are in the list * saved at first positions. For every grouping the record is added to map * {@link ModuleContext#groupings allGroupings} * * @param module * current module * @param groupings * collection of groupings from which types will be generated * */ private void groupingsToGenTypes(final ModuleContext context, final Collection groupings) { for (final GroupingDefinition grouping : new GroupingDefinitionDependencySort().sort(groupings)) { // Converts individual grouping to GeneratedType. Firstly generated type builder is created and every child // node of grouping is resolved to the method. final GeneratedTypeBuilder genType = addDefaultInterfaceDefinition(context, grouping); narrowImplementedInterface(genType); annotateDeprecatedIfNecessary(grouping, genType); context.addGroupingType(grouping, genType); resolveDataSchemaNodes(context, genType, genType, grouping.getChildNodes(), true); groupingsToGenTypes(context, grouping.getGroupings()); processUsesAugments(grouping, context, true); actionsToGenType(context, genType, grouping, null, true); notificationsToGenType(context, genType, grouping, null, true); } } /** * Adds enumeration builder created from enumTypeDef to typeBuilder. Each * enumTypeDef item is added to builder with its name and value. * * @param enumTypeDef EnumTypeDefinition contains enum data * @param enumName string contains name which will be assigned to enumeration builder * @param typeBuilder GeneratedTypeBuilder to which will be enum builder assigned * @param module Module in which type should be generated * @return enumeration builder which contains data from enumTypeDef */ private EnumBuilder resolveInnerEnumFromTypeDefinition(final EnumTypeDefinition enumTypeDef, final QName enumName, final GeneratedTypeBuilder typeBuilder, final ModuleContext context) { if (enumTypeDef != null && typeBuilder != null && enumTypeDef.getQName().getLocalName() != null) { final EnumBuilder enumBuilder = typeBuilder.addEnumeration(BindingMapping.getClassName(enumName)); typeProvider.addEnumDescription(enumBuilder, enumTypeDef); enumBuilder.updateEnumPairsFromEnumTypeDef(enumTypeDef); context.addInnerTypedefType(enumTypeDef.getPath(), enumBuilder); return enumBuilder; } return null; } /** * Generates type builder for module. * * @param module Module which is source of package name for generated type builder * @param postfix string which is added to the module class name representation as suffix * @return instance of GeneratedTypeBuilder which represents module. * @throws IllegalArgumentException if module is null */ private GeneratedTypeBuilder moduleTypeBuilder(final ModuleContext context, final String postfix) { final Module module = context.module(); final String moduleName = BindingMapping.getClassName(module.getName()) + postfix; final GeneratedTypeBuilder moduleBuilder = typeProvider.newGeneratedTypeBuilder( JavaTypeName.create(context.modulePackageName(), moduleName)); moduleBuilder.setModuleName(moduleName); addCodegenInformation(moduleBuilder, module); return moduleBuilder; } /** * Converts augSchema to list of Type which contains generated type for augmentation. * In addition there are also generated types for all containers, list and choices which are child of * augSchema node or a generated types for cases are added if augmented node is choice. * * @param augmentPackageName string with the name of the package to which the augmentation belongs * @param augSchema AugmentationSchema which is contains data about augmentation (target path, childs...) * @param module current module * @throws IllegalArgumentException *
    *
  • if augmentPackageName equals null
  • *
  • if augSchema equals null
  • *
* @throws IllegalStateException * if augment target path is null */ private void augmentationToGenTypes(final ModuleContext context, final AugmentationSchemaNode augSchema) { checkArgument(augSchema != null, "Augmentation Schema cannot be NULL."); checkState(augSchema.getTargetPath() != null, "Augmentation Schema does not contain Target Path (Target Path is NULL)."); processUsesAugments(augSchema, context, false); final SchemaPath targetPath = augSchema.getTargetPath(); SchemaNode targetSchemaNode = null; targetSchemaNode = findDataSchemaNode(schemaContext, targetPath); if (targetSchemaNode instanceof DataSchemaNode && ((DataSchemaNode) targetSchemaNode).isAddedByUses()) { if (targetSchemaNode instanceof DerivableSchemaNode) { targetSchemaNode = ((DerivableSchemaNode) targetSchemaNode).getOriginal().orElse(null); } if (targetSchemaNode == null) { throw new IllegalStateException("Failed to find target node from grouping in augmentation " + augSchema + " in module " + context.module().getName()); } } if (targetSchemaNode == null) { throw new IllegalArgumentException("augment target not found: " + targetPath); } GeneratedTypeBuilder targetTypeBuilder = findChildNodeByPath(targetSchemaNode.getPath()); if (targetTypeBuilder == null) { targetTypeBuilder = findCaseByPath(targetSchemaNode.getPath()); } if (targetTypeBuilder == null) { throw new NullPointerException("Target type not yet generated: " + targetSchemaNode); } if (!(targetSchemaNode instanceof ChoiceSchemaNode)) { final Type targetType = new ReferencedTypeImpl(targetTypeBuilder.getIdentifier()); addRawAugmentGenTypeDefinition(context, targetType, augSchema, false); } else { generateTypesFromAugmentedChoiceCases(context, targetTypeBuilder.build(), (ChoiceSchemaNode) targetSchemaNode, augSchema.getChildNodes(), null, false); } } private void usesAugmentationToGenTypes(final ModuleContext context, final AugmentationSchemaNode augSchema, final UsesNode usesNode, final DataNodeContainer usesNodeParent, final boolean inGrouping) { checkArgument(augSchema != null, "Augmentation Schema cannot be NULL."); checkState(augSchema.getTargetPath() != null, "Augmentation Schema does not contain Target Path (Target Path is NULL)."); processUsesAugments(augSchema, context, inGrouping); final SchemaPath targetPath = augSchema.getTargetPath(); final SchemaNode targetSchemaNode = findOriginalTargetFromGrouping(targetPath, usesNode); if (targetSchemaNode == null) { throw new IllegalArgumentException("augment target not found: " + targetPath); } GeneratedTypeBuilder targetTypeBuilder = findChildNodeByPath(targetSchemaNode.getPath()); if (targetTypeBuilder == null) { targetTypeBuilder = findCaseByPath(targetSchemaNode.getPath()); } if (targetTypeBuilder == null) { throw new NullPointerException("Target type not yet generated: " + targetSchemaNode); } if (!(targetSchemaNode instanceof ChoiceSchemaNode)) { if (usesNodeParent instanceof SchemaNode) { addRawAugmentGenTypeDefinition(context, packageNameForAugmentedGeneratedType(context.modulePackageName(), ((SchemaNode) usesNodeParent).getPath()), targetTypeBuilder.build(), augSchema, inGrouping); } else { addRawAugmentGenTypeDefinition(context, targetTypeBuilder.build(), augSchema, inGrouping); } } else { generateTypesFromAugmentedChoiceCases(context, targetTypeBuilder.build(), (ChoiceSchemaNode) targetSchemaNode, augSchema.getChildNodes(), usesNodeParent, inGrouping); } } private GroupingDefinition findUsedGrouping(final UsesNode uses) { final SchemaNode targetGrouping = findNodeInSchemaContext(schemaContext, uses.getGroupingPath() .getPathFromRoot()); if (targetGrouping instanceof GroupingDefinition) { return (GroupingDefinition) targetGrouping; } throw new IllegalArgumentException("Failed to resolve used grouping for " + uses); } /** * Convenient method to find node added by uses statement. * * @param targetPath node path * @param parentUsesNode parent of uses node * @return node from its original location in grouping */ private DataSchemaNode findOriginalTargetFromGrouping(final SchemaPath targetPath, final UsesNode parentUsesNode) { SchemaNode result = findUsedGrouping(parentUsesNode); for (final QName node : targetPath.getPathFromRoot()) { if (result instanceof DataNodeContainer) { final QName resultNode = node.withModule(result.getQName().getModule()); result = ((DataNodeContainer) result).getDataChildByName(resultNode); } else if (result instanceof ChoiceSchemaNode) { result = findNamedCase((ChoiceSchemaNode) result, node.getLocalName()); } } if (result == null) { return null; } if (result instanceof DerivableSchemaNode) { DerivableSchemaNode castedResult = (DerivableSchemaNode) result; Optional originalNode = castedResult.getOriginal(); if (castedResult.isAddedByUses() && originalNode.isPresent()) { result = originalNode.get(); } } if (result instanceof DataSchemaNode) { DataSchemaNode resultDataSchemaNode = (DataSchemaNode) result; if (resultDataSchemaNode.isAddedByUses()) { // The original node is required, but we have only the copy of // the original node. // Maybe this indicates a bug in Yang parser. throw new IllegalStateException("Failed to generate code for augment in " + parentUsesNode); } return resultDataSchemaNode; } throw new IllegalStateException( "Target node of uses-augment statement must be DataSchemaNode. Failed to generate code for augment in " + parentUsesNode); } /** * Returns a generated type builder for an augmentation. The name of the type builder is equal to the name * of augmented node with serial number as suffix. * * @param context current module * @param augmentPackageName string with contains the package name to which the augment belongs * @param basePackageName string with the package name to which the augmented node belongs * @param targetTypeRef target type * @param augSchema augmentation schema which contains data about the child nodes and uses of augment * @return generated type builder for augment */ private GeneratedTypeBuilder addRawAugmentGenTypeDefinition(final ModuleContext context, final String augmentPackageName, final Type targetTypeRef, final AugmentationSchemaNode augSchema, final boolean inGrouping) { Map augmentBuilders = genTypeBuilders.computeIfAbsent(augmentPackageName, k -> new HashMap<>()); final String augIdentifier = getAugmentIdentifier(augSchema.getUnknownSchemaNodes()); String augTypeName; if (augIdentifier != null) { augTypeName = BindingMapping.getClassName(augIdentifier); } else { augTypeName = augGenTypeName(augmentBuilders, targetTypeRef.getName()); } final GeneratedTypeBuilder augTypeBuilder = typeProvider.newGeneratedTypeBuilder( JavaTypeName.create(augmentPackageName, augTypeName)); augTypeBuilder.addImplementsType(DATA_OBJECT); defaultImplementedInterace(augTypeBuilder); augTypeBuilder.addImplementsType(augmentation(targetTypeRef)); annotateDeprecatedIfNecessary(augSchema, augTypeBuilder); addImplementedInterfaceFromUses(augSchema, augTypeBuilder); augSchemaNodeToMethods(context, augTypeBuilder, augSchema.getChildNodes(), inGrouping); actionsToGenType(context, augTypeBuilder, augSchema, null, inGrouping); notificationsToGenType(context, augTypeBuilder, augSchema, null, inGrouping); augmentBuilders.put(augTypeName, augTypeBuilder); if (!augSchema.getChildNodes().isEmpty()) { context.addTypeToAugmentation(augTypeBuilder, augSchema); } context.addAugmentType(augTypeBuilder); return augTypeBuilder; } private GeneratedTypeBuilder addRawAugmentGenTypeDefinition(final ModuleContext context, final Type targetTypeRef, final AugmentationSchemaNode augSchema, final boolean inGrouping) { return addRawAugmentGenTypeDefinition(context, context.modulePackageName(), targetTypeRef, augSchema, inGrouping); } private static String getAugmentIdentifier(final List unknownSchemaNodes) { for (final UnknownSchemaNode unknownSchemaNode : unknownSchemaNodes) { final QName nodeType = unknownSchemaNode.getNodeType(); if (AUGMENT_IDENTIFIER_NAME.equals(nodeType.getLocalName()) && YANG_EXT_NAMESPACE.equals(nodeType.getNamespace().toString())) { return unknownSchemaNode.getNodeParameter(); } } return null; } /** * Returns first unique name for the augment generated type builder. The generated type builder name for augment * consists from name of augmented node and serial number of its augmentation. * * @param builders map of builders which were created in the package to which the augmentation belongs * @param genTypeName string with name of augmented node * @return string with unique name for augmentation builder */ private static String augGenTypeName(final Map builders, final String genTypeName) { int index = 1; if (builders != null) { while (builders.containsKey(genTypeName + index)) { index = index + 1; } } return genTypeName + index; } /** * Adds the methods to typeBuilder which represent subnodes of node for which typeBuilder * was created. The subnodes aren't mapped to the methods if they are part of grouping or augment (in this case are * already part of them). * * @param module current module * @param parent generated type builder which represents any node. The subnodes of this node are added * to the typeBuilder as methods. The subnode can be of type leaf, leaf-list, list, * container, choice. * @param childOf parent type * @param schemaNodes set of data schema nodes which are the children of the node for which * typeBuilder was created * @return generated type builder which is the same builder as input parameter. The getter methods (representing * child nodes) could be added to it. */ private GeneratedTypeBuilder resolveDataSchemaNodes(final ModuleContext context, final GeneratedTypeBuilder parent, final @Nullable Type childOf, final Iterable schemaNodes, final boolean inGrouping) { if (schemaNodes != null && parent != null) { final Type baseInterface = childOf == null ? DATA_OBJECT : childOf(childOf); for (final DataSchemaNode schemaNode : schemaNodes) { if (!schemaNode.isAugmenting() && !schemaNode.isAddedByUses()) { addSchemaNodeToBuilderAsMethod(context, schemaNode, parent, baseInterface, inGrouping); } } } return parent; } /** * Adds the methods to typeBuilder what represents subnodes of node for which typeBuilder * was created. * * @param module current module * @param typeBuilder generated type builder which represents any node. The subnodes of this node are added * to the typeBuilder as methods. The subnode can be of type leaf, leaf-list, list, * container, choice. * @param childOf parent type * @param schemaNodes set of data schema nodes which are the children of the node for which typeBuilder * was created * @return generated type builder which is the same object as the input parameter typeBuilder. * The getter method could be added to it. */ private GeneratedTypeBuilder augSchemaNodeToMethods(final ModuleContext context, final GeneratedTypeBuilder typeBuilder, final Iterable schemaNodes, final boolean inGrouping) { if (schemaNodes != null && typeBuilder != null) { final Type baseInterface = childOf(typeBuilder); for (final DataSchemaNode schemaNode : schemaNodes) { if (!schemaNode.isAugmenting()) { addSchemaNodeToBuilderAsMethod(context, schemaNode, typeBuilder, baseInterface, inGrouping); } } } return typeBuilder; } /** * Adds to typeBuilder a method which is derived from schemaNode. * * @param node data schema node which is added to typeBuilder as a method * @param typeBuilder generated type builder to which is schemaNode added as a method. * @param childOf parent type * @param module current module */ private void addSchemaNodeToBuilderAsMethod(final ModuleContext context, final DataSchemaNode node, final GeneratedTypeBuilder typeBuilder, final Type baseInterface, final boolean inGrouping) { if (node != null && typeBuilder != null) { if (node instanceof LeafSchemaNode) { resolveLeafSchemaNodeAsMethod(typeBuilder, (LeafSchemaNode) node, context, inGrouping); } else if (node instanceof LeafListSchemaNode) { resolveLeafListSchemaNode(typeBuilder, (LeafListSchemaNode) node, context, inGrouping); } else if (node instanceof ContainerSchemaNode) { containerToGenType(context, typeBuilder, baseInterface, (ContainerSchemaNode) node, inGrouping); } else if (node instanceof ListSchemaNode) { listToGenType(context, typeBuilder, baseInterface, (ListSchemaNode) node, inGrouping); } else if (node instanceof ChoiceSchemaNode) { choiceToGeneratedType(context, typeBuilder, (ChoiceSchemaNode) node, inGrouping); } else if (node instanceof AnyXmlSchemaNode || node instanceof AnyDataSchemaNode) { opaqueToGeneratedType(context, typeBuilder, node); } else { LOG.debug("Unable to add schema node {} as method in {}: unsupported type of node.", node.getClass(), typeBuilder.getFullyQualifiedName()); } } } /** * Converts choiceNode to the list of generated types for choice and its cases. The package names * for choice and for its cases are created as concatenation of the module package (basePackageName) * and names of all parents node. * * @param context current module * @param basePackageName string with the module package name * @param parent parent type * @param choiceNode choice node which is mapped to generated type. Also child nodes - cases are mapped to generated * types. * @throws IllegalArgumentException *
    *
  • if basePackageName is null
  • *
  • if choiceNode is null
  • *
*/ private void choiceToGeneratedType(final ModuleContext context, final GeneratedTypeBuilder parent, final ChoiceSchemaNode choiceNode, final boolean inGrouping) { if (!choiceNode.isAddedByUses()) { final GeneratedTypeBuilder choiceTypeBuilder = addRawInterfaceDefinition( JavaTypeName.create(packageNameForGeneratedType(context.modulePackageName(), choiceNode.getPath()), BindingMapping.getClassName(choiceNode.getQName())), choiceNode); choiceTypeBuilder.addImplementsType(choiceIn(parent)); annotateDeprecatedIfNecessary(choiceNode, choiceTypeBuilder); context.addChildNodeType(choiceNode, choiceTypeBuilder); final GeneratedType choiceType = choiceTypeBuilder.build(); generateTypesFromChoiceCases(context, choiceType, choiceNode, inGrouping); constructGetter(parent, choiceType, choiceNode); } } private void opaqueToGeneratedType(final ModuleContext context, final GeneratedTypeBuilder parent, final DataSchemaNode anyNode) { if (!anyNode.isAddedByUses()) { final GeneratedTypeBuilder anyxmlTypeBuilder = addRawInterfaceDefinition( JavaTypeName.create(packageNameForGeneratedType(context.modulePackageName(), anyNode.getPath()), BindingMapping.getClassName(anyNode.getQName())), anyNode); anyxmlTypeBuilder.addImplementsType(opaqueObject(anyxmlTypeBuilder)).addImplementsType(childOf(parent)); defaultImplementedInterace(anyxmlTypeBuilder); annotateDeprecatedIfNecessary(anyNode, anyxmlTypeBuilder); context.addChildNodeType(anyNode, anyxmlTypeBuilder); constructGetter(parent, anyxmlTypeBuilder.build(), anyNode); } } /** * Converts caseNodes set to list of corresponding generated types. For every case which is not * added through augment or uses is created generated type builder. The package names for the builder is * created as concatenation of the module package and names of all parents nodes of the concrete case. There * is also relation "implements type" between every case builder and choice type * * @param context current module context * @param refChoiceType type which represents superior case * @param choiceNode choice case node which is mapped to generated type * @throws IllegalArgumentException *
    *
  • if refChoiceType equals null
  • *
  • if caseNodes equals null
  • *
*/ private void generateTypesFromChoiceCases(final ModuleContext context, final Type refChoiceType, final ChoiceSchemaNode choiceNode, final boolean inGrouping) { checkArgument(refChoiceType != null, "Referenced Choice Type cannot be NULL."); checkArgument(choiceNode != null, "ChoiceNode cannot be NULL."); for (final CaseSchemaNode caseNode : choiceNode.getCases().values()) { if (caseNode != null && !caseNode.isAddedByUses() && !caseNode.isAugmenting()) { final GeneratedTypeBuilder caseTypeBuilder = addDefaultInterfaceDefinition(context, caseNode); caseTypeBuilder.addImplementsType(refChoiceType); defaultImplementedInterace(caseTypeBuilder); annotateDeprecatedIfNecessary(caseNode, caseTypeBuilder); context.addCaseType(caseNode.getPath(), caseTypeBuilder); context.addChoiceToCaseMapping(refChoiceType, caseTypeBuilder, caseNode); final Iterable caseChildNodes = caseNode.getChildNodes(); if (caseChildNodes != null) { final SchemaPath choiceNodeParentPath = choiceNode.getPath().getParent(); if (!Iterables.isEmpty(choiceNodeParentPath.getPathFromRoot())) { SchemaNode parent = findDataSchemaNode(schemaContext, choiceNodeParentPath); if (parent instanceof AugmentationSchemaNode) { final AugmentationSchemaNode augSchema = (AugmentationSchemaNode) parent; final SchemaPath targetPath = augSchema.getTargetPath(); SchemaNode targetSchemaNode = findDataSchemaNode(schemaContext, targetPath); if (targetSchemaNode instanceof DataSchemaNode && ((DataSchemaNode) targetSchemaNode).isAddedByUses()) { if (targetSchemaNode instanceof DerivableSchemaNode) { targetSchemaNode = ((DerivableSchemaNode) targetSchemaNode).getOriginal() .orElse(null); } if (targetSchemaNode == null) { throw new IllegalStateException( "Failed to find target node from grouping for augmentation " + augSchema + " in module " + context.module().getName()); } } parent = targetSchemaNode; } checkState(parent != null, "Could not find Choice node parent %s", choiceNodeParentPath); Type childOfType = findChildNodeByPath(parent.getPath()); if (childOfType == null) { childOfType = findGroupingByPath(parent.getPath()); } resolveDataSchemaNodes(context, caseTypeBuilder, childOfType, caseChildNodes, inGrouping); } else { resolveDataSchemaNodes(context, caseTypeBuilder, moduleToDataType(context), caseChildNodes, inGrouping); } } } processUsesAugments(caseNode, context, inGrouping); } } /** * Generates list of generated types for all the cases of a choice which are added to the choice through * the augment. * * @param module current module * @param basePackageName string contains name of package to which augment belongs. If an augmented choice is * from an other package (pcg1) than an augmenting choice (pcg2) then case's * of the augmenting choice will belong to pcg2. * @param targetType Type which represents target choice * @param targetNode node which represents target choice * @param augmentedNodes set of choice case nodes for which is checked if are/are not added to choice through * augmentation * @throws IllegalArgumentException *
    *
  • if basePackageName is null
  • *
  • if targetType is null
  • *
  • if augmentedNodes is null
  • *
*/ // FIXME: nullness rules need to untangled in this method @SuppressFBWarnings("NP_NULL_ON_SOME_PATH") private void generateTypesFromAugmentedChoiceCases(final ModuleContext context, final Type targetType, final ChoiceSchemaNode targetNode, final Iterable augmentedNodes, final DataNodeContainer usesNodeParent, final boolean inGrouping) { checkArgument(targetType != null, "Referenced Choice Type cannot be NULL."); checkArgument(augmentedNodes != null, "Set of Choice Case Nodes cannot be NULL."); for (final DataSchemaNode caseNode : augmentedNodes) { if (caseNode != null) { final GeneratedTypeBuilder caseTypeBuilder = addDefaultInterfaceDefinition(context, caseNode); caseTypeBuilder.addImplementsType(targetType); defaultImplementedInterace(caseTypeBuilder); CaseSchemaNode node = null; final String caseLocalName = caseNode.getQName().getLocalName(); if (caseNode instanceof CaseSchemaNode) { node = (CaseSchemaNode) caseNode; } else if (findNamedCase(targetNode, caseLocalName) == null) { final String targetNodeLocalName = targetNode.getQName().getLocalName(); for (DataSchemaNode dataSchemaNode : usesNodeParent.getChildNodes()) { if (dataSchemaNode instanceof ChoiceSchemaNode && targetNodeLocalName.equals(dataSchemaNode.getQName().getLocalName())) { node = findNamedCase((ChoiceSchemaNode) dataSchemaNode, caseLocalName); break; } } } else { node = findNamedCase(targetNode, caseLocalName); } final Iterable childNodes = node.getChildNodes(); if (childNodes != null) { resolveDataSchemaNodes(context, caseTypeBuilder, findChildOfType(targetNode), childNodes, inGrouping); } context.addCaseType(caseNode.getPath(), caseTypeBuilder); context.addChoiceToCaseMapping(targetType, caseTypeBuilder, node); } } } private GeneratedTypeBuilder findChildOfType(final ChoiceSchemaNode targetNode) { final SchemaPath nodePath = targetNode.getPath(); final SchemaPath parentSp = nodePath.getParent(); if (parentSp.getParent() == null) { return moduleContext(nodePath.getLastComponent().getModule()).getModuleNode(); } final SchemaNode parent = findDataSchemaNode(schemaContext, parentSp); GeneratedTypeBuilder childOfType = null; if (parent instanceof CaseSchemaNode) { childOfType = findCaseByPath(parent.getPath()); } else if (parent instanceof DataSchemaNode || parent instanceof NotificationDefinition) { childOfType = findChildNodeByPath(parent.getPath()); } else if (parent instanceof GroupingDefinition) { childOfType = findGroupingByPath(parent.getPath()); } if (childOfType == null) { throw new IllegalArgumentException("Failed to find parent type of choice " + targetNode); } return childOfType; } private static CaseSchemaNode findNamedCase(final ChoiceSchemaNode choice, final String caseName) { final List cases = choice.findCaseNodes(caseName); return cases.isEmpty() ? null : cases.get(0); } private static boolean isInnerType(final LeafSchemaNode leaf, final TypeDefinition type) { // New parser with encapsulated type if (leaf.getPath().equals(type.getPath())) { return true; } // Embedded type definition with new parser. Also takes care of the old parser with bits if (leaf.getPath().equals(type.getPath().getParent())) { return true; } return false; } private void addPatternConstant(final GeneratedTypeBuilder typeBuilder, final String leafName, final List patternConstraints) { if (!patternConstraints.isEmpty()) { final StringBuilder field = new StringBuilder().append(TypeConstants.PATTERN_CONSTANT_NAME).append("_") .append(BindingMapping.getPropertyName(leafName)); typeBuilder.addConstant(LIST_STRING_TYPE, field.toString(), typeProvider.resolveRegExpressions(patternConstraints)); } } /** * Converts leaf to the getter method which is added to typeBuilder. * * @param typeBuilder generated type builder to which is added getter method as leaf mapping * @param leaf leaf schema node which is mapped as getter method which is added to typeBuilder * @param module Module in which type was defined * @return boolean value *
    *
  • false - if leaf or typeBuilder are * null
  • *
  • true - in other cases
  • *
*/ private Type resolveLeafSchemaNodeAsMethod(final GeneratedTypeBuilder typeBuilder, final LeafSchemaNode leaf, final ModuleContext context, final boolean inGrouping) { if (leaf == null || typeBuilder == null || leaf.isAddedByUses()) { return null; } final Module parentModule = findParentModule(schemaContext, leaf); Type returnType = null; final TypeDefinition typeDef = CompatUtils.compatType(leaf); if (isInnerType(leaf, typeDef)) { if (typeDef instanceof EnumTypeDefinition) { returnType = typeProvider.javaTypeForSchemaDefinitionType(typeDef, leaf, inGrouping); final EnumTypeDefinition enumTypeDef = (EnumTypeDefinition) typeDef; final EnumBuilder enumBuilder = resolveInnerEnumFromTypeDefinition(enumTypeDef, leaf.getQName(), typeBuilder, context); if (enumBuilder != null) { returnType = enumBuilder.toInstance(typeBuilder); } typeProvider.putReferencedType(leaf.getPath(), returnType); } else if (typeDef instanceof UnionTypeDefinition) { final UnionTypeDefinition unionDef = (UnionTypeDefinition)typeDef; returnType = addTOToTypeBuilder(unionDef, typeBuilder, leaf, parentModule); // Store the inner type within the union so that we can find the reference for it context.addInnerTypedefType(typeDef.getPath(), returnType); } else if (typeDef instanceof BitsTypeDefinition) { GeneratedTOBuilder genTOBuilder = addTOToTypeBuilder((BitsTypeDefinition) typeDef, typeBuilder, leaf, parentModule); if (genTOBuilder != null) { returnType = genTOBuilder.build(); } } else { // It is constrained version of already declared type (inner declared type exists, // onlyfor special cases (Enum, Union, Bits), which were already checked. // In order to get proper class we need to look up closest derived type // and apply restrictions from leaf type final Restrictions restrictions = BindingGeneratorUtil.getRestrictions(typeDef); returnType = typeProvider.javaTypeForSchemaDefinitionType(getBaseOrDeclaredType(typeDef), leaf, restrictions, inGrouping); addPatternConstant(typeBuilder, leaf.getQName().getLocalName(), restrictions.getPatternConstraints()); } } else { final Restrictions restrictions = BindingGeneratorUtil.getRestrictions(typeDef); returnType = typeProvider.javaTypeForSchemaDefinitionType(typeDef, leaf, restrictions, inGrouping); addPatternConstant(typeBuilder, leaf.getQName().getLocalName(), restrictions.getPatternConstraints()); } if (returnType == null) { return null; } if (typeDef instanceof EnumTypeDefinition) { typeProvider.putReferencedType(leaf.getPath(), returnType); } final MethodSignatureBuilder getter = constructGetter(typeBuilder, returnType, leaf); processContextRefExtension(leaf, getter, parentModule); return returnType; } private static TypeDefinition getBaseOrDeclaredType(final TypeDefinition typeDef) { // Returns DerivedType in case of new parser. final TypeDefinition baseType = typeDef.getBaseType(); return baseType != null && baseType.getBaseType() != null ? baseType : typeDef; } private void processContextRefExtension(final LeafSchemaNode leaf, final MethodSignatureBuilder getter, final Module module) { for (final UnknownSchemaNode node : leaf.getUnknownSchemaNodes()) { final QName nodeType = node.getNodeType(); if ("context-reference".equals(nodeType.getLocalName())) { final String nodeParam = node.getNodeParameter(); IdentitySchemaNode identity = null; String basePackageName = null; final Iterable splittedElement = COLON_SPLITTER.split(nodeParam); final Iterator iterator = splittedElement.iterator(); final int length = Iterables.size(splittedElement); if (length == 1) { identity = findIdentityByName(module.getIdentities(), iterator.next()); basePackageName = BindingMapping.getRootPackageName(module.getQNameModule()); } else if (length == 2) { final String prefix = iterator.next(); final Module dependentModule = findModuleFromImports(module.getImports(), prefix); if (dependentModule == null) { throw new IllegalArgumentException("Failed to process context-reference: unknown prefix " + prefix); } identity = findIdentityByName(dependentModule.getIdentities(), iterator.next()); basePackageName = BindingMapping.getRootPackageName(dependentModule.getQNameModule()); } else { throw new IllegalArgumentException("Failed to process context-reference: unknown identity " + nodeParam); } if (identity == null) { throw new IllegalArgumentException("Failed to process context-reference: unknown identity " + nodeParam); } final AnnotationTypeBuilder rc = getter.addAnnotation(ROUTING_CONTEXT); final String packageName = packageNameForGeneratedType(basePackageName, identity.getPath()); final String genTypeName = BindingMapping.getClassName(identity.getQName().getLocalName()); rc.addParameter("value", packageName + "." + genTypeName + ".class"); } } } private static IdentitySchemaNode findIdentityByName(final Set identities, final String name) { for (final IdentitySchemaNode id : identities) { if (id.getQName().getLocalName().equals(name)) { return id; } } return null; } private Module findModuleFromImports(final Set imports, final String prefix) { for (final ModuleImport imp : imports) { if (imp.getPrefix().equals(prefix)) { return schemaContext.findModule(imp.getModuleName(), imp.getRevision()).orElse(null); } } return null; } private boolean resolveLeafSchemaNodeAsProperty(final GeneratedTOBuilder toBuilder, final LeafSchemaNode leaf, final boolean isReadOnly) { if (leaf != null && toBuilder != null) { Type returnType; final TypeDefinition typeDef = CompatUtils.compatType(leaf); if (typeDef instanceof UnionTypeDefinition) { // GeneratedType for this type definition should have be already created final ModuleContext mc = moduleContext(typeDef.getQName().getModule()); returnType = mc.getTypedefs().get(typeDef.getPath()); if (returnType == null) { // This may still be an inner type, try to find it returnType = mc.getInnerType(typeDef.getPath()); } } else if (typeDef instanceof EnumTypeDefinition && typeDef.getBaseType() == null) { // Annonymous enumeration (already generated, since it is inherited via uses). LeafSchemaNode originalLeaf = (LeafSchemaNode) SchemaNodeUtils.getRootOriginalIfPossible(leaf); QName qname = originalLeaf.getQName(); returnType = moduleContext(qname.getModule()).getInnerType(originalLeaf.getType().getPath()); } else { returnType = typeProvider.javaTypeForSchemaDefinitionType(typeDef, leaf); } return resolveLeafSchemaNodeAsProperty(toBuilder, leaf, returnType, isReadOnly); } return false; } /** * Converts leaf schema node to property of generated TO builder. * * @param toBuilder generated TO builder to which is leaf added as property * @param leaf leaf schema node which is added to toBuilder as property * @param returnType property type * @param isReadOnly boolean value which says if leaf property is|isn't read only * @return boolean value *
    *
  • false - if leaf, toBuilder or leaf * name equals null or if leaf is added by uses.
  • *
  • true - other cases
  • *
*/ private boolean resolveLeafSchemaNodeAsProperty(final GeneratedTOBuilder toBuilder, final LeafSchemaNode leaf, final Type returnType, final boolean isReadOnly) { if (returnType == null) { return false; } final String leafName = leaf.getQName().getLocalName(); final GeneratedPropertyBuilder propBuilder = toBuilder.addProperty(BindingMapping.getPropertyName(leafName)); propBuilder.setReadOnly(isReadOnly); propBuilder.setReturnType(returnType); addComment(propBuilder, leaf); toBuilder.addEqualsIdentity(propBuilder); toBuilder.addHashIdentity(propBuilder); toBuilder.addToStringProperty(propBuilder); return true; } /** * Converts node leaf list schema node to getter method of typeBuilder. * * @param typeBuilder generated type builder to which is node added as getter method * @param node leaf list schema node which is added to typeBuilder as getter method * @param module module * @return boolean value *
    *
  • true - if node, typeBuilder, * nodeName equal null or node is added by uses
  • *
  • false - other cases
  • *
*/ private boolean resolveLeafListSchemaNode(final GeneratedTypeBuilder typeBuilder, final LeafListSchemaNode node, final ModuleContext context, final boolean inGrouping) { if (node == null || typeBuilder == null || node.isAddedByUses()) { return false; } final QName nodeName = node.getQName(); final TypeDefinition typeDef = node.getType(); final Module parentModule = findParentModule(schemaContext, node); Type returnType = null; if (typeDef.getBaseType() == null) { if (typeDef instanceof EnumTypeDefinition) { returnType = typeProvider.javaTypeForSchemaDefinitionType(typeDef, node, inGrouping); final EnumTypeDefinition enumTypeDef = (EnumTypeDefinition) typeDef; final EnumBuilder enumBuilder = resolveInnerEnumFromTypeDefinition(enumTypeDef, nodeName, typeBuilder, context); returnType = new ReferencedTypeImpl(enumBuilder.getIdentifier()); typeProvider.putReferencedType(node.getPath(), returnType); } else if (typeDef instanceof UnionTypeDefinition) { final UnionTypeDefinition unionDef = (UnionTypeDefinition)typeDef; returnType = addTOToTypeBuilder(unionDef, typeBuilder, node, parentModule); } else if (typeDef instanceof BitsTypeDefinition) { final GeneratedTOBuilder genTOBuilder = addTOToTypeBuilder((BitsTypeDefinition)typeDef, typeBuilder, node, parentModule); returnType = genTOBuilder.build(); } else { final Restrictions restrictions = BindingGeneratorUtil.getRestrictions(typeDef); returnType = typeProvider.javaTypeForSchemaDefinitionType(typeDef, node, restrictions, inGrouping); addPatternConstant(typeBuilder, node.getQName().getLocalName(), restrictions.getPatternConstraints()); } } else { final Restrictions restrictions = BindingGeneratorUtil.getRestrictions(typeDef); returnType = typeProvider.javaTypeForSchemaDefinitionType(typeDef, node, restrictions, inGrouping); addPatternConstant(typeBuilder, node.getQName().getLocalName(), restrictions.getPatternConstraints()); } constructGetter(typeBuilder, listTypeFor(returnType), node); return true; } private Type createReturnTypeForUnion(final GeneratedTOBuilder genTOBuilder, final UnionTypeDefinition typeDef, final GeneratedTypeBuilder typeBuilder, final Module parentModule) { final GeneratedTOBuilder returnTypeBuilder = typeProvider.newGeneratedTOBuilder(genTOBuilder.getIdentifier()); returnTypeBuilder.setIsUnion(true); addCodegenInformation(returnTypeBuilder, parentModule, typeDef); returnTypeBuilder.setSchemaPath(typeDef.getPath()); returnTypeBuilder.setModuleName(parentModule.getName()); final GeneratedTransferObject returnType = returnTypeBuilder.build(); genTOBuilder.setTypedef(true); genTOBuilder.setIsUnion(true); AbstractTypeProvider.addUnitsToGenTO(genTOBuilder, typeDef.getUnits().orElse(null)); createUnionBuilder(genTOBuilder, typeBuilder, returnType, parentModule); return returnType; } private void createUnionBuilder(final GeneratedTOBuilder genTOBuilder, final GeneratedTypeBuilder typeBuilder, final GeneratedTransferObject returnType, final Module parentModule) { // Append enclosing path hierarchy without dots final StringBuilder sb = new StringBuilder(); genTOBuilder.getIdentifier().localNameComponents().forEach(sb::append); final GeneratedTOBuilder unionBuilder = typeProvider.newGeneratedTOBuilder( JavaTypeName.create(typeBuilder.getPackageName(), sb.append("Builder").toString())); unionBuilder.setIsUnionBuilder(true); final MethodSignatureBuilder method = unionBuilder.addMethod("getDefaultInstance"); method.setReturnType(returnType); method.addParameter(STRING, "defaultValue"); method.setAccessModifier(AccessModifier.PUBLIC); method.setStatic(true); final GeneratedTransferObject unionBuilderType = unionBuilder.build(); typeProvider.getAdditionalTypes().computeIfAbsent(parentModule, key -> new HashSet<>()).add(unionBuilderType); } private GeneratedTypeBuilder addDefaultInterfaceDefinition(final ModuleContext context, final SchemaNode schemaNode) { return addDefaultInterfaceDefinition(context, schemaNode, DATA_OBJECT); } private GeneratedTypeBuilder addDefaultInterfaceDefinition(final ModuleContext context, final SchemaNode schemaNode, final Type baseInterface) { final String packageName = packageNameForGeneratedType(context.modulePackageName(), schemaNode.getPath()); return addDefaultInterfaceDefinition(packageName, schemaNode, baseInterface, context); } /** * Instantiates generated type builder with packageName and schemaNode. The new builder * always implements {@link org.opendaylight.yangtools.yang.binding.DataObject DataObject}.
* If schemaNode is instance of GroupingDefinition it also implements * {@link org.opendaylight.yangtools.yang.binding.Augmentable Augmentable}.
* If schemaNode is instance of * {@link org.opendaylight.yangtools.yang.model.api.DataNodeContainer DataNodeContainer} it can also implement nodes * which are specified in uses. * * @param packageName string with the name of the package to which schemaNode belongs. * @param schemaNode schema node for which is created generated type builder * @param parent parent type (can be null) * @return generated type builder schemaNode */ private GeneratedTypeBuilder addDefaultInterfaceDefinition(final String packageName, final SchemaNode schemaNode, final Type baseInterface, final ModuleContext context) { JavaTypeName name = renames.get(schemaNode); if (name == null) { name = JavaTypeName.create(packageName, BindingMapping.getClassName(schemaNode.getQName())); } final GeneratedTypeBuilder it = addRawInterfaceDefinition(name, schemaNode); it.addImplementsType(baseInterface); if (!(schemaNode instanceof GroupingDefinition)) { it.addImplementsType(augmentable(it)); } if (schemaNode instanceof DataNodeContainer) { final DataNodeContainer containerSchema = (DataNodeContainer) schemaNode; groupingsToGenTypes(context, containerSchema.getGroupings()); addImplementedInterfaceFromUses(containerSchema, it); } return it; } /** * Returns reference to generated type builder for specified schemaNode with packageName. * Firstly the generated type builder is searched in {@link BindingGeneratorImpl#genTypeBuilders genTypeBuilders}. * If it is not found it is created and added to genTypeBuilders. * * @param packageName string with the package name to which returning generated type builder belongs * @param schemaNode schema node which provide data about the schema node name * @param prefix return type name prefix * @return generated type builder for schemaNode * @throws IllegalArgumentException *
    *
  • if schemaNode is null
  • *
  • if packageName is null
  • *
  • if QName of schema node is null
  • *
  • if schemaNode name is null
  • *
*/ private GeneratedTypeBuilder addRawInterfaceDefinition(final JavaTypeName identifier, final SchemaNode schemaNode) { checkArgument(schemaNode != null, "Data Schema Node cannot be NULL."); checkArgument(schemaNode.getQName() != null, "QName for Data Schema Node cannot be NULL."); final String schemaNodeName = schemaNode.getQName().getLocalName(); checkArgument(schemaNodeName != null, "Local Name of QName for Data Schema Node cannot be NULL."); // FIXME: Validation of name conflict final GeneratedTypeBuilder newType = typeProvider.newGeneratedTypeBuilder(identifier); final Module module = findParentModule(schemaContext, schemaNode); qnameConstant(newType, JavaTypeName.create(BindingMapping.getRootPackageName(module.getQNameModule()), BindingMapping.MODULE_INFO_CLASS_NAME), schemaNode.getQName().getLocalName()); addCodegenInformation(newType, module, schemaNode); newType.setSchemaPath(schemaNode.getPath()); newType.setModuleName(module.getName()); final String packageName = identifier.packageName(); final String simpleName = identifier.simpleName(); if (!genTypeBuilders.containsKey(packageName)) { final Map builders = new HashMap<>(); builders.put(simpleName, newType); genTypeBuilders.put(packageName, builders); } else { final Map builders = genTypeBuilders.get(packageName); if (!builders.containsKey(simpleName)) { builders.put(simpleName, newType); } } return newType; } /** * Creates the name of the getter method name from localName. * * @param localName string with the name of the getter method * @param returnType return type * @return string with the name of the getter method for methodName in JAVA method format */ public static String getterMethodName(final String localName, final Type returnType) { return BindingMapping.getGetterMethodName(localName, BOOLEAN.equals(returnType)); } /** * Created a method signature builder as part of interfaceBuilder. The method signature builder is * created for the getter method of schemaNodeName. Also comment * and returnType information are added to the builder. * * @param interfaceBuilder generated type builder for which the getter method should be created * @param returnType type which represents the return type of the getter method * @param schemaNodeName string with schema node name. The name will be the part of the getter method name. * @param comment string with comment for the getter method * @param status status from yang file, for deprecated annotation * @return method signature builder which represents the getter method of interfaceBuilder */ private MethodSignatureBuilder constructGetter(final GeneratedTypeBuilder interfaceBuilder, final Type returnType, final SchemaNode node) { final MethodSignatureBuilder getMethod = interfaceBuilder.addMethod( getterMethodName(node.getQName().getLocalName(), returnType)); getMethod.setReturnType(returnType); annotateDeprecatedIfNecessary(node, getMethod); addComment(getMethod, node); return getMethod; } private static void constructNonnull(final GeneratedTypeBuilder interfaceBuilder, final Type returnType, final ListSchemaNode node) { final MethodSignatureBuilder getMethod = interfaceBuilder.addMethod( BindingMapping.getNonnullMethodName(node.getQName().getLocalName())); getMethod.setReturnType(returnType).setDefault(true); annotateDeprecatedIfNecessary(node, getMethod); } /** * Adds schemaNode to typeBuilder as getter method or to genTOBuilder * as a property. * * @param basePackageName string contains the module package name * @param schemaNode data schema node which should be added as getter method to typeBuilder * or as a property to genTOBuilder if is part of the list key * @param typeBuilder generated type builder for the list schema node * @param genTOBuilder generated TO builder for the list keys * @param listKeys list of string which contains names of the list keys * @param module current module * @throws IllegalArgumentException *
    *
  • if schemaNode equals null
  • *
  • if typeBuilder equals null
  • *
*/ private void addSchemaNodeToListBuilders(final ModuleContext context, final DataSchemaNode schemaNode, final GeneratedTypeBuilder typeBuilder, final GeneratedTOBuilder genTOBuilder, final List listKeys, final boolean inGrouping) { checkArgument(schemaNode != null, "Data Schema Node cannot be NULL."); checkArgument(typeBuilder != null, "Generated Type Builder cannot be NULL."); if (schemaNode instanceof LeafSchemaNode) { final LeafSchemaNode leaf = (LeafSchemaNode) schemaNode; final String leafName = leaf.getQName().getLocalName(); Type type = resolveLeafSchemaNodeAsMethod(typeBuilder, leaf, context, inGrouping); if (listKeys.contains(leafName)) { if (type == null) { resolveLeafSchemaNodeAsProperty(genTOBuilder, leaf, true); } else { resolveLeafSchemaNodeAsProperty(genTOBuilder, leaf, type, true); } } } else if (!schemaNode.isAddedByUses()) { if (schemaNode instanceof LeafListSchemaNode) { resolveLeafListSchemaNode(typeBuilder, (LeafListSchemaNode) schemaNode, context, inGrouping); } else if (schemaNode instanceof ContainerSchemaNode) { containerToGenType(context, typeBuilder, childOf(typeBuilder), (ContainerSchemaNode) schemaNode, inGrouping); } else if (schemaNode instanceof ChoiceSchemaNode) { choiceToGeneratedType(context, typeBuilder, (ChoiceSchemaNode) schemaNode, inGrouping); } else if (schemaNode instanceof ListSchemaNode) { listToGenType(context, typeBuilder, childOf(typeBuilder), (ListSchemaNode) schemaNode, inGrouping); } else if (schemaNode instanceof AnyXmlSchemaNode || schemaNode instanceof AnyDataSchemaNode) { opaqueToGeneratedType(context, typeBuilder, schemaNode); } } } private static void typeBuildersToGenTypes(final ModuleContext context, final GeneratedTypeBuilder typeBuilder, final GeneratedTOBuilder genTOBuilder) { checkArgument(typeBuilder != null, "Generated Type Builder cannot be NULL."); if (genTOBuilder != null) { final GeneratedTransferObject genTO = genTOBuilder.build(); // Add Identifiable.getKey() for items typeBuilder.addMethod(BindingMapping.IDENTIFIABLE_KEY_NAME).setReturnType(genTO) .addAnnotation(OVERRIDE_ANNOTATION); context.addGeneratedTOBuilder(genTOBuilder); } } /** * Selects the names of the list keys from list and returns them as the list of the strings. * * @param list of string with names of the list keys * @return list of string which represents names of the list keys. If the list contains no keys then * an empty list is returned. */ private static List listKeys(final ListSchemaNode list) { final List keyDefinition = list.getKeyDefinition(); switch (keyDefinition.size()) { case 0: return Collections.emptyList(); case 1: return Collections.singletonList(keyDefinition.get(0).getLocalName()); default: final List listKeys = new ArrayList<>(keyDefinition.size()); for (final QName keyDef : keyDefinition) { listKeys.add(keyDef.getLocalName()); } return listKeys; } } /** * Builds a GeneratedTOBuilder for a UnionType {@link UnionTypeDefinition}. If more then one generated TO builder * is created for enclosing then all of the generated TO builders are added to typeBuilder as * enclosing transfer objects. * * @param typeDef type definition which can be of type UnionType or BitsTypeDefinition * @param typeBuilder generated type builder to which is added generated TO created from typeDef * @param leaf string with name for generated TO builder * @param parentModule parent module * @return generated TO builder for typeDef */ private Type addTOToTypeBuilder(final UnionTypeDefinition typeDef, final GeneratedTypeBuilder typeBuilder, final DataSchemaNode leaf, final Module parentModule) { final List types = typeProvider.provideGeneratedTOBuildersForUnionTypeDef( allocateNestedType(typeBuilder.getIdentifier(), leaf.getQName()), typeDef, leaf); checkState(!types.isEmpty(), "No GeneratedTOBuilder objects generated from union %s", typeDef); final List genTOBuilders = new ArrayList<>(types); final GeneratedTOBuilder resultTOBuilder = types.remove(0); types.forEach(resultTOBuilder::addEnclosingTransferObject); genTOBuilders.forEach(typeBuilder::addEnclosingTransferObject); for (GeneratedTOBuilder builder : types) { if (builder.isUnion()) { final GeneratedTransferObject type = builder.build(); createUnionBuilder(builder, typeBuilder, type, parentModule); } } return createReturnTypeForUnion(resultTOBuilder, typeDef, typeBuilder, parentModule); } /** * Builds generated TO builders for typeDef of type {@link BitsTypeDefinition} which are also added * to typeBuilder as enclosing transfer object. If more then one generated TO builder is created * for enclosing then all of the generated TO builders are added to typeBuilder as enclosing transfer * objects. * * @param typeDef type definition which can be of type UnionType or BitsTypeDefinition * @param typeBuilder generated type builder to which is added generated TO created from typeDef * @param leaf string with name for generated TO builder * @param parentModule parent module * @return generated TO builder for typeDef */ private GeneratedTOBuilder addTOToTypeBuilder(final BitsTypeDefinition typeDef, final GeneratedTypeBuilder typeBuilder, final DataSchemaNode leaf, final Module parentModule) { final GeneratedTOBuilder genTOBuilder = typeProvider.provideGeneratedTOBuilderForBitsTypeDefinition( allocateNestedType(typeBuilder.getIdentifier(), leaf.getQName()), typeDef, parentModule.getName()); typeBuilder.addEnclosingTransferObject(genTOBuilder); return genTOBuilder; } /** * Adds the implemented types to type builder. The method passes through the list of uses in * {@code dataNodeContainer}. For every use is obtained corresponding generated type * from {@link ModuleContext#groupings allGroupings} which is added as implements type * to builder * * @param dataNodeContainer element which contains the list of used YANG groupings * @param builder builder to which are added implemented types according to dataNodeContainer * @return generated type builder with all implemented types */ private GeneratedTypeBuilder addImplementedInterfaceFromUses(final DataNodeContainer dataNodeContainer, final GeneratedTypeBuilder builder) { for (final UsesNode usesNode : dataNodeContainer.getUses()) { final GeneratedTypeBuilder genType = findGroupingByPath(usesNode.getGroupingPath()); if (genType == null) { throw new IllegalStateException("Grouping " + usesNode.getGroupingPath() + "is not resolved for " + builder.getName()); } builder.addImplementsType(genType.build()); } return builder; } private GeneratedTypeBuilder findChildNodeByPath(final SchemaPath path) { for (final ModuleContext ctx : genCtx.values()) { final GeneratedTypeBuilder result = ctx.getChildNode(path); if (result != null) { return result; } } return null; } private GeneratedTypeBuilder findGroupingByPath(final SchemaPath path) { for (final ModuleContext ctx : genCtx.values()) { final GeneratedTypeBuilder result = ctx.getGrouping(path); if (result != null) { return result; } } return null; } private GeneratedTypeBuilder findCaseByPath(final SchemaPath path) { for (final ModuleContext ctx : genCtx.values()) { final GeneratedTypeBuilder result = ctx.getCase(path); if (result != null) { return result; } } return null; } private static JavaTypeName allocateNestedType(final JavaTypeName parent, final QName child) { // Single '$' suffix cannot come from user, this mirrors AbstractGeneratedTypeBuilder.addEnumeration() return parent.createEnclosed(BindingMapping.getClassName(child), "$"); } private static void annotateDeprecatedIfNecessary(final WithStatus node, final AnnotableTypeBuilder builder) { switch (node.getStatus()) { case DEPRECATED: case OBSOLETE: // FIXME: we really want to use a pre-made annotation builder.addAnnotation(DEPRECATED_ANNOTATION); break; case CURRENT: // No-op break; default: throw new IllegalStateException("Unhandled status in " + node); } } private static void narrowImplementedInterface(final GeneratedTypeBuilder typeBuilder) { defineImplementedInterfaceMethod(typeBuilder, wildcardTypeFor(typeBuilder.getIdentifier())); } private static void defaultImplementedInterace(final GeneratedTypeBuilder typeBuilder) { defineImplementedInterfaceMethod(typeBuilder, new ReferencedTypeImpl(typeBuilder.getIdentifier())) .setDefault(true); } private static MethodSignatureBuilder defineImplementedInterfaceMethod(final GeneratedTypeBuilder typeBuilder, final Type classType) { final MethodSignatureBuilder ret = typeBuilder .addMethod(BindingMapping.DATA_CONTAINER_IMPLEMENTED_INTERFACE_NAME) .setAccessModifier(AccessModifier.PUBLIC) .setReturnType(classType(classType)); ret.addAnnotation(OVERRIDE_ANNOTATION); return ret; } }