Enforce explicit generator linkage
[mdsal.git] / binding / mdsal-binding-generator / src / main / java / org / opendaylight / mdsal / binding / generator / impl / reactor / AbstractTypeObjectGenerator.java
1 /*
2  * Copyright (c) 2021 PANTHEON.tech, s.r.o. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.mdsal.binding.generator.impl.reactor;
9
10 import static com.google.common.base.Preconditions.checkArgument;
11 import static com.google.common.base.Verify.verify;
12 import static com.google.common.base.Verify.verifyNotNull;
13
14 import com.google.common.collect.ImmutableMap;
15 import com.google.common.collect.Maps;
16 import java.util.ArrayList;
17 import java.util.HashMap;
18 import java.util.List;
19 import java.util.Map;
20 import java.util.Optional;
21 import java.util.stream.Collectors;
22 import org.eclipse.jdt.annotation.NonNull;
23 import org.eclipse.jdt.annotation.Nullable;
24 import org.opendaylight.mdsal.binding.generator.BindingGeneratorUtil;
25 import org.opendaylight.mdsal.binding.generator.impl.reactor.TypeReference.ResolvedLeafref;
26 import org.opendaylight.mdsal.binding.model.api.AccessModifier;
27 import org.opendaylight.mdsal.binding.model.api.ConcreteType;
28 import org.opendaylight.mdsal.binding.model.api.Enumeration;
29 import org.opendaylight.mdsal.binding.model.api.GeneratedProperty;
30 import org.opendaylight.mdsal.binding.model.api.GeneratedTransferObject;
31 import org.opendaylight.mdsal.binding.model.api.GeneratedType;
32 import org.opendaylight.mdsal.binding.model.api.JavaTypeName;
33 import org.opendaylight.mdsal.binding.model.api.Restrictions;
34 import org.opendaylight.mdsal.binding.model.api.Type;
35 import org.opendaylight.mdsal.binding.model.api.type.builder.GeneratedPropertyBuilder;
36 import org.opendaylight.mdsal.binding.model.api.type.builder.GeneratedTOBuilder;
37 import org.opendaylight.mdsal.binding.model.api.type.builder.GeneratedTypeBuilderBase;
38 import org.opendaylight.mdsal.binding.model.api.type.builder.MethodSignatureBuilder;
39 import org.opendaylight.mdsal.binding.model.ri.BaseYangTypes;
40 import org.opendaylight.mdsal.binding.model.ri.BindingTypes;
41 import org.opendaylight.mdsal.binding.model.ri.TypeConstants;
42 import org.opendaylight.mdsal.binding.model.ri.Types;
43 import org.opendaylight.mdsal.binding.model.ri.generated.type.builder.AbstractEnumerationBuilder;
44 import org.opendaylight.mdsal.binding.model.ri.generated.type.builder.GeneratedPropertyBuilderImpl;
45 import org.opendaylight.mdsal.binding.spec.naming.BindingMapping;
46 import org.opendaylight.yangtools.concepts.Immutable;
47 import org.opendaylight.yangtools.yang.binding.RegexPatterns;
48 import org.opendaylight.yangtools.yang.binding.TypeObject;
49 import org.opendaylight.yangtools.yang.common.QName;
50 import org.opendaylight.yangtools.yang.common.YangConstants;
51 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
52 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
53 import org.opendaylight.yangtools.yang.model.api.stmt.BaseEffectiveStatement;
54 import org.opendaylight.yangtools.yang.model.api.stmt.LengthEffectiveStatement;
55 import org.opendaylight.yangtools.yang.model.api.stmt.PathEffectiveStatement;
56 import org.opendaylight.yangtools.yang.model.api.stmt.PatternEffectiveStatement;
57 import org.opendaylight.yangtools.yang.model.api.stmt.PatternExpression;
58 import org.opendaylight.yangtools.yang.model.api.stmt.RangeEffectiveStatement;
59 import org.opendaylight.yangtools.yang.model.api.stmt.TypeEffectiveStatement;
60 import org.opendaylight.yangtools.yang.model.api.stmt.ValueRange;
61 import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition;
62 import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition.Bit;
63 import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
64 import org.opendaylight.yangtools.yang.model.api.type.ModifierKind;
65 import org.opendaylight.yangtools.yang.model.api.type.PatternConstraint;
66 import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition;
67 import org.opendaylight.yangtools.yang.model.api.type.TypeDefinitions;
68 import org.slf4j.Logger;
69 import org.slf4j.LoggerFactory;
70
71 /**
72  * Common base class for {@link TypedefGenerator} and {@link AbstractTypeAwareGenerator}. This encompasses three
73  * different statements with two different semantics:
74  * <ul>
75  *   <li>{@link TypedefGenerator}s always result in a generated {@link TypeObject}, even if the semantics is exactly
76  *       the same as its base type. This aligns with {@code typedef} defining a new type.<li>
77  *   <li>{@link LeafGenerator}s and {@link LeafListGenerator}s, on the other hand, do not generate a {@link TypeObject}
78  *       unless absolutely necassary. This aligns with {@code leaf} and {@code leaf-list} being mapped onto a property
79  *       of its parent type.<li>
80  * </ul>
81  *
82  * <p>
83  * To throw a bit of confusion into the mix, there are three exceptions to those rules:
84  * <ul>
85  *   <li>
86  *     {@code identityref} definitions never result in a type definition being emitted. The reason for this has to do
87  *     with identity type mapping as well as history of our codebase.
88  *
89  *     <p>
90  *     The problem at hand is inconsistency between the fact that identity is mapped to a {@link Class}, which is also
91  *     returned from leaves which specify it like this:
92  *     <pre>
93  *       <code>
94  *         identity iden;
95  *
96  *         container foo {
97  *           leaf foo {
98  *             type identityref {
99  *               base iden;
100  *             }
101  *           }
102  *         }
103  *       </code>
104  *     </pre>
105  *     which results in fine-looking
106  *     <pre>
107  *       <code>
108  *         interface Foo {
109  *           Class&lt;? extends Iden&gt; getFoo();
110  *         }
111  *       </code>
112  *     </pre>
113  *
114  *     <p>
115  *     This gets more dicey if we decide to extend the previous snippet to also include:
116  *     <pre>
117  *       <code>
118  *         typedef bar-ref {
119  *           type identityref {
120  *             base iden;
121  *           }
122  *         }
123  *
124  *         container bar {
125  *           leaf bar {
126  *             type bar-ref;
127  *           }
128  *         }
129  *       </code>
130  *     </pre>
131  *
132  *     <p>
133  *     Now we have competing requirements: {@code typedef} would like us to use encapsulation to capture the defined
134  *     type, while {@code getBar()} wants us to retain shape with getFoo(), as it should not matter how the
135  *     {@code identityref} was formed. We need to pick between:
136  *     <ol>
137  *       <li>
138  *         <pre>
139  *           <code>
140  *             public class BarRef extends ScalarTypeObject&lt;Class&lt;? extends Iden&gt;&gt; {
141  *               Class&lt;? extends Iden&gt; getValue() {
142  *                 // ...
143  *               }
144  *             }
145  *
146  *             interface Bar {
147  *               BarRef getBar();
148  *             }
149  *           </code>
150  *         </pre>
151  *       </li>
152  *       <li>
153  *         <pre>
154  *           <code>
155  *             interface Bar {
156  *               Class&lt;? extends Iden&gt; getBar();
157  *             }
158  *           </code>
159  *         </pre>
160  *       </li>
161  *     </ol>
162  *
163  *     <p>
164  *     Here the second option is more user-friendly, as the type system works along the lines of <b>reference</b>
165  *     semantics, treating and {@code Bar.getBar()} and {@code Foo.getFoo()} as equivalent. The first option would
166  *     force users to go through explicit encapsulation, for no added benefit as the {@code typedef} cannot possibly add
167  *     anything useful to the actual type semantics.
168  *   </li>
169  *   <li>
170  *     {@code leafref} definitions never result in a type definition being emitted. The reasons for this are similar to
171  *     {@code identityref}, but have an additional twist: a {@leafref} can target a relative path, which may only be
172  *     resolved at a particular instantiation.
173  *
174  *     Take the example of the following model:
175  *     <pre>
176  *       <code>
177  *         grouping grp {
178  *           typedef ref {
179  *             type leafref {
180  *               path ../xyzzy;
181  *             }
182  *           }
183  *
184  *           leaf foo {
185  *             type ref;
186  *           }
187  *         }
188  *
189  *         container bar {
190              uses grp;
191  *
192  *           leaf xyzzy {
193  *             type string;
194  *           }
195  *         }
196  *
197  *         container baz {
198  *           uses grp;
199  *
200  *           leaf xyzzy {
201  *             type int32;
202  *           }
203  *         }
204  *       </code>
205  *     </pre>
206  *
207  *     The {@code typedef ref} points to outside of the grouping, and hence the type of {@code leaf foo} is polymorphic:
208  *     the definition in {@code grouping grp} needs to use {@code Object}, whereas the instantiations in
209  *     {@code container bar} and {@code container baz} need to use {@code String} and {@link Integer} respectively.
210  *     Expressing the resulting interface contracts requires return type specialization and run-time checks. An
211  *     intermediate class generated for the typedef would end up being a hindrance without any benefit.
212  *   <li>
213  *   <li>
214  *     {@code enumeration} definitions never result in a derived type. This is because these are mapped to Java
215  *     {@code enum}, which does not allow subclassing.
216  *   <li>
217  * </ul>
218  *
219  * <p>
220  * At the end of the day, the mechanic translation rules are giving way to correctly mapping the semantics -- which in
221  * both of the exception cases boil down to tracking type indirection. Intermediate constructs involved in tracking
222  * type indirection in YANG constructs is therefore explicitly excluded from the generated Java code, but the Binding
223  * Specification still takes them into account when determining types as outlined above.
224  */
225 abstract class AbstractTypeObjectGenerator<T extends EffectiveStatement<?, ?>> extends AbstractDependentGenerator<T> {
226     private static final class UnionDependencies implements Immutable {
227         private final Map<EffectiveStatement<?, ?>, TypeReference> identityTypes = new HashMap<>();
228         private final Map<EffectiveStatement<?, ?>, TypeReference> leafTypes = new HashMap<>();
229         private final Map<QName, TypedefGenerator> baseTypes = new HashMap<>();
230
231         UnionDependencies(final TypeEffectiveStatement<?> type, final GeneratorContext context) {
232             resolveUnionDependencies(context, type);
233         }
234
235         private void resolveUnionDependencies(final GeneratorContext context, final TypeEffectiveStatement<?> union) {
236             for (EffectiveStatement<?, ?> stmt : union.effectiveSubstatements()) {
237                 if (stmt instanceof TypeEffectiveStatement) {
238                     final TypeEffectiveStatement<?> type = (TypeEffectiveStatement<?>) stmt;
239                     final QName typeName = type.argument();
240                     if (TypeDefinitions.IDENTITYREF.equals(typeName)) {
241                         if (!identityTypes.containsKey(stmt)) {
242                             identityTypes.put(stmt, TypeReference.identityRef(
243                                 type.streamEffectiveSubstatements(BaseEffectiveStatement.class)
244                                     .map(BaseEffectiveStatement::argument)
245                                     .map(context::resolveIdentity)
246                                     .collect(Collectors.toUnmodifiableList())));
247                         }
248                     } else if (TypeDefinitions.LEAFREF.equals(typeName)) {
249                         if (!leafTypes.containsKey(stmt)) {
250                             leafTypes.put(stmt, TypeReference.leafRef(context.resolveLeafref(
251                                 type.findFirstEffectiveSubstatementArgument(PathEffectiveStatement.class)
252                                 .orElseThrow())));
253                         }
254                     } else if (TypeDefinitions.UNION.equals(typeName)) {
255                         resolveUnionDependencies(context, type);
256                     } else if (!isBuiltinName(typeName) && !baseTypes.containsKey(typeName)) {
257                         baseTypes.put(typeName, context.resolveTypedef(typeName));
258                     }
259                 }
260             }
261         }
262     }
263
264     private static final Logger LOG = LoggerFactory.getLogger(AbstractTypeObjectGenerator.class);
265     static final ImmutableMap<QName, Type> SIMPLE_TYPES = ImmutableMap.<QName, Type>builder()
266         .put(TypeDefinitions.BINARY, BaseYangTypes.BINARY_TYPE)
267         .put(TypeDefinitions.BOOLEAN, BaseYangTypes.BOOLEAN_TYPE)
268         .put(TypeDefinitions.DECIMAL64, BaseYangTypes.DECIMAL64_TYPE)
269         .put(TypeDefinitions.EMPTY, BaseYangTypes.EMPTY_TYPE)
270         .put(TypeDefinitions.INSTANCE_IDENTIFIER, BaseYangTypes.INSTANCE_IDENTIFIER)
271         .put(TypeDefinitions.INT8, BaseYangTypes.INT8_TYPE)
272         .put(TypeDefinitions.INT16, BaseYangTypes.INT16_TYPE)
273         .put(TypeDefinitions.INT32, BaseYangTypes.INT32_TYPE)
274         .put(TypeDefinitions.INT64, BaseYangTypes.INT64_TYPE)
275         .put(TypeDefinitions.STRING, BaseYangTypes.STRING_TYPE)
276         .put(TypeDefinitions.UINT8, BaseYangTypes.UINT8_TYPE)
277         .put(TypeDefinitions.UINT16, BaseYangTypes.UINT16_TYPE)
278         .put(TypeDefinitions.UINT32, BaseYangTypes.UINT32_TYPE)
279         .put(TypeDefinitions.UINT64, BaseYangTypes.UINT64_TYPE)
280         .build();
281
282     private final TypeEffectiveStatement<?> type;
283
284     // FIXME: these fields should be better-controlled with explicit sequencing guards. It it currently stands, we are
285     //        expending two (or more) additional fields to express downstream linking. If we had the concept of
286     //        resolution step (an enum), we could just get by with a simple queue of Step/Callback pairs, which would
287     //        trigger as needed. See how we manage baseGen/inferred fields.
288
289     /**
290      * The generator corresponding to our YANG base type. It produces the superclass of our encapsulated type. If it is
291      * {@code null}, this generator is the root of the hierarchy.
292      */
293     private TypedefGenerator baseGen;
294     private TypeReference refType;
295     private List<GeneratedType> auxiliaryGeneratedTypes = List.of();
296     private UnionDependencies unionDependencies;
297     private List<AbstractTypeObjectGenerator<?>> inferred = List.of();
298
299     AbstractTypeObjectGenerator(final T statement, final AbstractCompositeGenerator<?> parent) {
300         super(statement, parent);
301         type = statement().findFirstEffectiveSubstatement(TypeEffectiveStatement.class).orElseThrow();
302     }
303
304     @Override
305     public final List<GeneratedType> auxiliaryGeneratedTypes() {
306         return auxiliaryGeneratedTypes;
307     }
308
309     @Override
310     final void linkDependencies(final GeneratorContext context) {
311         verify(inferred != null, "Duplicate linking of %s", this);
312
313         final QName typeName = type.argument();
314         if (isBuiltinName(typeName)) {
315             verify(inferred.isEmpty(), "Unexpected non-empty downstreams in %s", this);
316             inferred = null;
317             return;
318         }
319
320         final AbstractExplicitGenerator<?> prev = previous();
321         if (prev != null) {
322             verify(prev instanceof AbstractTypeObjectGenerator, "Unexpected previous %s", prev);
323             ((AbstractTypeObjectGenerator<?>) prev).linkInferred(this);
324         } else {
325             linkBaseGen(context.resolveTypedef(typeName));
326         }
327     }
328
329     private void linkInferred(final AbstractTypeObjectGenerator<?> downstream) {
330         if (inferred == null) {
331             downstream.linkBaseGen(verifyNotNull(baseGen, "Mismatch on linking between %s and %s", this, downstream));
332             return;
333         }
334
335         if (inferred.isEmpty()) {
336             inferred = new ArrayList<>(2);
337         }
338         inferred.add(downstream);
339     }
340
341     private void linkBaseGen(final TypedefGenerator upstreamBaseGen) {
342         verify(baseGen == null, "Attempted to replace base %s with %s in %s", baseGen, upstreamBaseGen, this);
343         final List<AbstractTypeObjectGenerator<?>> downstreams = verifyNotNull(inferred,
344             "Duplicated linking of %s", this);
345         baseGen = verifyNotNull(upstreamBaseGen);
346         baseGen.addDerivedGenerator(this);
347         inferred = null;
348
349         for (AbstractTypeObjectGenerator<?> downstream : downstreams) {
350             downstream.linkBaseGen(upstreamBaseGen);
351         }
352     }
353
354     void bindTypeDefinition(final GeneratorContext context) {
355         if (baseGen != null) {
356             // We have registered with baseGen, it will push the type to us
357             return;
358         }
359
360         final QName arg = type.argument();
361         if (TypeDefinitions.IDENTITYREF.equals(arg)) {
362             refType = TypeReference.identityRef(type.streamEffectiveSubstatements(BaseEffectiveStatement.class)
363                 .map(BaseEffectiveStatement::argument)
364                 .map(context::resolveIdentity)
365                 .collect(Collectors.toUnmodifiableList()));
366         } else if (TypeDefinitions.LEAFREF.equals(arg)) {
367             final AbstractTypeObjectGenerator<?> targetGenerator = context.resolveLeafref(
368                 type.findFirstEffectiveSubstatementArgument(PathEffectiveStatement.class).orElseThrow());
369             checkArgument(targetGenerator != this, "Effective model contains self-referencing leaf %s",
370                 statement().argument());
371             refType = TypeReference.leafRef(targetGenerator);
372         } else if (TypeDefinitions.UNION.equals(arg)) {
373             unionDependencies = new UnionDependencies(type, context);
374             LOG.trace("Resolved union {} to dependencies {}", type, unionDependencies);
375         }
376
377         LOG.trace("Resolved base {} to generator {}", type, refType);
378         bindDerivedGenerators(refType);
379     }
380
381     final void bindTypeDefinition(final @Nullable TypeReference reference) {
382         refType = reference;
383         LOG.trace("Resolved derived {} to generator {}", type, refType);
384     }
385
386     private static boolean isBuiltinName(final QName typeName) {
387         return YangConstants.RFC6020_YANG_MODULE.equals(typeName.getModule());
388     }
389
390     abstract void bindDerivedGenerators(@Nullable TypeReference reference);
391
392     @Override
393     final ClassPlacement classPlacement() {
394         if (refType != null) {
395             // Reference types never create a new type
396             return ClassPlacement.NONE;
397         }
398         if (isDerivedEnumeration()) {
399             // Types derived from an enumeration never create a new type, as that would have to be a subclass of an enum
400             // and since enums are final, that can never happen.
401             return ClassPlacement.NONE;
402         }
403         return classPlacementImpl();
404     }
405
406     @NonNull ClassPlacement classPlacementImpl() {
407         // TODO: make this a lot more accurate by comparing the effective delta between the base type and the effective
408         //       restricted type. We should not be generating a type for constructs like:
409         //
410         //         leaf foo {
411         //           type uint8 { range 0..255; }
412         //         }
413         //
414         //       or
415         //
416         //         typedef foo {
417         //           type uint8 { range 0..100; }
418         //         }
419         //
420         //         leaf foo {
421         //           type foo { range 0..100; }
422         //         }
423         //
424         //       Which is relatively easy to do for integral types, but is way more problematic for 'pattern'
425         //       restrictions. Nevertheless we can define the mapping in a way which can be implemented with relative
426         //       ease.
427         return baseGen != null || SIMPLE_TYPES.containsKey(type.argument()) || isAddedByUses() || isAugmenting()
428             ? ClassPlacement.NONE : ClassPlacement.MEMBER;
429     }
430
431     @Override
432     final GeneratedType getGeneratedType(final TypeBuilderFactory builderFactory) {
433         // For derived enumerations defer to base type
434         return isDerivedEnumeration() ? baseGen.getGeneratedType(builderFactory)
435             : super.getGeneratedType(builderFactory);
436     }
437
438     final boolean isEnumeration() {
439         return baseGen != null ? baseGen.isEnumeration() : TypeDefinitions.ENUMERATION.equals(type.argument());
440     }
441
442     final boolean isDerivedEnumeration() {
443         return baseGen != null && baseGen.isEnumeration();
444     }
445
446     @Override
447     Type methodReturnType(final TypeBuilderFactory builderFactory) {
448         return methodReturnElementType(builderFactory);
449     }
450
451     final @NonNull Type methodReturnElementType(final @NonNull TypeBuilderFactory builderFactory) {
452         final GeneratedType generatedType = tryGeneratedType(builderFactory);
453         if (generatedType != null) {
454             // We have generated a type here, so return it. This covers 'bits', 'enumeration' and 'union'.
455             return generatedType;
456         }
457
458         if (refType != null) {
459             // This is a reference type of some kind. Defer to its judgement as to what the return type is.
460             return refType.methodReturnType(builderFactory);
461         }
462
463         final AbstractExplicitGenerator<?> prev = previous();
464         if (prev != null) {
465             // We have been added through augment/uses, defer to the original definition
466             return prev.methodReturnType(builderFactory);
467         }
468
469         final Type baseType;
470         if (baseGen == null) {
471             final QName qname = type.argument();
472             baseType = verifyNotNull(SIMPLE_TYPES.get(qname), "Cannot resolve type %s in %s", qname, this);
473         } else {
474             // We are derived from a base generator. Defer to its type for return.
475             baseType = baseGen.getGeneratedType(builderFactory);
476         }
477
478         return restrictType(baseType, computeRestrictions(), builderFactory);
479     }
480
481     private static @NonNull Type restrictType(final @NonNull Type baseType, final Restrictions restrictions,
482             final TypeBuilderFactory builderFactory) {
483         if (restrictions == null || restrictions.isEmpty()) {
484             // No additional restrictions, return base type
485             return baseType;
486         }
487
488         if (!(baseType instanceof GeneratedTransferObject)) {
489             // This is a simple Java type, just wrap it with new restrictions
490             return Types.restrictedType(baseType, restrictions);
491         }
492
493         // Base type is a GTO, we need to re-adjust it with new restrictions
494         final GeneratedTransferObject gto = (GeneratedTransferObject) baseType;
495         final GeneratedTOBuilder builder = builderFactory.newGeneratedTOBuilder(gto.getIdentifier());
496         final GeneratedTransferObject parent = gto.getSuperType();
497         if (parent != null) {
498             builder.setExtendsType(parent);
499         }
500         builder.setRestrictions(restrictions);
501         for (GeneratedProperty gp : gto.getProperties()) {
502             builder.addProperty(gp.getName())
503                 .setValue(gp.getValue())
504                 .setReadOnly(gp.isReadOnly())
505                 .setAccessModifier(gp.getAccessModifier())
506                 .setReturnType(gp.getReturnType())
507                 .setFinal(gp.isFinal())
508                 .setStatic(gp.isStatic());
509         }
510         return builder.build();
511     }
512
513     @Override
514     final void addAsGetterMethodOverride(final GeneratedTypeBuilderBase<?> builder,
515             final TypeBuilderFactory builderFactory) {
516         if (!(refType instanceof ResolvedLeafref)) {
517             // We are not dealing with a leafref or have nothing to add
518             return;
519         }
520
521         final AbstractTypeObjectGenerator<?> prev =
522             (AbstractTypeObjectGenerator<?>) verifyNotNull(previous(), "Missing previous link in %s", this);
523         if (prev.refType instanceof ResolvedLeafref) {
524             // We should be already inheriting the correct type
525             return;
526         }
527
528         // Note: this may we wrapped for leaf-list, hence we need to deal with that
529         final Type myType = methodReturnType(builderFactory);
530         LOG.trace("Override of {} to {}", this, myType);
531         final MethodSignatureBuilder getter = constructGetter(builder, myType);
532         getter.addAnnotation(OVERRIDE_ANNOTATION);
533         annotateDeprecatedIfNecessary(getter);
534     }
535
536     final @Nullable Restrictions computeRestrictions() {
537         final List<ValueRange> length = type.findFirstEffectiveSubstatementArgument(LengthEffectiveStatement.class)
538             .orElse(List.of());
539         final List<ValueRange> range = type.findFirstEffectiveSubstatementArgument(RangeEffectiveStatement.class)
540             .orElse(List.of());
541         final List<PatternExpression> patterns = type.streamEffectiveSubstatements(PatternEffectiveStatement.class)
542             .map(PatternEffectiveStatement::argument)
543             .collect(Collectors.toUnmodifiableList());
544
545         if (length.isEmpty() && range.isEmpty() && patterns.isEmpty()) {
546             return null;
547         }
548
549         return BindingGeneratorUtil.getRestrictions(extractTypeDefinition());
550     }
551
552     @Override
553     final GeneratedType createTypeImpl(final TypeBuilderFactory builderFactory) {
554         if (baseGen != null) {
555             final GeneratedType baseType = baseGen.getGeneratedType(builderFactory);
556             verify(baseType instanceof GeneratedTransferObject, "Unexpected base type %s", baseType);
557             return createDerivedType(builderFactory, (GeneratedTransferObject) baseType);
558         }
559
560         // FIXME: why do we need this boolean?
561         final boolean isTypedef = this instanceof TypedefGenerator;
562         final QName arg = type.argument();
563         if (TypeDefinitions.BITS.equals(arg)) {
564             return createBits(builderFactory, typeName(), currentModule(), extractTypeDefinition(), isTypedef);
565         } else if (TypeDefinitions.ENUMERATION.equals(arg)) {
566             return createEnumeration(builderFactory, typeName(), currentModule(),
567                 (EnumTypeDefinition) extractTypeDefinition());
568         } else if (TypeDefinitions.UNION.equals(arg)) {
569             final List<GeneratedType> tmp = new ArrayList<>(1);
570             final GeneratedTransferObject ret = createUnion(tmp, builderFactory, statement(), unionDependencies,
571                 typeName(), currentModule(), type, isTypedef, extractTypeDefinition());
572             auxiliaryGeneratedTypes = List.copyOf(tmp);
573             return ret;
574         } else {
575             return createSimple(builderFactory, typeName(), currentModule(),
576                 verifyNotNull(SIMPLE_TYPES.get(arg), "Unhandled type %s", arg), extractTypeDefinition());
577         }
578     }
579
580     private static @NonNull GeneratedTransferObject createBits(final TypeBuilderFactory builderFactory,
581             final JavaTypeName typeName, final ModuleGenerator module, final TypeDefinition<?> typedef,
582             final boolean isTypedef) {
583         final GeneratedTOBuilder builder = builderFactory.newGeneratedTOBuilder(typeName);
584         builder.setTypedef(isTypedef);
585         builder.addImplementsType(BindingTypes.TYPE_OBJECT);
586         builder.setBaseType(typedef);
587
588         for (Bit bit : ((BitsTypeDefinition) typedef).getBits()) {
589             final String name = bit.getName();
590             GeneratedPropertyBuilder genPropertyBuilder = builder.addProperty(BindingMapping.getPropertyName(name));
591             genPropertyBuilder.setReadOnly(true);
592             genPropertyBuilder.setReturnType(BaseYangTypes.BOOLEAN_TYPE);
593
594             builder.addEqualsIdentity(genPropertyBuilder);
595             builder.addHashIdentity(genPropertyBuilder);
596             builder.addToStringProperty(genPropertyBuilder);
597         }
598
599         // builder.setSchemaPath(typedef.getPath());
600         builder.setModuleName(module.statement().argument().getLocalName());
601         addCodegenInformation(typedef, builder);
602         annotateDeprecatedIfNecessary(typedef, builder);
603         makeSerializable(builder);
604         return builder.build();
605     }
606
607     private static @NonNull Enumeration createEnumeration(final TypeBuilderFactory builderFactory,
608             final JavaTypeName typeName, final ModuleGenerator module, final EnumTypeDefinition typedef) {
609         // TODO units for typedef enum
610         final AbstractEnumerationBuilder builder = builderFactory.newEnumerationBuilder(typeName);
611
612         typedef.getDescription().map(BindingGeneratorUtil::encodeAngleBrackets)
613             .ifPresent(builder::setDescription);
614         typedef.getReference().ifPresent(builder::setReference);
615
616         builder.setModuleName(module.statement().argument().getLocalName());
617         builder.updateEnumPairsFromEnumTypeDef(typedef);
618         return builder.toInstance();
619     }
620
621     private static @NonNull GeneratedType createSimple(final TypeBuilderFactory builderFactory,
622             final JavaTypeName typeName, final ModuleGenerator module, final Type javaType,
623             final TypeDefinition<?> typedef) {
624         final String moduleName = module.statement().argument().getLocalName();
625         final GeneratedTOBuilder builder = builderFactory.newGeneratedTOBuilder(typeName);
626         builder.setTypedef(true);
627         builder.addImplementsType(BindingTypes.scalarTypeObject(javaType));
628
629         final GeneratedPropertyBuilder genPropBuilder = builder.addProperty(TypeConstants.VALUE_PROP);
630         genPropBuilder.setReturnType(javaType);
631         builder.addEqualsIdentity(genPropBuilder);
632         builder.addHashIdentity(genPropBuilder);
633         builder.addToStringProperty(genPropBuilder);
634
635         builder.setRestrictions(BindingGeneratorUtil.getRestrictions(typedef));
636
637 //        builder.setSchemaPath(typedef.getPath());
638         builder.setModuleName(moduleName);
639         addCodegenInformation(typedef, builder);
640
641         annotateDeprecatedIfNecessary(typedef, builder);
642
643         if (javaType instanceof ConcreteType
644             // FIXME: This looks very suspicious: we should by checking for Types.STRING
645             && "String".equals(javaType.getName()) && typedef.getBaseType() != null) {
646             addStringRegExAsConstant(builder, resolveRegExpressions(typedef));
647         }
648         addUnits(builder, typedef);
649
650         makeSerializable(builder);
651         return builder.build();
652     }
653
654     private static @NonNull GeneratedTransferObject createUnion(final List<GeneratedType> auxiliaryGeneratedTypes,
655             final TypeBuilderFactory builderFactory, final EffectiveStatement<?, ?> definingStatement,
656             final UnionDependencies dependencies, final JavaTypeName typeName, final ModuleGenerator module,
657             final TypeEffectiveStatement<?> type, final boolean isTypedef, final TypeDefinition<?> typedef) {
658         final GeneratedUnionBuilder builder = builderFactory.newGeneratedUnionBuilder(typeName);
659         builder.addImplementsType(BindingTypes.TYPE_OBJECT);
660         builder.setIsUnion(true);
661
662 //        builder.setSchemaPath(typedef.getPath());
663         builder.setModuleName(module.statement().argument().getLocalName());
664         addCodegenInformation(definingStatement, builder);
665
666         annotateDeprecatedIfNecessary(definingStatement, builder);
667
668         // Pattern string is the key, XSD regex is the value. The reason for this choice is that the pattern carries
669         // also negation information and hence guarantees uniqueness.
670         final Map<String, String> expressions = new HashMap<>();
671
672         // Linear list of properties generated from subtypes. We need this information for runtime types, as it allows
673         // direct mapping of type to corresponding property -- without having to resort to re-resolving the leafrefs
674         // again.
675         final List<String> typeProperties = new ArrayList<>();
676
677         for (EffectiveStatement<?, ?> stmt : type.effectiveSubstatements()) {
678             if (stmt instanceof TypeEffectiveStatement) {
679                 final TypeEffectiveStatement<?> subType = (TypeEffectiveStatement<?>) stmt;
680                 final QName subName = subType.argument();
681                 final String localName = subName.getLocalName();
682
683                 String propSource = localName;
684                 final Type generatedType;
685                 if (TypeDefinitions.UNION.equals(subName)) {
686                     final JavaTypeName subUnionName = typeName.createEnclosed(
687                         provideAvailableNameForGenTOBuilder(typeName.simpleName()));
688                     final GeneratedTransferObject subUnion = createUnion(auxiliaryGeneratedTypes, builderFactory,
689                         definingStatement, dependencies, subUnionName, module, subType, isTypedef,
690                         subType.getTypeDefinition());
691                     builder.addEnclosingTransferObject(subUnion);
692                     propSource = subUnionName.simpleName();
693                     generatedType = subUnion;
694                 } else if (TypeDefinitions.ENUMERATION.equals(subName)) {
695                     final Enumeration subEnumeration = createEnumeration(builderFactory,
696                         typeName.createEnclosed(BindingMapping.getClassName(localName), "$"), module,
697                         (EnumTypeDefinition) subType.getTypeDefinition());
698                     builder.addEnumeration(subEnumeration);
699                     generatedType = subEnumeration;
700                 } else if (TypeDefinitions.BITS.equals(subName)) {
701                     final GeneratedTransferObject subBits = createBits(builderFactory,
702                         typeName.createEnclosed(BindingMapping.getClassName(localName), "$"), module,
703                         subType.getTypeDefinition(), isTypedef);
704                     builder.addEnclosingTransferObject(subBits);
705                     generatedType = subBits;
706                 } else if (TypeDefinitions.IDENTITYREF.equals(subName)) {
707                     generatedType = verifyNotNull(dependencies.identityTypes.get(stmt),
708                         "Cannot resolve identityref %s in %s", stmt, definingStatement)
709                         .methodReturnType(builderFactory);
710                 } else if (TypeDefinitions.LEAFREF.equals(subName)) {
711                     generatedType = verifyNotNull(dependencies.leafTypes.get(stmt),
712                         "Cannot resolve leafref %s in %s", stmt, definingStatement)
713                         .methodReturnType(builderFactory);
714                 } else {
715                     Type baseType = SIMPLE_TYPES.get(subName);
716                     if (baseType == null) {
717                         // This has to be a reference to a typedef, let's lookup it up and pick up its type
718                         final AbstractTypeObjectGenerator<?> baseGen = verifyNotNull(
719                             dependencies.baseTypes.get(subName), "Cannot resolve base type %s in %s", subName,
720                             definingStatement);
721                         baseType = baseGen.methodReturnType(builderFactory);
722
723                         // FIXME: This is legacy behaviour for leafrefs:
724                         if (baseGen.refType instanceof TypeReference.Leafref) {
725                             // if there already is a compatible property, do not generate a new one
726                             final Type search = baseType;
727
728                             final String matching = builder.getProperties().stream()
729                                 .filter(prop -> search == ((GeneratedPropertyBuilderImpl) prop).getReturnType())
730                                 .findFirst()
731                                 .map(GeneratedPropertyBuilder::getName)
732                                 .orElse(null);
733                             if (matching != null) {
734                                 typeProperties.add(matching);
735                                 continue;
736                             }
737
738                             // ... otherwise generate this weird property name
739                             propSource = BindingMapping.getUnionLeafrefMemberName(builder.getName(),
740                                 baseType.getName());
741                         }
742                     }
743
744                     expressions.putAll(resolveRegExpressions(subType.getTypeDefinition()));
745
746                     generatedType = restrictType(baseType,
747                         BindingGeneratorUtil.getRestrictions(type.getTypeDefinition()), builderFactory);
748                 }
749
750                 final String propName = BindingMapping.getPropertyName(propSource);
751                 typeProperties.add(propName);
752
753                 if (builder.containsProperty(propName)) {
754                     /*
755                      *  FIXME: this is not okay, as we are ignoring multiple base types. For example in the case of:
756                      *
757                      *    type union {
758                      *      type string {
759                      *        length 1..5;
760                      *      }
761                      *      type string {
762                      *        length 8..10;
763                      *      }
764                      *    }
765                      *
766                      *  We are ending up losing the information about 8..10 being an alternative. This is also the case
767                      *  for leafrefs -- we are performing property compression as well (see above). While it is alluring
768                      *  to merge these into 'length 1..5|8..10', that may not be generally feasible.
769                      *
770                      *  We should resort to a counter of conflicting names, i.e. the second string would be mapped to
771                      *  'string1' or similar.
772                      */
773                     continue;
774                 }
775
776                 final GeneratedPropertyBuilder propBuilder = builder
777                     .addProperty(propName)
778                     .setReturnType(generatedType);
779
780                 builder.addEqualsIdentity(propBuilder);
781                 builder.addHashIdentity(propBuilder);
782                 builder.addToStringProperty(propBuilder);
783             }
784         }
785
786         // Record property names if needed
787         builder.setTypePropertyNames(typeProperties);
788
789         addStringRegExAsConstant(builder, expressions);
790         addUnits(builder, typedef);
791
792         makeSerializable(builder);
793         final GeneratedTransferObject ret = builder.build();
794
795         // Define a corresponding union builder. Typedefs are always anchored at a Java package root,
796         // so we are placing the builder alongside the union.
797         final GeneratedTOBuilder unionBuilder = builderFactory.newGeneratedTOBuilder(unionBuilderName(typeName));
798         unionBuilder.setIsUnionBuilder(true);
799         unionBuilder.addMethod("getDefaultInstance")
800             .setAccessModifier(AccessModifier.PUBLIC)
801             .setStatic(true)
802             .setReturnType(ret)
803             .addParameter(Types.STRING, "defaultValue");
804         auxiliaryGeneratedTypes.add(unionBuilder.build());
805
806         return ret;
807     }
808
809     // FIXME: this can be a source of conflicts as we are not guarding against nesting
810     private static @NonNull JavaTypeName unionBuilderName(final JavaTypeName unionName) {
811         final StringBuilder sb = new StringBuilder();
812         for (String part : unionName.localNameComponents()) {
813             sb.append(part);
814         }
815         return JavaTypeName.create(unionName.packageName(), sb.append(BindingMapping.BUILDER_SUFFIX).toString());
816     }
817
818     // FIXME: we should not rely on TypeDefinition
819     abstract @NonNull TypeDefinition<?> extractTypeDefinition();
820
821     abstract @NonNull GeneratedTransferObject createDerivedType(@NonNull TypeBuilderFactory builderFactory,
822         @NonNull GeneratedTransferObject baseType);
823
824     /**
825      * Adds to the {@code genTOBuilder} the constant which contains regular expressions from the {@code expressions}.
826      *
827      * @param genTOBuilder generated TO builder to which are {@code regular expressions} added
828      * @param expressions list of string which represent regular expressions
829      */
830     static void addStringRegExAsConstant(final GeneratedTOBuilder genTOBuilder, final Map<String, String> expressions) {
831         if (!expressions.isEmpty()) {
832             genTOBuilder.addConstant(Types.listTypeFor(BaseYangTypes.STRING_TYPE), TypeConstants.PATTERN_CONSTANT_NAME,
833                 ImmutableMap.copyOf(expressions));
834         }
835     }
836
837     /**
838      * Converts the pattern constraints from {@code typedef} to the list of the strings which represents these
839      * constraints.
840      *
841      * @param typedef extended type in which are the pattern constraints sought
842      * @return list of strings which represents the constraint patterns
843      * @throws IllegalArgumentException if <code>typedef</code> equals null
844      */
845     static Map<String, String> resolveRegExpressions(final TypeDefinition<?> typedef) {
846         return typedef instanceof StringTypeDefinition
847             // TODO: run diff against base ?
848             ? resolveRegExpressions(((StringTypeDefinition) typedef).getPatternConstraints())
849                 : ImmutableMap.of();
850     }
851
852     /**
853      * Converts the pattern constraints to the list of the strings which represents these constraints.
854      *
855      * @param patternConstraints list of pattern constraints
856      * @return list of strings which represents the constraint patterns
857      */
858     private static Map<String, String> resolveRegExpressions(final List<PatternConstraint> patternConstraints) {
859         if (patternConstraints.isEmpty()) {
860             return ImmutableMap.of();
861         }
862
863         final Map<String, String> regExps = Maps.newHashMapWithExpectedSize(patternConstraints.size());
864         for (PatternConstraint patternConstraint : patternConstraints) {
865             String regEx = patternConstraint.getJavaPatternString();
866
867             // The pattern can be inverted
868             final Optional<ModifierKind> optModifier = patternConstraint.getModifier();
869             if (optModifier.isPresent()) {
870                 regEx = applyModifier(optModifier.get(), regEx);
871             }
872
873             regExps.put(regEx, patternConstraint.getRegularExpressionString());
874         }
875
876         return regExps;
877     }
878
879     /**
880      * Returns string which contains the same value as <code>name</code> but integer suffix is incremented by one. If
881      * <code>name</code> contains no number suffix, a new suffix initialized at 1 is added. A suffix is actually
882      * composed of a '$' marker, which is safe, as no YANG identifier can contain '$', and a unsigned decimal integer.
883      *
884      * @param name string with name of augmented node
885      * @return string with the number suffix incremented by one (or 1 is added)
886      */
887     private static String provideAvailableNameForGenTOBuilder(final String name) {
888         final int dollar = name.indexOf('$');
889         if (dollar == -1) {
890             return name + "$1";
891         }
892
893         final int newSuffix = Integer.parseUnsignedInt(name.substring(dollar + 1)) + 1;
894         verify(newSuffix > 0, "Suffix counter overflow");
895         return name.substring(0, dollar + 1) + newSuffix;
896     }
897
898     private static String applyModifier(final ModifierKind modifier, final String pattern) {
899         switch (modifier) {
900             case INVERT_MATCH:
901                 return RegexPatterns.negatePatternString(pattern);
902             default:
903                 LOG.warn("Ignoring unhandled modifier {}", modifier);
904                 return pattern;
905         }
906     }
907 }