Revert "Revert "Replace #getPath in processInstanceIdentifierType""
[netconf.git] / restconf / sal-rest-docgen / src / main / java / org / opendaylight / netconf / sal / rest / doc / impl / DefinitionGenerator.java
1 /*
2  * Copyright (c) 2020 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.netconf.sal.rest.doc.impl;
9
10 import static org.opendaylight.netconf.sal.rest.doc.impl.BaseYangSwaggerGenerator.MODULE_NAME_SUFFIX;
11 import static org.opendaylight.netconf.sal.rest.doc.model.builder.OperationBuilder.CONFIG;
12 import static org.opendaylight.netconf.sal.rest.doc.model.builder.OperationBuilder.NAME_KEY;
13 import static org.opendaylight.netconf.sal.rest.doc.model.builder.OperationBuilder.POST_SUFFIX;
14 import static org.opendaylight.netconf.sal.rest.doc.model.builder.OperationBuilder.TOP;
15 import static org.opendaylight.netconf.sal.rest.doc.model.builder.OperationBuilder.XML_KEY;
16 import static org.opendaylight.netconf.sal.rest.doc.model.builder.OperationBuilder.XML_SUFFIX;
17 import static org.opendaylight.netconf.sal.rest.doc.model.builder.OperationBuilder.getAppropriateModelPrefix;
18
19 import com.fasterxml.jackson.databind.JsonNode;
20 import com.fasterxml.jackson.databind.node.ArrayNode;
21 import com.fasterxml.jackson.databind.node.JsonNodeFactory;
22 import com.fasterxml.jackson.databind.node.ObjectNode;
23 import com.fasterxml.jackson.databind.node.TextNode;
24 import com.google.common.collect.Range;
25 import com.google.common.collect.RangeSet;
26 import com.mifmif.common.regex.Generex;
27 import java.io.IOException;
28 import java.math.BigDecimal;
29 import java.util.Collection;
30 import java.util.Iterator;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.Optional;
34 import java.util.Set;
35 import java.util.stream.Collectors;
36 import org.opendaylight.netconf.sal.rest.doc.impl.ApiDocServiceImpl.OAversion;
37 import org.opendaylight.yangtools.yang.common.QName;
38 import org.opendaylight.yangtools.yang.model.api.ActionDefinition;
39 import org.opendaylight.yangtools.yang.model.api.ActionNodeContainer;
40 import org.opendaylight.yangtools.yang.model.api.AnydataSchemaNode;
41 import org.opendaylight.yangtools.yang.model.api.AnyxmlSchemaNode;
42 import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
43 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
44 import org.opendaylight.yangtools.yang.model.api.ContainerLike;
45 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
46 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
47 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
48 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
49 import org.opendaylight.yangtools.yang.model.api.ElementCountConstraint;
50 import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
51 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
52 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
53 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
54 import org.opendaylight.yangtools.yang.model.api.MandatoryAware;
55 import org.opendaylight.yangtools.yang.model.api.Module;
56 import org.opendaylight.yangtools.yang.model.api.OperationDefinition;
57 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
58 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
59 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
60 import org.opendaylight.yangtools.yang.model.api.type.BinaryTypeDefinition;
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.BooleanTypeDefinition;
64 import org.opendaylight.yangtools.yang.model.api.type.DecimalTypeDefinition;
65 import org.opendaylight.yangtools.yang.model.api.type.EmptyTypeDefinition;
66 import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
67 import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition.EnumPair;
68 import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
69 import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
70 import org.opendaylight.yangtools.yang.model.api.type.Int16TypeDefinition;
71 import org.opendaylight.yangtools.yang.model.api.type.Int32TypeDefinition;
72 import org.opendaylight.yangtools.yang.model.api.type.Int64TypeDefinition;
73 import org.opendaylight.yangtools.yang.model.api.type.Int8TypeDefinition;
74 import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
75 import org.opendaylight.yangtools.yang.model.api.type.LengthConstraint;
76 import org.opendaylight.yangtools.yang.model.api.type.PatternConstraint;
77 import org.opendaylight.yangtools.yang.model.api.type.RangeConstraint;
78 import org.opendaylight.yangtools.yang.model.api.type.RangeRestrictedTypeDefinition;
79 import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition;
80 import org.opendaylight.yangtools.yang.model.api.type.Uint16TypeDefinition;
81 import org.opendaylight.yangtools.yang.model.api.type.Uint32TypeDefinition;
82 import org.opendaylight.yangtools.yang.model.api.type.Uint64TypeDefinition;
83 import org.opendaylight.yangtools.yang.model.api.type.Uint8TypeDefinition;
84 import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
85 import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack;
86 import org.slf4j.Logger;
87 import org.slf4j.LoggerFactory;
88
89 /**
90  * Generates JSON Schema for data defined in YANG. This class is not thread-safe.
91  */
92 public class DefinitionGenerator {
93
94     private static final Logger LOG = LoggerFactory.getLogger(DefinitionGenerator.class);
95
96     private static final String UNIQUE_ITEMS_KEY = "uniqueItems";
97     private static final String MAX_ITEMS = "maxItems";
98     private static final String MIN_ITEMS = "minItems";
99     private static final String MAX_LENGTH_KEY = "maxLength";
100     private static final String MIN_LENGTH_KEY = "minLength";
101     private static final String REQUIRED_KEY = "required";
102     private static final String REF_KEY = "$ref";
103     private static final String ITEMS_KEY = "items";
104     private static final String TYPE_KEY = "type";
105     private static final String PROPERTIES_KEY = "properties";
106     private static final String DESCRIPTION_KEY = "description";
107     private static final String ARRAY_TYPE = "array";
108     private static final String ENUM_KEY = "enum";
109     private static final String TITLE_KEY = "title";
110     private static final String DEFAULT_KEY = "default";
111     private static final String FORMAT_KEY = "format";
112     private static final String NAMESPACE_KEY = "namespace";
113     public static final String INPUT = "input";
114     public static final String INPUT_SUFFIX = "_input";
115     public static final String OUTPUT = "output";
116     public static final String OUTPUT_SUFFIX = "_output";
117     private static final String STRING_TYPE = "string";
118     private static final String OBJECT_TYPE = "object";
119     private static final String NUMBER_TYPE = "number";
120     private static final String INTEGER_TYPE = "integer";
121     private static final String INT32_FORMAT = "int32";
122     private static final String INT64_FORMAT = "int64";
123     private static final String BOOLEAN_TYPE = "boolean";
124
125     private Module topLevelModule;
126
127     public DefinitionGenerator() {
128     }
129
130     /**
131      * Creates Json definitions from provided module according to swagger spec.
132      *
133      * @param module          - Yang module to be converted
134      * @param schemaContext   - SchemaContext of all Yang files used by Api Doc
135      * @param definitionNames - Store for definition names
136      * @return ObjectNode containing data used for creating examples and definitions in Api Doc
137      * @throws IOException if I/O operation fails
138      */
139
140
141     public ObjectNode convertToJsonSchema(final Module module, final EffectiveModelContext schemaContext,
142                                           final ObjectNode definitions, final DefinitionNames definitionNames,
143                                           final OAversion oaversion, final boolean isForSingleModule)
144             throws IOException {
145         topLevelModule = module;
146
147         processIdentities(module, definitions, definitionNames, schemaContext);
148         processContainersAndLists(module, definitions, definitionNames, schemaContext, oaversion);
149         processRPCs(module, definitions, definitionNames, schemaContext, oaversion);
150
151         if (isForSingleModule) {
152             processModule(module, definitions, definitionNames, schemaContext, oaversion);
153         }
154
155         return definitions;
156     }
157
158     public ObjectNode convertToJsonSchema(final Module module, final EffectiveModelContext schemaContext,
159                                           final DefinitionNames definitionNames, final OAversion oaversion,
160                                           final boolean isForSingleModule)
161             throws IOException {
162         final ObjectNode definitions = JsonNodeFactory.instance.objectNode();
163         if (isForSingleModule) {
164             definitionNames.addUnlinkedName(module.getName() + MODULE_NAME_SUFFIX);
165         }
166         return convertToJsonSchema(module, schemaContext, definitions, definitionNames, oaversion, isForSingleModule);
167     }
168
169     private void processModule(final Module module, final ObjectNode definitions, final DefinitionNames definitionNames,
170                                final EffectiveModelContext schemaContext, final OAversion oaversion) {
171         final ObjectNode definition = JsonNodeFactory.instance.objectNode();
172         final ObjectNode properties = JsonNodeFactory.instance.objectNode();
173         final ArrayNode required = JsonNodeFactory.instance.arrayNode();
174         final String moduleName = module.getName();
175         final String definitionName = moduleName + MODULE_NAME_SUFFIX;
176         final SchemaInferenceStack stack = SchemaInferenceStack.of(schemaContext);
177         for (final DataSchemaNode node : module.getChildNodes()) {
178             stack.enterSchemaTree(node.getQName());
179             final String localName = node.getQName().getLocalName();
180             if (node.isConfiguration()) {
181                 if (node instanceof ContainerSchemaNode || node instanceof ListSchemaNode) {
182                     for (final DataSchemaNode childNode : ((DataNodeContainer) node).getChildNodes()) {
183                         final ObjectNode childNodeProperties = JsonNodeFactory.instance.objectNode();
184
185                         final String ref = getAppropriateModelPrefix(oaversion)
186                                 + moduleName + CONFIG
187                                 + "_" + localName
188                                 + definitionNames.getDiscriminator(node);
189
190                         if (node instanceof ListSchemaNode) {
191                             childNodeProperties.put(TYPE_KEY, ARRAY_TYPE);
192                             final ObjectNode items = JsonNodeFactory.instance.objectNode();
193                             items.put(REF_KEY, ref);
194                             childNodeProperties.set(ITEMS_KEY, items);
195                             childNodeProperties.put(DESCRIPTION_KEY, childNode.getDescription().orElse(""));
196                             childNodeProperties.put(TITLE_KEY, localName + CONFIG);
197                         } else {
198                          /*
199                             Description can't be added, because nothing allowed alongside $ref.
200                             allOf is not an option, because ServiceNow can't parse it.
201                           */
202                             childNodeProperties.put(REF_KEY, ref);
203                         }
204                         //add module name prefix to property name, when ServiceNow can process colons
205                         properties.set(localName, childNodeProperties);
206                     }
207                 } else {
208                     if (node instanceof LeafSchemaNode) {
209                         /*
210                             Add module name prefix to property name, when ServiceNow can process colons(second parameter
211                             of processLeafNode).
212                          */
213                         processLeafNode((LeafSchemaNode) node, localName, properties, required, stack,
214                                 definitions, definitionNames, oaversion);
215                     }
216                 }
217             }
218             stack.exit();
219         }
220         definition.put(TITLE_KEY, definitionName);
221         definition.put(TYPE_KEY, OBJECT_TYPE);
222         definition.set(PROPERTIES_KEY, properties);
223         definition.put(DESCRIPTION_KEY, module.getDescription().orElse(""));
224         setRequiredIfNotEmpty(definition, required);
225
226         definitions.set(definitionName, definition);
227     }
228
229     private void processContainersAndLists(final Module module, final ObjectNode definitions,
230             final DefinitionNames definitionNames, final EffectiveModelContext schemaContext, final OAversion oaversion)
231                 throws IOException {
232         final String moduleName = module.getName();
233         final SchemaInferenceStack stack = SchemaInferenceStack.of(schemaContext);
234         for (final DataSchemaNode childNode : module.getChildNodes()) {
235             stack.enterSchemaTree(childNode.getQName());
236             // For every container and list in the module
237             if (childNode instanceof ContainerSchemaNode || childNode instanceof ListSchemaNode) {
238                 if (childNode.isConfiguration()) {
239                     processDataNodeContainer((DataNodeContainer) childNode, moduleName, definitions, definitionNames,
240                             true, stack, oaversion);
241                 }
242                 processDataNodeContainer((DataNodeContainer) childNode, moduleName, definitions, definitionNames,
243                         false, stack, oaversion);
244                 processActionNodeContainer(childNode, moduleName, definitions, definitionNames, stack, oaversion);
245             }
246             stack.exit();
247         }
248     }
249
250     private void processActionNodeContainer(final DataSchemaNode childNode, final String moduleName,
251                                             final ObjectNode definitions, final DefinitionNames definitionNames,
252                                             final SchemaInferenceStack stack, final OAversion oaversion)
253             throws IOException {
254         for (final ActionDefinition actionDef : ((ActionNodeContainer) childNode).getActions()) {
255             processOperations(actionDef, moduleName, definitions, definitionNames, stack, oaversion);
256         }
257     }
258
259     private void processRPCs(final Module module, final ObjectNode definitions, final DefinitionNames definitionNames,
260                              final EffectiveModelContext schemaContext, final OAversion oaversion) throws IOException {
261         final String moduleName = module.getName();
262         final SchemaInferenceStack stack = SchemaInferenceStack.of(schemaContext);
263         for (final RpcDefinition rpcDefinition : module.getRpcs()) {
264             stack.enterSchemaTree(rpcDefinition.getQName());
265             processOperations(rpcDefinition, moduleName, definitions, definitionNames, stack, oaversion);
266             stack.exit();
267         }
268     }
269
270     private void processOperations(final OperationDefinition operationDef, final String parentName,
271             final ObjectNode definitions, final DefinitionNames definitionNames,
272             final SchemaInferenceStack stack, final OAversion oaversion)
273                 throws IOException {
274         final String operationName = operationDef.getQName().getLocalName();
275         processOperationInputOutput(operationDef.getInput(), operationName, parentName, true, definitions,
276                 definitionNames, stack, oaversion);
277         processOperationInputOutput(operationDef.getOutput(), operationName, parentName, false, definitions,
278                 definitionNames, stack, oaversion);
279     }
280
281     private void processOperationInputOutput(final ContainerLike container, final String operationName,
282                                              final String parentName, final boolean isInput,
283                                              final ObjectNode definitions, final DefinitionNames definitionNames,
284                                              final SchemaInferenceStack stack, final OAversion oaversion)
285             throws IOException {
286         stack.enterSchemaTree(container.getQName());
287         if (!container.getChildNodes().isEmpty()) {
288             final String filename = parentName + "_" + operationName + (isInput ? INPUT_SUFFIX : OUTPUT_SUFFIX);
289             final ObjectNode childSchema = JsonNodeFactory.instance.objectNode();
290             processChildren(childSchema, container.getChildNodes(), parentName, definitions, definitionNames,
291                     false, stack, oaversion);
292
293             childSchema.put(TYPE_KEY, OBJECT_TYPE);
294             final ObjectNode xml = JsonNodeFactory.instance.objectNode();
295             xml.put(NAME_KEY, isInput ? INPUT : OUTPUT);
296             childSchema.set(XML_KEY, xml);
297             childSchema.put(TITLE_KEY, filename);
298             final String discriminator =
299                     definitionNames.pickDiscriminator(container, List.of(filename, filename + TOP));
300             definitions.set(filename + discriminator, childSchema);
301
302             processTopData(filename, discriminator, definitions, container, oaversion);
303         }
304         stack.exit();
305     }
306
307     private static ObjectNode processTopData(final String filename, final String discriminator,
308             final ObjectNode definitions, final SchemaNode schemaNode, final OAversion oaversion) {
309         final ObjectNode dataNodeProperties = JsonNodeFactory.instance.objectNode();
310         final String name = filename + discriminator;
311         final String ref = getAppropriateModelPrefix(oaversion) + name;
312         final String topName = filename + TOP;
313
314         if (schemaNode instanceof ListSchemaNode) {
315             dataNodeProperties.put(TYPE_KEY, ARRAY_TYPE);
316             final ObjectNode items = JsonNodeFactory.instance.objectNode();
317             items.put(REF_KEY, ref);
318             dataNodeProperties.set(ITEMS_KEY, items);
319             dataNodeProperties.put(DESCRIPTION_KEY, schemaNode.getDescription().orElse(""));
320         } else {
321              /*
322                 Description can't be added, because nothing allowed alongside $ref.
323                 allOf is not an option, because ServiceNow can't parse it.
324               */
325             dataNodeProperties.put(REF_KEY, ref);
326         }
327
328         final ObjectNode properties = JsonNodeFactory.instance.objectNode();
329         /*
330             Add module name prefix to property name, when needed, when ServiceNow can process colons,
331             use RestDocGenUtil#resolveNodesName for creating property name
332          */
333         properties.set(schemaNode.getQName().getLocalName(), dataNodeProperties);
334         final ObjectNode finalChildSchema = JsonNodeFactory.instance.objectNode();
335         finalChildSchema.put(TYPE_KEY, OBJECT_TYPE);
336         finalChildSchema.set(PROPERTIES_KEY, properties);
337         finalChildSchema.put(TITLE_KEY, topName);
338
339
340         definitions.set(topName + discriminator, finalChildSchema);
341
342         return dataNodeProperties;
343     }
344
345     /**
346      * Processes the 'identity' statement in a yang model and maps it to a 'model' in the Swagger JSON spec.
347      * @param module          The module from which the identity stmt will be processed
348      * @param definitions     The ObjectNode in which the parsed identity will be put as a 'model' obj
349      * @param definitionNames Store for definition names
350      */
351     private static void processIdentities(final Module module, final ObjectNode definitions,
352                                           final DefinitionNames definitionNames, final EffectiveModelContext context) {
353         final String moduleName = module.getName();
354         final Collection<? extends IdentitySchemaNode> idNodes = module.getIdentities();
355         LOG.debug("Processing Identities for module {} . Found {} identity statements", moduleName, idNodes.size());
356
357         for (final IdentitySchemaNode idNode : idNodes) {
358             final ObjectNode identityObj = buildIdentityObject(idNode, context);
359             final String idName = idNode.getQName().getLocalName();
360             final String discriminator = definitionNames.pickDiscriminator(idNode, List.of(idName));
361             final String name = idName + discriminator;
362             definitions.set(name, identityObj);
363         }
364     }
365
366     private static void populateEnumWithDerived(final Collection<? extends IdentitySchemaNode> derivedIds,
367                                                 final ArrayNode enumPayload, final EffectiveModelContext context) {
368         for (final IdentitySchemaNode derivedId : derivedIds) {
369             enumPayload.add(derivedId.getQName().getLocalName());
370             populateEnumWithDerived(context.getDerivedIdentities(derivedId), enumPayload, context);
371         }
372     }
373
374     private ObjectNode processDataNodeContainer(final DataNodeContainer dataNode, final String parentName,
375                                                 final ObjectNode definitions, final DefinitionNames definitionNames,
376                                                 final boolean isConfig, final SchemaInferenceStack stack,
377                                                 final OAversion oaversion) throws IOException {
378         if (dataNode instanceof ListSchemaNode || dataNode instanceof ContainerSchemaNode) {
379             final Collection<? extends DataSchemaNode> containerChildren = dataNode.getChildNodes();
380             final SchemaNode schemaNode = (SchemaNode) dataNode;
381             final String localName = schemaNode.getQName().getLocalName();
382             final ObjectNode childSchema = JsonNodeFactory.instance.objectNode();
383             final String nameAsParent = parentName + "_" + localName;
384             final ObjectNode properties =
385                     processChildren(childSchema, containerChildren, parentName + "_" + localName, definitions,
386                             definitionNames, isConfig, stack, oaversion);
387
388             final String nodeName = parentName + (isConfig ? CONFIG : "") + "_" + localName;
389             final String postNodeName = parentName + CONFIG + "_" + localName + POST_SUFFIX;
390             final String postXmlNodeName = postNodeName + XML_SUFFIX;
391             final String parentNameConfigLocalName = parentName + CONFIG + "_" + localName;
392
393             final String description = schemaNode.getDescription().orElse("");
394             final String discriminator;
395
396             if (!definitionNames.isListedNode(schemaNode)) {
397                 final List<String> names = List.of(parentNameConfigLocalName,
398                         parentNameConfigLocalName + TOP,
399                         nameAsParent,
400                         nameAsParent + TOP,
401                         postNodeName,
402                         postXmlNodeName);
403                 discriminator = definitionNames.pickDiscriminator(schemaNode, names);
404             } else {
405                 discriminator = definitionNames.getDiscriminator(schemaNode);
406             }
407
408             if (isConfig) {
409                 final ObjectNode postSchema = createPostJsonSchema(schemaNode, properties, postNodeName, description);
410                 String truePostNodeName = postNodeName + discriminator;
411                 definitions.set(truePostNodeName, postSchema);
412
413                 final ObjectNode postXmlSchema = JsonNodeFactory.instance.objectNode();
414                 postXmlSchema.put(REF_KEY, getAppropriateModelPrefix(oaversion) + truePostNodeName);
415                 definitions.set(postXmlNodeName + discriminator, postXmlSchema);
416             }
417
418             childSchema.put(TYPE_KEY, OBJECT_TYPE);
419             childSchema.set(PROPERTIES_KEY, properties);
420             childSchema.put(TITLE_KEY, nodeName);
421             childSchema.put(DESCRIPTION_KEY, description);
422
423             final String defName = nodeName + discriminator;
424             childSchema.set(XML_KEY, buildXmlParameter(schemaNode));
425             definitions.set(defName, childSchema);
426
427             return processTopData(nodeName, discriminator, definitions, schemaNode, oaversion);
428         }
429         return null;
430     }
431
432     private static ObjectNode createPostJsonSchema(final SchemaNode dataNode, final ObjectNode properties,
433             final String postNodeName, final String description) {
434         final ObjectNode postSchema = JsonNodeFactory.instance.objectNode();
435         final ObjectNode postItemProperties;
436         if (dataNode instanceof ListSchemaNode) {
437             postItemProperties = createListItemProperties(properties, (ListSchemaNode) dataNode);
438         } else {
439             postItemProperties = properties.deepCopy();
440         }
441         postSchema.put(TYPE_KEY, OBJECT_TYPE);
442         postSchema.set(PROPERTIES_KEY, postItemProperties);
443         postSchema.put(TITLE_KEY, postNodeName);
444         postSchema.put(DESCRIPTION_KEY, description);
445         postSchema.set(XML_KEY, buildXmlParameter(dataNode));
446         return postSchema;
447     }
448
449     private static ObjectNode createListItemProperties(final ObjectNode properties, final ListSchemaNode listNode) {
450         final ObjectNode postListItemProperties = JsonNodeFactory.instance.objectNode();
451         final List<QName> keyDefinition = listNode.getKeyDefinition();
452         final Set<String> keys = listNode.getChildNodes().stream()
453                 .filter(node -> keyDefinition.contains(node.getQName()))
454                 .map(node -> node.getQName().getLocalName())
455                 .collect(Collectors.toSet());
456
457         Iterator<Map.Entry<String, JsonNode>> it = properties.fields();
458         while (it.hasNext()) {
459             Map.Entry<String, JsonNode> property = it.next();
460             if (!keys.contains(property.getKey())) {
461                 postListItemProperties.set(property.getKey(), property.getValue());
462             }
463         }
464
465         return postListItemProperties;
466     }
467
468     /**
469      * Processes the nodes.
470      */
471     private ObjectNode processChildren(
472             final ObjectNode parentNode, final Collection<? extends DataSchemaNode> nodes, final String parentName,
473             final ObjectNode definitions, final DefinitionNames definitionNames, final boolean isConfig,
474             final SchemaInferenceStack stack, final OAversion oaversion) throws IOException {
475         final ObjectNode properties = JsonNodeFactory.instance.objectNode();
476         final ArrayNode required = JsonNodeFactory.instance.arrayNode();
477         for (final DataSchemaNode node : nodes) {
478             stack.enterSchemaTree(node.getQName());
479             if (!isConfig || node.isConfiguration()) {
480                 /*
481                     Add module name prefix to property name, when needed, when ServiceNow can process colons,
482                     use RestDocGenUtil#resolveNodesName for creating property name
483                  */
484                 final String propertyName = node.getQName().getLocalName();
485                 final ObjectNode property;
486                 if (node instanceof LeafSchemaNode) {
487                     processLeafNode((LeafSchemaNode) node, propertyName, properties,
488                             required, stack, definitions, definitionNames, oaversion);
489                 } else if (node instanceof AnyxmlSchemaNode) {
490                     processAnyXMLNode((AnyxmlSchemaNode) node, propertyName, properties,
491                             required);
492                 } else if (node instanceof AnydataSchemaNode) {
493                     processAnydataNode((AnydataSchemaNode) node, propertyName, properties, required);
494                 } else {
495                     if (node instanceof ListSchemaNode || node instanceof ContainerSchemaNode) {
496                         property = processDataNodeContainer((DataNodeContainer) node, parentName, definitions,
497                                 definitionNames, isConfig, stack, oaversion);
498                         if (!isConfig) {
499                             processActionNodeContainer(node, parentName, definitions, definitionNames, stack,
500                                     oaversion);
501                         }
502                     } else if (node instanceof LeafListSchemaNode) {
503                         property = processLeafListNode((LeafListSchemaNode) node, stack, definitions,
504                                 definitionNames, oaversion);
505
506                     } else if (node instanceof ChoiceSchemaNode) {
507                         for (final CaseSchemaNode variant : ((ChoiceSchemaNode) node).getCases()) {
508                             stack.enterSchemaTree(variant.getQName());
509                             processChoiceNode(variant.getChildNodes(), parentName, definitions, definitionNames,
510                                     isConfig, stack, properties, oaversion);
511                             stack.exit();
512                         }
513                         stack.exit();
514                         // FIXME dangerous statement here! Try to rework without continue.
515                         continue;
516                     } else {
517                         throw new IllegalArgumentException("Unknown DataSchemaNode type: " + node.getClass());
518                     }
519                     properties.set(propertyName, property);
520                 }
521             }
522             stack.exit();
523         }
524         parentNode.set(PROPERTIES_KEY, properties);
525         setRequiredIfNotEmpty(parentNode, required);
526         return properties;
527     }
528
529     private ObjectNode processLeafListNode(final LeafListSchemaNode listNode, final SchemaInferenceStack stack,
530                                            final ObjectNode definitions, final DefinitionNames definitionNames,
531                                            final OAversion oaversion) {
532         final ObjectNode props = JsonNodeFactory.instance.objectNode();
533         props.put(TYPE_KEY, ARRAY_TYPE);
534
535         final ObjectNode itemsVal = JsonNodeFactory.instance.objectNode();
536         final Optional<ElementCountConstraint> optConstraint = listNode.getElementCountConstraint();
537         processElementCount(optConstraint, props);
538
539         processTypeDef(listNode.getType(), listNode, itemsVal, stack, definitions, definitionNames, oaversion);
540         props.set(ITEMS_KEY, itemsVal);
541
542         props.put(DESCRIPTION_KEY, listNode.getDescription().orElse(""));
543
544         return props;
545     }
546
547     private void processChoiceNode(
548             final Iterable<? extends DataSchemaNode> nodes, final String parentName, final ObjectNode definitions,
549             final DefinitionNames definitionNames, final boolean isConfig,
550             final SchemaInferenceStack stack, final ObjectNode properties, final OAversion oaversion)
551             throws IOException {
552         for (final DataSchemaNode node : nodes) {
553             stack.enterSchemaTree(node.getQName());
554             /*
555                 Add module name prefix to property name, when needed, when ServiceNow can process colons,
556                 use RestDocGenUtil#resolveNodesName for creating property name
557              */
558             final String name = node.getQName().getLocalName();
559             final ObjectNode property;
560
561             /*
562                 Ignore mandatoriness(passing unreferenced arrayNode to process...Node), because choice produces multiple
563                 properties
564              */
565             if (node instanceof LeafSchemaNode) {
566                 processLeafNode((LeafSchemaNode) node, name, properties,
567                         JsonNodeFactory.instance.arrayNode(), stack, definitions, definitionNames, oaversion);
568             } else if (node instanceof AnyxmlSchemaNode) {
569                 processAnyXMLNode((AnyxmlSchemaNode) node, name, properties,
570                         JsonNodeFactory.instance.arrayNode());
571             } else if (node instanceof AnydataSchemaNode) {
572                 processAnydataNode((AnydataSchemaNode) node, name, properties,
573                         JsonNodeFactory.instance.arrayNode());
574             } else {
575                 if (node instanceof ListSchemaNode || node instanceof ContainerSchemaNode) {
576                     property = processDataNodeContainer((DataNodeContainer) node, parentName, definitions,
577                             definitionNames, isConfig, stack, oaversion);
578                     if (!isConfig) {
579                         processActionNodeContainer(node, parentName, definitions, definitionNames, stack,
580                                 oaversion);
581                     }
582                 } else if (node instanceof LeafListSchemaNode) {
583                     property = processLeafListNode((LeafListSchemaNode) node, stack, definitions,
584                             definitionNames, oaversion);
585
586                 } else if (node instanceof ChoiceSchemaNode) {
587                     for (final CaseSchemaNode variant : ((ChoiceSchemaNode) node).getCases()) {
588                         processChoiceNode(variant.getChildNodes(), parentName, definitions, definitionNames, isConfig,
589                                 stack, properties, oaversion);
590                     }
591                     continue;
592                 } else {
593                     throw new IllegalArgumentException("Unknown DataSchemaNode type: " + node.getClass());
594                 }
595                 properties.set(name, property);
596             }
597             stack.exit();
598         }
599     }
600
601     private static void processElementCount(final Optional<ElementCountConstraint> constraint, final ObjectNode props) {
602         if (constraint.isPresent()) {
603             final ElementCountConstraint constr = constraint.get();
604             final Integer minElements = constr.getMinElements();
605             if (minElements != null) {
606                 props.put(MIN_ITEMS, minElements);
607             }
608             final Integer maxElements = constr.getMaxElements();
609             if (maxElements != null) {
610                 props.put(MAX_ITEMS, maxElements);
611             }
612         }
613     }
614
615     private static void processMandatory(final MandatoryAware node, final String nodeName, final ArrayNode required) {
616         if (node.isMandatory()) {
617             required.add(nodeName);
618         }
619     }
620
621     private ObjectNode processLeafNode(final LeafSchemaNode leafNode, final String jsonLeafName,
622                                        final ObjectNode properties, final ArrayNode required,
623                                        final SchemaInferenceStack stack, final ObjectNode definitions,
624                                        final DefinitionNames definitionNames, final OAversion oaversion) {
625         final ObjectNode property = JsonNodeFactory.instance.objectNode();
626
627         final String leafDescription = leafNode.getDescription().orElse("");
628         /*
629             Description can't be added, because nothing allowed alongside $ref.
630             allOf is not an option, because ServiceNow can't parse it.
631         */
632         if (!(leafNode.getType() instanceof IdentityrefTypeDefinition)) {
633             property.put(DESCRIPTION_KEY, leafDescription);
634         }
635
636         processTypeDef(leafNode.getType(), leafNode, property, stack, definitions, definitionNames, oaversion);
637         properties.set(jsonLeafName, property);
638         property.set(XML_KEY, buildXmlParameter(leafNode));
639         processMandatory(leafNode, jsonLeafName, required);
640
641         return property;
642     }
643
644     private static ObjectNode processAnydataNode(final AnydataSchemaNode leafNode, final String name,
645                                                  final ObjectNode properties, final ArrayNode required) {
646         final ObjectNode property = JsonNodeFactory.instance.objectNode();
647
648         final String leafDescription = leafNode.getDescription().orElse("");
649         property.put(DESCRIPTION_KEY, leafDescription);
650
651         final String localName = leafNode.getQName().getLocalName();
652         setDefaultValue(property, String.format("<%s> ... </%s>", localName, localName));
653         property.put(TYPE_KEY, STRING_TYPE);
654         property.set(XML_KEY, buildXmlParameter(leafNode));
655         processMandatory(leafNode, name, required);
656         properties.set(name, property);
657
658         return property;
659     }
660
661     private static ObjectNode processAnyXMLNode(final AnyxmlSchemaNode leafNode, final String name,
662                                                 final ObjectNode properties, final ArrayNode required) {
663         final ObjectNode property = JsonNodeFactory.instance.objectNode();
664
665         final String leafDescription = leafNode.getDescription().orElse("");
666         property.put(DESCRIPTION_KEY, leafDescription);
667
668         final String localName = leafNode.getQName().getLocalName();
669         setDefaultValue(property, String.format("<%s> ... </%s>", localName, localName));
670         property.put(TYPE_KEY, STRING_TYPE);
671         property.set(XML_KEY, buildXmlParameter(leafNode));
672         processMandatory(leafNode, name, required);
673         properties.set(name, property);
674
675         return property;
676     }
677
678     private String processTypeDef(final TypeDefinition<?> leafTypeDef, final DataSchemaNode node,
679                                   final ObjectNode property, final SchemaInferenceStack stack,
680                                   final ObjectNode definitions, final DefinitionNames definitionNames,
681                                   final OAversion oaversion) {
682         final String jsonType;
683         if (leafTypeDef instanceof BinaryTypeDefinition) {
684             jsonType = processBinaryType(property);
685
686         } else if (leafTypeDef instanceof BitsTypeDefinition) {
687             jsonType = processBitsType((BitsTypeDefinition) leafTypeDef, property);
688
689         } else if (leafTypeDef instanceof EnumTypeDefinition) {
690             jsonType = processEnumType((EnumTypeDefinition) leafTypeDef, property);
691
692         } else if (leafTypeDef instanceof IdentityrefTypeDefinition) {
693             jsonType = processIdentityRefType((IdentityrefTypeDefinition) leafTypeDef, property, definitions,
694                     definitionNames, oaversion, stack.getEffectiveModelContext());
695
696         } else if (leafTypeDef instanceof StringTypeDefinition) {
697             jsonType = processStringType(leafTypeDef, property, node.getQName().getLocalName());
698
699         } else if (leafTypeDef instanceof UnionTypeDefinition) {
700             jsonType = processUnionType((UnionTypeDefinition) leafTypeDef);
701
702         } else if (leafTypeDef instanceof EmptyTypeDefinition) {
703             jsonType = OBJECT_TYPE;
704         } else if (leafTypeDef instanceof LeafrefTypeDefinition) {
705             return processTypeDef(stack.resolveLeafref((LeafrefTypeDefinition) leafTypeDef), node, property,
706                 stack, definitions, definitionNames, oaversion);
707         } else if (leafTypeDef instanceof BooleanTypeDefinition) {
708             jsonType = BOOLEAN_TYPE;
709             setDefaultValue(property, true);
710         } else if (leafTypeDef instanceof RangeRestrictedTypeDefinition) {
711             jsonType = processNumberType((RangeRestrictedTypeDefinition<?, ?>) leafTypeDef, property);
712         } else if (leafTypeDef instanceof InstanceIdentifierTypeDefinition) {
713             jsonType = processInstanceIdentifierType(node, property, stack.getEffectiveModelContext());
714         } else {
715             jsonType = STRING_TYPE;
716         }
717         if (!(leafTypeDef instanceof IdentityrefTypeDefinition)) {
718             putIfNonNull(property, TYPE_KEY, jsonType);
719             if (leafTypeDef.getDefaultValue().isPresent()) {
720                 final Object defaultValue = leafTypeDef.getDefaultValue().get();
721                 if (defaultValue instanceof String) {
722                     final String stringDefaultValue = (String) defaultValue;
723                     if (leafTypeDef instanceof BooleanTypeDefinition) {
724                         setDefaultValue(property, Boolean.valueOf(stringDefaultValue));
725                     } else if (leafTypeDef instanceof DecimalTypeDefinition
726                             || leafTypeDef instanceof Uint64TypeDefinition) {
727                         setDefaultValue(property, new BigDecimal(stringDefaultValue));
728                     } else if (leafTypeDef instanceof RangeRestrictedTypeDefinition) {
729                         //uint8,16,32 int8,16,32,64
730                         if (isHexadecimalOrOctal((RangeRestrictedTypeDefinition<?, ?>)leafTypeDef)) {
731                             setDefaultValue(property, stringDefaultValue);
732                         } else {
733                             setDefaultValue(property, Long.valueOf(stringDefaultValue));
734                         }
735                     } else {
736                         setDefaultValue(property, stringDefaultValue);
737                     }
738                 } else {
739                     //we should never get here. getDefaultValue always gives us string
740                     setDefaultValue(property, defaultValue.toString());
741                 }
742             }
743         }
744         return jsonType;
745     }
746
747     private static String processBinaryType(final ObjectNode property) {
748         property.put(FORMAT_KEY, "byte");
749         return STRING_TYPE;
750     }
751
752     private static String processEnumType(final EnumTypeDefinition enumLeafType,
753                                           final ObjectNode property) {
754         final List<EnumPair> enumPairs = enumLeafType.getValues();
755         final ArrayNode enumNames = new ArrayNode(JsonNodeFactory.instance);
756         for (final EnumPair enumPair : enumPairs) {
757             enumNames.add(new TextNode(enumPair.getName()));
758         }
759
760         property.set(ENUM_KEY, enumNames);
761         setDefaultValue(property, enumLeafType.getValues().iterator().next().getName());
762         return STRING_TYPE;
763     }
764
765     private String processIdentityRefType(final IdentityrefTypeDefinition leafTypeDef, final ObjectNode property,
766                                           final ObjectNode definitions, final DefinitionNames definitionNames,
767                                           final OAversion oaversion, final EffectiveModelContext schemaContext) {
768         final String definitionName;
769         if (isImported(leafTypeDef)) {
770             definitionName = addImportedIdentity(leafTypeDef, definitions, definitionNames, schemaContext);
771         } else {
772             final SchemaNode node = leafTypeDef.getIdentities().iterator().next();
773             definitionName = node.getQName().getLocalName() + definitionNames.getDiscriminator(node);
774         }
775         property.put(REF_KEY, getAppropriateModelPrefix(oaversion) + definitionName);
776         return STRING_TYPE;
777     }
778
779     private static String addImportedIdentity(final IdentityrefTypeDefinition leafTypeDef,
780                                               final ObjectNode definitions, final DefinitionNames definitionNames,
781                                               final EffectiveModelContext context) {
782         final IdentitySchemaNode idNode = leafTypeDef.getIdentities().iterator().next();
783         final String identityName = idNode.getQName().getLocalName();
784         if (!definitionNames.isListedNode(idNode)) {
785             final ObjectNode identityObj = buildIdentityObject(idNode, context);
786             final String discriminator = definitionNames.pickDiscriminator(idNode, List.of(identityName));
787             final String name = identityName + discriminator;
788             definitions.set(name, identityObj);
789             return name;
790         } else {
791             return identityName + definitionNames.getDiscriminator(idNode);
792         }
793     }
794
795     private static ObjectNode buildIdentityObject(final IdentitySchemaNode idNode,
796             final EffectiveModelContext context) {
797         final ObjectNode identityObj = JsonNodeFactory.instance.objectNode();
798         final String identityName = idNode.getQName().getLocalName();
799         LOG.debug("Processing Identity: {}", identityName);
800
801         identityObj.put(TITLE_KEY, identityName);
802         identityObj.put(DESCRIPTION_KEY, idNode.getDescription().orElse(""));
803
804         final Collection<? extends IdentitySchemaNode> derivedIds = context.getDerivedIdentities(idNode);
805
806         final ArrayNode enumPayload = JsonNodeFactory.instance.arrayNode();
807         enumPayload.add(identityName);
808         populateEnumWithDerived(derivedIds, enumPayload, context);
809         identityObj.set(ENUM_KEY, enumPayload);
810         identityObj.put(TYPE_KEY, STRING_TYPE);
811         return identityObj;
812     }
813
814     private boolean isImported(final IdentityrefTypeDefinition leafTypeDef) {
815         return !leafTypeDef.getQName().getModule().equals(topLevelModule.getQNameModule());
816     }
817
818     private static String processBitsType(final BitsTypeDefinition bitsType,
819                                           final ObjectNode property) {
820         property.put(MIN_ITEMS, 0);
821         property.put(UNIQUE_ITEMS_KEY, true);
822         final ArrayNode enumNames = new ArrayNode(JsonNodeFactory.instance);
823         final Collection<? extends Bit> bits = bitsType.getBits();
824         for (final Bit bit : bits) {
825             enumNames.add(new TextNode(bit.getName()));
826         }
827         property.set(ENUM_KEY, enumNames);
828         property.put(DEFAULT_KEY, enumNames.iterator().next() + " " + enumNames.get(enumNames.size() - 1));
829         return STRING_TYPE;
830     }
831
832     private static String processStringType(final TypeDefinition<?> stringType, final ObjectNode property,
833                                             final String nodeName) {
834         StringTypeDefinition type = (StringTypeDefinition) stringType;
835         Optional<LengthConstraint> lengthConstraints = ((StringTypeDefinition) stringType).getLengthConstraint();
836         while (lengthConstraints.isEmpty() && type.getBaseType() != null) {
837             type = type.getBaseType();
838             lengthConstraints = type.getLengthConstraint();
839         }
840
841         if (lengthConstraints.isPresent()) {
842             final Range<Integer> range = lengthConstraints.get().getAllowedRanges().span();
843             putIfNonNull(property, MIN_LENGTH_KEY, range.lowerEndpoint());
844             putIfNonNull(property, MAX_LENGTH_KEY, range.upperEndpoint());
845         }
846
847         if (type.getPatternConstraints().iterator().hasNext()) {
848             final PatternConstraint pattern = type.getPatternConstraints().iterator().next();
849             String regex = pattern.getJavaPatternString();
850             regex = regex.substring(1, regex.length() - 1);
851             String defaultValue = "";
852             try {
853                 final Generex generex = new Generex(regex);
854                 defaultValue = generex.random();
855             } catch (IllegalArgumentException ex) {
856                 LOG.warn("Cannot create example string for type: {} with regex: {}.", stringType.getQName(), regex);
857             }
858             setDefaultValue(property, defaultValue);
859         } else {
860             setDefaultValue(property, "Some " + nodeName);
861         }
862         return STRING_TYPE;
863     }
864
865     private static String processNumberType(final RangeRestrictedTypeDefinition<?, ?> leafTypeDef,
866             final ObjectNode property) {
867         final Optional<Number> maybeLower = ((RangeRestrictedTypeDefinition<?, ?>) leafTypeDef).getRangeConstraint()
868                 .map(RangeConstraint::getAllowedRanges).map(RangeSet::span).map(Range::lowerEndpoint);
869
870         if (isHexadecimalOrOctal(leafTypeDef)) {
871             return STRING_TYPE;
872         }
873
874         if (leafTypeDef instanceof DecimalTypeDefinition) {
875             maybeLower.ifPresent(number -> setDefaultValue(property, (BigDecimal) number));
876             return NUMBER_TYPE;
877         }
878         if (leafTypeDef instanceof Uint8TypeDefinition
879                 || leafTypeDef instanceof Uint16TypeDefinition
880                 || leafTypeDef instanceof Int8TypeDefinition
881                 || leafTypeDef instanceof Int16TypeDefinition
882                 || leafTypeDef instanceof Int32TypeDefinition) {
883
884             property.put(FORMAT_KEY, INT32_FORMAT);
885             maybeLower.ifPresent(number -> setDefaultValue(property, Integer.valueOf(number.toString())));
886         } else if (leafTypeDef instanceof Uint32TypeDefinition
887                 || leafTypeDef instanceof Int64TypeDefinition) {
888
889             property.put(FORMAT_KEY, INT64_FORMAT);
890             maybeLower.ifPresent(number -> setDefaultValue(property, Long.valueOf(number.toString())));
891         } else {
892             //uint64
893             setDefaultValue(property, 0);
894         }
895         return INTEGER_TYPE;
896     }
897
898     private static boolean isHexadecimalOrOctal(final RangeRestrictedTypeDefinition<?, ?> typeDef) {
899         final Optional<?> optDefaultValue = typeDef.getDefaultValue();
900         if (optDefaultValue.isPresent()) {
901             final String defaultValue = (String)optDefaultValue.get();
902             return defaultValue.startsWith("0") || defaultValue.startsWith("-0");
903         }
904         return false;
905     }
906
907     private static String processInstanceIdentifierType(final DataSchemaNode node, final ObjectNode property,
908                                                         final EffectiveModelContext schemaContext) {
909         // create example instance-identifier to the first container of node's module if exists or leave it empty
910         final var module = schemaContext.findModule(node.getQName().getModule());
911         if (module.isPresent()) {
912             final var container = module.get().getChildNodes().stream()
913                     .filter(n -> n instanceof ContainerSchemaNode)
914                     .findFirst();
915             container.ifPresent(c -> setDefaultValue(property, String.format("/%s:%s", module.get().getPrefix(),
916                     c.getQName().getLocalName())));
917         }
918
919         return STRING_TYPE;
920     }
921
922     private static String processUnionType(final UnionTypeDefinition unionType) {
923         boolean isStringTakePlace = false;
924         boolean isNumberTakePlace = false;
925         boolean isBooleanTakePlace = false;
926         for (final TypeDefinition<?> typeDef : unionType.getTypes()) {
927             if (!isStringTakePlace) {
928                 if (typeDef instanceof StringTypeDefinition
929                         || typeDef instanceof BitsTypeDefinition
930                         || typeDef instanceof BinaryTypeDefinition
931                         || typeDef instanceof IdentityrefTypeDefinition
932                         || typeDef instanceof EnumTypeDefinition
933                         || typeDef instanceof LeafrefTypeDefinition
934                         || typeDef instanceof UnionTypeDefinition) {
935                     isStringTakePlace = true;
936                 } else if (!isNumberTakePlace && typeDef instanceof RangeRestrictedTypeDefinition) {
937                     isNumberTakePlace = true;
938                 } else if (!isBooleanTakePlace && typeDef instanceof BooleanTypeDefinition) {
939                     isBooleanTakePlace = true;
940                 }
941             }
942         }
943         if (isStringTakePlace) {
944             return STRING_TYPE;
945         }
946         if (isBooleanTakePlace) {
947             if (isNumberTakePlace) {
948                 return STRING_TYPE;
949             }
950             return BOOLEAN_TYPE;
951         }
952         return NUMBER_TYPE;
953     }
954
955     private static ObjectNode buildXmlParameter(final SchemaNode node) {
956         final ObjectNode xml = JsonNodeFactory.instance.objectNode();
957         final QName qName = node.getQName();
958         xml.put(NAME_KEY, qName.getLocalName());
959         xml.put(NAMESPACE_KEY, qName.getNamespace().toString());
960         return xml;
961     }
962
963     private static void putIfNonNull(final ObjectNode property, final String key, final Number number) {
964         if (key != null && number != null) {
965             if (number instanceof Double) {
966                 property.put(key, (Double) number);
967             } else if (number instanceof Float) {
968                 property.put(key, (Float) number);
969             } else if (number instanceof Integer) {
970                 property.put(key, (Integer) number);
971             } else if (number instanceof Short) {
972                 property.put(key, (Short) number);
973             } else if (number instanceof Long) {
974                 property.put(key, (Long) number);
975             }
976         }
977     }
978
979     private static void putIfNonNull(final ObjectNode property, final String key, final String value) {
980         if (key != null && value != null) {
981             property.put(key, value);
982         }
983     }
984
985     private static void setRequiredIfNotEmpty(final ObjectNode node, final ArrayNode required) {
986         if (required.size() > 0) {
987             node.set(REQUIRED_KEY, required);
988         }
989     }
990
991     private static void setDefaultValue(final ObjectNode property, final String value) {
992         property.put(DEFAULT_KEY, value);
993     }
994
995     private static void setDefaultValue(final ObjectNode property, final Integer value) {
996         property.put(DEFAULT_KEY, value);
997     }
998
999     private static void setDefaultValue(final ObjectNode property, final Long value) {
1000         property.put(DEFAULT_KEY, value);
1001     }
1002
1003     private static void setDefaultValue(final ObjectNode property, final BigDecimal value) {
1004         property.put(DEFAULT_KEY, value);
1005     }
1006
1007     private static void setDefaultValue(final ObjectNode property, final Boolean value) {
1008         property.put(DEFAULT_KEY, value);
1009     }
1010
1011 }