2 * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.mdsal.binding.yang.types;
10 import static java.util.Objects.requireNonNull;
11 import static org.opendaylight.mdsal.binding.model.util.BindingTypes.TYPE_OBJECT;
13 import com.google.common.annotations.Beta;
14 import com.google.common.base.Preconditions;
15 import com.google.common.base.Strings;
16 import com.google.common.collect.ImmutableMap;
17 import java.util.ArrayList;
18 import java.util.Collection;
19 import java.util.Collections;
20 import java.util.HashMap;
21 import java.util.HashSet;
22 import java.util.List;
24 import java.util.Optional;
26 import java.util.TreeMap;
27 import org.opendaylight.mdsal.binding.generator.spi.TypeProvider;
28 import org.opendaylight.mdsal.binding.generator.util.BaseYangTypesProvider;
29 import org.opendaylight.mdsal.binding.model.api.AccessModifier;
30 import org.opendaylight.mdsal.binding.model.api.ConcreteType;
31 import org.opendaylight.mdsal.binding.model.api.Enumeration;
32 import org.opendaylight.mdsal.binding.model.api.GeneratedTransferObject;
33 import org.opendaylight.mdsal.binding.model.api.GeneratedType;
34 import org.opendaylight.mdsal.binding.model.api.JavaTypeName;
35 import org.opendaylight.mdsal.binding.model.api.Restrictions;
36 import org.opendaylight.mdsal.binding.model.api.Type;
37 import org.opendaylight.mdsal.binding.model.api.type.builder.EnumBuilder;
38 import org.opendaylight.mdsal.binding.model.api.type.builder.GeneratedPropertyBuilder;
39 import org.opendaylight.mdsal.binding.model.api.type.builder.GeneratedTOBuilder;
40 import org.opendaylight.mdsal.binding.model.api.type.builder.GeneratedTypeBuilder;
41 import org.opendaylight.mdsal.binding.model.api.type.builder.GeneratedTypeBuilderBase;
42 import org.opendaylight.mdsal.binding.model.api.type.builder.MethodSignatureBuilder;
43 import org.opendaylight.mdsal.binding.model.util.BaseYangTypes;
44 import org.opendaylight.mdsal.binding.model.util.BindingGeneratorUtil;
45 import org.opendaylight.mdsal.binding.model.util.BindingTypes;
46 import org.opendaylight.mdsal.binding.model.util.TypeConstants;
47 import org.opendaylight.mdsal.binding.model.util.Types;
48 import org.opendaylight.mdsal.binding.model.util.generated.type.builder.AbstractEnumerationBuilder;
49 import org.opendaylight.mdsal.binding.model.util.generated.type.builder.GeneratedPropertyBuilderImpl;
50 import org.opendaylight.mdsal.binding.spec.naming.BindingMapping;
51 import org.opendaylight.yangtools.yang.common.Revision;
52 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
53 import org.opendaylight.yangtools.yang.model.api.Module;
54 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
55 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
56 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
57 import org.opendaylight.yangtools.yang.model.api.Status;
58 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
59 import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition;
60 import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition.Bit;
61 import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
62 import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
63 import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
64 import org.opendaylight.yangtools.yang.model.api.type.PatternConstraint;
65 import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition;
66 import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
67 import org.opendaylight.yangtools.yang.model.spi.ModuleDependencySort;
70 public abstract class AbstractTypeProvider implements TypeProvider {
71 private static final JavaTypeName DEPRECATED_ANNOTATION = JavaTypeName.create(Deprecated.class);
74 * Contains the schema data red from YANG files.
76 private final SchemaContext schemaContext;
78 private final Map<String, Map<Optional<Revision>, Map<String, GeneratedType>>> genTypeDefsContextMap =
82 * The map which maps schema paths to JAVA <code>Type</code>.
84 private final Map<SchemaPath, Type> referencedTypes = new HashMap<>();
85 private final Map<Module, Set<GeneratedType>> additionalTypes = new HashMap<>();
86 private final Map<SchemaNode, JavaTypeName> renames;
89 * Creates new instance of class <code>TypeProviderImpl</code>.
91 * @param schemaContext contains the schema data red from YANG files
92 * @param renames renaming table
93 * @throws IllegalArgumentException if <code>schemaContext</code> equal null.
95 AbstractTypeProvider(final EffectiveModelContext schemaContext, final Map<SchemaNode, JavaTypeName> renames) {
96 this.schemaContext = requireNonNull(schemaContext);
97 this.renames = requireNonNull(renames);
99 resolveTypeDefsFromContext();
103 * Puts <code>refType</code> to map with key <code>refTypePath</code>.
105 * @param refTypePath schema path used as the map key
106 * @param refType type which represents the map value
107 * @throws IllegalArgumentException
109 * <li>if <code>refTypePath</code> equal null</li>
110 * <li>if <code>refType</code> equal null</li>
114 public void putReferencedType(final SchemaPath refTypePath, final Type refType) {
115 Preconditions.checkArgument(refTypePath != null,
116 "Path reference of Enumeration Type Definition cannot be NULL!");
117 Preconditions.checkArgument(refType != null, "Reference to Enumeration Type cannot be NULL!");
118 referencedTypes.put(refTypePath, refType);
121 public Map<Module, Set<GeneratedType>> getAdditionalTypes() {
122 return additionalTypes;
126 public Type javaTypeForSchemaDefinitionType(final TypeDefinition<?> typeDefinition, final SchemaNode parentNode,
127 final boolean lenientRelativeLeafrefs) {
128 return javaTypeForSchemaDefinitionType(typeDefinition, parentNode, null, lenientRelativeLeafrefs);
132 * Converts schema definition type <code>typeDefinition</code> to JAVA <code>Type</code>.
134 * @param typeDefinition type definition which is converted to JAVA type
135 * @throws IllegalArgumentException
137 * <li>if <code>typeDefinition</code> equal null</li>
138 * <li>if Qname of <code>typeDefinition</code> equal null</li>
139 * <li>if name of <code>typeDefinition</code> equal null</li>
143 public Type javaTypeForSchemaDefinitionType(final TypeDefinition<?> typeDefinition, final SchemaNode parentNode,
144 final Restrictions restrictions, final boolean lenientRelativeLeafrefs) {
145 throw new UnsupportedOperationException();
149 * Converts <code>typeDefinition</code> to concrete JAVA <code>Type</code>.
151 * @param typeDefinition
152 * type definition which should be converted to JAVA
154 * @return JAVA <code>Type</code> which represents
155 * <code>typeDefinition</code>
156 * @throws IllegalArgumentException
158 * <li>if <code>typeDefinition</code> equal null</li>
159 * <li>if Q name of <code>typeDefinition</code></li>
160 * <li>if name of <code>typeDefinition</code></li>
163 public GeneratedType generatedTypeForExtendedDefinitionType(final TypeDefinition<?> typeDefinition,
164 final SchemaNode parentNode) {
165 Preconditions.checkArgument(typeDefinition != null, "Type Definition cannot be NULL!");
166 if (typeDefinition.getQName() == null) {
167 throw new IllegalArgumentException("Type Definition cannot have unspecified QName (QName cannot be NULL!)");
169 Preconditions.checkArgument(typeDefinition.getQName().getLocalName() != null,
170 "Type Definitions Local Name cannot be NULL!");
172 final TypeDefinition<?> baseTypeDef = baseTypeDefForExtendedType(typeDefinition);
173 if (baseTypeDef instanceof LeafrefTypeDefinition || baseTypeDef instanceof IdentityrefTypeDefinition) {
175 * This is backwards compatibility baggage from way back when. The problem at hand is inconsistency between
176 * the fact that identity is mapped to a Class, which is also returned from leaves which specify it like
189 * This results in getFoo() returning Class<? extends Iden>, which looks fine on the surface, but gets more
190 * dicey when we throw in:
204 * Now we have competing requirements: typedef would like us to use encapsulation to capture the defined
205 * type, while getBar() wants us to retain shape with getFoo(), as it should not matter how the identityref
208 * In this particular case getFoo() won just after the Binding Spec was frozen, hence we do not generate
209 * an encapsulation for identityref typedefs.
211 * In case you are thinking we could get by having foo-ref map to a subclass of Iden, that is not a good
212 * option, as it would look as though it is the product of a different construct:
218 * Leading to a rather nice namespace clash and also slight incompatibility with unknown third-party
219 * sub-identities of iden.
221 * The story behind leafrefs is probably similar, but that needs to be ascertained.
226 final Module module = findParentModule(schemaContext, parentNode);
227 if (module != null) {
228 final Map<Optional<Revision>, Map<String, GeneratedType>> modulesByDate = genTypeDefsContextMap.get(
230 final Map<String, GeneratedType> genTOs = modulesByDate.get(module.getRevision());
231 if (genTOs != null) {
232 return genTOs.get(typeDefinition.getQName().getLocalName());
239 * Gets base type definition for <code>extendTypeDef</code>. The method is
240 * recursively called until non <code>ExtendedType</code> type is found.
242 * @param extendTypeDef
243 * type definition for which is the base type definition sought
244 * @return type definition which is base type for <code>extendTypeDef</code>
245 * @throws IllegalArgumentException
246 * if <code>extendTypeDef</code> equal null
248 private static TypeDefinition<?> baseTypeDefForExtendedType(final TypeDefinition<?> extendTypeDef) {
249 Preconditions.checkArgument(extendTypeDef != null, "Type Definition reference cannot be NULL!");
251 TypeDefinition<?> ret = extendTypeDef;
252 while (ret.getBaseType() != null) {
253 ret = ret.getBaseType();
260 * Converts <code>enumTypeDef</code> to {@link Enumeration enumeration}.
262 * @param enumTypeDef enumeration type definition which is converted to enumeration
263 * @param enumName string with name which is used as the enumeration name
264 * @return enumeration type which is built with data (name, enum values) from <code>enumTypeDef</code>
265 * @throws IllegalArgumentException
267 * <li>if <code>enumTypeDef</code> equals null</li>
268 * <li>if enum values of <code>enumTypeDef</code> equal null</li>
269 * <li>if Q name of <code>enumTypeDef</code> equal null</li>
270 * <li>if name of <code>enumTypeDef</code> equal null</li>
273 private Enumeration provideTypeForEnum(final EnumTypeDefinition enumTypeDef, final String enumName,
274 final SchemaNode parentNode) {
275 Preconditions.checkArgument(enumTypeDef != null, "EnumTypeDefinition reference cannot be NULL!");
276 Preconditions.checkArgument(enumTypeDef.getValues() != null,
277 "EnumTypeDefinition MUST contain at least ONE value definition!");
278 Preconditions.checkArgument(enumTypeDef.getQName() != null, "EnumTypeDefinition MUST contain NON-NULL QName!");
279 Preconditions.checkArgument(enumTypeDef.getQName().getLocalName() != null,
280 "Local Name in EnumTypeDefinition QName cannot be NULL!");
282 final Module module = findParentModule(schemaContext, parentNode);
283 final AbstractEnumerationBuilder enumBuilder = newEnumerationBuilder(JavaTypeName.create(
284 BindingMapping.getRootPackageName(module.getQNameModule()), BindingMapping.getClassName(enumName)));
285 addEnumDescription(enumBuilder, enumTypeDef);
286 enumTypeDef.getReference().ifPresent(enumBuilder::setReference);
287 enumBuilder.setModuleName(module.getName());
288 enumBuilder.updateEnumPairsFromEnumTypeDef(enumTypeDef);
289 return enumBuilder.toInstance();
293 * Adds enumeration to <code>typeBuilder</code>. The enumeration data are taken from <code>enumTypeDef</code>.
295 * @param enumTypeDef enumeration type definition is source of enumeration data for <code>typeBuilder</code>
296 * @param enumName string with the name of enumeration
297 * @param typeBuilder generated type builder to which is enumeration added
298 * @return enumeration type which contains enumeration data form <code>enumTypeDef</code>
299 * @throws IllegalArgumentException
301 * <li>if <code>enumTypeDef</code> equals null</li>
302 * <li>if enum values of <code>enumTypeDef</code> equal null</li>
303 * <li>if Q name of <code>enumTypeDef</code> equal null</li>
304 * <li>if name of <code>enumTypeDef</code> equal null</li>
305 * <li>if name of <code>typeBuilder</code> equal null</li>
309 private Enumeration addInnerEnumerationToTypeBuilder(final EnumTypeDefinition enumTypeDef,
310 final String enumName, final GeneratedTypeBuilderBase<?> typeBuilder) {
311 Preconditions.checkArgument(enumTypeDef != null, "EnumTypeDefinition reference cannot be NULL!");
312 Preconditions.checkArgument(enumTypeDef.getValues() != null,
313 "EnumTypeDefinition MUST contain at least ONE value definition!");
314 Preconditions.checkArgument(enumTypeDef.getQName() != null, "EnumTypeDefinition MUST contain NON-NULL QName!");
315 Preconditions.checkArgument(enumTypeDef.getQName().getLocalName() != null,
316 "Local Name in EnumTypeDefinition QName cannot be NULL!");
317 Preconditions.checkArgument(typeBuilder != null, "Generated Type Builder reference cannot be NULL!");
319 final EnumBuilder enumBuilder = newEnumerationBuilder(
320 typeBuilder.getIdentifier().createEnclosed(BindingMapping.getClassName(enumName), "$"));
321 addEnumDescription(enumBuilder, enumTypeDef);
322 enumBuilder.updateEnumPairsFromEnumTypeDef(enumTypeDef);
323 final Enumeration ret = enumBuilder.toInstance();
324 typeBuilder.addEnumeration(ret);
329 public abstract void addEnumDescription(EnumBuilder enumBuilder, EnumTypeDefinition enumTypeDef);
331 public abstract AbstractEnumerationBuilder newEnumerationBuilder(JavaTypeName identifier);
333 public abstract GeneratedTOBuilder newGeneratedTOBuilder(JavaTypeName identifier);
335 public abstract GeneratedTypeBuilder newGeneratedTypeBuilder(JavaTypeName identifier);
338 * Converts the pattern constraints to the list of the strings which represents these constraints.
340 * @param patternConstraints list of pattern constraints
341 * @return list of strings which represents the constraint patterns
343 public abstract Map<String, String> resolveRegExpressions(List<PatternConstraint> patternConstraints);
345 abstract void addCodegenInformation(GeneratedTypeBuilderBase<?> genTOBuilder, TypeDefinition<?> typeDef);
348 * Converts the pattern constraints from <code>typedef</code> to the list of the strings which represents these
351 * @param typedef extended type in which are the pattern constraints sought
352 * @return list of strings which represents the constraint patterns
353 * @throws IllegalArgumentException if <code>typedef</code> equals null
356 private Map<String, String> resolveRegExpressionsFromTypedef(final TypeDefinition<?> typedef) {
357 if (!(typedef instanceof StringTypeDefinition)) {
358 return ImmutableMap.of();
361 // TODO: run diff against base ?
362 return resolveRegExpressions(((StringTypeDefinition) typedef).getPatternConstraints());
366 * Passes through all modules and through all its type definitions and convert it to generated types.
369 * The modules are first sorted by mutual dependencies. The modules are sequentially passed. All type definitions
370 * of a module are at the beginning sorted so that type definition with less amount of references to other type
371 * definition are processed first.<br>
372 * For each module is created mapping record in the map
373 * {@link AbstractTypeProvider#genTypeDefsContextMap genTypeDefsContextMap}
374 * which map current module name to the map which maps type names to returned types (generated types).
376 private void resolveTypeDefsFromContext() {
377 final List<Module> modulesSortedByDependency = ModuleDependencySort.sort(schemaContext.getModules());
379 for (Module module : modulesSortedByDependency) {
380 Map<Optional<Revision>, Map<String, GeneratedType>> dateTypeMap = genTypeDefsContextMap.computeIfAbsent(
381 module.getName(), key -> new HashMap<>());
382 dateTypeMap.put(module.getRevision(), Collections.emptyMap());
383 genTypeDefsContextMap.put(module.getName(), dateTypeMap);
386 for (Module module : modulesSortedByDependency) {
387 if (module != null) {
388 final String basePackageName = BindingMapping.getRootPackageName(module.getQNameModule());
389 if (basePackageName != null) {
390 final List<TypeDefinition<?>> typeDefinitions = TypedefResolver.getAllTypedefs(module);
391 for (TypeDefinition<?> typedef : sortTypeDefinitionAccordingDepth(typeDefinitions)) {
392 typedefToGeneratedType(basePackageName, module, typedef);
400 * Create Type for specified type definition.
402 * @param basePackageName string with name of package to which the module belongs
403 * @param module string with the name of the module for to which the <code>typedef</code> belongs
404 * @param typedef type definition of the node for which should be created JAVA <code>Type</code>
405 * (usually generated TO)
406 * @return JAVA <code>Type</code> representation of <code>typedef</code> or
407 * <code>null</code> value if <code>basePackageName</code> or
408 * <code>modulName</code> or <code>typedef</code> or Q name of
409 * <code>typedef</code> equals <code>null</code>
411 private Type typedefToGeneratedType(final String basePackageName, final Module module,
412 final TypeDefinition<?> typedef) {
413 final TypeDefinition<?> baseTypedef = typedef.getBaseType();
415 // See generatedTypeForExtendedDefinitionType() above for rationale behind this special case.
416 if (baseTypedef instanceof LeafrefTypeDefinition || baseTypedef instanceof IdentityrefTypeDefinition) {
420 final String typedefName = typedef.getQName().getLocalName();
422 final GeneratedType returnType;
423 if (baseTypedef.getBaseType() != null) {
424 returnType = provideGeneratedTOFromExtendedType(typedef, baseTypedef, basePackageName,
426 } else if (baseTypedef instanceof UnionTypeDefinition) {
427 final GeneratedTOBuilder genTOBuilder = provideGeneratedTOBuilderForUnionTypeDef(
428 JavaTypeName.create(basePackageName, BindingMapping.getClassName(typedef.getQName())),
429 (UnionTypeDefinition) baseTypedef, typedef);
430 genTOBuilder.setTypedef(true);
431 genTOBuilder.setIsUnion(true);
432 addUnitsToGenTO(genTOBuilder, typedef.getUnits().orElse(null));
433 makeSerializable(genTOBuilder);
434 returnType = genTOBuilder.build();
436 // Define a corresponding union builder. Typedefs are always anchored at a Java package root,
437 // so we are placing the builder alongside the union.
438 final GeneratedTOBuilder unionBuilder = newGeneratedTOBuilder(
439 JavaTypeName.create(genTOBuilder.getPackageName(), genTOBuilder.getName() + "Builder"));
440 unionBuilder.setIsUnionBuilder(true);
441 final MethodSignatureBuilder method = unionBuilder.addMethod("getDefaultInstance");
442 method.setReturnType(returnType);
443 method.addParameter(Types.STRING, "defaultValue");
444 method.setAccessModifier(AccessModifier.PUBLIC);
445 method.setStatic(true);
446 additionalTypes.computeIfAbsent(module, key -> new HashSet<>()).add(unionBuilder.build());
447 } else if (baseTypedef instanceof EnumTypeDefinition) {
448 // enums are automatically Serializable
449 final EnumTypeDefinition enumTypeDef = (EnumTypeDefinition) baseTypedef;
450 // TODO units for typedef enum
451 returnType = provideTypeForEnum(enumTypeDef, typedefName, typedef);
452 } else if (baseTypedef instanceof BitsTypeDefinition) {
453 final GeneratedTOBuilder genTOBuilder = provideGeneratedTOBuilderForBitsTypeDefinition(
454 JavaTypeName.create(basePackageName, BindingMapping.getClassName(typedef.getQName())),
455 (BitsTypeDefinition) baseTypedef, module.getName());
456 genTOBuilder.setTypedef(true);
457 addUnitsToGenTO(genTOBuilder, typedef.getUnits().orElse(null));
458 makeSerializable(genTOBuilder);
459 returnType = genTOBuilder.build();
461 final Type javaType = javaTypeForSchemaDefinitionType(baseTypedef, typedef);
462 returnType = wrapJavaTypeIntoTO(basePackageName, typedef, javaType, module.getName());
464 if (returnType != null) {
465 final Map<Optional<Revision>, Map<String, GeneratedType>> modulesByDate =
466 genTypeDefsContextMap.get(module.getName());
467 final Optional<Revision> moduleRevision = module.getRevision();
468 Map<String, GeneratedType> typeMap = modulesByDate.get(moduleRevision);
469 if (typeMap != null) {
470 if (typeMap.isEmpty()) {
471 typeMap = new HashMap<>(4);
472 modulesByDate.put(moduleRevision, typeMap);
474 typeMap.put(typedefName, returnType);
482 * Wraps base YANG type to generated TO.
484 * @param basePackageName string with name of package to which the module belongs
485 * @param typedef type definition which is converted to the TO
486 * @param javaType JAVA <code>Type</code> to which is <code>typedef</code> mapped
487 * @return generated transfer object which represent<code>javaType</code>
489 private GeneratedTransferObject wrapJavaTypeIntoTO(final String basePackageName, final TypeDefinition<?> typedef,
490 final Type javaType, final String moduleName) {
491 requireNonNull(javaType, "javaType cannot be null");
493 final GeneratedTOBuilder genTOBuilder = typedefToTransferObject(basePackageName, typedef, moduleName);
494 genTOBuilder.setRestrictions(BindingGeneratorUtil.getRestrictions(typedef));
495 final GeneratedPropertyBuilder genPropBuilder = genTOBuilder.addProperty(TypeConstants.VALUE_PROP);
496 genPropBuilder.setReturnType(javaType);
498 genTOBuilder.addEqualsIdentity(genPropBuilder);
499 genTOBuilder.addHashIdentity(genPropBuilder);
500 genTOBuilder.addToStringProperty(genPropBuilder);
501 genTOBuilder.addImplementsType(BindingTypes.scalarTypeObject(javaType));
502 if (typedef.getStatus() == Status.DEPRECATED) {
503 genTOBuilder.addAnnotation(DEPRECATED_ANNOTATION);
505 if (javaType instanceof ConcreteType && "String".equals(javaType.getName()) && typedef.getBaseType() != null) {
506 addStringRegExAsConstant(genTOBuilder, resolveRegExpressionsFromTypedef(typedef));
508 addUnitsToGenTO(genTOBuilder, typedef.getUnits().orElse(null));
509 genTOBuilder.setTypedef(true);
510 makeSerializable(genTOBuilder);
511 return genTOBuilder.build();
515 * Converts output list of generated TO builders to one TO builder (first
516 * from list) which contains the remaining builders as its enclosing TO.
518 * @param typeName new type identifier
519 * @param typedef type definition which should be of type {@link UnionTypeDefinition}
520 * @return generated TO builder with the list of enclosed generated TO builders
522 public GeneratedTOBuilder provideGeneratedTOBuilderForUnionTypeDef(final JavaTypeName typeName,
523 final UnionTypeDefinition typedef, final TypeDefinition<?> parentNode) {
524 final List<GeneratedTOBuilder> builders = provideGeneratedTOBuildersForUnionTypeDef(typeName, typedef,
526 Preconditions.checkState(!builders.isEmpty(), "No GeneratedTOBuilder objects generated from union %s", typedef);
528 final GeneratedTOBuilder resultTOBuilder = builders.remove(0);
529 builders.forEach(builder -> resultTOBuilder.addEnclosingTransferObject(builder.build()));
530 return resultTOBuilder;
534 * Converts <code>typedef</code> to generated TO with <code>typeDefName</code>. Every union type from
535 * <code>typedef</code> is added to generated TO builder as property.
537 * @param typeName new type identifier
538 * @param typedef type definition which should be of type <code>UnionTypeDefinition</code>
539 * @return generated TO builder which represents <code>typedef</code>
540 * @throws NullPointerException
542 * <li>if <code>basePackageName</code> is null</li>
543 * <li>if <code>typedef</code> is null</li>
544 * <li>if Qname of <code>typedef</code> is null</li>
547 public List<GeneratedTOBuilder> provideGeneratedTOBuildersForUnionTypeDef(final JavaTypeName typeName,
548 final UnionTypeDefinition typedef, final SchemaNode parentNode) {
549 requireNonNull(typedef, "Type Definition cannot be NULL!");
550 requireNonNull(typedef.getQName(), "Type definition QName cannot be NULL!");
552 final List<GeneratedTOBuilder> generatedTOBuilders = new ArrayList<>();
553 final List<TypeDefinition<?>> unionTypes = typedef.getTypes();
554 final Module module = findParentModule(schemaContext, parentNode);
556 final GeneratedTOBuilder unionGenTOBuilder = newGeneratedTOBuilder(typeName);
557 unionGenTOBuilder.setIsUnion(true);
558 unionGenTOBuilder.setSchemaPath(typedef.getPath());
559 unionGenTOBuilder.setModuleName(module.getName());
560 unionGenTOBuilder.addImplementsType(TYPE_OBJECT);
561 addCodegenInformation(unionGenTOBuilder, typedef);
562 generatedTOBuilders.add(unionGenTOBuilder);
564 // Pattern string is the key, XSD regex is the value. The reason for this choice is that the pattern carries
565 // also negation information and hence guarantees uniqueness.
566 final Map<String, String> expressions = new HashMap<>();
567 for (TypeDefinition<?> unionType : unionTypes) {
568 final String unionTypeName = unionType.getQName().getLocalName();
570 // If we have a base type we should follow the type definition backwards, except for identityrefs, as those
571 // do not follow type encapsulation -- we use the general case for that.
572 if (unionType.getBaseType() != null && !(unionType instanceof IdentityrefTypeDefinition)) {
573 resolveExtendedSubtypeAsUnion(unionGenTOBuilder, unionType, expressions, parentNode);
574 } else if (unionType instanceof UnionTypeDefinition) {
575 generatedTOBuilders.addAll(resolveUnionSubtypeAsUnion(unionGenTOBuilder,
576 (UnionTypeDefinition) unionType, parentNode));
577 } else if (unionType instanceof EnumTypeDefinition) {
578 final Enumeration enumeration = addInnerEnumerationToTypeBuilder((EnumTypeDefinition) unionType,
579 unionTypeName, unionGenTOBuilder);
580 updateUnionTypeAsProperty(unionGenTOBuilder, enumeration, unionTypeName);
582 final Type javaType = javaTypeForSchemaDefinitionType(unionType, parentNode);
583 updateUnionTypeAsProperty(unionGenTOBuilder, javaType, unionTypeName);
586 addStringRegExAsConstant(unionGenTOBuilder, expressions);
588 storeGenTO(typedef, unionGenTOBuilder, parentNode);
590 return generatedTOBuilders;
594 * Wraps code which handles the case when union subtype is also of the type <code>UnionType</code>.
597 * In this case the new generated TO is created for union subtype (recursive call of method
598 * {@link #provideGeneratedTOBuildersForUnionTypeDef(String, UnionTypeDefinition, String, SchemaNode)}
599 * provideGeneratedTOBuilderForUnionTypeDef} and in parent TO builder <code>parentUnionGenTOBuilder</code> is
600 * created property which type is equal to new generated TO.
602 * @param parentUnionGenTOBuilder generated TO builder to which is the property with the child union subtype added
603 * @param basePackageName string with the name of the module package
604 * @param unionSubtype type definition which represents union subtype
605 * @return list of generated TO builders. The number of the builders can be bigger one due to recursive call of
606 * <code>provideGeneratedTOBuildersForUnionTypeDef</code> method.
608 private List<GeneratedTOBuilder> resolveUnionSubtypeAsUnion(final GeneratedTOBuilder parentUnionGenTOBuilder,
609 final UnionTypeDefinition unionSubtype, final SchemaNode parentNode) {
610 final JavaTypeName newTOBuilderName = parentUnionGenTOBuilder.getIdentifier().createSibling(
611 provideAvailableNameForGenTOBuilder(parentUnionGenTOBuilder.getName()));
612 final List<GeneratedTOBuilder> subUnionGenTOBUilders = provideGeneratedTOBuildersForUnionTypeDef(
613 newTOBuilderName, unionSubtype, parentNode);
615 final GeneratedPropertyBuilder propertyBuilder;
616 propertyBuilder = parentUnionGenTOBuilder.addProperty(BindingMapping.getPropertyName(
617 newTOBuilderName.simpleName()));
618 propertyBuilder.setReturnType(subUnionGenTOBUilders.get(0).build());
619 parentUnionGenTOBuilder.addEqualsIdentity(propertyBuilder);
620 parentUnionGenTOBuilder.addToStringProperty(propertyBuilder);
622 return subUnionGenTOBUilders;
626 * Wraps code which handle case when union subtype is of the type <code>ExtendedType</code>. If TO for this type
627 * already exists it is used for the creation of the property in <code>parentUnionGenTOBuilder</code>. Otherwise
628 * the base type is used for the property creation.
630 * @param parentUnionGenTOBuilder generated TO builder in which new property is created
631 * @param unionSubtype type definition of the <code>ExtendedType</code> type which represents union subtype
632 * @param expressions list of strings with the regular expressions
633 * @param parentNode parent Schema Node for Extended Subtype
635 private void resolveExtendedSubtypeAsUnion(final GeneratedTOBuilder parentUnionGenTOBuilder,
636 final TypeDefinition<?> unionSubtype, final Map<String, String> expressions, final SchemaNode parentNode) {
637 final String unionTypeName = unionSubtype.getQName().getLocalName();
638 final Type genTO = findGenTO(unionTypeName, unionSubtype);
640 updateUnionTypeAsProperty(parentUnionGenTOBuilder, genTO, genTO.getName());
644 final TypeDefinition<?> baseType = baseTypeDefForExtendedType(unionSubtype);
645 if (unionTypeName.equals(baseType.getQName().getLocalName())) {
646 final Type javaType = BaseYangTypesProvider.INSTANCE.javaTypeForSchemaDefinitionType(baseType, parentNode,
647 BindingGeneratorUtil.getRestrictions(unionSubtype));
648 if (javaType != null) {
649 updateUnionTypeAsProperty(parentUnionGenTOBuilder, javaType, unionTypeName);
651 } else if (baseType instanceof LeafrefTypeDefinition) {
652 final Type javaType = javaTypeForSchemaDefinitionType(baseType, parentNode);
653 boolean typeExist = false;
654 for (GeneratedPropertyBuilder generatedPropertyBuilder : parentUnionGenTOBuilder.getProperties()) {
655 final Type origType = ((GeneratedPropertyBuilderImpl) generatedPropertyBuilder).getReturnType();
656 if (origType != null && javaType != null && javaType == origType) {
661 if (!typeExist && javaType != null) {
662 updateUnionTypeAsProperty(parentUnionGenTOBuilder, javaType,
663 BindingMapping.getUnionLeafrefMemberName(parentUnionGenTOBuilder.getName(), javaType.getName()));
666 if (baseType instanceof StringTypeDefinition) {
667 expressions.putAll(resolveRegExpressionsFromTypedef(unionSubtype));
672 * Searches for generated TO for <code>searchedTypeDef</code> type definition
673 * in {@link #genTypeDefsContextMap genTypeDefsContextMap}.
675 * @param searchedTypeName string with name of <code>searchedTypeDef</code>
676 * @return generated TO for <code>searchedTypeDef</code> or <code>null</code> it it doesn't exist
678 private Type findGenTO(final String searchedTypeName, final SchemaNode parentNode) {
679 final Module typeModule = findParentModule(schemaContext, parentNode);
680 if (typeModule != null && typeModule.getName() != null) {
681 final Map<Optional<Revision>, Map<String, GeneratedType>> modulesByDate = genTypeDefsContextMap.get(
682 typeModule.getName());
683 final Map<String, GeneratedType> genTOs = modulesByDate.get(typeModule.getRevision());
684 if (genTOs != null) {
685 return genTOs.get(searchedTypeName);
692 * Stores generated TO created from <code>genTOBuilder</code> for <code>newTypeDef</code>
693 * to {@link #genTypeDefsContextMap genTypeDefsContextMap} if the module for <code>newTypeDef</code> exists.
695 * @param newTypeDef type definition for which is <code>genTOBuilder</code> created
696 * @param genTOBuilder generated TO builder which is converted to generated TO and stored
698 private void storeGenTO(final TypeDefinition<?> newTypeDef, final GeneratedTOBuilder genTOBuilder,
699 final SchemaNode parentNode) {
700 if (!(newTypeDef instanceof UnionTypeDefinition)) {
701 final Module parentModule = findParentModule(schemaContext, parentNode);
702 if (parentModule != null && parentModule.getName() != null) {
703 final Map<Optional<Revision>, Map<String, GeneratedType>> modulesByDate = genTypeDefsContextMap.get(
704 parentModule.getName());
705 final Map<String, GeneratedType> genTOsMap = modulesByDate.get(parentModule.getRevision());
706 genTOsMap.put(newTypeDef.getQName().getLocalName(), genTOBuilder.build());
712 * Adds a new property with the name <code>propertyName</code> and with type <code>type</code>
713 * to <code>unonGenTransObject</code>.
715 * @param unionGenTransObject generated TO to which should be property added
716 * @param type JAVA <code>type</code> of the property which should be added to <code>unionGentransObject</code>
717 * @param propertyName string with name of property which should be added to <code>unionGentransObject</code>
719 private static void updateUnionTypeAsProperty(final GeneratedTOBuilder unionGenTransObject, final Type type,
720 final String propertyName) {
721 if (unionGenTransObject != null && type != null && !unionGenTransObject.containsProperty(propertyName)) {
722 final GeneratedPropertyBuilder propBuilder = unionGenTransObject
723 .addProperty(BindingMapping.getPropertyName(propertyName));
724 propBuilder.setReturnType(type);
726 unionGenTransObject.addEqualsIdentity(propBuilder);
727 unionGenTransObject.addHashIdentity(propBuilder);
728 unionGenTransObject.addToStringProperty(propBuilder);
733 * Converts <code>typedef</code> to the generated TO builder.
735 * @param basePackageName string with name of package to which the module belongs
736 * @param typedef type definition from which is the generated TO builder created
737 * @return generated TO builder which contains data from <code>typedef</code> and <code>basePackageName</code>
739 private GeneratedTOBuilder typedefToTransferObject(final String basePackageName,
740 final TypeDefinition<?> typedef, final String moduleName) {
741 JavaTypeName name = renames.get(typedef);
743 name = JavaTypeName.create(
744 BindingGeneratorUtil.packageNameForGeneratedType(basePackageName, typedef.getPath()),
745 BindingMapping.getClassName(typedef.getQName().getLocalName()));
748 final GeneratedTOBuilder newType = newGeneratedTOBuilder(name);
749 newType.setSchemaPath(typedef.getPath());
750 newType.setModuleName(moduleName);
751 addCodegenInformation(newType, typedef);
756 * Converts <code>typeDef</code> which should be of the type <code>BitsTypeDefinition</code>
757 * to <code>GeneratedTOBuilder</code>. All the bits of the typeDef are added to returning generated TO as
760 * @param typeName new type identifier
761 * @param typeDef type definition from which is the generated TO builder created
762 * @return generated TO builder which represents <code>typeDef</code>
763 * @throws IllegalArgumentException
765 * <li>if <code>typeDef</code> equals null</li>
766 * <li>if <code>basePackageName</code> equals null</li>
769 public GeneratedTOBuilder provideGeneratedTOBuilderForBitsTypeDefinition(final JavaTypeName typeName,
770 final BitsTypeDefinition typeDef, final String moduleName) {
771 final GeneratedTOBuilder genTOBuilder = newGeneratedTOBuilder(typeName);
772 genTOBuilder.setSchemaPath(typeDef.getPath());
773 genTOBuilder.setModuleName(moduleName);
774 genTOBuilder.setBaseType(typeDef);
775 genTOBuilder.addImplementsType(TYPE_OBJECT);
776 addCodegenInformation(genTOBuilder, typeDef);
778 for (Bit bit : typeDef.getBits()) {
779 final String name = bit.getName();
780 GeneratedPropertyBuilder genPropertyBuilder = genTOBuilder.addProperty(
781 BindingMapping.getPropertyName(name));
782 genPropertyBuilder.setReadOnly(true);
783 genPropertyBuilder.setReturnType(BaseYangTypes.BOOLEAN_TYPE);
785 genTOBuilder.addEqualsIdentity(genPropertyBuilder);
786 genTOBuilder.addHashIdentity(genPropertyBuilder);
787 genTOBuilder.addToStringProperty(genPropertyBuilder);
794 * Adds to the <code>genTOBuilder</code> the constant which contains regular expressions from
795 * the <code>regularExpressions</code>.
797 * @param genTOBuilder generated TO builder to which are <code>regular expressions</code> added
798 * @param expressions list of string which represent regular expressions
800 private static void addStringRegExAsConstant(final GeneratedTOBuilder genTOBuilder,
801 final Map<String, String> expressions) {
802 if (!expressions.isEmpty()) {
803 genTOBuilder.addConstant(Types.listTypeFor(BaseYangTypes.STRING_TYPE), TypeConstants.PATTERN_CONSTANT_NAME,
804 ImmutableMap.copyOf(expressions));
809 * Creates generated TO with data about inner extended type <code>innerExtendedType</code>, about the package name
810 * <code>typedefName</code> and about the generated TO name <code>typedefName</code>.
813 * It is assumed that <code>innerExtendedType</code> is already present in
814 * {@link AbstractTypeProvider#genTypeDefsContextMap genTypeDefsContextMap} to be possible set it as extended type
815 * for the returning generated TO.
817 * @param typedef Type Definition
818 * @param innerExtendedType extended type which is part of some other extended type
819 * @param basePackageName string with the package name of the module
820 * @param moduleName Module Name
821 * @return generated TO which extends generated TO for <code>innerExtendedType</code>
822 * @throws IllegalArgumentException
824 * <li>if <code>extendedType</code> equals null</li>
825 * <li>if <code>basePackageName</code> equals null</li>
826 * <li>if <code>typedefName</code> equals null</li>
829 private GeneratedTransferObject provideGeneratedTOFromExtendedType(final TypeDefinition<?> typedef,
830 final TypeDefinition<?> innerExtendedType, final String basePackageName, final String moduleName) {
831 Preconditions.checkArgument(innerExtendedType != null, "Extended type cannot be NULL!");
832 Preconditions.checkArgument(basePackageName != null, "String with base package name cannot be NULL!");
834 final GeneratedTOBuilder genTOBuilder = newGeneratedTOBuilder(JavaTypeName.create(basePackageName,
835 BindingMapping.getClassName(typedef.getQName())));
836 genTOBuilder.setSchemaPath(typedef.getPath());
837 genTOBuilder.setModuleName(moduleName);
838 genTOBuilder.setTypedef(true);
839 addCodegenInformation(genTOBuilder, typedef);
841 final Restrictions r = BindingGeneratorUtil.getRestrictions(typedef);
842 genTOBuilder.setRestrictions(r);
843 addStringRegExAsConstant(genTOBuilder, resolveRegExpressionsFromTypedef(typedef));
845 if (typedef.getStatus() == Status.DEPRECATED) {
846 genTOBuilder.addAnnotation(DEPRECATED_ANNOTATION);
849 if (baseTypeDefForExtendedType(innerExtendedType) instanceof UnionTypeDefinition) {
850 genTOBuilder.setIsUnion(true);
853 Map<Optional<Revision>, Map<String, GeneratedType>> modulesByDate = null;
854 Map<String, GeneratedType> typeMap = null;
855 final Module parentModule = findParentModule(schemaContext, innerExtendedType);
856 if (parentModule != null) {
857 modulesByDate = genTypeDefsContextMap.get(parentModule.getName());
858 typeMap = modulesByDate.get(parentModule.getRevision());
861 if (typeMap != null) {
862 final String innerTypeDef = innerExtendedType.getQName().getLocalName();
863 final GeneratedType type = typeMap.get(innerTypeDef);
864 if (type instanceof GeneratedTransferObject) {
865 genTOBuilder.setExtendsType((GeneratedTransferObject) type);
868 addUnitsToGenTO(genTOBuilder, typedef.getUnits().orElse(null));
869 makeSerializable(genTOBuilder);
871 return genTOBuilder.build();
875 * Add {@link java.io.Serializable} to implemented interfaces of this TO. Also compute and add serialVersionUID
878 * @param gto transfer object which needs to be made serializable
880 private static void makeSerializable(final GeneratedTOBuilder gto) {
881 gto.addImplementsType(Types.serializableType());
882 final GeneratedPropertyBuilder prop = new GeneratedPropertyBuilderImpl("serialVersionUID");
883 prop.setValue(Long.toString(BindingGeneratorUtil.computeDefaultSUID(gto)));
888 * Finds out for each type definition how many immersion (depth) is necessary to get to the base type. Every type
889 * definition is inserted to the map which key is depth and value is list of type definitions with equal depth.
890 * In next step are lists from this map concatenated to one list in ascending order according to their depth. All
891 * type definitions are in the list behind all type definitions on which depends.
893 * @param unsortedTypeDefinitions list of type definitions which should be sorted by depth
894 * @return list of type definitions sorted according their each other dependencies (type definitions which are
895 * dependent on other type definitions are in list behind them).
897 private static List<TypeDefinition<?>> sortTypeDefinitionAccordingDepth(
898 final Collection<TypeDefinition<?>> unsortedTypeDefinitions) {
899 final List<TypeDefinition<?>> sortedTypeDefinition = new ArrayList<>();
901 final Map<Integer, List<TypeDefinition<?>>> typeDefinitionsDepths = new TreeMap<>();
902 for (TypeDefinition<?> unsortedTypeDefinition : unsortedTypeDefinitions) {
903 final Integer depth = getTypeDefinitionDepth(unsortedTypeDefinition);
904 List<TypeDefinition<?>> typeDefinitionsConcreteDepth =
905 typeDefinitionsDepths.computeIfAbsent(depth, k -> new ArrayList<>());
906 typeDefinitionsConcreteDepth.add(unsortedTypeDefinition);
909 // SortedMap guarantees order corresponding to keys in ascending order
910 for (List<TypeDefinition<?>> v : typeDefinitionsDepths.values()) {
911 sortedTypeDefinition.addAll(v);
914 return sortedTypeDefinition;
918 * Returns how many immersion is necessary to get from the type definition to the base type.
920 * @param typeDefinition type definition for which is depth sought.
921 * @return number of immersions which are necessary to get from the type definition to the base type
923 private static int getTypeDefinitionDepth(final TypeDefinition<?> typeDefinition) {
924 // FIXME: rewrite this in a non-recursive manner
925 if (typeDefinition == null) {
928 final TypeDefinition<?> baseType = typeDefinition.getBaseType();
929 if (baseType == null) {
934 if (baseType.getBaseType() != null) {
935 depth = depth + getTypeDefinitionDepth(baseType);
936 } else if (baseType instanceof UnionTypeDefinition) {
937 final List<TypeDefinition<?>> childTypeDefinitions = ((UnionTypeDefinition) baseType).getTypes();
938 int maxChildDepth = 0;
940 for (TypeDefinition<?> childTypeDefinition : childTypeDefinitions) {
941 childDepth = childDepth + getTypeDefinitionDepth(childTypeDefinition);
942 if (childDepth > maxChildDepth) {
943 maxChildDepth = childDepth;
946 return maxChildDepth;
952 * Returns string which contains the same value as <code>name</code> but integer suffix is incremented by one. If
953 * <code>name</code> contains no number suffix, a new suffix initialized at 1 is added. A suffix is actually
954 * composed of a '$' marker, which is safe, as no YANG identifier can contain '$', and a unsigned decimal integer.
956 * @param name string with name of augmented node
957 * @return string with the number suffix incremented by one (or 1 is added)
959 private static String provideAvailableNameForGenTOBuilder(final String name) {
960 final int dollar = name.indexOf('$');
965 final int newSuffix = Integer.parseUnsignedInt(name.substring(dollar + 1)) + 1;
966 Preconditions.checkState(newSuffix > 0, "Suffix counter overflow");
967 return name.substring(0, dollar + 1) + newSuffix;
970 public static void addUnitsToGenTO(final GeneratedTOBuilder to, final String units) {
971 if (!Strings.isNullOrEmpty(units)) {
972 to.addConstant(Types.STRING, "_UNITS", "\"" + units + "\"");
973 final GeneratedPropertyBuilder prop = new GeneratedPropertyBuilderImpl("UNITS");
974 prop.setReturnType(Types.STRING);
975 to.addToStringProperty(prop);
980 * Returns parent Yang Module for specified Schema Context in which Schema
981 * Node is declared. If the Schema Node is not present in Schema Context the
982 * operation will return <code>null</code>.
984 * @param context Schema Context
985 * @param schemaNode Schema Node
986 * @return Yang Module for specified Schema Context and Schema Node, if Schema Node is NOT present, the method will
987 * return <code>null</code>
988 * @throws NullPointerException if any of the arguments is null
990 private static Module findParentModule(final SchemaContext context, final SchemaNode schemaNode) {
991 return context.findModule(schemaNode.getQName().getModule()).orElse(null);