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 com.google.common.base.Verify.verifyNotNull;
11 import static java.util.Objects.requireNonNull;
12 import static org.opendaylight.mdsal.binding.model.util.BindingTypes.TYPE_OBJECT;
13 import static org.opendaylight.yangtools.yang.model.util.SchemaContextUtil.findDataSchemaNode;
14 import static org.opendaylight.yangtools.yang.model.util.SchemaContextUtil.findDataSchemaNodeForRelativeXPath;
15 import static org.opendaylight.yangtools.yang.model.util.SchemaContextUtil.findParentModule;
17 import com.google.common.annotations.Beta;
18 import com.google.common.base.Preconditions;
19 import com.google.common.base.Strings;
20 import com.google.common.collect.ImmutableMap;
21 import java.math.BigDecimal;
22 import java.math.BigInteger;
23 import java.util.ArrayList;
24 import java.util.Base64;
25 import java.util.Collection;
26 import java.util.Collections;
27 import java.util.Comparator;
28 import java.util.HashMap;
29 import java.util.HashSet;
30 import java.util.Iterator;
31 import java.util.List;
33 import java.util.Optional;
35 import java.util.TreeMap;
36 import java.util.regex.Pattern;
37 import org.opendaylight.mdsal.binding.generator.spi.TypeProvider;
38 import org.opendaylight.mdsal.binding.model.api.AccessModifier;
39 import org.opendaylight.mdsal.binding.model.api.ConcreteType;
40 import org.opendaylight.mdsal.binding.model.api.Enumeration;
41 import org.opendaylight.mdsal.binding.model.api.GeneratedProperty;
42 import org.opendaylight.mdsal.binding.model.api.GeneratedTransferObject;
43 import org.opendaylight.mdsal.binding.model.api.JavaTypeName;
44 import org.opendaylight.mdsal.binding.model.api.Restrictions;
45 import org.opendaylight.mdsal.binding.model.api.Type;
46 import org.opendaylight.mdsal.binding.model.api.type.builder.EnumBuilder;
47 import org.opendaylight.mdsal.binding.model.api.type.builder.GeneratedPropertyBuilder;
48 import org.opendaylight.mdsal.binding.model.api.type.builder.GeneratedTOBuilder;
49 import org.opendaylight.mdsal.binding.model.api.type.builder.GeneratedTypeBuilder;
50 import org.opendaylight.mdsal.binding.model.api.type.builder.GeneratedTypeBuilderBase;
51 import org.opendaylight.mdsal.binding.model.api.type.builder.MethodSignatureBuilder;
52 import org.opendaylight.mdsal.binding.model.util.BindingGeneratorUtil;
53 import org.opendaylight.mdsal.binding.model.util.TypeConstants;
54 import org.opendaylight.mdsal.binding.model.util.Types;
55 import org.opendaylight.mdsal.binding.model.util.generated.type.builder.AbstractEnumerationBuilder;
56 import org.opendaylight.mdsal.binding.model.util.generated.type.builder.GeneratedPropertyBuilderImpl;
57 import org.opendaylight.mdsal.binding.spec.naming.BindingMapping;
58 import org.opendaylight.yangtools.yang.common.QName;
59 import org.opendaylight.yangtools.yang.common.Revision;
60 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
61 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
62 import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
63 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
64 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
65 import org.opendaylight.yangtools.yang.model.api.Module;
66 import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath;
67 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
68 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
69 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
70 import org.opendaylight.yangtools.yang.model.api.Status;
71 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
72 import org.opendaylight.yangtools.yang.model.api.type.BinaryTypeDefinition;
73 import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition;
74 import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition.Bit;
75 import org.opendaylight.yangtools.yang.model.api.type.BooleanTypeDefinition;
76 import org.opendaylight.yangtools.yang.model.api.type.DecimalTypeDefinition;
77 import org.opendaylight.yangtools.yang.model.api.type.EmptyTypeDefinition;
78 import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
79 import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
80 import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
81 import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
82 import org.opendaylight.yangtools.yang.model.api.type.PatternConstraint;
83 import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition;
84 import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
85 import org.opendaylight.yangtools.yang.model.util.ModuleDependencySort;
86 import org.opendaylight.yangtools.yang.model.util.RevisionAwareXPathImpl;
87 import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
88 import org.opendaylight.yangtools.yang.model.util.type.BaseTypes;
89 import org.opendaylight.yangtools.yang.model.util.type.CompatUtils;
90 import org.slf4j.Logger;
91 import org.slf4j.LoggerFactory;
94 public abstract class AbstractTypeProvider implements TypeProvider {
95 private static final Logger LOG = LoggerFactory.getLogger(AbstractTypeProvider.class);
96 private static final Pattern GROUPS_PATTERN = Pattern.compile("\\[(.*?)\\]");
98 // Backwards compatibility: Union types used to be instantiated in YANG namespace, which is no longer
99 // the case, as unions are emitted to their correct schema path.
100 private static final SchemaPath UNION_PATH = SchemaPath.create(true,
101 org.opendaylight.yangtools.yang.model.util.BaseTypes.UNION_QNAME);
104 * Contains the schema data red from YANG files.
106 private final SchemaContext schemaContext;
108 private final Map<String, Map<Optional<Revision>, Map<String, Type>>> genTypeDefsContextMap = new HashMap<>();
111 * The map which maps schema paths to JAVA <code>Type</code>.
113 private final Map<SchemaPath, Type> referencedTypes = new HashMap<>();
114 private final Map<Module, Set<Type>> additionalTypes = new HashMap<>();
115 private final Map<SchemaNode, JavaTypeName> renames;
118 * Creates new instance of class <code>TypeProviderImpl</code>.
120 * @param schemaContext contains the schema data red from YANG files
121 * @param renames renaming table
122 * @throws IllegalArgumentException if <code>schemaContext</code> equal null.
124 AbstractTypeProvider(final SchemaContext schemaContext, final Map<SchemaNode, JavaTypeName> renames) {
125 Preconditions.checkArgument(schemaContext != null, "Schema Context cannot be null!");
126 this.schemaContext = schemaContext;
127 this.renames = requireNonNull(renames);
128 resolveTypeDefsFromContext();
132 * Puts <code>refType</code> to map with key <code>refTypePath</code>.
134 * @param refTypePath schema path used as the map key
135 * @param refType type which represents the map value
136 * @throws IllegalArgumentException
138 * <li>if <code>refTypePath</code> equal null</li>
139 * <li>if <code>refType</code> equal null</li>
143 public void putReferencedType(final SchemaPath refTypePath, final Type refType) {
144 Preconditions.checkArgument(refTypePath != null,
145 "Path reference of Enumeration Type Definition cannot be NULL!");
146 Preconditions.checkArgument(refType != null, "Reference to Enumeration Type cannot be NULL!");
147 referencedTypes.put(refTypePath, refType);
150 public Map<Module, Set<Type>> getAdditionalTypes() {
151 return additionalTypes;
155 public Type javaTypeForSchemaDefinitionType(final TypeDefinition<?> typeDefinition, final SchemaNode parentNode,
156 final boolean lenientRelativeLeafrefs) {
157 return javaTypeForSchemaDefinitionType(typeDefinition, parentNode, null, lenientRelativeLeafrefs);
161 * Converts schema definition type <code>typeDefinition</code> to JAVA <code>Type</code>.
163 * @param typeDefinition type definition which is converted to JAVA type
164 * @throws IllegalArgumentException
166 * <li>if <code>typeDefinition</code> equal null</li>
167 * <li>if Qname of <code>typeDefinition</code> equal null</li>
168 * <li>if name of <code>typeDefinition</code> equal null</li>
172 public Type javaTypeForSchemaDefinitionType(final TypeDefinition<?> typeDefinition, final SchemaNode parentNode,
173 final Restrictions restrictions, final boolean lenientRelativeLeafrefs) {
174 Preconditions.checkArgument(typeDefinition != null, "Type Definition cannot be NULL!");
175 Preconditions.checkArgument(typeDefinition.getQName() != null,
176 "Type Definition cannot have non specified QName (QName cannot be NULL!)");
177 final String typedefName = typeDefinition.getQName().getLocalName();
178 Preconditions.checkArgument(typedefName != null, "Type Definitions Local Name cannot be NULL!");
180 // Deal with base types
181 if (typeDefinition.getBaseType() == null) {
182 // We have to deal with differing handling of decimal64. The old parser used a fixed Decimal64 type
183 // and generated an enclosing ExtendedType to hold any range constraints. The new parser instantiates
184 // a base type which holds these constraints.
185 if (typeDefinition instanceof DecimalTypeDefinition) {
186 final Type ret = BaseYangTypes.BASE_YANG_TYPES_PROVIDER.javaTypeForSchemaDefinitionType(typeDefinition,
187 parentNode, restrictions, lenientRelativeLeafrefs);
193 // Deal with leafrefs/identityrefs
194 Type ret = javaTypeForLeafrefOrIdentityRef(typeDefinition, parentNode, lenientRelativeLeafrefs);
199 // FIXME: it looks as though we could be using the same codepath as above...
200 ret = BaseYangTypes.javaTypeForYangType(typeDefinition.getQName().getLocalName());
202 LOG.debug("Failed to resolve Java type for {}", typeDefinition);
208 Type returnType = javaTypeForExtendedType(typeDefinition, lenientRelativeLeafrefs);
209 if (restrictions != null && returnType instanceof GeneratedTransferObject) {
210 final GeneratedTransferObject gto = (GeneratedTransferObject) returnType;
211 final Module module = findParentModule(schemaContext, parentNode);
212 final String basePackageName = BindingMapping.getRootPackageName(module.getQNameModule());
213 final String packageName = BindingGeneratorUtil.packageNameForGeneratedType(basePackageName,
214 typeDefinition.getPath());
215 final String genTOName = BindingMapping.getClassName(typedefName);
216 final String name = packageName + "." + genTOName;
217 if (!returnType.getFullyQualifiedName().equals(name)) {
218 returnType = shadedTOWithRestrictions(gto, restrictions);
224 private GeneratedTransferObject shadedTOWithRestrictions(final GeneratedTransferObject gto,
225 final Restrictions restrictions) {
226 final GeneratedTOBuilder gtob = newGeneratedTOBuilder(gto.getIdentifier());
227 final GeneratedTransferObject parent = gto.getSuperType();
228 if (parent != null) {
229 gtob.setExtendsType(parent);
231 gtob.setRestrictions(restrictions);
232 for (GeneratedProperty gp : gto.getProperties()) {
233 final GeneratedPropertyBuilder gpb = gtob.addProperty(gp.getName());
234 gpb.setValue(gp.getValue());
235 gpb.setReadOnly(gp.isReadOnly());
236 gpb.setAccessModifier(gp.getAccessModifier());
237 gpb.setReturnType(gp.getReturnType());
238 gpb.setFinal(gp.isFinal());
239 gpb.setStatic(gp.isStatic());
244 private boolean isLeafRefSelfReference(final LeafrefTypeDefinition leafref, final SchemaNode parentNode) {
245 final SchemaNode leafRefValueNode;
246 final RevisionAwareXPath leafRefXPath = leafref.getPathStatement();
247 final RevisionAwareXPath leafRefStrippedXPath = new RevisionAwareXPathImpl(
248 GROUPS_PATTERN.matcher(leafRefXPath.toString()).replaceAll(""), leafRefXPath.isAbsolute());
250 ///// skip leafrefs in augments - they're checked once augments are resolved
251 final Iterator<QName> iterator = parentNode.getPath().getPathFromRoot().iterator();
252 boolean isAugmenting = false;
253 DataNodeContainer current = null;
254 DataSchemaNode dataChildByName;
256 while (iterator.hasNext() && !isAugmenting) {
257 final QName next = iterator.next();
258 if (current == null) {
259 dataChildByName = schemaContext.getDataChildByName(next);
261 dataChildByName = current.getDataChildByName(next);
263 if (dataChildByName != null) {
264 isAugmenting = dataChildByName.isAugmenting();
268 if (dataChildByName instanceof DataNodeContainer) {
269 current = (DataNodeContainer) dataChildByName;
277 final Module parentModule = getParentModule(parentNode);
278 if (!leafRefStrippedXPath.isAbsolute()) {
279 leafRefValueNode = SchemaContextUtil.findDataSchemaNodeForRelativeXPath(schemaContext, parentModule,
280 parentNode, leafRefStrippedXPath);
282 leafRefValueNode = SchemaContextUtil.findDataSchemaNode(schemaContext, parentModule, leafRefStrippedXPath);
284 return leafRefValueNode != null && leafRefValueNode.equals(parentNode);
288 * Returns JAVA <code>Type</code> for instances of the type <code>LeafrefTypeDefinition</code> or
289 * <code>IdentityrefTypeDefinition</code>.
291 * @param typeDefinition type definition which is converted to JAVA <code>Type</code>
292 * @return JAVA <code>Type</code> instance for <code>typeDefinition</code>
294 private Type javaTypeForLeafrefOrIdentityRef(final TypeDefinition<?> typeDefinition, final SchemaNode parentNode,
295 final boolean inGrouping) {
296 if (typeDefinition instanceof LeafrefTypeDefinition) {
297 final LeafrefTypeDefinition leafref = (LeafrefTypeDefinition) typeDefinition;
298 Preconditions.checkArgument(!isLeafRefSelfReference(leafref, parentNode),
299 "Leafref %s is referencing itself, incoming StackOverFlowError detected.", leafref);
300 return provideTypeForLeafref(leafref, parentNode, inGrouping);
301 } else if (typeDefinition instanceof IdentityrefTypeDefinition) {
302 return provideTypeForIdentityref((IdentityrefTypeDefinition) typeDefinition);
309 * Returns JAVA <code>Type</code> for instances of the type <code>ExtendedType</code>.
311 * @param typeDefinition type definition which is converted to JAVA <code>Type</code>
312 * @return JAVA <code>Type</code> instance for <code>typeDefinition</code>
314 private Type javaTypeForExtendedType(final TypeDefinition<?> typeDefinition, final boolean lenient) {
315 final String typedefName = typeDefinition.getQName().getLocalName();
316 final TypeDefinition<?> baseTypeDef = baseTypeDefForExtendedType(typeDefinition);
317 Type returnType = javaTypeForLeafrefOrIdentityRef(baseTypeDef, typeDefinition, lenient);
318 if (returnType == null) {
319 if (baseTypeDef instanceof EnumTypeDefinition) {
320 final EnumTypeDefinition enumTypeDef = (EnumTypeDefinition) baseTypeDef;
321 returnType = provideTypeForEnum(enumTypeDef, typedefName, typeDefinition);
323 final Module module = findParentModule(schemaContext, typeDefinition);
324 final Restrictions r = BindingGeneratorUtil.getRestrictions(typeDefinition);
325 if (module != null) {
326 final Map<Optional<Revision>, Map<String, Type>> modulesByDate = genTypeDefsContextMap.get(
328 final Map<String, Type> genTOs = modulesByDate.get(module.getRevision());
329 if (genTOs != null) {
330 returnType = genTOs.get(typedefName);
332 if (returnType == null) {
333 returnType = BaseYangTypes.BASE_YANG_TYPES_PROVIDER.javaTypeForSchemaDefinitionType(
334 baseTypeDef, typeDefinition, r, lenient);
343 * Seeks for identity reference <code>idref</code> the JAVA <code>type</code>.
347 * If identy which is referenced via <code>idref</code> has name <b>Idn</b>
348 * then returning type is <b>{@code Class<? extends Idn>}</b></i>
350 * @param idref identityref type definition for which JAVA <code>Type</code> is sought
351 * @return JAVA <code>Type</code> of the identity which is referenced through <code>idref</code>
353 private Type provideTypeForIdentityref(final IdentityrefTypeDefinition idref) {
354 final Collection<IdentitySchemaNode> identities = idref.getIdentities();
355 if (identities.size() > 1) {
356 LOG.warn("Identity reference {} has multiple identities, using only the first one", idref);
359 final QName baseIdQName = identities.iterator().next().getQName();
360 final Module module = schemaContext.findModule(baseIdQName.getModule()).orElse(null);
361 IdentitySchemaNode identity = null;
362 for (IdentitySchemaNode id : module.getIdentities()) {
363 if (id.getQName().equals(baseIdQName)) {
367 Preconditions.checkArgument(identity != null, "Target identity '" + baseIdQName + "' do not exists");
369 final String basePackageName = BindingMapping.getRootPackageName(module.getQNameModule());
370 final JavaTypeName identifier = JavaTypeName.create(BindingGeneratorUtil.packageNameForGeneratedType(
371 basePackageName, identity.getPath()), BindingMapping.getClassName(identity.getQName()));
372 return Types.classType(Types.wildcardTypeFor(identifier));
376 * Converts <code>typeDefinition</code> to concrete JAVA <code>Type</code>.
378 * @param typeDefinition
379 * type definition which should be converted to JAVA
381 * @return JAVA <code>Type</code> which represents
382 * <code>typeDefinition</code>
383 * @throws IllegalArgumentException
385 * <li>if <code>typeDefinition</code> equal null</li>
386 * <li>if Q name of <code>typeDefinition</code></li>
387 * <li>if name of <code>typeDefinition</code></li>
390 public Type generatedTypeForExtendedDefinitionType(final TypeDefinition<?> typeDefinition,
391 final SchemaNode parentNode) {
392 Preconditions.checkArgument(typeDefinition != null, "Type Definition cannot be NULL!");
393 if (typeDefinition.getQName() == null) {
394 throw new IllegalArgumentException("Type Definition cannot have unspecified QName (QName cannot be NULL!)");
396 Preconditions.checkArgument(typeDefinition.getQName().getLocalName() != null,
397 "Type Definitions Local Name cannot be NULL!");
399 final TypeDefinition<?> baseTypeDef = baseTypeDefForExtendedType(typeDefinition);
400 if (baseTypeDef instanceof LeafrefTypeDefinition || baseTypeDef instanceof IdentityrefTypeDefinition) {
402 * This is backwards compatibility baggage from way back when. The problem at hand is inconsistency between
403 * the fact that identity is mapped to a Class, which is also returned from leaves which specify it like
416 * This results in getFoo() returning Class<? extends Iden>, which looks fine on the surface, but gets more
417 * dicey when we throw in:
431 * Now we have competing requirements: typedef would like us to use encapsulation to capture the defined
432 * type, while getBar() wants us to retain shape with getFoo(), as it should not matter how the identityref
435 * In this particular case getFoo() won just after the Binding Spec was frozen, hence we do not generate
436 * an encapsulation for identityref typedefs.
438 * In case you are thinking we could get by having foo-ref map to a subclass of Iden, that is not a good
439 * option, as it would look as though it is the product of a different construct:
445 * Leading to a rather nice namespace clash and also slight incompatibility with unknown third-party
446 * sub-identities of iden.
448 * The story behind leafrefs is probably similar, but that needs to be ascertained.
453 final Module module = findParentModule(schemaContext, parentNode);
454 if (module != null) {
455 final Map<Optional<Revision>, Map<String, Type>> modulesByDate = genTypeDefsContextMap.get(
457 final Map<String, Type> genTOs = modulesByDate.get(module.getRevision());
458 if (genTOs != null) {
459 return genTOs.get(typeDefinition.getQName().getLocalName());
466 * Gets base type definition for <code>extendTypeDef</code>. The method is
467 * recursively called until non <code>ExtendedType</code> type is found.
469 * @param extendTypeDef
470 * type definition for which is the base type definition sought
471 * @return type definition which is base type for <code>extendTypeDef</code>
472 * @throws IllegalArgumentException
473 * if <code>extendTypeDef</code> equal null
475 private static TypeDefinition<?> baseTypeDefForExtendedType(final TypeDefinition<?> extendTypeDef) {
476 Preconditions.checkArgument(extendTypeDef != null, "Type Definition reference cannot be NULL!");
478 TypeDefinition<?> ret = extendTypeDef;
479 while (ret.getBaseType() != null) {
480 ret = ret.getBaseType();
487 * Converts <code>leafrefType</code> to JAVA <code>Type</code>. The path of <code>leafrefType</code> is followed
488 * to find referenced node and its <code>Type</code> is returned.
490 * @param leafrefType leafref type definition for which is the type sought
491 * @param parentNode parent node of the leaf being resolved
492 * @param inGrouping true if we are resolving the type within a grouping.
493 * @return JAVA <code>Type</code> of data schema node which is referenced in <code>leafrefType</code>
494 * @throws IllegalArgumentException
496 * <li>if <code>leafrefType</code> equal null</li>
497 * <li>if path statement of <code>leafrefType</code> equal null</li>
500 public Type provideTypeForLeafref(final LeafrefTypeDefinition leafrefType, final SchemaNode parentNode,
501 final boolean inGrouping) {
502 Preconditions.checkArgument(leafrefType != null, "Leafref Type Definition reference cannot be NULL!");
504 final RevisionAwareXPath xpath = leafrefType.getPathStatement();
505 Preconditions.checkArgument(xpath != null, "The Path Statement for Leafref Type Definition cannot be NULL!");
507 final String strXPath = verifyNotNull(xpath.toString());
508 if (strXPath.indexOf('[') != -1) {
509 // XXX: why are we special-casing this?
510 return Types.objectType();
513 final Module module = findParentModule(schemaContext, parentNode);
514 Preconditions.checkArgument(module != null, "Failed to find module for parent %s", parentNode);
516 final SchemaNode dataNode;
517 if (xpath.isAbsolute()) {
518 dataNode = findDataSchemaNode(schemaContext, module, xpath);
520 dataNode = findDataSchemaNodeForRelativeXPath(schemaContext, module, parentNode, xpath);
521 if (dataNode == null && inGrouping) {
522 // Relative path within a grouping may end up being unresolvable because it may refer outside
523 // the grouping, in which case it is polymorphic based on instantiation, for example:
546 LOG.debug("Leafref type {} not found in parent {}, assuming polymorphic object", leafrefType,
548 return Types.objectType();
551 Preconditions.checkArgument(dataNode != null, "Failed to find leafref target: %s in module %s (%s)",
552 strXPath, this.getParentModule(parentNode).getName(), parentNode.getQName().getModule());
554 // FIXME: this block seems to be some weird magic hack. Analyze and refactor it.
555 Type returnType = null;
556 if (leafContainsEnumDefinition(dataNode)) {
557 returnType = referencedTypes.get(dataNode.getPath());
558 } else if (leafListContainsEnumDefinition(dataNode)) {
559 returnType = Types.listTypeFor(referencedTypes.get(dataNode.getPath()));
561 if (returnType == null) {
562 returnType = resolveTypeFromDataSchemaNode(dataNode);
564 Preconditions.checkArgument(returnType != null, "Failed to find leafref target: %s in module %s (%s)",
565 strXPath, this.getParentModule(parentNode).getName(), parentNode.getQName().getModule(), this);
570 * Checks if <code>dataNode</code> is <code>LeafSchemaNode</code> and if it so then checks if it is of type
571 * <code>EnumTypeDefinition</code>.
573 * @param dataNode data schema node for which is checked if it is leaf and if it is of enum type
574 * @return boolean value
576 * <li>true - if <code>dataNode</code> is leaf of type enumeration</li>
577 * <li>false - other cases</li>
580 private static boolean leafContainsEnumDefinition(final SchemaNode dataNode) {
581 if (dataNode instanceof LeafSchemaNode) {
582 final LeafSchemaNode leaf = (LeafSchemaNode) dataNode;
583 return CompatUtils.compatLeafType(leaf) instanceof EnumTypeDefinition;
589 * Checks if <code>dataNode</code> is <code>LeafListSchemaNode</code> and if it so then checks if it is of type
590 * <code>EnumTypeDefinition</code>.
592 * @param dataNode data schema node for which is checked if it is leaflist and if it is of enum type
593 * @return boolean value
595 * <li>true - if <code>dataNode</code> is leaflist of type
597 * <li>false - other cases</li>
600 private static boolean leafListContainsEnumDefinition(final SchemaNode dataNode) {
601 if (dataNode instanceof LeafListSchemaNode) {
602 final LeafListSchemaNode leafList = (LeafListSchemaNode) dataNode;
603 return leafList.getType() instanceof EnumTypeDefinition;
609 * Converts <code>enumTypeDef</code> to {@link Enumeration enumeration}.
611 * @param enumTypeDef enumeration type definition which is converted to enumeration
612 * @param enumName string with name which is used as the enumeration name
613 * @return enumeration type which is built with data (name, enum values) from <code>enumTypeDef</code>
614 * @throws IllegalArgumentException
616 * <li>if <code>enumTypeDef</code> equals null</li>
617 * <li>if enum values of <code>enumTypeDef</code> equal null</li>
618 * <li>if Q name of <code>enumTypeDef</code> equal null</li>
619 * <li>if name of <code>enumTypeDef</code> equal null</li>
622 private Enumeration provideTypeForEnum(final EnumTypeDefinition enumTypeDef, final String enumName,
623 final SchemaNode parentNode) {
624 Preconditions.checkArgument(enumTypeDef != null, "EnumTypeDefinition reference cannot be NULL!");
625 Preconditions.checkArgument(enumTypeDef.getValues() != null,
626 "EnumTypeDefinition MUST contain at least ONE value definition!");
627 Preconditions.checkArgument(enumTypeDef.getQName() != null, "EnumTypeDefinition MUST contain NON-NULL QName!");
628 Preconditions.checkArgument(enumTypeDef.getQName().getLocalName() != null,
629 "Local Name in EnumTypeDefinition QName cannot be NULL!");
631 final Module module = findParentModule(schemaContext, parentNode);
632 final AbstractEnumerationBuilder enumBuilder = newEnumerationBuilder(JavaTypeName.create(
633 BindingMapping.getRootPackageName(module.getQNameModule()), BindingMapping.getClassName(enumName)));
634 addEnumDescription(enumBuilder, enumTypeDef);
635 enumTypeDef.getReference().ifPresent(enumBuilder::setReference);
636 enumBuilder.setModuleName(module.getName());
637 enumBuilder.setSchemaPath(enumTypeDef.getPath());
638 enumBuilder.updateEnumPairsFromEnumTypeDef(enumTypeDef);
639 return enumBuilder.toInstance(null);
643 * Adds enumeration to <code>typeBuilder</code>. The enumeration data are taken from <code>enumTypeDef</code>.
645 * @param enumTypeDef enumeration type definition is source of enumeration data for <code>typeBuilder</code>
646 * @param enumName string with the name of enumeration
647 * @param typeBuilder generated type builder to which is enumeration added
648 * @return enumeration type which contains enumeration data form <code>enumTypeDef</code>
649 * @throws IllegalArgumentException
651 * <li>if <code>enumTypeDef</code> equals null</li>
652 * <li>if enum values of <code>enumTypeDef</code> equal null</li>
653 * <li>if Q name of <code>enumTypeDef</code> equal null</li>
654 * <li>if name of <code>enumTypeDef</code> equal null</li>
655 * <li>if name of <code>typeBuilder</code> equal null</li>
659 private Enumeration addInnerEnumerationToTypeBuilder(final EnumTypeDefinition enumTypeDef,
660 final String enumName, final GeneratedTypeBuilderBase<?> typeBuilder) {
661 Preconditions.checkArgument(enumTypeDef != null, "EnumTypeDefinition reference cannot be NULL!");
662 Preconditions.checkArgument(enumTypeDef.getValues() != null,
663 "EnumTypeDefinition MUST contain at least ONE value definition!");
664 Preconditions.checkArgument(enumTypeDef.getQName() != null, "EnumTypeDefinition MUST contain NON-NULL QName!");
665 Preconditions.checkArgument(enumTypeDef.getQName().getLocalName() != null,
666 "Local Name in EnumTypeDefinition QName cannot be NULL!");
667 Preconditions.checkArgument(typeBuilder != null, "Generated Type Builder reference cannot be NULL!");
669 final EnumBuilder enumBuilder = typeBuilder.addEnumeration(BindingMapping.getClassName(enumName));
671 addEnumDescription(enumBuilder, enumTypeDef);
672 enumBuilder.updateEnumPairsFromEnumTypeDef(enumTypeDef);
673 return enumBuilder.toInstance(enumBuilder);
676 public abstract void addEnumDescription(EnumBuilder enumBuilder, EnumTypeDefinition enumTypeDef);
678 public abstract AbstractEnumerationBuilder newEnumerationBuilder(JavaTypeName identifier);
680 public abstract GeneratedTOBuilder newGeneratedTOBuilder(JavaTypeName identifier);
682 public abstract GeneratedTypeBuilder newGeneratedTypeBuilder(JavaTypeName identifier);
685 * Converts the pattern constraints to the list of the strings which represents these constraints.
687 * @param patternConstraints list of pattern constraints
688 * @return list of strings which represents the constraint patterns
690 public abstract Map<String, String> resolveRegExpressions(List<PatternConstraint> patternConstraints);
692 abstract void addCodegenInformation(GeneratedTypeBuilderBase<?> genTOBuilder, TypeDefinition<?> typeDef);
695 * Converts the pattern constraints from <code>typedef</code> to the list of the strings which represents these
698 * @param typedef extended type in which are the pattern constraints sought
699 * @return list of strings which represents the constraint patterns
700 * @throws IllegalArgumentException if <code>typedef</code> equals null
703 private Map<String, String> resolveRegExpressionsFromTypedef(final TypeDefinition<?> typedef) {
704 if (!(typedef instanceof StringTypeDefinition)) {
705 return ImmutableMap.of();
708 // TODO: run diff against base ?
709 return resolveRegExpressions(((StringTypeDefinition) typedef).getPatternConstraints());
713 * Converts <code>dataNode</code> to JAVA <code>Type</code>.
715 * @param dataNode contains information about YANG type
716 * @return JAVA <code>Type</code> representation of <code>dataNode</code>
718 private Type resolveTypeFromDataSchemaNode(final SchemaNode dataNode) {
719 Type returnType = null;
720 if (dataNode != null) {
721 if (dataNode instanceof LeafSchemaNode) {
722 final LeafSchemaNode leaf = (LeafSchemaNode) dataNode;
723 final TypeDefinition<?> type = CompatUtils.compatLeafType(leaf);
724 returnType = javaTypeForSchemaDefinitionType(type, leaf);
725 } else if (dataNode instanceof LeafListSchemaNode) {
726 final LeafListSchemaNode leafList = (LeafListSchemaNode) dataNode;
727 returnType = javaTypeForSchemaDefinitionType(leafList.getType(), leafList);
734 * Passes through all modules and through all its type definitions and convert it to generated types.
737 * The modules are first sorted by mutual dependencies. The modules are sequentially passed. All type definitions
738 * of a module are at the beginning sorted so that type definition with less amount of references to other type
739 * definition are processed first.<br>
740 * For each module is created mapping record in the map
741 * {@link AbstractTypeProvider#genTypeDefsContextMap genTypeDefsContextMap}
742 * which map current module name to the map which maps type names to returned types (generated types).
744 private void resolveTypeDefsFromContext() {
745 final Set<Module> modules = schemaContext.getModules();
746 Preconditions.checkArgument(modules != null, "Set of Modules cannot be NULL!");
747 final List<Module> modulesSortedByDependency = ModuleDependencySort.sort(modules);
749 for (Module module : modulesSortedByDependency) {
750 Map<Optional<Revision>, Map<String, Type>> dateTypeMap = genTypeDefsContextMap.computeIfAbsent(
751 module.getName(), key -> new HashMap<>());
752 dateTypeMap.put(module.getRevision(), Collections.emptyMap());
753 genTypeDefsContextMap.put(module.getName(), dateTypeMap);
756 for (Module module : modulesSortedByDependency) {
757 if (module != null) {
758 final String basePackageName = BindingMapping.getRootPackageName(module.getQNameModule());
759 if (basePackageName != null) {
760 final List<TypeDefinition<?>> typeDefinitions = TypedefResolver.getAllTypedefs(module);
761 for (TypeDefinition<?> typedef : sortTypeDefinitionAccordingDepth(typeDefinitions)) {
762 typedefToGeneratedType(basePackageName, module, typedef);
770 * Create Type for specified type definition.
772 * @param basePackageName string with name of package to which the module belongs
773 * @param module string with the name of the module for to which the <code>typedef</code> belongs
774 * @param typedef type definition of the node for which should be created JAVA <code>Type</code>
775 * (usually generated TO)
776 * @return JAVA <code>Type</code> representation of <code>typedef</code> or
777 * <code>null</code> value if <code>basePackageName</code> or
778 * <code>modulName</code> or <code>typedef</code> or Q name of
779 * <code>typedef</code> equals <code>null</code>
781 private Type typedefToGeneratedType(final String basePackageName, final Module module,
782 final TypeDefinition<?> typedef) {
783 final TypeDefinition<?> baseTypedef = typedef.getBaseType();
785 // See generatedTypeForExtendedDefinitionType() above for rationale behind this special case.
786 if (baseTypedef instanceof LeafrefTypeDefinition || baseTypedef instanceof IdentityrefTypeDefinition) {
790 final String typedefName = typedef.getQName().getLocalName();
792 final Type returnType;
793 if (baseTypedef.getBaseType() != null) {
794 returnType = provideGeneratedTOFromExtendedType(typedef, baseTypedef, basePackageName,
796 } else if (baseTypedef instanceof UnionTypeDefinition) {
797 final GeneratedTOBuilder genTOBuilder = provideGeneratedTOBuilderForUnionTypeDef(
798 JavaTypeName.create(basePackageName, BindingMapping.getClassName(typedef.getQName())),
799 (UnionTypeDefinition) baseTypedef, typedef);
800 genTOBuilder.setTypedef(true);
801 genTOBuilder.setIsUnion(true);
802 addUnitsToGenTO(genTOBuilder, typedef.getUnits().orElse(null));
803 makeSerializable(genTOBuilder);
804 returnType = genTOBuilder.build();
806 // Define a corresponding union builder. Typedefs are always anchored at a Java package root,
807 // so we are placing the builder alongside the union.
808 final GeneratedTOBuilder unionBuilder = newGeneratedTOBuilder(
809 JavaTypeName.create(genTOBuilder.getPackageName(), genTOBuilder.getName() + "Builder"));
810 unionBuilder.setIsUnionBuilder(true);
811 final MethodSignatureBuilder method = unionBuilder.addMethod("getDefaultInstance");
812 method.setReturnType(returnType);
813 method.addParameter(Types.STRING, "defaultValue");
814 method.setAccessModifier(AccessModifier.PUBLIC);
815 method.setStatic(true);
816 additionalTypes.computeIfAbsent(module, key -> new HashSet<>()).add(unionBuilder.build());
817 } else if (baseTypedef instanceof EnumTypeDefinition) {
818 // enums are automatically Serializable
819 final EnumTypeDefinition enumTypeDef = (EnumTypeDefinition) baseTypedef;
820 // TODO units for typedef enum
821 returnType = provideTypeForEnum(enumTypeDef, typedefName, typedef);
822 } else if (baseTypedef instanceof BitsTypeDefinition) {
823 final GeneratedTOBuilder genTOBuilder = provideGeneratedTOBuilderForBitsTypeDefinition(
824 JavaTypeName.create(basePackageName, BindingMapping.getClassName(typedef.getQName())),
825 (BitsTypeDefinition) baseTypedef, module.getName());
826 genTOBuilder.setTypedef(true);
827 addUnitsToGenTO(genTOBuilder, typedef.getUnits().orElse(null));
828 makeSerializable(genTOBuilder);
829 returnType = genTOBuilder.build();
831 final Type javaType = javaTypeForSchemaDefinitionType(baseTypedef, typedef);
832 returnType = wrapJavaTypeIntoTO(basePackageName, typedef, javaType, module.getName());
834 if (returnType != null) {
835 final Map<Optional<Revision>, Map<String, Type>> modulesByDate =
836 genTypeDefsContextMap.get(module.getName());
837 final Optional<Revision> moduleRevision = module.getRevision();
838 Map<String, Type> typeMap = modulesByDate.get(moduleRevision);
839 if (typeMap != null) {
840 if (typeMap.isEmpty()) {
841 typeMap = new HashMap<>(4);
842 modulesByDate.put(moduleRevision, typeMap);
844 typeMap.put(typedefName, returnType);
852 * Wraps base YANG type to generated TO.
854 * @param basePackageName string with name of package to which the module belongs
855 * @param typedef type definition which is converted to the TO
856 * @param javaType JAVA <code>Type</code> to which is <code>typedef</code> mapped
857 * @return generated transfer object which represent<code>javaType</code>
859 private GeneratedTransferObject wrapJavaTypeIntoTO(final String basePackageName, final TypeDefinition<?> typedef,
860 final Type javaType, final String moduleName) {
861 requireNonNull(javaType, "javaType cannot be null");
863 final GeneratedTOBuilder genTOBuilder = typedefToTransferObject(basePackageName, typedef, moduleName);
864 genTOBuilder.setRestrictions(BindingGeneratorUtil.getRestrictions(typedef));
865 final GeneratedPropertyBuilder genPropBuilder = genTOBuilder.addProperty("value");
866 genPropBuilder.setReturnType(javaType);
867 genTOBuilder.addEqualsIdentity(genPropBuilder);
868 genTOBuilder.addHashIdentity(genPropBuilder);
869 genTOBuilder.addToStringProperty(genPropBuilder);
870 genTOBuilder.addImplementsType(TYPE_OBJECT);
871 if (typedef.getStatus() == Status.DEPRECATED) {
872 genTOBuilder.addAnnotation("java.lang", "Deprecated");
874 if (javaType instanceof ConcreteType && "String".equals(javaType.getName()) && typedef.getBaseType() != null) {
875 addStringRegExAsConstant(genTOBuilder, resolveRegExpressionsFromTypedef(typedef));
877 addUnitsToGenTO(genTOBuilder, typedef.getUnits().orElse(null));
878 genTOBuilder.setTypedef(true);
879 makeSerializable(genTOBuilder);
880 return genTOBuilder.build();
884 * Converts output list of generated TO builders to one TO builder (first
885 * from list) which contains the remaining builders as its enclosing TO.
887 * @param typeName new type identifier
888 * @param typedef type definition which should be of type {@link UnionTypeDefinition}
889 * @return generated TO builder with the list of enclosed generated TO builders
891 public GeneratedTOBuilder provideGeneratedTOBuilderForUnionTypeDef(final JavaTypeName typeName,
892 final UnionTypeDefinition typedef, final TypeDefinition<?> parentNode) {
893 final List<GeneratedTOBuilder> builders = provideGeneratedTOBuildersForUnionTypeDef(typeName, typedef,
895 Preconditions.checkState(!builders.isEmpty(), "No GeneratedTOBuilder objects generated from union %s", typedef);
897 final GeneratedTOBuilder resultTOBuilder = builders.remove(0);
898 builders.forEach(resultTOBuilder::addEnclosingTransferObject);
899 return resultTOBuilder;
903 * Converts <code>typedef</code> to generated TO with <code>typeDefName</code>. Every union type from
904 * <code>typedef</code> is added to generated TO builder as property.
906 * @param typeName new type identifier
907 * @param typedef type definition which should be of type <code>UnionTypeDefinition</code>
908 * @return generated TO builder which represents <code>typedef</code>
909 * @throws NullPointerException
911 * <li>if <code>basePackageName</code> is null</li>
912 * <li>if <code>typedef</code> is null</li>
913 * <li>if Qname of <code>typedef</code> is null</li>
916 public List<GeneratedTOBuilder> provideGeneratedTOBuildersForUnionTypeDef(final JavaTypeName typeName,
917 final UnionTypeDefinition typedef, final SchemaNode parentNode) {
918 requireNonNull(typedef, "Type Definition cannot be NULL!");
919 requireNonNull(typedef.getQName(), "Type definition QName cannot be NULL!");
921 final List<GeneratedTOBuilder> generatedTOBuilders = new ArrayList<>();
922 final List<TypeDefinition<?>> unionTypes = typedef.getTypes();
923 final Module module = findParentModule(schemaContext, parentNode);
925 final GeneratedTOBuilder unionGenTOBuilder = newGeneratedTOBuilder(typeName);
926 unionGenTOBuilder.setIsUnion(true);
927 unionGenTOBuilder.setSchemaPath(typedef.getPath());
928 unionGenTOBuilder.setModuleName(module.getName());
929 unionGenTOBuilder.addImplementsType(TYPE_OBJECT);
930 addCodegenInformation(unionGenTOBuilder, typedef);
931 generatedTOBuilders.add(unionGenTOBuilder);
933 // Pattern string is the key, XSD regex is the value. The reason for this choice is that the pattern carries
934 // also negation information and hence guarantees uniqueness.
935 final Map<String, String> expressions = new HashMap<>();
936 for (TypeDefinition<?> unionType : unionTypes) {
937 final String unionTypeName = unionType.getQName().getLocalName();
939 // If we have a base type we should follow the type definition backwards, except for identityrefs, as those
940 // do not follow type encapsulation -- we use the general case for that.
941 if (unionType.getBaseType() != null && !(unionType instanceof IdentityrefTypeDefinition)) {
942 resolveExtendedSubtypeAsUnion(unionGenTOBuilder, unionType, expressions, parentNode);
943 } else if (unionType instanceof UnionTypeDefinition) {
944 generatedTOBuilders.addAll(resolveUnionSubtypeAsUnion(unionGenTOBuilder,
945 (UnionTypeDefinition) unionType, parentNode));
946 } else if (unionType instanceof EnumTypeDefinition) {
947 final Enumeration enumeration = addInnerEnumerationToTypeBuilder((EnumTypeDefinition) unionType,
948 unionTypeName, unionGenTOBuilder);
949 updateUnionTypeAsProperty(unionGenTOBuilder, enumeration, unionTypeName);
951 final Type javaType = javaTypeForSchemaDefinitionType(unionType, parentNode);
952 updateUnionTypeAsProperty(unionGenTOBuilder, javaType, unionTypeName);
955 addStringRegExAsConstant(unionGenTOBuilder, expressions);
957 storeGenTO(typedef, unionGenTOBuilder, parentNode);
959 return generatedTOBuilders;
963 * Wraps code which handles the case when union subtype is also of the type <code>UnionType</code>.
966 * In this case the new generated TO is created for union subtype (recursive call of method
967 * {@link #provideGeneratedTOBuildersForUnionTypeDef(String, UnionTypeDefinition, String, SchemaNode)}
968 * provideGeneratedTOBuilderForUnionTypeDef} and in parent TO builder <code>parentUnionGenTOBuilder</code> is
969 * created property which type is equal to new generated TO.
971 * @param parentUnionGenTOBuilder generated TO builder to which is the property with the child union subtype added
972 * @param basePackageName string with the name of the module package
973 * @param unionSubtype type definition which represents union subtype
974 * @return list of generated TO builders. The number of the builders can be bigger one due to recursive call of
975 * <code>provideGeneratedTOBuildersForUnionTypeDef</code> method.
977 private List<GeneratedTOBuilder> resolveUnionSubtypeAsUnion(final GeneratedTOBuilder parentUnionGenTOBuilder,
978 final UnionTypeDefinition unionSubtype, final SchemaNode parentNode) {
979 final JavaTypeName newTOBuilderName = parentUnionGenTOBuilder.getIdentifier().createSibling(
980 provideAvailableNameForGenTOBuilder(parentUnionGenTOBuilder.getName()));
981 final List<GeneratedTOBuilder> subUnionGenTOBUilders = provideGeneratedTOBuildersForUnionTypeDef(
982 newTOBuilderName, unionSubtype, parentNode);
984 final GeneratedPropertyBuilder propertyBuilder;
985 propertyBuilder = parentUnionGenTOBuilder.addProperty(BindingMapping.getPropertyName(
986 newTOBuilderName.simpleName()));
987 propertyBuilder.setReturnType(subUnionGenTOBUilders.get(0).build());
988 parentUnionGenTOBuilder.addEqualsIdentity(propertyBuilder);
989 parentUnionGenTOBuilder.addToStringProperty(propertyBuilder);
991 return subUnionGenTOBUilders;
995 * Wraps code which handle case when union subtype is of the type <code>ExtendedType</code>. If TO for this type
996 * already exists it is used for the creation of the property in <code>parentUnionGenTOBuilder</code>. Otherwise
997 * the base type is used for the property creation.
999 * @param parentUnionGenTOBuilder generated TO builder in which new property is created
1000 * @param unionSubtype type definition of the <code>ExtendedType</code> type which represents union subtype
1001 * @param expressions list of strings with the regular expressions
1002 * @param parentNode parent Schema Node for Extended Subtype
1004 private void resolveExtendedSubtypeAsUnion(final GeneratedTOBuilder parentUnionGenTOBuilder,
1005 final TypeDefinition<?> unionSubtype, final Map<String, String> expressions, final SchemaNode parentNode) {
1006 final String unionTypeName = unionSubtype.getQName().getLocalName();
1007 final Type genTO = findGenTO(unionTypeName, unionSubtype);
1008 if (genTO != null) {
1009 updateUnionTypeAsProperty(parentUnionGenTOBuilder, genTO, genTO.getName());
1013 final TypeDefinition<?> baseType = baseTypeDefForExtendedType(unionSubtype);
1014 if (unionTypeName.equals(baseType.getQName().getLocalName())) {
1015 final Type javaType = BaseYangTypes.BASE_YANG_TYPES_PROVIDER.javaTypeForSchemaDefinitionType(baseType,
1016 parentNode, BindingGeneratorUtil.getRestrictions(unionSubtype));
1017 if (javaType != null) {
1018 updateUnionTypeAsProperty(parentUnionGenTOBuilder, javaType, unionTypeName);
1020 } else if (baseType instanceof LeafrefTypeDefinition) {
1021 final Type javaType = javaTypeForSchemaDefinitionType(baseType, parentNode);
1022 boolean typeExist = false;
1023 for (GeneratedPropertyBuilder generatedPropertyBuilder : parentUnionGenTOBuilder.getProperties()) {
1024 final Type origType = ((GeneratedPropertyBuilderImpl) generatedPropertyBuilder).getReturnType();
1025 if (origType != null && javaType != null && javaType == origType) {
1030 if (!typeExist && javaType != null) {
1031 updateUnionTypeAsProperty(parentUnionGenTOBuilder, javaType,
1032 javaType.getName() + parentUnionGenTOBuilder.getName() + "Value");
1035 if (baseType instanceof StringTypeDefinition) {
1036 expressions.putAll(resolveRegExpressionsFromTypedef(unionSubtype));
1041 * Searches for generated TO for <code>searchedTypeDef</code> type definition
1042 * in {@link #genTypeDefsContextMap genTypeDefsContextMap}.
1044 * @param searchedTypeName string with name of <code>searchedTypeDef</code>
1045 * @return generated TO for <code>searchedTypeDef</code> or <code>null</code> it it doesn't exist
1047 private Type findGenTO(final String searchedTypeName, final SchemaNode parentNode) {
1048 final Module typeModule = findParentModule(schemaContext, parentNode);
1049 if (typeModule != null && typeModule.getName() != null) {
1050 final Map<Optional<Revision>, Map<String, Type>> modulesByDate = genTypeDefsContextMap.get(
1051 typeModule.getName());
1052 final Map<String, Type> genTOs = modulesByDate.get(typeModule.getRevision());
1053 if (genTOs != null) {
1054 return genTOs.get(searchedTypeName);
1061 * Stores generated TO created from <code>genTOBuilder</code> for <code>newTypeDef</code>
1062 * to {@link #genTypeDefsContextMap genTypeDefsContextMap} if the module for <code>newTypeDef</code> exists.
1064 * @param newTypeDef type definition for which is <code>genTOBuilder</code> created
1065 * @param genTOBuilder generated TO builder which is converted to generated TO and stored
1067 private void storeGenTO(final TypeDefinition<?> newTypeDef, final GeneratedTOBuilder genTOBuilder,
1068 final SchemaNode parentNode) {
1069 if (!(newTypeDef instanceof UnionTypeDefinition)) {
1070 final Module parentModule = findParentModule(schemaContext, parentNode);
1071 if (parentModule != null && parentModule.getName() != null) {
1072 final Map<Optional<Revision>, Map<String, Type>> modulesByDate = genTypeDefsContextMap.get(
1073 parentModule.getName());
1074 final Map<String, Type> genTOsMap = modulesByDate.get(parentModule.getRevision());
1075 genTOsMap.put(newTypeDef.getQName().getLocalName(), genTOBuilder.build());
1081 * Adds a new property with the name <code>propertyName</code> and with type <code>type</code>
1082 * to <code>unonGenTransObject</code>.
1084 * @param unionGenTransObject generated TO to which should be property added
1085 * @param type JAVA <code>type</code> of the property which should be added to <code>unionGentransObject</code>
1086 * @param propertyName string with name of property which should be added to <code>unionGentransObject</code>
1088 private static void updateUnionTypeAsProperty(final GeneratedTOBuilder unionGenTransObject, final Type type,
1089 final String propertyName) {
1090 if (unionGenTransObject != null && type != null && !unionGenTransObject.containsProperty(propertyName)) {
1091 final GeneratedPropertyBuilder propBuilder = unionGenTransObject
1092 .addProperty(BindingMapping.getPropertyName(propertyName));
1093 propBuilder.setReturnType(type);
1095 unionGenTransObject.addEqualsIdentity(propBuilder);
1096 unionGenTransObject.addHashIdentity(propBuilder);
1097 unionGenTransObject.addToStringProperty(propBuilder);
1102 * Converts <code>typedef</code> to the generated TO builder.
1104 * @param basePackageName string with name of package to which the module belongs
1105 * @param typedef type definition from which is the generated TO builder created
1106 * @return generated TO builder which contains data from <code>typedef</code> and <code>basePackageName</code>
1108 private GeneratedTOBuilder typedefToTransferObject(final String basePackageName,
1109 final TypeDefinition<?> typedef, final String moduleName) {
1110 JavaTypeName name = renames.get(typedef);
1112 name = JavaTypeName.create(
1113 BindingGeneratorUtil.packageNameForGeneratedType(basePackageName, typedef.getPath()),
1114 BindingMapping.getClassName(typedef.getQName().getLocalName()));
1117 final GeneratedTOBuilder newType = newGeneratedTOBuilder(name);
1118 newType.setSchemaPath(typedef.getPath());
1119 newType.setModuleName(moduleName);
1120 addCodegenInformation(newType, typedef);
1125 * Converts <code>typeDef</code> which should be of the type <code>BitsTypeDefinition</code>
1126 * to <code>GeneratedTOBuilder</code>. All the bits of the typeDef are added to returning generated TO as
1129 * @param typeName new type identifier
1130 * @param typeDef type definition from which is the generated TO builder created
1131 * @return generated TO builder which represents <code>typeDef</code>
1132 * @throws IllegalArgumentException
1134 * <li>if <code>typeDef</code> equals null</li>
1135 * <li>if <code>basePackageName</code> equals null</li>
1138 public GeneratedTOBuilder provideGeneratedTOBuilderForBitsTypeDefinition(final JavaTypeName typeName,
1139 final BitsTypeDefinition typeDef, final String moduleName) {
1140 final GeneratedTOBuilder genTOBuilder = newGeneratedTOBuilder(typeName);
1141 genTOBuilder.setSchemaPath(typeDef.getPath());
1142 genTOBuilder.setModuleName(moduleName);
1143 genTOBuilder.setBaseType(typeDef);
1144 genTOBuilder.addImplementsType(TYPE_OBJECT);
1145 addCodegenInformation(genTOBuilder, typeDef);
1147 final List<Bit> bitList = typeDef.getBits();
1148 GeneratedPropertyBuilder genPropertyBuilder;
1149 for (Bit bit : bitList) {
1150 final String name = bit.getName();
1151 genPropertyBuilder = genTOBuilder.addProperty(BindingMapping.getPropertyName(name));
1152 genPropertyBuilder.setReadOnly(true);
1153 genPropertyBuilder.setReturnType(BaseYangTypes.BOOLEAN_TYPE);
1155 genTOBuilder.addEqualsIdentity(genPropertyBuilder);
1156 genTOBuilder.addHashIdentity(genPropertyBuilder);
1157 genTOBuilder.addToStringProperty(genPropertyBuilder);
1160 return genTOBuilder;
1164 * Adds to the <code>genTOBuilder</code> the constant which contains regular expressions from
1165 * the <code>regularExpressions</code>.
1167 * @param genTOBuilder generated TO builder to which are <code>regular expressions</code> added
1168 * @param expressions list of string which represent regular expressions
1170 private static void addStringRegExAsConstant(final GeneratedTOBuilder genTOBuilder,
1171 final Map<String, String> expressions) {
1172 if (!expressions.isEmpty()) {
1173 genTOBuilder.addConstant(Types.listTypeFor(BaseYangTypes.STRING_TYPE), TypeConstants.PATTERN_CONSTANT_NAME,
1174 ImmutableMap.copyOf(expressions));
1179 * Creates generated TO with data about inner extended type <code>innerExtendedType</code>, about the package name
1180 * <code>typedefName</code> and about the generated TO name <code>typedefName</code>.
1183 * It is assumed that <code>innerExtendedType</code> is already present in
1184 * {@link AbstractTypeProvider#genTypeDefsContextMap genTypeDefsContextMap} to be possible set it as extended type
1185 * for the returning generated TO.
1187 * @param typedef Type Definition
1188 * @param innerExtendedType extended type which is part of some other extended type
1189 * @param basePackageName string with the package name of the module
1190 * @param moduleName Module Name
1191 * @return generated TO which extends generated TO for <code>innerExtendedType</code>
1192 * @throws IllegalArgumentException
1194 * <li>if <code>extendedType</code> equals null</li>
1195 * <li>if <code>basePackageName</code> equals null</li>
1196 * <li>if <code>typedefName</code> equals null</li>
1199 private GeneratedTransferObject provideGeneratedTOFromExtendedType(final TypeDefinition<?> typedef,
1200 final TypeDefinition<?> innerExtendedType, final String basePackageName, final String moduleName) {
1201 Preconditions.checkArgument(innerExtendedType != null, "Extended type cannot be NULL!");
1202 Preconditions.checkArgument(basePackageName != null, "String with base package name cannot be NULL!");
1204 final GeneratedTOBuilder genTOBuilder = newGeneratedTOBuilder(JavaTypeName.create(basePackageName,
1205 BindingMapping.getClassName(typedef.getQName())));
1206 genTOBuilder.setSchemaPath(typedef.getPath());
1207 genTOBuilder.setModuleName(moduleName);
1208 genTOBuilder.setTypedef(true);
1209 addCodegenInformation(genTOBuilder, typedef);
1211 final Restrictions r = BindingGeneratorUtil.getRestrictions(typedef);
1212 genTOBuilder.setRestrictions(r);
1213 addStringRegExAsConstant(genTOBuilder, resolveRegExpressionsFromTypedef(typedef));
1215 if (typedef.getStatus() == Status.DEPRECATED) {
1216 genTOBuilder.addAnnotation("java.lang", "Deprecated");
1219 if (baseTypeDefForExtendedType(innerExtendedType) instanceof UnionTypeDefinition) {
1220 genTOBuilder.setIsUnion(true);
1223 Map<Optional<Revision>, Map<String, Type>> modulesByDate = null;
1224 Map<String, Type> typeMap = null;
1225 final Module parentModule = findParentModule(schemaContext, innerExtendedType);
1226 if (parentModule != null) {
1227 modulesByDate = genTypeDefsContextMap.get(parentModule.getName());
1228 typeMap = modulesByDate.get(parentModule.getRevision());
1231 if (typeMap != null) {
1232 final String innerTypeDef = innerExtendedType.getQName().getLocalName();
1233 final Type type = typeMap.get(innerTypeDef);
1234 if (type instanceof GeneratedTransferObject) {
1235 genTOBuilder.setExtendsType((GeneratedTransferObject) type);
1238 addUnitsToGenTO(genTOBuilder, typedef.getUnits().orElse(null));
1239 makeSerializable(genTOBuilder);
1241 return genTOBuilder.build();
1245 * Add {@link java.io.Serializable} to implemented interfaces of this TO. Also compute and add serialVersionUID
1248 * @param gto transfer object which needs to be made serializable
1250 private static void makeSerializable(final GeneratedTOBuilder gto) {
1251 gto.addImplementsType(Types.serializableType());
1252 final GeneratedPropertyBuilder prop = new GeneratedPropertyBuilderImpl("serialVersionUID");
1253 prop.setValue(Long.toString(BindingGeneratorUtil.computeDefaultSUID(gto)));
1258 * Finds out for each type definition how many immersion (depth) is necessary to get to the base type. Every type
1259 * definition is inserted to the map which key is depth and value is list of type definitions with equal depth.
1260 * In next step are lists from this map concatenated to one list in ascending order according to their depth. All
1261 * type definitions are in the list behind all type definitions on which depends.
1263 * @param unsortedTypeDefinitions list of type definitions which should be sorted by depth
1264 * @return list of type definitions sorted according their each other dependencies (type definitions which are
1265 * dependent on other type definitions are in list behind them).
1267 private static List<TypeDefinition<?>> sortTypeDefinitionAccordingDepth(
1268 final Collection<TypeDefinition<?>> unsortedTypeDefinitions) {
1269 final List<TypeDefinition<?>> sortedTypeDefinition = new ArrayList<>();
1271 final Map<Integer, List<TypeDefinition<?>>> typeDefinitionsDepths = new TreeMap<>();
1272 for (TypeDefinition<?> unsortedTypeDefinition : unsortedTypeDefinitions) {
1273 final Integer depth = getTypeDefinitionDepth(unsortedTypeDefinition);
1274 List<TypeDefinition<?>> typeDefinitionsConcreteDepth =
1275 typeDefinitionsDepths.computeIfAbsent(depth, k -> new ArrayList<>());
1276 typeDefinitionsConcreteDepth.add(unsortedTypeDefinition);
1279 // SortedMap guarantees order corresponding to keys in ascending order
1280 for (List<TypeDefinition<?>> v : typeDefinitionsDepths.values()) {
1281 sortedTypeDefinition.addAll(v);
1284 return sortedTypeDefinition;
1288 * Returns how many immersion is necessary to get from the type definition to the base type.
1290 * @param typeDefinition type definition for which is depth sought.
1291 * @return number of immersions which are necessary to get from the type definition to the base type
1293 private static int getTypeDefinitionDepth(final TypeDefinition<?> typeDefinition) {
1294 // FIXME: rewrite this in a non-recursive manner
1295 if (typeDefinition == null) {
1298 final TypeDefinition<?> baseType = typeDefinition.getBaseType();
1299 if (baseType == null) {
1304 if (baseType.getBaseType() != null) {
1305 depth = depth + getTypeDefinitionDepth(baseType);
1306 } else if (baseType instanceof UnionTypeDefinition) {
1307 final List<TypeDefinition<?>> childTypeDefinitions = ((UnionTypeDefinition) baseType).getTypes();
1308 int maxChildDepth = 0;
1310 for (TypeDefinition<?> childTypeDefinition : childTypeDefinitions) {
1311 childDepth = childDepth + getTypeDefinitionDepth(childTypeDefinition);
1312 if (childDepth > maxChildDepth) {
1313 maxChildDepth = childDepth;
1316 return maxChildDepth;
1322 * Returns string which contains the same value as <code>name</code> but integer suffix is incremented by one. If
1323 * <code>name</code> contains no number suffix, a new suffix initialized at 1 is added. A suffix is actually
1324 * composed of a '$' marker, which is safe, as no YANG identifier can contain '$', and a unsigned decimal integer.
1326 * @param name string with name of augmented node
1327 * @return string with the number suffix incremented by one (or 1 is added)
1329 private static String provideAvailableNameForGenTOBuilder(final String name) {
1330 final int dollar = name.indexOf('$');
1335 final int newSuffix = Integer.parseUnsignedInt(name.substring(dollar + 1)) + 1;
1336 Preconditions.checkState(newSuffix > 0, "Suffix counter overflow");
1337 return name.substring(0, dollar + 1) + newSuffix;
1340 public static void addUnitsToGenTO(final GeneratedTOBuilder to, final String units) {
1341 if (!Strings.isNullOrEmpty(units)) {
1342 to.addConstant(Types.STRING, "_UNITS", "\"" + units + "\"");
1343 final GeneratedPropertyBuilder prop = new GeneratedPropertyBuilderImpl("UNITS");
1344 prop.setReturnType(Types.STRING);
1345 to.addToStringProperty(prop);
1350 public String getTypeDefaultConstruction(final LeafSchemaNode node) {
1351 return getTypeDefaultConstruction(node, (String) node.getType().getDefaultValue().orElse(null));
1354 public String getTypeDefaultConstruction(final LeafSchemaNode node, final String defaultValue) {
1355 final TypeDefinition<?> type = CompatUtils.compatLeafType(node);
1356 final QName typeQName = type.getQName();
1357 final TypeDefinition<?> base = baseTypeDefForExtendedType(type);
1358 requireNonNull(type, () -> "Cannot provide default construction for null type of " + node);
1359 requireNonNull(defaultValue, () -> "Cannot provide default construction for null default statement of "
1362 final StringBuilder sb = new StringBuilder();
1363 String result = null;
1364 if (base instanceof BinaryTypeDefinition) {
1365 result = binaryToDef(defaultValue);
1366 } else if (base instanceof BitsTypeDefinition) {
1369 final Module parent = getParentModule(node);
1370 final Iterator<QName> path = node.getPath().getPathFromRoot().iterator();
1372 if (!path.hasNext()) {
1373 parentName = BindingMapping.getClassName(parent.getName()) + "Data";
1374 final String basePackageName = BindingMapping.getRootPackageName(parent.getQNameModule());
1375 className = basePackageName + "." + parentName + "." + BindingMapping.getClassName(node.getQName());
1377 final String basePackageName = BindingMapping.getRootPackageName(parent.getQNameModule());
1378 final String packageName = BindingGeneratorUtil.packageNameForGeneratedType(basePackageName,
1380 parentName = BindingMapping.getClassName(parent.getName());
1381 className = packageName + "." + parentName + "." + BindingMapping.getClassName(node.getQName());
1383 result = bitsToDef((BitsTypeDefinition) base, className, defaultValue, type.getBaseType() != null);
1384 } else if (base instanceof BooleanTypeDefinition) {
1385 result = typeToBooleanDef(defaultValue);
1386 } else if (base instanceof DecimalTypeDefinition) {
1387 result = typeToDef(BigDecimal.class, defaultValue);
1388 } else if (base instanceof EmptyTypeDefinition) {
1389 result = typeToBooleanDef(defaultValue);
1390 } else if (base instanceof EnumTypeDefinition) {
1391 final char[] defValArray = defaultValue.toCharArray();
1392 final char first = Character.toUpperCase(defaultValue.charAt(0));
1393 defValArray[0] = first;
1394 final String newDefVal = new String(defValArray);
1396 if (type.getBaseType() != null) {
1397 final Module m = getParentModule(type);
1398 final String basePackageName = BindingMapping.getRootPackageName(m.getQNameModule());
1399 final String packageName = BindingGeneratorUtil.packageNameForGeneratedType(basePackageName,
1401 className = packageName + "." + BindingMapping.getClassName(typeQName);
1403 final Module parentModule = getParentModule(node);
1404 final String basePackageName = BindingMapping.getRootPackageName(parentModule.getQNameModule());
1405 final String packageName = BindingGeneratorUtil.packageNameForGeneratedType(basePackageName,
1407 className = packageName + "." + BindingMapping.getClassName(node.getQName());
1409 result = className + "." + newDefVal;
1410 } else if (base instanceof IdentityrefTypeDefinition) {
1411 throw new UnsupportedOperationException("Cannot get default construction for identityref type");
1412 } else if (base instanceof InstanceIdentifierTypeDefinition) {
1413 throw new UnsupportedOperationException("Cannot get default construction for instance-identifier type");
1414 } else if (BaseTypes.isInt8(base)) {
1415 result = typeToValueOfDef(Byte.class, defaultValue);
1416 } else if (BaseTypes.isInt16(base)) {
1417 result = typeToValueOfDef(Short.class, defaultValue);
1418 } else if (BaseTypes.isInt32(base)) {
1419 result = typeToValueOfDef(Integer.class, defaultValue);
1420 } else if (BaseTypes.isInt64(base)) {
1421 result = typeToValueOfDef(Long.class, defaultValue);
1422 } else if (base instanceof LeafrefTypeDefinition) {
1423 result = leafrefToDef(node, (LeafrefTypeDefinition) base, defaultValue);
1424 } else if (base instanceof StringTypeDefinition) {
1425 result = "\"" + defaultValue + "\"";
1426 } else if (BaseTypes.isUint8(base)) {
1427 result = typeToValueOfDef(Short.class, defaultValue);
1428 } else if (BaseTypes.isUint16(base)) {
1429 result = typeToValueOfDef(Integer.class, defaultValue);
1430 } else if (BaseTypes.isUint32(base)) {
1431 result = typeToValueOfDef(Long.class, defaultValue);
1432 } else if (BaseTypes.isUint64(base)) {
1433 switch (defaultValue) {
1435 result = "java.math.BigInteger.ZERO";
1438 result = "java.math.BigInteger.ONE";
1441 result = "java.math.BigInteger.TEN";
1444 result = typeToDef(BigInteger.class, defaultValue);
1446 } else if (base instanceof UnionTypeDefinition) {
1447 result = unionToDef(node);
1453 if (type.getBaseType() != null && !(base instanceof LeafrefTypeDefinition)
1454 && !(base instanceof EnumTypeDefinition) && !(base instanceof UnionTypeDefinition)) {
1455 final Module m = getParentModule(type);
1456 final String basePackageName = BindingMapping.getRootPackageName(m.getQNameModule());
1457 final String packageName = BindingGeneratorUtil.packageNameForGeneratedType(basePackageName,
1459 final String className = packageName + "." + BindingMapping.getClassName(typeQName);
1460 sb.insert(0, "new " + className + "(");
1461 sb.insert(sb.length(), ')');
1464 return sb.toString();
1467 private static String typeToDef(final Class<?> clazz, final String defaultValue) {
1468 return "new " + clazz.getName() + "(\"" + defaultValue + "\")";
1471 private static String typeToValueOfDef(final Class<?> clazz, final String defaultValue) {
1472 return clazz.getName() + ".valueOf(\"" + defaultValue + "\")";
1475 private static String typeToBooleanDef(final String defaultValue) {
1476 switch (defaultValue) {
1478 return "java.lang.Boolean.FALSE";
1480 return "java.lang.Boolean.TRUE";
1482 return typeToValueOfDef(Boolean.class, defaultValue);
1486 private static String binaryToDef(final String defaultValue) {
1487 final StringBuilder sb = new StringBuilder();
1488 final byte[] encoded = Base64.getDecoder().decode(defaultValue);
1489 sb.append("new byte[] {");
1490 for (int i = 0; i < encoded.length; i++) {
1491 sb.append(encoded[i]);
1492 if (i != encoded.length - 1) {
1497 return sb.toString();
1500 private static final Comparator<Bit> BIT_NAME_COMPARATOR = Comparator.comparing(Bit::getName);
1502 private static String bitsToDef(final BitsTypeDefinition type, final String className, final String defaultValue,
1503 final boolean isExt) {
1504 final List<Bit> bits = new ArrayList<>(type.getBits());
1505 bits.sort(BIT_NAME_COMPARATOR);
1506 final StringBuilder sb = new StringBuilder();
1509 sb.append(className);
1512 for (int i = 0; i < bits.size(); i++) {
1513 if (bits.get(i).getName().equals(defaultValue)) {
1518 if (i != bits.size() - 1) {
1525 return sb.toString();
1528 private Module getParentModule(final SchemaNode node) {
1529 final QName qname = node.getPath().getPathFromRoot().iterator().next();
1530 return schemaContext.findModule(qname.getModule()).orElse(null);
1533 private String leafrefToDef(final LeafSchemaNode parentNode, final LeafrefTypeDefinition leafrefType,
1534 final String defaultValue) {
1535 Preconditions.checkArgument(leafrefType != null, "Leafref Type Definition reference cannot be NULL!");
1536 Preconditions.checkArgument(leafrefType.getPathStatement() != null,
1537 "The Path Statement for Leafref Type Definition cannot be NULL!");
1539 final RevisionAwareXPath xpath = leafrefType.getPathStatement();
1540 final String strXPath = xpath.toString();
1542 if (strXPath != null) {
1543 if (strXPath.indexOf('[') == -1) {
1544 final Module module = findParentModule(schemaContext, parentNode);
1545 if (module != null) {
1546 final SchemaNode dataNode;
1547 if (xpath.isAbsolute()) {
1548 dataNode = findDataSchemaNode(schemaContext, module, xpath);
1550 dataNode = findDataSchemaNodeForRelativeXPath(schemaContext, module, parentNode, xpath);
1552 final String result = getTypeDefaultConstruction((LeafSchemaNode) dataNode, defaultValue);
1556 return "new java.lang.Object()";
1563 private String unionToDef(final LeafSchemaNode node) {
1564 final TypeDefinition<?> type = CompatUtils.compatLeafType(node);
1568 if (type.getBaseType() != null) {
1569 final QName typeQName = type.getQName();
1570 Module module = null;
1571 final Set<Module> modules = schemaContext.findModules(typeQName.getNamespace());
1572 if (modules.size() > 1) {
1573 for (Module m : modules) {
1574 if (m.getRevision().equals(typeQName.getRevision())) {
1579 if (module == null) {
1580 final List<Module> modulesList = new ArrayList<>(modules);
1581 modulesList.sort((o1, o2) -> Revision.compare(o1.getRevision(), o2.getRevision()));
1582 module = modulesList.get(0);
1585 module = modules.iterator().next();
1588 final String basePackageName = BindingMapping.getRootPackageName(module.getQNameModule());
1589 className = basePackageName + "." + BindingMapping.getClassName(typeQName);
1591 final Iterator<QName> path = node.getPath().getPathFromRoot().iterator();
1592 final QName first = path.next();
1593 final Module parent = schemaContext.findModule(first.getModule()).orElse(null);
1594 final String basePackageName = BindingMapping.getRootPackageName(parent.getQNameModule());
1595 if (!path.hasNext()) {
1596 parentName = BindingMapping.getClassName(parent.getName()) + "Data";
1597 className = basePackageName + "." + parentName + "." + BindingMapping.getClassName(node.getQName());
1599 final String packageName = BindingGeneratorUtil.packageNameForGeneratedType(basePackageName,
1601 className = packageName + "." + BindingMapping.getClassName(node.getQName());
1604 return union(className, (String) node.getType().getDefaultValue().orElse(null), node);
1607 private static String union(final String className, final String defaultValue, final LeafSchemaNode node) {
1608 final StringBuilder sb = new StringBuilder();
1610 sb.append(className);
1612 sb.append(defaultValue);
1613 sb.append("\".toCharArray())");
1614 return sb.toString();
1618 public String getConstructorPropertyName(final SchemaNode node) {
1619 return node instanceof TypeDefinition<?> ? "value" : "";
1623 public String getParamNameFromType(final TypeDefinition<?> type) {
1624 return BindingMapping.getPropertyName(type.getQName().getLocalName());