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